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)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 0)
38 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
45 /* for MovePlayer() */
46 #define MF_NO_ACTION 0
50 /* for ScrollPlayer() */
52 #define SCROLL_GO_ON 1
54 /* for Bang()/Explode() */
55 #define EX_PHASE_START 0
56 #define EX_TYPE_NONE 0
57 #define EX_TYPE_NORMAL (1 << 0)
58 #define EX_TYPE_CENTER (1 << 1)
59 #define EX_TYPE_BORDER (1 << 2)
60 #define EX_TYPE_CROSS (1 << 3)
61 #define EX_TYPE_DYNA (1 << 4)
62 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
64 /* special positions in the game control window (relative to control window) */
67 #define XX_EMERALDS 29
68 #define YY_EMERALDS 54
69 #define XX_DYNAMITE 29
70 #define YY_DYNAMITE 89
79 /* special positions in the game control window (relative to main window) */
80 #define DX_LEVEL (DX + XX_LEVEL)
81 #define DY_LEVEL (DY + YY_LEVEL)
82 #define DX_EMERALDS (DX + XX_EMERALDS)
83 #define DY_EMERALDS (DY + YY_EMERALDS)
84 #define DX_DYNAMITE (DX + XX_DYNAMITE)
85 #define DY_DYNAMITE (DY + YY_DYNAMITE)
86 #define DX_KEYS (DX + XX_KEYS)
87 #define DY_KEYS (DY + YY_KEYS)
88 #define DX_SCORE (DX + XX_SCORE)
89 #define DY_SCORE (DY + YY_SCORE)
90 #define DX_TIME1 (DX + XX_TIME1)
91 #define DX_TIME2 (DX + XX_TIME2)
92 #define DY_TIME (DY + YY_TIME)
94 /* values for initial player move delay (initial delay counter value) */
95 #define INITIAL_MOVE_DELAY_OFF -1
96 #define INITIAL_MOVE_DELAY_ON 0
98 /* values for player movement speed (which is in fact a delay value) */
99 #define MOVE_DELAY_MIN_SPEED 32
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED 4
102 #define MOVE_DELAY_MAX_SPEED 1
105 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
106 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
108 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
109 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
111 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
112 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
114 /* values for other actions */
115 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
116 #define MOVE_STEPSIZE_MIN (1)
117 #define MOVE_STEPSIZE_MAX (TILEX)
119 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
120 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
122 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
124 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
125 RND(element_info[e].push_delay_random))
126 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
127 RND(element_info[e].drop_delay_random))
128 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
129 RND(element_info[e].move_delay_random))
130 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
131 (element_info[e].move_delay_random))
132 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
133 RND(element_info[e].ce_value_random_initial))
134 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
135 RND((c)->delay_random * (c)->delay_frames))
136 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
137 RND((c)->delay_random))
139 #define GET_TARGET_ELEMENT(e, ch) \
140 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
141 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
143 #define CAN_GROW_INTO(e) \
144 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
146 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
150 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 (CAN_MOVE_INTO_ACID(e) && \
153 Feld[x][y] == EL_ACID) || \
156 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
157 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
158 (CAN_MOVE_INTO_ACID(e) && \
159 Feld[x][y] == EL_ACID) || \
162 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
163 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 (CAN_MOVE_INTO_ACID(e) && \
166 Feld[x][y] == EL_ACID) || \
167 (DONT_COLLIDE_WITH(e) && \
169 !PLAYER_ENEMY_PROTECTED(x, y))))
171 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
174 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
177 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
180 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
183 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
186 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
189 #define PIG_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
192 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
194 IS_FOOD_PENGUIN(Feld[x][y])))
195 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
198 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
201 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
204 #define GROUP_NR(e) ((e) - EL_GROUP_START)
205 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
206 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
207 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
209 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
210 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
212 #define CE_ENTER_FIELD_COND(e, x, y) \
213 (!IS_PLAYER(x, y) && \
214 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
216 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
219 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
220 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
222 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
223 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
224 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
225 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
227 /* game button identifiers */
228 #define GAME_CTRL_ID_STOP 0
229 #define GAME_CTRL_ID_PAUSE 1
230 #define GAME_CTRL_ID_PLAY 2
231 #define SOUND_CTRL_ID_MUSIC 3
232 #define SOUND_CTRL_ID_LOOPS 4
233 #define SOUND_CTRL_ID_SIMPLE 5
235 #define NUM_GAME_BUTTONS 6
238 /* forward declaration for internal use */
240 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
241 static void AdvanceFrameAndPlayerCounters(int);
243 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
244 static boolean MovePlayer(struct PlayerInfo *, int, int);
245 static void ScrollPlayer(struct PlayerInfo *, int);
246 static void ScrollScreen(struct PlayerInfo *, int);
248 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
250 static void InitBeltMovement(void);
251 static void CloseAllOpenTimegates(void);
252 static void CheckGravityMovement(struct PlayerInfo *);
253 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
254 static void KillPlayerUnlessEnemyProtected(int, int);
255 static void KillPlayerUnlessExplosionProtected(int, int);
257 static void TestIfPlayerTouchesCustomElement(int, int);
258 static void TestIfElementTouchesCustomElement(int, int);
259 static void TestIfElementHitsCustomElement(int, int, int);
261 static void TestIfElementSmashesCustomElement(int, int, int);
264 static void ChangeElement(int, int, int);
266 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
267 #define CheckTriggeredElementChange(x, y, e, ev) \
268 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
269 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
270 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
271 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
272 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
273 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
274 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
276 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
277 #define CheckElementChange(x, y, e, te, ev) \
278 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
279 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
280 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
281 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
282 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
284 static void PlayLevelSound(int, int, int);
285 static void PlayLevelSoundNearest(int, int, int);
286 static void PlayLevelSoundAction(int, int, int);
287 static void PlayLevelSoundElementAction(int, int, int, int);
288 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
289 static void PlayLevelSoundActionIfLoop(int, int, int);
290 static void StopLevelSoundActionIfLoop(int, int, int);
291 static void PlayLevelMusic();
293 static void MapGameButtons();
294 static void HandleGameButtons(struct GadgetInfo *);
296 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
299 /* ------------------------------------------------------------------------- */
300 /* definition of elements that automatically change to other elements after */
301 /* a specified time, eventually calling a function when changing */
302 /* ------------------------------------------------------------------------- */
304 /* forward declaration for changer functions */
305 static void InitBuggyBase(int x, int y);
306 static void WarnBuggyBase(int x, int y);
308 static void InitTrap(int x, int y);
309 static void ActivateTrap(int x, int y);
310 static void ChangeActiveTrap(int x, int y);
312 static void InitRobotWheel(int x, int y);
313 static void RunRobotWheel(int x, int y);
314 static void StopRobotWheel(int x, int y);
316 static void InitTimegateWheel(int x, int y);
317 static void RunTimegateWheel(int x, int y);
319 struct ChangingElementInfo
324 void (*pre_change_function)(int x, int y);
325 void (*change_function)(int x, int y);
326 void (*post_change_function)(int x, int y);
329 static struct ChangingElementInfo change_delay_list[] =
380 EL_SWITCHGATE_OPENING,
388 EL_SWITCHGATE_CLOSING,
389 EL_SWITCHGATE_CLOSED,
421 EL_ACID_SPLASH_RIGHT,
430 EL_SP_BUGGY_BASE_ACTIVATING,
437 EL_SP_BUGGY_BASE_ACTIVATING,
438 EL_SP_BUGGY_BASE_ACTIVE,
445 EL_SP_BUGGY_BASE_ACTIVE,
469 EL_ROBOT_WHEEL_ACTIVE,
477 EL_TIMEGATE_SWITCH_ACTIVE,
498 int push_delay_fixed, push_delay_random;
503 { EL_BALLOON, 0, 0 },
505 { EL_SOKOBAN_OBJECT, 2, 0 },
506 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
507 { EL_SATELLITE, 2, 0 },
508 { EL_SP_DISK_YELLOW, 2, 0 },
510 { EL_UNDEFINED, 0, 0 },
518 move_stepsize_list[] =
520 { EL_AMOEBA_DROP, 2 },
521 { EL_AMOEBA_DROPPING, 2 },
522 { EL_QUICKSAND_FILLING, 1 },
523 { EL_QUICKSAND_EMPTYING, 1 },
524 { EL_MAGIC_WALL_FILLING, 2 },
525 { EL_BD_MAGIC_WALL_FILLING, 2 },
526 { EL_MAGIC_WALL_EMPTYING, 2 },
527 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
537 collect_count_list[] =
540 { EL_BD_DIAMOND, 1 },
541 { EL_EMERALD_YELLOW, 1 },
542 { EL_EMERALD_RED, 1 },
543 { EL_EMERALD_PURPLE, 1 },
545 { EL_SP_INFOTRON, 1 },
557 access_direction_list[] =
559 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
560 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
561 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
562 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
563 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
564 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
565 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
566 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
567 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
568 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
569 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
571 { EL_SP_PORT_LEFT, MV_RIGHT },
572 { EL_SP_PORT_RIGHT, MV_LEFT },
573 { EL_SP_PORT_UP, MV_DOWN },
574 { EL_SP_PORT_DOWN, MV_UP },
575 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
576 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
577 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
578 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
579 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
580 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
581 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
582 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
583 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
584 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
585 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
586 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
587 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
588 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
589 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
591 { EL_UNDEFINED, MV_NONE }
594 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
596 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
597 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
598 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
599 IS_JUST_CHANGING(x, y))
601 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
604 void GetPlayerConfig()
606 if (!audio.sound_available)
607 setup.sound_simple = FALSE;
609 if (!audio.loops_available)
610 setup.sound_loops = FALSE;
612 if (!audio.music_available)
613 setup.sound_music = FALSE;
615 if (!video.fullscreen_available)
616 setup.fullscreen = FALSE;
618 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
620 SetAudioMode(setup.sound);
624 static int getBeltNrFromBeltElement(int element)
626 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
627 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
628 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
631 static int getBeltNrFromBeltActiveElement(int element)
633 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
634 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
635 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
638 static int getBeltNrFromBeltSwitchElement(int element)
640 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
641 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
642 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
645 static int getBeltDirNrFromBeltSwitchElement(int element)
647 static int belt_base_element[4] =
649 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
650 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
651 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
652 EL_CONVEYOR_BELT_4_SWITCH_LEFT
655 int belt_nr = getBeltNrFromBeltSwitchElement(element);
656 int belt_dir_nr = element - belt_base_element[belt_nr];
658 return (belt_dir_nr % 3);
661 static int getBeltDirFromBeltSwitchElement(int element)
663 static int belt_move_dir[3] =
670 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
672 return belt_move_dir[belt_dir_nr];
675 static void InitPlayerField(int x, int y, int element, boolean init_game)
677 if (element == EL_SP_MURPHY)
681 if (stored_player[0].present)
683 Feld[x][y] = EL_SP_MURPHY_CLONE;
689 stored_player[0].use_murphy = TRUE;
692 Feld[x][y] = EL_PLAYER_1;
698 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
699 int jx = player->jx, jy = player->jy;
701 player->present = TRUE;
703 player->block_last_field = (element == EL_SP_MURPHY ?
704 level.sp_block_last_field :
705 level.block_last_field);
707 /* ---------- initialize player's last field block delay --------------- */
709 /* always start with reliable default value (no adjustment needed) */
710 player->block_delay_adjustment = 0;
712 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
713 if (player->block_last_field && element == EL_SP_MURPHY)
714 player->block_delay_adjustment = 1;
716 /* special case 2: in game engines before 3.1.1, blocking was different */
717 if (game.use_block_last_field_bug)
718 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
720 if (!options.network || player->connected)
722 player->active = TRUE;
724 /* remove potentially duplicate players */
725 if (StorePlayer[jx][jy] == Feld[x][y])
726 StorePlayer[jx][jy] = 0;
728 StorePlayer[x][y] = Feld[x][y];
732 printf("Player %d activated.\n", player->element_nr);
733 printf("[Local player is %d and currently %s.]\n",
734 local_player->element_nr,
735 local_player->active ? "active" : "not active");
739 Feld[x][y] = EL_EMPTY;
741 player->jx = player->last_jx = x;
742 player->jy = player->last_jy = y;
746 static void InitField(int x, int y, boolean init_game)
748 int element = Feld[x][y];
757 InitPlayerField(x, y, element, init_game);
760 case EL_SOKOBAN_FIELD_PLAYER:
761 element = Feld[x][y] = EL_PLAYER_1;
762 InitField(x, y, init_game);
764 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
765 InitField(x, y, init_game);
768 case EL_SOKOBAN_FIELD_EMPTY:
769 local_player->sokobanfields_still_needed++;
773 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
774 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
775 else if (x > 0 && Feld[x-1][y] == EL_ACID)
776 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
777 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
778 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
779 else if (y > 0 && Feld[x][y-1] == EL_ACID)
780 Feld[x][y] = EL_ACID_POOL_BOTTOM;
781 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
782 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
790 case EL_SPACESHIP_RIGHT:
791 case EL_SPACESHIP_UP:
792 case EL_SPACESHIP_LEFT:
793 case EL_SPACESHIP_DOWN:
795 case EL_BD_BUTTERFLY_RIGHT:
796 case EL_BD_BUTTERFLY_UP:
797 case EL_BD_BUTTERFLY_LEFT:
798 case EL_BD_BUTTERFLY_DOWN:
799 case EL_BD_BUTTERFLY:
800 case EL_BD_FIREFLY_RIGHT:
801 case EL_BD_FIREFLY_UP:
802 case EL_BD_FIREFLY_LEFT:
803 case EL_BD_FIREFLY_DOWN:
805 case EL_PACMAN_RIGHT:
829 if (y == lev_fieldy - 1)
831 Feld[x][y] = EL_AMOEBA_GROWING;
832 Store[x][y] = EL_AMOEBA_WET;
836 case EL_DYNAMITE_ACTIVE:
837 case EL_SP_DISK_RED_ACTIVE:
838 case EL_DYNABOMB_PLAYER_1_ACTIVE:
839 case EL_DYNABOMB_PLAYER_2_ACTIVE:
840 case EL_DYNABOMB_PLAYER_3_ACTIVE:
841 case EL_DYNABOMB_PLAYER_4_ACTIVE:
846 local_player->lights_still_needed++;
850 local_player->friends_still_needed++;
855 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
858 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
859 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
860 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
861 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
862 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
863 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
864 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
865 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
866 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
867 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
868 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
869 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
872 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
873 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
874 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
876 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
878 game.belt_dir[belt_nr] = belt_dir;
879 game.belt_dir_nr[belt_nr] = belt_dir_nr;
881 else /* more than one switch -- set it like the first switch */
883 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
888 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
890 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
893 case EL_LIGHT_SWITCH_ACTIVE:
895 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
900 if (IS_CUSTOM_ELEMENT(element))
902 if (CAN_MOVE(element))
905 #if USE_NEW_CUSTOM_VALUE
906 if (!element_info[element].use_last_ce_value || init_game)
907 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
911 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
914 else if (IS_GROUP_ELEMENT(element))
916 struct ElementGroupInfo *group = element_info[element].group;
917 int last_anim_random_frame = gfx.anim_random_frame;
920 if (group->choice_mode == ANIM_RANDOM)
921 gfx.anim_random_frame = RND(group->num_elements_resolved);
923 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
924 group->choice_mode, 0,
927 if (group->choice_mode == ANIM_RANDOM)
928 gfx.anim_random_frame = last_anim_random_frame;
932 Feld[x][y] = group->element_resolved[element_pos];
934 InitField(x, y, init_game);
941 #if USE_NEW_CUSTOM_VALUE
944 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
946 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
954 static inline void InitField_WithBug1(int x, int y, boolean init_game)
956 InitField(x, y, init_game);
958 /* not needed to call InitMovDir() -- already done by InitField()! */
959 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
960 CAN_MOVE(Feld[x][y]))
964 static inline void InitField_WithBug2(int x, int y, boolean init_game)
966 int old_element = Feld[x][y];
968 InitField(x, y, init_game);
970 /* not needed to call InitMovDir() -- already done by InitField()! */
971 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
972 CAN_MOVE(old_element) &&
973 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
976 /* this case is in fact a combination of not less than three bugs:
977 first, it calls InitMovDir() for elements that can move, although this is
978 already done by InitField(); then, it checks the element that was at this
979 field _before_ the call to InitField() (which can change it); lastly, it
980 was not called for "mole with direction" elements, which were treated as
981 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
985 inline void DrawGameValue_Emeralds(int value)
987 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
990 inline void DrawGameValue_Dynamite(int value)
992 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
995 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
999 /* currently only 4 of 8 possible keys are displayed */
1000 for (i = 0; i < STD_NUM_KEYS; i++)
1003 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1004 el2edimg(EL_KEY_1 + i));
1006 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1007 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1008 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1012 inline void DrawGameValue_Score(int value)
1014 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1017 inline void DrawGameValue_Time(int value)
1020 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1022 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1025 inline void DrawGameValue_Level(int value)
1028 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1031 /* misuse area for displaying emeralds to draw bigger level number */
1032 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1033 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1035 /* now copy it to the area for displaying level number */
1036 BlitBitmap(drawto, drawto,
1037 DX_EMERALDS, DY_EMERALDS + 1,
1038 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1039 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1040 DX_LEVEL - 1, DY_LEVEL + 1);
1042 /* restore the area for displaying emeralds */
1043 DrawGameValue_Emeralds(local_player->gems_still_needed);
1045 /* yes, this is all really ugly :-) */
1049 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1052 int key[MAX_NUM_KEYS];
1055 for (i = 0; i < MAX_NUM_KEYS; i++)
1056 key[i] = key_bits & (1 << i);
1058 DrawGameValue_Level(level_nr);
1060 DrawGameValue_Emeralds(emeralds);
1061 DrawGameValue_Dynamite(dynamite);
1062 DrawGameValue_Score(score);
1063 DrawGameValue_Time(time);
1065 DrawGameValue_Keys(key);
1068 void DrawGameDoorValues()
1072 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1074 DrawGameDoorValues_EM();
1079 DrawGameValue_Level(level_nr);
1081 DrawGameValue_Emeralds(local_player->gems_still_needed);
1082 DrawGameValue_Dynamite(local_player->inventory_size);
1083 DrawGameValue_Score(local_player->score);
1084 DrawGameValue_Time(TimeLeft);
1086 for (i = 0; i < MAX_PLAYERS; i++)
1087 DrawGameValue_Keys(stored_player[i].key);
1090 static void resolve_group_element(int group_element, int recursion_depth)
1092 static int group_nr;
1093 static struct ElementGroupInfo *group;
1094 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1097 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1099 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1100 group_element - EL_GROUP_START + 1);
1102 /* replace element which caused too deep recursion by question mark */
1103 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1108 if (recursion_depth == 0) /* initialization */
1110 group = element_info[group_element].group;
1111 group_nr = group_element - EL_GROUP_START;
1113 group->num_elements_resolved = 0;
1114 group->choice_pos = 0;
1117 for (i = 0; i < actual_group->num_elements; i++)
1119 int element = actual_group->element[i];
1121 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1124 if (IS_GROUP_ELEMENT(element))
1125 resolve_group_element(element, recursion_depth + 1);
1128 group->element_resolved[group->num_elements_resolved++] = element;
1129 element_info[element].in_group[group_nr] = TRUE;
1136 =============================================================================
1138 -----------------------------------------------------------------------------
1139 initialize game engine due to level / tape version number
1140 =============================================================================
1143 static void InitGameEngine()
1145 int i, j, k, l, x, y;
1147 /* set game engine from tape file when re-playing, else from level file */
1148 game.engine_version = (tape.playing ? tape.engine_version :
1149 level.game_version);
1151 /* ---------------------------------------------------------------------- */
1152 /* set flags for bugs and changes according to active game engine version */
1153 /* ---------------------------------------------------------------------- */
1156 Summary of bugfix/change:
1157 Fixed handling for custom elements that change when pushed by the player.
1159 Fixed/changed in version:
1163 Before 3.1.0, custom elements that "change when pushing" changed directly
1164 after the player started pushing them (until then handled in "DigField()").
1165 Since 3.1.0, these custom elements are not changed until the "pushing"
1166 move of the element is finished (now handled in "ContinueMoving()").
1168 Affected levels/tapes:
1169 The first condition is generally needed for all levels/tapes before version
1170 3.1.0, which might use the old behaviour before it was changed; known tapes
1171 that are affected are some tapes from the level set "Walpurgis Gardens" by
1173 The second condition is an exception from the above case and is needed for
1174 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1175 above (including some development versions of 3.1.0), but before it was
1176 known that this change would break tapes like the above and was fixed in
1177 3.1.1, so that the changed behaviour was active although the engine version
1178 while recording maybe was before 3.1.0. There is at least one tape that is
1179 affected by this exception, which is the tape for the one-level set "Bug
1180 Machine" by Juergen Bonhagen.
1183 game.use_change_when_pushing_bug =
1184 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1186 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1187 tape.game_version < VERSION_IDENT(3,1,1,0)));
1190 Summary of bugfix/change:
1191 Fixed handling for blocking the field the player leaves when moving.
1193 Fixed/changed in version:
1197 Before 3.1.1, when "block last field when moving" was enabled, the field
1198 the player is leaving when moving was blocked for the time of the move,
1199 and was directly unblocked afterwards. This resulted in the last field
1200 being blocked for exactly one less than the number of frames of one player
1201 move. Additionally, even when blocking was disabled, the last field was
1202 blocked for exactly one frame.
1203 Since 3.1.1, due to changes in player movement handling, the last field
1204 is not blocked at all when blocking is disabled. When blocking is enabled,
1205 the last field is blocked for exactly the number of frames of one player
1206 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1207 last field is blocked for exactly one more than the number of frames of
1210 Affected levels/tapes:
1211 (!!! yet to be determined -- probably many !!!)
1214 game.use_block_last_field_bug =
1215 (game.engine_version < VERSION_IDENT(3,1,1,0));
1218 Summary of bugfix/change:
1219 Changed behaviour of CE changes with multiple changes per single frame.
1221 Fixed/changed in version:
1225 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1226 This resulted in race conditions where CEs seem to behave strange in some
1227 situations (where triggered CE changes were just skipped because there was
1228 already a CE change on that tile in the playfield in that engine frame).
1229 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1230 (The number of changes per frame must be limited in any case, because else
1231 it is easily possible to define CE changes that would result in an infinite
1232 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1233 should be set large enough so that it would only be reached in cases where
1234 the corresponding CE change conditions run into a loop. Therefore, it seems
1235 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1236 maximal number of change pages for custom elements.)
1238 Affected levels/tapes:
1242 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1243 game.max_num_changes_per_frame = 1;
1245 game.max_num_changes_per_frame =
1246 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1249 /* ---------------------------------------------------------------------- */
1251 /* dynamically adjust element properties according to game engine version */
1252 InitElementPropertiesEngine(game.engine_version);
1255 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1256 printf(" tape version == %06d [%s] [file: %06d]\n",
1257 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1259 printf(" => game.engine_version == %06d\n", game.engine_version);
1262 /* ---------- recursively resolve group elements ------------------------- */
1264 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1265 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1266 element_info[i].in_group[j] = FALSE;
1268 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1269 resolve_group_element(EL_GROUP_START + i, 0);
1271 /* ---------- initialize player's initial move delay --------------------- */
1273 /* dynamically adjust player properties according to level information */
1274 game.initial_move_delay_value =
1275 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1277 /* dynamically adjust player properties according to game engine version */
1278 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1279 game.initial_move_delay_value : 0);
1281 /* ---------- initialize player's initial push delay --------------------- */
1283 /* dynamically adjust player properties according to game engine version */
1284 game.initial_push_delay_value =
1285 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1287 /* ---------- initialize changing elements ------------------------------- */
1289 /* initialize changing elements information */
1290 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1292 struct ElementInfo *ei = &element_info[i];
1294 /* this pointer might have been changed in the level editor */
1295 ei->change = &ei->change_page[0];
1297 if (!IS_CUSTOM_ELEMENT(i))
1299 ei->change->target_element = EL_EMPTY_SPACE;
1300 ei->change->delay_fixed = 0;
1301 ei->change->delay_random = 0;
1302 ei->change->delay_frames = 1;
1305 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1307 ei->has_change_event[j] = FALSE;
1309 ei->event_page_nr[j] = 0;
1310 ei->event_page[j] = &ei->change_page[0];
1314 /* add changing elements from pre-defined list */
1315 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1317 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1318 struct ElementInfo *ei = &element_info[ch_delay->element];
1320 ei->change->target_element = ch_delay->target_element;
1321 ei->change->delay_fixed = ch_delay->change_delay;
1323 ei->change->pre_change_function = ch_delay->pre_change_function;
1324 ei->change->change_function = ch_delay->change_function;
1325 ei->change->post_change_function = ch_delay->post_change_function;
1327 ei->change->can_change = TRUE;
1328 ei->change->can_change_or_has_action = TRUE;
1330 ei->has_change_event[CE_DELAY] = TRUE;
1332 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1333 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1336 /* ---------- initialize internal run-time variables ------------- */
1338 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1340 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1342 for (j = 0; j < ei->num_change_pages; j++)
1344 ei->change_page[j].can_change_or_has_action =
1345 (ei->change_page[j].can_change |
1346 ei->change_page[j].has_action);
1350 /* add change events from custom element configuration */
1351 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1353 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1355 for (j = 0; j < ei->num_change_pages; j++)
1357 if (!ei->change_page[j].can_change_or_has_action)
1360 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1362 /* only add event page for the first page found with this event */
1363 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1365 ei->has_change_event[k] = TRUE;
1367 ei->event_page_nr[k] = j;
1368 ei->event_page[k] = &ei->change_page[j];
1374 /* ---------- initialize run-time trigger player and element ------------- */
1376 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1378 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1380 for (j = 0; j < ei->num_change_pages; j++)
1382 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1383 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1384 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1385 ei->change_page[j].actual_trigger_ce_value = 0;
1389 /* ---------- initialize trigger events ---------------------------------- */
1391 /* initialize trigger events information */
1392 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1393 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1394 trigger_events[i][j] = FALSE;
1396 /* add trigger events from element change event properties */
1397 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1399 struct ElementInfo *ei = &element_info[i];
1401 for (j = 0; j < ei->num_change_pages; j++)
1403 if (!ei->change_page[j].can_change_or_has_action)
1406 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1408 int trigger_element = ei->change_page[j].trigger_element;
1410 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1412 if (ei->change_page[j].has_event[k])
1414 if (IS_GROUP_ELEMENT(trigger_element))
1416 struct ElementGroupInfo *group =
1417 element_info[trigger_element].group;
1419 for (l = 0; l < group->num_elements_resolved; l++)
1420 trigger_events[group->element_resolved[l]][k] = TRUE;
1423 trigger_events[trigger_element][k] = TRUE;
1430 /* ---------- initialize push delay -------------------------------------- */
1432 /* initialize push delay values to default */
1433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1435 if (!IS_CUSTOM_ELEMENT(i))
1437 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1438 element_info[i].push_delay_random = game.default_push_delay_random;
1442 /* set push delay value for certain elements from pre-defined list */
1443 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1445 int e = push_delay_list[i].element;
1447 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1448 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1451 /* set push delay value for Supaplex elements for newer engine versions */
1452 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1454 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1456 if (IS_SP_ELEMENT(i))
1458 /* set SP push delay to just enough to push under a falling zonk */
1459 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1461 element_info[i].push_delay_fixed = delay;
1462 element_info[i].push_delay_random = 0;
1467 /* ---------- initialize move stepsize ----------------------------------- */
1469 /* initialize move stepsize values to default */
1470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1471 if (!IS_CUSTOM_ELEMENT(i))
1472 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1474 /* set move stepsize value for certain elements from pre-defined list */
1475 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1477 int e = move_stepsize_list[i].element;
1479 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1482 /* ---------- initialize collect score ----------------------------------- */
1484 /* initialize collect score values for custom elements from initial value */
1485 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1486 if (IS_CUSTOM_ELEMENT(i))
1487 element_info[i].collect_score = element_info[i].collect_score_initial;
1489 /* ---------- initialize collect count ----------------------------------- */
1491 /* initialize collect count values for non-custom elements */
1492 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1493 if (!IS_CUSTOM_ELEMENT(i))
1494 element_info[i].collect_count_initial = 0;
1496 /* add collect count values for all elements from pre-defined list */
1497 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1498 element_info[collect_count_list[i].element].collect_count_initial =
1499 collect_count_list[i].count;
1501 /* ---------- initialize access direction -------------------------------- */
1503 /* initialize access direction values to default (access from every side) */
1504 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1505 if (!IS_CUSTOM_ELEMENT(i))
1506 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1508 /* set access direction value for certain elements from pre-defined list */
1509 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1510 element_info[access_direction_list[i].element].access_direction =
1511 access_direction_list[i].direction;
1513 /* ---------- initialize explosion content ------------------------------- */
1514 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1516 if (IS_CUSTOM_ELEMENT(i))
1519 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1521 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1523 element_info[i].content.e[x][y] =
1524 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1525 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1526 i == EL_PLAYER_3 ? EL_EMERALD :
1527 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1528 i == EL_MOLE ? EL_EMERALD_RED :
1529 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1530 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1531 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1532 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1533 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1534 i == EL_WALL_EMERALD ? EL_EMERALD :
1535 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1536 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1537 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1538 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1539 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1540 i == EL_WALL_PEARL ? EL_PEARL :
1541 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1547 int get_num_special_action(int element, int action_first, int action_last)
1549 int num_special_action = 0;
1552 for (i = action_first; i <= action_last; i++)
1554 boolean found = FALSE;
1556 for (j = 0; j < NUM_DIRECTIONS; j++)
1557 if (el_act_dir2img(element, i, j) !=
1558 el_act_dir2img(element, ACTION_DEFAULT, j))
1562 num_special_action++;
1567 return num_special_action;
1571 =============================================================================
1573 -----------------------------------------------------------------------------
1574 initialize and start new game
1575 =============================================================================
1580 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1581 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1582 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1587 /* don't play tapes over network */
1588 network_playing = (options.network && !tape.playing);
1590 for (i = 0; i < MAX_PLAYERS; i++)
1592 struct PlayerInfo *player = &stored_player[i];
1594 player->index_nr = i;
1595 player->index_bit = (1 << i);
1596 player->element_nr = EL_PLAYER_1 + i;
1598 player->present = FALSE;
1599 player->active = FALSE;
1602 player->effective_action = 0;
1603 player->programmed_action = 0;
1606 player->gems_still_needed = level.gems_needed;
1607 player->sokobanfields_still_needed = 0;
1608 player->lights_still_needed = 0;
1609 player->friends_still_needed = 0;
1611 for (j = 0; j < MAX_NUM_KEYS; j++)
1612 player->key[j] = FALSE;
1614 player->dynabomb_count = 0;
1615 player->dynabomb_size = 1;
1616 player->dynabombs_left = 0;
1617 player->dynabomb_xl = FALSE;
1619 player->MovDir = MV_NONE;
1622 player->GfxDir = MV_NONE;
1623 player->GfxAction = ACTION_DEFAULT;
1625 player->StepFrame = 0;
1627 player->use_murphy = FALSE;
1628 player->artwork_element =
1629 (level.use_artwork_element[i] ? level.artwork_element[i] :
1630 player->element_nr);
1632 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1633 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1635 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1637 player->actual_frame_counter = 0;
1639 player->step_counter = 0;
1641 player->last_move_dir = MV_NONE;
1643 player->is_waiting = FALSE;
1644 player->is_moving = FALSE;
1645 player->is_auto_moving = FALSE;
1646 player->is_digging = FALSE;
1647 player->is_snapping = FALSE;
1648 player->is_collecting = FALSE;
1649 player->is_pushing = FALSE;
1650 player->is_switching = FALSE;
1651 player->is_dropping = FALSE;
1653 player->is_bored = FALSE;
1654 player->is_sleeping = FALSE;
1656 player->cannot_move = FALSE;
1658 player->frame_counter_bored = -1;
1659 player->frame_counter_sleeping = -1;
1661 player->anim_delay_counter = 0;
1662 player->post_delay_counter = 0;
1664 player->action_waiting = ACTION_DEFAULT;
1665 player->last_action_waiting = ACTION_DEFAULT;
1666 player->special_action_bored = ACTION_DEFAULT;
1667 player->special_action_sleeping = ACTION_DEFAULT;
1669 /* set number of special actions for bored and sleeping animation */
1670 player->num_special_action_bored =
1671 get_num_special_action(player->artwork_element,
1672 ACTION_BORING_1, ACTION_BORING_LAST);
1673 player->num_special_action_sleeping =
1674 get_num_special_action(player->artwork_element,
1675 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1677 player->switch_x = -1;
1678 player->switch_y = -1;
1680 player->drop_x = -1;
1681 player->drop_y = -1;
1683 player->show_envelope = 0;
1685 player->move_delay = game.initial_move_delay;
1686 player->move_delay_value = game.initial_move_delay_value;
1688 player->move_delay_value_next = -1;
1690 player->move_delay_reset_counter = 0;
1692 player->push_delay = -1; /* initialized when pushing starts */
1693 player->push_delay_value = game.initial_push_delay_value;
1695 player->drop_delay = 0;
1697 player->last_jx = player->last_jy = 0;
1698 player->jx = player->jy = 0;
1700 player->shield_normal_time_left = 0;
1701 player->shield_deadly_time_left = 0;
1703 player->inventory_infinite_element = EL_UNDEFINED;
1704 player->inventory_size = 0;
1706 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1707 SnapField(player, 0, 0);
1709 player->LevelSolved = FALSE;
1710 player->GameOver = FALSE;
1713 network_player_action_received = FALSE;
1715 #if defined(NETWORK_AVALIABLE)
1716 /* initial null action */
1717 if (network_playing)
1718 SendToServer_MovePlayer(MV_NONE);
1727 TimeLeft = level.time;
1730 ScreenMovDir = MV_NONE;
1734 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1736 AllPlayersGone = FALSE;
1738 game.yamyam_content_nr = 0;
1739 game.magic_wall_active = FALSE;
1740 game.magic_wall_time_left = 0;
1741 game.light_time_left = 0;
1742 game.timegate_time_left = 0;
1743 game.switchgate_pos = 0;
1744 game.wind_direction = level.wind_direction_initial;
1745 game.gravity = level.initial_gravity;
1746 game.explosions_delayed = TRUE;
1748 game.lenses_time_left = 0;
1749 game.magnify_time_left = 0;
1751 game.envelope_active = FALSE;
1753 for (i = 0; i < NUM_BELTS; i++)
1755 game.belt_dir[i] = MV_NONE;
1756 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1759 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1760 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1762 for (x = 0; x < lev_fieldx; x++)
1764 for (y = 0; y < lev_fieldy; y++)
1766 Feld[x][y] = level.field[x][y];
1767 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1768 ChangeDelay[x][y] = 0;
1769 ChangePage[x][y] = -1;
1770 #if USE_NEW_CUSTOM_VALUE
1771 CustomValue[x][y] = 0; /* initialized in InitField() */
1773 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1775 WasJustMoving[x][y] = 0;
1776 WasJustFalling[x][y] = 0;
1777 CheckCollision[x][y] = 0;
1779 Pushed[x][y] = FALSE;
1781 ChangeCount[x][y] = 0;
1782 ChangeEvent[x][y] = -1;
1784 ExplodePhase[x][y] = 0;
1785 ExplodeDelay[x][y] = 0;
1786 ExplodeField[x][y] = EX_TYPE_NONE;
1788 RunnerVisit[x][y] = 0;
1789 PlayerVisit[x][y] = 0;
1792 GfxRandom[x][y] = INIT_GFX_RANDOM();
1793 GfxElement[x][y] = EL_UNDEFINED;
1794 GfxAction[x][y] = ACTION_DEFAULT;
1795 GfxDir[x][y] = MV_NONE;
1799 for (y = 0; y < lev_fieldy; y++)
1801 for (x = 0; x < lev_fieldx; x++)
1803 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1805 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1807 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1810 InitField(x, y, TRUE);
1816 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1817 emulate_sb ? EMU_SOKOBAN :
1818 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1820 #if USE_NEW_ALL_SLIPPERY
1821 /* initialize type of slippery elements */
1822 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1824 if (!IS_CUSTOM_ELEMENT(i))
1826 /* default: elements slip down either to the left or right randomly */
1827 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1829 /* SP style elements prefer to slip down on the left side */
1830 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1831 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1833 /* BD style elements prefer to slip down on the left side */
1834 if (game.emulation == EMU_BOULDERDASH)
1835 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1840 /* initialize explosion and ignition delay */
1841 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1843 if (!IS_CUSTOM_ELEMENT(i))
1846 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1847 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1848 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1849 int last_phase = (num_phase + 1) * delay;
1850 int half_phase = (num_phase / 2) * delay;
1852 element_info[i].explosion_delay = last_phase - 1;
1853 element_info[i].ignition_delay = half_phase;
1855 if (i == EL_BLACK_ORB)
1856 element_info[i].ignition_delay = 1;
1860 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1861 element_info[i].explosion_delay = 1;
1863 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1864 element_info[i].ignition_delay = 1;
1868 /* correct non-moving belts to start moving left */
1869 for (i = 0; i < NUM_BELTS; i++)
1870 if (game.belt_dir[i] == MV_NONE)
1871 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1873 /* check if any connected player was not found in playfield */
1874 for (i = 0; i < MAX_PLAYERS; i++)
1876 struct PlayerInfo *player = &stored_player[i];
1878 if (player->connected && !player->present)
1880 for (j = 0; j < MAX_PLAYERS; j++)
1882 struct PlayerInfo *some_player = &stored_player[j];
1883 int jx = some_player->jx, jy = some_player->jy;
1885 /* assign first free player found that is present in the playfield */
1886 if (some_player->present && !some_player->connected)
1888 player->present = TRUE;
1889 player->active = TRUE;
1891 some_player->present = FALSE;
1892 some_player->active = FALSE;
1895 player->element_nr = some_player->element_nr;
1898 player->artwork_element = some_player->artwork_element;
1900 player->block_last_field = some_player->block_last_field;
1901 player->block_delay_adjustment = some_player->block_delay_adjustment;
1903 StorePlayer[jx][jy] = player->element_nr;
1904 player->jx = player->last_jx = jx;
1905 player->jy = player->last_jy = jy;
1915 /* when playing a tape, eliminate all players which do not participate */
1917 for (i = 0; i < MAX_PLAYERS; i++)
1919 if (stored_player[i].active && !tape.player_participates[i])
1921 struct PlayerInfo *player = &stored_player[i];
1922 int jx = player->jx, jy = player->jy;
1924 player->active = FALSE;
1925 StorePlayer[jx][jy] = 0;
1926 Feld[jx][jy] = EL_EMPTY;
1930 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1932 /* when in single player mode, eliminate all but the first active player */
1934 for (i = 0; i < MAX_PLAYERS; i++)
1936 if (stored_player[i].active)
1938 for (j = i + 1; j < MAX_PLAYERS; j++)
1940 if (stored_player[j].active)
1942 struct PlayerInfo *player = &stored_player[j];
1943 int jx = player->jx, jy = player->jy;
1945 player->active = FALSE;
1946 player->present = FALSE;
1948 StorePlayer[jx][jy] = 0;
1949 Feld[jx][jy] = EL_EMPTY;
1956 /* when recording the game, store which players take part in the game */
1959 for (i = 0; i < MAX_PLAYERS; i++)
1960 if (stored_player[i].active)
1961 tape.player_participates[i] = TRUE;
1966 for (i = 0; i < MAX_PLAYERS; i++)
1968 struct PlayerInfo *player = &stored_player[i];
1970 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1975 if (local_player == player)
1976 printf("Player %d is local player.\n", i+1);
1980 if (BorderElement == EL_EMPTY)
1983 SBX_Right = lev_fieldx - SCR_FIELDX;
1985 SBY_Lower = lev_fieldy - SCR_FIELDY;
1990 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1992 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1995 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1996 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1998 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1999 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2001 /* if local player not found, look for custom element that might create
2002 the player (make some assumptions about the right custom element) */
2003 if (!local_player->present)
2005 int start_x = 0, start_y = 0;
2006 int found_rating = 0;
2007 int found_element = EL_UNDEFINED;
2008 int player_nr = local_player->index_nr;
2010 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2012 int element = Feld[x][y];
2017 if (level.use_start_element[player_nr] &&
2018 level.start_element[player_nr] == element &&
2025 found_element = element;
2028 if (!IS_CUSTOM_ELEMENT(element))
2031 if (CAN_CHANGE(element))
2033 for (i = 0; i < element_info[element].num_change_pages; i++)
2035 /* check for player created from custom element as single target */
2036 content = element_info[element].change_page[i].target_element;
2037 is_player = ELEM_IS_PLAYER(content);
2039 if (is_player && (found_rating < 3 || element < found_element))
2045 found_element = element;
2050 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2052 /* check for player created from custom element as explosion content */
2053 content = element_info[element].content.e[xx][yy];
2054 is_player = ELEM_IS_PLAYER(content);
2056 if (is_player && (found_rating < 2 || element < found_element))
2058 start_x = x + xx - 1;
2059 start_y = y + yy - 1;
2062 found_element = element;
2065 if (!CAN_CHANGE(element))
2068 for (i = 0; i < element_info[element].num_change_pages; i++)
2070 /* check for player created from custom element as extended target */
2072 element_info[element].change_page[i].target_content.e[xx][yy];
2074 is_player = ELEM_IS_PLAYER(content);
2076 if (is_player && (found_rating < 1 || element < found_element))
2078 start_x = x + xx - 1;
2079 start_y = y + yy - 1;
2082 found_element = element;
2088 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2089 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2092 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2093 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2098 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2099 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2100 local_player->jx - MIDPOSX);
2102 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2103 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2104 local_player->jy - MIDPOSY);
2107 if (!game.restart_level)
2108 CloseDoor(DOOR_CLOSE_1);
2110 /* !!! FIX THIS (START) !!! */
2111 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2113 InitGameEngine_EM();
2120 /* after drawing the level, correct some elements */
2121 if (game.timegate_time_left == 0)
2122 CloseAllOpenTimegates();
2124 if (setup.soft_scrolling)
2125 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2127 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2130 /* !!! FIX THIS (END) !!! */
2132 if (!game.restart_level)
2134 /* copy default game door content to main double buffer */
2135 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2136 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2139 DrawGameDoorValues();
2141 if (!game.restart_level)
2145 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2146 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2147 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2151 /* copy actual game door content to door double buffer for OpenDoor() */
2152 BlitBitmap(drawto, bitmap_db_door,
2153 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2155 OpenDoor(DOOR_OPEN_ALL);
2157 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2159 if (setup.sound_music)
2162 KeyboardAutoRepeatOffUnlessAutoplay();
2166 for (i = 0; i < MAX_PLAYERS; i++)
2167 printf("Player %d %sactive.\n",
2168 i + 1, (stored_player[i].active ? "" : "not "));
2172 game.restart_level = FALSE;
2175 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2177 /* this is used for non-R'n'D game engines to update certain engine values */
2179 /* needed to determine if sounds are played within the visible screen area */
2180 scroll_x = actual_scroll_x;
2181 scroll_y = actual_scroll_y;
2184 void InitMovDir(int x, int y)
2186 int i, element = Feld[x][y];
2187 static int xy[4][2] =
2194 static int direction[3][4] =
2196 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2197 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2198 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2207 Feld[x][y] = EL_BUG;
2208 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2211 case EL_SPACESHIP_RIGHT:
2212 case EL_SPACESHIP_UP:
2213 case EL_SPACESHIP_LEFT:
2214 case EL_SPACESHIP_DOWN:
2215 Feld[x][y] = EL_SPACESHIP;
2216 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2219 case EL_BD_BUTTERFLY_RIGHT:
2220 case EL_BD_BUTTERFLY_UP:
2221 case EL_BD_BUTTERFLY_LEFT:
2222 case EL_BD_BUTTERFLY_DOWN:
2223 Feld[x][y] = EL_BD_BUTTERFLY;
2224 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2227 case EL_BD_FIREFLY_RIGHT:
2228 case EL_BD_FIREFLY_UP:
2229 case EL_BD_FIREFLY_LEFT:
2230 case EL_BD_FIREFLY_DOWN:
2231 Feld[x][y] = EL_BD_FIREFLY;
2232 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2235 case EL_PACMAN_RIGHT:
2237 case EL_PACMAN_LEFT:
2238 case EL_PACMAN_DOWN:
2239 Feld[x][y] = EL_PACMAN;
2240 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2243 case EL_SP_SNIKSNAK:
2244 MovDir[x][y] = MV_UP;
2247 case EL_SP_ELECTRON:
2248 MovDir[x][y] = MV_LEFT;
2255 Feld[x][y] = EL_MOLE;
2256 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2260 if (IS_CUSTOM_ELEMENT(element))
2262 struct ElementInfo *ei = &element_info[element];
2263 int move_direction_initial = ei->move_direction_initial;
2264 int move_pattern = ei->move_pattern;
2266 if (move_direction_initial == MV_START_PREVIOUS)
2268 if (MovDir[x][y] != MV_NONE)
2271 move_direction_initial = MV_START_AUTOMATIC;
2274 if (move_direction_initial == MV_START_RANDOM)
2275 MovDir[x][y] = 1 << RND(4);
2276 else if (move_direction_initial & MV_ANY_DIRECTION)
2277 MovDir[x][y] = move_direction_initial;
2278 else if (move_pattern == MV_ALL_DIRECTIONS ||
2279 move_pattern == MV_TURNING_LEFT ||
2280 move_pattern == MV_TURNING_RIGHT ||
2281 move_pattern == MV_TURNING_LEFT_RIGHT ||
2282 move_pattern == MV_TURNING_RIGHT_LEFT ||
2283 move_pattern == MV_TURNING_RANDOM)
2284 MovDir[x][y] = 1 << RND(4);
2285 else if (move_pattern == MV_HORIZONTAL)
2286 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2287 else if (move_pattern == MV_VERTICAL)
2288 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2289 else if (move_pattern & MV_ANY_DIRECTION)
2290 MovDir[x][y] = element_info[element].move_pattern;
2291 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2292 move_pattern == MV_ALONG_RIGHT_SIDE)
2294 /* use random direction as default start direction */
2295 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2296 MovDir[x][y] = 1 << RND(4);
2298 for (i = 0; i < NUM_DIRECTIONS; i++)
2300 int x1 = x + xy[i][0];
2301 int y1 = y + xy[i][1];
2303 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2305 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2306 MovDir[x][y] = direction[0][i];
2308 MovDir[x][y] = direction[1][i];
2317 MovDir[x][y] = 1 << RND(4);
2319 if (element != EL_BUG &&
2320 element != EL_SPACESHIP &&
2321 element != EL_BD_BUTTERFLY &&
2322 element != EL_BD_FIREFLY)
2325 for (i = 0; i < NUM_DIRECTIONS; i++)
2327 int x1 = x + xy[i][0];
2328 int y1 = y + xy[i][1];
2330 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2332 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2334 MovDir[x][y] = direction[0][i];
2337 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2338 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2340 MovDir[x][y] = direction[1][i];
2349 GfxDir[x][y] = MovDir[x][y];
2352 void InitAmoebaNr(int x, int y)
2355 int group_nr = AmoebeNachbarNr(x, y);
2359 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2361 if (AmoebaCnt[i] == 0)
2369 AmoebaNr[x][y] = group_nr;
2370 AmoebaCnt[group_nr]++;
2371 AmoebaCnt2[group_nr]++;
2377 boolean raise_level = FALSE;
2379 if (local_player->MovPos)
2382 if (tape.auto_play) /* tape might already be stopped here */
2383 tape.auto_play_level_solved = TRUE;
2385 local_player->LevelSolved = FALSE;
2387 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2391 if (!tape.playing && setup.sound_loops)
2392 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2393 SND_CTRL_PLAY_LOOP);
2395 while (TimeLeft > 0)
2397 if (!tape.playing && !setup.sound_loops)
2398 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2400 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2403 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2408 RaiseScore(level.score[SC_TIME_BONUS]);
2411 DrawGameValue_Time(TimeLeft);
2419 if (!tape.playing && setup.sound_loops)
2420 StopSound(SND_GAME_LEVELTIME_BONUS);
2422 else if (level.time == 0) /* level without time limit */
2424 if (!tape.playing && setup.sound_loops)
2425 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2426 SND_CTRL_PLAY_LOOP);
2428 while (TimePlayed < 999)
2430 if (!tape.playing && !setup.sound_loops)
2431 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2433 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2436 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2441 RaiseScore(level.score[SC_TIME_BONUS]);
2444 DrawGameValue_Time(TimePlayed);
2452 if (!tape.playing && setup.sound_loops)
2453 StopSound(SND_GAME_LEVELTIME_BONUS);
2456 /* close exit door after last player */
2457 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2458 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2459 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2461 int element = Feld[ExitX][ExitY];
2463 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2464 EL_SP_EXIT_CLOSING);
2466 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2469 /* player disappears */
2470 if (ExitX >= 0 && ExitY >= 0)
2471 DrawLevelField(ExitX, ExitY);
2478 CloseDoor(DOOR_CLOSE_1);
2483 SaveTape(tape.level_nr); /* Ask to save tape */
2486 if (level_nr == leveldir_current->handicap_level)
2488 leveldir_current->handicap_level++;
2489 SaveLevelSetup_SeriesInfo();
2492 if (level_editor_test_game)
2493 local_player->score = -1; /* no highscore when playing from editor */
2494 else if (level_nr < leveldir_current->last_level)
2495 raise_level = TRUE; /* advance to next level */
2497 if ((hi_pos = NewHiScore()) >= 0)
2499 game_status = GAME_MODE_SCORES;
2500 DrawHallOfFame(hi_pos);
2509 game_status = GAME_MODE_MAIN;
2526 LoadScore(level_nr);
2528 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2529 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2532 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2534 if (local_player->score > highscore[k].Score)
2536 /* player has made it to the hall of fame */
2538 if (k < MAX_SCORE_ENTRIES - 1)
2540 int m = MAX_SCORE_ENTRIES - 1;
2543 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2544 if (!strcmp(setup.player_name, highscore[l].Name))
2546 if (m == k) /* player's new highscore overwrites his old one */
2550 for (l = m; l > k; l--)
2552 strcpy(highscore[l].Name, highscore[l - 1].Name);
2553 highscore[l].Score = highscore[l - 1].Score;
2560 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2561 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2562 highscore[k].Score = local_player->score;
2568 else if (!strncmp(setup.player_name, highscore[k].Name,
2569 MAX_PLAYER_NAME_LEN))
2570 break; /* player already there with a higher score */
2576 SaveScore(level_nr);
2581 inline static int getElementMoveStepsize(int x, int y)
2583 int element = Feld[x][y];
2584 int direction = MovDir[x][y];
2585 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2586 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2587 int horiz_move = (dx != 0);
2588 int sign = (horiz_move ? dx : dy);
2589 int step = sign * element_info[element].move_stepsize;
2591 /* special values for move stepsize for spring and things on conveyor belt */
2595 if (element == EL_SPRING)
2596 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2597 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2598 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2599 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2601 if (CAN_FALL(element) &&
2602 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2603 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2604 else if (element == EL_SPRING)
2605 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2612 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2614 if (player->GfxAction != action || player->GfxDir != dir)
2617 printf("Player frame reset! (%d => %d, %d => %d)\n",
2618 player->GfxAction, action, player->GfxDir, dir);
2621 player->GfxAction = action;
2622 player->GfxDir = dir;
2624 player->StepFrame = 0;
2628 static void ResetRandomAnimationValue(int x, int y)
2630 GfxRandom[x][y] = INIT_GFX_RANDOM();
2633 static void ResetGfxAnimation(int x, int y)
2636 GfxAction[x][y] = ACTION_DEFAULT;
2637 GfxDir[x][y] = MovDir[x][y];
2640 void InitMovingField(int x, int y, int direction)
2642 int element = Feld[x][y];
2643 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2644 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2648 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2649 ResetGfxAnimation(x, y);
2651 MovDir[x][y] = direction;
2652 GfxDir[x][y] = direction;
2653 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2654 ACTION_FALLING : ACTION_MOVING);
2656 /* this is needed for CEs with property "can move" / "not moving" */
2658 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2660 if (Feld[newx][newy] == EL_EMPTY)
2661 Feld[newx][newy] = EL_BLOCKED;
2663 MovDir[newx][newy] = MovDir[x][y];
2665 #if USE_NEW_CUSTOM_VALUE
2666 CustomValue[newx][newy] = CustomValue[x][y];
2669 GfxFrame[newx][newy] = GfxFrame[x][y];
2670 GfxRandom[newx][newy] = GfxRandom[x][y];
2671 GfxAction[newx][newy] = GfxAction[x][y];
2672 GfxDir[newx][newy] = GfxDir[x][y];
2676 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2678 int direction = MovDir[x][y];
2679 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2680 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2686 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2688 int oldx = x, oldy = y;
2689 int direction = MovDir[x][y];
2691 if (direction == MV_LEFT)
2693 else if (direction == MV_RIGHT)
2695 else if (direction == MV_UP)
2697 else if (direction == MV_DOWN)
2700 *comes_from_x = oldx;
2701 *comes_from_y = oldy;
2704 int MovingOrBlocked2Element(int x, int y)
2706 int element = Feld[x][y];
2708 if (element == EL_BLOCKED)
2712 Blocked2Moving(x, y, &oldx, &oldy);
2713 return Feld[oldx][oldy];
2719 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2721 /* like MovingOrBlocked2Element(), but if element is moving
2722 and (x,y) is the field the moving element is just leaving,
2723 return EL_BLOCKED instead of the element value */
2724 int element = Feld[x][y];
2726 if (IS_MOVING(x, y))
2728 if (element == EL_BLOCKED)
2732 Blocked2Moving(x, y, &oldx, &oldy);
2733 return Feld[oldx][oldy];
2742 static void RemoveField(int x, int y)
2744 Feld[x][y] = EL_EMPTY;
2750 #if USE_NEW_CUSTOM_VALUE
2751 CustomValue[x][y] = 0;
2755 ChangeDelay[x][y] = 0;
2756 ChangePage[x][y] = -1;
2757 Pushed[x][y] = FALSE;
2760 ExplodeField[x][y] = EX_TYPE_NONE;
2763 GfxElement[x][y] = EL_UNDEFINED;
2764 GfxAction[x][y] = ACTION_DEFAULT;
2765 GfxDir[x][y] = MV_NONE;
2768 void RemoveMovingField(int x, int y)
2770 int oldx = x, oldy = y, newx = x, newy = y;
2771 int element = Feld[x][y];
2772 int next_element = EL_UNDEFINED;
2774 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2777 if (IS_MOVING(x, y))
2779 Moving2Blocked(x, y, &newx, &newy);
2781 if (Feld[newx][newy] != EL_BLOCKED)
2783 /* element is moving, but target field is not free (blocked), but
2784 already occupied by something different (example: acid pool);
2785 in this case, only remove the moving field, but not the target */
2787 RemoveField(oldx, oldy);
2789 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2791 DrawLevelField(oldx, oldy);
2796 else if (element == EL_BLOCKED)
2798 Blocked2Moving(x, y, &oldx, &oldy);
2799 if (!IS_MOVING(oldx, oldy))
2803 if (element == EL_BLOCKED &&
2804 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2805 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2806 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2807 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2808 next_element = get_next_element(Feld[oldx][oldy]);
2810 RemoveField(oldx, oldy);
2811 RemoveField(newx, newy);
2813 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2815 if (next_element != EL_UNDEFINED)
2816 Feld[oldx][oldy] = next_element;
2818 DrawLevelField(oldx, oldy);
2819 DrawLevelField(newx, newy);
2822 void DrawDynamite(int x, int y)
2824 int sx = SCREENX(x), sy = SCREENY(y);
2825 int graphic = el2img(Feld[x][y]);
2828 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2831 if (IS_WALKABLE_INSIDE(Back[x][y]))
2835 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2836 else if (Store[x][y])
2837 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2839 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2841 if (Back[x][y] || Store[x][y])
2842 DrawGraphicThruMask(sx, sy, graphic, frame);
2844 DrawGraphic(sx, sy, graphic, frame);
2847 void CheckDynamite(int x, int y)
2849 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2853 if (MovDelay[x][y] != 0)
2856 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2862 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2867 void DrawRelocatePlayer(struct PlayerInfo *player)
2869 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2870 boolean no_delay = (tape.warp_forward);
2871 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2872 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2873 int jx = player->jx;
2874 int jy = player->jy;
2876 if (level.instant_relocation)
2878 int offset = (setup.scroll_delay ? 3 : 0);
2880 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2882 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2883 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2884 local_player->jx - MIDPOSX);
2886 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2887 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2888 local_player->jy - MIDPOSY);
2892 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2893 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2894 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2896 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2897 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2898 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2900 /* don't scroll over playfield boundaries */
2901 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2902 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2904 /* don't scroll over playfield boundaries */
2905 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2906 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2909 RedrawPlayfield(TRUE, 0,0,0,0);
2913 int scroll_xx = -999, scroll_yy = -999;
2915 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2917 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2920 int fx = FX, fy = FY;
2922 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2923 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2924 local_player->jx - MIDPOSX);
2926 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2927 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2928 local_player->jy - MIDPOSY);
2930 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2931 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2933 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2939 fx += dx * TILEX / 2;
2940 fy += dy * TILEY / 2;
2942 ScrollLevel(dx, dy);
2945 /* scroll in two steps of half tile size to make things smoother */
2946 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2948 Delay(wait_delay_value);
2950 /* scroll second step to align at full tile size */
2952 Delay(wait_delay_value);
2957 Delay(wait_delay_value);
2961 void RelocatePlayer(int jx, int jy, int el_player_raw)
2963 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
2964 int player_nr = GET_PLAYER_NR(el_player);
2965 struct PlayerInfo *player = &stored_player[player_nr];
2966 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2967 boolean no_delay = (tape.warp_forward);
2968 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2969 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2970 int old_jx = player->jx;
2971 int old_jy = player->jy;
2972 int old_element = Feld[old_jx][old_jy];
2973 int element = Feld[jx][jy];
2974 boolean player_relocated = (old_jx != jx || old_jy != jy);
2976 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2977 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2978 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2979 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2980 int leave_side_horiz = move_dir_horiz;
2981 int leave_side_vert = move_dir_vert;
2982 int enter_side = enter_side_horiz | enter_side_vert;
2983 int leave_side = leave_side_horiz | leave_side_vert;
2985 if (player->GameOver) /* do not reanimate dead player */
2988 if (!player_relocated) /* no need to relocate the player */
2991 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2993 RemoveField(jx, jy); /* temporarily remove newly placed player */
2994 DrawLevelField(jx, jy);
2997 if (player->present)
2999 while (player->MovPos)
3001 ScrollPlayer(player, SCROLL_GO_ON);
3002 ScrollScreen(NULL, SCROLL_GO_ON);
3004 AdvanceFrameAndPlayerCounters(player->index_nr);
3009 Delay(wait_delay_value);
3012 DrawPlayer(player); /* needed here only to cleanup last field */
3013 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3015 player->is_moving = FALSE;
3018 if (IS_CUSTOM_ELEMENT(old_element))
3019 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3021 player->index_bit, leave_side);
3023 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3025 player->index_bit, leave_side);
3027 Feld[jx][jy] = el_player;
3028 InitPlayerField(jx, jy, el_player, TRUE);
3030 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3032 Feld[jx][jy] = element;
3033 InitField(jx, jy, FALSE);
3036 if (player == local_player) /* only visually relocate local player */
3037 DrawRelocatePlayer(player);
3039 TestIfPlayerTouchesBadThing(jx, jy);
3040 TestIfPlayerTouchesCustomElement(jx, jy);
3042 if (IS_CUSTOM_ELEMENT(element))
3043 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3044 player->index_bit, enter_side);
3046 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3047 player->index_bit, enter_side);
3050 void Explode(int ex, int ey, int phase, int mode)
3056 /* !!! eliminate this variable !!! */
3057 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3059 if (game.explosions_delayed)
3061 ExplodeField[ex][ey] = mode;
3065 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3067 int center_element = Feld[ex][ey];
3068 int artwork_element, explosion_element; /* set these values later */
3071 /* --- This is only really needed (and now handled) in "Impact()". --- */
3072 /* do not explode moving elements that left the explode field in time */
3073 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3074 center_element == EL_EMPTY &&
3075 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3080 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3081 if (mode == EX_TYPE_NORMAL ||
3082 mode == EX_TYPE_CENTER ||
3083 mode == EX_TYPE_CROSS)
3084 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3087 /* remove things displayed in background while burning dynamite */
3088 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3091 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3093 /* put moving element to center field (and let it explode there) */
3094 center_element = MovingOrBlocked2Element(ex, ey);
3095 RemoveMovingField(ex, ey);
3096 Feld[ex][ey] = center_element;
3099 /* now "center_element" is finally determined -- set related values now */
3100 artwork_element = center_element; /* for custom player artwork */
3101 explosion_element = center_element; /* for custom player artwork */
3103 if (IS_PLAYER(ex, ey))
3105 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3107 artwork_element = stored_player[player_nr].artwork_element;
3109 if (level.use_explosion_element[player_nr])
3111 explosion_element = level.explosion_element[player_nr];
3112 artwork_element = explosion_element;
3117 if (mode == EX_TYPE_NORMAL ||
3118 mode == EX_TYPE_CENTER ||
3119 mode == EX_TYPE_CROSS)
3120 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3124 last_phase = element_info[explosion_element].explosion_delay + 1;
3126 last_phase = element_info[center_element].explosion_delay + 1;
3129 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3131 int xx = x - ex + 1;
3132 int yy = y - ey + 1;
3135 if (!IN_LEV_FIELD(x, y) ||
3136 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3137 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3140 element = Feld[x][y];
3142 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3144 element = MovingOrBlocked2Element(x, y);
3146 if (!IS_EXPLOSION_PROOF(element))
3147 RemoveMovingField(x, y);
3150 /* indestructible elements can only explode in center (but not flames) */
3151 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3152 mode == EX_TYPE_BORDER)) ||
3153 element == EL_FLAMES)
3156 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3157 behaviour, for example when touching a yamyam that explodes to rocks
3158 with active deadly shield, a rock is created under the player !!! */
3159 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3161 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3162 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3163 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3165 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3168 if (IS_ACTIVE_BOMB(element))
3170 /* re-activate things under the bomb like gate or penguin */
3171 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3178 /* save walkable background elements while explosion on same tile */
3179 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3180 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3181 Back[x][y] = element;
3183 /* ignite explodable elements reached by other explosion */
3184 if (element == EL_EXPLOSION)
3185 element = Store2[x][y];
3187 if (AmoebaNr[x][y] &&
3188 (element == EL_AMOEBA_FULL ||
3189 element == EL_BD_AMOEBA ||
3190 element == EL_AMOEBA_GROWING))
3192 AmoebaCnt[AmoebaNr[x][y]]--;
3193 AmoebaCnt2[AmoebaNr[x][y]]--;
3198 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3201 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3203 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3205 switch(StorePlayer[ex][ey])
3208 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3211 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3214 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3218 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3223 if (PLAYERINFO(ex, ey)->use_murphy)
3224 Store[x][y] = EL_EMPTY;
3227 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3228 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3229 else if (ELEM_IS_PLAYER(center_element))
3230 Store[x][y] = EL_EMPTY;
3231 else if (center_element == EL_YAMYAM)
3232 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3233 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3234 Store[x][y] = element_info[center_element].content.e[xx][yy];
3236 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3237 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3238 otherwise) -- FIX THIS !!! */
3239 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3240 Store[x][y] = element_info[element].content.e[1][1];
3242 else if (!CAN_EXPLODE(element))
3243 Store[x][y] = element_info[element].content.e[1][1];
3246 Store[x][y] = EL_EMPTY;
3248 else if (center_element == EL_MOLE)
3249 Store[x][y] = EL_EMERALD_RED;
3250 else if (center_element == EL_PENGUIN)
3251 Store[x][y] = EL_EMERALD_PURPLE;
3252 else if (center_element == EL_BUG)
3253 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3254 else if (center_element == EL_BD_BUTTERFLY)
3255 Store[x][y] = EL_BD_DIAMOND;
3256 else if (center_element == EL_SP_ELECTRON)
3257 Store[x][y] = EL_SP_INFOTRON;
3258 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3259 Store[x][y] = level.amoeba_content;
3260 else if (center_element == EL_YAMYAM)
3261 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3262 else if (IS_CUSTOM_ELEMENT(center_element) &&
3263 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3264 Store[x][y] = element_info[center_element].content.e[xx][yy];
3265 else if (element == EL_WALL_EMERALD)
3266 Store[x][y] = EL_EMERALD;
3267 else if (element == EL_WALL_DIAMOND)
3268 Store[x][y] = EL_DIAMOND;
3269 else if (element == EL_WALL_BD_DIAMOND)
3270 Store[x][y] = EL_BD_DIAMOND;
3271 else if (element == EL_WALL_EMERALD_YELLOW)
3272 Store[x][y] = EL_EMERALD_YELLOW;
3273 else if (element == EL_WALL_EMERALD_RED)
3274 Store[x][y] = EL_EMERALD_RED;
3275 else if (element == EL_WALL_EMERALD_PURPLE)
3276 Store[x][y] = EL_EMERALD_PURPLE;
3277 else if (element == EL_WALL_PEARL)
3278 Store[x][y] = EL_PEARL;
3279 else if (element == EL_WALL_CRYSTAL)
3280 Store[x][y] = EL_CRYSTAL;
3281 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3282 Store[x][y] = element_info[element].content.e[1][1];
3284 Store[x][y] = EL_EMPTY;
3287 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3288 center_element == EL_AMOEBA_TO_DIAMOND)
3289 Store2[x][y] = element;
3291 Feld[x][y] = EL_EXPLOSION;
3292 GfxElement[x][y] = artwork_element;
3294 ExplodePhase[x][y] = 1;
3295 ExplodeDelay[x][y] = last_phase;
3300 if (center_element == EL_YAMYAM)
3301 game.yamyam_content_nr =
3302 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3314 GfxFrame[x][y] = 0; /* restart explosion animation */
3316 last_phase = ExplodeDelay[x][y];
3318 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3322 /* activate this even in non-DEBUG version until cause for crash in
3323 getGraphicAnimationFrame() (see below) is found and eliminated */
3328 if (GfxElement[x][y] == EL_UNDEFINED)
3331 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3332 printf("Explode(): This should never happen!\n");
3335 GfxElement[x][y] = EL_EMPTY;
3339 border_element = Store2[x][y];
3340 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3341 border_element = StorePlayer[x][y];
3343 if (phase == element_info[border_element].ignition_delay ||
3344 phase == last_phase)
3346 boolean border_explosion = FALSE;
3348 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3349 !PLAYER_EXPLOSION_PROTECTED(x, y))
3351 KillPlayerUnlessExplosionProtected(x, y);
3352 border_explosion = TRUE;
3354 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3356 Feld[x][y] = Store2[x][y];
3359 border_explosion = TRUE;
3361 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3363 AmoebeUmwandeln(x, y);
3365 border_explosion = TRUE;
3368 /* if an element just explodes due to another explosion (chain-reaction),
3369 do not immediately end the new explosion when it was the last frame of
3370 the explosion (as it would be done in the following "if"-statement!) */
3371 if (border_explosion && phase == last_phase)
3375 if (phase == last_phase)
3379 element = Feld[x][y] = Store[x][y];
3380 Store[x][y] = Store2[x][y] = 0;
3381 GfxElement[x][y] = EL_UNDEFINED;
3383 /* player can escape from explosions and might therefore be still alive */
3384 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3385 element <= EL_PLAYER_IS_EXPLODING_4)
3387 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3388 int explosion_element = EL_PLAYER_1 + player_nr;
3389 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3390 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3392 if (level.use_explosion_element[player_nr])
3393 explosion_element = level.explosion_element[player_nr];
3395 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3396 element_info[explosion_element].content.e[xx][yy]);
3399 /* restore probably existing indestructible background element */
3400 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3401 element = Feld[x][y] = Back[x][y];
3404 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3405 GfxDir[x][y] = MV_NONE;
3406 ChangeDelay[x][y] = 0;
3407 ChangePage[x][y] = -1;
3409 #if USE_NEW_CUSTOM_VALUE
3410 CustomValue[x][y] = 0;
3413 InitField_WithBug2(x, y, FALSE);
3415 DrawLevelField(x, y);
3417 TestIfElementTouchesCustomElement(x, y);
3419 if (GFX_CRUMBLED(element))
3420 DrawLevelFieldCrumbledSandNeighbours(x, y);
3422 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3423 StorePlayer[x][y] = 0;
3425 if (ELEM_IS_PLAYER(element))
3426 RelocatePlayer(x, y, element);
3428 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3430 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3431 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3434 DrawLevelFieldCrumbledSand(x, y);
3436 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3438 DrawLevelElement(x, y, Back[x][y]);
3439 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3441 else if (IS_WALKABLE_UNDER(Back[x][y]))
3443 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3444 DrawLevelElementThruMask(x, y, Back[x][y]);
3446 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3447 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3451 void DynaExplode(int ex, int ey)
3454 int dynabomb_element = Feld[ex][ey];
3455 int dynabomb_size = 1;
3456 boolean dynabomb_xl = FALSE;
3457 struct PlayerInfo *player;
3458 static int xy[4][2] =
3466 if (IS_ACTIVE_BOMB(dynabomb_element))
3468 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3469 dynabomb_size = player->dynabomb_size;
3470 dynabomb_xl = player->dynabomb_xl;
3471 player->dynabombs_left++;
3474 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3476 for (i = 0; i < NUM_DIRECTIONS; i++)
3478 for (j = 1; j <= dynabomb_size; j++)
3480 int x = ex + j * xy[i][0];
3481 int y = ey + j * xy[i][1];
3484 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3487 element = Feld[x][y];
3489 /* do not restart explosions of fields with active bombs */
3490 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3493 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3495 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3496 !IS_DIGGABLE(element) && !dynabomb_xl)
3502 void Bang(int x, int y)
3504 int element = MovingOrBlocked2Element(x, y);
3505 int explosion_type = EX_TYPE_NORMAL;
3507 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3509 struct PlayerInfo *player = PLAYERINFO(x, y);
3511 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3512 player->element_nr);
3514 if (level.use_explosion_element[player->index_nr])
3516 int explosion_element = level.explosion_element[player->index_nr];
3518 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3519 explosion_type = EX_TYPE_CROSS;
3520 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3521 explosion_type = EX_TYPE_CENTER;
3529 case EL_BD_BUTTERFLY:
3532 case EL_DARK_YAMYAM:
3536 RaiseScoreElement(element);
3539 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3540 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3541 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3542 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3543 case EL_DYNABOMB_INCREASE_NUMBER:
3544 case EL_DYNABOMB_INCREASE_SIZE:
3545 case EL_DYNABOMB_INCREASE_POWER:
3546 explosion_type = EX_TYPE_DYNA;
3551 case EL_LAMP_ACTIVE:
3552 case EL_AMOEBA_TO_DIAMOND:
3553 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3554 explosion_type = EX_TYPE_CENTER;
3558 if (element_info[element].explosion_type == EXPLODES_CROSS)
3559 explosion_type = EX_TYPE_CROSS;
3560 else if (element_info[element].explosion_type == EXPLODES_1X1)
3561 explosion_type = EX_TYPE_CENTER;
3565 if (explosion_type == EX_TYPE_DYNA)
3568 Explode(x, y, EX_PHASE_START, explosion_type);
3570 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3573 void SplashAcid(int x, int y)
3575 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3576 (!IN_LEV_FIELD(x - 1, y - 2) ||
3577 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3578 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3580 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3581 (!IN_LEV_FIELD(x + 1, y - 2) ||
3582 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3583 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3585 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3588 static void InitBeltMovement()
3590 static int belt_base_element[4] =
3592 EL_CONVEYOR_BELT_1_LEFT,
3593 EL_CONVEYOR_BELT_2_LEFT,
3594 EL_CONVEYOR_BELT_3_LEFT,
3595 EL_CONVEYOR_BELT_4_LEFT
3597 static int belt_base_active_element[4] =
3599 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3600 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3601 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3602 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3607 /* set frame order for belt animation graphic according to belt direction */
3608 for (i = 0; i < NUM_BELTS; i++)
3612 for (j = 0; j < NUM_BELT_PARTS; j++)
3614 int element = belt_base_active_element[belt_nr] + j;
3615 int graphic = el2img(element);
3617 if (game.belt_dir[i] == MV_LEFT)
3618 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3620 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3624 for (y = 0; y < lev_fieldy; y++)
3626 for (x = 0; x < lev_fieldx; x++)
3628 int element = Feld[x][y];
3630 for (i = 0; i < NUM_BELTS; i++)
3632 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3634 int e_belt_nr = getBeltNrFromBeltElement(element);
3637 if (e_belt_nr == belt_nr)
3639 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3641 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3649 static void ToggleBeltSwitch(int x, int y)
3651 static int belt_base_element[4] =
3653 EL_CONVEYOR_BELT_1_LEFT,
3654 EL_CONVEYOR_BELT_2_LEFT,
3655 EL_CONVEYOR_BELT_3_LEFT,
3656 EL_CONVEYOR_BELT_4_LEFT
3658 static int belt_base_active_element[4] =
3660 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3661 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3662 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3663 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3665 static int belt_base_switch_element[4] =
3667 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3668 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3669 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3670 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3672 static int belt_move_dir[4] =
3680 int element = Feld[x][y];
3681 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3682 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3683 int belt_dir = belt_move_dir[belt_dir_nr];
3686 if (!IS_BELT_SWITCH(element))
3689 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3690 game.belt_dir[belt_nr] = belt_dir;
3692 if (belt_dir_nr == 3)
3695 /* set frame order for belt animation graphic according to belt direction */
3696 for (i = 0; i < NUM_BELT_PARTS; i++)
3698 int element = belt_base_active_element[belt_nr] + i;
3699 int graphic = el2img(element);
3701 if (belt_dir == MV_LEFT)
3702 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3704 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3707 for (yy = 0; yy < lev_fieldy; yy++)
3709 for (xx = 0; xx < lev_fieldx; xx++)
3711 int element = Feld[xx][yy];
3713 if (IS_BELT_SWITCH(element))
3715 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3717 if (e_belt_nr == belt_nr)
3719 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3720 DrawLevelField(xx, yy);
3723 else if (IS_BELT(element) && belt_dir != MV_NONE)
3725 int e_belt_nr = getBeltNrFromBeltElement(element);
3727 if (e_belt_nr == belt_nr)
3729 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3731 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3732 DrawLevelField(xx, yy);
3735 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3737 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3739 if (e_belt_nr == belt_nr)
3741 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3743 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3744 DrawLevelField(xx, yy);
3751 static void ToggleSwitchgateSwitch(int x, int y)
3755 game.switchgate_pos = !game.switchgate_pos;
3757 for (yy = 0; yy < lev_fieldy; yy++)
3759 for (xx = 0; xx < lev_fieldx; xx++)
3761 int element = Feld[xx][yy];
3763 if (element == EL_SWITCHGATE_SWITCH_UP ||
3764 element == EL_SWITCHGATE_SWITCH_DOWN)
3766 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3767 DrawLevelField(xx, yy);
3769 else if (element == EL_SWITCHGATE_OPEN ||
3770 element == EL_SWITCHGATE_OPENING)
3772 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3774 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3776 else if (element == EL_SWITCHGATE_CLOSED ||
3777 element == EL_SWITCHGATE_CLOSING)
3779 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3781 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3787 static int getInvisibleActiveFromInvisibleElement(int element)
3789 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3790 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3791 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3795 static int getInvisibleFromInvisibleActiveElement(int element)
3797 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3798 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3799 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3803 static void RedrawAllLightSwitchesAndInvisibleElements()
3807 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3809 int element = Feld[x][y];
3811 if (element == EL_LIGHT_SWITCH &&
3812 game.light_time_left > 0)
3814 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3815 DrawLevelField(x, y);
3817 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3818 game.light_time_left == 0)
3820 Feld[x][y] = EL_LIGHT_SWITCH;
3821 DrawLevelField(x, y);
3823 else if (element == EL_EMC_DRIPPER &&
3824 game.light_time_left > 0)
3826 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3827 DrawLevelField(x, y);
3829 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3830 game.light_time_left == 0)
3832 Feld[x][y] = EL_EMC_DRIPPER;
3833 DrawLevelField(x, y);
3835 else if (element == EL_INVISIBLE_STEELWALL ||
3836 element == EL_INVISIBLE_WALL ||
3837 element == EL_INVISIBLE_SAND)
3839 if (game.light_time_left > 0)
3840 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3842 DrawLevelField(x, y);
3844 /* uncrumble neighbour fields, if needed */
3845 if (element == EL_INVISIBLE_SAND)
3846 DrawLevelFieldCrumbledSandNeighbours(x, y);
3848 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3849 element == EL_INVISIBLE_WALL_ACTIVE ||
3850 element == EL_INVISIBLE_SAND_ACTIVE)
3852 if (game.light_time_left == 0)
3853 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3855 DrawLevelField(x, y);
3857 /* re-crumble neighbour fields, if needed */
3858 if (element == EL_INVISIBLE_SAND)
3859 DrawLevelFieldCrumbledSandNeighbours(x, y);
3864 static void RedrawAllInvisibleElementsForLenses()
3868 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3870 int element = Feld[x][y];
3872 if (element == EL_EMC_DRIPPER &&
3873 game.lenses_time_left > 0)
3875 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3876 DrawLevelField(x, y);
3878 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3879 game.lenses_time_left == 0)
3881 Feld[x][y] = EL_EMC_DRIPPER;
3882 DrawLevelField(x, y);
3884 else if (element == EL_INVISIBLE_STEELWALL ||
3885 element == EL_INVISIBLE_WALL ||
3886 element == EL_INVISIBLE_SAND)
3888 if (game.lenses_time_left > 0)
3889 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3891 DrawLevelField(x, y);
3893 /* uncrumble neighbour fields, if needed */
3894 if (element == EL_INVISIBLE_SAND)
3895 DrawLevelFieldCrumbledSandNeighbours(x, y);
3897 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3898 element == EL_INVISIBLE_WALL_ACTIVE ||
3899 element == EL_INVISIBLE_SAND_ACTIVE)
3901 if (game.lenses_time_left == 0)
3902 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3904 DrawLevelField(x, y);
3906 /* re-crumble neighbour fields, if needed */
3907 if (element == EL_INVISIBLE_SAND)
3908 DrawLevelFieldCrumbledSandNeighbours(x, y);
3913 static void RedrawAllInvisibleElementsForMagnifier()
3917 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3919 int element = Feld[x][y];
3921 if (element == EL_EMC_FAKE_GRASS &&
3922 game.magnify_time_left > 0)
3924 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
3925 DrawLevelField(x, y);
3927 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
3928 game.magnify_time_left == 0)
3930 Feld[x][y] = EL_EMC_FAKE_GRASS;
3931 DrawLevelField(x, y);
3933 else if (IS_GATE_GRAY(element) &&
3934 game.magnify_time_left > 0)
3936 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
3937 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
3938 IS_EM_GATE_GRAY(element) ?
3939 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
3940 IS_EMC_GATE_GRAY(element) ?
3941 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
3943 DrawLevelField(x, y);
3945 else if (IS_GATE_GRAY_ACTIVE(element) &&
3946 game.magnify_time_left == 0)
3948 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
3949 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
3950 IS_EM_GATE_GRAY_ACTIVE(element) ?
3951 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
3952 IS_EMC_GATE_GRAY_ACTIVE(element) ?
3953 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
3955 DrawLevelField(x, y);
3960 static void ToggleLightSwitch(int x, int y)
3962 int element = Feld[x][y];
3964 game.light_time_left =
3965 (element == EL_LIGHT_SWITCH ?
3966 level.time_light * FRAMES_PER_SECOND : 0);
3968 RedrawAllLightSwitchesAndInvisibleElements();
3971 static void ActivateTimegateSwitch(int x, int y)
3975 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3977 for (yy = 0; yy < lev_fieldy; yy++)
3979 for (xx = 0; xx < lev_fieldx; xx++)
3981 int element = Feld[xx][yy];
3983 if (element == EL_TIMEGATE_CLOSED ||
3984 element == EL_TIMEGATE_CLOSING)
3986 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3987 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3991 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3993 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3994 DrawLevelField(xx, yy);
4001 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4004 void Impact(int x, int y)
4006 boolean last_line = (y == lev_fieldy - 1);
4007 boolean object_hit = FALSE;
4008 boolean impact = (last_line || object_hit);
4009 int element = Feld[x][y];
4010 int smashed = EL_STEELWALL;
4012 if (!last_line) /* check if element below was hit */
4014 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4017 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4018 MovDir[x][y + 1] != MV_DOWN ||
4019 MovPos[x][y + 1] <= TILEY / 2));
4021 /* do not smash moving elements that left the smashed field in time */
4022 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4023 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4026 #if USE_QUICKSAND_IMPACT_BUGFIX
4027 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4029 RemoveMovingField(x, y + 1);
4030 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4031 Feld[x][y + 2] = EL_ROCK;
4032 DrawLevelField(x, y + 2);
4039 smashed = MovingOrBlocked2Element(x, y + 1);
4041 impact = (last_line || object_hit);
4044 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4046 SplashAcid(x, y + 1);
4050 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4051 /* only reset graphic animation if graphic really changes after impact */
4053 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4055 ResetGfxAnimation(x, y);
4056 DrawLevelField(x, y);
4059 if (impact && CAN_EXPLODE_IMPACT(element))
4064 else if (impact && element == EL_PEARL)
4066 ResetGfxAnimation(x, y);
4068 Feld[x][y] = EL_PEARL_BREAKING;
4069 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4072 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4074 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4079 if (impact && element == EL_AMOEBA_DROP)
4081 if (object_hit && IS_PLAYER(x, y + 1))
4082 KillPlayerUnlessEnemyProtected(x, y + 1);
4083 else if (object_hit && smashed == EL_PENGUIN)
4087 Feld[x][y] = EL_AMOEBA_GROWING;
4088 Store[x][y] = EL_AMOEBA_WET;
4090 ResetRandomAnimationValue(x, y);
4095 if (object_hit) /* check which object was hit */
4097 if (CAN_PASS_MAGIC_WALL(element) &&
4098 (smashed == EL_MAGIC_WALL ||
4099 smashed == EL_BD_MAGIC_WALL))
4102 int activated_magic_wall =
4103 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4104 EL_BD_MAGIC_WALL_ACTIVE);
4106 /* activate magic wall / mill */
4107 for (yy = 0; yy < lev_fieldy; yy++)
4108 for (xx = 0; xx < lev_fieldx; xx++)
4109 if (Feld[xx][yy] == smashed)
4110 Feld[xx][yy] = activated_magic_wall;
4112 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4113 game.magic_wall_active = TRUE;
4115 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4116 SND_MAGIC_WALL_ACTIVATING :
4117 SND_BD_MAGIC_WALL_ACTIVATING));
4120 if (IS_PLAYER(x, y + 1))
4122 if (CAN_SMASH_PLAYER(element))
4124 KillPlayerUnlessEnemyProtected(x, y + 1);
4128 else if (smashed == EL_PENGUIN)
4130 if (CAN_SMASH_PLAYER(element))
4136 else if (element == EL_BD_DIAMOND)
4138 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4144 else if (((element == EL_SP_INFOTRON ||
4145 element == EL_SP_ZONK) &&
4146 (smashed == EL_SP_SNIKSNAK ||
4147 smashed == EL_SP_ELECTRON ||
4148 smashed == EL_SP_DISK_ORANGE)) ||
4149 (element == EL_SP_INFOTRON &&
4150 smashed == EL_SP_DISK_YELLOW))
4155 else if (CAN_SMASH_EVERYTHING(element))
4157 if (IS_CLASSIC_ENEMY(smashed) ||
4158 CAN_EXPLODE_SMASHED(smashed))
4163 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4165 if (smashed == EL_LAMP ||
4166 smashed == EL_LAMP_ACTIVE)
4171 else if (smashed == EL_NUT)
4173 Feld[x][y + 1] = EL_NUT_BREAKING;
4174 PlayLevelSound(x, y, SND_NUT_BREAKING);
4175 RaiseScoreElement(EL_NUT);
4178 else if (smashed == EL_PEARL)
4180 ResetGfxAnimation(x, y);
4182 Feld[x][y + 1] = EL_PEARL_BREAKING;
4183 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4186 else if (smashed == EL_DIAMOND)
4188 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4189 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4192 else if (IS_BELT_SWITCH(smashed))
4194 ToggleBeltSwitch(x, y + 1);
4196 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4197 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4199 ToggleSwitchgateSwitch(x, y + 1);
4201 else if (smashed == EL_LIGHT_SWITCH ||
4202 smashed == EL_LIGHT_SWITCH_ACTIVE)
4204 ToggleLightSwitch(x, y + 1);
4209 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4212 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4214 CheckElementChangeBySide(x, y + 1, smashed, element,
4215 CE_SWITCHED, CH_SIDE_TOP);
4216 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4222 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4227 /* play sound of magic wall / mill */
4229 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4230 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4232 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4233 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4234 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4235 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4240 /* play sound of object that hits the ground */
4241 if (last_line || object_hit)
4242 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4245 inline static void TurnRoundExt(int x, int y)
4257 { 0, 0 }, { 0, 0 }, { 0, 0 },
4262 int left, right, back;
4266 { MV_DOWN, MV_UP, MV_RIGHT },
4267 { MV_UP, MV_DOWN, MV_LEFT },
4269 { MV_LEFT, MV_RIGHT, MV_DOWN },
4273 { MV_RIGHT, MV_LEFT, MV_UP }
4276 int element = Feld[x][y];
4277 int move_pattern = element_info[element].move_pattern;
4279 int old_move_dir = MovDir[x][y];
4280 int left_dir = turn[old_move_dir].left;
4281 int right_dir = turn[old_move_dir].right;
4282 int back_dir = turn[old_move_dir].back;
4284 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4285 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4286 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4287 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4289 int left_x = x + left_dx, left_y = y + left_dy;
4290 int right_x = x + right_dx, right_y = y + right_dy;
4291 int move_x = x + move_dx, move_y = y + move_dy;
4295 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4297 TestIfBadThingTouchesOtherBadThing(x, y);
4299 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4300 MovDir[x][y] = right_dir;
4301 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4302 MovDir[x][y] = left_dir;
4304 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4306 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4309 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4311 TestIfBadThingTouchesOtherBadThing(x, y);
4313 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4314 MovDir[x][y] = left_dir;
4315 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4316 MovDir[x][y] = right_dir;
4318 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4320 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4323 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4325 TestIfBadThingTouchesOtherBadThing(x, y);
4327 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4328 MovDir[x][y] = left_dir;
4329 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4330 MovDir[x][y] = right_dir;
4332 if (MovDir[x][y] != old_move_dir)
4335 else if (element == EL_YAMYAM)
4337 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4338 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4340 if (can_turn_left && can_turn_right)
4341 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4342 else if (can_turn_left)
4343 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4344 else if (can_turn_right)
4345 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4347 MovDir[x][y] = back_dir;
4349 MovDelay[x][y] = 16 + 16 * RND(3);
4351 else if (element == EL_DARK_YAMYAM)
4353 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4355 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4358 if (can_turn_left && can_turn_right)
4359 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4360 else if (can_turn_left)
4361 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4362 else if (can_turn_right)
4363 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4365 MovDir[x][y] = back_dir;
4367 MovDelay[x][y] = 16 + 16 * RND(3);
4369 else if (element == EL_PACMAN)
4371 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4372 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4374 if (can_turn_left && can_turn_right)
4375 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4376 else if (can_turn_left)
4377 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4378 else if (can_turn_right)
4379 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4381 MovDir[x][y] = back_dir;
4383 MovDelay[x][y] = 6 + RND(40);
4385 else if (element == EL_PIG)
4387 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4388 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4389 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4390 boolean should_turn_left, should_turn_right, should_move_on;
4392 int rnd = RND(rnd_value);
4394 should_turn_left = (can_turn_left &&
4396 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4397 y + back_dy + left_dy)));
4398 should_turn_right = (can_turn_right &&
4400 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4401 y + back_dy + right_dy)));
4402 should_move_on = (can_move_on &&
4405 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4406 y + move_dy + left_dy) ||
4407 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4408 y + move_dy + right_dy)));
4410 if (should_turn_left || should_turn_right || should_move_on)
4412 if (should_turn_left && should_turn_right && should_move_on)
4413 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4414 rnd < 2 * rnd_value / 3 ? right_dir :
4416 else if (should_turn_left && should_turn_right)
4417 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4418 else if (should_turn_left && should_move_on)
4419 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4420 else if (should_turn_right && should_move_on)
4421 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4422 else if (should_turn_left)
4423 MovDir[x][y] = left_dir;
4424 else if (should_turn_right)
4425 MovDir[x][y] = right_dir;
4426 else if (should_move_on)
4427 MovDir[x][y] = old_move_dir;
4429 else if (can_move_on && rnd > rnd_value / 8)
4430 MovDir[x][y] = old_move_dir;
4431 else if (can_turn_left && can_turn_right)
4432 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4433 else if (can_turn_left && rnd > rnd_value / 8)
4434 MovDir[x][y] = left_dir;
4435 else if (can_turn_right && rnd > rnd_value/8)
4436 MovDir[x][y] = right_dir;
4438 MovDir[x][y] = back_dir;
4440 xx = x + move_xy[MovDir[x][y]].dx;
4441 yy = y + move_xy[MovDir[x][y]].dy;
4443 if (!IN_LEV_FIELD(xx, yy) ||
4444 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4445 MovDir[x][y] = old_move_dir;
4449 else if (element == EL_DRAGON)
4451 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4452 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4453 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4455 int rnd = RND(rnd_value);
4457 if (can_move_on && rnd > rnd_value / 8)
4458 MovDir[x][y] = old_move_dir;
4459 else if (can_turn_left && can_turn_right)
4460 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4461 else if (can_turn_left && rnd > rnd_value / 8)
4462 MovDir[x][y] = left_dir;
4463 else if (can_turn_right && rnd > rnd_value / 8)
4464 MovDir[x][y] = right_dir;
4466 MovDir[x][y] = back_dir;
4468 xx = x + move_xy[MovDir[x][y]].dx;
4469 yy = y + move_xy[MovDir[x][y]].dy;
4471 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4472 MovDir[x][y] = old_move_dir;
4476 else if (element == EL_MOLE)
4478 boolean can_move_on =
4479 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4480 IS_AMOEBOID(Feld[move_x][move_y]) ||
4481 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4484 boolean can_turn_left =
4485 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4486 IS_AMOEBOID(Feld[left_x][left_y])));
4488 boolean can_turn_right =
4489 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4490 IS_AMOEBOID(Feld[right_x][right_y])));
4492 if (can_turn_left && can_turn_right)
4493 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4494 else if (can_turn_left)
4495 MovDir[x][y] = left_dir;
4497 MovDir[x][y] = right_dir;
4500 if (MovDir[x][y] != old_move_dir)
4503 else if (element == EL_BALLOON)
4505 MovDir[x][y] = game.wind_direction;
4508 else if (element == EL_SPRING)
4510 if (MovDir[x][y] & MV_HORIZONTAL &&
4511 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4512 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4513 MovDir[x][y] = MV_NONE;
4517 else if (element == EL_ROBOT ||
4518 element == EL_SATELLITE ||
4519 element == EL_PENGUIN)
4521 int attr_x = -1, attr_y = -1;
4532 for (i = 0; i < MAX_PLAYERS; i++)
4534 struct PlayerInfo *player = &stored_player[i];
4535 int jx = player->jx, jy = player->jy;
4537 if (!player->active)
4541 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4549 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4550 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4551 game.engine_version < VERSION_IDENT(3,1,0,0)))
4557 if (element == EL_PENGUIN)
4560 static int xy[4][2] =
4568 for (i = 0; i < NUM_DIRECTIONS; i++)
4570 int ex = x + xy[i][0];
4571 int ey = y + xy[i][1];
4573 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4582 MovDir[x][y] = MV_NONE;
4584 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4585 else if (attr_x > x)
4586 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4588 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4589 else if (attr_y > y)
4590 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4592 if (element == EL_ROBOT)
4596 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4597 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4598 Moving2Blocked(x, y, &newx, &newy);
4600 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4601 MovDelay[x][y] = 8 + 8 * !RND(3);
4603 MovDelay[x][y] = 16;
4605 else if (element == EL_PENGUIN)
4611 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4613 boolean first_horiz = RND(2);
4614 int new_move_dir = MovDir[x][y];
4617 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4618 Moving2Blocked(x, y, &newx, &newy);
4620 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4624 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4625 Moving2Blocked(x, y, &newx, &newy);
4627 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4630 MovDir[x][y] = old_move_dir;
4634 else /* (element == EL_SATELLITE) */
4640 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4642 boolean first_horiz = RND(2);
4643 int new_move_dir = MovDir[x][y];
4646 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4647 Moving2Blocked(x, y, &newx, &newy);
4649 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4653 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4654 Moving2Blocked(x, y, &newx, &newy);
4656 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4659 MovDir[x][y] = old_move_dir;
4664 else if (move_pattern == MV_TURNING_LEFT ||
4665 move_pattern == MV_TURNING_RIGHT ||
4666 move_pattern == MV_TURNING_LEFT_RIGHT ||
4667 move_pattern == MV_TURNING_RIGHT_LEFT ||
4668 move_pattern == MV_TURNING_RANDOM ||
4669 move_pattern == MV_ALL_DIRECTIONS)
4671 boolean can_turn_left =
4672 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4673 boolean can_turn_right =
4674 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4676 if (element_info[element].move_stepsize == 0) /* "not moving" */
4679 if (move_pattern == MV_TURNING_LEFT)
4680 MovDir[x][y] = left_dir;
4681 else if (move_pattern == MV_TURNING_RIGHT)
4682 MovDir[x][y] = right_dir;
4683 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4684 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4685 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4686 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4687 else if (move_pattern == MV_TURNING_RANDOM)
4688 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4689 can_turn_right && !can_turn_left ? right_dir :
4690 RND(2) ? left_dir : right_dir);
4691 else if (can_turn_left && can_turn_right)
4692 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4693 else if (can_turn_left)
4694 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4695 else if (can_turn_right)
4696 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4698 MovDir[x][y] = back_dir;
4700 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4702 else if (move_pattern == MV_HORIZONTAL ||
4703 move_pattern == MV_VERTICAL)
4705 if (move_pattern & old_move_dir)
4706 MovDir[x][y] = back_dir;
4707 else if (move_pattern == MV_HORIZONTAL)
4708 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4709 else if (move_pattern == MV_VERTICAL)
4710 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4712 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4714 else if (move_pattern & MV_ANY_DIRECTION)
4716 MovDir[x][y] = move_pattern;
4717 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4719 else if (move_pattern & MV_WIND_DIRECTION)
4721 MovDir[x][y] = game.wind_direction;
4722 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4724 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4726 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4727 MovDir[x][y] = left_dir;
4728 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4729 MovDir[x][y] = right_dir;
4731 if (MovDir[x][y] != old_move_dir)
4732 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4734 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4736 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4737 MovDir[x][y] = right_dir;
4738 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4739 MovDir[x][y] = left_dir;
4741 if (MovDir[x][y] != old_move_dir)
4742 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4744 else if (move_pattern == MV_TOWARDS_PLAYER ||
4745 move_pattern == MV_AWAY_FROM_PLAYER)
4747 int attr_x = -1, attr_y = -1;
4749 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4760 for (i = 0; i < MAX_PLAYERS; i++)
4762 struct PlayerInfo *player = &stored_player[i];
4763 int jx = player->jx, jy = player->jy;
4765 if (!player->active)
4769 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4777 MovDir[x][y] = MV_NONE;
4779 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4780 else if (attr_x > x)
4781 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4783 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4784 else if (attr_y > y)
4785 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4787 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4789 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4791 boolean first_horiz = RND(2);
4792 int new_move_dir = MovDir[x][y];
4794 if (element_info[element].move_stepsize == 0) /* "not moving" */
4796 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4797 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4803 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4804 Moving2Blocked(x, y, &newx, &newy);
4806 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4810 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4811 Moving2Blocked(x, y, &newx, &newy);
4813 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4816 MovDir[x][y] = old_move_dir;
4819 else if (move_pattern == MV_WHEN_PUSHED ||
4820 move_pattern == MV_WHEN_DROPPED)
4822 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4823 MovDir[x][y] = MV_NONE;
4827 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4829 static int test_xy[7][2] =
4839 static int test_dir[7] =
4849 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4850 int move_preference = -1000000; /* start with very low preference */
4851 int new_move_dir = MV_NONE;
4852 int start_test = RND(4);
4855 for (i = 0; i < NUM_DIRECTIONS; i++)
4857 int move_dir = test_dir[start_test + i];
4858 int move_dir_preference;
4860 xx = x + test_xy[start_test + i][0];
4861 yy = y + test_xy[start_test + i][1];
4863 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4864 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4866 new_move_dir = move_dir;
4871 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4874 move_dir_preference = -1 * RunnerVisit[xx][yy];
4875 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4876 move_dir_preference = PlayerVisit[xx][yy];
4878 if (move_dir_preference > move_preference)
4880 /* prefer field that has not been visited for the longest time */
4881 move_preference = move_dir_preference;
4882 new_move_dir = move_dir;
4884 else if (move_dir_preference == move_preference &&
4885 move_dir == old_move_dir)
4887 /* prefer last direction when all directions are preferred equally */
4888 move_preference = move_dir_preference;
4889 new_move_dir = move_dir;
4893 MovDir[x][y] = new_move_dir;
4894 if (old_move_dir != new_move_dir)
4895 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4899 static void TurnRound(int x, int y)
4901 int direction = MovDir[x][y];
4905 GfxDir[x][y] = MovDir[x][y];
4907 if (direction != MovDir[x][y])
4911 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4914 static boolean JustBeingPushed(int x, int y)
4918 for (i = 0; i < MAX_PLAYERS; i++)
4920 struct PlayerInfo *player = &stored_player[i];
4922 if (player->active && player->is_pushing && player->MovPos)
4924 int next_jx = player->jx + (player->jx - player->last_jx);
4925 int next_jy = player->jy + (player->jy - player->last_jy);
4927 if (x == next_jx && y == next_jy)
4935 void StartMoving(int x, int y)
4937 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4938 int element = Feld[x][y];
4943 if (MovDelay[x][y] == 0)
4944 GfxAction[x][y] = ACTION_DEFAULT;
4946 if (CAN_FALL(element) && y < lev_fieldy - 1)
4948 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4949 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4950 if (JustBeingPushed(x, y))
4953 if (element == EL_QUICKSAND_FULL)
4955 if (IS_FREE(x, y + 1))
4957 InitMovingField(x, y, MV_DOWN);
4958 started_moving = TRUE;
4960 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4961 Store[x][y] = EL_ROCK;
4963 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4965 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4967 if (!MovDelay[x][y])
4968 MovDelay[x][y] = TILEY + 1;
4977 Feld[x][y] = EL_QUICKSAND_EMPTY;
4978 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4979 Store[x][y + 1] = Store[x][y];
4982 PlayLevelSoundAction(x, y, ACTION_FILLING);
4985 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4986 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4988 InitMovingField(x, y, MV_DOWN);
4989 started_moving = TRUE;
4991 Feld[x][y] = EL_QUICKSAND_FILLING;
4992 Store[x][y] = element;
4994 PlayLevelSoundAction(x, y, ACTION_FILLING);
4996 else if (element == EL_MAGIC_WALL_FULL)
4998 if (IS_FREE(x, y + 1))
5000 InitMovingField(x, y, MV_DOWN);
5001 started_moving = TRUE;
5003 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5004 Store[x][y] = EL_CHANGED(Store[x][y]);
5006 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5008 if (!MovDelay[x][y])
5009 MovDelay[x][y] = TILEY/4 + 1;
5018 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5019 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5020 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5024 else if (element == EL_BD_MAGIC_WALL_FULL)
5026 if (IS_FREE(x, y + 1))
5028 InitMovingField(x, y, MV_DOWN);
5029 started_moving = TRUE;
5031 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5032 Store[x][y] = EL_CHANGED2(Store[x][y]);
5034 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5036 if (!MovDelay[x][y])
5037 MovDelay[x][y] = TILEY/4 + 1;
5046 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5047 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5048 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5052 else if (CAN_PASS_MAGIC_WALL(element) &&
5053 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5054 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5056 InitMovingField(x, y, MV_DOWN);
5057 started_moving = TRUE;
5060 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5061 EL_BD_MAGIC_WALL_FILLING);
5062 Store[x][y] = element;
5064 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5066 SplashAcid(x, y + 1);
5068 InitMovingField(x, y, MV_DOWN);
5069 started_moving = TRUE;
5071 Store[x][y] = EL_ACID;
5073 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5074 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5076 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5077 CAN_FALL(element) && WasJustFalling[x][y] &&
5078 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5080 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5081 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5082 (Feld[x][y + 1] == EL_BLOCKED)))
5084 /* this is needed for a special case not covered by calling "Impact()"
5085 from "ContinueMoving()": if an element moves to a tile directly below
5086 another element which was just falling on that tile (which was empty
5087 in the previous frame), the falling element above would just stop
5088 instead of smashing the element below (in previous version, the above
5089 element was just checked for "moving" instead of "falling", resulting
5090 in incorrect smashes caused by horizontal movement of the above
5091 element; also, the case of the player being the element to smash was
5092 simply not covered here... :-/ ) */
5094 CheckCollision[x][y] = 0;
5098 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5100 if (MovDir[x][y] == MV_NONE)
5102 InitMovingField(x, y, MV_DOWN);
5103 started_moving = TRUE;
5106 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5108 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5109 MovDir[x][y] = MV_DOWN;
5111 InitMovingField(x, y, MV_DOWN);
5112 started_moving = TRUE;
5114 else if (element == EL_AMOEBA_DROP)
5116 Feld[x][y] = EL_AMOEBA_GROWING;
5117 Store[x][y] = EL_AMOEBA_WET;
5119 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5120 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5121 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5122 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5124 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5125 (IS_FREE(x - 1, y + 1) ||
5126 Feld[x - 1][y + 1] == EL_ACID));
5127 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5128 (IS_FREE(x + 1, y + 1) ||
5129 Feld[x + 1][y + 1] == EL_ACID));
5130 boolean can_fall_any = (can_fall_left || can_fall_right);
5131 boolean can_fall_both = (can_fall_left && can_fall_right);
5132 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5134 #if USE_NEW_ALL_SLIPPERY
5135 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5137 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5138 can_fall_right = FALSE;
5139 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5140 can_fall_left = FALSE;
5141 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5142 can_fall_right = FALSE;
5143 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5144 can_fall_left = FALSE;
5146 can_fall_any = (can_fall_left || can_fall_right);
5147 can_fall_both = FALSE;
5150 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5152 if (slippery_type == SLIPPERY_ONLY_LEFT)
5153 can_fall_right = FALSE;
5154 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5155 can_fall_left = FALSE;
5156 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5157 can_fall_right = FALSE;
5158 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5159 can_fall_left = FALSE;
5161 can_fall_any = (can_fall_left || can_fall_right);
5162 can_fall_both = (can_fall_left && can_fall_right);
5166 #if USE_NEW_ALL_SLIPPERY
5168 #if USE_NEW_SP_SLIPPERY
5169 /* !!! better use the same properties as for custom elements here !!! */
5170 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5171 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5173 can_fall_right = FALSE; /* slip down on left side */
5174 can_fall_both = FALSE;
5179 #if USE_NEW_ALL_SLIPPERY
5182 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5183 can_fall_right = FALSE; /* slip down on left side */
5185 can_fall_left = !(can_fall_right = RND(2));
5187 can_fall_both = FALSE;
5192 if (game.emulation == EMU_BOULDERDASH ||
5193 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5194 can_fall_right = FALSE; /* slip down on left side */
5196 can_fall_left = !(can_fall_right = RND(2));
5198 can_fall_both = FALSE;
5204 /* if not determined otherwise, prefer left side for slipping down */
5205 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5206 started_moving = TRUE;
5210 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5212 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5215 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5216 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5217 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5218 int belt_dir = game.belt_dir[belt_nr];
5220 if ((belt_dir == MV_LEFT && left_is_free) ||
5221 (belt_dir == MV_RIGHT && right_is_free))
5223 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5225 InitMovingField(x, y, belt_dir);
5226 started_moving = TRUE;
5228 Pushed[x][y] = TRUE;
5229 Pushed[nextx][y] = TRUE;
5231 GfxAction[x][y] = ACTION_DEFAULT;
5235 MovDir[x][y] = 0; /* if element was moving, stop it */
5240 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5242 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5244 if (CAN_MOVE(element) && !started_moving)
5247 int move_pattern = element_info[element].move_pattern;
5252 if (MovDir[x][y] == MV_NONE)
5254 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5255 x, y, element, element_info[element].token_name);
5256 printf("StartMoving(): This should never happen!\n");
5261 Moving2Blocked(x, y, &newx, &newy);
5263 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5266 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5267 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5269 WasJustMoving[x][y] = 0;
5270 CheckCollision[x][y] = 0;
5272 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5274 if (Feld[x][y] != element) /* element has changed */
5278 if (!MovDelay[x][y]) /* start new movement phase */
5280 /* all objects that can change their move direction after each step
5281 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5283 if (element != EL_YAMYAM &&
5284 element != EL_DARK_YAMYAM &&
5285 element != EL_PACMAN &&
5286 !(move_pattern & MV_ANY_DIRECTION) &&
5287 move_pattern != MV_TURNING_LEFT &&
5288 move_pattern != MV_TURNING_RIGHT &&
5289 move_pattern != MV_TURNING_LEFT_RIGHT &&
5290 move_pattern != MV_TURNING_RIGHT_LEFT &&
5291 move_pattern != MV_TURNING_RANDOM)
5295 if (MovDelay[x][y] && (element == EL_BUG ||
5296 element == EL_SPACESHIP ||
5297 element == EL_SP_SNIKSNAK ||
5298 element == EL_SP_ELECTRON ||
5299 element == EL_MOLE))
5300 DrawLevelField(x, y);
5304 if (MovDelay[x][y]) /* wait some time before next movement */
5308 if (element == EL_ROBOT ||
5309 element == EL_YAMYAM ||
5310 element == EL_DARK_YAMYAM)
5312 DrawLevelElementAnimationIfNeeded(x, y, element);
5313 PlayLevelSoundAction(x, y, ACTION_WAITING);
5315 else if (element == EL_SP_ELECTRON)
5316 DrawLevelElementAnimationIfNeeded(x, y, element);
5317 else if (element == EL_DRAGON)
5320 int dir = MovDir[x][y];
5321 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5322 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5323 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5324 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5325 dir == MV_UP ? IMG_FLAMES_1_UP :
5326 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5327 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5329 GfxAction[x][y] = ACTION_ATTACKING;
5331 if (IS_PLAYER(x, y))
5332 DrawPlayerField(x, y);
5334 DrawLevelField(x, y);
5336 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5338 for (i = 1; i <= 3; i++)
5340 int xx = x + i * dx;
5341 int yy = y + i * dy;
5342 int sx = SCREENX(xx);
5343 int sy = SCREENY(yy);
5344 int flame_graphic = graphic + (i - 1);
5346 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5351 int flamed = MovingOrBlocked2Element(xx, yy);
5355 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5357 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5358 RemoveMovingField(xx, yy);
5360 RemoveField(xx, yy);
5362 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5365 RemoveMovingField(xx, yy);
5368 ChangeDelay[xx][yy] = 0;
5370 Feld[xx][yy] = EL_FLAMES;
5372 if (IN_SCR_FIELD(sx, sy))
5374 DrawLevelFieldCrumbledSand(xx, yy);
5375 DrawGraphic(sx, sy, flame_graphic, frame);
5380 if (Feld[xx][yy] == EL_FLAMES)
5381 Feld[xx][yy] = EL_EMPTY;
5382 DrawLevelField(xx, yy);
5387 if (MovDelay[x][y]) /* element still has to wait some time */
5389 PlayLevelSoundAction(x, y, ACTION_WAITING);
5395 /* now make next step */
5397 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5399 if (DONT_COLLIDE_WITH(element) &&
5400 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5401 !PLAYER_ENEMY_PROTECTED(newx, newy))
5403 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5408 else if (CAN_MOVE_INTO_ACID(element) &&
5409 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5410 (MovDir[x][y] == MV_DOWN ||
5411 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5413 SplashAcid(newx, newy);
5414 Store[x][y] = EL_ACID;
5416 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5418 if (Feld[newx][newy] == EL_EXIT_OPEN)
5421 DrawLevelField(x, y);
5423 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5424 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5425 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5427 local_player->friends_still_needed--;
5428 if (!local_player->friends_still_needed &&
5429 !local_player->GameOver && AllPlayersGone)
5430 local_player->LevelSolved = local_player->GameOver = TRUE;
5434 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5436 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5437 DrawLevelField(newx, newy);
5439 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5441 else if (!IS_FREE(newx, newy))
5443 GfxAction[x][y] = ACTION_WAITING;
5445 if (IS_PLAYER(x, y))
5446 DrawPlayerField(x, y);
5448 DrawLevelField(x, y);
5453 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5455 if (IS_FOOD_PIG(Feld[newx][newy]))
5457 if (IS_MOVING(newx, newy))
5458 RemoveMovingField(newx, newy);
5461 Feld[newx][newy] = EL_EMPTY;
5462 DrawLevelField(newx, newy);
5465 PlayLevelSound(x, y, SND_PIG_DIGGING);
5467 else if (!IS_FREE(newx, newy))
5469 if (IS_PLAYER(x, y))
5470 DrawPlayerField(x, y);
5472 DrawLevelField(x, y);
5477 else if (IS_CUSTOM_ELEMENT(element) &&
5478 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5480 int new_element = Feld[newx][newy];
5482 if (!IS_FREE(newx, newy))
5484 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5485 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5488 /* no element can dig solid indestructible elements */
5489 if (IS_INDESTRUCTIBLE(new_element) &&
5490 !IS_DIGGABLE(new_element) &&
5491 !IS_COLLECTIBLE(new_element))
5494 if (AmoebaNr[newx][newy] &&
5495 (new_element == EL_AMOEBA_FULL ||
5496 new_element == EL_BD_AMOEBA ||
5497 new_element == EL_AMOEBA_GROWING))
5499 AmoebaCnt[AmoebaNr[newx][newy]]--;
5500 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5503 if (IS_MOVING(newx, newy))
5504 RemoveMovingField(newx, newy);
5507 RemoveField(newx, newy);
5508 DrawLevelField(newx, newy);
5511 /* if digged element was about to explode, prevent the explosion */
5512 ExplodeField[newx][newy] = EX_TYPE_NONE;
5514 PlayLevelSoundAction(x, y, action);
5517 Store[newx][newy] = EL_EMPTY;
5518 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5520 int move_leave_element = element_info[element].move_leave_element;
5522 /* this makes it possible to leave the removed element again */
5523 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5524 new_element : move_leave_element);
5527 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5529 RunnerVisit[x][y] = FrameCounter;
5530 PlayerVisit[x][y] /= 8; /* expire player visit path */
5533 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5535 if (!IS_FREE(newx, newy))
5537 if (IS_PLAYER(x, y))
5538 DrawPlayerField(x, y);
5540 DrawLevelField(x, y);
5546 boolean wanna_flame = !RND(10);
5547 int dx = newx - x, dy = newy - y;
5548 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5549 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5550 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5551 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5552 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5553 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5556 IS_CLASSIC_ENEMY(element1) ||
5557 IS_CLASSIC_ENEMY(element2)) &&
5558 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5559 element1 != EL_FLAMES && element2 != EL_FLAMES)
5561 ResetGfxAnimation(x, y);
5562 GfxAction[x][y] = ACTION_ATTACKING;
5564 if (IS_PLAYER(x, y))
5565 DrawPlayerField(x, y);
5567 DrawLevelField(x, y);
5569 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5571 MovDelay[x][y] = 50;
5575 RemoveField(newx, newy);
5577 Feld[newx][newy] = EL_FLAMES;
5578 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5581 RemoveField(newx1, newy1);
5583 Feld[newx1][newy1] = EL_FLAMES;
5585 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5588 RemoveField(newx2, newy2);
5590 Feld[newx2][newy2] = EL_FLAMES;
5597 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5598 Feld[newx][newy] == EL_DIAMOND)
5600 if (IS_MOVING(newx, newy))
5601 RemoveMovingField(newx, newy);
5604 Feld[newx][newy] = EL_EMPTY;
5605 DrawLevelField(newx, newy);
5608 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5610 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5611 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5613 if (AmoebaNr[newx][newy])
5615 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5616 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5617 Feld[newx][newy] == EL_BD_AMOEBA)
5618 AmoebaCnt[AmoebaNr[newx][newy]]--;
5623 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5625 RemoveMovingField(newx, newy);
5628 if (IS_MOVING(newx, newy))
5630 RemoveMovingField(newx, newy);
5635 Feld[newx][newy] = EL_EMPTY;
5636 DrawLevelField(newx, newy);
5639 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5641 else if ((element == EL_PACMAN || element == EL_MOLE)
5642 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5644 if (AmoebaNr[newx][newy])
5646 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5647 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5648 Feld[newx][newy] == EL_BD_AMOEBA)
5649 AmoebaCnt[AmoebaNr[newx][newy]]--;
5652 if (element == EL_MOLE)
5654 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5655 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5657 ResetGfxAnimation(x, y);
5658 GfxAction[x][y] = ACTION_DIGGING;
5659 DrawLevelField(x, y);
5661 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5663 return; /* wait for shrinking amoeba */
5665 else /* element == EL_PACMAN */
5667 Feld[newx][newy] = EL_EMPTY;
5668 DrawLevelField(newx, newy);
5669 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5672 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5673 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5674 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5676 /* wait for shrinking amoeba to completely disappear */
5679 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5681 /* object was running against a wall */
5686 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5687 if (move_pattern & MV_ANY_DIRECTION &&
5688 move_pattern == MovDir[x][y])
5690 int blocking_element =
5691 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5693 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5696 element = Feld[x][y]; /* element might have changed */
5700 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5701 DrawLevelElementAnimation(x, y, element);
5703 if (DONT_TOUCH(element))
5704 TestIfBadThingTouchesPlayer(x, y);
5709 InitMovingField(x, y, MovDir[x][y]);
5711 PlayLevelSoundAction(x, y, ACTION_MOVING);
5715 ContinueMoving(x, y);
5718 void ContinueMoving(int x, int y)
5720 int element = Feld[x][y];
5721 int stored = Store[x][y];
5722 struct ElementInfo *ei = &element_info[element];
5723 int direction = MovDir[x][y];
5724 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5725 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5726 int newx = x + dx, newy = y + dy;
5727 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5728 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5729 boolean last_line = (newy == lev_fieldy - 1);
5731 MovPos[x][y] += getElementMoveStepsize(x, y);
5733 if (pushed_by_player) /* special case: moving object pushed by player */
5734 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5736 if (ABS(MovPos[x][y]) < TILEX)
5738 DrawLevelField(x, y);
5740 return; /* element is still moving */
5743 /* element reached destination field */
5745 Feld[x][y] = EL_EMPTY;
5746 Feld[newx][newy] = element;
5747 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5749 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5751 element = Feld[newx][newy] = EL_ACID;
5753 else if (element == EL_MOLE)
5755 Feld[x][y] = EL_SAND;
5757 DrawLevelFieldCrumbledSandNeighbours(x, y);
5759 else if (element == EL_QUICKSAND_FILLING)
5761 element = Feld[newx][newy] = get_next_element(element);
5762 Store[newx][newy] = Store[x][y];
5764 else if (element == EL_QUICKSAND_EMPTYING)
5766 Feld[x][y] = get_next_element(element);
5767 element = Feld[newx][newy] = Store[x][y];
5769 else if (element == EL_MAGIC_WALL_FILLING)
5771 element = Feld[newx][newy] = get_next_element(element);
5772 if (!game.magic_wall_active)
5773 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5774 Store[newx][newy] = Store[x][y];
5776 else if (element == EL_MAGIC_WALL_EMPTYING)
5778 Feld[x][y] = get_next_element(element);
5779 if (!game.magic_wall_active)
5780 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5781 element = Feld[newx][newy] = Store[x][y];
5783 #if USE_NEW_CUSTOM_VALUE
5784 InitField(newx, newy, FALSE);
5787 else if (element == EL_BD_MAGIC_WALL_FILLING)
5789 element = Feld[newx][newy] = get_next_element(element);
5790 if (!game.magic_wall_active)
5791 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5792 Store[newx][newy] = Store[x][y];
5794 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5796 Feld[x][y] = get_next_element(element);
5797 if (!game.magic_wall_active)
5798 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5799 element = Feld[newx][newy] = Store[x][y];
5801 #if USE_NEW_CUSTOM_VALUE
5802 InitField(newx, newy, FALSE);
5805 else if (element == EL_AMOEBA_DROPPING)
5807 Feld[x][y] = get_next_element(element);
5808 element = Feld[newx][newy] = Store[x][y];
5810 else if (element == EL_SOKOBAN_OBJECT)
5813 Feld[x][y] = Back[x][y];
5815 if (Back[newx][newy])
5816 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5818 Back[x][y] = Back[newx][newy] = 0;
5821 Store[x][y] = EL_EMPTY;
5826 MovDelay[newx][newy] = 0;
5829 if (CAN_CHANGE_OR_HAS_ACTION(element))
5831 if (CAN_CHANGE(element))
5834 /* copy element change control values to new field */
5835 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5836 ChangePage[newx][newy] = ChangePage[x][y];
5837 ChangeCount[newx][newy] = ChangeCount[x][y];
5838 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5841 #if USE_NEW_CUSTOM_VALUE
5842 CustomValue[newx][newy] = CustomValue[x][y];
5848 #if USE_NEW_CUSTOM_VALUE
5849 CustomValue[newx][newy] = CustomValue[x][y];
5853 ChangeDelay[x][y] = 0;
5854 ChangePage[x][y] = -1;
5855 ChangeCount[x][y] = 0;
5856 ChangeEvent[x][y] = -1;
5858 #if USE_NEW_CUSTOM_VALUE
5859 CustomValue[x][y] = 0;
5862 /* copy animation control values to new field */
5863 GfxFrame[newx][newy] = GfxFrame[x][y];
5864 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5865 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5866 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5868 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5870 /* some elements can leave other elements behind after moving */
5871 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5872 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5873 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5875 int move_leave_element = ei->move_leave_element;
5877 /* this makes it possible to leave the removed element again */
5878 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5879 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5880 move_leave_element = stored;
5882 Feld[x][y] = move_leave_element;
5884 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5885 MovDir[x][y] = direction;
5887 InitField(x, y, FALSE);
5889 if (GFX_CRUMBLED(Feld[x][y]))
5890 DrawLevelFieldCrumbledSandNeighbours(x, y);
5892 if (ELEM_IS_PLAYER(move_leave_element))
5893 RelocatePlayer(x, y, move_leave_element);
5896 /* do this after checking for left-behind element */
5897 ResetGfxAnimation(x, y); /* reset animation values for old field */
5899 if (!CAN_MOVE(element) ||
5900 (CAN_FALL(element) && direction == MV_DOWN &&
5901 (element == EL_SPRING ||
5902 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5903 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5904 GfxDir[x][y] = MovDir[newx][newy] = 0;
5906 DrawLevelField(x, y);
5907 DrawLevelField(newx, newy);
5909 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5911 /* prevent pushed element from moving on in pushed direction */
5912 if (pushed_by_player && CAN_MOVE(element) &&
5913 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5914 !(element_info[element].move_pattern & direction))
5915 TurnRound(newx, newy);
5917 /* prevent elements on conveyor belt from moving on in last direction */
5918 if (pushed_by_conveyor && CAN_FALL(element) &&
5919 direction & MV_HORIZONTAL)
5920 MovDir[newx][newy] = 0;
5922 if (!pushed_by_player)
5924 int nextx = newx + dx, nexty = newy + dy;
5925 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5927 WasJustMoving[newx][newy] = 3;
5929 if (CAN_FALL(element) && direction == MV_DOWN)
5930 WasJustFalling[newx][newy] = 3;
5932 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5933 CheckCollision[newx][newy] = 2;
5936 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5938 TestIfBadThingTouchesPlayer(newx, newy);
5939 TestIfBadThingTouchesFriend(newx, newy);
5941 if (!IS_CUSTOM_ELEMENT(element))
5942 TestIfBadThingTouchesOtherBadThing(newx, newy);
5944 else if (element == EL_PENGUIN)
5945 TestIfFriendTouchesBadThing(newx, newy);
5947 /* give the player one last chance (one more frame) to move away */
5948 if (CAN_FALL(element) && direction == MV_DOWN &&
5949 (last_line || (!IS_FREE(x, newy + 1) &&
5950 (!IS_PLAYER(x, newy + 1) ||
5951 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5954 if (pushed_by_player && !game.use_change_when_pushing_bug)
5956 int push_side = MV_DIR_OPPOSITE(direction);
5957 struct PlayerInfo *player = PLAYERINFO(x, y);
5959 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5960 player->index_bit, push_side);
5961 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5962 player->index_bit, push_side);
5965 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
5967 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5969 TestIfElementHitsCustomElement(newx, newy, direction);
5970 TestIfPlayerTouchesCustomElement(newx, newy);
5971 TestIfElementTouchesCustomElement(newx, newy);
5974 int AmoebeNachbarNr(int ax, int ay)
5977 int element = Feld[ax][ay];
5979 static int xy[4][2] =
5987 for (i = 0; i < NUM_DIRECTIONS; i++)
5989 int x = ax + xy[i][0];
5990 int y = ay + xy[i][1];
5992 if (!IN_LEV_FIELD(x, y))
5995 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5996 group_nr = AmoebaNr[x][y];
6002 void AmoebenVereinigen(int ax, int ay)
6004 int i, x, y, xx, yy;
6005 int new_group_nr = AmoebaNr[ax][ay];
6006 static int xy[4][2] =
6014 if (new_group_nr == 0)
6017 for (i = 0; i < NUM_DIRECTIONS; i++)
6022 if (!IN_LEV_FIELD(x, y))
6025 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6026 Feld[x][y] == EL_BD_AMOEBA ||
6027 Feld[x][y] == EL_AMOEBA_DEAD) &&
6028 AmoebaNr[x][y] != new_group_nr)
6030 int old_group_nr = AmoebaNr[x][y];
6032 if (old_group_nr == 0)
6035 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6036 AmoebaCnt[old_group_nr] = 0;
6037 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6038 AmoebaCnt2[old_group_nr] = 0;
6040 for (yy = 0; yy < lev_fieldy; yy++)
6042 for (xx = 0; xx < lev_fieldx; xx++)
6044 if (AmoebaNr[xx][yy] == old_group_nr)
6045 AmoebaNr[xx][yy] = new_group_nr;
6052 void AmoebeUmwandeln(int ax, int ay)
6056 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6058 int group_nr = AmoebaNr[ax][ay];
6063 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6064 printf("AmoebeUmwandeln(): This should never happen!\n");
6069 for (y = 0; y < lev_fieldy; y++)
6071 for (x = 0; x < lev_fieldx; x++)
6073 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6076 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6080 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6081 SND_AMOEBA_TURNING_TO_GEM :
6082 SND_AMOEBA_TURNING_TO_ROCK));
6087 static int xy[4][2] =
6095 for (i = 0; i < NUM_DIRECTIONS; i++)
6100 if (!IN_LEV_FIELD(x, y))
6103 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6105 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6106 SND_AMOEBA_TURNING_TO_GEM :
6107 SND_AMOEBA_TURNING_TO_ROCK));
6114 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6117 int group_nr = AmoebaNr[ax][ay];
6118 boolean done = FALSE;
6123 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6124 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6129 for (y = 0; y < lev_fieldy; y++)
6131 for (x = 0; x < lev_fieldx; x++)
6133 if (AmoebaNr[x][y] == group_nr &&
6134 (Feld[x][y] == EL_AMOEBA_DEAD ||
6135 Feld[x][y] == EL_BD_AMOEBA ||
6136 Feld[x][y] == EL_AMOEBA_GROWING))
6139 Feld[x][y] = new_element;
6140 InitField(x, y, FALSE);
6141 DrawLevelField(x, y);
6148 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6149 SND_BD_AMOEBA_TURNING_TO_ROCK :
6150 SND_BD_AMOEBA_TURNING_TO_GEM));
6153 void AmoebeWaechst(int x, int y)
6155 static unsigned long sound_delay = 0;
6156 static unsigned long sound_delay_value = 0;
6158 if (!MovDelay[x][y]) /* start new growing cycle */
6162 if (DelayReached(&sound_delay, sound_delay_value))
6164 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6165 sound_delay_value = 30;
6169 if (MovDelay[x][y]) /* wait some time before growing bigger */
6172 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6174 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6175 6 - MovDelay[x][y]);
6177 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6180 if (!MovDelay[x][y])
6182 Feld[x][y] = Store[x][y];
6184 DrawLevelField(x, y);
6189 void AmoebaDisappearing(int x, int y)
6191 static unsigned long sound_delay = 0;
6192 static unsigned long sound_delay_value = 0;
6194 if (!MovDelay[x][y]) /* start new shrinking cycle */
6198 if (DelayReached(&sound_delay, sound_delay_value))
6199 sound_delay_value = 30;
6202 if (MovDelay[x][y]) /* wait some time before shrinking */
6205 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6207 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6208 6 - MovDelay[x][y]);
6210 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6213 if (!MovDelay[x][y])
6215 Feld[x][y] = EL_EMPTY;
6216 DrawLevelField(x, y);
6218 /* don't let mole enter this field in this cycle;
6219 (give priority to objects falling to this field from above) */
6225 void AmoebeAbleger(int ax, int ay)
6228 int element = Feld[ax][ay];
6229 int graphic = el2img(element);
6230 int newax = ax, neway = ay;
6231 static int xy[4][2] =
6239 if (!level.amoeba_speed)
6241 Feld[ax][ay] = EL_AMOEBA_DEAD;
6242 DrawLevelField(ax, ay);
6246 if (IS_ANIMATED(graphic))
6247 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6249 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6250 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6252 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6255 if (MovDelay[ax][ay])
6259 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6262 int x = ax + xy[start][0];
6263 int y = ay + xy[start][1];
6265 if (!IN_LEV_FIELD(x, y))
6268 if (IS_FREE(x, y) ||
6269 CAN_GROW_INTO(Feld[x][y]) ||
6270 Feld[x][y] == EL_QUICKSAND_EMPTY)
6276 if (newax == ax && neway == ay)
6279 else /* normal or "filled" (BD style) amoeba */
6282 boolean waiting_for_player = FALSE;
6284 for (i = 0; i < NUM_DIRECTIONS; i++)
6286 int j = (start + i) % 4;
6287 int x = ax + xy[j][0];
6288 int y = ay + xy[j][1];
6290 if (!IN_LEV_FIELD(x, y))
6293 if (IS_FREE(x, y) ||
6294 CAN_GROW_INTO(Feld[x][y]) ||
6295 Feld[x][y] == EL_QUICKSAND_EMPTY)
6301 else if (IS_PLAYER(x, y))
6302 waiting_for_player = TRUE;
6305 if (newax == ax && neway == ay) /* amoeba cannot grow */
6307 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6309 Feld[ax][ay] = EL_AMOEBA_DEAD;
6310 DrawLevelField(ax, ay);
6311 AmoebaCnt[AmoebaNr[ax][ay]]--;
6313 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6315 if (element == EL_AMOEBA_FULL)
6316 AmoebeUmwandeln(ax, ay);
6317 else if (element == EL_BD_AMOEBA)
6318 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6323 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6325 /* amoeba gets larger by growing in some direction */
6327 int new_group_nr = AmoebaNr[ax][ay];
6330 if (new_group_nr == 0)
6332 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6333 printf("AmoebeAbleger(): This should never happen!\n");
6338 AmoebaNr[newax][neway] = new_group_nr;
6339 AmoebaCnt[new_group_nr]++;
6340 AmoebaCnt2[new_group_nr]++;
6342 /* if amoeba touches other amoeba(s) after growing, unify them */
6343 AmoebenVereinigen(newax, neway);
6345 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6347 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6353 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6354 (neway == lev_fieldy - 1 && newax != ax))
6356 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6357 Store[newax][neway] = element;
6359 else if (neway == ay)
6361 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6363 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6367 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6368 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6369 Store[ax][ay] = EL_AMOEBA_DROP;
6370 ContinueMoving(ax, ay);
6374 DrawLevelField(newax, neway);
6377 void Life(int ax, int ay)
6381 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6384 int element = Feld[ax][ay];
6385 int graphic = el2img(element);
6386 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6388 boolean changed = FALSE;
6390 if (IS_ANIMATED(graphic))
6391 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6396 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6397 MovDelay[ax][ay] = life_time;
6399 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6402 if (MovDelay[ax][ay])
6406 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6408 int xx = ax+x1, yy = ay+y1;
6411 if (!IN_LEV_FIELD(xx, yy))
6414 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6416 int x = xx+x2, y = yy+y2;
6418 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6421 if (((Feld[x][y] == element ||
6422 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6424 (IS_FREE(x, y) && Stop[x][y]))
6428 if (xx == ax && yy == ay) /* field in the middle */
6430 if (nachbarn < life_parameter[0] ||
6431 nachbarn > life_parameter[1])
6433 Feld[xx][yy] = EL_EMPTY;
6435 DrawLevelField(xx, yy);
6436 Stop[xx][yy] = TRUE;
6440 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6441 { /* free border field */
6442 if (nachbarn >= life_parameter[2] &&
6443 nachbarn <= life_parameter[3])
6445 Feld[xx][yy] = element;
6446 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6448 DrawLevelField(xx, yy);
6449 Stop[xx][yy] = TRUE;
6456 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6457 SND_GAME_OF_LIFE_GROWING);
6460 static void InitRobotWheel(int x, int y)
6462 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6465 static void RunRobotWheel(int x, int y)
6467 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6470 static void StopRobotWheel(int x, int y)
6472 if (ZX == x && ZY == y)
6476 static void InitTimegateWheel(int x, int y)
6478 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6481 static void RunTimegateWheel(int x, int y)
6483 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6486 void CheckExit(int x, int y)
6488 if (local_player->gems_still_needed > 0 ||
6489 local_player->sokobanfields_still_needed > 0 ||
6490 local_player->lights_still_needed > 0)
6492 int element = Feld[x][y];
6493 int graphic = el2img(element);
6495 if (IS_ANIMATED(graphic))
6496 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6501 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6504 Feld[x][y] = EL_EXIT_OPENING;
6506 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6509 void CheckExitSP(int x, int y)
6511 if (local_player->gems_still_needed > 0)
6513 int element = Feld[x][y];
6514 int graphic = el2img(element);
6516 if (IS_ANIMATED(graphic))
6517 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6522 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6525 Feld[x][y] = EL_SP_EXIT_OPENING;
6527 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6530 static void CloseAllOpenTimegates()
6534 for (y = 0; y < lev_fieldy; y++)
6536 for (x = 0; x < lev_fieldx; x++)
6538 int element = Feld[x][y];
6540 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6542 Feld[x][y] = EL_TIMEGATE_CLOSING;
6544 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6550 void EdelsteinFunkeln(int x, int y)
6552 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6555 if (Feld[x][y] == EL_BD_DIAMOND)
6558 if (MovDelay[x][y] == 0) /* next animation frame */
6559 MovDelay[x][y] = 11 * !SimpleRND(500);
6561 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6565 if (setup.direct_draw && MovDelay[x][y])
6566 SetDrawtoField(DRAW_BUFFERED);
6568 DrawLevelElementAnimation(x, y, Feld[x][y]);
6570 if (MovDelay[x][y] != 0)
6572 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6573 10 - MovDelay[x][y]);
6575 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6577 if (setup.direct_draw)
6581 dest_x = FX + SCREENX(x) * TILEX;
6582 dest_y = FY + SCREENY(y) * TILEY;
6584 BlitBitmap(drawto_field, window,
6585 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6586 SetDrawtoField(DRAW_DIRECT);
6592 void MauerWaechst(int x, int y)
6596 if (!MovDelay[x][y]) /* next animation frame */
6597 MovDelay[x][y] = 3 * delay;
6599 if (MovDelay[x][y]) /* wait some time before next frame */
6603 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6605 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6606 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6608 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6611 if (!MovDelay[x][y])
6613 if (MovDir[x][y] == MV_LEFT)
6615 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6616 DrawLevelField(x - 1, y);
6618 else if (MovDir[x][y] == MV_RIGHT)
6620 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6621 DrawLevelField(x + 1, y);
6623 else if (MovDir[x][y] == MV_UP)
6625 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6626 DrawLevelField(x, y - 1);
6630 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6631 DrawLevelField(x, y + 1);
6634 Feld[x][y] = Store[x][y];
6636 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6637 DrawLevelField(x, y);
6642 void MauerAbleger(int ax, int ay)
6644 int element = Feld[ax][ay];
6645 int graphic = el2img(element);
6646 boolean oben_frei = FALSE, unten_frei = FALSE;
6647 boolean links_frei = FALSE, rechts_frei = FALSE;
6648 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6649 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6650 boolean new_wall = FALSE;
6652 if (IS_ANIMATED(graphic))
6653 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6655 if (!MovDelay[ax][ay]) /* start building new wall */
6656 MovDelay[ax][ay] = 6;
6658 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6661 if (MovDelay[ax][ay])
6665 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6667 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6669 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6671 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6674 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6675 element == EL_EXPANDABLE_WALL_ANY)
6679 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6680 Store[ax][ay-1] = element;
6681 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6682 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6683 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6684 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6689 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6690 Store[ax][ay+1] = element;
6691 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6692 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6693 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6694 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6699 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6700 element == EL_EXPANDABLE_WALL_ANY ||
6701 element == EL_EXPANDABLE_WALL)
6705 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6706 Store[ax-1][ay] = element;
6707 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6708 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6709 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6710 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6716 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6717 Store[ax+1][ay] = element;
6718 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6719 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6720 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6721 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6726 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6727 DrawLevelField(ax, ay);
6729 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6731 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6732 unten_massiv = TRUE;
6733 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6734 links_massiv = TRUE;
6735 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6736 rechts_massiv = TRUE;
6738 if (((oben_massiv && unten_massiv) ||
6739 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6740 element == EL_EXPANDABLE_WALL) &&
6741 ((links_massiv && rechts_massiv) ||
6742 element == EL_EXPANDABLE_WALL_VERTICAL))
6743 Feld[ax][ay] = EL_WALL;
6746 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6749 void CheckForDragon(int x, int y)
6752 boolean dragon_found = FALSE;
6753 static int xy[4][2] =
6761 for (i = 0; i < NUM_DIRECTIONS; i++)
6763 for (j = 0; j < 4; j++)
6765 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6767 if (IN_LEV_FIELD(xx, yy) &&
6768 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6770 if (Feld[xx][yy] == EL_DRAGON)
6771 dragon_found = TRUE;
6780 for (i = 0; i < NUM_DIRECTIONS; i++)
6782 for (j = 0; j < 3; j++)
6784 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6786 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6788 Feld[xx][yy] = EL_EMPTY;
6789 DrawLevelField(xx, yy);
6798 static void InitBuggyBase(int x, int y)
6800 int element = Feld[x][y];
6801 int activating_delay = FRAMES_PER_SECOND / 4;
6804 (element == EL_SP_BUGGY_BASE ?
6805 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6806 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6808 element == EL_SP_BUGGY_BASE_ACTIVE ?
6809 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6812 static void WarnBuggyBase(int x, int y)
6815 static int xy[4][2] =
6823 for (i = 0; i < NUM_DIRECTIONS; i++)
6825 int xx = x + xy[i][0], yy = y + xy[i][1];
6827 if (IS_PLAYER(xx, yy))
6829 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6836 static void InitTrap(int x, int y)
6838 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6841 static void ActivateTrap(int x, int y)
6843 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6846 static void ChangeActiveTrap(int x, int y)
6848 int graphic = IMG_TRAP_ACTIVE;
6850 /* if new animation frame was drawn, correct crumbled sand border */
6851 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6852 DrawLevelFieldCrumbledSand(x, y);
6855 static int getSpecialActionElement(int element, int number, int base_element)
6857 return (element != EL_EMPTY ? element :
6858 number != -1 ? base_element + number - 1 :
6862 static int getModifiedActionNumber(int value_old, int operator, int operand,
6863 int value_min, int value_max)
6865 int value_new = (operator == CA_MODE_SET ? operand :
6866 operator == CA_MODE_ADD ? value_old + operand :
6867 operator == CA_MODE_SUBTRACT ? value_old - operand :
6868 operator == CA_MODE_MULTIPLY ? value_old * operand :
6869 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6870 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6873 return (value_new < value_min ? value_min :
6874 value_new > value_max ? value_max :
6878 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6880 struct ElementInfo *ei = &element_info[element];
6881 struct ElementChangeInfo *change = &ei->change_page[page];
6882 int action_type = change->action_type;
6883 int action_mode = change->action_mode;
6884 int action_arg = change->action_arg;
6887 if (!change->has_action)
6890 /* ---------- determine action paramater values -------------------------- */
6892 int level_time_value =
6893 (level.time > 0 ? TimeLeft :
6896 int action_arg_element =
6897 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6898 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6899 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6902 int action_arg_direction =
6903 (action_arg >= CA_ARG_DIRECTION_LEFT &&
6904 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
6905 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6906 change->actual_trigger_side :
6907 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
6908 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6911 int action_arg_number_min =
6912 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6915 int action_arg_number_max =
6916 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6917 action_type == CA_SET_LEVEL_GEMS ? 999 :
6918 action_type == CA_SET_LEVEL_TIME ? 9999 :
6919 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6920 action_type == CA_SET_CE_SCORE ? 9999 :
6921 action_type == CA_SET_CE_VALUE ? 9999 :
6924 int action_arg_number_reset =
6925 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6926 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6927 action_type == CA_SET_LEVEL_TIME ? level.time :
6928 action_type == CA_SET_LEVEL_SCORE ? 0 :
6929 action_type == CA_SET_CE_SCORE ? 0 :
6931 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6933 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6937 int action_arg_number =
6938 (action_arg <= CA_ARG_MAX ? action_arg :
6939 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
6940 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6941 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6942 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6943 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6944 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6945 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6946 #if USE_NEW_CUSTOM_VALUE
6947 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6949 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6951 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
6952 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
6953 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
6954 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
6955 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6956 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6959 int action_arg_number_old =
6960 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6961 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6962 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6963 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6964 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6967 int action_arg_number_new =
6968 getModifiedActionNumber(action_arg_number_old,
6969 action_mode, action_arg_number,
6970 action_arg_number_min, action_arg_number_max);
6972 int trigger_player_bits =
6973 (change->actual_trigger_player >= EL_PLAYER_1 &&
6974 change->actual_trigger_player <= EL_PLAYER_4 ?
6975 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6978 int action_arg_player_bits =
6979 (action_arg >= CA_ARG_PLAYER_1 &&
6980 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6981 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
6984 /* ---------- execute action -------------------------------------------- */
6993 /* ---------- level actions ------------------------------------------- */
6995 case CA_RESTART_LEVEL:
6997 game.restart_level = TRUE;
7002 case CA_SHOW_ENVELOPE:
7004 int element = getSpecialActionElement(action_arg_element,
7005 action_arg_number, EL_ENVELOPE_1);
7007 if (IS_ENVELOPE(element))
7008 local_player->show_envelope = element;
7013 case CA_SET_LEVEL_TIME:
7015 if (level.time > 0) /* only modify limited time value */
7017 TimeLeft = action_arg_number_new;
7019 DrawGameValue_Time(TimeLeft);
7021 if (!TimeLeft && setup.time_limit)
7022 for (i = 0; i < MAX_PLAYERS; i++)
7023 KillPlayer(&stored_player[i]);
7029 case CA_SET_LEVEL_SCORE:
7031 local_player->score = action_arg_number_new;
7033 DrawGameValue_Score(local_player->score);
7038 case CA_SET_LEVEL_GEMS:
7040 local_player->gems_still_needed = action_arg_number_new;
7042 DrawGameValue_Emeralds(local_player->gems_still_needed);
7047 case CA_SET_LEVEL_GRAVITY:
7049 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7050 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7051 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7056 case CA_SET_LEVEL_WIND:
7058 game.wind_direction = action_arg_direction;
7063 /* ---------- player actions ------------------------------------------ */
7065 case CA_MOVE_PLAYER:
7067 /* automatically move to the next field in specified direction */
7068 for (i = 0; i < MAX_PLAYERS; i++)
7069 if (trigger_player_bits & (1 << i))
7070 stored_player[i].programmed_action = action_arg_direction;
7075 case CA_EXIT_PLAYER:
7077 for (i = 0; i < MAX_PLAYERS; i++)
7078 if (action_arg_player_bits & (1 << i))
7079 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7084 case CA_KILL_PLAYER:
7086 for (i = 0; i < MAX_PLAYERS; i++)
7087 if (action_arg_player_bits & (1 << i))
7088 KillPlayer(&stored_player[i]);
7093 case CA_SET_PLAYER_KEYS:
7095 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7096 int element = getSpecialActionElement(action_arg_element,
7097 action_arg_number, EL_KEY_1);
7099 if (IS_KEY(element))
7101 for (i = 0; i < MAX_PLAYERS; i++)
7103 if (trigger_player_bits & (1 << i))
7105 stored_player[i].key[KEY_NR(element)] = key_state;
7107 DrawGameValue_Keys(stored_player[i].key);
7109 redraw_mask |= REDRAW_DOOR_1;
7117 case CA_SET_PLAYER_SPEED:
7119 for (i = 0; i < MAX_PLAYERS; i++)
7121 if (trigger_player_bits & (1 << i))
7123 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7125 if (action_arg == CA_ARG_SPEED_SLOWER ||
7126 action_arg == CA_ARG_SPEED_FASTER)
7128 action_arg_number = 2;
7129 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7134 getModifiedActionNumber(move_stepsize,
7137 action_arg_number_min,
7138 action_arg_number_max);
7140 /* make sure that value is power of 2 */
7141 move_stepsize = (1 << log_2(move_stepsize));
7143 /* do no immediately change -- the player might just be moving */
7144 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7146 stored_player[i].cannot_move =
7147 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7154 case CA_SET_PLAYER_SHIELD:
7156 for (i = 0; i < MAX_PLAYERS; i++)
7158 if (trigger_player_bits & (1 << i))
7160 if (action_arg == CA_ARG_SHIELD_OFF)
7162 stored_player[i].shield_normal_time_left = 0;
7163 stored_player[i].shield_deadly_time_left = 0;
7165 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7167 stored_player[i].shield_normal_time_left = 999999;
7169 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7171 stored_player[i].shield_normal_time_left = 999999;
7172 stored_player[i].shield_deadly_time_left = 999999;
7180 case CA_SET_PLAYER_ARTWORK:
7182 for (i = 0; i < MAX_PLAYERS; i++)
7184 if (trigger_player_bits & (1 << i))
7186 int artwork_element = action_arg_element;
7188 if (action_arg == CA_ARG_ELEMENT_RESET)
7190 (level.use_artwork_element[i] ? level.artwork_element[i] :
7191 stored_player[i].element_nr);
7193 stored_player[i].artwork_element = artwork_element;
7195 SetPlayerWaiting(&stored_player[i], FALSE);
7197 /* set number of special actions for bored and sleeping animation */
7198 stored_player[i].num_special_action_bored =
7199 get_num_special_action(artwork_element,
7200 ACTION_BORING_1, ACTION_BORING_LAST);
7201 stored_player[i].num_special_action_sleeping =
7202 get_num_special_action(artwork_element,
7203 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7210 /* ---------- CE actions ---------------------------------------------- */
7212 case CA_SET_CE_SCORE:
7214 ei->collect_score = action_arg_number_new;
7219 case CA_SET_CE_VALUE:
7221 #if USE_NEW_CUSTOM_VALUE
7222 int last_custom_value = CustomValue[x][y];
7224 CustomValue[x][y] = action_arg_number_new;
7227 printf("::: Count == %d\n", CustomValue[x][y]);
7230 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7233 printf("::: CE_VALUE_GETS_ZERO\n");
7236 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7237 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7249 static void ChangeElementNowExt(struct ElementChangeInfo *change,
7250 int x, int y, int target_element)
7252 int previous_move_direction = MovDir[x][y];
7253 #if USE_NEW_CUSTOM_VALUE
7254 int last_ce_value = CustomValue[x][y];
7256 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7257 IS_WALKABLE(Feld[x][y]));
7259 /* check if element under player changes from accessible to unaccessible
7260 (needed for special case of dropping element which then changes) */
7261 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7262 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7270 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7271 RemoveMovingField(x, y);
7275 Feld[x][y] = target_element;
7277 ResetGfxAnimation(x, y);
7278 ResetRandomAnimationValue(x, y);
7280 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7281 MovDir[x][y] = previous_move_direction;
7283 #if USE_NEW_CUSTOM_VALUE
7284 if (element_info[Feld[x][y]].use_last_ce_value)
7285 CustomValue[x][y] = last_ce_value;
7288 InitField_WithBug1(x, y, FALSE);
7290 DrawLevelField(x, y);
7292 if (GFX_CRUMBLED(Feld[x][y]))
7293 DrawLevelFieldCrumbledSandNeighbours(x, y);
7296 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7297 if (ELEM_IS_PLAYER(target_element))
7298 RelocatePlayer(x, y, target_element);
7300 ChangeCount[x][y]++; /* count number of changes in the same frame */
7302 TestIfBadThingTouchesPlayer(x, y);
7303 TestIfPlayerTouchesCustomElement(x, y);
7304 TestIfElementTouchesCustomElement(x, y);
7307 static boolean ChangeElementNow(int x, int y, int element, int page)
7309 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7311 int old_element = Feld[x][y];
7313 /* always use default change event to prevent running into a loop */
7314 if (ChangeEvent[x][y] == -1)
7315 ChangeEvent[x][y] = CE_DELAY;
7317 if (ChangeEvent[x][y] == CE_DELAY)
7319 /* reset actual trigger element, trigger player and action element */
7320 change->actual_trigger_element = EL_EMPTY;
7321 change->actual_trigger_player = EL_PLAYER_1;
7322 change->actual_trigger_side = CH_SIDE_NONE;
7323 change->actual_trigger_ce_value = 0;
7326 /* do not change elements more than a specified maximum number of changes */
7327 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7330 ChangeCount[x][y]++; /* count number of changes in the same frame */
7332 if (change->explode)
7339 if (change->use_target_content)
7341 boolean complete_replace = TRUE;
7342 boolean can_replace[3][3];
7345 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7348 boolean is_walkable;
7349 boolean is_diggable;
7350 boolean is_collectible;
7351 boolean is_removable;
7352 boolean is_destructible;
7353 int ex = x + xx - 1;
7354 int ey = y + yy - 1;
7355 int content_element = change->target_content.e[xx][yy];
7358 can_replace[xx][yy] = TRUE;
7360 if (ex == x && ey == y) /* do not check changing element itself */
7363 if (content_element == EL_EMPTY_SPACE)
7365 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7370 if (!IN_LEV_FIELD(ex, ey))
7372 can_replace[xx][yy] = FALSE;
7373 complete_replace = FALSE;
7380 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7381 e = MovingOrBlocked2Element(ex, ey);
7383 is_empty = (IS_FREE(ex, ey) ||
7384 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7386 is_walkable = (is_empty || IS_WALKABLE(e));
7387 is_diggable = (is_empty || IS_DIGGABLE(e));
7388 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7389 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7390 is_removable = (is_diggable || is_collectible);
7392 can_replace[xx][yy] =
7393 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7394 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7395 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7396 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7397 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7398 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7399 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7401 if (!can_replace[xx][yy])
7402 complete_replace = FALSE;
7405 if (!change->only_if_complete || complete_replace)
7407 boolean something_has_changed = FALSE;
7409 if (change->only_if_complete && change->use_random_replace &&
7410 RND(100) < change->random_percentage)
7413 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7415 int ex = x + xx - 1;
7416 int ey = y + yy - 1;
7417 int content_element;
7419 if (can_replace[xx][yy] && (!change->use_random_replace ||
7420 RND(100) < change->random_percentage))
7422 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7423 RemoveMovingField(ex, ey);
7425 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7427 content_element = change->target_content.e[xx][yy];
7428 target_element = GET_TARGET_ELEMENT(content_element, change);
7430 ChangeElementNowExt(change, ex, ey, target_element);
7432 something_has_changed = TRUE;
7434 /* for symmetry reasons, freeze newly created border elements */
7435 if (ex != x || ey != y)
7436 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7440 if (something_has_changed)
7442 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7443 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7449 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7451 ChangeElementNowExt(change, x, y, target_element);
7453 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7454 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7457 /* this uses direct change before indirect change */
7458 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7463 #if USE_NEW_DELAYED_ACTION
7465 static void ChangeElement(int x, int y, int page)
7467 int element = MovingOrBlocked2Element(x, y);
7468 struct ElementInfo *ei = &element_info[element];
7469 struct ElementChangeInfo *change = &ei->change_page[page];
7472 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7473 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7476 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7477 x, y, element, element_info[element].token_name);
7478 printf("ChangeElement(): This should never happen!\n");
7483 /* this can happen with classic bombs on walkable, changing elements */
7484 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7487 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7488 ChangeDelay[x][y] = 0;
7494 if (ChangeDelay[x][y] == 0) /* initialize element change */
7496 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7498 if (change->can_change)
7500 ResetGfxAnimation(x, y);
7501 ResetRandomAnimationValue(x, y);
7503 if (change->pre_change_function)
7504 change->pre_change_function(x, y);
7508 ChangeDelay[x][y]--;
7510 if (ChangeDelay[x][y] != 0) /* continue element change */
7512 if (change->can_change)
7514 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7516 if (IS_ANIMATED(graphic))
7517 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7519 if (change->change_function)
7520 change->change_function(x, y);
7523 else /* finish element change */
7525 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7527 page = ChangePage[x][y];
7528 ChangePage[x][y] = -1;
7530 change = &ei->change_page[page];
7533 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7535 ChangeDelay[x][y] = 1; /* try change after next move step */
7536 ChangePage[x][y] = page; /* remember page to use for change */
7541 if (change->can_change)
7543 if (ChangeElementNow(x, y, element, page))
7545 if (change->post_change_function)
7546 change->post_change_function(x, y);
7550 if (change->has_action)
7551 ExecuteCustomElementAction(x, y, element, page);
7557 static void ChangeElement(int x, int y, int page)
7559 int element = MovingOrBlocked2Element(x, y);
7560 struct ElementInfo *ei = &element_info[element];
7561 struct ElementChangeInfo *change = &ei->change_page[page];
7564 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7567 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7568 x, y, element, element_info[element].token_name);
7569 printf("ChangeElement(): This should never happen!\n");
7574 /* this can happen with classic bombs on walkable, changing elements */
7575 if (!CAN_CHANGE(element))
7578 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7579 ChangeDelay[x][y] = 0;
7585 if (ChangeDelay[x][y] == 0) /* initialize element change */
7587 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7589 ResetGfxAnimation(x, y);
7590 ResetRandomAnimationValue(x, y);
7592 if (change->pre_change_function)
7593 change->pre_change_function(x, y);
7596 ChangeDelay[x][y]--;
7598 if (ChangeDelay[x][y] != 0) /* continue element change */
7600 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7602 if (IS_ANIMATED(graphic))
7603 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7605 if (change->change_function)
7606 change->change_function(x, y);
7608 else /* finish element change */
7610 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7612 page = ChangePage[x][y];
7613 ChangePage[x][y] = -1;
7615 change = &ei->change_page[page];
7618 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7620 ChangeDelay[x][y] = 1; /* try change after next move step */
7621 ChangePage[x][y] = page; /* remember page to use for change */
7626 if (ChangeElementNow(x, y, element, page))
7628 if (change->post_change_function)
7629 change->post_change_function(x, y);
7636 static boolean CheckTriggeredElementChangeExt(int x, int y,
7637 int trigger_element,
7643 boolean change_done_any = FALSE;
7644 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7647 if (!(trigger_events[trigger_element][trigger_event]))
7650 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7652 int element = EL_CUSTOM_START + i;
7653 boolean change_done = FALSE;
7656 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7657 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7660 for (p = 0; p < element_info[element].num_change_pages; p++)
7662 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7664 if (change->can_change_or_has_action &&
7665 change->has_event[trigger_event] &&
7666 change->trigger_side & trigger_side &&
7667 change->trigger_player & trigger_player &&
7668 change->trigger_page & trigger_page_bits &&
7669 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7671 change->actual_trigger_element = trigger_element;
7672 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7673 change->actual_trigger_side = trigger_side;
7674 change->actual_trigger_ce_value = CustomValue[x][y];
7676 if ((change->can_change && !change_done) || change->has_action)
7680 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7682 if (Feld[x][y] == element)
7684 if (change->can_change && !change_done)
7686 ChangeDelay[x][y] = 1;
7687 ChangeEvent[x][y] = trigger_event;
7688 ChangeElement(x, y, p);
7690 #if USE_NEW_DELAYED_ACTION
7691 else if (change->has_action)
7693 ExecuteCustomElementAction(x, y, element, p);
7694 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7697 if (change->has_action)
7699 ExecuteCustomElementAction(x, y, element, p);
7700 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7706 if (change->can_change)
7709 change_done_any = TRUE;
7716 return change_done_any;
7719 static boolean CheckElementChangeExt(int x, int y,
7721 int trigger_element,
7726 boolean change_done = FALSE;
7729 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7730 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7733 if (Feld[x][y] == EL_BLOCKED)
7735 Blocked2Moving(x, y, &x, &y);
7736 element = Feld[x][y];
7739 if (Feld[x][y] != element) /* check if element has already changed */
7742 for (p = 0; p < element_info[element].num_change_pages; p++)
7744 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7746 boolean check_trigger_element =
7747 (trigger_event == CE_TOUCHING_X ||
7748 trigger_event == CE_HITTING_X ||
7749 trigger_event == CE_HIT_BY_X);
7751 if (change->can_change_or_has_action &&
7752 change->has_event[trigger_event] &&
7753 change->trigger_side & trigger_side &&
7754 change->trigger_player & trigger_player &&
7755 (!check_trigger_element ||
7756 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7758 change->actual_trigger_element = trigger_element;
7759 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7760 change->actual_trigger_side = trigger_side;
7761 change->actual_trigger_ce_value = CustomValue[x][y];
7763 /* special case: trigger element not at (x,y) position for some events */
7764 if (check_trigger_element)
7776 { 0, 0 }, { 0, 0 }, { 0, 0 },
7780 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
7781 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
7783 change->actual_trigger_ce_value = CustomValue[xx][yy];
7786 if (change->can_change && !change_done)
7788 ChangeDelay[x][y] = 1;
7789 ChangeEvent[x][y] = trigger_event;
7790 ChangeElement(x, y, p);
7794 #if USE_NEW_DELAYED_ACTION
7795 else if (change->has_action)
7797 ExecuteCustomElementAction(x, y, element, p);
7798 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7801 if (change->has_action)
7803 ExecuteCustomElementAction(x, y, element, p);
7804 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7813 static void PlayPlayerSound(struct PlayerInfo *player)
7815 int jx = player->jx, jy = player->jy;
7816 int sound_element = player->artwork_element;
7817 int last_action = player->last_action_waiting;
7818 int action = player->action_waiting;
7820 if (player->is_waiting)
7822 if (action != last_action)
7823 PlayLevelSoundElementAction(jx, jy, sound_element, action);
7825 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
7829 if (action != last_action)
7830 StopSound(element_info[sound_element].sound[last_action]);
7832 if (last_action == ACTION_SLEEPING)
7833 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
7837 static void PlayAllPlayersSound()
7841 for (i = 0; i < MAX_PLAYERS; i++)
7842 if (stored_player[i].active)
7843 PlayPlayerSound(&stored_player[i]);
7846 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7848 boolean last_waiting = player->is_waiting;
7849 int move_dir = player->MovDir;
7851 player->last_action_waiting = player->action_waiting;
7855 if (!last_waiting) /* not waiting -> waiting */
7857 player->is_waiting = TRUE;
7859 player->frame_counter_bored =
7861 game.player_boring_delay_fixed +
7862 SimpleRND(game.player_boring_delay_random);
7863 player->frame_counter_sleeping =
7865 game.player_sleeping_delay_fixed +
7866 SimpleRND(game.player_sleeping_delay_random);
7868 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7871 if (game.player_sleeping_delay_fixed +
7872 game.player_sleeping_delay_random > 0 &&
7873 player->anim_delay_counter == 0 &&
7874 player->post_delay_counter == 0 &&
7875 FrameCounter >= player->frame_counter_sleeping)
7876 player->is_sleeping = TRUE;
7877 else if (game.player_boring_delay_fixed +
7878 game.player_boring_delay_random > 0 &&
7879 FrameCounter >= player->frame_counter_bored)
7880 player->is_bored = TRUE;
7882 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7883 player->is_bored ? ACTION_BORING :
7886 if (player->is_sleeping)
7888 if (player->num_special_action_sleeping > 0)
7890 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7892 int last_special_action = player->special_action_sleeping;
7893 int num_special_action = player->num_special_action_sleeping;
7894 int special_action =
7895 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7896 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7897 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7898 last_special_action + 1 : ACTION_SLEEPING);
7899 int special_graphic =
7900 el_act_dir2img(player->artwork_element, special_action, move_dir);
7902 player->anim_delay_counter =
7903 graphic_info[special_graphic].anim_delay_fixed +
7904 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7905 player->post_delay_counter =
7906 graphic_info[special_graphic].post_delay_fixed +
7907 SimpleRND(graphic_info[special_graphic].post_delay_random);
7909 player->special_action_sleeping = special_action;
7912 if (player->anim_delay_counter > 0)
7914 player->action_waiting = player->special_action_sleeping;
7915 player->anim_delay_counter--;
7917 else if (player->post_delay_counter > 0)
7919 player->post_delay_counter--;
7923 else if (player->is_bored)
7925 if (player->num_special_action_bored > 0)
7927 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7929 int special_action =
7930 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7931 int special_graphic =
7932 el_act_dir2img(player->artwork_element, special_action, move_dir);
7934 player->anim_delay_counter =
7935 graphic_info[special_graphic].anim_delay_fixed +
7936 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7937 player->post_delay_counter =
7938 graphic_info[special_graphic].post_delay_fixed +
7939 SimpleRND(graphic_info[special_graphic].post_delay_random);
7941 player->special_action_bored = special_action;
7944 if (player->anim_delay_counter > 0)
7946 player->action_waiting = player->special_action_bored;
7947 player->anim_delay_counter--;
7949 else if (player->post_delay_counter > 0)
7951 player->post_delay_counter--;
7956 else if (last_waiting) /* waiting -> not waiting */
7958 player->is_waiting = FALSE;
7959 player->is_bored = FALSE;
7960 player->is_sleeping = FALSE;
7962 player->frame_counter_bored = -1;
7963 player->frame_counter_sleeping = -1;
7965 player->anim_delay_counter = 0;
7966 player->post_delay_counter = 0;
7968 player->action_waiting = ACTION_DEFAULT;
7970 player->special_action_bored = ACTION_DEFAULT;
7971 player->special_action_sleeping = ACTION_DEFAULT;
7975 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7977 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7978 int left = player_action & JOY_LEFT;
7979 int right = player_action & JOY_RIGHT;
7980 int up = player_action & JOY_UP;
7981 int down = player_action & JOY_DOWN;
7982 int button1 = player_action & JOY_BUTTON_1;
7983 int button2 = player_action & JOY_BUTTON_2;
7984 int dx = (left ? -1 : right ? 1 : 0);
7985 int dy = (up ? -1 : down ? 1 : 0);
7987 if (!player->active || tape.pausing)
7993 snapped = SnapField(player, dx, dy);
7997 dropped = DropElement(player);
7999 moved = MovePlayer(player, dx, dy);
8002 if (tape.single_step && tape.recording && !tape.pausing)
8004 if (button1 || (dropped && !moved))
8006 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8007 SnapField(player, 0, 0); /* stop snapping */
8011 SetPlayerWaiting(player, FALSE);
8013 return player_action;
8017 /* no actions for this player (no input at player's configured device) */
8019 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8020 SnapField(player, 0, 0);
8021 CheckGravityMovementWhenNotMoving(player);
8023 if (player->MovPos == 0)
8024 SetPlayerWaiting(player, TRUE);
8026 if (player->MovPos == 0) /* needed for tape.playing */
8027 player->is_moving = FALSE;
8029 player->is_dropping = FALSE;
8035 void AdvanceFrameAndPlayerCounters(int player_nr)
8039 /* advance frame counters (global frame counter and time frame counter) */
8043 /* advance player counters (counters for move delay, move animation etc.) */
8044 for (i = 0; i < MAX_PLAYERS; i++)
8046 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8047 int move_delay_value = stored_player[i].move_delay_value;
8048 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8050 if (!advance_player_counters) /* not all players may be affected */
8053 #if USE_NEW_PLAYER_ANIM
8054 if (move_frames == 0) /* less than one move per game frame */
8056 int stepsize = TILEX / move_delay_value;
8057 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8058 int count = (stored_player[i].is_moving ?
8059 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8061 if (count % delay == 0)
8066 stored_player[i].Frame += move_frames;
8068 if (stored_player[i].MovPos != 0)
8069 stored_player[i].StepFrame += move_frames;
8071 if (stored_player[i].move_delay > 0)
8072 stored_player[i].move_delay--;
8074 /* due to bugs in previous versions, counter must count up, not down */
8075 if (stored_player[i].push_delay != -1)
8076 stored_player[i].push_delay++;
8078 if (stored_player[i].drop_delay > 0)
8079 stored_player[i].drop_delay--;
8085 static unsigned long game_frame_delay = 0;
8086 unsigned long game_frame_delay_value;
8087 int magic_wall_x = 0, magic_wall_y = 0;
8088 int i, x, y, element, graphic;
8089 byte *recorded_player_action;
8090 byte summarized_player_action = 0;
8091 byte tape_action[MAX_PLAYERS];
8093 if (game_status != GAME_MODE_PLAYING)
8096 game_frame_delay_value =
8097 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8099 if (tape.playing && tape.warp_forward && !tape.pausing)
8100 game_frame_delay_value = 0;
8102 /* ---------- main game synchronization point ---------- */
8104 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8106 if (network_playing && !network_player_action_received)
8108 /* try to get network player actions in time */
8110 #if defined(NETWORK_AVALIABLE)
8111 /* last chance to get network player actions without main loop delay */
8115 /* game was quit by network peer */
8116 if (game_status != GAME_MODE_PLAYING)
8119 if (!network_player_action_received)
8120 return; /* failed to get network player actions in time */
8126 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8129 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8130 if (recorded_player_action == NULL && tape.pausing)
8134 for (i = 0; i < MAX_PLAYERS; i++)
8136 summarized_player_action |= stored_player[i].action;
8138 if (!network_playing)
8139 stored_player[i].effective_action = stored_player[i].action;
8142 #if defined(NETWORK_AVALIABLE)
8143 if (network_playing)
8144 SendToServer_MovePlayer(summarized_player_action);
8147 if (!options.network && !setup.team_mode)
8148 local_player->effective_action = summarized_player_action;
8150 if (recorded_player_action != NULL)
8151 for (i = 0; i < MAX_PLAYERS; i++)
8152 stored_player[i].effective_action = recorded_player_action[i];
8154 for (i = 0; i < MAX_PLAYERS; i++)
8156 tape_action[i] = stored_player[i].effective_action;
8158 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8159 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8162 /* only save actions from input devices, but not programmed actions */
8164 TapeRecordAction(tape_action);
8166 for (i = 0; i < MAX_PLAYERS; i++)
8168 int actual_player_action = stored_player[i].effective_action;
8171 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8172 - rnd_equinox_tetrachloride 048
8173 - rnd_equinox_tetrachloride_ii 096
8174 - rnd_emanuel_schmieg 002
8175 - doctor_sloan_ww 001, 020
8177 if (stored_player[i].MovPos == 0)
8178 CheckGravityMovement(&stored_player[i]);
8181 /* overwrite programmed action with tape action */
8182 if (stored_player[i].programmed_action)
8183 actual_player_action = stored_player[i].programmed_action;
8186 PlayerActions(&stored_player[i], actual_player_action);
8188 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8190 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8191 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8194 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8197 network_player_action_received = FALSE;
8199 ScrollScreen(NULL, SCROLL_GO_ON);
8201 /* for backwards compatibility, the following code emulates a fixed bug that
8202 occured when pushing elements (causing elements that just made their last
8203 pushing step to already (if possible) make their first falling step in the
8204 same game frame, which is bad); this code is also needed to use the famous
8205 "spring push bug" which is used in older levels and might be wanted to be
8206 used also in newer levels, but in this case the buggy pushing code is only
8207 affecting the "spring" element and no other elements */
8209 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8211 for (i = 0; i < MAX_PLAYERS; i++)
8213 struct PlayerInfo *player = &stored_player[i];
8217 if (player->active && player->is_pushing && player->is_moving &&
8219 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8220 Feld[x][y] == EL_SPRING))
8222 ContinueMoving(x, y);
8224 /* continue moving after pushing (this is actually a bug) */
8225 if (!IS_MOVING(x, y))
8233 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8235 ChangeCount[x][y] = 0;
8236 ChangeEvent[x][y] = -1;
8238 /* this must be handled before main playfield loop */
8239 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8242 if (MovDelay[x][y] <= 0)
8246 #if USE_NEW_SNAP_DELAY
8247 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8250 if (MovDelay[x][y] <= 0)
8253 DrawLevelField(x, y);
8255 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8261 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8263 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8264 printf("GameActions(): This should never happen!\n");
8266 ChangePage[x][y] = -1;
8271 if (WasJustMoving[x][y] > 0)
8272 WasJustMoving[x][y]--;
8273 if (WasJustFalling[x][y] > 0)
8274 WasJustFalling[x][y]--;
8275 if (CheckCollision[x][y] > 0)
8276 CheckCollision[x][y]--;
8280 /* reset finished pushing action (not done in ContinueMoving() to allow
8281 continous pushing animation for elements with zero push delay) */
8282 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8284 ResetGfxAnimation(x, y);
8285 DrawLevelField(x, y);
8289 if (IS_BLOCKED(x, y))
8293 Blocked2Moving(x, y, &oldx, &oldy);
8294 if (!IS_MOVING(oldx, oldy))
8296 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8297 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8298 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8299 printf("GameActions(): This should never happen!\n");
8305 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8307 element = Feld[x][y];
8308 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8310 if (graphic_info[graphic].anim_global_sync)
8311 GfxFrame[x][y] = FrameCounter;
8312 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8314 int old_gfx_frame = GfxFrame[x][y];
8316 GfxFrame[x][y] = CustomValue[x][y];
8319 if (GfxFrame[x][y] != old_gfx_frame)
8321 DrawLevelGraphicAnimation(x, y, graphic);
8323 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8325 int old_gfx_frame = GfxFrame[x][y];
8327 GfxFrame[x][y] = element_info[element].collect_score;
8330 if (GfxFrame[x][y] != old_gfx_frame)
8332 DrawLevelGraphicAnimation(x, y, graphic);
8335 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8336 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8337 ResetRandomAnimationValue(x, y);
8339 SetRandomAnimationValue(x, y);
8341 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8343 if (IS_INACTIVE(element))
8345 if (IS_ANIMATED(graphic))
8346 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8351 /* this may take place after moving, so 'element' may have changed */
8352 if (IS_CHANGING(x, y) &&
8353 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8355 int page = element_info[element].event_page_nr[CE_DELAY];
8357 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8361 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8365 if (element == EL_CUSTOM_255)
8366 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8370 ChangeElement(x, y, page);
8372 if (CAN_CHANGE(element))
8373 ChangeElement(x, y, page);
8375 if (HAS_ACTION(element))
8376 ExecuteCustomElementAction(x, y, element, page);
8381 element = Feld[x][y];
8382 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8385 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8389 element = Feld[x][y];
8390 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8392 if (IS_ANIMATED(graphic) &&
8395 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8397 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8398 EdelsteinFunkeln(x, y);
8400 else if ((element == EL_ACID ||
8401 element == EL_EXIT_OPEN ||
8402 element == EL_SP_EXIT_OPEN ||
8403 element == EL_SP_TERMINAL ||
8404 element == EL_SP_TERMINAL_ACTIVE ||
8405 element == EL_EXTRA_TIME ||
8406 element == EL_SHIELD_NORMAL ||
8407 element == EL_SHIELD_DEADLY) &&
8408 IS_ANIMATED(graphic))
8409 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8410 else if (IS_MOVING(x, y))
8411 ContinueMoving(x, y);
8412 else if (IS_ACTIVE_BOMB(element))
8413 CheckDynamite(x, y);
8414 else if (element == EL_AMOEBA_GROWING)
8415 AmoebeWaechst(x, y);
8416 else if (element == EL_AMOEBA_SHRINKING)
8417 AmoebaDisappearing(x, y);
8419 #if !USE_NEW_AMOEBA_CODE
8420 else if (IS_AMOEBALIVE(element))
8421 AmoebeAbleger(x, y);
8424 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8426 else if (element == EL_EXIT_CLOSED)
8428 else if (element == EL_SP_EXIT_CLOSED)
8430 else if (element == EL_EXPANDABLE_WALL_GROWING)
8432 else if (element == EL_EXPANDABLE_WALL ||
8433 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8434 element == EL_EXPANDABLE_WALL_VERTICAL ||
8435 element == EL_EXPANDABLE_WALL_ANY)
8437 else if (element == EL_FLAMES)
8438 CheckForDragon(x, y);
8439 else if (element == EL_EXPLOSION)
8440 ; /* drawing of correct explosion animation is handled separately */
8441 else if (element == EL_ELEMENT_SNAPPING)
8444 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8446 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8449 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8450 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8453 if (element == EL_CUSTOM_255 ||
8454 element == EL_CUSTOM_256)
8455 DrawLevelGraphicAnimation(x, y, graphic);
8458 if (IS_BELT_ACTIVE(element))
8459 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8461 if (game.magic_wall_active)
8463 int jx = local_player->jx, jy = local_player->jy;
8465 /* play the element sound at the position nearest to the player */
8466 if ((element == EL_MAGIC_WALL_FULL ||
8467 element == EL_MAGIC_WALL_ACTIVE ||
8468 element == EL_MAGIC_WALL_EMPTYING ||
8469 element == EL_BD_MAGIC_WALL_FULL ||
8470 element == EL_BD_MAGIC_WALL_ACTIVE ||
8471 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8472 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8480 #if USE_NEW_AMOEBA_CODE
8481 /* new experimental amoeba growth stuff */
8482 if (!(FrameCounter % 8))
8484 static unsigned long random = 1684108901;
8486 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8488 x = RND(lev_fieldx);
8489 y = RND(lev_fieldy);
8490 element = Feld[x][y];
8492 if (!IS_PLAYER(x,y) &&
8493 (element == EL_EMPTY ||
8494 CAN_GROW_INTO(element) ||
8495 element == EL_QUICKSAND_EMPTY ||
8496 element == EL_ACID_SPLASH_LEFT ||
8497 element == EL_ACID_SPLASH_RIGHT))
8499 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8500 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8501 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8502 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8503 Feld[x][y] = EL_AMOEBA_DROP;
8506 random = random * 129 + 1;
8512 if (game.explosions_delayed)
8515 game.explosions_delayed = FALSE;
8517 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8519 element = Feld[x][y];
8521 if (ExplodeField[x][y])
8522 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8523 else if (element == EL_EXPLOSION)
8524 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8526 ExplodeField[x][y] = EX_TYPE_NONE;
8529 game.explosions_delayed = TRUE;
8532 if (game.magic_wall_active)
8534 if (!(game.magic_wall_time_left % 4))
8536 int element = Feld[magic_wall_x][magic_wall_y];
8538 if (element == EL_BD_MAGIC_WALL_FULL ||
8539 element == EL_BD_MAGIC_WALL_ACTIVE ||
8540 element == EL_BD_MAGIC_WALL_EMPTYING)
8541 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8543 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8546 if (game.magic_wall_time_left > 0)
8548 game.magic_wall_time_left--;
8549 if (!game.magic_wall_time_left)
8551 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8553 element = Feld[x][y];
8555 if (element == EL_MAGIC_WALL_ACTIVE ||
8556 element == EL_MAGIC_WALL_FULL)
8558 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8559 DrawLevelField(x, y);
8561 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8562 element == EL_BD_MAGIC_WALL_FULL)
8564 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8565 DrawLevelField(x, y);
8569 game.magic_wall_active = FALSE;
8574 if (game.light_time_left > 0)
8576 game.light_time_left--;
8578 if (game.light_time_left == 0)
8579 RedrawAllLightSwitchesAndInvisibleElements();
8582 if (game.timegate_time_left > 0)
8584 game.timegate_time_left--;
8586 if (game.timegate_time_left == 0)
8587 CloseAllOpenTimegates();
8590 if (game.lenses_time_left > 0)
8592 game.lenses_time_left--;
8594 if (game.lenses_time_left == 0)
8595 RedrawAllInvisibleElementsForLenses();
8598 if (game.magnify_time_left > 0)
8600 game.magnify_time_left--;
8602 if (game.magnify_time_left == 0)
8603 RedrawAllInvisibleElementsForMagnifier();
8606 for (i = 0; i < MAX_PLAYERS; i++)
8608 struct PlayerInfo *player = &stored_player[i];
8610 if (SHIELD_ON(player))
8612 if (player->shield_deadly_time_left)
8613 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8614 else if (player->shield_normal_time_left)
8615 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8619 if (TimeFrames >= FRAMES_PER_SECOND)
8624 for (i = 0; i < MAX_PLAYERS; i++)
8626 struct PlayerInfo *player = &stored_player[i];
8628 if (SHIELD_ON(player))
8630 player->shield_normal_time_left--;
8632 if (player->shield_deadly_time_left > 0)
8633 player->shield_deadly_time_left--;
8637 if (!level.use_step_counter)
8645 if (TimeLeft <= 10 && setup.time_limit)
8646 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8648 DrawGameValue_Time(TimeLeft);
8650 if (!TimeLeft && setup.time_limit)
8651 for (i = 0; i < MAX_PLAYERS; i++)
8652 KillPlayer(&stored_player[i]);
8654 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8655 DrawGameValue_Time(TimePlayed);
8658 if (tape.recording || tape.playing)
8659 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8663 PlayAllPlayersSound();
8665 if (options.debug) /* calculate frames per second */
8667 static unsigned long fps_counter = 0;
8668 static int fps_frames = 0;
8669 unsigned long fps_delay_ms = Counter() - fps_counter;
8673 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8675 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8678 fps_counter = Counter();
8681 redraw_mask |= REDRAW_FPS;
8684 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8686 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8688 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8690 local_player->show_envelope = 0;
8693 /* use random number generator in every frame to make it less predictable */
8694 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8698 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8700 int min_x = x, min_y = y, max_x = x, max_y = y;
8703 for (i = 0; i < MAX_PLAYERS; i++)
8705 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8707 if (!stored_player[i].active || &stored_player[i] == player)
8710 min_x = MIN(min_x, jx);
8711 min_y = MIN(min_y, jy);
8712 max_x = MAX(max_x, jx);
8713 max_y = MAX(max_y, jy);
8716 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8719 static boolean AllPlayersInVisibleScreen()
8723 for (i = 0; i < MAX_PLAYERS; i++)
8725 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8727 if (!stored_player[i].active)
8730 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8737 void ScrollLevel(int dx, int dy)
8739 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8742 BlitBitmap(drawto_field, drawto_field,
8743 FX + TILEX * (dx == -1) - softscroll_offset,
8744 FY + TILEY * (dy == -1) - softscroll_offset,
8745 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8746 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8747 FX + TILEX * (dx == 1) - softscroll_offset,
8748 FY + TILEY * (dy == 1) - softscroll_offset);
8752 x = (dx == 1 ? BX1 : BX2);
8753 for (y = BY1; y <= BY2; y++)
8754 DrawScreenField(x, y);
8759 y = (dy == 1 ? BY1 : BY2);
8760 for (x = BX1; x <= BX2; x++)
8761 DrawScreenField(x, y);
8764 redraw_mask |= REDRAW_FIELD;
8767 static boolean canFallDown(struct PlayerInfo *player)
8769 int jx = player->jx, jy = player->jy;
8771 return (IN_LEV_FIELD(jx, jy + 1) &&
8772 (IS_FREE(jx, jy + 1) ||
8773 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8774 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8775 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8778 static boolean canPassField(int x, int y, int move_dir)
8780 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8781 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8782 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8785 int element = Feld[x][y];
8787 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8788 !CAN_MOVE(element) &&
8789 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8790 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8791 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8794 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8796 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8797 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8798 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8802 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8803 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8804 (IS_DIGGABLE(Feld[newx][newy]) ||
8805 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8806 canPassField(newx, newy, move_dir)));
8809 static void CheckGravityMovement(struct PlayerInfo *player)
8811 if (game.gravity && !player->programmed_action)
8813 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8814 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8815 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8816 int jx = player->jx, jy = player->jy;
8817 boolean player_is_moving_to_valid_field =
8818 (!player_is_snapping &&
8819 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8820 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8821 boolean player_can_fall_down = canFallDown(player);
8823 if (player_can_fall_down &&
8824 !player_is_moving_to_valid_field)
8825 player->programmed_action = MV_DOWN;
8829 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8831 return CheckGravityMovement(player);
8833 if (game.gravity && !player->programmed_action)
8835 int jx = player->jx, jy = player->jy;
8836 boolean field_under_player_is_free =
8837 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8838 boolean player_is_standing_on_valid_field =
8839 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8840 (IS_WALKABLE(Feld[jx][jy]) &&
8841 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8843 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8844 player->programmed_action = MV_DOWN;
8850 -----------------------------------------------------------------------------
8851 dx, dy: direction (non-diagonal) to try to move the player to
8852 real_dx, real_dy: direction as read from input device (can be diagonal)
8855 boolean MovePlayerOneStep(struct PlayerInfo *player,
8856 int dx, int dy, int real_dx, int real_dy)
8858 int jx = player->jx, jy = player->jy;
8859 int new_jx = jx + dx, new_jy = jy + dy;
8863 if (!player->active || (!dx && !dy))
8864 return MF_NO_ACTION;
8866 player->MovDir = (dx < 0 ? MV_LEFT :
8869 dy > 0 ? MV_DOWN : MV_NONE);
8871 if (!IN_LEV_FIELD(new_jx, new_jy))
8872 return MF_NO_ACTION;
8874 if (player->cannot_move)
8877 if (player->MovPos == 0)
8879 player->is_moving = FALSE;
8880 player->is_digging = FALSE;
8881 player->is_collecting = FALSE;
8882 player->is_snapping = FALSE;
8883 player->is_pushing = FALSE;
8886 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8887 SnapField(player, 0, 0);
8890 return MF_NO_ACTION;
8893 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8894 return MF_NO_ACTION;
8896 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8898 if (DONT_RUN_INTO(element))
8900 if (element == EL_ACID && dx == 0 && dy == 1)
8902 SplashAcid(new_jx, new_jy);
8903 Feld[jx][jy] = EL_PLAYER_1;
8904 InitMovingField(jx, jy, MV_DOWN);
8905 Store[jx][jy] = EL_ACID;
8906 ContinueMoving(jx, jy);
8910 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8915 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8916 if (can_move != MF_MOVING)
8919 /* check if DigField() has caused relocation of the player */
8920 if (player->jx != jx || player->jy != jy)
8921 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8923 StorePlayer[jx][jy] = 0;
8924 player->last_jx = jx;
8925 player->last_jy = jy;
8926 player->jx = new_jx;
8927 player->jy = new_jy;
8928 StorePlayer[new_jx][new_jy] = player->element_nr;
8930 if (player->move_delay_value_next != -1)
8932 player->move_delay_value = player->move_delay_value_next;
8933 player->move_delay_value_next = -1;
8937 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8939 player->step_counter++;
8941 PlayerVisit[jx][jy] = FrameCounter;
8943 ScrollPlayer(player, SCROLL_INIT);
8948 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8950 int jx = player->jx, jy = player->jy;
8951 int old_jx = jx, old_jy = jy;
8952 int moved = MF_NO_ACTION;
8954 if (!player->active)
8959 if (player->MovPos == 0)
8961 player->is_moving = FALSE;
8962 player->is_digging = FALSE;
8963 player->is_collecting = FALSE;
8964 player->is_snapping = FALSE;
8965 player->is_pushing = FALSE;
8971 if (player->move_delay > 0)
8974 player->move_delay = -1; /* set to "uninitialized" value */
8976 /* store if player is automatically moved to next field */
8977 player->is_auto_moving = (player->programmed_action != MV_NONE);
8979 /* remove the last programmed player action */
8980 player->programmed_action = 0;
8984 /* should only happen if pre-1.2 tape recordings are played */
8985 /* this is only for backward compatibility */
8987 int original_move_delay_value = player->move_delay_value;
8990 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8994 /* scroll remaining steps with finest movement resolution */
8995 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8997 while (player->MovPos)
8999 ScrollPlayer(player, SCROLL_GO_ON);
9000 ScrollScreen(NULL, SCROLL_GO_ON);
9002 AdvanceFrameAndPlayerCounters(player->index_nr);
9008 player->move_delay_value = original_move_delay_value;
9011 if (player->last_move_dir & MV_HORIZONTAL)
9013 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9014 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9018 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9019 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9025 if (moved & MF_MOVING && !ScreenMovPos &&
9026 (player == local_player || !options.network))
9028 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9029 int offset = (setup.scroll_delay ? 3 : 0);
9031 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9033 /* actual player has left the screen -- scroll in that direction */
9034 if (jx != old_jx) /* player has moved horizontally */
9035 scroll_x += (jx - old_jx);
9036 else /* player has moved vertically */
9037 scroll_y += (jy - old_jy);
9041 if (jx != old_jx) /* player has moved horizontally */
9043 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9044 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9045 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9047 /* don't scroll over playfield boundaries */
9048 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9049 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9051 /* don't scroll more than one field at a time */
9052 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9054 /* don't scroll against the player's moving direction */
9055 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9056 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9057 scroll_x = old_scroll_x;
9059 else /* player has moved vertically */
9061 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9062 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9063 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9065 /* don't scroll over playfield boundaries */
9066 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9067 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9069 /* don't scroll more than one field at a time */
9070 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9072 /* don't scroll against the player's moving direction */
9073 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9074 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9075 scroll_y = old_scroll_y;
9079 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9081 if (!options.network && !AllPlayersInVisibleScreen())
9083 scroll_x = old_scroll_x;
9084 scroll_y = old_scroll_y;
9088 ScrollScreen(player, SCROLL_INIT);
9089 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9094 player->StepFrame = 0;
9096 if (moved & MF_MOVING)
9098 if (old_jx != jx && old_jy == jy)
9099 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9100 else if (old_jx == jx && old_jy != jy)
9101 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9103 DrawLevelField(jx, jy); /* for "crumbled sand" */
9105 player->last_move_dir = player->MovDir;
9106 player->is_moving = TRUE;
9107 player->is_snapping = FALSE;
9108 player->is_switching = FALSE;
9109 player->is_dropping = FALSE;
9113 CheckGravityMovementWhenNotMoving(player);
9115 player->is_moving = FALSE;
9117 /* at this point, the player is allowed to move, but cannot move right now
9118 (e.g. because of something blocking the way) -- ensure that the player
9119 is also allowed to move in the next frame (in old versions before 3.1.1,
9120 the player was forced to wait again for eight frames before next try) */
9122 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9123 player->move_delay = 0; /* allow direct movement in the next frame */
9126 if (player->move_delay == -1) /* not yet initialized by DigField() */
9127 player->move_delay = player->move_delay_value;
9129 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9131 TestIfPlayerTouchesBadThing(jx, jy);
9132 TestIfPlayerTouchesCustomElement(jx, jy);
9135 if (!player->active)
9136 RemovePlayer(player);
9141 void ScrollPlayer(struct PlayerInfo *player, int mode)
9143 int jx = player->jx, jy = player->jy;
9144 int last_jx = player->last_jx, last_jy = player->last_jy;
9145 int move_stepsize = TILEX / player->move_delay_value;
9147 #if USE_NEW_PLAYER_SPEED
9148 if (!player->active)
9151 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9154 if (!player->active || player->MovPos == 0)
9158 if (mode == SCROLL_INIT)
9160 player->actual_frame_counter = FrameCounter;
9161 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9163 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9164 Feld[last_jx][last_jy] == EL_EMPTY)
9166 int last_field_block_delay = 0; /* start with no blocking at all */
9167 int block_delay_adjustment = player->block_delay_adjustment;
9169 /* if player blocks last field, add delay for exactly one move */
9170 if (player->block_last_field)
9172 last_field_block_delay += player->move_delay_value;
9174 /* when blocking enabled, prevent moving up despite gravity */
9175 if (game.gravity && player->MovDir == MV_UP)
9176 block_delay_adjustment = -1;
9179 /* add block delay adjustment (also possible when not blocking) */
9180 last_field_block_delay += block_delay_adjustment;
9182 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9183 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9186 #if USE_NEW_PLAYER_SPEED
9187 if (player->MovPos != 0) /* player has not yet reached destination */
9193 else if (!FrameReached(&player->actual_frame_counter, 1))
9197 printf("::: player->MovPos: %d -> %d\n",
9199 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9202 #if USE_NEW_PLAYER_SPEED
9203 if (player->MovPos != 0)
9205 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9206 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9208 /* before DrawPlayer() to draw correct player graphic for this case */
9209 if (player->MovPos == 0)
9210 CheckGravityMovement(player);
9213 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9214 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9216 /* before DrawPlayer() to draw correct player graphic for this case */
9217 if (player->MovPos == 0)
9218 CheckGravityMovement(player);
9221 if (player->MovPos == 0) /* player reached destination field */
9224 printf("::: player reached destination field\n");
9227 if (player->move_delay_reset_counter > 0)
9229 player->move_delay_reset_counter--;
9231 if (player->move_delay_reset_counter == 0)
9233 /* continue with normal speed after quickly moving through gate */
9234 HALVE_PLAYER_SPEED(player);
9236 /* be able to make the next move without delay */
9237 player->move_delay = 0;
9241 player->last_jx = jx;
9242 player->last_jy = jy;
9244 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9245 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9246 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9248 DrawPlayer(player); /* needed here only to cleanup last field */
9249 RemovePlayer(player);
9251 if (local_player->friends_still_needed == 0 ||
9252 IS_SP_ELEMENT(Feld[jx][jy]))
9253 player->LevelSolved = player->GameOver = TRUE;
9256 /* this breaks one level: "machine", level 000 */
9258 int move_direction = player->MovDir;
9259 int enter_side = MV_DIR_OPPOSITE(move_direction);
9260 int leave_side = move_direction;
9261 int old_jx = last_jx;
9262 int old_jy = last_jy;
9263 int old_element = Feld[old_jx][old_jy];
9264 int new_element = Feld[jx][jy];
9266 if (IS_CUSTOM_ELEMENT(old_element))
9267 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9269 player->index_bit, leave_side);
9271 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9273 player->index_bit, leave_side);
9275 if (IS_CUSTOM_ELEMENT(new_element))
9276 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9277 player->index_bit, enter_side);
9279 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9281 player->index_bit, enter_side);
9283 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9284 CE_MOVE_OF_X, move_direction);
9287 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9289 TestIfPlayerTouchesBadThing(jx, jy);
9290 TestIfPlayerTouchesCustomElement(jx, jy);
9292 /* needed because pushed element has not yet reached its destination,
9293 so it would trigger a change event at its previous field location */
9294 if (!player->is_pushing)
9295 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9297 if (!player->active)
9298 RemovePlayer(player);
9301 if (level.use_step_counter)
9311 if (TimeLeft <= 10 && setup.time_limit)
9312 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9314 DrawGameValue_Time(TimeLeft);
9316 if (!TimeLeft && setup.time_limit)
9317 for (i = 0; i < MAX_PLAYERS; i++)
9318 KillPlayer(&stored_player[i]);
9320 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9321 DrawGameValue_Time(TimePlayed);
9324 if (tape.single_step && tape.recording && !tape.pausing &&
9325 !player->programmed_action)
9326 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9330 void ScrollScreen(struct PlayerInfo *player, int mode)
9332 static unsigned long screen_frame_counter = 0;
9334 if (mode == SCROLL_INIT)
9336 /* set scrolling step size according to actual player's moving speed */
9337 ScrollStepSize = TILEX / player->move_delay_value;
9339 screen_frame_counter = FrameCounter;
9340 ScreenMovDir = player->MovDir;
9341 ScreenMovPos = player->MovPos;
9342 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9345 else if (!FrameReached(&screen_frame_counter, 1))
9350 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9351 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9352 redraw_mask |= REDRAW_FIELD;
9355 ScreenMovDir = MV_NONE;
9358 void TestIfPlayerTouchesCustomElement(int x, int y)
9360 static int xy[4][2] =
9367 static int trigger_sides[4][2] =
9369 /* center side border side */
9370 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9371 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9372 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9373 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9375 static int touch_dir[4] =
9382 int center_element = Feld[x][y]; /* should always be non-moving! */
9385 for (i = 0; i < NUM_DIRECTIONS; i++)
9387 int xx = x + xy[i][0];
9388 int yy = y + xy[i][1];
9389 int center_side = trigger_sides[i][0];
9390 int border_side = trigger_sides[i][1];
9393 if (!IN_LEV_FIELD(xx, yy))
9396 if (IS_PLAYER(x, y))
9398 struct PlayerInfo *player = PLAYERINFO(x, y);
9400 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9401 border_element = Feld[xx][yy]; /* may be moving! */
9402 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9403 border_element = Feld[xx][yy];
9404 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9405 border_element = MovingOrBlocked2Element(xx, yy);
9407 continue; /* center and border element do not touch */
9409 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9410 player->index_bit, border_side);
9411 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9412 CE_PLAYER_TOUCHES_X,
9413 player->index_bit, border_side);
9415 else if (IS_PLAYER(xx, yy))
9417 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9419 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9421 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9422 continue; /* center and border element do not touch */
9425 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9426 player->index_bit, center_side);
9427 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9428 CE_PLAYER_TOUCHES_X,
9429 player->index_bit, center_side);
9435 void TestIfElementTouchesCustomElement(int x, int y)
9437 static int xy[4][2] =
9444 static int trigger_sides[4][2] =
9446 /* center side border side */
9447 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9448 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9449 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9450 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9452 static int touch_dir[4] =
9459 boolean change_center_element = FALSE;
9460 int center_element = Feld[x][y]; /* should always be non-moving! */
9463 for (i = 0; i < NUM_DIRECTIONS; i++)
9465 int xx = x + xy[i][0];
9466 int yy = y + xy[i][1];
9467 int center_side = trigger_sides[i][0];
9468 int border_side = trigger_sides[i][1];
9471 if (!IN_LEV_FIELD(xx, yy))
9474 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9475 border_element = Feld[xx][yy]; /* may be moving! */
9476 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9477 border_element = Feld[xx][yy];
9478 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9479 border_element = MovingOrBlocked2Element(xx, yy);
9481 continue; /* center and border element do not touch */
9483 /* check for change of center element (but change it only once) */
9484 if (!change_center_element)
9485 change_center_element =
9486 CheckElementChangeBySide(x, y, center_element, border_element,
9487 CE_TOUCHING_X, border_side);
9489 /* check for change of border element */
9490 CheckElementChangeBySide(xx, yy, border_element, center_element,
9491 CE_TOUCHING_X, center_side);
9495 void TestIfElementHitsCustomElement(int x, int y, int direction)
9497 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9498 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9499 int hitx = x + dx, hity = y + dy;
9500 int hitting_element = Feld[x][y];
9501 int touched_element;
9503 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9506 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9507 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9509 if (IN_LEV_FIELD(hitx, hity))
9511 int opposite_direction = MV_DIR_OPPOSITE(direction);
9512 int hitting_side = direction;
9513 int touched_side = opposite_direction;
9514 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9515 MovDir[hitx][hity] != direction ||
9516 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9522 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9523 CE_HITTING_X, touched_side);
9525 CheckElementChangeBySide(hitx, hity, touched_element,
9526 hitting_element, CE_HIT_BY_X, hitting_side);
9528 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9529 CE_HIT_BY_SOMETHING, opposite_direction);
9533 /* "hitting something" is also true when hitting the playfield border */
9534 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9535 CE_HITTING_SOMETHING, direction);
9539 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9541 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9542 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9543 int hitx = x + dx, hity = y + dy;
9544 int hitting_element = Feld[x][y];
9545 int touched_element;
9547 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9548 !IS_FREE(hitx, hity) &&
9549 (!IS_MOVING(hitx, hity) ||
9550 MovDir[hitx][hity] != direction ||
9551 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9554 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9558 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9562 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9563 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9565 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9566 EP_CAN_SMASH_EVERYTHING, direction);
9568 if (IN_LEV_FIELD(hitx, hity))
9570 int opposite_direction = MV_DIR_OPPOSITE(direction);
9571 int hitting_side = direction;
9572 int touched_side = opposite_direction;
9574 int touched_element = MovingOrBlocked2Element(hitx, hity);
9577 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9578 MovDir[hitx][hity] != direction ||
9579 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9588 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9589 CE_SMASHED_BY_SOMETHING, opposite_direction);
9591 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9592 CE_OTHER_IS_SMASHING, touched_side);
9594 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9595 CE_OTHER_GETS_SMASHED, hitting_side);
9601 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9603 int i, kill_x = -1, kill_y = -1;
9604 int bad_element = -1;
9605 static int test_xy[4][2] =
9612 static int test_dir[4] =
9620 for (i = 0; i < NUM_DIRECTIONS; i++)
9622 int test_x, test_y, test_move_dir, test_element;
9624 test_x = good_x + test_xy[i][0];
9625 test_y = good_y + test_xy[i][1];
9627 if (!IN_LEV_FIELD(test_x, test_y))
9631 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9633 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9635 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9636 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9638 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9639 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9643 bad_element = test_element;
9649 if (kill_x != -1 || kill_y != -1)
9651 if (IS_PLAYER(good_x, good_y))
9653 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9655 if (player->shield_deadly_time_left > 0 &&
9656 !IS_INDESTRUCTIBLE(bad_element))
9657 Bang(kill_x, kill_y);
9658 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9662 Bang(good_x, good_y);
9666 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9668 int i, kill_x = -1, kill_y = -1;
9669 int bad_element = Feld[bad_x][bad_y];
9670 static int test_xy[4][2] =
9677 static int touch_dir[4] =
9684 static int test_dir[4] =
9692 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9695 for (i = 0; i < NUM_DIRECTIONS; i++)
9697 int test_x, test_y, test_move_dir, test_element;
9699 test_x = bad_x + test_xy[i][0];
9700 test_y = bad_y + test_xy[i][1];
9701 if (!IN_LEV_FIELD(test_x, test_y))
9705 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9707 test_element = Feld[test_x][test_y];
9709 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9710 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9712 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9713 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9715 /* good thing is player or penguin that does not move away */
9716 if (IS_PLAYER(test_x, test_y))
9718 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9720 if (bad_element == EL_ROBOT && player->is_moving)
9721 continue; /* robot does not kill player if he is moving */
9723 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9725 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9726 continue; /* center and border element do not touch */
9733 else if (test_element == EL_PENGUIN)
9742 if (kill_x != -1 || kill_y != -1)
9744 if (IS_PLAYER(kill_x, kill_y))
9746 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9748 if (player->shield_deadly_time_left > 0 &&
9749 !IS_INDESTRUCTIBLE(bad_element))
9751 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9755 Bang(kill_x, kill_y);
9759 void TestIfPlayerTouchesBadThing(int x, int y)
9761 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9764 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9766 TestIfGoodThingHitsBadThing(x, y, move_dir);
9769 void TestIfBadThingTouchesPlayer(int x, int y)
9771 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9774 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9776 TestIfBadThingHitsGoodThing(x, y, move_dir);
9779 void TestIfFriendTouchesBadThing(int x, int y)
9781 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9784 void TestIfBadThingTouchesFriend(int x, int y)
9786 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9789 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9791 int i, kill_x = bad_x, kill_y = bad_y;
9792 static int xy[4][2] =
9800 for (i = 0; i < NUM_DIRECTIONS; i++)
9804 x = bad_x + xy[i][0];
9805 y = bad_y + xy[i][1];
9806 if (!IN_LEV_FIELD(x, y))
9809 element = Feld[x][y];
9810 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9811 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9819 if (kill_x != bad_x || kill_y != bad_y)
9823 void KillPlayer(struct PlayerInfo *player)
9825 int jx = player->jx, jy = player->jy;
9827 if (!player->active)
9830 /* remove accessible field at the player's position */
9831 Feld[jx][jy] = EL_EMPTY;
9833 /* deactivate shield (else Bang()/Explode() would not work right) */
9834 player->shield_normal_time_left = 0;
9835 player->shield_deadly_time_left = 0;
9841 static void KillPlayerUnlessEnemyProtected(int x, int y)
9843 if (!PLAYER_ENEMY_PROTECTED(x, y))
9844 KillPlayer(PLAYERINFO(x, y));
9847 static void KillPlayerUnlessExplosionProtected(int x, int y)
9849 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9850 KillPlayer(PLAYERINFO(x, y));
9853 void BuryPlayer(struct PlayerInfo *player)
9855 int jx = player->jx, jy = player->jy;
9857 if (!player->active)
9860 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
9861 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9863 player->GameOver = TRUE;
9864 RemovePlayer(player);
9867 void RemovePlayer(struct PlayerInfo *player)
9869 int jx = player->jx, jy = player->jy;
9870 int i, found = FALSE;
9872 player->present = FALSE;
9873 player->active = FALSE;
9875 if (!ExplodeField[jx][jy])
9876 StorePlayer[jx][jy] = 0;
9878 if (player->is_moving)
9879 DrawLevelField(player->last_jx, player->last_jy);
9881 for (i = 0; i < MAX_PLAYERS; i++)
9882 if (stored_player[i].active)
9886 AllPlayersGone = TRUE;
9892 #if USE_NEW_SNAP_DELAY
9893 static void setFieldForSnapping(int x, int y, int element, int direction)
9895 struct ElementInfo *ei = &element_info[element];
9896 int direction_bit = MV_DIR_BIT(direction);
9897 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9898 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9899 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9901 Feld[x][y] = EL_ELEMENT_SNAPPING;
9902 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9904 ResetGfxAnimation(x, y);
9906 GfxElement[x][y] = element;
9907 GfxAction[x][y] = action;
9908 GfxDir[x][y] = direction;
9909 GfxFrame[x][y] = -1;
9914 =============================================================================
9915 checkDiagonalPushing()
9916 -----------------------------------------------------------------------------
9917 check if diagonal input device direction results in pushing of object
9918 (by checking if the alternative direction is walkable, diggable, ...)
9919 =============================================================================
9922 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9923 int x, int y, int real_dx, int real_dy)
9925 int jx, jy, dx, dy, xx, yy;
9927 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9930 /* diagonal direction: check alternative direction */
9935 xx = jx + (dx == 0 ? real_dx : 0);
9936 yy = jy + (dy == 0 ? real_dy : 0);
9938 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9942 =============================================================================
9944 -----------------------------------------------------------------------------
9945 x, y: field next to player (non-diagonal) to try to dig to
9946 real_dx, real_dy: direction as read from input device (can be diagonal)
9947 =============================================================================
9950 int DigField(struct PlayerInfo *player,
9951 int oldx, int oldy, int x, int y,
9952 int real_dx, int real_dy, int mode)
9954 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9955 boolean player_was_pushing = player->is_pushing;
9956 int jx = oldx, jy = oldy;
9957 int dx = x - jx, dy = y - jy;
9958 int nextx = x + dx, nexty = y + dy;
9959 int move_direction = (dx == -1 ? MV_LEFT :
9960 dx == +1 ? MV_RIGHT :
9962 dy == +1 ? MV_DOWN : MV_NONE);
9963 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9964 int dig_side = MV_DIR_OPPOSITE(move_direction);
9965 int old_element = Feld[jx][jy];
9969 if (is_player) /* function can also be called by EL_PENGUIN */
9971 if (player->MovPos == 0)
9973 player->is_digging = FALSE;
9974 player->is_collecting = FALSE;
9977 if (player->MovPos == 0) /* last pushing move finished */
9978 player->is_pushing = FALSE;
9980 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9982 player->is_switching = FALSE;
9983 player->push_delay = -1;
9985 return MF_NO_ACTION;
9989 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9990 return MF_NO_ACTION;
9992 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9993 old_element = Back[jx][jy];
9995 /* in case of element dropped at player position, check background */
9996 else if (Back[jx][jy] != EL_EMPTY &&
9997 game.engine_version >= VERSION_IDENT(2,2,0,0))
9998 old_element = Back[jx][jy];
10000 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10001 return MF_NO_ACTION; /* field has no opening in this direction */
10003 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10004 return MF_NO_ACTION; /* field has no opening in this direction */
10006 element = Feld[x][y];
10007 #if USE_NEW_CUSTOM_VALUE
10010 collect_count = element_info[element].collect_count_initial;
10012 collect_count = CustomValue[x][y];
10016 collect_count = element_info[element].collect_count_initial;
10020 if (element != EL_BLOCKED &&
10021 CustomValue[x][y] != element_info[element].collect_count_initial)
10022 printf("::: %d: %d != %d\n",
10025 element_info[element].collect_count_initial);
10028 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10029 return MF_NO_ACTION;
10031 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10032 game.engine_version >= VERSION_IDENT(2,2,0,0))
10034 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10035 player->index_bit, dig_side);
10036 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10037 player->index_bit, dig_side);
10039 if (Feld[x][y] != element) /* field changed by snapping */
10042 return MF_NO_ACTION;
10045 if (game.gravity && is_player && !player->is_auto_moving &&
10046 canFallDown(player) && move_direction != MV_DOWN &&
10047 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10048 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10050 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10052 int sound_element = SND_ELEMENT(element);
10053 int sound_action = ACTION_WALKING;
10055 if (IS_RND_GATE(element))
10057 if (!player->key[RND_GATE_NR(element)])
10058 return MF_NO_ACTION;
10060 else if (IS_RND_GATE_GRAY(element))
10062 if (!player->key[RND_GATE_GRAY_NR(element)])
10063 return MF_NO_ACTION;
10065 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10067 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10068 return MF_NO_ACTION;
10070 else if (element == EL_EXIT_OPEN ||
10071 element == EL_SP_EXIT_OPEN ||
10072 element == EL_SP_EXIT_OPENING)
10074 sound_action = ACTION_PASSING; /* player is passing exit */
10076 else if (element == EL_EMPTY)
10078 sound_action = ACTION_MOVING; /* nothing to walk on */
10081 /* play sound from background or player, whatever is available */
10082 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10083 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10085 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10087 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10089 if (!ACCESS_FROM(element, opposite_direction))
10090 return MF_NO_ACTION; /* field not accessible from this direction */
10092 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10093 return MF_NO_ACTION;
10095 if (IS_EM_GATE(element))
10097 if (!player->key[EM_GATE_NR(element)])
10098 return MF_NO_ACTION;
10100 else if (IS_EM_GATE_GRAY(element))
10102 if (!player->key[EM_GATE_GRAY_NR(element)])
10103 return MF_NO_ACTION;
10105 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10107 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10108 return MF_NO_ACTION;
10110 else if (IS_SP_PORT(element))
10112 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10113 element == EL_SP_GRAVITY_PORT_RIGHT ||
10114 element == EL_SP_GRAVITY_PORT_UP ||
10115 element == EL_SP_GRAVITY_PORT_DOWN)
10116 game.gravity = !game.gravity;
10117 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10118 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10119 element == EL_SP_GRAVITY_ON_PORT_UP ||
10120 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10121 game.gravity = TRUE;
10122 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10123 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10124 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10125 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10126 game.gravity = FALSE;
10129 /* automatically move to the next field with double speed */
10130 player->programmed_action = move_direction;
10132 if (player->move_delay_reset_counter == 0)
10134 player->move_delay_reset_counter = 2; /* two double speed steps */
10136 DOUBLE_PLAYER_SPEED(player);
10139 PlayLevelSoundAction(x, y, ACTION_PASSING);
10141 else if (IS_DIGGABLE(element))
10145 if (mode != DF_SNAP)
10147 GfxElement[x][y] = GFX_ELEMENT(element);
10148 player->is_digging = TRUE;
10151 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10153 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10154 player->index_bit, dig_side);
10156 if (mode == DF_SNAP)
10158 #if USE_NEW_SNAP_DELAY
10159 if (level.block_snap_field)
10160 setFieldForSnapping(x, y, element, move_direction);
10162 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10164 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10167 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10168 player->index_bit, dig_side);
10171 else if (IS_COLLECTIBLE(element))
10175 if (is_player && mode != DF_SNAP)
10177 GfxElement[x][y] = element;
10178 player->is_collecting = TRUE;
10181 if (element == EL_SPEED_PILL)
10183 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10185 else if (element == EL_EXTRA_TIME && level.time > 0)
10187 TimeLeft += level.extra_time;
10188 DrawGameValue_Time(TimeLeft);
10190 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10192 player->shield_normal_time_left += level.shield_normal_time;
10193 if (element == EL_SHIELD_DEADLY)
10194 player->shield_deadly_time_left += level.shield_deadly_time;
10196 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10198 if (player->inventory_size < MAX_INVENTORY_SIZE)
10199 player->inventory_element[player->inventory_size++] = element;
10201 DrawGameValue_Dynamite(local_player->inventory_size);
10203 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10205 player->dynabomb_count++;
10206 player->dynabombs_left++;
10208 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10210 player->dynabomb_size++;
10212 else if (element == EL_DYNABOMB_INCREASE_POWER)
10214 player->dynabomb_xl = TRUE;
10216 else if (IS_KEY(element))
10218 player->key[KEY_NR(element)] = TRUE;
10220 DrawGameValue_Keys(player->key);
10222 redraw_mask |= REDRAW_DOOR_1;
10224 else if (IS_ENVELOPE(element))
10226 player->show_envelope = element;
10228 else if (element == EL_EMC_LENSES)
10230 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10232 RedrawAllInvisibleElementsForLenses();
10234 else if (element == EL_EMC_MAGNIFIER)
10236 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10238 RedrawAllInvisibleElementsForMagnifier();
10240 else if (IS_DROPPABLE(element) ||
10241 IS_THROWABLE(element)) /* can be collected and dropped */
10245 if (collect_count == 0)
10246 player->inventory_infinite_element = element;
10248 for (i = 0; i < collect_count; i++)
10249 if (player->inventory_size < MAX_INVENTORY_SIZE)
10250 player->inventory_element[player->inventory_size++] = element;
10252 DrawGameValue_Dynamite(local_player->inventory_size);
10254 else if (collect_count > 0)
10256 local_player->gems_still_needed -= collect_count;
10257 if (local_player->gems_still_needed < 0)
10258 local_player->gems_still_needed = 0;
10260 DrawGameValue_Emeralds(local_player->gems_still_needed);
10263 RaiseScoreElement(element);
10264 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10267 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10268 player->index_bit, dig_side);
10270 if (mode == DF_SNAP)
10272 #if USE_NEW_SNAP_DELAY
10273 if (level.block_snap_field)
10274 setFieldForSnapping(x, y, element, move_direction);
10276 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10278 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10281 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10282 player->index_bit, dig_side);
10285 else if (IS_PUSHABLE(element))
10287 if (mode == DF_SNAP && element != EL_BD_ROCK)
10288 return MF_NO_ACTION;
10290 if (CAN_FALL(element) && dy)
10291 return MF_NO_ACTION;
10293 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10294 !(element == EL_SPRING && level.use_spring_bug))
10295 return MF_NO_ACTION;
10297 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10298 ((move_direction & MV_VERTICAL &&
10299 ((element_info[element].move_pattern & MV_LEFT &&
10300 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10301 (element_info[element].move_pattern & MV_RIGHT &&
10302 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10303 (move_direction & MV_HORIZONTAL &&
10304 ((element_info[element].move_pattern & MV_UP &&
10305 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10306 (element_info[element].move_pattern & MV_DOWN &&
10307 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10308 return MF_NO_ACTION;
10310 /* do not push elements already moving away faster than player */
10311 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10312 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10313 return MF_NO_ACTION;
10315 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10317 if (player->push_delay_value == -1 || !player_was_pushing)
10318 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10320 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10322 if (player->push_delay_value == -1)
10323 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10325 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10327 if (!player->is_pushing)
10328 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10331 player->is_pushing = TRUE;
10333 if (!(IN_LEV_FIELD(nextx, nexty) &&
10334 (IS_FREE(nextx, nexty) ||
10335 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10336 IS_SB_ELEMENT(element)))))
10337 return MF_NO_ACTION;
10339 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10340 return MF_NO_ACTION;
10342 if (player->push_delay == -1) /* new pushing; restart delay */
10343 player->push_delay = 0;
10345 if (player->push_delay < player->push_delay_value &&
10346 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10347 element != EL_SPRING && element != EL_BALLOON)
10349 /* make sure that there is no move delay before next try to push */
10350 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10351 player->move_delay = 0;
10353 return MF_NO_ACTION;
10356 if (IS_SB_ELEMENT(element))
10358 if (element == EL_SOKOBAN_FIELD_FULL)
10360 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10361 local_player->sokobanfields_still_needed++;
10364 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10366 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10367 local_player->sokobanfields_still_needed--;
10370 Feld[x][y] = EL_SOKOBAN_OBJECT;
10372 if (Back[x][y] == Back[nextx][nexty])
10373 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10374 else if (Back[x][y] != 0)
10375 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10378 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10381 if (local_player->sokobanfields_still_needed == 0 &&
10382 game.emulation == EMU_SOKOBAN)
10384 player->LevelSolved = player->GameOver = TRUE;
10385 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10389 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10391 InitMovingField(x, y, move_direction);
10392 GfxAction[x][y] = ACTION_PUSHING;
10394 if (mode == DF_SNAP)
10395 ContinueMoving(x, y);
10397 MovPos[x][y] = (dx != 0 ? dx : dy);
10399 Pushed[x][y] = TRUE;
10400 Pushed[nextx][nexty] = TRUE;
10402 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10403 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10405 player->push_delay_value = -1; /* get new value later */
10407 /* check for element change _after_ element has been pushed */
10408 if (game.use_change_when_pushing_bug)
10410 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10411 player->index_bit, dig_side);
10412 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10413 player->index_bit, dig_side);
10416 else if (IS_SWITCHABLE(element))
10418 if (PLAYER_SWITCHING(player, x, y))
10420 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10421 player->index_bit, dig_side);
10426 player->is_switching = TRUE;
10427 player->switch_x = x;
10428 player->switch_y = y;
10430 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10432 if (element == EL_ROBOT_WHEEL)
10434 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10438 DrawLevelField(x, y);
10440 else if (element == EL_SP_TERMINAL)
10444 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10446 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10448 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10449 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10452 else if (IS_BELT_SWITCH(element))
10454 ToggleBeltSwitch(x, y);
10456 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10457 element == EL_SWITCHGATE_SWITCH_DOWN)
10459 ToggleSwitchgateSwitch(x, y);
10461 else if (element == EL_LIGHT_SWITCH ||
10462 element == EL_LIGHT_SWITCH_ACTIVE)
10464 ToggleLightSwitch(x, y);
10466 else if (element == EL_TIMEGATE_SWITCH)
10468 ActivateTimegateSwitch(x, y);
10470 else if (element == EL_BALLOON_SWITCH_LEFT ||
10471 element == EL_BALLOON_SWITCH_RIGHT ||
10472 element == EL_BALLOON_SWITCH_UP ||
10473 element == EL_BALLOON_SWITCH_DOWN ||
10474 element == EL_BALLOON_SWITCH_NONE ||
10475 element == EL_BALLOON_SWITCH_ANY)
10477 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10478 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10479 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10480 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10481 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10484 else if (element == EL_LAMP)
10486 Feld[x][y] = EL_LAMP_ACTIVE;
10487 local_player->lights_still_needed--;
10489 ResetGfxAnimation(x, y);
10490 DrawLevelField(x, y);
10492 else if (element == EL_TIME_ORB_FULL)
10494 Feld[x][y] = EL_TIME_ORB_EMPTY;
10496 if (level.time > 0 || level.use_time_orb_bug)
10498 TimeLeft += level.time_orb_time;
10499 DrawGameValue_Time(TimeLeft);
10502 ResetGfxAnimation(x, y);
10503 DrawLevelField(x, y);
10506 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10507 player->index_bit, dig_side);
10509 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10510 player->index_bit, dig_side);
10512 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10513 player->index_bit, dig_side);
10519 if (!PLAYER_SWITCHING(player, x, y))
10521 player->is_switching = TRUE;
10522 player->switch_x = x;
10523 player->switch_y = y;
10525 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10526 player->index_bit, dig_side);
10527 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10528 player->index_bit, dig_side);
10530 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10531 player->index_bit, dig_side);
10532 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10533 player->index_bit, dig_side);
10536 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10537 player->index_bit, dig_side);
10538 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10539 player->index_bit, dig_side);
10541 return MF_NO_ACTION;
10544 player->push_delay = -1;
10546 if (is_player) /* function can also be called by EL_PENGUIN */
10548 if (Feld[x][y] != element) /* really digged/collected something */
10549 player->is_collecting = !player->is_digging;
10555 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10557 int jx = player->jx, jy = player->jy;
10558 int x = jx + dx, y = jy + dy;
10559 int snap_direction = (dx == -1 ? MV_LEFT :
10560 dx == +1 ? MV_RIGHT :
10562 dy == +1 ? MV_DOWN : MV_NONE);
10564 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10567 if (!player->active || !IN_LEV_FIELD(x, y))
10575 if (player->MovPos == 0)
10576 player->is_pushing = FALSE;
10578 player->is_snapping = FALSE;
10580 if (player->MovPos == 0)
10582 player->is_moving = FALSE;
10583 player->is_digging = FALSE;
10584 player->is_collecting = FALSE;
10590 if (player->is_snapping)
10593 player->MovDir = snap_direction;
10595 if (player->MovPos == 0)
10597 player->is_moving = FALSE;
10598 player->is_digging = FALSE;
10599 player->is_collecting = FALSE;
10602 player->is_dropping = FALSE;
10604 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10607 player->is_snapping = TRUE;
10609 if (player->MovPos == 0)
10611 player->is_moving = FALSE;
10612 player->is_digging = FALSE;
10613 player->is_collecting = FALSE;
10616 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10617 DrawLevelField(player->last_jx, player->last_jy);
10619 DrawLevelField(x, y);
10624 boolean DropElement(struct PlayerInfo *player)
10626 int old_element, new_element;
10627 int dropx = player->jx, dropy = player->jy;
10628 int drop_direction = player->MovDir;
10629 int drop_side = drop_direction;
10630 int drop_element = (player->inventory_size > 0 ?
10631 player->inventory_element[player->inventory_size - 1] :
10632 player->inventory_infinite_element != EL_UNDEFINED ?
10633 player->inventory_infinite_element :
10634 player->dynabombs_left > 0 ?
10635 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10638 /* do not drop an element on top of another element; when holding drop key
10639 pressed without moving, dropped element must move away before the next
10640 element can be dropped (this is especially important if the next element
10641 is dynamite, which can be placed on background for historical reasons) */
10642 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10645 if (IS_THROWABLE(drop_element))
10647 dropx += GET_DX_FROM_DIR(drop_direction);
10648 dropy += GET_DY_FROM_DIR(drop_direction);
10650 if (!IN_LEV_FIELD(dropx, dropy))
10654 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10655 new_element = drop_element; /* default: no change when dropping */
10657 /* check if player is active, not moving and ready to drop */
10658 if (!player->active || player->MovPos || player->drop_delay > 0)
10661 /* check if player has anything that can be dropped */
10662 if (new_element == EL_UNDEFINED)
10665 /* check if anything can be dropped at the current position */
10666 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10669 /* collected custom elements can only be dropped on empty fields */
10670 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10673 if (old_element != EL_EMPTY)
10674 Back[dropx][dropy] = old_element; /* store old element on this field */
10676 ResetGfxAnimation(dropx, dropy);
10677 ResetRandomAnimationValue(dropx, dropy);
10679 if (player->inventory_size > 0 ||
10680 player->inventory_infinite_element != EL_UNDEFINED)
10682 if (player->inventory_size > 0)
10684 player->inventory_size--;
10686 DrawGameValue_Dynamite(local_player->inventory_size);
10688 if (new_element == EL_DYNAMITE)
10689 new_element = EL_DYNAMITE_ACTIVE;
10690 else if (new_element == EL_SP_DISK_RED)
10691 new_element = EL_SP_DISK_RED_ACTIVE;
10694 Feld[dropx][dropy] = new_element;
10696 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10697 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10698 el2img(Feld[dropx][dropy]), 0);
10700 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10702 /* needed if previous element just changed to "empty" in the last frame */
10703 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
10705 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10706 player->index_bit, drop_side);
10707 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10709 player->index_bit, drop_side);
10711 TestIfElementTouchesCustomElement(dropx, dropy);
10713 else /* player is dropping a dyna bomb */
10715 player->dynabombs_left--;
10717 Feld[dropx][dropy] = new_element;
10719 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10720 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10721 el2img(Feld[dropx][dropy]), 0);
10723 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10726 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10727 InitField_WithBug1(dropx, dropy, FALSE);
10729 new_element = Feld[dropx][dropy]; /* element might have changed */
10731 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10732 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10734 int move_direction, nextx, nexty;
10736 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10737 MovDir[dropx][dropy] = drop_direction;
10739 move_direction = MovDir[dropx][dropy];
10740 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10741 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10743 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
10744 CheckCollision[dropx][dropy] = 2;
10747 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10748 player->is_dropping = TRUE;
10750 player->drop_x = dropx;
10751 player->drop_y = dropy;
10756 /* ------------------------------------------------------------------------- */
10757 /* game sound playing functions */
10758 /* ------------------------------------------------------------------------- */
10760 static int *loop_sound_frame = NULL;
10761 static int *loop_sound_volume = NULL;
10763 void InitPlayLevelSound()
10765 int num_sounds = getSoundListSize();
10767 checked_free(loop_sound_frame);
10768 checked_free(loop_sound_volume);
10770 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10771 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10774 static void PlayLevelSound(int x, int y, int nr)
10776 int sx = SCREENX(x), sy = SCREENY(y);
10777 int volume, stereo_position;
10778 int max_distance = 8;
10779 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10781 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10782 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10785 if (!IN_LEV_FIELD(x, y) ||
10786 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10787 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10790 volume = SOUND_MAX_VOLUME;
10792 if (!IN_SCR_FIELD(sx, sy))
10794 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10795 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10797 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10800 stereo_position = (SOUND_MAX_LEFT +
10801 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10802 (SCR_FIELDX + 2 * max_distance));
10804 if (IS_LOOP_SOUND(nr))
10806 /* This assures that quieter loop sounds do not overwrite louder ones,
10807 while restarting sound volume comparison with each new game frame. */
10809 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10812 loop_sound_volume[nr] = volume;
10813 loop_sound_frame[nr] = FrameCounter;
10816 PlaySoundExt(nr, volume, stereo_position, type);
10819 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10821 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10822 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10823 y < LEVELY(BY1) ? LEVELY(BY1) :
10824 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10828 static void PlayLevelSoundAction(int x, int y, int action)
10830 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10833 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10835 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10837 if (sound_effect != SND_UNDEFINED)
10838 PlayLevelSound(x, y, sound_effect);
10841 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10844 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10846 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10847 PlayLevelSound(x, y, sound_effect);
10850 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10852 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10854 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10855 PlayLevelSound(x, y, sound_effect);
10858 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10860 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10862 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10863 StopSound(sound_effect);
10866 static void PlayLevelMusic()
10868 if (levelset.music[level_nr] != MUS_UNDEFINED)
10869 PlayMusic(levelset.music[level_nr]); /* from config file */
10871 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10874 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10876 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10881 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10885 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10889 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10893 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10897 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10901 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10905 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10908 case SAMPLE_android_clone:
10909 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10912 case SAMPLE_android_move:
10913 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10916 case SAMPLE_spring:
10917 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10921 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10925 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10928 case SAMPLE_eater_eat:
10929 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10933 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10936 case SAMPLE_collect:
10937 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10940 case SAMPLE_diamond:
10941 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10944 case SAMPLE_squash:
10945 /* !!! CHECK THIS !!! */
10947 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10949 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10953 case SAMPLE_wonderfall:
10954 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10958 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10962 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10966 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10970 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10974 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10978 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10981 case SAMPLE_wonder:
10982 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10986 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10989 case SAMPLE_exit_open:
10990 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10993 case SAMPLE_exit_leave:
10994 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10997 case SAMPLE_dynamite:
10998 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11002 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11006 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11010 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11014 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11018 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11022 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11026 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11031 void RaiseScore(int value)
11033 local_player->score += value;
11035 DrawGameValue_Score(local_player->score);
11038 void RaiseScoreElement(int element)
11043 case EL_BD_DIAMOND:
11044 case EL_EMERALD_YELLOW:
11045 case EL_EMERALD_RED:
11046 case EL_EMERALD_PURPLE:
11047 case EL_SP_INFOTRON:
11048 RaiseScore(level.score[SC_EMERALD]);
11051 RaiseScore(level.score[SC_DIAMOND]);
11054 RaiseScore(level.score[SC_CRYSTAL]);
11057 RaiseScore(level.score[SC_PEARL]);
11060 case EL_BD_BUTTERFLY:
11061 case EL_SP_ELECTRON:
11062 RaiseScore(level.score[SC_BUG]);
11065 case EL_BD_FIREFLY:
11066 case EL_SP_SNIKSNAK:
11067 RaiseScore(level.score[SC_SPACESHIP]);
11070 case EL_DARK_YAMYAM:
11071 RaiseScore(level.score[SC_YAMYAM]);
11074 RaiseScore(level.score[SC_ROBOT]);
11077 RaiseScore(level.score[SC_PACMAN]);
11080 RaiseScore(level.score[SC_NUT]);
11083 case EL_SP_DISK_RED:
11084 case EL_DYNABOMB_INCREASE_NUMBER:
11085 case EL_DYNABOMB_INCREASE_SIZE:
11086 case EL_DYNABOMB_INCREASE_POWER:
11087 RaiseScore(level.score[SC_DYNAMITE]);
11089 case EL_SHIELD_NORMAL:
11090 case EL_SHIELD_DEADLY:
11091 RaiseScore(level.score[SC_SHIELD]);
11093 case EL_EXTRA_TIME:
11094 RaiseScore(level.extra_time_score);
11108 RaiseScore(level.score[SC_KEY]);
11111 RaiseScore(element_info[element].collect_score);
11116 void RequestQuitGame(boolean ask_if_really_quit)
11118 if (AllPlayersGone ||
11119 !ask_if_really_quit ||
11120 level_editor_test_game ||
11121 Request("Do you really want to quit the game ?",
11122 REQ_ASK | REQ_STAY_CLOSED))
11124 #if defined(NETWORK_AVALIABLE)
11125 if (options.network)
11126 SendToServer_StopPlaying();
11130 game_status = GAME_MODE_MAIN;
11136 if (tape.playing && tape.deactivate_display)
11137 TapeDeactivateDisplayOff(TRUE);
11139 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11141 if (tape.playing && tape.deactivate_display)
11142 TapeDeactivateDisplayOn();
11147 /* ---------- new game button stuff ---------------------------------------- */
11149 /* graphic position values for game buttons */
11150 #define GAME_BUTTON_XSIZE 30
11151 #define GAME_BUTTON_YSIZE 30
11152 #define GAME_BUTTON_XPOS 5
11153 #define GAME_BUTTON_YPOS 215
11154 #define SOUND_BUTTON_XPOS 5
11155 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11157 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11158 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11159 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11160 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11161 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11162 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11169 } gamebutton_info[NUM_GAME_BUTTONS] =
11172 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11177 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11178 GAME_CTRL_ID_PAUSE,
11182 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11187 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11188 SOUND_CTRL_ID_MUSIC,
11189 "background music on/off"
11192 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11193 SOUND_CTRL_ID_LOOPS,
11194 "sound loops on/off"
11197 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11198 SOUND_CTRL_ID_SIMPLE,
11199 "normal sounds on/off"
11203 void CreateGameButtons()
11207 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11209 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11210 struct GadgetInfo *gi;
11213 unsigned long event_mask;
11214 int gd_xoffset, gd_yoffset;
11215 int gd_x1, gd_x2, gd_y1, gd_y2;
11218 gd_xoffset = gamebutton_info[i].x;
11219 gd_yoffset = gamebutton_info[i].y;
11220 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11221 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11223 if (id == GAME_CTRL_ID_STOP ||
11224 id == GAME_CTRL_ID_PAUSE ||
11225 id == GAME_CTRL_ID_PLAY)
11227 button_type = GD_TYPE_NORMAL_BUTTON;
11229 event_mask = GD_EVENT_RELEASED;
11230 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11231 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11235 button_type = GD_TYPE_CHECK_BUTTON;
11237 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11238 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11239 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11240 event_mask = GD_EVENT_PRESSED;
11241 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11242 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11245 gi = CreateGadget(GDI_CUSTOM_ID, id,
11246 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11247 GDI_X, DX + gd_xoffset,
11248 GDI_Y, DY + gd_yoffset,
11249 GDI_WIDTH, GAME_BUTTON_XSIZE,
11250 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11251 GDI_TYPE, button_type,
11252 GDI_STATE, GD_BUTTON_UNPRESSED,
11253 GDI_CHECKED, checked,
11254 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11255 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11256 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11257 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11258 GDI_EVENT_MASK, event_mask,
11259 GDI_CALLBACK_ACTION, HandleGameButtons,
11263 Error(ERR_EXIT, "cannot create gadget");
11265 game_gadget[id] = gi;
11269 void FreeGameButtons()
11273 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11274 FreeGadget(game_gadget[i]);
11277 static void MapGameButtons()
11281 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11282 MapGadget(game_gadget[i]);
11285 void UnmapGameButtons()
11289 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11290 UnmapGadget(game_gadget[i]);
11293 static void HandleGameButtons(struct GadgetInfo *gi)
11295 int id = gi->custom_id;
11297 if (game_status != GAME_MODE_PLAYING)
11302 case GAME_CTRL_ID_STOP:
11303 RequestQuitGame(TRUE);
11306 case GAME_CTRL_ID_PAUSE:
11307 if (options.network)
11309 #if defined(NETWORK_AVALIABLE)
11311 SendToServer_ContinuePlaying();
11313 SendToServer_PausePlaying();
11317 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11320 case GAME_CTRL_ID_PLAY:
11323 #if defined(NETWORK_AVALIABLE)
11324 if (options.network)
11325 SendToServer_ContinuePlaying();
11329 tape.pausing = FALSE;
11330 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11335 case SOUND_CTRL_ID_MUSIC:
11336 if (setup.sound_music)
11338 setup.sound_music = FALSE;
11341 else if (audio.music_available)
11343 setup.sound = setup.sound_music = TRUE;
11345 SetAudioMode(setup.sound);
11351 case SOUND_CTRL_ID_LOOPS:
11352 if (setup.sound_loops)
11353 setup.sound_loops = FALSE;
11354 else if (audio.loops_available)
11356 setup.sound = setup.sound_loops = TRUE;
11357 SetAudioMode(setup.sound);
11361 case SOUND_CTRL_ID_SIMPLE:
11362 if (setup.sound_simple)
11363 setup.sound_simple = FALSE;
11364 else if (audio.sound_available)
11366 setup.sound = setup.sound_simple = TRUE;
11367 SetAudioMode(setup.sound);