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 (TRUE * 1)
30 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
31 #define USE_NEW_COLLECT_COUNT (TRUE * USE_NEW_STUFF * 1)
39 /* for MovePlayer() */
40 #define MF_NO_ACTION 0
44 /* for ScrollPlayer() */
46 #define SCROLL_GO_ON 1
49 #define EX_PHASE_START 0
50 #define EX_TYPE_NONE 0
51 #define EX_TYPE_NORMAL (1 << 0)
52 #define EX_TYPE_CENTER (1 << 1)
53 #define EX_TYPE_BORDER (1 << 2)
54 #define EX_TYPE_CROSS (1 << 3)
55 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
57 /* special positions in the game control window (relative to control window) */
60 #define XX_EMERALDS 29
61 #define YY_EMERALDS 54
62 #define XX_DYNAMITE 29
63 #define YY_DYNAMITE 89
72 /* special positions in the game control window (relative to main window) */
73 #define DX_LEVEL (DX + XX_LEVEL)
74 #define DY_LEVEL (DY + YY_LEVEL)
75 #define DX_EMERALDS (DX + XX_EMERALDS)
76 #define DY_EMERALDS (DY + YY_EMERALDS)
77 #define DX_DYNAMITE (DX + XX_DYNAMITE)
78 #define DY_DYNAMITE (DY + YY_DYNAMITE)
79 #define DX_KEYS (DX + XX_KEYS)
80 #define DY_KEYS (DY + YY_KEYS)
81 #define DX_SCORE (DX + XX_SCORE)
82 #define DY_SCORE (DY + YY_SCORE)
83 #define DX_TIME1 (DX + XX_TIME1)
84 #define DX_TIME2 (DX + XX_TIME2)
85 #define DY_TIME (DY + YY_TIME)
87 /* values for initial player move delay (initial delay counter value) */
88 #define INITIAL_MOVE_DELAY_OFF -1
89 #define INITIAL_MOVE_DELAY_ON 0
91 /* values for player movement speed (which is in fact a delay value) */
92 #define MOVE_DELAY_NORMAL_SPEED 8
93 #define MOVE_DELAY_HIGH_SPEED 4
95 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
96 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
97 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
98 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
100 /* values for other actions */
101 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
103 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
104 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
106 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
108 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
109 RND(element_info[e].push_delay_random))
110 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
111 RND(element_info[e].drop_delay_random))
112 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
113 RND(element_info[e].move_delay_random))
114 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
115 (element_info[e].move_delay_random))
116 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
117 RND((c)->delay_random * (c)->delay_frames))
119 #define GET_TARGET_ELEMENT(e, ch) \
120 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
121 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
123 #define GET_VALID_PLAYER_ELEMENT(e) \
124 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
126 #define CAN_GROW_INTO(e) \
127 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
129 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
133 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
135 (CAN_MOVE_INTO_ACID(e) && \
136 Feld[x][y] == EL_ACID) || \
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
141 (CAN_MOVE_INTO_ACID(e) && \
142 Feld[x][y] == EL_ACID) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 (CAN_MOVE_INTO_ACID(e) && \
149 Feld[x][y] == EL_ACID) || \
150 (DONT_COLLIDE_WITH(e) && \
152 !PLAYER_ENEMY_PROTECTED(x, y))))
154 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
155 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
157 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
158 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
160 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
161 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
163 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
164 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
166 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
169 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
172 #define PIG_CAN_ENTER_FIELD(e, x, y) \
173 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
175 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
176 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
177 IS_FOOD_PENGUIN(Feld[x][y])))
178 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
181 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
182 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
184 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
187 #define GROUP_NR(e) ((e) - EL_GROUP_START)
188 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
189 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
190 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
192 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
193 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
195 #define CE_ENTER_FIELD_COND(e, x, y) \
196 (!IS_PLAYER(x, y) && \
197 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
199 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
202 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
203 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
205 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
206 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
207 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
208 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
210 /* game button identifiers */
211 #define GAME_CTRL_ID_STOP 0
212 #define GAME_CTRL_ID_PAUSE 1
213 #define GAME_CTRL_ID_PLAY 2
214 #define SOUND_CTRL_ID_MUSIC 3
215 #define SOUND_CTRL_ID_LOOPS 4
216 #define SOUND_CTRL_ID_SIMPLE 5
218 #define NUM_GAME_BUTTONS 6
221 /* forward declaration for internal use */
223 static void AdvanceFrameAndPlayerCounters(int);
225 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
226 static boolean MovePlayer(struct PlayerInfo *, int, int);
227 static void ScrollPlayer(struct PlayerInfo *, int);
228 static void ScrollScreen(struct PlayerInfo *, int);
230 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
232 static void InitBeltMovement(void);
233 static void CloseAllOpenTimegates(void);
234 static void CheckGravityMovement(struct PlayerInfo *);
235 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
236 static void KillHeroUnlessEnemyProtected(int, int);
237 static void KillHeroUnlessExplosionProtected(int, int);
239 static void TestIfPlayerTouchesCustomElement(int, int);
240 static void TestIfElementTouchesCustomElement(int, int);
241 static void TestIfElementHitsCustomElement(int, int, int);
243 static void TestIfElementSmashesCustomElement(int, int, int);
246 static void ChangeElement(int, int, int);
248 static boolean CheckTriggeredElementChangeExt(int, int, int,int,int);
249 #define CheckTriggeredElementChange(e, ev) \
250 CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
251 #define CheckTriggeredElementChangeByPlayer(e, ev, p, s) \
252 CheckTriggeredElementChangeExt(e, ev, p, s, -1)
253 #define CheckTriggeredElementChangeBySide(e, ev, s) \
254 CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, s, -1)
255 #define CheckTriggeredElementChangeByPage(e, ev, p) \
256 CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
258 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
259 #define CheckElementChange(x, y, e, te, ev) \
260 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
261 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
262 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
263 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
264 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
266 static void PlayLevelSound(int, int, int);
267 static void PlayLevelSoundNearest(int, int, int);
268 static void PlayLevelSoundAction(int, int, int);
269 static void PlayLevelSoundElementAction(int, int, int, int);
270 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
271 static void PlayLevelSoundActionIfLoop(int, int, int);
272 static void StopLevelSoundActionIfLoop(int, int, int);
273 static void PlayLevelMusic();
275 static void MapGameButtons();
276 static void HandleGameButtons(struct GadgetInfo *);
278 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
281 /* ------------------------------------------------------------------------- */
282 /* definition of elements that automatically change to other elements after */
283 /* a specified time, eventually calling a function when changing */
284 /* ------------------------------------------------------------------------- */
286 /* forward declaration for changer functions */
287 static void InitBuggyBase(int x, int y);
288 static void WarnBuggyBase(int x, int y);
290 static void InitTrap(int x, int y);
291 static void ActivateTrap(int x, int y);
292 static void ChangeActiveTrap(int x, int y);
294 static void InitRobotWheel(int x, int y);
295 static void RunRobotWheel(int x, int y);
296 static void StopRobotWheel(int x, int y);
298 static void InitTimegateWheel(int x, int y);
299 static void RunTimegateWheel(int x, int y);
301 struct ChangingElementInfo
306 void (*pre_change_function)(int x, int y);
307 void (*change_function)(int x, int y);
308 void (*post_change_function)(int x, int y);
311 static struct ChangingElementInfo change_delay_list[] =
362 EL_SWITCHGATE_OPENING,
370 EL_SWITCHGATE_CLOSING,
371 EL_SWITCHGATE_CLOSED,
403 EL_ACID_SPLASH_RIGHT,
412 EL_SP_BUGGY_BASE_ACTIVATING,
419 EL_SP_BUGGY_BASE_ACTIVATING,
420 EL_SP_BUGGY_BASE_ACTIVE,
427 EL_SP_BUGGY_BASE_ACTIVE,
451 EL_ROBOT_WHEEL_ACTIVE,
459 EL_TIMEGATE_SWITCH_ACTIVE,
480 int push_delay_fixed, push_delay_random;
485 { EL_BALLOON, 0, 0 },
487 { EL_SOKOBAN_OBJECT, 2, 0 },
488 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
489 { EL_SATELLITE, 2, 0 },
490 { EL_SP_DISK_YELLOW, 2, 0 },
492 { EL_UNDEFINED, 0, 0 },
500 move_stepsize_list[] =
502 { EL_AMOEBA_DROP, 2 },
503 { EL_AMOEBA_DROPPING, 2 },
504 { EL_QUICKSAND_FILLING, 1 },
505 { EL_QUICKSAND_EMPTYING, 1 },
506 { EL_MAGIC_WALL_FILLING, 2 },
507 { EL_BD_MAGIC_WALL_FILLING, 2 },
508 { EL_MAGIC_WALL_EMPTYING, 2 },
509 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
519 collect_count_list[] =
522 { EL_BD_DIAMOND, 1 },
523 { EL_EMERALD_YELLOW, 1 },
524 { EL_EMERALD_RED, 1 },
525 { EL_EMERALD_PURPLE, 1 },
527 { EL_SP_INFOTRON, 1 },
539 access_direction_list[] =
541 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
542 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
543 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
544 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
545 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
546 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
547 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
548 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
549 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
550 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
551 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
553 { EL_SP_PORT_LEFT, MV_RIGHT },
554 { EL_SP_PORT_RIGHT, MV_LEFT },
555 { EL_SP_PORT_UP, MV_DOWN },
556 { EL_SP_PORT_DOWN, MV_UP },
557 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
558 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
559 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
560 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
561 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
562 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
563 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
564 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
565 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
566 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
567 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
568 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
569 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
570 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
571 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
573 { EL_UNDEFINED, MV_NO_MOVING }
576 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
578 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
579 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
580 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
581 IS_JUST_CHANGING(x, y))
583 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
586 void GetPlayerConfig()
588 if (!audio.sound_available)
589 setup.sound_simple = FALSE;
591 if (!audio.loops_available)
592 setup.sound_loops = FALSE;
594 if (!audio.music_available)
595 setup.sound_music = FALSE;
597 if (!video.fullscreen_available)
598 setup.fullscreen = FALSE;
600 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
602 SetAudioMode(setup.sound);
606 static int getBeltNrFromBeltElement(int element)
608 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
609 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
610 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
613 static int getBeltNrFromBeltActiveElement(int element)
615 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
616 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
617 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
620 static int getBeltNrFromBeltSwitchElement(int element)
622 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
623 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
624 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
627 static int getBeltDirNrFromBeltSwitchElement(int element)
629 static int belt_base_element[4] =
631 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
632 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
633 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
634 EL_CONVEYOR_BELT_4_SWITCH_LEFT
637 int belt_nr = getBeltNrFromBeltSwitchElement(element);
638 int belt_dir_nr = element - belt_base_element[belt_nr];
640 return (belt_dir_nr % 3);
643 static int getBeltDirFromBeltSwitchElement(int element)
645 static int belt_move_dir[3] =
652 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
654 return belt_move_dir[belt_dir_nr];
657 static void InitPlayerField(int x, int y, int element, boolean init_game)
659 if (element == EL_SP_MURPHY)
663 if (stored_player[0].present)
665 Feld[x][y] = EL_SP_MURPHY_CLONE;
671 stored_player[0].use_murphy_graphic = TRUE;
674 Feld[x][y] = EL_PLAYER_1;
680 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
681 int jx = player->jx, jy = player->jy;
683 player->present = TRUE;
685 player->block_last_field = (element == EL_SP_MURPHY ?
686 level.sp_block_last_field :
687 level.block_last_field);
689 /* ---------- initialize player's last field block delay --------------- */
691 /* always start with reliable default value (no adjustment needed) */
692 player->block_delay_adjustment = 0;
694 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
695 if (player->block_last_field && element == EL_SP_MURPHY)
696 player->block_delay_adjustment = 1;
698 /* special case 2: in game engines before 3.1.1, blocking was different */
699 if (game.use_block_last_field_bug)
700 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
702 if (!options.network || player->connected)
704 player->active = TRUE;
706 /* remove potentially duplicate players */
707 if (StorePlayer[jx][jy] == Feld[x][y])
708 StorePlayer[jx][jy] = 0;
710 StorePlayer[x][y] = Feld[x][y];
714 printf("Player %d activated.\n", player->element_nr);
715 printf("[Local player is %d and currently %s.]\n",
716 local_player->element_nr,
717 local_player->active ? "active" : "not active");
721 Feld[x][y] = EL_EMPTY;
723 player->jx = player->last_jx = x;
724 player->jy = player->last_jy = y;
728 static void InitField(int x, int y, boolean init_game)
730 int element = Feld[x][y];
739 InitPlayerField(x, y, element, init_game);
742 case EL_SOKOBAN_FIELD_PLAYER:
743 element = Feld[x][y] = EL_PLAYER_1;
744 InitField(x, y, init_game);
746 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
747 InitField(x, y, init_game);
750 case EL_SOKOBAN_FIELD_EMPTY:
751 local_player->sokobanfields_still_needed++;
755 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
756 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
757 else if (x > 0 && Feld[x-1][y] == EL_ACID)
758 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
759 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
760 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
761 else if (y > 0 && Feld[x][y-1] == EL_ACID)
762 Feld[x][y] = EL_ACID_POOL_BOTTOM;
763 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
764 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
772 case EL_SPACESHIP_RIGHT:
773 case EL_SPACESHIP_UP:
774 case EL_SPACESHIP_LEFT:
775 case EL_SPACESHIP_DOWN:
777 case EL_BD_BUTTERFLY_RIGHT:
778 case EL_BD_BUTTERFLY_UP:
779 case EL_BD_BUTTERFLY_LEFT:
780 case EL_BD_BUTTERFLY_DOWN:
781 case EL_BD_BUTTERFLY:
782 case EL_BD_FIREFLY_RIGHT:
783 case EL_BD_FIREFLY_UP:
784 case EL_BD_FIREFLY_LEFT:
785 case EL_BD_FIREFLY_DOWN:
787 case EL_PACMAN_RIGHT:
811 if (y == lev_fieldy - 1)
813 Feld[x][y] = EL_AMOEBA_GROWING;
814 Store[x][y] = EL_AMOEBA_WET;
818 case EL_DYNAMITE_ACTIVE:
819 case EL_SP_DISK_RED_ACTIVE:
820 case EL_DYNABOMB_PLAYER_1_ACTIVE:
821 case EL_DYNABOMB_PLAYER_2_ACTIVE:
822 case EL_DYNABOMB_PLAYER_3_ACTIVE:
823 case EL_DYNABOMB_PLAYER_4_ACTIVE:
828 local_player->lights_still_needed++;
832 local_player->friends_still_needed++;
837 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
840 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
841 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
842 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
843 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
844 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
845 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
846 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
847 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
848 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
849 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
850 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
851 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
854 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
855 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
856 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
858 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
860 game.belt_dir[belt_nr] = belt_dir;
861 game.belt_dir_nr[belt_nr] = belt_dir_nr;
863 else /* more than one switch -- set it like the first switch */
865 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
870 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
872 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
875 case EL_LIGHT_SWITCH_ACTIVE:
877 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
881 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
883 else if (IS_GROUP_ELEMENT(element))
885 struct ElementGroupInfo *group = element_info[element].group;
886 int last_anim_random_frame = gfx.anim_random_frame;
889 if (group->choice_mode == ANIM_RANDOM)
890 gfx.anim_random_frame = RND(group->num_elements_resolved);
892 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
893 group->choice_mode, 0,
896 if (group->choice_mode == ANIM_RANDOM)
897 gfx.anim_random_frame = last_anim_random_frame;
901 Feld[x][y] = group->element_resolved[element_pos];
903 InitField(x, y, init_game);
908 #if USE_NEW_COLLECT_COUNT
909 Count[x][y] = element_info[Feld[x][y]].collect_count_initial;
913 static inline void InitField_WithBug1(int x, int y, boolean init_game)
915 InitField(x, y, init_game);
917 /* not needed to call InitMovDir() -- already done by InitField()! */
918 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
919 CAN_MOVE(Feld[x][y]))
923 static inline void InitField_WithBug2(int x, int y, boolean init_game)
925 int old_element = Feld[x][y];
927 InitField(x, y, init_game);
929 /* not needed to call InitMovDir() -- already done by InitField()! */
930 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
931 CAN_MOVE(old_element) &&
932 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
935 /* this case is in fact a combination of not less than three bugs:
936 first, it calls InitMovDir() for elements that can move, although this is
937 already done by InitField(); then, it checks the element that was at this
938 field _before_ the call to InitField() (which can change it); lastly, it
939 was not called for "mole with direction" elements, which were treated as
940 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
944 inline void DrawGameValue_Emeralds(int value)
946 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
949 inline void DrawGameValue_Dynamite(int value)
951 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
954 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
958 /* currently only 4 of 8 possible keys are displayed */
959 for (i = 0; i < STD_NUM_KEYS; i++)
962 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
963 el2edimg(EL_KEY_1 + i));
965 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
966 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
967 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
971 inline void DrawGameValue_Score(int value)
973 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
976 inline void DrawGameValue_Time(int value)
979 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
981 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
984 inline void DrawGameValue_Level(int value)
987 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
990 /* misuse area for displaying emeralds to draw bigger level number */
991 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
992 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
994 /* now copy it to the area for displaying level number */
995 BlitBitmap(drawto, drawto,
996 DX_EMERALDS, DY_EMERALDS + 1,
997 getFontWidth(FONT_LEVEL_NUMBER) * 3,
998 getFontHeight(FONT_LEVEL_NUMBER) - 1,
999 DX_LEVEL - 1, DY_LEVEL + 1);
1001 /* restore the area for displaying emeralds */
1002 DrawGameValue_Emeralds(local_player->gems_still_needed);
1004 /* yes, this is all really ugly :-) */
1008 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1011 int key[MAX_NUM_KEYS];
1014 for (i = 0; i < MAX_NUM_KEYS; i++)
1015 key[i] = key_bits & (1 << i);
1017 DrawGameValue_Level(level_nr);
1019 DrawGameValue_Emeralds(emeralds);
1020 DrawGameValue_Dynamite(dynamite);
1021 DrawGameValue_Score(score);
1022 DrawGameValue_Time(time);
1024 DrawGameValue_Keys(key);
1027 void DrawGameDoorValues()
1031 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1033 DrawGameDoorValues_EM();
1038 DrawGameValue_Level(level_nr);
1040 DrawGameValue_Emeralds(local_player->gems_still_needed);
1041 DrawGameValue_Dynamite(local_player->inventory_size);
1042 DrawGameValue_Score(local_player->score);
1043 DrawGameValue_Time(TimeLeft);
1045 for (i = 0; i < MAX_PLAYERS; i++)
1046 DrawGameValue_Keys(stored_player[i].key);
1049 static void resolve_group_element(int group_element, int recursion_depth)
1051 static int group_nr;
1052 static struct ElementGroupInfo *group;
1053 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1056 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1058 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1059 group_element - EL_GROUP_START + 1);
1061 /* replace element which caused too deep recursion by question mark */
1062 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1067 if (recursion_depth == 0) /* initialization */
1069 group = element_info[group_element].group;
1070 group_nr = group_element - EL_GROUP_START;
1072 group->num_elements_resolved = 0;
1073 group->choice_pos = 0;
1076 for (i = 0; i < actual_group->num_elements; i++)
1078 int element = actual_group->element[i];
1080 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1083 if (IS_GROUP_ELEMENT(element))
1084 resolve_group_element(element, recursion_depth + 1);
1087 group->element_resolved[group->num_elements_resolved++] = element;
1088 element_info[element].in_group[group_nr] = TRUE;
1095 =============================================================================
1097 -----------------------------------------------------------------------------
1098 initialize game engine due to level / tape version number
1099 =============================================================================
1102 static void InitGameEngine()
1106 /* set game engine from tape file when re-playing, else from level file */
1107 game.engine_version = (tape.playing ? tape.engine_version :
1108 level.game_version);
1110 /* ---------------------------------------------------------------------- */
1111 /* set flags for bugs and changes according to active game engine version */
1112 /* ---------------------------------------------------------------------- */
1115 Summary of bugfix/change:
1116 Fixed handling for custom elements that change when pushed by the player.
1118 Fixed/changed in version:
1122 Before 3.1.0, custom elements that "change when pushing" changed directly
1123 after the player started pushing them (until then handled in "DigField()").
1124 Since 3.1.0, these custom elements are not changed until the "pushing"
1125 move of the element is finished (now handled in "ContinueMoving()").
1127 Affected levels/tapes:
1128 The first condition is generally needed for all levels/tapes before version
1129 3.1.0, which might use the old behaviour before it was changed; known tapes
1130 that are affected are some tapes from the level set "Walpurgis Gardens" by
1132 The second condition is an exception from the above case and is needed for
1133 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1134 above (including some development versions of 3.1.0), but before it was
1135 known that this change would break tapes like the above and was fixed in
1136 3.1.1, so that the changed behaviour was active although the engine version
1137 while recording maybe was before 3.1.0. There is at least one tape that is
1138 affected by this exception, which is the tape for the one-level set "Bug
1139 Machine" by Juergen Bonhagen.
1142 game.use_change_when_pushing_bug =
1143 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1145 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1146 tape.game_version < VERSION_IDENT(3,1,1,0)));
1149 Summary of bugfix/change:
1150 Fixed handling for blocking the field the player leaves when moving.
1152 Fixed/changed in version:
1156 Before 3.1.1, when "block last field when moving" was enabled, the field
1157 the player is leaving when moving was blocked for the time of the move,
1158 and was directly unblocked afterwards. This resulted in the last field
1159 being blocked for exactly one less than the number of frames of one player
1160 move. Additionally, even when blocking was disabled, the last field was
1161 blocked for exactly one frame.
1162 Since 3.1.1, due to changes in player movement handling, the last field
1163 is not blocked at all when blocking is disabled. When blocking is enabled,
1164 the last field is blocked for exactly the number of frames of one player
1165 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1166 last field is blocked for exactly one more than the number of frames of
1169 Affected levels/tapes:
1170 (!!! yet to be determined -- probably many !!!)
1173 game.use_block_last_field_bug =
1174 (game.engine_version < VERSION_IDENT(3,1,1,0));
1176 /* ---------------------------------------------------------------------- */
1178 /* dynamically adjust element properties according to game engine version */
1179 InitElementPropertiesEngine(game.engine_version);
1182 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1183 printf(" tape version == %06d [%s] [file: %06d]\n",
1184 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1186 printf(" => game.engine_version == %06d\n", game.engine_version);
1189 /* ---------- recursively resolve group elements ------------------------- */
1191 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1192 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1193 element_info[i].in_group[j] = FALSE;
1195 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1196 resolve_group_element(EL_GROUP_START + i, 0);
1198 /* ---------- initialize player's initial move delay --------------------- */
1200 /* dynamically adjust player properties according to level information */
1201 game.initial_move_delay_value =
1202 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1204 /* dynamically adjust player properties according to game engine version */
1205 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1206 game.initial_move_delay_value : 0);
1208 /* ---------- initialize player's initial push delay --------------------- */
1210 /* dynamically adjust player properties according to game engine version */
1211 game.initial_push_delay_value =
1212 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1214 /* ---------- initialize changing elements ------------------------------- */
1216 /* initialize changing elements information */
1217 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1219 struct ElementInfo *ei = &element_info[i];
1221 /* this pointer might have been changed in the level editor */
1222 ei->change = &ei->change_page[0];
1224 if (!IS_CUSTOM_ELEMENT(i))
1226 ei->change->target_element = EL_EMPTY_SPACE;
1227 ei->change->delay_fixed = 0;
1228 ei->change->delay_random = 0;
1229 ei->change->delay_frames = 1;
1232 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1234 ei->has_change_event[j] = FALSE;
1236 ei->event_page_nr[j] = 0;
1237 ei->event_page[j] = &ei->change_page[0];
1241 /* add changing elements from pre-defined list */
1242 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1244 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1245 struct ElementInfo *ei = &element_info[ch_delay->element];
1247 ei->change->target_element = ch_delay->target_element;
1248 ei->change->delay_fixed = ch_delay->change_delay;
1250 ei->change->pre_change_function = ch_delay->pre_change_function;
1251 ei->change->change_function = ch_delay->change_function;
1252 ei->change->post_change_function = ch_delay->post_change_function;
1254 ei->has_change_event[CE_DELAY] = TRUE;
1256 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1257 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1260 /* ---------- initialize internal run-time variables ------------- */
1262 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1264 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1266 for (j = 0; j < ei->num_change_pages; j++)
1268 ei->change_page[j].can_change_or_has_action =
1269 (ei->change_page[j].can_change |
1270 ei->change_page[j].has_action);
1274 /* add change events from custom element configuration */
1275 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1277 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1279 for (j = 0; j < ei->num_change_pages; j++)
1281 if (!ei->change_page[j].can_change_or_has_action)
1284 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1286 /* only add event page for the first page found with this event */
1287 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1289 ei->has_change_event[k] = TRUE;
1291 ei->event_page_nr[k] = j;
1292 ei->event_page[k] = &ei->change_page[j];
1298 /* ---------- initialize run-time trigger player and element ------------- */
1300 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1302 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1304 for (j = 0; j < ei->num_change_pages; j++)
1306 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1307 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1311 /* ---------- initialize trigger events ---------------------------------- */
1313 /* initialize trigger events information */
1314 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1315 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1316 trigger_events[i][j] = FALSE;
1318 /* add trigger events from element change event properties */
1319 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1321 struct ElementInfo *ei = &element_info[i];
1323 for (j = 0; j < ei->num_change_pages; j++)
1325 if (!ei->change_page[j].can_change_or_has_action)
1328 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1330 int trigger_element = ei->change_page[j].trigger_element;
1332 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1334 if (ei->change_page[j].has_event[k])
1336 if (IS_GROUP_ELEMENT(trigger_element))
1338 struct ElementGroupInfo *group =
1339 element_info[trigger_element].group;
1341 for (l = 0; l < group->num_elements_resolved; l++)
1342 trigger_events[group->element_resolved[l]][k] = TRUE;
1345 trigger_events[trigger_element][k] = TRUE;
1352 /* ---------- initialize push delay -------------------------------------- */
1354 /* initialize push delay values to default */
1355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1357 if (!IS_CUSTOM_ELEMENT(i))
1359 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1360 element_info[i].push_delay_random = game.default_push_delay_random;
1364 /* set push delay value for certain elements from pre-defined list */
1365 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1367 int e = push_delay_list[i].element;
1369 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1370 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1373 /* set push delay value for Supaplex elements for newer engine versions */
1374 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1376 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1378 if (IS_SP_ELEMENT(i))
1380 /* set SP push delay to just enough to push under a falling zonk */
1381 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1383 element_info[i].push_delay_fixed = delay;
1384 element_info[i].push_delay_random = 0;
1389 /* ---------- initialize move stepsize ----------------------------------- */
1391 /* initialize move stepsize values to default */
1392 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1393 if (!IS_CUSTOM_ELEMENT(i))
1394 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1396 /* set move stepsize value for certain elements from pre-defined list */
1397 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1399 int e = move_stepsize_list[i].element;
1401 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1404 /* ---------- initialize collect score ----------------------------------- */
1406 /* initialize collect score values for custom elements from initial value */
1407 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1408 if (IS_CUSTOM_ELEMENT(i))
1409 element_info[i].collect_score = element_info[i].collect_score_initial;
1411 /* ---------- initialize collect count ----------------------------------- */
1413 /* initialize collect count values for non-custom elements */
1414 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1415 if (!IS_CUSTOM_ELEMENT(i))
1416 element_info[i].collect_count_initial = 0;
1418 /* add collect count values for all elements from pre-defined list */
1419 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1420 element_info[collect_count_list[i].element].collect_count_initial =
1421 collect_count_list[i].count;
1423 /* ---------- initialize access direction -------------------------------- */
1425 /* initialize access direction values to default (access from every side) */
1426 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1427 if (!IS_CUSTOM_ELEMENT(i))
1428 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1430 /* set access direction value for certain elements from pre-defined list */
1431 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1432 element_info[access_direction_list[i].element].access_direction =
1433 access_direction_list[i].direction;
1438 =============================================================================
1440 -----------------------------------------------------------------------------
1441 initialize and start new game
1442 =============================================================================
1447 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1448 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1449 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1454 /* don't play tapes over network */
1455 network_playing = (options.network && !tape.playing);
1457 for (i = 0; i < MAX_PLAYERS; i++)
1459 struct PlayerInfo *player = &stored_player[i];
1461 player->index_nr = i;
1462 player->index_bit = (1 << i);
1463 player->element_nr = EL_PLAYER_1 + i;
1465 player->present = FALSE;
1466 player->active = FALSE;
1469 player->effective_action = 0;
1470 player->programmed_action = 0;
1473 player->gems_still_needed = level.gems_needed;
1474 player->sokobanfields_still_needed = 0;
1475 player->lights_still_needed = 0;
1476 player->friends_still_needed = 0;
1478 for (j = 0; j < MAX_NUM_KEYS; j++)
1479 player->key[j] = FALSE;
1481 player->dynabomb_count = 0;
1482 player->dynabomb_size = 1;
1483 player->dynabombs_left = 0;
1484 player->dynabomb_xl = FALSE;
1486 player->MovDir = MV_NO_MOVING;
1489 player->GfxDir = MV_NO_MOVING;
1490 player->GfxAction = ACTION_DEFAULT;
1492 player->StepFrame = 0;
1494 player->use_murphy_graphic = FALSE;
1496 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1497 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1499 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1501 player->actual_frame_counter = 0;
1503 player->step_counter = 0;
1505 player->last_move_dir = MV_NO_MOVING;
1507 player->is_waiting = FALSE;
1508 player->is_moving = FALSE;
1509 player->is_auto_moving = FALSE;
1510 player->is_digging = FALSE;
1511 player->is_snapping = FALSE;
1512 player->is_collecting = FALSE;
1513 player->is_pushing = FALSE;
1514 player->is_switching = FALSE;
1515 player->is_dropping = FALSE;
1517 player->is_bored = FALSE;
1518 player->is_sleeping = FALSE;
1520 player->frame_counter_bored = -1;
1521 player->frame_counter_sleeping = -1;
1523 player->anim_delay_counter = 0;
1524 player->post_delay_counter = 0;
1526 player->action_waiting = ACTION_DEFAULT;
1527 player->last_action_waiting = ACTION_DEFAULT;
1528 player->special_action_bored = ACTION_DEFAULT;
1529 player->special_action_sleeping = ACTION_DEFAULT;
1531 player->num_special_action_bored = 0;
1532 player->num_special_action_sleeping = 0;
1534 /* determine number of special actions for bored and sleeping animation */
1535 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1537 boolean found = FALSE;
1539 for (k = 0; k < NUM_DIRECTIONS; k++)
1540 if (el_act_dir2img(player->element_nr, j, k) !=
1541 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1545 player->num_special_action_bored++;
1549 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1551 boolean found = FALSE;
1553 for (k = 0; k < NUM_DIRECTIONS; k++)
1554 if (el_act_dir2img(player->element_nr, j, k) !=
1555 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1559 player->num_special_action_sleeping++;
1564 player->switch_x = -1;
1565 player->switch_y = -1;
1567 player->drop_x = -1;
1568 player->drop_y = -1;
1570 player->show_envelope = 0;
1572 player->move_delay = game.initial_move_delay;
1573 player->move_delay_value = game.initial_move_delay_value;
1575 player->move_delay_reset_counter = 0;
1577 player->push_delay = -1; /* initialized when pushing starts */
1578 player->push_delay_value = game.initial_push_delay_value;
1580 player->drop_delay = 0;
1582 player->last_jx = player->last_jy = 0;
1583 player->jx = player->jy = 0;
1585 player->shield_normal_time_left = 0;
1586 player->shield_deadly_time_left = 0;
1588 player->inventory_infinite_element = EL_UNDEFINED;
1589 player->inventory_size = 0;
1591 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1592 SnapField(player, 0, 0);
1594 player->LevelSolved = FALSE;
1595 player->GameOver = FALSE;
1598 network_player_action_received = FALSE;
1600 #if defined(NETWORK_AVALIABLE)
1601 /* initial null action */
1602 if (network_playing)
1603 SendToServer_MovePlayer(MV_NO_MOVING);
1612 TimeLeft = level.time;
1615 ScreenMovDir = MV_NO_MOVING;
1619 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1621 AllPlayersGone = FALSE;
1623 game.yamyam_content_nr = 0;
1624 game.magic_wall_active = FALSE;
1625 game.magic_wall_time_left = 0;
1626 game.light_time_left = 0;
1627 game.timegate_time_left = 0;
1628 game.switchgate_pos = 0;
1629 game.balloon_dir = MV_NO_MOVING;
1630 game.gravity = level.initial_gravity;
1631 game.explosions_delayed = TRUE;
1633 game.envelope_active = FALSE;
1635 for (i = 0; i < NUM_BELTS; i++)
1637 game.belt_dir[i] = MV_NO_MOVING;
1638 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1641 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1642 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1644 for (x = 0; x < lev_fieldx; x++)
1646 for (y = 0; y < lev_fieldy; y++)
1648 Feld[x][y] = level.field[x][y];
1649 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1650 ChangeDelay[x][y] = 0;
1651 ChangePage[x][y] = -1;
1652 #if USE_NEW_COLLECT_COUNT
1653 Count[x][y] = 0; /* initialized in InitField() */
1655 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1657 WasJustMoving[x][y] = 0;
1658 WasJustFalling[x][y] = 0;
1659 CheckCollision[x][y] = 0;
1661 Pushed[x][y] = FALSE;
1663 Changed[x][y] = FALSE;
1664 ChangeEvent[x][y] = -1;
1666 ExplodePhase[x][y] = 0;
1667 ExplodeDelay[x][y] = 0;
1668 ExplodeField[x][y] = EX_TYPE_NONE;
1670 RunnerVisit[x][y] = 0;
1671 PlayerVisit[x][y] = 0;
1674 GfxRandom[x][y] = INIT_GFX_RANDOM();
1675 GfxElement[x][y] = EL_UNDEFINED;
1676 GfxAction[x][y] = ACTION_DEFAULT;
1677 GfxDir[x][y] = MV_NO_MOVING;
1681 for (y = 0; y < lev_fieldy; y++)
1683 for (x = 0; x < lev_fieldx; x++)
1685 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1687 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1689 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1692 InitField(x, y, TRUE);
1698 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1699 emulate_sb ? EMU_SOKOBAN :
1700 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1702 /* initialize explosion and ignition delay */
1703 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1705 if (!IS_CUSTOM_ELEMENT(i))
1708 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1709 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1710 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1711 int last_phase = (num_phase + 1) * delay;
1712 int half_phase = (num_phase / 2) * delay;
1714 element_info[i].explosion_delay = last_phase - 1;
1715 element_info[i].ignition_delay = half_phase;
1717 if (i == EL_BLACK_ORB)
1718 element_info[i].ignition_delay = 1;
1722 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1723 element_info[i].explosion_delay = 1;
1725 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1726 element_info[i].ignition_delay = 1;
1730 /* correct non-moving belts to start moving left */
1731 for (i = 0; i < NUM_BELTS; i++)
1732 if (game.belt_dir[i] == MV_NO_MOVING)
1733 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1735 /* check if any connected player was not found in playfield */
1736 for (i = 0; i < MAX_PLAYERS; i++)
1738 struct PlayerInfo *player = &stored_player[i];
1740 if (player->connected && !player->present)
1742 for (j = 0; j < MAX_PLAYERS; j++)
1744 struct PlayerInfo *some_player = &stored_player[j];
1745 int jx = some_player->jx, jy = some_player->jy;
1747 /* assign first free player found that is present in the playfield */
1748 if (some_player->present && !some_player->connected)
1750 player->present = TRUE;
1751 player->active = TRUE;
1753 some_player->present = FALSE;
1754 some_player->active = FALSE;
1757 player->element_nr = some_player->element_nr;
1760 player->block_last_field = some_player->block_last_field;
1761 player->block_delay_adjustment = some_player->block_delay_adjustment;
1763 StorePlayer[jx][jy] = player->element_nr;
1764 player->jx = player->last_jx = jx;
1765 player->jy = player->last_jy = jy;
1775 /* when playing a tape, eliminate all players which do not participate */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (stored_player[i].active && !tape.player_participates[i])
1781 struct PlayerInfo *player = &stored_player[i];
1782 int jx = player->jx, jy = player->jy;
1784 player->active = FALSE;
1785 StorePlayer[jx][jy] = 0;
1786 Feld[jx][jy] = EL_EMPTY;
1790 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1792 /* when in single player mode, eliminate all but the first active player */
1794 for (i = 0; i < MAX_PLAYERS; i++)
1796 if (stored_player[i].active)
1798 for (j = i + 1; j < MAX_PLAYERS; j++)
1800 if (stored_player[j].active)
1802 struct PlayerInfo *player = &stored_player[j];
1803 int jx = player->jx, jy = player->jy;
1805 player->active = FALSE;
1806 player->present = FALSE;
1808 StorePlayer[jx][jy] = 0;
1809 Feld[jx][jy] = EL_EMPTY;
1816 /* when recording the game, store which players take part in the game */
1819 for (i = 0; i < MAX_PLAYERS; i++)
1820 if (stored_player[i].active)
1821 tape.player_participates[i] = TRUE;
1826 for (i = 0; i < MAX_PLAYERS; i++)
1828 struct PlayerInfo *player = &stored_player[i];
1830 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1835 if (local_player == player)
1836 printf("Player %d is local player.\n", i+1);
1840 if (BorderElement == EL_EMPTY)
1843 SBX_Right = lev_fieldx - SCR_FIELDX;
1845 SBY_Lower = lev_fieldy - SCR_FIELDY;
1850 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1852 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1855 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1856 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1858 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1859 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1861 /* if local player not found, look for custom element that might create
1862 the player (make some assumptions about the right custom element) */
1863 if (!local_player->present)
1865 int start_x = 0, start_y = 0;
1866 int found_rating = 0;
1867 int found_element = EL_UNDEFINED;
1869 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1871 int element = Feld[x][y];
1876 if (!IS_CUSTOM_ELEMENT(element))
1879 if (CAN_CHANGE(element))
1881 for (i = 0; i < element_info[element].num_change_pages; i++)
1883 content = element_info[element].change_page[i].target_element;
1884 is_player = ELEM_IS_PLAYER(content);
1886 if (is_player && (found_rating < 3 || element < found_element))
1892 found_element = element;
1897 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1899 content = element_info[element].content[xx][yy];
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 2 || element < found_element))
1904 start_x = x + xx - 1;
1905 start_y = y + yy - 1;
1908 found_element = element;
1911 if (!CAN_CHANGE(element))
1914 for (i = 0; i < element_info[element].num_change_pages; i++)
1916 content= element_info[element].change_page[i].target_content[xx][yy];
1917 is_player = ELEM_IS_PLAYER(content);
1919 if (is_player && (found_rating < 1 || element < found_element))
1921 start_x = x + xx - 1;
1922 start_y = y + yy - 1;
1925 found_element = element;
1931 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1932 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1935 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1936 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1941 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1942 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1943 local_player->jx - MIDPOSX);
1945 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1946 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1947 local_player->jy - MIDPOSY);
1950 if (!game.restart_level)
1951 CloseDoor(DOOR_CLOSE_1);
1953 /* !!! FIX THIS (START) !!! */
1954 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1956 InitGameEngine_EM();
1963 /* after drawing the level, correct some elements */
1964 if (game.timegate_time_left == 0)
1965 CloseAllOpenTimegates();
1967 if (setup.soft_scrolling)
1968 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1970 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1973 /* !!! FIX THIS (END) !!! */
1975 if (!game.restart_level)
1977 /* copy default game door content to main double buffer */
1978 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1979 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1982 DrawGameDoorValues();
1984 if (!game.restart_level)
1988 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1989 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1990 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1994 /* copy actual game door content to door double buffer for OpenDoor() */
1995 BlitBitmap(drawto, bitmap_db_door,
1996 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1998 OpenDoor(DOOR_OPEN_ALL);
2000 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2002 if (setup.sound_music)
2005 KeyboardAutoRepeatOffUnlessAutoplay();
2009 for (i = 0; i < MAX_PLAYERS; i++)
2010 printf("Player %d %sactive.\n",
2011 i + 1, (stored_player[i].active ? "" : "not "));
2015 game.restart_level = FALSE;
2018 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2020 /* this is used for non-R'n'D game engines to update certain engine values */
2022 /* needed to determine if sounds are played within the visible screen area */
2023 scroll_x = actual_scroll_x;
2024 scroll_y = actual_scroll_y;
2027 void InitMovDir(int x, int y)
2029 int i, element = Feld[x][y];
2030 static int xy[4][2] =
2037 static int direction[3][4] =
2039 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2040 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2041 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2050 Feld[x][y] = EL_BUG;
2051 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2054 case EL_SPACESHIP_RIGHT:
2055 case EL_SPACESHIP_UP:
2056 case EL_SPACESHIP_LEFT:
2057 case EL_SPACESHIP_DOWN:
2058 Feld[x][y] = EL_SPACESHIP;
2059 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2062 case EL_BD_BUTTERFLY_RIGHT:
2063 case EL_BD_BUTTERFLY_UP:
2064 case EL_BD_BUTTERFLY_LEFT:
2065 case EL_BD_BUTTERFLY_DOWN:
2066 Feld[x][y] = EL_BD_BUTTERFLY;
2067 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2070 case EL_BD_FIREFLY_RIGHT:
2071 case EL_BD_FIREFLY_UP:
2072 case EL_BD_FIREFLY_LEFT:
2073 case EL_BD_FIREFLY_DOWN:
2074 Feld[x][y] = EL_BD_FIREFLY;
2075 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2078 case EL_PACMAN_RIGHT:
2080 case EL_PACMAN_LEFT:
2081 case EL_PACMAN_DOWN:
2082 Feld[x][y] = EL_PACMAN;
2083 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2086 case EL_SP_SNIKSNAK:
2087 MovDir[x][y] = MV_UP;
2090 case EL_SP_ELECTRON:
2091 MovDir[x][y] = MV_LEFT;
2098 Feld[x][y] = EL_MOLE;
2099 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2103 if (IS_CUSTOM_ELEMENT(element))
2105 struct ElementInfo *ei = &element_info[element];
2106 int move_direction_initial = ei->move_direction_initial;
2107 int move_pattern = ei->move_pattern;
2109 if (move_direction_initial == MV_START_PREVIOUS)
2111 if (MovDir[x][y] != MV_NO_MOVING)
2114 move_direction_initial = MV_START_AUTOMATIC;
2117 if (move_direction_initial == MV_START_RANDOM)
2118 MovDir[x][y] = 1 << RND(4);
2119 else if (move_direction_initial & MV_ANY_DIRECTION)
2120 MovDir[x][y] = move_direction_initial;
2121 else if (move_pattern == MV_ALL_DIRECTIONS ||
2122 move_pattern == MV_TURNING_LEFT ||
2123 move_pattern == MV_TURNING_RIGHT ||
2124 move_pattern == MV_TURNING_LEFT_RIGHT ||
2125 move_pattern == MV_TURNING_RIGHT_LEFT ||
2126 move_pattern == MV_TURNING_RANDOM)
2127 MovDir[x][y] = 1 << RND(4);
2128 else if (move_pattern == MV_HORIZONTAL)
2129 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2130 else if (move_pattern == MV_VERTICAL)
2131 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2132 else if (move_pattern & MV_ANY_DIRECTION)
2133 MovDir[x][y] = element_info[element].move_pattern;
2134 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2135 move_pattern == MV_ALONG_RIGHT_SIDE)
2137 /* use random direction as default start direction */
2138 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2139 MovDir[x][y] = 1 << RND(4);
2141 for (i = 0; i < NUM_DIRECTIONS; i++)
2143 int x1 = x + xy[i][0];
2144 int y1 = y + xy[i][1];
2146 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2148 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2149 MovDir[x][y] = direction[0][i];
2151 MovDir[x][y] = direction[1][i];
2160 MovDir[x][y] = 1 << RND(4);
2162 if (element != EL_BUG &&
2163 element != EL_SPACESHIP &&
2164 element != EL_BD_BUTTERFLY &&
2165 element != EL_BD_FIREFLY)
2168 for (i = 0; i < NUM_DIRECTIONS; i++)
2170 int x1 = x + xy[i][0];
2171 int y1 = y + xy[i][1];
2173 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2175 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2177 MovDir[x][y] = direction[0][i];
2180 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2181 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2183 MovDir[x][y] = direction[1][i];
2192 GfxDir[x][y] = MovDir[x][y];
2195 void InitAmoebaNr(int x, int y)
2198 int group_nr = AmoebeNachbarNr(x, y);
2202 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2204 if (AmoebaCnt[i] == 0)
2212 AmoebaNr[x][y] = group_nr;
2213 AmoebaCnt[group_nr]++;
2214 AmoebaCnt2[group_nr]++;
2220 boolean raise_level = FALSE;
2222 if (local_player->MovPos)
2225 if (tape.auto_play) /* tape might already be stopped here */
2226 tape.auto_play_level_solved = TRUE;
2228 local_player->LevelSolved = FALSE;
2230 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2234 if (!tape.playing && setup.sound_loops)
2235 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2236 SND_CTRL_PLAY_LOOP);
2238 while (TimeLeft > 0)
2240 if (!tape.playing && !setup.sound_loops)
2241 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2242 if (TimeLeft > 0 && !(TimeLeft % 10))
2243 RaiseScore(level.score[SC_TIME_BONUS]);
2244 if (TimeLeft > 100 && !(TimeLeft % 10))
2249 DrawGameValue_Time(TimeLeft);
2257 if (!tape.playing && setup.sound_loops)
2258 StopSound(SND_GAME_LEVELTIME_BONUS);
2260 else if (level.time == 0) /* level without time limit */
2262 if (!tape.playing && setup.sound_loops)
2263 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2264 SND_CTRL_PLAY_LOOP);
2266 while (TimePlayed < 999)
2268 if (!tape.playing && !setup.sound_loops)
2269 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2270 if (TimePlayed < 999 && !(TimePlayed % 10))
2271 RaiseScore(level.score[SC_TIME_BONUS]);
2272 if (TimePlayed < 900 && !(TimePlayed % 10))
2277 DrawGameValue_Time(TimePlayed);
2285 if (!tape.playing && setup.sound_loops)
2286 StopSound(SND_GAME_LEVELTIME_BONUS);
2289 /* close exit door after last player */
2290 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2291 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2292 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2294 int element = Feld[ExitX][ExitY];
2296 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2297 EL_SP_EXIT_CLOSING);
2299 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2302 /* Hero disappears */
2303 if (ExitX >= 0 && ExitY >= 0)
2304 DrawLevelField(ExitX, ExitY);
2311 CloseDoor(DOOR_CLOSE_1);
2316 SaveTape(tape.level_nr); /* Ask to save tape */
2319 if (level_nr == leveldir_current->handicap_level)
2321 leveldir_current->handicap_level++;
2322 SaveLevelSetup_SeriesInfo();
2325 if (level_editor_test_game)
2326 local_player->score = -1; /* no highscore when playing from editor */
2327 else if (level_nr < leveldir_current->last_level)
2328 raise_level = TRUE; /* advance to next level */
2330 if ((hi_pos = NewHiScore()) >= 0)
2332 game_status = GAME_MODE_SCORES;
2333 DrawHallOfFame(hi_pos);
2342 game_status = GAME_MODE_MAIN;
2359 LoadScore(level_nr);
2361 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2362 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2365 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2367 if (local_player->score > highscore[k].Score)
2369 /* player has made it to the hall of fame */
2371 if (k < MAX_SCORE_ENTRIES - 1)
2373 int m = MAX_SCORE_ENTRIES - 1;
2376 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2377 if (!strcmp(setup.player_name, highscore[l].Name))
2379 if (m == k) /* player's new highscore overwrites his old one */
2383 for (l = m; l > k; l--)
2385 strcpy(highscore[l].Name, highscore[l - 1].Name);
2386 highscore[l].Score = highscore[l - 1].Score;
2393 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2394 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2395 highscore[k].Score = local_player->score;
2401 else if (!strncmp(setup.player_name, highscore[k].Name,
2402 MAX_PLAYER_NAME_LEN))
2403 break; /* player already there with a higher score */
2409 SaveScore(level_nr);
2414 inline static int getElementMoveStepsize(int x, int y)
2416 int element = Feld[x][y];
2417 int direction = MovDir[x][y];
2418 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2419 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2420 int horiz_move = (dx != 0);
2421 int sign = (horiz_move ? dx : dy);
2422 int step = sign * element_info[element].move_stepsize;
2424 /* special values for move stepsize for spring and things on conveyor belt */
2428 if (element == EL_SPRING)
2429 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2430 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2431 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2432 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2434 if (CAN_FALL(element) &&
2435 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2436 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2437 else if (element == EL_SPRING)
2438 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2445 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2447 if (player->GfxAction != action || player->GfxDir != dir)
2450 printf("Player frame reset! (%d => %d, %d => %d)\n",
2451 player->GfxAction, action, player->GfxDir, dir);
2454 player->GfxAction = action;
2455 player->GfxDir = dir;
2457 player->StepFrame = 0;
2461 static void ResetRandomAnimationValue(int x, int y)
2463 GfxRandom[x][y] = INIT_GFX_RANDOM();
2466 static void ResetGfxAnimation(int x, int y)
2469 GfxAction[x][y] = ACTION_DEFAULT;
2470 GfxDir[x][y] = MovDir[x][y];
2473 void InitMovingField(int x, int y, int direction)
2475 int element = Feld[x][y];
2476 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2477 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2481 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2482 ResetGfxAnimation(x, y);
2484 MovDir[x][y] = direction;
2485 GfxDir[x][y] = direction;
2486 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2487 ACTION_FALLING : ACTION_MOVING);
2489 /* this is needed for CEs with property "can move" / "not moving" */
2491 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2493 if (Feld[newx][newy] == EL_EMPTY)
2494 Feld[newx][newy] = EL_BLOCKED;
2496 MovDir[newx][newy] = MovDir[x][y];
2498 #if USE_NEW_COLLECT_COUNT
2499 Count[newx][newy] = Count[x][y];
2502 GfxFrame[newx][newy] = GfxFrame[x][y];
2503 GfxRandom[newx][newy] = GfxRandom[x][y];
2504 GfxAction[newx][newy] = GfxAction[x][y];
2505 GfxDir[newx][newy] = GfxDir[x][y];
2509 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2511 int direction = MovDir[x][y];
2512 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2513 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2519 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2521 int oldx = x, oldy = y;
2522 int direction = MovDir[x][y];
2524 if (direction == MV_LEFT)
2526 else if (direction == MV_RIGHT)
2528 else if (direction == MV_UP)
2530 else if (direction == MV_DOWN)
2533 *comes_from_x = oldx;
2534 *comes_from_y = oldy;
2537 int MovingOrBlocked2Element(int x, int y)
2539 int element = Feld[x][y];
2541 if (element == EL_BLOCKED)
2545 Blocked2Moving(x, y, &oldx, &oldy);
2546 return Feld[oldx][oldy];
2552 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2554 /* like MovingOrBlocked2Element(), but if element is moving
2555 and (x,y) is the field the moving element is just leaving,
2556 return EL_BLOCKED instead of the element value */
2557 int element = Feld[x][y];
2559 if (IS_MOVING(x, y))
2561 if (element == EL_BLOCKED)
2565 Blocked2Moving(x, y, &oldx, &oldy);
2566 return Feld[oldx][oldy];
2575 static void RemoveField(int x, int y)
2577 Feld[x][y] = EL_EMPTY;
2583 #if USE_NEW_COLLECT_COUNT
2588 ChangeDelay[x][y] = 0;
2589 ChangePage[x][y] = -1;
2590 Pushed[x][y] = FALSE;
2593 ExplodeField[x][y] = EX_TYPE_NONE;
2596 GfxElement[x][y] = EL_UNDEFINED;
2597 GfxAction[x][y] = ACTION_DEFAULT;
2598 GfxDir[x][y] = MV_NO_MOVING;
2601 void RemoveMovingField(int x, int y)
2603 int oldx = x, oldy = y, newx = x, newy = y;
2604 int element = Feld[x][y];
2605 int next_element = EL_UNDEFINED;
2607 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2610 if (IS_MOVING(x, y))
2612 Moving2Blocked(x, y, &newx, &newy);
2614 if (Feld[newx][newy] != EL_BLOCKED)
2616 /* element is moving, but target field is not free (blocked), but
2617 already occupied by something different (example: acid pool);
2618 in this case, only remove the moving field, but not the target */
2620 RemoveField(oldx, oldy);
2622 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2624 DrawLevelField(oldx, oldy);
2629 else if (element == EL_BLOCKED)
2631 Blocked2Moving(x, y, &oldx, &oldy);
2632 if (!IS_MOVING(oldx, oldy))
2636 if (element == EL_BLOCKED &&
2637 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2638 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2639 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2640 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2641 next_element = get_next_element(Feld[oldx][oldy]);
2643 RemoveField(oldx, oldy);
2644 RemoveField(newx, newy);
2646 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2648 if (next_element != EL_UNDEFINED)
2649 Feld[oldx][oldy] = next_element;
2651 DrawLevelField(oldx, oldy);
2652 DrawLevelField(newx, newy);
2655 void DrawDynamite(int x, int y)
2657 int sx = SCREENX(x), sy = SCREENY(y);
2658 int graphic = el2img(Feld[x][y]);
2661 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2664 if (IS_WALKABLE_INSIDE(Back[x][y]))
2668 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2669 else if (Store[x][y])
2670 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2672 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2674 if (Back[x][y] || Store[x][y])
2675 DrawGraphicThruMask(sx, sy, graphic, frame);
2677 DrawGraphic(sx, sy, graphic, frame);
2680 void CheckDynamite(int x, int y)
2682 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2686 if (MovDelay[x][y] != 0)
2689 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2695 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2700 void DrawRelocatePlayer(struct PlayerInfo *player)
2702 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2703 boolean no_delay = (tape.warp_forward);
2704 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2705 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2706 int jx = player->jx;
2707 int jy = player->jy;
2709 if (level.instant_relocation)
2711 int offset = (setup.scroll_delay ? 3 : 0);
2713 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2715 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2716 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2717 local_player->jx - MIDPOSX);
2719 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2720 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2721 local_player->jy - MIDPOSY);
2725 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2726 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2727 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2729 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2730 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2731 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2733 /* don't scroll over playfield boundaries */
2734 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2735 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2737 /* don't scroll over playfield boundaries */
2738 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2739 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2742 RedrawPlayfield(TRUE, 0,0,0,0);
2746 int scroll_xx = -999, scroll_yy = -999;
2748 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2750 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2753 int fx = FX, fy = FY;
2755 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2756 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2757 local_player->jx - MIDPOSX);
2759 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2760 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2761 local_player->jy - MIDPOSY);
2763 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2764 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2766 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2772 fx += dx * TILEX / 2;
2773 fy += dy * TILEY / 2;
2775 ScrollLevel(dx, dy);
2778 /* scroll in two steps of half tile size to make things smoother */
2779 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2781 Delay(wait_delay_value);
2783 /* scroll second step to align at full tile size */
2785 Delay(wait_delay_value);
2790 Delay(wait_delay_value);
2794 void RelocatePlayer(int jx, int jy, int el_player_raw)
2796 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2797 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2798 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2799 boolean no_delay = (tape.warp_forward);
2800 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2801 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2802 int old_jx = player->jx;
2803 int old_jy = player->jy;
2804 int old_element = Feld[old_jx][old_jy];
2805 int element = Feld[jx][jy];
2806 boolean player_relocated = (old_jx != jx || old_jy != jy);
2808 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2809 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2810 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2811 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2812 int leave_side_horiz = move_dir_horiz;
2813 int leave_side_vert = move_dir_vert;
2814 int enter_side = enter_side_horiz | enter_side_vert;
2815 int leave_side = leave_side_horiz | leave_side_vert;
2817 if (player->GameOver) /* do not reanimate dead player */
2820 if (!player_relocated) /* no need to relocate the player */
2823 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2825 RemoveField(jx, jy); /* temporarily remove newly placed player */
2826 DrawLevelField(jx, jy);
2829 if (player->present)
2831 while (player->MovPos)
2833 ScrollPlayer(player, SCROLL_GO_ON);
2834 ScrollScreen(NULL, SCROLL_GO_ON);
2836 AdvanceFrameAndPlayerCounters(player->index_nr);
2841 Delay(wait_delay_value);
2844 DrawPlayer(player); /* needed here only to cleanup last field */
2845 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2847 player->is_moving = FALSE;
2850 if (IS_CUSTOM_ELEMENT(old_element))
2851 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2853 player->index_bit, leave_side);
2855 CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
2856 player->index_bit, leave_side);
2858 Feld[jx][jy] = el_player;
2859 InitPlayerField(jx, jy, el_player, TRUE);
2861 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2863 Feld[jx][jy] = element;
2864 InitField(jx, jy, FALSE);
2867 if (player == local_player) /* only visually relocate local player */
2868 DrawRelocatePlayer(player);
2870 TestIfHeroTouchesBadThing(jx, jy);
2871 TestIfPlayerTouchesCustomElement(jx, jy);
2873 if (IS_CUSTOM_ELEMENT(element))
2874 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2875 player->index_bit, enter_side);
2877 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_ENTERS_X,
2878 player->index_bit, enter_side);
2881 void Explode(int ex, int ey, int phase, int mode)
2887 /* !!! eliminate this variable !!! */
2888 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2890 if (game.explosions_delayed)
2892 ExplodeField[ex][ey] = mode;
2896 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2898 int center_element = Feld[ex][ey];
2901 /* --- This is only really needed (and now handled) in "Impact()". --- */
2902 /* do not explode moving elements that left the explode field in time */
2903 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2904 center_element == EL_EMPTY &&
2905 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2909 if (mode == EX_TYPE_NORMAL ||
2910 mode == EX_TYPE_CENTER ||
2911 mode == EX_TYPE_CROSS)
2912 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2914 /* remove things displayed in background while burning dynamite */
2915 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2918 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2920 /* put moving element to center field (and let it explode there) */
2921 center_element = MovingOrBlocked2Element(ex, ey);
2922 RemoveMovingField(ex, ey);
2923 Feld[ex][ey] = center_element;
2926 last_phase = element_info[center_element].explosion_delay + 1;
2928 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2930 int xx = x - ex + 1;
2931 int yy = y - ey + 1;
2934 if (!IN_LEV_FIELD(x, y) ||
2935 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2936 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2939 element = Feld[x][y];
2941 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2943 element = MovingOrBlocked2Element(x, y);
2945 if (!IS_EXPLOSION_PROOF(element))
2946 RemoveMovingField(x, y);
2949 /* indestructible elements can only explode in center (but not flames) */
2950 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
2951 mode == EX_TYPE_BORDER)) ||
2952 element == EL_FLAMES)
2955 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
2956 behaviour, for example when touching a yamyam that explodes to rocks
2957 with active deadly shield, a rock is created under the player !!! */
2958 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
2960 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
2961 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
2962 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
2964 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2967 if (IS_ACTIVE_BOMB(element))
2969 /* re-activate things under the bomb like gate or penguin */
2970 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
2977 /* save walkable background elements while explosion on same tile */
2978 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
2979 (x != ex || y != ey || mode == EX_TYPE_BORDER))
2980 Back[x][y] = element;
2982 /* ignite explodable elements reached by other explosion */
2983 if (element == EL_EXPLOSION)
2984 element = Store2[x][y];
2986 if (AmoebaNr[x][y] &&
2987 (element == EL_AMOEBA_FULL ||
2988 element == EL_BD_AMOEBA ||
2989 element == EL_AMOEBA_GROWING))
2991 AmoebaCnt[AmoebaNr[x][y]]--;
2992 AmoebaCnt2[AmoebaNr[x][y]]--;
2997 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2999 switch(StorePlayer[ex][ey])
3002 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3005 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3008 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3012 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3016 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3017 Store[x][y] = EL_EMPTY;
3019 else if (center_element == EL_MOLE)
3020 Store[x][y] = EL_EMERALD_RED;
3021 else if (center_element == EL_PENGUIN)
3022 Store[x][y] = EL_EMERALD_PURPLE;
3023 else if (center_element == EL_BUG)
3024 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3025 else if (center_element == EL_BD_BUTTERFLY)
3026 Store[x][y] = EL_BD_DIAMOND;
3027 else if (center_element == EL_SP_ELECTRON)
3028 Store[x][y] = EL_SP_INFOTRON;
3029 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3030 Store[x][y] = level.amoeba_content;
3031 else if (center_element == EL_YAMYAM)
3032 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3033 else if (IS_CUSTOM_ELEMENT(center_element) &&
3034 element_info[center_element].content[xx][yy] != EL_EMPTY)
3035 Store[x][y] = element_info[center_element].content[xx][yy];
3036 else if (element == EL_WALL_EMERALD)
3037 Store[x][y] = EL_EMERALD;
3038 else if (element == EL_WALL_DIAMOND)
3039 Store[x][y] = EL_DIAMOND;
3040 else if (element == EL_WALL_BD_DIAMOND)
3041 Store[x][y] = EL_BD_DIAMOND;
3042 else if (element == EL_WALL_EMERALD_YELLOW)
3043 Store[x][y] = EL_EMERALD_YELLOW;
3044 else if (element == EL_WALL_EMERALD_RED)
3045 Store[x][y] = EL_EMERALD_RED;
3046 else if (element == EL_WALL_EMERALD_PURPLE)
3047 Store[x][y] = EL_EMERALD_PURPLE;
3048 else if (element == EL_WALL_PEARL)
3049 Store[x][y] = EL_PEARL;
3050 else if (element == EL_WALL_CRYSTAL)
3051 Store[x][y] = EL_CRYSTAL;
3052 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3053 Store[x][y] = element_info[element].content[1][1];
3055 Store[x][y] = EL_EMPTY;
3057 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3058 center_element == EL_AMOEBA_TO_DIAMOND)
3059 Store2[x][y] = element;
3061 Feld[x][y] = EL_EXPLOSION;
3062 GfxElement[x][y] = center_element;
3064 ExplodePhase[x][y] = 1;
3065 ExplodeDelay[x][y] = last_phase;
3070 if (center_element == EL_YAMYAM)
3071 game.yamyam_content_nr =
3072 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3084 GfxFrame[x][y] = 0; /* restart explosion animation */
3086 last_phase = ExplodeDelay[x][y];
3088 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3092 /* activate this even in non-DEBUG version until cause for crash in
3093 getGraphicAnimationFrame() (see below) is found and eliminated */
3098 if (GfxElement[x][y] == EL_UNDEFINED)
3101 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3102 printf("Explode(): This should never happen!\n");
3105 GfxElement[x][y] = EL_EMPTY;
3109 border_element = Store2[x][y];
3110 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3111 border_element = StorePlayer[x][y];
3113 if (phase == element_info[border_element].ignition_delay ||
3114 phase == last_phase)
3116 boolean border_explosion = FALSE;
3118 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3119 !PLAYER_EXPLOSION_PROTECTED(x, y))
3121 KillHeroUnlessExplosionProtected(x, y);
3122 border_explosion = TRUE;
3124 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3126 Feld[x][y] = Store2[x][y];
3129 border_explosion = TRUE;
3131 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3133 AmoebeUmwandeln(x, y);
3135 border_explosion = TRUE;
3138 /* if an element just explodes due to another explosion (chain-reaction),
3139 do not immediately end the new explosion when it was the last frame of
3140 the explosion (as it would be done in the following "if"-statement!) */
3141 if (border_explosion && phase == last_phase)
3145 if (phase == last_phase)
3149 element = Feld[x][y] = Store[x][y];
3150 Store[x][y] = Store2[x][y] = 0;
3151 GfxElement[x][y] = EL_UNDEFINED;
3153 /* player can escape from explosions and might therefore be still alive */
3154 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3155 element <= EL_PLAYER_IS_EXPLODING_4)
3156 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3158 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3159 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3160 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3163 /* restore probably existing indestructible background element */
3164 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3165 element = Feld[x][y] = Back[x][y];
3168 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3169 GfxDir[x][y] = MV_NO_MOVING;
3170 ChangeDelay[x][y] = 0;
3171 ChangePage[x][y] = -1;
3173 #if USE_NEW_COLLECT_COUNT
3177 InitField_WithBug2(x, y, FALSE);
3179 DrawLevelField(x, y);
3181 TestIfElementTouchesCustomElement(x, y);
3183 if (GFX_CRUMBLED(element))
3184 DrawLevelFieldCrumbledSandNeighbours(x, y);
3186 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3187 StorePlayer[x][y] = 0;
3189 if (ELEM_IS_PLAYER(element))
3190 RelocatePlayer(x, y, element);
3192 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3194 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3195 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3198 DrawLevelFieldCrumbledSand(x, y);
3200 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3202 DrawLevelElement(x, y, Back[x][y]);
3203 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3205 else if (IS_WALKABLE_UNDER(Back[x][y]))
3207 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3208 DrawLevelElementThruMask(x, y, Back[x][y]);
3210 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3211 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3215 void DynaExplode(int ex, int ey)
3218 int dynabomb_element = Feld[ex][ey];
3219 int dynabomb_size = 1;
3220 boolean dynabomb_xl = FALSE;
3221 struct PlayerInfo *player;
3222 static int xy[4][2] =
3230 if (IS_ACTIVE_BOMB(dynabomb_element))
3232 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3233 dynabomb_size = player->dynabomb_size;
3234 dynabomb_xl = player->dynabomb_xl;
3235 player->dynabombs_left++;
3238 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3240 for (i = 0; i < NUM_DIRECTIONS; i++)
3242 for (j = 1; j <= dynabomb_size; j++)
3244 int x = ex + j * xy[i][0];
3245 int y = ey + j * xy[i][1];
3248 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3251 element = Feld[x][y];
3253 /* do not restart explosions of fields with active bombs */
3254 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3257 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3259 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3260 !IS_DIGGABLE(element) && !dynabomb_xl)
3266 void Bang(int x, int y)
3268 int element = MovingOrBlocked2Element(x, y);
3270 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3272 struct PlayerInfo *player = PLAYERINFO(x, y);
3274 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3275 player->element_nr);
3282 case EL_BD_BUTTERFLY:
3285 case EL_DARK_YAMYAM:
3289 RaiseScoreElement(element);
3290 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3292 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3293 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3294 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3295 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3296 case EL_DYNABOMB_INCREASE_NUMBER:
3297 case EL_DYNABOMB_INCREASE_SIZE:
3298 case EL_DYNABOMB_INCREASE_POWER:
3303 case EL_LAMP_ACTIVE:
3304 case EL_AMOEBA_TO_DIAMOND:
3305 if (IS_PLAYER(x, y))
3306 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3308 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3311 if (element_info[element].explosion_type == EXPLODES_CROSS)
3312 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3313 else if (element_info[element].explosion_type == EXPLODES_1X1)
3314 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3316 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3320 CheckTriggeredElementChange(element, CE_EXPLOSION_OF_X);
3323 void SplashAcid(int x, int y)
3325 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3326 (!IN_LEV_FIELD(x - 1, y - 2) ||
3327 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3328 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3330 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3331 (!IN_LEV_FIELD(x + 1, y - 2) ||
3332 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3333 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3335 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3338 static void InitBeltMovement()
3340 static int belt_base_element[4] =
3342 EL_CONVEYOR_BELT_1_LEFT,
3343 EL_CONVEYOR_BELT_2_LEFT,
3344 EL_CONVEYOR_BELT_3_LEFT,
3345 EL_CONVEYOR_BELT_4_LEFT
3347 static int belt_base_active_element[4] =
3349 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3350 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3351 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3352 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3357 /* set frame order for belt animation graphic according to belt direction */
3358 for (i = 0; i < NUM_BELTS; i++)
3362 for (j = 0; j < NUM_BELT_PARTS; j++)
3364 int element = belt_base_active_element[belt_nr] + j;
3365 int graphic = el2img(element);
3367 if (game.belt_dir[i] == MV_LEFT)
3368 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3370 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3374 for (y = 0; y < lev_fieldy; y++)
3376 for (x = 0; x < lev_fieldx; x++)
3378 int element = Feld[x][y];
3380 for (i = 0; i < NUM_BELTS; i++)
3382 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3384 int e_belt_nr = getBeltNrFromBeltElement(element);
3387 if (e_belt_nr == belt_nr)
3389 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3391 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3399 static void ToggleBeltSwitch(int x, int y)
3401 static int belt_base_element[4] =
3403 EL_CONVEYOR_BELT_1_LEFT,
3404 EL_CONVEYOR_BELT_2_LEFT,
3405 EL_CONVEYOR_BELT_3_LEFT,
3406 EL_CONVEYOR_BELT_4_LEFT
3408 static int belt_base_active_element[4] =
3410 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3411 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3412 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3413 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3415 static int belt_base_switch_element[4] =
3417 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3418 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3419 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3420 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3422 static int belt_move_dir[4] =
3430 int element = Feld[x][y];
3431 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3432 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3433 int belt_dir = belt_move_dir[belt_dir_nr];
3436 if (!IS_BELT_SWITCH(element))
3439 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3440 game.belt_dir[belt_nr] = belt_dir;
3442 if (belt_dir_nr == 3)
3445 /* set frame order for belt animation graphic according to belt direction */
3446 for (i = 0; i < NUM_BELT_PARTS; i++)
3448 int element = belt_base_active_element[belt_nr] + i;
3449 int graphic = el2img(element);
3451 if (belt_dir == MV_LEFT)
3452 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3454 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3457 for (yy = 0; yy < lev_fieldy; yy++)
3459 for (xx = 0; xx < lev_fieldx; xx++)
3461 int element = Feld[xx][yy];
3463 if (IS_BELT_SWITCH(element))
3465 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3467 if (e_belt_nr == belt_nr)
3469 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3470 DrawLevelField(xx, yy);
3473 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3475 int e_belt_nr = getBeltNrFromBeltElement(element);
3477 if (e_belt_nr == belt_nr)
3479 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3481 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3482 DrawLevelField(xx, yy);
3485 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3487 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3489 if (e_belt_nr == belt_nr)
3491 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3493 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3494 DrawLevelField(xx, yy);
3501 static void ToggleSwitchgateSwitch(int x, int y)
3505 game.switchgate_pos = !game.switchgate_pos;
3507 for (yy = 0; yy < lev_fieldy; yy++)
3509 for (xx = 0; xx < lev_fieldx; xx++)
3511 int element = Feld[xx][yy];
3513 if (element == EL_SWITCHGATE_SWITCH_UP ||
3514 element == EL_SWITCHGATE_SWITCH_DOWN)
3516 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3517 DrawLevelField(xx, yy);
3519 else if (element == EL_SWITCHGATE_OPEN ||
3520 element == EL_SWITCHGATE_OPENING)
3522 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3524 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3526 else if (element == EL_SWITCHGATE_CLOSED ||
3527 element == EL_SWITCHGATE_CLOSING)
3529 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3531 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3537 static int getInvisibleActiveFromInvisibleElement(int element)
3539 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3540 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3541 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3545 static int getInvisibleFromInvisibleActiveElement(int element)
3547 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3548 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3549 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3553 static void RedrawAllLightSwitchesAndInvisibleElements()
3557 for (y = 0; y < lev_fieldy; y++)
3559 for (x = 0; x < lev_fieldx; x++)
3561 int element = Feld[x][y];
3563 if (element == EL_LIGHT_SWITCH &&
3564 game.light_time_left > 0)
3566 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3567 DrawLevelField(x, y);
3569 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3570 game.light_time_left == 0)
3572 Feld[x][y] = EL_LIGHT_SWITCH;
3573 DrawLevelField(x, y);
3575 else if (element == EL_INVISIBLE_STEELWALL ||
3576 element == EL_INVISIBLE_WALL ||
3577 element == EL_INVISIBLE_SAND)
3579 if (game.light_time_left > 0)
3580 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3582 DrawLevelField(x, y);
3584 /* uncrumble neighbour fields, if needed */
3585 if (element == EL_INVISIBLE_SAND)
3586 DrawLevelFieldCrumbledSandNeighbours(x, y);
3588 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3589 element == EL_INVISIBLE_WALL_ACTIVE ||
3590 element == EL_INVISIBLE_SAND_ACTIVE)
3592 if (game.light_time_left == 0)
3593 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3595 DrawLevelField(x, y);
3597 /* re-crumble neighbour fields, if needed */
3598 if (element == EL_INVISIBLE_SAND)
3599 DrawLevelFieldCrumbledSandNeighbours(x, y);
3605 static void ToggleLightSwitch(int x, int y)
3607 int element = Feld[x][y];
3609 game.light_time_left =
3610 (element == EL_LIGHT_SWITCH ?
3611 level.time_light * FRAMES_PER_SECOND : 0);
3613 RedrawAllLightSwitchesAndInvisibleElements();
3616 static void ActivateTimegateSwitch(int x, int y)
3620 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3622 for (yy = 0; yy < lev_fieldy; yy++)
3624 for (xx = 0; xx < lev_fieldx; xx++)
3626 int element = Feld[xx][yy];
3628 if (element == EL_TIMEGATE_CLOSED ||
3629 element == EL_TIMEGATE_CLOSING)
3631 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3632 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3636 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3638 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3639 DrawLevelField(xx, yy);
3646 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3649 void Impact(int x, int y)
3651 boolean last_line = (y == lev_fieldy - 1);
3652 boolean object_hit = FALSE;
3653 boolean impact = (last_line || object_hit);
3654 int element = Feld[x][y];
3655 int smashed = EL_STEELWALL;
3657 if (!last_line) /* check if element below was hit */
3659 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3662 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3663 MovDir[x][y + 1] != MV_DOWN ||
3664 MovPos[x][y + 1] <= TILEY / 2));
3666 /* do not smash moving elements that left the smashed field in time */
3667 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3668 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3672 smashed = MovingOrBlocked2Element(x, y + 1);
3674 impact = (last_line || object_hit);
3677 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3679 SplashAcid(x, y + 1);
3683 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3684 /* only reset graphic animation if graphic really changes after impact */
3686 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3688 ResetGfxAnimation(x, y);
3689 DrawLevelField(x, y);
3692 if (impact && CAN_EXPLODE_IMPACT(element))
3697 else if (impact && element == EL_PEARL)
3699 ResetGfxAnimation(x, y);
3701 Feld[x][y] = EL_PEARL_BREAKING;
3702 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3705 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3707 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3712 if (impact && element == EL_AMOEBA_DROP)
3714 if (object_hit && IS_PLAYER(x, y + 1))
3715 KillHeroUnlessEnemyProtected(x, y + 1);
3716 else if (object_hit && smashed == EL_PENGUIN)
3720 Feld[x][y] = EL_AMOEBA_GROWING;
3721 Store[x][y] = EL_AMOEBA_WET;
3723 ResetRandomAnimationValue(x, y);
3728 if (object_hit) /* check which object was hit */
3730 if (CAN_PASS_MAGIC_WALL(element) &&
3731 (smashed == EL_MAGIC_WALL ||
3732 smashed == EL_BD_MAGIC_WALL))
3735 int activated_magic_wall =
3736 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3737 EL_BD_MAGIC_WALL_ACTIVE);
3739 /* activate magic wall / mill */
3740 for (yy = 0; yy < lev_fieldy; yy++)
3741 for (xx = 0; xx < lev_fieldx; xx++)
3742 if (Feld[xx][yy] == smashed)
3743 Feld[xx][yy] = activated_magic_wall;
3745 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3746 game.magic_wall_active = TRUE;
3748 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3749 SND_MAGIC_WALL_ACTIVATING :
3750 SND_BD_MAGIC_WALL_ACTIVATING));
3753 if (IS_PLAYER(x, y + 1))
3755 if (CAN_SMASH_PLAYER(element))
3757 KillHeroUnlessEnemyProtected(x, y + 1);
3761 else if (smashed == EL_PENGUIN)
3763 if (CAN_SMASH_PLAYER(element))
3769 else if (element == EL_BD_DIAMOND)
3771 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3777 else if (((element == EL_SP_INFOTRON ||
3778 element == EL_SP_ZONK) &&
3779 (smashed == EL_SP_SNIKSNAK ||
3780 smashed == EL_SP_ELECTRON ||
3781 smashed == EL_SP_DISK_ORANGE)) ||
3782 (element == EL_SP_INFOTRON &&
3783 smashed == EL_SP_DISK_YELLOW))
3788 else if (CAN_SMASH_EVERYTHING(element))
3790 if (IS_CLASSIC_ENEMY(smashed) ||
3791 CAN_EXPLODE_SMASHED(smashed))
3796 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3798 if (smashed == EL_LAMP ||
3799 smashed == EL_LAMP_ACTIVE)
3804 else if (smashed == EL_NUT)
3806 Feld[x][y + 1] = EL_NUT_BREAKING;
3807 PlayLevelSound(x, y, SND_NUT_BREAKING);
3808 RaiseScoreElement(EL_NUT);
3811 else if (smashed == EL_PEARL)
3813 ResetGfxAnimation(x, y);
3815 Feld[x][y + 1] = EL_PEARL_BREAKING;
3816 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3819 else if (smashed == EL_DIAMOND)
3821 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3822 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3825 else if (IS_BELT_SWITCH(smashed))
3827 ToggleBeltSwitch(x, y + 1);
3829 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3830 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3832 ToggleSwitchgateSwitch(x, y + 1);
3834 else if (smashed == EL_LIGHT_SWITCH ||
3835 smashed == EL_LIGHT_SWITCH_ACTIVE)
3837 ToggleLightSwitch(x, y + 1);
3842 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3845 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3847 CheckElementChangeBySide(x, y + 1, smashed, element,
3848 CE_SWITCHED, CH_SIDE_TOP);
3849 CheckTriggeredElementChangeBySide(smashed, CE_SWITCH_OF_X,
3855 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3860 /* play sound of magic wall / mill */
3862 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3863 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3865 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3866 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3867 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3868 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3873 /* play sound of object that hits the ground */
3874 if (last_line || object_hit)
3875 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3878 inline static void TurnRoundExt(int x, int y)
3890 { 0, 0 }, { 0, 0 }, { 0, 0 },
3895 int left, right, back;
3899 { MV_DOWN, MV_UP, MV_RIGHT },
3900 { MV_UP, MV_DOWN, MV_LEFT },
3902 { MV_LEFT, MV_RIGHT, MV_DOWN },
3906 { MV_RIGHT, MV_LEFT, MV_UP }
3909 int element = Feld[x][y];
3910 int move_pattern = element_info[element].move_pattern;
3912 int old_move_dir = MovDir[x][y];
3913 int left_dir = turn[old_move_dir].left;
3914 int right_dir = turn[old_move_dir].right;
3915 int back_dir = turn[old_move_dir].back;
3917 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3918 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3919 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3920 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3922 int left_x = x + left_dx, left_y = y + left_dy;
3923 int right_x = x + right_dx, right_y = y + right_dy;
3924 int move_x = x + move_dx, move_y = y + move_dy;
3928 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3930 TestIfBadThingTouchesOtherBadThing(x, y);
3932 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3933 MovDir[x][y] = right_dir;
3934 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3935 MovDir[x][y] = left_dir;
3937 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3939 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3942 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
3944 TestIfBadThingTouchesOtherBadThing(x, y);
3946 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3947 MovDir[x][y] = left_dir;
3948 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3949 MovDir[x][y] = right_dir;
3951 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
3953 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3956 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3958 TestIfBadThingTouchesOtherBadThing(x, y);
3960 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
3961 MovDir[x][y] = left_dir;
3962 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
3963 MovDir[x][y] = right_dir;
3965 if (MovDir[x][y] != old_move_dir)
3968 else if (element == EL_YAMYAM)
3970 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
3971 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
3973 if (can_turn_left && can_turn_right)
3974 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3975 else if (can_turn_left)
3976 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3977 else if (can_turn_right)
3978 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3980 MovDir[x][y] = back_dir;
3982 MovDelay[x][y] = 16 + 16 * RND(3);
3984 else if (element == EL_DARK_YAMYAM)
3986 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
3988 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
3991 if (can_turn_left && can_turn_right)
3992 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3993 else if (can_turn_left)
3994 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3995 else if (can_turn_right)
3996 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3998 MovDir[x][y] = back_dir;
4000 MovDelay[x][y] = 16 + 16 * RND(3);
4002 else if (element == EL_PACMAN)
4004 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4005 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4007 if (can_turn_left && can_turn_right)
4008 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4009 else if (can_turn_left)
4010 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4011 else if (can_turn_right)
4012 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4014 MovDir[x][y] = back_dir;
4016 MovDelay[x][y] = 6 + RND(40);
4018 else if (element == EL_PIG)
4020 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4021 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4022 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4023 boolean should_turn_left, should_turn_right, should_move_on;
4025 int rnd = RND(rnd_value);
4027 should_turn_left = (can_turn_left &&
4029 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4030 y + back_dy + left_dy)));
4031 should_turn_right = (can_turn_right &&
4033 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4034 y + back_dy + right_dy)));
4035 should_move_on = (can_move_on &&
4038 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4039 y + move_dy + left_dy) ||
4040 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4041 y + move_dy + right_dy)));
4043 if (should_turn_left || should_turn_right || should_move_on)
4045 if (should_turn_left && should_turn_right && should_move_on)
4046 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4047 rnd < 2 * rnd_value / 3 ? right_dir :
4049 else if (should_turn_left && should_turn_right)
4050 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4051 else if (should_turn_left && should_move_on)
4052 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4053 else if (should_turn_right && should_move_on)
4054 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4055 else if (should_turn_left)
4056 MovDir[x][y] = left_dir;
4057 else if (should_turn_right)
4058 MovDir[x][y] = right_dir;
4059 else if (should_move_on)
4060 MovDir[x][y] = old_move_dir;
4062 else if (can_move_on && rnd > rnd_value / 8)
4063 MovDir[x][y] = old_move_dir;
4064 else if (can_turn_left && can_turn_right)
4065 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4066 else if (can_turn_left && rnd > rnd_value / 8)
4067 MovDir[x][y] = left_dir;
4068 else if (can_turn_right && rnd > rnd_value/8)
4069 MovDir[x][y] = right_dir;
4071 MovDir[x][y] = back_dir;
4073 xx = x + move_xy[MovDir[x][y]].x;
4074 yy = y + move_xy[MovDir[x][y]].y;
4076 if (!IN_LEV_FIELD(xx, yy) ||
4077 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4078 MovDir[x][y] = old_move_dir;
4082 else if (element == EL_DRAGON)
4084 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4085 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4086 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4088 int rnd = RND(rnd_value);
4090 if (can_move_on && rnd > rnd_value / 8)
4091 MovDir[x][y] = old_move_dir;
4092 else if (can_turn_left && can_turn_right)
4093 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4094 else if (can_turn_left && rnd > rnd_value / 8)
4095 MovDir[x][y] = left_dir;
4096 else if (can_turn_right && rnd > rnd_value / 8)
4097 MovDir[x][y] = right_dir;
4099 MovDir[x][y] = back_dir;
4101 xx = x + move_xy[MovDir[x][y]].x;
4102 yy = y + move_xy[MovDir[x][y]].y;
4104 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4105 MovDir[x][y] = old_move_dir;
4109 else if (element == EL_MOLE)
4111 boolean can_move_on =
4112 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4113 IS_AMOEBOID(Feld[move_x][move_y]) ||
4114 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4117 boolean can_turn_left =
4118 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4119 IS_AMOEBOID(Feld[left_x][left_y])));
4121 boolean can_turn_right =
4122 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4123 IS_AMOEBOID(Feld[right_x][right_y])));
4125 if (can_turn_left && can_turn_right)
4126 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4127 else if (can_turn_left)
4128 MovDir[x][y] = left_dir;
4130 MovDir[x][y] = right_dir;
4133 if (MovDir[x][y] != old_move_dir)
4136 else if (element == EL_BALLOON)
4138 MovDir[x][y] = game.balloon_dir;
4141 else if (element == EL_SPRING)
4143 if (MovDir[x][y] & MV_HORIZONTAL &&
4144 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4145 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4146 MovDir[x][y] = MV_NO_MOVING;
4150 else if (element == EL_ROBOT ||
4151 element == EL_SATELLITE ||
4152 element == EL_PENGUIN)
4154 int attr_x = -1, attr_y = -1;
4165 for (i = 0; i < MAX_PLAYERS; i++)
4167 struct PlayerInfo *player = &stored_player[i];
4168 int jx = player->jx, jy = player->jy;
4170 if (!player->active)
4174 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4182 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4183 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4184 game.engine_version < VERSION_IDENT(3,1,0,0)))
4190 if (element == EL_PENGUIN)
4193 static int xy[4][2] =
4201 for (i = 0; i < NUM_DIRECTIONS; i++)
4203 int ex = x + xy[i][0];
4204 int ey = y + xy[i][1];
4206 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4215 MovDir[x][y] = MV_NO_MOVING;
4217 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4218 else if (attr_x > x)
4219 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4221 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4222 else if (attr_y > y)
4223 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4225 if (element == EL_ROBOT)
4229 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4230 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4231 Moving2Blocked(x, y, &newx, &newy);
4233 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4234 MovDelay[x][y] = 8 + 8 * !RND(3);
4236 MovDelay[x][y] = 16;
4238 else if (element == EL_PENGUIN)
4244 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4246 boolean first_horiz = RND(2);
4247 int new_move_dir = MovDir[x][y];
4250 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4251 Moving2Blocked(x, y, &newx, &newy);
4253 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4257 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4258 Moving2Blocked(x, y, &newx, &newy);
4260 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4263 MovDir[x][y] = old_move_dir;
4267 else /* (element == EL_SATELLITE) */
4273 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4275 boolean first_horiz = RND(2);
4276 int new_move_dir = MovDir[x][y];
4279 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4280 Moving2Blocked(x, y, &newx, &newy);
4282 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4286 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4287 Moving2Blocked(x, y, &newx, &newy);
4289 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4292 MovDir[x][y] = old_move_dir;
4297 else if (move_pattern == MV_TURNING_LEFT ||
4298 move_pattern == MV_TURNING_RIGHT ||
4299 move_pattern == MV_TURNING_LEFT_RIGHT ||
4300 move_pattern == MV_TURNING_RIGHT_LEFT ||
4301 move_pattern == MV_TURNING_RANDOM ||
4302 move_pattern == MV_ALL_DIRECTIONS)
4304 boolean can_turn_left =
4305 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4306 boolean can_turn_right =
4307 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4309 if (element_info[element].move_stepsize == 0) /* "not moving" */
4312 if (move_pattern == MV_TURNING_LEFT)
4313 MovDir[x][y] = left_dir;
4314 else if (move_pattern == MV_TURNING_RIGHT)
4315 MovDir[x][y] = right_dir;
4316 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4317 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4318 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4319 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4320 else if (move_pattern == MV_TURNING_RANDOM)
4321 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4322 can_turn_right && !can_turn_left ? right_dir :
4323 RND(2) ? left_dir : right_dir);
4324 else if (can_turn_left && can_turn_right)
4325 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4326 else if (can_turn_left)
4327 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4328 else if (can_turn_right)
4329 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4331 MovDir[x][y] = back_dir;
4333 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4335 else if (move_pattern == MV_HORIZONTAL ||
4336 move_pattern == MV_VERTICAL)
4338 if (move_pattern & old_move_dir)
4339 MovDir[x][y] = back_dir;
4340 else if (move_pattern == MV_HORIZONTAL)
4341 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4342 else if (move_pattern == MV_VERTICAL)
4343 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4345 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4347 else if (move_pattern & MV_ANY_DIRECTION)
4349 MovDir[x][y] = move_pattern;
4350 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4352 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4354 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4355 MovDir[x][y] = left_dir;
4356 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4357 MovDir[x][y] = right_dir;
4359 if (MovDir[x][y] != old_move_dir)
4360 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4362 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4364 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4365 MovDir[x][y] = right_dir;
4366 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4367 MovDir[x][y] = left_dir;
4369 if (MovDir[x][y] != old_move_dir)
4370 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4372 else if (move_pattern == MV_TOWARDS_PLAYER ||
4373 move_pattern == MV_AWAY_FROM_PLAYER)
4375 int attr_x = -1, attr_y = -1;
4377 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4388 for (i = 0; i < MAX_PLAYERS; i++)
4390 struct PlayerInfo *player = &stored_player[i];
4391 int jx = player->jx, jy = player->jy;
4393 if (!player->active)
4397 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4405 MovDir[x][y] = MV_NO_MOVING;
4407 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4408 else if (attr_x > x)
4409 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4411 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4412 else if (attr_y > y)
4413 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4415 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4417 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4419 boolean first_horiz = RND(2);
4420 int new_move_dir = MovDir[x][y];
4422 if (element_info[element].move_stepsize == 0) /* "not moving" */
4424 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4425 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4431 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4432 Moving2Blocked(x, y, &newx, &newy);
4434 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4438 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4439 Moving2Blocked(x, y, &newx, &newy);
4441 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4444 MovDir[x][y] = old_move_dir;
4447 else if (move_pattern == MV_WHEN_PUSHED ||
4448 move_pattern == MV_WHEN_DROPPED)
4450 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4451 MovDir[x][y] = MV_NO_MOVING;
4455 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4457 static int test_xy[7][2] =
4467 static int test_dir[7] =
4477 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4478 int move_preference = -1000000; /* start with very low preference */
4479 int new_move_dir = MV_NO_MOVING;
4480 int start_test = RND(4);
4483 for (i = 0; i < NUM_DIRECTIONS; i++)
4485 int move_dir = test_dir[start_test + i];
4486 int move_dir_preference;
4488 xx = x + test_xy[start_test + i][0];
4489 yy = y + test_xy[start_test + i][1];
4491 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4492 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4494 new_move_dir = move_dir;
4499 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4502 move_dir_preference = -1 * RunnerVisit[xx][yy];
4503 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4504 move_dir_preference = PlayerVisit[xx][yy];
4506 if (move_dir_preference > move_preference)
4508 /* prefer field that has not been visited for the longest time */
4509 move_preference = move_dir_preference;
4510 new_move_dir = move_dir;
4512 else if (move_dir_preference == move_preference &&
4513 move_dir == old_move_dir)
4515 /* prefer last direction when all directions are preferred equally */
4516 move_preference = move_dir_preference;
4517 new_move_dir = move_dir;
4521 MovDir[x][y] = new_move_dir;
4522 if (old_move_dir != new_move_dir)
4523 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4527 static void TurnRound(int x, int y)
4529 int direction = MovDir[x][y];
4533 GfxDir[x][y] = MovDir[x][y];
4535 if (direction != MovDir[x][y])
4539 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4542 static boolean JustBeingPushed(int x, int y)
4546 for (i = 0; i < MAX_PLAYERS; i++)
4548 struct PlayerInfo *player = &stored_player[i];
4550 if (player->active && player->is_pushing && player->MovPos)
4552 int next_jx = player->jx + (player->jx - player->last_jx);
4553 int next_jy = player->jy + (player->jy - player->last_jy);
4555 if (x == next_jx && y == next_jy)
4563 void StartMoving(int x, int y)
4565 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4566 int element = Feld[x][y];
4571 if (MovDelay[x][y] == 0)
4572 GfxAction[x][y] = ACTION_DEFAULT;
4574 if (CAN_FALL(element) && y < lev_fieldy - 1)
4576 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4577 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4578 if (JustBeingPushed(x, y))
4581 if (element == EL_QUICKSAND_FULL)
4583 if (IS_FREE(x, y + 1))
4585 InitMovingField(x, y, MV_DOWN);
4586 started_moving = TRUE;
4588 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4589 Store[x][y] = EL_ROCK;
4591 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4593 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4595 if (!MovDelay[x][y])
4596 MovDelay[x][y] = TILEY + 1;
4605 Feld[x][y] = EL_QUICKSAND_EMPTY;
4606 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4607 Store[x][y + 1] = Store[x][y];
4610 PlayLevelSoundAction(x, y, ACTION_FILLING);
4613 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4614 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4616 InitMovingField(x, y, MV_DOWN);
4617 started_moving = TRUE;
4619 Feld[x][y] = EL_QUICKSAND_FILLING;
4620 Store[x][y] = element;
4622 PlayLevelSoundAction(x, y, ACTION_FILLING);
4624 else if (element == EL_MAGIC_WALL_FULL)
4626 if (IS_FREE(x, y + 1))
4628 InitMovingField(x, y, MV_DOWN);
4629 started_moving = TRUE;
4631 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4632 Store[x][y] = EL_CHANGED(Store[x][y]);
4634 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4636 if (!MovDelay[x][y])
4637 MovDelay[x][y] = TILEY/4 + 1;
4646 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4647 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4648 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4652 else if (element == EL_BD_MAGIC_WALL_FULL)
4654 if (IS_FREE(x, y + 1))
4656 InitMovingField(x, y, MV_DOWN);
4657 started_moving = TRUE;
4659 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4660 Store[x][y] = EL_CHANGED2(Store[x][y]);
4662 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4664 if (!MovDelay[x][y])
4665 MovDelay[x][y] = TILEY/4 + 1;
4674 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4675 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4676 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4680 else if (CAN_PASS_MAGIC_WALL(element) &&
4681 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4682 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4684 InitMovingField(x, y, MV_DOWN);
4685 started_moving = TRUE;
4688 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4689 EL_BD_MAGIC_WALL_FILLING);
4690 Store[x][y] = element;
4692 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4694 SplashAcid(x, y + 1);
4696 InitMovingField(x, y, MV_DOWN);
4697 started_moving = TRUE;
4699 Store[x][y] = EL_ACID;
4701 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4702 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4704 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4705 CAN_FALL(element) && WasJustFalling[x][y] &&
4706 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4708 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4709 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4710 (Feld[x][y + 1] == EL_BLOCKED)))
4712 /* this is needed for a special case not covered by calling "Impact()"
4713 from "ContinueMoving()": if an element moves to a tile directly below
4714 another element which was just falling on that tile (which was empty
4715 in the previous frame), the falling element above would just stop
4716 instead of smashing the element below (in previous version, the above
4717 element was just checked for "moving" instead of "falling", resulting
4718 in incorrect smashes caused by horizontal movement of the above
4719 element; also, the case of the player being the element to smash was
4720 simply not covered here... :-/ ) */
4722 CheckCollision[x][y] = 0;
4726 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4728 if (MovDir[x][y] == MV_NO_MOVING)
4730 InitMovingField(x, y, MV_DOWN);
4731 started_moving = TRUE;
4734 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4736 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4737 MovDir[x][y] = MV_DOWN;
4739 InitMovingField(x, y, MV_DOWN);
4740 started_moving = TRUE;
4742 else if (element == EL_AMOEBA_DROP)
4744 Feld[x][y] = EL_AMOEBA_GROWING;
4745 Store[x][y] = EL_AMOEBA_WET;
4747 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4748 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4749 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4750 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4752 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4753 (IS_FREE(x - 1, y + 1) ||
4754 Feld[x - 1][y + 1] == EL_ACID));
4755 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4756 (IS_FREE(x + 1, y + 1) ||
4757 Feld[x + 1][y + 1] == EL_ACID));
4758 boolean can_fall_any = (can_fall_left || can_fall_right);
4759 boolean can_fall_both = (can_fall_left && can_fall_right);
4761 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4763 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4765 if (slippery_type == SLIPPERY_ONLY_LEFT)
4766 can_fall_right = FALSE;
4767 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4768 can_fall_left = FALSE;
4769 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4770 can_fall_right = FALSE;
4771 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4772 can_fall_left = FALSE;
4774 can_fall_any = (can_fall_left || can_fall_right);
4775 can_fall_both = (can_fall_left && can_fall_right);
4778 #if USE_NEW_SP_SLIPPERY
4779 /* !!! better use the same properties as for custom elements here !!! */
4780 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4781 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4783 can_fall_right = FALSE; /* slip down on left side */
4784 can_fall_both = FALSE;
4790 if (game.emulation == EMU_BOULDERDASH ||
4791 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4792 can_fall_right = FALSE; /* slip down on left side */
4794 can_fall_left = !(can_fall_right = RND(2));
4796 can_fall_both = FALSE;
4801 /* if not determined otherwise, prefer left side for slipping down */
4802 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4803 started_moving = TRUE;
4807 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4809 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4812 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4813 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4814 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4815 int belt_dir = game.belt_dir[belt_nr];
4817 if ((belt_dir == MV_LEFT && left_is_free) ||
4818 (belt_dir == MV_RIGHT && right_is_free))
4820 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4822 InitMovingField(x, y, belt_dir);
4823 started_moving = TRUE;
4825 Pushed[x][y] = TRUE;
4826 Pushed[nextx][y] = TRUE;
4828 GfxAction[x][y] = ACTION_DEFAULT;
4832 MovDir[x][y] = 0; /* if element was moving, stop it */
4837 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4839 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
4841 if (CAN_MOVE(element) && !started_moving)
4844 int move_pattern = element_info[element].move_pattern;
4849 if (MovDir[x][y] == MV_NO_MOVING)
4851 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4852 x, y, element, element_info[element].token_name);
4853 printf("StartMoving(): This should never happen!\n");
4858 Moving2Blocked(x, y, &newx, &newy);
4860 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4863 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4864 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4866 WasJustMoving[x][y] = 0;
4867 CheckCollision[x][y] = 0;
4869 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4871 if (Feld[x][y] != element) /* element has changed */
4875 if (!MovDelay[x][y]) /* start new movement phase */
4877 /* all objects that can change their move direction after each step
4878 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4880 if (element != EL_YAMYAM &&
4881 element != EL_DARK_YAMYAM &&
4882 element != EL_PACMAN &&
4883 !(move_pattern & MV_ANY_DIRECTION) &&
4884 move_pattern != MV_TURNING_LEFT &&
4885 move_pattern != MV_TURNING_RIGHT &&
4886 move_pattern != MV_TURNING_LEFT_RIGHT &&
4887 move_pattern != MV_TURNING_RIGHT_LEFT &&
4888 move_pattern != MV_TURNING_RANDOM)
4892 if (MovDelay[x][y] && (element == EL_BUG ||
4893 element == EL_SPACESHIP ||
4894 element == EL_SP_SNIKSNAK ||
4895 element == EL_SP_ELECTRON ||
4896 element == EL_MOLE))
4897 DrawLevelField(x, y);
4901 if (MovDelay[x][y]) /* wait some time before next movement */
4905 if (element == EL_ROBOT ||
4906 element == EL_YAMYAM ||
4907 element == EL_DARK_YAMYAM)
4909 DrawLevelElementAnimationIfNeeded(x, y, element);
4910 PlayLevelSoundAction(x, y, ACTION_WAITING);
4912 else if (element == EL_SP_ELECTRON)
4913 DrawLevelElementAnimationIfNeeded(x, y, element);
4914 else if (element == EL_DRAGON)
4917 int dir = MovDir[x][y];
4918 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4919 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4920 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4921 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4922 dir == MV_UP ? IMG_FLAMES_1_UP :
4923 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4924 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4926 GfxAction[x][y] = ACTION_ATTACKING;
4928 if (IS_PLAYER(x, y))
4929 DrawPlayerField(x, y);
4931 DrawLevelField(x, y);
4933 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4935 for (i = 1; i <= 3; i++)
4937 int xx = x + i * dx;
4938 int yy = y + i * dy;
4939 int sx = SCREENX(xx);
4940 int sy = SCREENY(yy);
4941 int flame_graphic = graphic + (i - 1);
4943 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4948 int flamed = MovingOrBlocked2Element(xx, yy);
4952 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4954 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
4955 RemoveMovingField(xx, yy);
4957 RemoveField(xx, yy);
4959 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4962 RemoveMovingField(xx, yy);
4965 ChangeDelay[xx][yy] = 0;
4967 Feld[xx][yy] = EL_FLAMES;
4969 if (IN_SCR_FIELD(sx, sy))
4971 DrawLevelFieldCrumbledSand(xx, yy);
4972 DrawGraphic(sx, sy, flame_graphic, frame);
4977 if (Feld[xx][yy] == EL_FLAMES)
4978 Feld[xx][yy] = EL_EMPTY;
4979 DrawLevelField(xx, yy);
4984 if (MovDelay[x][y]) /* element still has to wait some time */
4986 PlayLevelSoundAction(x, y, ACTION_WAITING);
4992 /* now make next step */
4994 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4996 if (DONT_COLLIDE_WITH(element) &&
4997 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4998 !PLAYER_ENEMY_PROTECTED(newx, newy))
5000 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5005 else if (CAN_MOVE_INTO_ACID(element) &&
5006 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5007 (MovDir[x][y] == MV_DOWN ||
5008 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5010 SplashAcid(newx, newy);
5011 Store[x][y] = EL_ACID;
5013 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5015 if (Feld[newx][newy] == EL_EXIT_OPEN)
5018 DrawLevelField(x, y);
5020 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5021 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5022 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5024 local_player->friends_still_needed--;
5025 if (!local_player->friends_still_needed &&
5026 !local_player->GameOver && AllPlayersGone)
5027 local_player->LevelSolved = local_player->GameOver = TRUE;
5031 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5033 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5034 DrawLevelField(newx, newy);
5036 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5038 else if (!IS_FREE(newx, newy))
5040 GfxAction[x][y] = ACTION_WAITING;
5042 if (IS_PLAYER(x, y))
5043 DrawPlayerField(x, y);
5045 DrawLevelField(x, y);
5050 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5052 if (IS_FOOD_PIG(Feld[newx][newy]))
5054 if (IS_MOVING(newx, newy))
5055 RemoveMovingField(newx, newy);
5058 Feld[newx][newy] = EL_EMPTY;
5059 DrawLevelField(newx, newy);
5062 PlayLevelSound(x, y, SND_PIG_DIGGING);
5064 else if (!IS_FREE(newx, newy))
5066 if (IS_PLAYER(x, y))
5067 DrawPlayerField(x, y);
5069 DrawLevelField(x, y);
5074 else if (IS_CUSTOM_ELEMENT(element) &&
5075 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5077 int new_element = Feld[newx][newy];
5079 if (!IS_FREE(newx, newy))
5081 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5082 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5085 /* no element can dig solid indestructible elements */
5086 if (IS_INDESTRUCTIBLE(new_element) &&
5087 !IS_DIGGABLE(new_element) &&
5088 !IS_COLLECTIBLE(new_element))
5091 if (AmoebaNr[newx][newy] &&
5092 (new_element == EL_AMOEBA_FULL ||
5093 new_element == EL_BD_AMOEBA ||
5094 new_element == EL_AMOEBA_GROWING))
5096 AmoebaCnt[AmoebaNr[newx][newy]]--;
5097 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5100 if (IS_MOVING(newx, newy))
5101 RemoveMovingField(newx, newy);
5104 RemoveField(newx, newy);
5105 DrawLevelField(newx, newy);
5108 /* if digged element was about to explode, prevent the explosion */
5109 ExplodeField[newx][newy] = EX_TYPE_NONE;
5111 PlayLevelSoundAction(x, y, action);
5114 Store[newx][newy] = EL_EMPTY;
5115 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5117 int move_leave_element = element_info[element].move_leave_element;
5119 /* this makes it possible to leave the removed element again */
5120 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5121 new_element : move_leave_element);
5124 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5126 RunnerVisit[x][y] = FrameCounter;
5127 PlayerVisit[x][y] /= 8; /* expire player visit path */
5130 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5132 if (!IS_FREE(newx, newy))
5134 if (IS_PLAYER(x, y))
5135 DrawPlayerField(x, y);
5137 DrawLevelField(x, y);
5143 boolean wanna_flame = !RND(10);
5144 int dx = newx - x, dy = newy - y;
5145 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5146 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5147 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5148 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5149 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5150 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5153 IS_CLASSIC_ENEMY(element1) ||
5154 IS_CLASSIC_ENEMY(element2)) &&
5155 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5156 element1 != EL_FLAMES && element2 != EL_FLAMES)
5158 ResetGfxAnimation(x, y);
5159 GfxAction[x][y] = ACTION_ATTACKING;
5161 if (IS_PLAYER(x, y))
5162 DrawPlayerField(x, y);
5164 DrawLevelField(x, y);
5166 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5168 MovDelay[x][y] = 50;
5172 RemoveField(newx, newy);
5174 Feld[newx][newy] = EL_FLAMES;
5175 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5178 RemoveField(newx1, newy1);
5180 Feld[newx1][newy1] = EL_FLAMES;
5182 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5185 RemoveField(newx2, newy2);
5187 Feld[newx2][newy2] = EL_FLAMES;
5194 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5195 Feld[newx][newy] == EL_DIAMOND)
5197 if (IS_MOVING(newx, newy))
5198 RemoveMovingField(newx, newy);
5201 Feld[newx][newy] = EL_EMPTY;
5202 DrawLevelField(newx, newy);
5205 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5207 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5208 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5210 if (AmoebaNr[newx][newy])
5212 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5213 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5214 Feld[newx][newy] == EL_BD_AMOEBA)
5215 AmoebaCnt[AmoebaNr[newx][newy]]--;
5220 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5222 RemoveMovingField(newx, newy);
5225 if (IS_MOVING(newx, newy))
5227 RemoveMovingField(newx, newy);
5232 Feld[newx][newy] = EL_EMPTY;
5233 DrawLevelField(newx, newy);
5236 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5238 else if ((element == EL_PACMAN || element == EL_MOLE)
5239 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5241 if (AmoebaNr[newx][newy])
5243 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5244 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5245 Feld[newx][newy] == EL_BD_AMOEBA)
5246 AmoebaCnt[AmoebaNr[newx][newy]]--;
5249 if (element == EL_MOLE)
5251 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5252 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5254 ResetGfxAnimation(x, y);
5255 GfxAction[x][y] = ACTION_DIGGING;
5256 DrawLevelField(x, y);
5258 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5260 return; /* wait for shrinking amoeba */
5262 else /* element == EL_PACMAN */
5264 Feld[newx][newy] = EL_EMPTY;
5265 DrawLevelField(newx, newy);
5266 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5269 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5270 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5271 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5273 /* wait for shrinking amoeba to completely disappear */
5276 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5278 /* object was running against a wall */
5283 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5284 if (move_pattern & MV_ANY_DIRECTION &&
5285 move_pattern == MovDir[x][y])
5287 int blocking_element =
5288 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5290 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5293 element = Feld[x][y]; /* element might have changed */
5297 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5298 DrawLevelElementAnimation(x, y, element);
5300 if (DONT_TOUCH(element))
5301 TestIfBadThingTouchesHero(x, y);
5306 InitMovingField(x, y, MovDir[x][y]);
5308 PlayLevelSoundAction(x, y, ACTION_MOVING);
5312 ContinueMoving(x, y);
5315 /* (emacs is confused here for some reason; this makes it happy again ;-) ) */
5320 void ContinueMoving(int x, int y)
5322 int element = Feld[x][y];
5323 int stored = Store[x][y];
5324 struct ElementInfo *ei = &element_info[element];
5325 int direction = MovDir[x][y];
5326 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5327 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5328 int newx = x + dx, newy = y + dy;
5329 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5330 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5331 boolean last_line = (newy == lev_fieldy - 1);
5333 MovPos[x][y] += getElementMoveStepsize(x, y);
5335 if (pushed_by_player) /* special case: moving object pushed by player */
5336 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5338 if (ABS(MovPos[x][y]) < TILEX)
5340 DrawLevelField(x, y);
5342 return; /* element is still moving */
5345 /* element reached destination field */
5347 Feld[x][y] = EL_EMPTY;
5348 Feld[newx][newy] = element;
5349 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5351 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5353 element = Feld[newx][newy] = EL_ACID;
5355 else if (element == EL_MOLE)
5357 Feld[x][y] = EL_SAND;
5359 DrawLevelFieldCrumbledSandNeighbours(x, y);
5361 else if (element == EL_QUICKSAND_FILLING)
5363 element = Feld[newx][newy] = get_next_element(element);
5364 Store[newx][newy] = Store[x][y];
5366 else if (element == EL_QUICKSAND_EMPTYING)
5368 Feld[x][y] = get_next_element(element);
5369 element = Feld[newx][newy] = Store[x][y];
5371 else if (element == EL_MAGIC_WALL_FILLING)
5373 element = Feld[newx][newy] = get_next_element(element);
5374 if (!game.magic_wall_active)
5375 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5376 Store[newx][newy] = Store[x][y];
5378 else if (element == EL_MAGIC_WALL_EMPTYING)
5380 Feld[x][y] = get_next_element(element);
5381 if (!game.magic_wall_active)
5382 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5383 element = Feld[newx][newy] = Store[x][y];
5385 #if USE_NEW_COLLECT_COUNT
5386 InitField(newx, newy, FALSE);
5389 else if (element == EL_BD_MAGIC_WALL_FILLING)
5391 element = Feld[newx][newy] = get_next_element(element);
5392 if (!game.magic_wall_active)
5393 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5394 Store[newx][newy] = Store[x][y];
5396 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5398 Feld[x][y] = get_next_element(element);
5399 if (!game.magic_wall_active)
5400 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5401 element = Feld[newx][newy] = Store[x][y];
5403 #if USE_NEW_COLLECT_COUNT
5404 InitField(newx, newy, FALSE);
5407 else if (element == EL_AMOEBA_DROPPING)
5409 Feld[x][y] = get_next_element(element);
5410 element = Feld[newx][newy] = Store[x][y];
5412 else if (element == EL_SOKOBAN_OBJECT)
5415 Feld[x][y] = Back[x][y];
5417 if (Back[newx][newy])
5418 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5420 Back[x][y] = Back[newx][newy] = 0;
5423 Store[x][y] = EL_EMPTY;
5428 MovDelay[newx][newy] = 0;
5430 if (CAN_CHANGE(element))
5432 /* copy element change control values to new field */
5433 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5434 ChangePage[newx][newy] = ChangePage[x][y];
5435 Changed[newx][newy] = Changed[x][y];
5436 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5438 #if USE_NEW_COLLECT_COUNT
5439 Count[newx][newy] = Count[x][y];
5443 ChangeDelay[x][y] = 0;
5444 ChangePage[x][y] = -1;
5445 Changed[x][y] = FALSE;
5446 ChangeEvent[x][y] = -1;
5448 #if USE_NEW_COLLECT_COUNT
5452 /* copy animation control values to new field */
5453 GfxFrame[newx][newy] = GfxFrame[x][y];
5454 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5455 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5456 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5458 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5460 /* some elements can leave other elements behind after moving */
5461 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5462 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5463 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5465 int move_leave_element = ei->move_leave_element;
5467 /* this makes it possible to leave the removed element again */
5468 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5469 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5470 move_leave_element = stored;
5472 Feld[x][y] = move_leave_element;
5474 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5475 MovDir[x][y] = direction;
5477 InitField(x, y, FALSE);
5479 if (GFX_CRUMBLED(Feld[x][y]))
5480 DrawLevelFieldCrumbledSandNeighbours(x, y);
5482 if (ELEM_IS_PLAYER(move_leave_element))
5483 RelocatePlayer(x, y, move_leave_element);
5486 /* do this after checking for left-behind element */
5487 ResetGfxAnimation(x, y); /* reset animation values for old field */
5489 if (!CAN_MOVE(element) ||
5490 (CAN_FALL(element) && direction == MV_DOWN &&
5491 (element == EL_SPRING ||
5492 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5493 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5494 GfxDir[x][y] = MovDir[newx][newy] = 0;
5496 DrawLevelField(x, y);
5497 DrawLevelField(newx, newy);
5499 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5501 /* prevent pushed element from moving on in pushed direction */
5502 if (pushed_by_player && CAN_MOVE(element) &&
5503 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5504 !(element_info[element].move_pattern & direction))
5505 TurnRound(newx, newy);
5507 /* prevent elements on conveyor belt from moving on in last direction */
5508 if (pushed_by_conveyor && CAN_FALL(element) &&
5509 direction & MV_HORIZONTAL)
5510 MovDir[newx][newy] = 0;
5512 if (!pushed_by_player)
5514 int nextx = newx + dx, nexty = newy + dy;
5515 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5517 WasJustMoving[newx][newy] = 3;
5519 if (CAN_FALL(element) && direction == MV_DOWN)
5520 WasJustFalling[newx][newy] = 3;
5522 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5523 CheckCollision[newx][newy] = 2;
5526 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5528 TestIfBadThingTouchesHero(newx, newy);
5529 TestIfBadThingTouchesFriend(newx, newy);
5531 if (!IS_CUSTOM_ELEMENT(element))
5532 TestIfBadThingTouchesOtherBadThing(newx, newy);
5534 else if (element == EL_PENGUIN)
5535 TestIfFriendTouchesBadThing(newx, newy);
5537 /* give the player one last chance (one more frame) to move away */
5538 if (CAN_FALL(element) && direction == MV_DOWN &&
5539 (last_line || (!IS_FREE(x, newy + 1) &&
5540 (!IS_PLAYER(x, newy + 1) ||
5541 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5544 if (pushed_by_player && !game.use_change_when_pushing_bug)
5546 int dig_side = MV_DIR_OPPOSITE(direction);
5547 struct PlayerInfo *player = PLAYERINFO(x, y);
5549 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5550 player->index_bit, dig_side);
5551 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
5552 player->index_bit, dig_side);
5555 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5557 TestIfElementHitsCustomElement(newx, newy, direction);
5558 TestIfPlayerTouchesCustomElement(newx, newy);
5559 TestIfElementTouchesCustomElement(newx, newy);
5562 int AmoebeNachbarNr(int ax, int ay)
5565 int element = Feld[ax][ay];
5567 static int xy[4][2] =
5575 for (i = 0; i < NUM_DIRECTIONS; i++)
5577 int x = ax + xy[i][0];
5578 int y = ay + xy[i][1];
5580 if (!IN_LEV_FIELD(x, y))
5583 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5584 group_nr = AmoebaNr[x][y];
5590 void AmoebenVereinigen(int ax, int ay)
5592 int i, x, y, xx, yy;
5593 int new_group_nr = AmoebaNr[ax][ay];
5594 static int xy[4][2] =
5602 if (new_group_nr == 0)
5605 for (i = 0; i < NUM_DIRECTIONS; i++)
5610 if (!IN_LEV_FIELD(x, y))
5613 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5614 Feld[x][y] == EL_BD_AMOEBA ||
5615 Feld[x][y] == EL_AMOEBA_DEAD) &&
5616 AmoebaNr[x][y] != new_group_nr)
5618 int old_group_nr = AmoebaNr[x][y];
5620 if (old_group_nr == 0)
5623 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5624 AmoebaCnt[old_group_nr] = 0;
5625 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5626 AmoebaCnt2[old_group_nr] = 0;
5628 for (yy = 0; yy < lev_fieldy; yy++)
5630 for (xx = 0; xx < lev_fieldx; xx++)
5632 if (AmoebaNr[xx][yy] == old_group_nr)
5633 AmoebaNr[xx][yy] = new_group_nr;
5640 void AmoebeUmwandeln(int ax, int ay)
5644 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5646 int group_nr = AmoebaNr[ax][ay];
5651 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5652 printf("AmoebeUmwandeln(): This should never happen!\n");
5657 for (y = 0; y < lev_fieldy; y++)
5659 for (x = 0; x < lev_fieldx; x++)
5661 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5664 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5668 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5669 SND_AMOEBA_TURNING_TO_GEM :
5670 SND_AMOEBA_TURNING_TO_ROCK));
5675 static int xy[4][2] =
5683 for (i = 0; i < NUM_DIRECTIONS; i++)
5688 if (!IN_LEV_FIELD(x, y))
5691 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5693 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5694 SND_AMOEBA_TURNING_TO_GEM :
5695 SND_AMOEBA_TURNING_TO_ROCK));
5702 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5705 int group_nr = AmoebaNr[ax][ay];
5706 boolean done = FALSE;
5711 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5712 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5717 for (y = 0; y < lev_fieldy; y++)
5719 for (x = 0; x < lev_fieldx; x++)
5721 if (AmoebaNr[x][y] == group_nr &&
5722 (Feld[x][y] == EL_AMOEBA_DEAD ||
5723 Feld[x][y] == EL_BD_AMOEBA ||
5724 Feld[x][y] == EL_AMOEBA_GROWING))
5727 Feld[x][y] = new_element;
5728 InitField(x, y, FALSE);
5729 DrawLevelField(x, y);
5736 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5737 SND_BD_AMOEBA_TURNING_TO_ROCK :
5738 SND_BD_AMOEBA_TURNING_TO_GEM));
5741 void AmoebeWaechst(int x, int y)
5743 static unsigned long sound_delay = 0;
5744 static unsigned long sound_delay_value = 0;
5746 if (!MovDelay[x][y]) /* start new growing cycle */
5750 if (DelayReached(&sound_delay, sound_delay_value))
5752 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5753 sound_delay_value = 30;
5757 if (MovDelay[x][y]) /* wait some time before growing bigger */
5760 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5762 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5763 6 - MovDelay[x][y]);
5765 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5768 if (!MovDelay[x][y])
5770 Feld[x][y] = Store[x][y];
5772 DrawLevelField(x, y);
5777 void AmoebaDisappearing(int x, int y)
5779 static unsigned long sound_delay = 0;
5780 static unsigned long sound_delay_value = 0;
5782 if (!MovDelay[x][y]) /* start new shrinking cycle */
5786 if (DelayReached(&sound_delay, sound_delay_value))
5787 sound_delay_value = 30;
5790 if (MovDelay[x][y]) /* wait some time before shrinking */
5793 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5795 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5796 6 - MovDelay[x][y]);
5798 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5801 if (!MovDelay[x][y])
5803 Feld[x][y] = EL_EMPTY;
5804 DrawLevelField(x, y);
5806 /* don't let mole enter this field in this cycle;
5807 (give priority to objects falling to this field from above) */
5813 void AmoebeAbleger(int ax, int ay)
5816 int element = Feld[ax][ay];
5817 int graphic = el2img(element);
5818 int newax = ax, neway = ay;
5819 static int xy[4][2] =
5827 if (!level.amoeba_speed)
5829 Feld[ax][ay] = EL_AMOEBA_DEAD;
5830 DrawLevelField(ax, ay);
5834 if (IS_ANIMATED(graphic))
5835 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5837 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5838 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5840 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5843 if (MovDelay[ax][ay])
5847 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5850 int x = ax + xy[start][0];
5851 int y = ay + xy[start][1];
5853 if (!IN_LEV_FIELD(x, y))
5856 if (IS_FREE(x, y) ||
5857 CAN_GROW_INTO(Feld[x][y]) ||
5858 Feld[x][y] == EL_QUICKSAND_EMPTY)
5864 if (newax == ax && neway == ay)
5867 else /* normal or "filled" (BD style) amoeba */
5870 boolean waiting_for_player = FALSE;
5872 for (i = 0; i < NUM_DIRECTIONS; i++)
5874 int j = (start + i) % 4;
5875 int x = ax + xy[j][0];
5876 int y = ay + xy[j][1];
5878 if (!IN_LEV_FIELD(x, y))
5881 if (IS_FREE(x, y) ||
5882 CAN_GROW_INTO(Feld[x][y]) ||
5883 Feld[x][y] == EL_QUICKSAND_EMPTY)
5889 else if (IS_PLAYER(x, y))
5890 waiting_for_player = TRUE;
5893 if (newax == ax && neway == ay) /* amoeba cannot grow */
5895 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5897 Feld[ax][ay] = EL_AMOEBA_DEAD;
5898 DrawLevelField(ax, ay);
5899 AmoebaCnt[AmoebaNr[ax][ay]]--;
5901 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5903 if (element == EL_AMOEBA_FULL)
5904 AmoebeUmwandeln(ax, ay);
5905 else if (element == EL_BD_AMOEBA)
5906 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5911 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5913 /* amoeba gets larger by growing in some direction */
5915 int new_group_nr = AmoebaNr[ax][ay];
5918 if (new_group_nr == 0)
5920 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5921 printf("AmoebeAbleger(): This should never happen!\n");
5926 AmoebaNr[newax][neway] = new_group_nr;
5927 AmoebaCnt[new_group_nr]++;
5928 AmoebaCnt2[new_group_nr]++;
5930 /* if amoeba touches other amoeba(s) after growing, unify them */
5931 AmoebenVereinigen(newax, neway);
5933 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5935 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5941 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5942 (neway == lev_fieldy - 1 && newax != ax))
5944 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5945 Store[newax][neway] = element;
5947 else if (neway == ay)
5949 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5951 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5955 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5956 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5957 Store[ax][ay] = EL_AMOEBA_DROP;
5958 ContinueMoving(ax, ay);
5962 DrawLevelField(newax, neway);
5965 void Life(int ax, int ay)
5968 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5970 int element = Feld[ax][ay];
5971 int graphic = el2img(element);
5972 boolean changed = FALSE;
5974 if (IS_ANIMATED(graphic))
5975 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5980 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5981 MovDelay[ax][ay] = life_time;
5983 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5986 if (MovDelay[ax][ay])
5990 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5992 int xx = ax+x1, yy = ay+y1;
5995 if (!IN_LEV_FIELD(xx, yy))
5998 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6000 int x = xx+x2, y = yy+y2;
6002 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6005 if (((Feld[x][y] == element ||
6006 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6008 (IS_FREE(x, y) && Stop[x][y]))
6012 if (xx == ax && yy == ay) /* field in the middle */
6014 if (nachbarn < life[0] || nachbarn > life[1])
6016 Feld[xx][yy] = EL_EMPTY;
6018 DrawLevelField(xx, yy);
6019 Stop[xx][yy] = TRUE;
6023 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6024 { /* free border field */
6025 if (nachbarn >= life[2] && nachbarn <= life[3])
6027 Feld[xx][yy] = element;
6028 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6030 DrawLevelField(xx, yy);
6031 Stop[xx][yy] = TRUE;
6038 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6039 SND_GAME_OF_LIFE_GROWING);
6042 static void InitRobotWheel(int x, int y)
6044 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6047 static void RunRobotWheel(int x, int y)
6049 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6052 static void StopRobotWheel(int x, int y)
6054 if (ZX == x && ZY == y)
6058 static void InitTimegateWheel(int x, int y)
6060 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6063 static void RunTimegateWheel(int x, int y)
6065 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6068 void CheckExit(int x, int y)
6070 if (local_player->gems_still_needed > 0 ||
6071 local_player->sokobanfields_still_needed > 0 ||
6072 local_player->lights_still_needed > 0)
6074 int element = Feld[x][y];
6075 int graphic = el2img(element);
6077 if (IS_ANIMATED(graphic))
6078 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6083 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6086 Feld[x][y] = EL_EXIT_OPENING;
6088 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6091 void CheckExitSP(int x, int y)
6093 if (local_player->gems_still_needed > 0)
6095 int element = Feld[x][y];
6096 int graphic = el2img(element);
6098 if (IS_ANIMATED(graphic))
6099 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6104 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6107 Feld[x][y] = EL_SP_EXIT_OPENING;
6109 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6112 static void CloseAllOpenTimegates()
6116 for (y = 0; y < lev_fieldy; y++)
6118 for (x = 0; x < lev_fieldx; x++)
6120 int element = Feld[x][y];
6122 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6124 Feld[x][y] = EL_TIMEGATE_CLOSING;
6126 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6132 void EdelsteinFunkeln(int x, int y)
6134 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6137 if (Feld[x][y] == EL_BD_DIAMOND)
6140 if (MovDelay[x][y] == 0) /* next animation frame */
6141 MovDelay[x][y] = 11 * !SimpleRND(500);
6143 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6147 if (setup.direct_draw && MovDelay[x][y])
6148 SetDrawtoField(DRAW_BUFFERED);
6150 DrawLevelElementAnimation(x, y, Feld[x][y]);
6152 if (MovDelay[x][y] != 0)
6154 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6155 10 - MovDelay[x][y]);
6157 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6159 if (setup.direct_draw)
6163 dest_x = FX + SCREENX(x) * TILEX;
6164 dest_y = FY + SCREENY(y) * TILEY;
6166 BlitBitmap(drawto_field, window,
6167 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6168 SetDrawtoField(DRAW_DIRECT);
6174 void MauerWaechst(int x, int y)
6178 if (!MovDelay[x][y]) /* next animation frame */
6179 MovDelay[x][y] = 3 * delay;
6181 if (MovDelay[x][y]) /* wait some time before next frame */
6185 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6187 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6188 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6190 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6193 if (!MovDelay[x][y])
6195 if (MovDir[x][y] == MV_LEFT)
6197 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6198 DrawLevelField(x - 1, y);
6200 else if (MovDir[x][y] == MV_RIGHT)
6202 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6203 DrawLevelField(x + 1, y);
6205 else if (MovDir[x][y] == MV_UP)
6207 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6208 DrawLevelField(x, y - 1);
6212 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6213 DrawLevelField(x, y + 1);
6216 Feld[x][y] = Store[x][y];
6218 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6219 DrawLevelField(x, y);
6224 void MauerAbleger(int ax, int ay)
6226 int element = Feld[ax][ay];
6227 int graphic = el2img(element);
6228 boolean oben_frei = FALSE, unten_frei = FALSE;
6229 boolean links_frei = FALSE, rechts_frei = FALSE;
6230 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6231 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6232 boolean new_wall = FALSE;
6234 if (IS_ANIMATED(graphic))
6235 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6237 if (!MovDelay[ax][ay]) /* start building new wall */
6238 MovDelay[ax][ay] = 6;
6240 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6243 if (MovDelay[ax][ay])
6247 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6249 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6251 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6253 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6256 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6257 element == EL_EXPANDABLE_WALL_ANY)
6261 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6262 Store[ax][ay-1] = element;
6263 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6264 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6265 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6266 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6271 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6272 Store[ax][ay+1] = element;
6273 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6274 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6275 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6276 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6281 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6282 element == EL_EXPANDABLE_WALL_ANY ||
6283 element == EL_EXPANDABLE_WALL)
6287 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6288 Store[ax-1][ay] = element;
6289 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6290 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6291 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6292 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6298 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6299 Store[ax+1][ay] = element;
6300 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6301 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6302 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6303 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6308 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6309 DrawLevelField(ax, ay);
6311 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6313 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6314 unten_massiv = TRUE;
6315 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6316 links_massiv = TRUE;
6317 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6318 rechts_massiv = TRUE;
6320 if (((oben_massiv && unten_massiv) ||
6321 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6322 element == EL_EXPANDABLE_WALL) &&
6323 ((links_massiv && rechts_massiv) ||
6324 element == EL_EXPANDABLE_WALL_VERTICAL))
6325 Feld[ax][ay] = EL_WALL;
6328 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6331 void CheckForDragon(int x, int y)
6334 boolean dragon_found = FALSE;
6335 static int xy[4][2] =
6343 for (i = 0; i < NUM_DIRECTIONS; i++)
6345 for (j = 0; j < 4; j++)
6347 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6349 if (IN_LEV_FIELD(xx, yy) &&
6350 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6352 if (Feld[xx][yy] == EL_DRAGON)
6353 dragon_found = TRUE;
6362 for (i = 0; i < NUM_DIRECTIONS; i++)
6364 for (j = 0; j < 3; j++)
6366 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6368 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6370 Feld[xx][yy] = EL_EMPTY;
6371 DrawLevelField(xx, yy);
6380 static void InitBuggyBase(int x, int y)
6382 int element = Feld[x][y];
6383 int activating_delay = FRAMES_PER_SECOND / 4;
6386 (element == EL_SP_BUGGY_BASE ?
6387 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6388 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6390 element == EL_SP_BUGGY_BASE_ACTIVE ?
6391 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6394 static void WarnBuggyBase(int x, int y)
6397 static int xy[4][2] =
6405 for (i = 0; i < NUM_DIRECTIONS; i++)
6407 int xx = x + xy[i][0], yy = y + xy[i][1];
6409 if (IS_PLAYER(xx, yy))
6411 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6418 static void InitTrap(int x, int y)
6420 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6423 static void ActivateTrap(int x, int y)
6425 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6428 static void ChangeActiveTrap(int x, int y)
6430 int graphic = IMG_TRAP_ACTIVE;
6432 /* if new animation frame was drawn, correct crumbled sand border */
6433 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6434 DrawLevelFieldCrumbledSand(x, y);
6437 static int getSpecialActionElement(int element, int number, int base_element)
6439 return (element != EL_EMPTY ? element :
6440 number != -1 ? base_element + number - 1 :
6444 static int getModifiedActionNumber(int value_old, int value_min, int value_max,
6445 int operator, int operand)
6447 int value_new = (operator == CA_MODE_ADD ? value_old + operand :
6448 operator == CA_MODE_SUBTRACT ? value_old - operand :
6449 operator == CA_MODE_MULTIPLY ? value_old * operand :
6450 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6451 operator == CA_MODE_SET ? operand :
6454 return (value_new < value_min ? value_min :
6455 value_new > value_max ? value_max :
6459 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6461 struct ElementInfo *ei = &element_info[element];
6462 struct ElementChangeInfo *change = &ei->change_page[page];
6463 int action_type = change->action_type;
6464 int action_mode = change->action_mode;
6465 int action_arg = change->action_arg;
6468 if (!change->has_action)
6471 /* ---------- determine action paramater values ---------- */
6473 int action_arg_element =
6474 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6475 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6476 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6479 int action_arg_number =
6480 (action_arg <= CA_ARG_MAX ? action_arg :
6481 action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN :
6482 action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX :
6483 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6484 #if USE_NEW_COLLECT_COUNT
6485 action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] :
6487 action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial :
6489 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6492 /* (for explicit player choice, set invalid value to "no player") */
6493 int action_arg_player_bits =
6494 (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
6495 action_arg >= CA_ARG_PLAYER_1 &&
6496 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6497 action_arg >= CA_ARG_1 &&
6498 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
6499 action_arg_element >= EL_PLAYER_1 &&
6500 action_arg_element <= EL_PLAYER_4 ?
6501 (1 << (action_arg_element - EL_PLAYER_1)) :
6504 /* (for implicit player choice, set invalid value to "all players") */
6505 int trigger_player_bits =
6506 (change->actual_trigger_player >= EL_PLAYER_1 &&
6507 change->actual_trigger_player <= EL_PLAYER_4 ?
6508 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6511 /* ---------- execute action ---------- */
6520 case CA_EXIT_PLAYER:
6522 for (i = 0; i < MAX_PLAYERS; i++)
6523 if (action_arg_player_bits & (1 << i))
6524 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6529 case CA_KILL_PLAYER:
6531 for (i = 0; i < MAX_PLAYERS; i++)
6532 if (action_arg_player_bits & (1 << i))
6533 KillHero(&stored_player[i]);
6538 case CA_RESTART_LEVEL:
6540 game.restart_level = TRUE;
6545 case CA_SHOW_ENVELOPE:
6547 int element = getSpecialActionElement(action_arg_element,
6548 action_arg_number, EL_ENVELOPE_1);
6550 if (IS_ENVELOPE(element))
6551 local_player->show_envelope = element;
6558 int element = getSpecialActionElement(action_arg_element,
6559 action_arg_number, EL_KEY_1);
6561 if (IS_KEY(element))
6563 for (i = 0; i < MAX_PLAYERS; i++)
6565 if (trigger_player_bits & (1 << i))
6567 stored_player[i].key[KEY_NR(element)] = TRUE;
6569 DrawGameValue_Keys(stored_player[i].key);
6571 redraw_mask |= REDRAW_DOOR_1;
6581 int element = getSpecialActionElement(action_arg_element,
6582 action_arg_number, EL_KEY_1);
6584 if (IS_KEY(element))
6586 for (i = 0; i < MAX_PLAYERS; i++)
6588 if (trigger_player_bits & (1 << i))
6590 stored_player[i].key[KEY_NR(element)] = FALSE;
6592 DrawGameValue_Keys(stored_player[i].key);
6594 redraw_mask |= REDRAW_DOOR_1;
6602 case CA_SET_PLAYER_SPEED:
6604 for (i = 0; i < MAX_PLAYERS; i++)
6606 if (trigger_player_bits & (1 << i))
6608 if (action_arg == CA_ARG_NUMBER_RESET)
6609 stored_player[i].move_delay_value = game.initial_move_delay_value;
6610 else if (action_arg == CA_ARG_NUMBER_NORMAL)
6611 stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6612 else if (action_arg == CA_ARG_NUMBER_MIN)
6613 stored_player[i].move_delay_value = 16;
6614 else if (action_arg == CA_ARG_NUMBER_MAX)
6615 stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED;
6619 if (action_mode == CA_MODE_ADD)
6621 action_mode = CA_MODE_DIVIDE;
6622 action_arg_number = (1 << action_arg_number);
6624 else if (action_mode == CA_MODE_SUBTRACT)
6626 action_mode = CA_MODE_MULTIPLY;
6627 action_arg_number = (1 << action_arg_number);
6630 int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE :
6631 action_mode == CA_MODE_DIVIDE ? CA_MODE_MULTIPLY :
6634 stored_player[i].move_delay_value =
6635 getModifiedActionNumber(stored_player[i].move_delay_value,
6637 action_mode, action_arg_number);
6648 local_player->gems_still_needed =
6649 getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
6650 action_mode, action_arg_number);
6652 DrawGameValue_Emeralds(local_player->gems_still_needed);
6659 if (level.time > 0) /* only modify limited time value */
6661 TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
6662 action_mode, action_arg_number);
6664 DrawGameValue_Time(TimeLeft);
6672 local_player->score =
6673 getModifiedActionNumber(local_player->score, 0, 9999,
6674 action_mode, action_arg_number);
6676 DrawGameValue_Score(local_player->score);
6681 case CA_SET_CE_SCORE:
6684 getModifiedActionNumber(ei->collect_score, 0, 9999,
6685 action_mode, action_arg_number);
6689 case CA_SET_CE_COUNT:
6691 #if USE_NEW_COLLECT_COUNT
6692 int count_last = Count[x][y];
6694 Count[x][y] = getModifiedActionNumber(Count[x][y], 0, 9999,
6695 action_mode, action_arg_number);
6698 printf("::: Count == %d\n", Count[x][y]);
6701 if (Count[x][y] == 0 && count_last > 0)
6704 printf("::: CE_COUNT_AT_ZERO\n");
6707 CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO);
6708 CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X);
6715 case CA_SET_DYNABOMB_NUMBER:
6717 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6722 case CA_SET_DYNABOMB_SIZE:
6724 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6729 case CA_SET_DYNABOMB_POWER:
6731 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6736 case CA_TOGGLE_PLAYER_GRAVITY:
6738 game.gravity = !game.gravity;
6743 case CA_ENABLE_PLAYER_GRAVITY:
6745 game.gravity = TRUE;
6750 case CA_DISABLE_PLAYER_GRAVITY:
6752 game.gravity = FALSE;
6762 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6763 int x, int y, int target_element)
6765 int previous_move_direction = MovDir[x][y];
6766 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6767 IS_WALKABLE(Feld[x][y]));
6769 /* check if element under player changes from accessible to unaccessible
6770 (needed for special case of dropping element which then changes) */
6771 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6772 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6780 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6781 RemoveMovingField(x, y);
6785 Feld[x][y] = target_element;
6787 ResetGfxAnimation(x, y);
6788 ResetRandomAnimationValue(x, y);
6790 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6791 MovDir[x][y] = previous_move_direction;
6793 InitField_WithBug1(x, y, FALSE);
6795 DrawLevelField(x, y);
6797 if (GFX_CRUMBLED(Feld[x][y]))
6798 DrawLevelFieldCrumbledSandNeighbours(x, y);
6801 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6802 if (ELEM_IS_PLAYER(target_element))
6803 RelocatePlayer(x, y, target_element);
6806 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6808 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6811 TestIfBadThingTouchesHero(x, y);
6812 TestIfPlayerTouchesCustomElement(x, y);
6813 TestIfElementTouchesCustomElement(x, y);
6816 static boolean ChangeElementNow(int x, int y, int element, int page)
6818 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6820 int old_element = Feld[x][y];
6822 /* always use default change event to prevent running into a loop */
6823 if (ChangeEvent[x][y] == -1)
6824 ChangeEvent[x][y] = CE_DELAY;
6826 if (ChangeEvent[x][y] == CE_DELAY)
6828 /* reset actual trigger element, trigger player and action element */
6829 change->actual_trigger_element = EL_EMPTY;
6830 change->actual_trigger_player = EL_PLAYER_1;
6834 /* do not change any elements that have already changed in this frame */
6838 /* do not change already changed elements with same change event */
6839 if (Changed[x][y] & ChangeEvent[x][y])
6844 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6846 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6849 if (change->explode)
6856 if (change->use_target_content)
6858 boolean complete_replace = TRUE;
6859 boolean can_replace[3][3];
6862 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6865 boolean is_walkable;
6866 boolean is_diggable;
6867 boolean is_collectible;
6868 boolean is_removable;
6869 boolean is_destructible;
6870 int ex = x + xx - 1;
6871 int ey = y + yy - 1;
6872 int content_element = change->target_content[xx][yy];
6875 can_replace[xx][yy] = TRUE;
6877 if (ex == x && ey == y) /* do not check changing element itself */
6880 if (content_element == EL_EMPTY_SPACE)
6882 can_replace[xx][yy] = FALSE; /* do not replace border with space */
6887 if (!IN_LEV_FIELD(ex, ey))
6889 can_replace[xx][yy] = FALSE;
6890 complete_replace = FALSE;
6897 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6898 e = MovingOrBlocked2Element(ex, ey);
6900 is_empty = (IS_FREE(ex, ey) ||
6901 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
6903 is_walkable = (is_empty || IS_WALKABLE(e));
6904 is_diggable = (is_empty || IS_DIGGABLE(e));
6905 is_collectible = (is_empty || IS_COLLECTIBLE(e));
6906 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
6907 is_removable = (is_diggable || is_collectible);
6909 can_replace[xx][yy] =
6910 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
6911 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
6912 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
6913 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
6914 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
6915 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
6916 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
6918 if (!can_replace[xx][yy])
6919 complete_replace = FALSE;
6922 if (!change->only_if_complete || complete_replace)
6924 boolean something_has_changed = FALSE;
6926 if (change->only_if_complete && change->use_random_replace &&
6927 RND(100) < change->random_percentage)
6930 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6932 int ex = x + xx - 1;
6933 int ey = y + yy - 1;
6934 int content_element;
6936 if (can_replace[xx][yy] && (!change->use_random_replace ||
6937 RND(100) < change->random_percentage))
6939 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6940 RemoveMovingField(ex, ey);
6942 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6944 content_element = change->target_content[xx][yy];
6945 target_element = GET_TARGET_ELEMENT(content_element, change);
6947 ChangeElementNowExt(change, ex, ey, target_element);
6949 something_has_changed = TRUE;
6951 /* for symmetry reasons, freeze newly created border elements */
6952 if (ex != x || ey != y)
6953 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6957 if (something_has_changed)
6958 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6963 target_element = GET_TARGET_ELEMENT(change->target_element, change);
6965 ChangeElementNowExt(change, x, y, target_element);
6967 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6970 /* this uses direct change before indirect change */
6971 CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page);
6976 static void ChangeElement(int x, int y, int page)
6978 int element = MovingOrBlocked2Element(x, y);
6979 struct ElementInfo *ei = &element_info[element];
6980 struct ElementChangeInfo *change = &ei->change_page[page];
6983 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
6986 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6987 x, y, element, element_info[element].token_name);
6988 printf("ChangeElement(): This should never happen!\n");
6993 /* this can happen with classic bombs on walkable, changing elements */
6994 if (!CAN_CHANGE(element))
6997 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
6998 ChangeDelay[x][y] = 0;
7004 if (ChangeDelay[x][y] == 0) /* initialize element change */
7006 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7008 ResetGfxAnimation(x, y);
7009 ResetRandomAnimationValue(x, y);
7011 if (change->pre_change_function)
7012 change->pre_change_function(x, y);
7015 ChangeDelay[x][y]--;
7017 if (ChangeDelay[x][y] != 0) /* continue element change */
7019 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7021 if (IS_ANIMATED(graphic))
7022 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7024 if (change->change_function)
7025 change->change_function(x, y);
7027 else /* finish element change */
7029 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7031 page = ChangePage[x][y];
7032 ChangePage[x][y] = -1;
7034 change = &ei->change_page[page];
7037 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7039 ChangeDelay[x][y] = 1; /* try change after next move step */
7040 ChangePage[x][y] = page; /* remember page to use for change */
7045 if (ChangeElementNow(x, y, element, page))
7047 if (change->post_change_function)
7048 change->post_change_function(x, y);
7053 static boolean CheckTriggeredElementChangeExt(int trigger_element,
7059 boolean change_done_any = FALSE;
7060 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7063 if (!(trigger_events[trigger_element][trigger_event]))
7066 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7068 int element = EL_CUSTOM_START + i;
7069 boolean change_done = FALSE;
7072 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7073 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7076 for (p = 0; p < element_info[element].num_change_pages; p++)
7078 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7080 if (change->can_change_or_has_action &&
7081 change->has_event[trigger_event] &&
7082 change->trigger_side & trigger_side &&
7083 change->trigger_player & trigger_player &&
7084 change->trigger_page & trigger_page_bits &&
7085 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7087 change->actual_trigger_element = trigger_element;
7088 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7090 if ((change->can_change && !change_done) || change->has_action)
7094 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7096 if (Feld[x][y] == element)
7098 if (change->can_change && !change_done)
7100 ChangeDelay[x][y] = 1;
7101 ChangeEvent[x][y] = trigger_event;
7102 ChangeElement(x, y, p);
7105 if (change->has_action)
7106 ExecuteCustomElementAction(x, y, element, p);
7110 if (change->can_change)
7113 change_done_any = TRUE;
7120 return change_done_any;
7123 static boolean CheckElementChangeExt(int x, int y,
7125 int trigger_element,
7130 boolean change_done = FALSE;
7133 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7134 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7137 if (Feld[x][y] == EL_BLOCKED)
7139 Blocked2Moving(x, y, &x, &y);
7140 element = Feld[x][y];
7143 if (Feld[x][y] != element) /* check if element has already changed */
7146 for (p = 0; p < element_info[element].num_change_pages; p++)
7148 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7150 boolean check_trigger_element =
7151 (trigger_event == CE_TOUCHING_X ||
7152 trigger_event == CE_HITTING_X ||
7153 trigger_event == CE_HIT_BY_X);
7155 if (change->can_change_or_has_action &&
7156 change->has_event[trigger_event] &&
7157 change->trigger_side & trigger_side &&
7158 change->trigger_player & trigger_player &&
7159 (!check_trigger_element ||
7160 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7162 change->actual_trigger_element = trigger_element;
7163 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7165 if (change->can_change && !change_done)
7167 ChangeDelay[x][y] = 1;
7168 ChangeEvent[x][y] = trigger_event;
7169 ChangeElement(x, y, p);
7174 if (change->has_action)
7175 ExecuteCustomElementAction(x, y, element, p);
7182 static void PlayPlayerSound(struct PlayerInfo *player)
7184 int jx = player->jx, jy = player->jy;
7185 int element = player->element_nr;
7186 int last_action = player->last_action_waiting;
7187 int action = player->action_waiting;
7189 if (player->is_waiting)
7191 if (action != last_action)
7192 PlayLevelSoundElementAction(jx, jy, element, action);
7194 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7198 if (action != last_action)
7199 StopSound(element_info[element].sound[last_action]);
7201 if (last_action == ACTION_SLEEPING)
7202 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7206 static void PlayAllPlayersSound()
7210 for (i = 0; i < MAX_PLAYERS; i++)
7211 if (stored_player[i].active)
7212 PlayPlayerSound(&stored_player[i]);
7215 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7217 boolean last_waiting = player->is_waiting;
7218 int move_dir = player->MovDir;
7220 player->last_action_waiting = player->action_waiting;
7224 if (!last_waiting) /* not waiting -> waiting */
7226 player->is_waiting = TRUE;
7228 player->frame_counter_bored =
7230 game.player_boring_delay_fixed +
7231 SimpleRND(game.player_boring_delay_random);
7232 player->frame_counter_sleeping =
7234 game.player_sleeping_delay_fixed +
7235 SimpleRND(game.player_sleeping_delay_random);
7237 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7240 if (game.player_sleeping_delay_fixed +
7241 game.player_sleeping_delay_random > 0 &&
7242 player->anim_delay_counter == 0 &&
7243 player->post_delay_counter == 0 &&
7244 FrameCounter >= player->frame_counter_sleeping)
7245 player->is_sleeping = TRUE;
7246 else if (game.player_boring_delay_fixed +
7247 game.player_boring_delay_random > 0 &&
7248 FrameCounter >= player->frame_counter_bored)
7249 player->is_bored = TRUE;
7251 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7252 player->is_bored ? ACTION_BORING :
7255 if (player->is_sleeping)
7257 if (player->num_special_action_sleeping > 0)
7259 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7261 int last_special_action = player->special_action_sleeping;
7262 int num_special_action = player->num_special_action_sleeping;
7263 int special_action =
7264 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7265 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7266 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7267 last_special_action + 1 : ACTION_SLEEPING);
7268 int special_graphic =
7269 el_act_dir2img(player->element_nr, special_action, move_dir);
7271 player->anim_delay_counter =
7272 graphic_info[special_graphic].anim_delay_fixed +
7273 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7274 player->post_delay_counter =
7275 graphic_info[special_graphic].post_delay_fixed +
7276 SimpleRND(graphic_info[special_graphic].post_delay_random);
7278 player->special_action_sleeping = special_action;
7281 if (player->anim_delay_counter > 0)
7283 player->action_waiting = player->special_action_sleeping;
7284 player->anim_delay_counter--;
7286 else if (player->post_delay_counter > 0)
7288 player->post_delay_counter--;
7292 else if (player->is_bored)
7294 if (player->num_special_action_bored > 0)
7296 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7298 int special_action =
7299 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7300 int special_graphic =
7301 el_act_dir2img(player->element_nr, special_action, move_dir);
7303 player->anim_delay_counter =
7304 graphic_info[special_graphic].anim_delay_fixed +
7305 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7306 player->post_delay_counter =
7307 graphic_info[special_graphic].post_delay_fixed +
7308 SimpleRND(graphic_info[special_graphic].post_delay_random);
7310 player->special_action_bored = special_action;
7313 if (player->anim_delay_counter > 0)
7315 player->action_waiting = player->special_action_bored;
7316 player->anim_delay_counter--;
7318 else if (player->post_delay_counter > 0)
7320 player->post_delay_counter--;
7325 else if (last_waiting) /* waiting -> not waiting */
7327 player->is_waiting = FALSE;
7328 player->is_bored = FALSE;
7329 player->is_sleeping = FALSE;
7331 player->frame_counter_bored = -1;
7332 player->frame_counter_sleeping = -1;
7334 player->anim_delay_counter = 0;
7335 player->post_delay_counter = 0;
7337 player->action_waiting = ACTION_DEFAULT;
7339 player->special_action_bored = ACTION_DEFAULT;
7340 player->special_action_sleeping = ACTION_DEFAULT;
7344 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7346 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7347 int left = player_action & JOY_LEFT;
7348 int right = player_action & JOY_RIGHT;
7349 int up = player_action & JOY_UP;
7350 int down = player_action & JOY_DOWN;
7351 int button1 = player_action & JOY_BUTTON_1;
7352 int button2 = player_action & JOY_BUTTON_2;
7353 int dx = (left ? -1 : right ? 1 : 0);
7354 int dy = (up ? -1 : down ? 1 : 0);
7356 if (!player->active || tape.pausing)
7362 snapped = SnapField(player, dx, dy);
7366 dropped = DropElement(player);
7368 moved = MovePlayer(player, dx, dy);
7371 if (tape.single_step && tape.recording && !tape.pausing)
7373 if (button1 || (dropped && !moved))
7375 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7376 SnapField(player, 0, 0); /* stop snapping */
7380 SetPlayerWaiting(player, FALSE);
7382 return player_action;
7386 /* no actions for this player (no input at player's configured device) */
7388 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7389 SnapField(player, 0, 0);
7390 CheckGravityMovementWhenNotMoving(player);
7392 if (player->MovPos == 0)
7393 SetPlayerWaiting(player, TRUE);
7395 if (player->MovPos == 0) /* needed for tape.playing */
7396 player->is_moving = FALSE;
7398 player->is_dropping = FALSE;
7404 void AdvanceFrameAndPlayerCounters(int player_nr)
7408 /* advance frame counters (global frame counter and time frame counter) */
7412 /* advance player counters (counters for move delay, move animation etc.) */
7413 for (i = 0; i < MAX_PLAYERS; i++)
7415 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7417 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7419 if (!advance_player_counters) /* not all players may be affected */
7422 stored_player[i].Frame += move_frames;
7424 if (stored_player[i].MovPos != 0)
7425 stored_player[i].StepFrame += move_frames;
7427 if (stored_player[i].move_delay > 0)
7428 stored_player[i].move_delay--;
7430 /* due to bugs in previous versions, counter must count up, not down */
7431 if (stored_player[i].push_delay != -1)
7432 stored_player[i].push_delay++;
7434 if (stored_player[i].drop_delay > 0)
7435 stored_player[i].drop_delay--;
7441 static unsigned long game_frame_delay = 0;
7442 unsigned long game_frame_delay_value;
7443 int magic_wall_x = 0, magic_wall_y = 0;
7444 int i, x, y, element, graphic;
7445 byte *recorded_player_action;
7446 byte summarized_player_action = 0;
7447 byte tape_action[MAX_PLAYERS];
7449 if (game_status != GAME_MODE_PLAYING)
7452 game_frame_delay_value =
7453 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7455 if (tape.playing && tape.warp_forward && !tape.pausing)
7456 game_frame_delay_value = 0;
7458 /* ---------- main game synchronization point ---------- */
7460 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7462 if (network_playing && !network_player_action_received)
7464 /* try to get network player actions in time */
7466 #if defined(NETWORK_AVALIABLE)
7467 /* last chance to get network player actions without main loop delay */
7471 /* game was quit by network peer */
7472 if (game_status != GAME_MODE_PLAYING)
7475 if (!network_player_action_received)
7476 return; /* failed to get network player actions in time */
7482 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7485 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7486 if (recorded_player_action == NULL && tape.pausing)
7490 for (i = 0; i < MAX_PLAYERS; i++)
7492 summarized_player_action |= stored_player[i].action;
7494 if (!network_playing)
7495 stored_player[i].effective_action = stored_player[i].action;
7498 #if defined(NETWORK_AVALIABLE)
7499 if (network_playing)
7500 SendToServer_MovePlayer(summarized_player_action);
7503 if (!options.network && !setup.team_mode)
7504 local_player->effective_action = summarized_player_action;
7506 if (recorded_player_action != NULL)
7507 for (i = 0; i < MAX_PLAYERS; i++)
7508 stored_player[i].effective_action = recorded_player_action[i];
7510 for (i = 0; i < MAX_PLAYERS; i++)
7512 tape_action[i] = stored_player[i].effective_action;
7514 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7515 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7518 /* only save actions from input devices, but not programmed actions */
7520 TapeRecordAction(tape_action);
7522 for (i = 0; i < MAX_PLAYERS; i++)
7524 int actual_player_action = stored_player[i].effective_action;
7527 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7528 - rnd_equinox_tetrachloride 048
7529 - rnd_equinox_tetrachloride_ii 096
7530 - rnd_emanuel_schmieg 002
7531 - doctor_sloan_ww 001, 020
7533 if (stored_player[i].MovPos == 0)
7534 CheckGravityMovement(&stored_player[i]);
7537 /* overwrite programmed action with tape action */
7538 if (stored_player[i].programmed_action)
7539 actual_player_action = stored_player[i].programmed_action;
7542 PlayerActions(&stored_player[i], actual_player_action);
7544 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7546 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7547 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7550 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7553 network_player_action_received = FALSE;
7555 ScrollScreen(NULL, SCROLL_GO_ON);
7557 /* for backwards compatibility, the following code emulates a fixed bug that
7558 occured when pushing elements (causing elements that just made their last
7559 pushing step to already (if possible) make their first falling step in the
7560 same game frame, which is bad); this code is also needed to use the famous
7561 "spring push bug" which is used in older levels and might be wanted to be
7562 used also in newer levels, but in this case the buggy pushing code is only
7563 affecting the "spring" element and no other elements */
7565 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7567 for (i = 0; i < MAX_PLAYERS; i++)
7569 struct PlayerInfo *player = &stored_player[i];
7573 if (player->active && player->is_pushing && player->is_moving &&
7575 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7576 Feld[x][y] == EL_SPRING))
7578 ContinueMoving(x, y);
7580 /* continue moving after pushing (this is actually a bug) */
7581 if (!IS_MOVING(x, y))
7589 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7591 Changed[x][y] = FALSE;
7592 ChangeEvent[x][y] = -1;
7594 /* this must be handled before main playfield loop */
7595 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7598 if (MovDelay[x][y] <= 0)
7603 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7605 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7606 printf("GameActions(): This should never happen!\n");
7608 ChangePage[x][y] = -1;
7613 if (WasJustMoving[x][y] > 0)
7614 WasJustMoving[x][y]--;
7615 if (WasJustFalling[x][y] > 0)
7616 WasJustFalling[x][y]--;
7617 if (CheckCollision[x][y] > 0)
7618 CheckCollision[x][y]--;
7622 /* reset finished pushing action (not done in ContinueMoving() to allow
7623 continous pushing animation for elements with zero push delay) */
7624 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7626 ResetGfxAnimation(x, y);
7627 DrawLevelField(x, y);
7631 if (IS_BLOCKED(x, y))
7635 Blocked2Moving(x, y, &oldx, &oldy);
7636 if (!IS_MOVING(oldx, oldy))
7638 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7639 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7640 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7641 printf("GameActions(): This should never happen!\n");
7647 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7649 element = Feld[x][y];
7650 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7652 if (graphic_info[graphic].anim_global_sync)
7653 GfxFrame[x][y] = FrameCounter;
7655 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7656 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7657 ResetRandomAnimationValue(x, y);
7659 SetRandomAnimationValue(x, y);
7661 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7663 if (IS_INACTIVE(element))
7665 if (IS_ANIMATED(graphic))
7666 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7671 /* this may take place after moving, so 'element' may have changed */
7672 if (IS_CHANGING(x, y) &&
7673 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7676 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7677 element_info[element].event_page_nr[CE_DELAY]);
7679 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7682 element = Feld[x][y];
7683 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7686 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7690 element = Feld[x][y];
7691 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7693 if (IS_ANIMATED(graphic) &&
7696 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7698 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7699 EdelsteinFunkeln(x, y);
7701 else if ((element == EL_ACID ||
7702 element == EL_EXIT_OPEN ||
7703 element == EL_SP_EXIT_OPEN ||
7704 element == EL_SP_TERMINAL ||
7705 element == EL_SP_TERMINAL_ACTIVE ||
7706 element == EL_EXTRA_TIME ||
7707 element == EL_SHIELD_NORMAL ||
7708 element == EL_SHIELD_DEADLY) &&
7709 IS_ANIMATED(graphic))
7710 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7711 else if (IS_MOVING(x, y))
7712 ContinueMoving(x, y);
7713 else if (IS_ACTIVE_BOMB(element))
7714 CheckDynamite(x, y);
7715 else if (element == EL_AMOEBA_GROWING)
7716 AmoebeWaechst(x, y);
7717 else if (element == EL_AMOEBA_SHRINKING)
7718 AmoebaDisappearing(x, y);
7720 #if !USE_NEW_AMOEBA_CODE
7721 else if (IS_AMOEBALIVE(element))
7722 AmoebeAbleger(x, y);
7725 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7727 else if (element == EL_EXIT_CLOSED)
7729 else if (element == EL_SP_EXIT_CLOSED)
7731 else if (element == EL_EXPANDABLE_WALL_GROWING)
7733 else if (element == EL_EXPANDABLE_WALL ||
7734 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7735 element == EL_EXPANDABLE_WALL_VERTICAL ||
7736 element == EL_EXPANDABLE_WALL_ANY)
7738 else if (element == EL_FLAMES)
7739 CheckForDragon(x, y);
7740 else if (element == EL_EXPLOSION)
7741 ; /* drawing of correct explosion animation is handled separately */
7742 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7743 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7745 if (IS_BELT_ACTIVE(element))
7746 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7748 if (game.magic_wall_active)
7750 int jx = local_player->jx, jy = local_player->jy;
7752 /* play the element sound at the position nearest to the player */
7753 if ((element == EL_MAGIC_WALL_FULL ||
7754 element == EL_MAGIC_WALL_ACTIVE ||
7755 element == EL_MAGIC_WALL_EMPTYING ||
7756 element == EL_BD_MAGIC_WALL_FULL ||
7757 element == EL_BD_MAGIC_WALL_ACTIVE ||
7758 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7759 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7767 #if USE_NEW_AMOEBA_CODE
7768 /* new experimental amoeba growth stuff */
7769 if (!(FrameCounter % 8))
7771 static unsigned long random = 1684108901;
7773 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7775 x = RND(lev_fieldx);
7776 y = RND(lev_fieldy);
7777 element = Feld[x][y];
7779 if (!IS_PLAYER(x,y) &&
7780 (element == EL_EMPTY ||
7781 CAN_GROW_INTO(element) ||
7782 element == EL_QUICKSAND_EMPTY ||
7783 element == EL_ACID_SPLASH_LEFT ||
7784 element == EL_ACID_SPLASH_RIGHT))
7786 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7787 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7788 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7789 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7790 Feld[x][y] = EL_AMOEBA_DROP;
7793 random = random * 129 + 1;
7799 if (game.explosions_delayed)
7802 game.explosions_delayed = FALSE;
7804 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7806 element = Feld[x][y];
7808 if (ExplodeField[x][y])
7809 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7810 else if (element == EL_EXPLOSION)
7811 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
7813 ExplodeField[x][y] = EX_TYPE_NONE;
7816 game.explosions_delayed = TRUE;
7819 if (game.magic_wall_active)
7821 if (!(game.magic_wall_time_left % 4))
7823 int element = Feld[magic_wall_x][magic_wall_y];
7825 if (element == EL_BD_MAGIC_WALL_FULL ||
7826 element == EL_BD_MAGIC_WALL_ACTIVE ||
7827 element == EL_BD_MAGIC_WALL_EMPTYING)
7828 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7830 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7833 if (game.magic_wall_time_left > 0)
7835 game.magic_wall_time_left--;
7836 if (!game.magic_wall_time_left)
7838 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7840 element = Feld[x][y];
7842 if (element == EL_MAGIC_WALL_ACTIVE ||
7843 element == EL_MAGIC_WALL_FULL)
7845 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7846 DrawLevelField(x, y);
7848 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7849 element == EL_BD_MAGIC_WALL_FULL)
7851 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7852 DrawLevelField(x, y);
7856 game.magic_wall_active = FALSE;
7861 if (game.light_time_left > 0)
7863 game.light_time_left--;
7865 if (game.light_time_left == 0)
7866 RedrawAllLightSwitchesAndInvisibleElements();
7869 if (game.timegate_time_left > 0)
7871 game.timegate_time_left--;
7873 if (game.timegate_time_left == 0)
7874 CloseAllOpenTimegates();
7877 for (i = 0; i < MAX_PLAYERS; i++)
7879 struct PlayerInfo *player = &stored_player[i];
7881 if (SHIELD_ON(player))
7883 if (player->shield_deadly_time_left)
7884 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7885 else if (player->shield_normal_time_left)
7886 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7890 if (TimeFrames >= FRAMES_PER_SECOND)
7895 for (i = 0; i < MAX_PLAYERS; i++)
7897 struct PlayerInfo *player = &stored_player[i];
7899 if (SHIELD_ON(player))
7901 player->shield_normal_time_left--;
7903 if (player->shield_deadly_time_left > 0)
7904 player->shield_deadly_time_left--;
7908 if (!level.use_step_counter)
7916 if (TimeLeft <= 10 && setup.time_limit)
7917 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7919 DrawGameValue_Time(TimeLeft);
7921 if (!TimeLeft && setup.time_limit)
7922 for (i = 0; i < MAX_PLAYERS; i++)
7923 KillHero(&stored_player[i]);
7925 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7926 DrawGameValue_Time(TimePlayed);
7929 if (tape.recording || tape.playing)
7930 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7934 PlayAllPlayersSound();
7936 if (options.debug) /* calculate frames per second */
7938 static unsigned long fps_counter = 0;
7939 static int fps_frames = 0;
7940 unsigned long fps_delay_ms = Counter() - fps_counter;
7944 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7946 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7949 fps_counter = Counter();
7952 redraw_mask |= REDRAW_FPS;
7955 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
7957 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7959 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7961 local_player->show_envelope = 0;
7964 /* use random number generator in every frame to make it less predictable */
7965 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
7969 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7971 int min_x = x, min_y = y, max_x = x, max_y = y;
7974 for (i = 0; i < MAX_PLAYERS; i++)
7976 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7978 if (!stored_player[i].active || &stored_player[i] == player)
7981 min_x = MIN(min_x, jx);
7982 min_y = MIN(min_y, jy);
7983 max_x = MAX(max_x, jx);
7984 max_y = MAX(max_y, jy);
7987 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7990 static boolean AllPlayersInVisibleScreen()
7994 for (i = 0; i < MAX_PLAYERS; i++)
7996 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7998 if (!stored_player[i].active)
8001 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8008 void ScrollLevel(int dx, int dy)
8010 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8013 BlitBitmap(drawto_field, drawto_field,
8014 FX + TILEX * (dx == -1) - softscroll_offset,
8015 FY + TILEY * (dy == -1) - softscroll_offset,
8016 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8017 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8018 FX + TILEX * (dx == 1) - softscroll_offset,
8019 FY + TILEY * (dy == 1) - softscroll_offset);
8023 x = (dx == 1 ? BX1 : BX2);
8024 for (y = BY1; y <= BY2; y++)
8025 DrawScreenField(x, y);
8030 y = (dy == 1 ? BY1 : BY2);
8031 for (x = BX1; x <= BX2; x++)
8032 DrawScreenField(x, y);
8035 redraw_mask |= REDRAW_FIELD;
8038 static boolean canFallDown(struct PlayerInfo *player)
8040 int jx = player->jx, jy = player->jy;
8042 return (IN_LEV_FIELD(jx, jy + 1) &&
8043 (IS_FREE(jx, jy + 1) ||
8044 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8045 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8046 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8049 static boolean canPassField(int x, int y, int move_dir)
8051 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8052 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8053 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8056 int element = Feld[x][y];
8058 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8059 !CAN_MOVE(element) &&
8060 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8061 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8062 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8065 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8067 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8068 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8069 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8073 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8074 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8075 (IS_DIGGABLE(Feld[newx][newy]) ||
8076 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8077 canPassField(newx, newy, move_dir)));
8080 static void CheckGravityMovement(struct PlayerInfo *player)
8082 if (game.gravity && !player->programmed_action)
8084 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8085 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8086 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8087 int jx = player->jx, jy = player->jy;
8088 boolean player_is_moving_to_valid_field =
8089 (!player_is_snapping &&
8090 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8091 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8092 boolean player_can_fall_down = canFallDown(player);
8094 if (player_can_fall_down &&
8095 !player_is_moving_to_valid_field)
8096 player->programmed_action = MV_DOWN;
8100 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8102 return CheckGravityMovement(player);
8104 if (game.gravity && !player->programmed_action)
8106 int jx = player->jx, jy = player->jy;
8107 boolean field_under_player_is_free =
8108 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8109 boolean player_is_standing_on_valid_field =
8110 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8111 (IS_WALKABLE(Feld[jx][jy]) &&
8112 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8114 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8115 player->programmed_action = MV_DOWN;
8121 -----------------------------------------------------------------------------
8122 dx, dy: direction (non-diagonal) to try to move the player to
8123 real_dx, real_dy: direction as read from input device (can be diagonal)
8126 boolean MovePlayerOneStep(struct PlayerInfo *player,
8127 int dx, int dy, int real_dx, int real_dy)
8129 int jx = player->jx, jy = player->jy;
8130 int new_jx = jx + dx, new_jy = jy + dy;
8134 if (!player->active || (!dx && !dy))
8135 return MF_NO_ACTION;
8137 player->MovDir = (dx < 0 ? MV_LEFT :
8140 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8142 if (!IN_LEV_FIELD(new_jx, new_jy))
8143 return MF_NO_ACTION;
8145 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8146 return MF_NO_ACTION;
8148 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8150 if (DONT_RUN_INTO(element))
8152 if (element == EL_ACID && dx == 0 && dy == 1)
8154 SplashAcid(new_jx, new_jy);
8155 Feld[jx][jy] = EL_PLAYER_1;
8156 InitMovingField(jx, jy, MV_DOWN);
8157 Store[jx][jy] = EL_ACID;
8158 ContinueMoving(jx, jy);
8162 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8167 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8168 if (can_move != MF_MOVING)
8171 /* check if DigField() has caused relocation of the player */
8172 if (player->jx != jx || player->jy != jy)
8173 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8175 StorePlayer[jx][jy] = 0;
8176 player->last_jx = jx;
8177 player->last_jy = jy;
8178 player->jx = new_jx;
8179 player->jy = new_jy;
8180 StorePlayer[new_jx][new_jy] = player->element_nr;
8183 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8185 player->step_counter++;
8187 PlayerVisit[jx][jy] = FrameCounter;
8189 ScrollPlayer(player, SCROLL_INIT);
8194 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8196 int jx = player->jx, jy = player->jy;
8197 int old_jx = jx, old_jy = jy;
8198 int moved = MF_NO_ACTION;
8200 if (!player->active)
8205 if (player->MovPos == 0)
8207 player->is_moving = FALSE;
8208 player->is_digging = FALSE;
8209 player->is_collecting = FALSE;
8210 player->is_snapping = FALSE;
8211 player->is_pushing = FALSE;
8217 if (player->move_delay > 0)
8220 player->move_delay = -1; /* set to "uninitialized" value */
8222 /* store if player is automatically moved to next field */
8223 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
8225 /* remove the last programmed player action */
8226 player->programmed_action = 0;
8230 /* should only happen if pre-1.2 tape recordings are played */
8231 /* this is only for backward compatibility */
8233 int original_move_delay_value = player->move_delay_value;
8236 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8240 /* scroll remaining steps with finest movement resolution */
8241 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8243 while (player->MovPos)
8245 ScrollPlayer(player, SCROLL_GO_ON);
8246 ScrollScreen(NULL, SCROLL_GO_ON);
8248 AdvanceFrameAndPlayerCounters(player->index_nr);
8254 player->move_delay_value = original_move_delay_value;
8257 if (player->last_move_dir & MV_HORIZONTAL)
8259 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8260 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8264 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8265 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8271 if (moved & MF_MOVING && !ScreenMovPos &&
8272 (player == local_player || !options.network))
8274 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8275 int offset = (setup.scroll_delay ? 3 : 0);
8277 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8279 /* actual player has left the screen -- scroll in that direction */
8280 if (jx != old_jx) /* player has moved horizontally */
8281 scroll_x += (jx - old_jx);
8282 else /* player has moved vertically */
8283 scroll_y += (jy - old_jy);
8287 if (jx != old_jx) /* player has moved horizontally */
8289 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8290 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8291 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8293 /* don't scroll over playfield boundaries */
8294 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8295 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8297 /* don't scroll more than one field at a time */
8298 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8300 /* don't scroll against the player's moving direction */
8301 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8302 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8303 scroll_x = old_scroll_x;
8305 else /* player has moved vertically */
8307 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8308 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8309 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8311 /* don't scroll over playfield boundaries */
8312 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8313 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8315 /* don't scroll more than one field at a time */
8316 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8318 /* don't scroll against the player's moving direction */
8319 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8320 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8321 scroll_y = old_scroll_y;
8325 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8327 if (!options.network && !AllPlayersInVisibleScreen())
8329 scroll_x = old_scroll_x;
8330 scroll_y = old_scroll_y;
8334 ScrollScreen(player, SCROLL_INIT);
8335 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8340 player->StepFrame = 0;
8342 if (moved & MF_MOVING)
8344 if (old_jx != jx && old_jy == jy)
8345 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8346 else if (old_jx == jx && old_jy != jy)
8347 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8349 DrawLevelField(jx, jy); /* for "crumbled sand" */
8351 player->last_move_dir = player->MovDir;
8352 player->is_moving = TRUE;
8353 player->is_snapping = FALSE;
8354 player->is_switching = FALSE;
8355 player->is_dropping = FALSE;
8359 CheckGravityMovementWhenNotMoving(player);
8361 player->is_moving = FALSE;
8363 /* at this point, the player is allowed to move, but cannot move right now
8364 (e.g. because of something blocking the way) -- ensure that the player
8365 is also allowed to move in the next frame (in old versions before 3.1.1,
8366 the player was forced to wait again for eight frames before next try) */
8368 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8369 player->move_delay = 0; /* allow direct movement in the next frame */
8372 if (player->move_delay == -1) /* not yet initialized by DigField() */
8373 player->move_delay = player->move_delay_value;
8375 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8377 TestIfHeroTouchesBadThing(jx, jy);
8378 TestIfPlayerTouchesCustomElement(jx, jy);
8381 if (!player->active)
8387 void ScrollPlayer(struct PlayerInfo *player, int mode)
8389 int jx = player->jx, jy = player->jy;
8390 int last_jx = player->last_jx, last_jy = player->last_jy;
8391 int move_stepsize = TILEX / player->move_delay_value;
8393 if (!player->active || !player->MovPos)
8396 if (mode == SCROLL_INIT)
8398 player->actual_frame_counter = FrameCounter;
8399 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8401 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8402 Feld[last_jx][last_jy] == EL_EMPTY)
8404 int last_field_block_delay = 0; /* start with no blocking at all */
8405 int block_delay_adjustment = player->block_delay_adjustment;
8407 /* if player blocks last field, add delay for exactly one move */
8408 if (player->block_last_field)
8410 last_field_block_delay += player->move_delay_value;
8412 /* when blocking enabled, prevent moving up despite gravity */
8413 if (game.gravity && player->MovDir == MV_UP)
8414 block_delay_adjustment = -1;
8417 /* add block delay adjustment (also possible when not blocking) */
8418 last_field_block_delay += block_delay_adjustment;
8420 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8421 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8426 else if (!FrameReached(&player->actual_frame_counter, 1))
8429 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8430 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8432 /* before DrawPlayer() to draw correct player graphic for this case */
8433 if (player->MovPos == 0)
8434 CheckGravityMovement(player);
8436 if (player->MovPos == 0) /* player reached destination field */
8438 if (player->move_delay_reset_counter > 0)
8440 player->move_delay_reset_counter--;
8442 if (player->move_delay_reset_counter == 0)
8444 /* continue with normal speed after quickly moving through gate */
8445 HALVE_PLAYER_SPEED(player);
8447 /* be able to make the next move without delay */
8448 player->move_delay = 0;
8452 player->last_jx = jx;
8453 player->last_jy = jy;
8455 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8456 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8457 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8459 DrawPlayer(player); /* needed here only to cleanup last field */
8462 if (local_player->friends_still_needed == 0 ||
8463 IS_SP_ELEMENT(Feld[jx][jy]))
8464 player->LevelSolved = player->GameOver = TRUE;
8467 /* this breaks one level: "machine", level 000 */
8469 int move_direction = player->MovDir;
8470 int enter_side = MV_DIR_OPPOSITE(move_direction);
8471 int leave_side = move_direction;
8472 int old_jx = last_jx;
8473 int old_jy = last_jy;
8474 int old_element = Feld[old_jx][old_jy];
8475 int new_element = Feld[jx][jy];
8477 if (IS_CUSTOM_ELEMENT(old_element))
8478 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8480 player->index_bit, leave_side);
8482 CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
8483 player->index_bit, leave_side);
8485 if (IS_CUSTOM_ELEMENT(new_element))
8486 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8487 player->index_bit, enter_side);
8489 CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X,
8490 player->index_bit, enter_side);
8493 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8495 TestIfHeroTouchesBadThing(jx, jy);
8496 TestIfPlayerTouchesCustomElement(jx, jy);
8498 /* needed because pushed element has not yet reached its destination,
8499 so it would trigger a change event at its previous field location */
8500 if (!player->is_pushing)
8501 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8503 if (!player->active)
8507 if (level.use_step_counter)
8517 if (TimeLeft <= 10 && setup.time_limit)
8518 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8520 DrawGameValue_Time(TimeLeft);
8522 if (!TimeLeft && setup.time_limit)
8523 for (i = 0; i < MAX_PLAYERS; i++)
8524 KillHero(&stored_player[i]);
8526 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8527 DrawGameValue_Time(TimePlayed);
8530 if (tape.single_step && tape.recording && !tape.pausing &&
8531 !player->programmed_action)
8532 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8536 void ScrollScreen(struct PlayerInfo *player, int mode)
8538 static unsigned long screen_frame_counter = 0;
8540 if (mode == SCROLL_INIT)
8542 /* set scrolling step size according to actual player's moving speed */
8543 ScrollStepSize = TILEX / player->move_delay_value;
8545 screen_frame_counter = FrameCounter;
8546 ScreenMovDir = player->MovDir;
8547 ScreenMovPos = player->MovPos;
8548 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8551 else if (!FrameReached(&screen_frame_counter, 1))
8556 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8557 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8558 redraw_mask |= REDRAW_FIELD;
8561 ScreenMovDir = MV_NO_MOVING;
8564 void TestIfPlayerTouchesCustomElement(int x, int y)
8566 static int xy[4][2] =
8573 static int trigger_sides[4][2] =
8575 /* center side border side */
8576 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8577 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8578 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8579 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8581 static int touch_dir[4] =
8588 int center_element = Feld[x][y]; /* should always be non-moving! */
8591 for (i = 0; i < NUM_DIRECTIONS; i++)
8593 int xx = x + xy[i][0];
8594 int yy = y + xy[i][1];
8595 int center_side = trigger_sides[i][0];
8596 int border_side = trigger_sides[i][1];
8599 if (!IN_LEV_FIELD(xx, yy))
8602 if (IS_PLAYER(x, y))
8604 struct PlayerInfo *player = PLAYERINFO(x, y);
8606 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8607 border_element = Feld[xx][yy]; /* may be moving! */
8608 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8609 border_element = Feld[xx][yy];
8610 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8611 border_element = MovingOrBlocked2Element(xx, yy);
8613 continue; /* center and border element do not touch */
8615 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8616 player->index_bit, border_side);
8617 CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X,
8618 player->index_bit, border_side);
8620 else if (IS_PLAYER(xx, yy))
8622 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8624 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8626 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8627 continue; /* center and border element do not touch */
8630 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8631 player->index_bit, center_side);
8632 CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X,
8633 player->index_bit, center_side);
8639 void TestIfElementTouchesCustomElement(int x, int y)
8641 static int xy[4][2] =
8648 static int trigger_sides[4][2] =
8650 /* center side border side */
8651 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8652 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8653 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8654 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8656 static int touch_dir[4] =
8663 boolean change_center_element = FALSE;
8664 int center_element = Feld[x][y]; /* should always be non-moving! */
8667 for (i = 0; i < NUM_DIRECTIONS; i++)
8669 int xx = x + xy[i][0];
8670 int yy = y + xy[i][1];
8671 int center_side = trigger_sides[i][0];
8672 int border_side = trigger_sides[i][1];
8675 if (!IN_LEV_FIELD(xx, yy))
8678 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8679 border_element = Feld[xx][yy]; /* may be moving! */
8680 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8681 border_element = Feld[xx][yy];
8682 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8683 border_element = MovingOrBlocked2Element(xx, yy);
8685 continue; /* center and border element do not touch */
8687 /* check for change of center element (but change it only once) */
8688 if (!change_center_element)
8689 change_center_element =
8690 CheckElementChangeBySide(x, y, center_element, border_element,
8691 CE_TOUCHING_X, border_side);
8693 /* check for change of border element */
8694 CheckElementChangeBySide(xx, yy, border_element, center_element,
8695 CE_TOUCHING_X, center_side);
8699 void TestIfElementHitsCustomElement(int x, int y, int direction)
8701 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8702 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8703 int hitx = x + dx, hity = y + dy;
8704 int hitting_element = Feld[x][y];
8705 int touched_element;
8707 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8710 touched_element = (IN_LEV_FIELD(hitx, hity) ?
8711 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
8713 if (IN_LEV_FIELD(hitx, hity))
8715 int opposite_direction = MV_DIR_OPPOSITE(direction);
8716 int hitting_side = direction;
8717 int touched_side = opposite_direction;
8718 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8719 MovDir[hitx][hity] != direction ||
8720 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8726 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8727 CE_HITTING_X, touched_side);
8729 CheckElementChangeBySide(hitx, hity, touched_element,
8730 hitting_element, CE_HIT_BY_X, hitting_side);
8732 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
8733 CE_HIT_BY_SOMETHING, opposite_direction);
8737 /* "hitting something" is also true when hitting the playfield border */
8738 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8739 CE_HITTING_SOMETHING, direction);
8743 void TestIfElementSmashesCustomElement(int x, int y, int direction)
8745 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8746 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8747 int hitx = x + dx, hity = y + dy;
8748 int hitting_element = Feld[x][y];
8749 int touched_element;
8751 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8752 !IS_FREE(hitx, hity) &&
8753 (!IS_MOVING(hitx, hity) ||
8754 MovDir[hitx][hity] != direction ||
8755 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8758 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8762 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8766 touched_element = (IN_LEV_FIELD(hitx, hity) ?
8767 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
8769 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8770 EP_CAN_SMASH_EVERYTHING, direction);
8772 if (IN_LEV_FIELD(hitx, hity))
8774 int opposite_direction = MV_DIR_OPPOSITE(direction);
8775 int hitting_side = direction;
8776 int touched_side = opposite_direction;
8778 int touched_element = MovingOrBlocked2Element(hitx, hity);
8781 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8782 MovDir[hitx][hity] != direction ||
8783 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8792 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
8793 CE_SMASHED_BY_SOMETHING, opposite_direction);
8795 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8796 CE_OTHER_IS_SMASHING, touched_side);
8798 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
8799 CE_OTHER_GETS_SMASHED, hitting_side);
8805 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8807 int i, kill_x = -1, kill_y = -1;
8808 int bad_element = -1;
8809 static int test_xy[4][2] =
8816 static int test_dir[4] =
8824 for (i = 0; i < NUM_DIRECTIONS; i++)
8826 int test_x, test_y, test_move_dir, test_element;
8828 test_x = good_x + test_xy[i][0];
8829 test_y = good_y + test_xy[i][1];
8831 if (!IN_LEV_FIELD(test_x, test_y))
8835 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8837 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8839 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8840 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8842 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8843 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8847 bad_element = test_element;
8853 if (kill_x != -1 || kill_y != -1)
8855 if (IS_PLAYER(good_x, good_y))
8857 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8859 if (player->shield_deadly_time_left > 0 &&
8860 !IS_INDESTRUCTIBLE(bad_element))
8861 Bang(kill_x, kill_y);
8862 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8866 Bang(good_x, good_y);
8870 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8872 int i, kill_x = -1, kill_y = -1;
8873 int bad_element = Feld[bad_x][bad_y];
8874 static int test_xy[4][2] =
8881 static int touch_dir[4] =
8888 static int test_dir[4] =
8896 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8899 for (i = 0; i < NUM_DIRECTIONS; i++)
8901 int test_x, test_y, test_move_dir, test_element;
8903 test_x = bad_x + test_xy[i][0];
8904 test_y = bad_y + test_xy[i][1];
8905 if (!IN_LEV_FIELD(test_x, test_y))
8909 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8911 test_element = Feld[test_x][test_y];
8913 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8914 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8916 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8917 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8919 /* good thing is player or penguin that does not move away */
8920 if (IS_PLAYER(test_x, test_y))
8922 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8924 if (bad_element == EL_ROBOT && player->is_moving)
8925 continue; /* robot does not kill player if he is moving */
8927 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8929 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8930 continue; /* center and border element do not touch */
8937 else if (test_element == EL_PENGUIN)
8946 if (kill_x != -1 || kill_y != -1)
8948 if (IS_PLAYER(kill_x, kill_y))
8950 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8952 if (player->shield_deadly_time_left > 0 &&
8953 !IS_INDESTRUCTIBLE(bad_element))
8955 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8959 Bang(kill_x, kill_y);
8963 void TestIfHeroTouchesBadThing(int x, int y)
8965 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8968 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8970 TestIfGoodThingHitsBadThing(x, y, move_dir);
8973 void TestIfBadThingTouchesHero(int x, int y)
8975 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8978 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8980 TestIfBadThingHitsGoodThing(x, y, move_dir);
8983 void TestIfFriendTouchesBadThing(int x, int y)
8985 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8988 void TestIfBadThingTouchesFriend(int x, int y)
8990 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8993 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8995 int i, kill_x = bad_x, kill_y = bad_y;
8996 static int xy[4][2] =
9004 for (i = 0; i < NUM_DIRECTIONS; i++)
9008 x = bad_x + xy[i][0];
9009 y = bad_y + xy[i][1];
9010 if (!IN_LEV_FIELD(x, y))
9013 element = Feld[x][y];
9014 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9015 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9023 if (kill_x != bad_x || kill_y != bad_y)
9027 void KillHero(struct PlayerInfo *player)
9029 int jx = player->jx, jy = player->jy;
9031 if (!player->active)
9034 /* remove accessible field at the player's position */
9035 Feld[jx][jy] = EL_EMPTY;
9037 /* deactivate shield (else Bang()/Explode() would not work right) */
9038 player->shield_normal_time_left = 0;
9039 player->shield_deadly_time_left = 0;
9045 static void KillHeroUnlessEnemyProtected(int x, int y)
9047 if (!PLAYER_ENEMY_PROTECTED(x, y))
9048 KillHero(PLAYERINFO(x, y));
9051 static void KillHeroUnlessExplosionProtected(int x, int y)
9053 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9054 KillHero(PLAYERINFO(x, y));
9057 void BuryHero(struct PlayerInfo *player)
9059 int jx = player->jx, jy = player->jy;
9061 if (!player->active)
9064 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9065 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9067 player->GameOver = TRUE;
9071 void RemoveHero(struct PlayerInfo *player)
9073 int jx = player->jx, jy = player->jy;
9074 int i, found = FALSE;
9076 player->present = FALSE;
9077 player->active = FALSE;
9079 if (!ExplodeField[jx][jy])
9080 StorePlayer[jx][jy] = 0;
9082 for (i = 0; i < MAX_PLAYERS; i++)
9083 if (stored_player[i].active)
9087 AllPlayersGone = TRUE;
9094 =============================================================================
9095 checkDiagonalPushing()
9096 -----------------------------------------------------------------------------
9097 check if diagonal input device direction results in pushing of object
9098 (by checking if the alternative direction is walkable, diggable, ...)
9099 =============================================================================
9102 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9103 int x, int y, int real_dx, int real_dy)
9105 int jx, jy, dx, dy, xx, yy;
9107 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9110 /* diagonal direction: check alternative direction */
9115 xx = jx + (dx == 0 ? real_dx : 0);
9116 yy = jy + (dy == 0 ? real_dy : 0);
9118 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9122 =============================================================================
9124 -----------------------------------------------------------------------------
9125 x, y: field next to player (non-diagonal) to try to dig to
9126 real_dx, real_dy: direction as read from input device (can be diagonal)
9127 =============================================================================
9130 int DigField(struct PlayerInfo *player,
9131 int oldx, int oldy, int x, int y,
9132 int real_dx, int real_dy, int mode)
9134 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9135 boolean player_was_pushing = player->is_pushing;
9136 int jx = oldx, jy = oldy;
9137 int dx = x - jx, dy = y - jy;
9138 int nextx = x + dx, nexty = y + dy;
9139 int move_direction = (dx == -1 ? MV_LEFT :
9140 dx == +1 ? MV_RIGHT :
9142 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9143 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9144 int dig_side = MV_DIR_OPPOSITE(move_direction);
9145 int old_element = Feld[jx][jy];
9149 if (is_player) /* function can also be called by EL_PENGUIN */
9151 if (player->MovPos == 0)
9153 player->is_digging = FALSE;
9154 player->is_collecting = FALSE;
9157 if (player->MovPos == 0) /* last pushing move finished */
9158 player->is_pushing = FALSE;
9160 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9162 player->is_switching = FALSE;
9163 player->push_delay = -1;
9165 return MF_NO_ACTION;
9169 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9170 return MF_NO_ACTION;
9172 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9173 old_element = Back[jx][jy];
9175 /* in case of element dropped at player position, check background */
9176 else if (Back[jx][jy] != EL_EMPTY &&
9177 game.engine_version >= VERSION_IDENT(2,2,0,0))
9178 old_element = Back[jx][jy];
9180 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9181 return MF_NO_ACTION; /* field has no opening in this direction */
9183 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9184 return MF_NO_ACTION; /* field has no opening in this direction */
9186 element = Feld[x][y];
9187 #if USE_NEW_COLLECT_COUNT
9188 collect_count = Count[x][y];
9190 collect_count = element_info[element].collect_count_initial;
9194 if (element != EL_BLOCKED &&
9195 Count[x][y] != element_info[element].collect_count_initial)
9196 printf("::: %d: %d != %d\n",
9199 element_info[element].collect_count_initial);
9202 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9203 return MF_NO_ACTION;
9205 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9206 game.engine_version >= VERSION_IDENT(2,2,0,0))
9207 return MF_NO_ACTION;
9209 if (game.gravity && is_player && !player->is_auto_moving &&
9210 canFallDown(player) && move_direction != MV_DOWN &&
9211 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9212 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9214 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9216 int sound_element = SND_ELEMENT(element);
9217 int sound_action = ACTION_WALKING;
9219 if (IS_RND_GATE(element))
9221 if (!player->key[RND_GATE_NR(element)])
9222 return MF_NO_ACTION;
9224 else if (IS_RND_GATE_GRAY(element))
9226 if (!player->key[RND_GATE_GRAY_NR(element)])
9227 return MF_NO_ACTION;
9229 else if (element == EL_EXIT_OPEN ||
9230 element == EL_SP_EXIT_OPEN ||
9231 element == EL_SP_EXIT_OPENING)
9233 sound_action = ACTION_PASSING; /* player is passing exit */
9235 else if (element == EL_EMPTY)
9237 sound_action = ACTION_MOVING; /* nothing to walk on */
9240 /* play sound from background or player, whatever is available */
9241 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9242 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9244 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9246 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9248 if (!ACCESS_FROM(element, opposite_direction))
9249 return MF_NO_ACTION; /* field not accessible from this direction */
9251 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9252 return MF_NO_ACTION;
9254 if (IS_EM_GATE(element))
9256 if (!player->key[EM_GATE_NR(element)])
9257 return MF_NO_ACTION;
9259 else if (IS_EM_GATE_GRAY(element))
9261 if (!player->key[EM_GATE_GRAY_NR(element)])
9262 return MF_NO_ACTION;
9264 else if (IS_SP_PORT(element))
9266 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9267 element == EL_SP_GRAVITY_PORT_RIGHT ||
9268 element == EL_SP_GRAVITY_PORT_UP ||
9269 element == EL_SP_GRAVITY_PORT_DOWN)
9270 game.gravity = !game.gravity;
9271 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9272 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9273 element == EL_SP_GRAVITY_ON_PORT_UP ||
9274 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9275 game.gravity = TRUE;
9276 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9277 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9278 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9279 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9280 game.gravity = FALSE;
9283 /* automatically move to the next field with double speed */
9284 player->programmed_action = move_direction;
9286 if (player->move_delay_reset_counter == 0)
9288 player->move_delay_reset_counter = 2; /* two double speed steps */
9290 DOUBLE_PLAYER_SPEED(player);
9293 PlayLevelSoundAction(x, y, ACTION_PASSING);
9295 else if (IS_DIGGABLE(element))
9299 if (mode != DF_SNAP)
9301 GfxElement[x][y] = GFX_ELEMENT(element);
9302 player->is_digging = TRUE;
9305 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9307 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X,
9308 player->index_bit, dig_side);
9310 if (mode == DF_SNAP)
9311 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9313 else if (IS_COLLECTIBLE(element))
9317 if (is_player && mode != DF_SNAP)
9319 GfxElement[x][y] = element;
9320 player->is_collecting = TRUE;
9323 if (element == EL_SPEED_PILL)
9325 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9327 else if (element == EL_EXTRA_TIME && level.time > 0)
9330 DrawGameValue_Time(TimeLeft);
9332 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9334 player->shield_normal_time_left += 10;
9335 if (element == EL_SHIELD_DEADLY)
9336 player->shield_deadly_time_left += 10;
9338 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9340 if (player->inventory_size < MAX_INVENTORY_SIZE)
9341 player->inventory_element[player->inventory_size++] = element;
9343 DrawGameValue_Dynamite(local_player->inventory_size);
9345 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9347 player->dynabomb_count++;
9348 player->dynabombs_left++;
9350 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9352 player->dynabomb_size++;
9354 else if (element == EL_DYNABOMB_INCREASE_POWER)
9356 player->dynabomb_xl = TRUE;
9358 else if (IS_KEY(element))
9360 player->key[KEY_NR(element)] = TRUE;
9362 DrawGameValue_Keys(player->key);
9364 redraw_mask |= REDRAW_DOOR_1;
9366 else if (IS_ENVELOPE(element))
9368 player->show_envelope = element;
9370 else if (IS_DROPPABLE(element) ||
9371 IS_THROWABLE(element)) /* can be collected and dropped */
9375 if (collect_count == 0)
9376 player->inventory_infinite_element = element;
9378 for (i = 0; i < collect_count; i++)
9379 if (player->inventory_size < MAX_INVENTORY_SIZE)
9380 player->inventory_element[player->inventory_size++] = element;
9382 DrawGameValue_Dynamite(local_player->inventory_size);
9384 else if (collect_count > 0)
9386 local_player->gems_still_needed -= collect_count;
9387 if (local_player->gems_still_needed < 0)
9388 local_player->gems_still_needed = 0;
9390 DrawGameValue_Emeralds(local_player->gems_still_needed);
9393 RaiseScoreElement(element);
9394 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9397 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X,
9398 player->index_bit, dig_side);
9400 if (mode == DF_SNAP)
9401 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9403 else if (IS_PUSHABLE(element))
9405 if (mode == DF_SNAP && element != EL_BD_ROCK)
9406 return MF_NO_ACTION;
9408 if (CAN_FALL(element) && dy)
9409 return MF_NO_ACTION;
9411 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9412 !(element == EL_SPRING && level.use_spring_bug))
9413 return MF_NO_ACTION;
9415 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9416 ((move_direction & MV_VERTICAL &&
9417 ((element_info[element].move_pattern & MV_LEFT &&
9418 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9419 (element_info[element].move_pattern & MV_RIGHT &&
9420 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9421 (move_direction & MV_HORIZONTAL &&
9422 ((element_info[element].move_pattern & MV_UP &&
9423 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9424 (element_info[element].move_pattern & MV_DOWN &&
9425 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9426 return MF_NO_ACTION;
9428 /* do not push elements already moving away faster than player */
9429 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9430 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9431 return MF_NO_ACTION;
9433 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9435 if (player->push_delay_value == -1 || !player_was_pushing)
9436 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9438 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9440 if (player->push_delay_value == -1)
9441 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9443 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9445 if (!player->is_pushing)
9446 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9449 player->is_pushing = TRUE;
9451 if (!(IN_LEV_FIELD(nextx, nexty) &&
9452 (IS_FREE(nextx, nexty) ||
9453 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9454 IS_SB_ELEMENT(element)))))
9455 return MF_NO_ACTION;
9457 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9458 return MF_NO_ACTION;
9460 if (player->push_delay == -1) /* new pushing; restart delay */
9461 player->push_delay = 0;
9463 if (player->push_delay < player->push_delay_value &&
9464 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9465 element != EL_SPRING && element != EL_BALLOON)
9467 /* make sure that there is no move delay before next try to push */
9468 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9469 player->move_delay = 0;
9471 return MF_NO_ACTION;
9474 if (IS_SB_ELEMENT(element))
9476 if (element == EL_SOKOBAN_FIELD_FULL)
9478 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9479 local_player->sokobanfields_still_needed++;
9482 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9484 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9485 local_player->sokobanfields_still_needed--;
9488 Feld[x][y] = EL_SOKOBAN_OBJECT;
9490 if (Back[x][y] == Back[nextx][nexty])
9491 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9492 else if (Back[x][y] != 0)
9493 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9496 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9499 if (local_player->sokobanfields_still_needed == 0 &&
9500 game.emulation == EMU_SOKOBAN)
9502 player->LevelSolved = player->GameOver = TRUE;
9503 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9507 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9509 InitMovingField(x, y, move_direction);
9510 GfxAction[x][y] = ACTION_PUSHING;
9512 if (mode == DF_SNAP)
9513 ContinueMoving(x, y);
9515 MovPos[x][y] = (dx != 0 ? dx : dy);
9517 Pushed[x][y] = TRUE;
9518 Pushed[nextx][nexty] = TRUE;
9520 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9521 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9523 player->push_delay_value = -1; /* get new value later */
9525 /* check for element change _after_ element has been pushed */
9526 if (game.use_change_when_pushing_bug)
9528 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9529 player->index_bit, dig_side);
9530 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
9531 player->index_bit, dig_side);
9534 else if (IS_SWITCHABLE(element))
9536 if (PLAYER_SWITCHING(player, x, y))
9538 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9539 player->index_bit, dig_side);
9544 player->is_switching = TRUE;
9545 player->switch_x = x;
9546 player->switch_y = y;
9548 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9550 if (element == EL_ROBOT_WHEEL)
9552 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9556 DrawLevelField(x, y);
9558 else if (element == EL_SP_TERMINAL)
9562 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9564 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9566 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9567 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9570 else if (IS_BELT_SWITCH(element))
9572 ToggleBeltSwitch(x, y);
9574 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9575 element == EL_SWITCHGATE_SWITCH_DOWN)
9577 ToggleSwitchgateSwitch(x, y);
9579 else if (element == EL_LIGHT_SWITCH ||
9580 element == EL_LIGHT_SWITCH_ACTIVE)
9582 ToggleLightSwitch(x, y);
9584 else if (element == EL_TIMEGATE_SWITCH)
9586 ActivateTimegateSwitch(x, y);
9588 else if (element == EL_BALLOON_SWITCH_LEFT ||
9589 element == EL_BALLOON_SWITCH_RIGHT ||
9590 element == EL_BALLOON_SWITCH_UP ||
9591 element == EL_BALLOON_SWITCH_DOWN ||
9592 element == EL_BALLOON_SWITCH_ANY)
9594 if (element == EL_BALLOON_SWITCH_ANY)
9595 game.balloon_dir = move_direction;
9597 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9598 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9599 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9600 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9603 else if (element == EL_LAMP)
9605 Feld[x][y] = EL_LAMP_ACTIVE;
9606 local_player->lights_still_needed--;
9608 ResetGfxAnimation(x, y);
9609 DrawLevelField(x, y);
9611 else if (element == EL_TIME_ORB_FULL)
9613 Feld[x][y] = EL_TIME_ORB_EMPTY;
9615 DrawGameValue_Time(TimeLeft);
9617 ResetGfxAnimation(x, y);
9618 DrawLevelField(x, y);
9621 CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
9622 player->index_bit, dig_side);
9624 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9625 player->index_bit, dig_side);
9631 if (!PLAYER_SWITCHING(player, x, y))
9633 player->is_switching = TRUE;
9634 player->switch_x = x;
9635 player->switch_y = y;
9637 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
9638 player->index_bit, dig_side);
9639 CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
9640 player->index_bit, dig_side);
9643 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9644 player->index_bit, dig_side);
9645 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9646 player->index_bit, dig_side);
9648 return MF_NO_ACTION;
9651 player->push_delay = -1;
9653 if (is_player) /* function can also be called by EL_PENGUIN */
9655 if (Feld[x][y] != element) /* really digged/collected something */
9656 player->is_collecting = !player->is_digging;
9662 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9664 int jx = player->jx, jy = player->jy;
9665 int x = jx + dx, y = jy + dy;
9666 int snap_direction = (dx == -1 ? MV_LEFT :
9667 dx == +1 ? MV_RIGHT :
9669 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9671 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
9674 if (!player->active || !IN_LEV_FIELD(x, y))
9682 if (player->MovPos == 0)
9683 player->is_pushing = FALSE;
9685 player->is_snapping = FALSE;
9687 if (player->MovPos == 0)
9689 player->is_moving = FALSE;
9690 player->is_digging = FALSE;
9691 player->is_collecting = FALSE;
9697 if (player->is_snapping)
9700 player->MovDir = snap_direction;
9702 if (player->MovPos == 0)
9704 player->is_moving = FALSE;
9705 player->is_digging = FALSE;
9706 player->is_collecting = FALSE;
9709 player->is_dropping = FALSE;
9711 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9714 player->is_snapping = TRUE;
9716 if (player->MovPos == 0)
9718 player->is_moving = FALSE;
9719 player->is_digging = FALSE;
9720 player->is_collecting = FALSE;
9723 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
9724 DrawLevelField(player->last_jx, player->last_jy);
9726 DrawLevelField(x, y);
9731 boolean DropElement(struct PlayerInfo *player)
9733 int old_element, new_element;
9734 int dropx = player->jx, dropy = player->jy;
9735 int drop_direction = player->MovDir;
9736 int drop_side = drop_direction;
9737 int drop_element = (player->inventory_size > 0 ?
9738 player->inventory_element[player->inventory_size - 1] :
9739 player->inventory_infinite_element != EL_UNDEFINED ?
9740 player->inventory_infinite_element :
9741 player->dynabombs_left > 0 ?
9742 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
9745 /* do not drop an element on top of another element; when holding drop key
9746 pressed without moving, dropped element must move away before the next
9747 element can be dropped (this is especially important if the next element
9748 is dynamite, which can be placed on background for historical reasons) */
9749 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
9752 if (IS_THROWABLE(drop_element))
9754 dropx += GET_DX_FROM_DIR(drop_direction);
9755 dropy += GET_DY_FROM_DIR(drop_direction);
9757 if (!IN_LEV_FIELD(dropx, dropy))
9761 old_element = Feld[dropx][dropy]; /* old element at dropping position */
9762 new_element = drop_element; /* default: no change when dropping */
9764 /* check if player is active, not moving and ready to drop */
9765 if (!player->active || player->MovPos || player->drop_delay > 0)
9768 /* check if player has anything that can be dropped */
9769 if (new_element == EL_UNDEFINED)
9772 /* check if anything can be dropped at the current position */
9773 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9776 /* collected custom elements can only be dropped on empty fields */
9777 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
9780 if (old_element != EL_EMPTY)
9781 Back[dropx][dropy] = old_element; /* store old element on this field */
9783 ResetGfxAnimation(dropx, dropy);
9784 ResetRandomAnimationValue(dropx, dropy);
9786 if (player->inventory_size > 0 ||
9787 player->inventory_infinite_element != EL_UNDEFINED)
9789 if (player->inventory_size > 0)
9791 player->inventory_size--;
9793 DrawGameValue_Dynamite(local_player->inventory_size);
9795 if (new_element == EL_DYNAMITE)
9796 new_element = EL_DYNAMITE_ACTIVE;
9797 else if (new_element == EL_SP_DISK_RED)
9798 new_element = EL_SP_DISK_RED_ACTIVE;
9801 Feld[dropx][dropy] = new_element;
9803 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
9804 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
9805 el2img(Feld[dropx][dropy]), 0);
9807 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
9809 /* needed if previous element just changed to "empty" in the last frame */
9810 Changed[dropx][dropy] = FALSE; /* allow another change */
9812 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
9813 player->index_bit, drop_side);
9814 CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X,
9815 player->index_bit, drop_side);
9817 TestIfElementTouchesCustomElement(dropx, dropy);
9819 else /* player is dropping a dyna bomb */
9821 player->dynabombs_left--;
9823 Feld[dropx][dropy] = new_element;
9825 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
9826 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
9827 el2img(Feld[dropx][dropy]), 0);
9829 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
9832 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
9833 InitField_WithBug1(dropx, dropy, FALSE);
9835 new_element = Feld[dropx][dropy]; /* element might have changed */
9837 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9838 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9840 int move_direction, nextx, nexty;
9842 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9843 MovDir[dropx][dropy] = drop_direction;
9845 move_direction = MovDir[dropx][dropy];
9846 nextx = dropx + GET_DX_FROM_DIR(move_direction);
9847 nexty = dropy + GET_DY_FROM_DIR(move_direction);
9849 Changed[dropx][dropy] = FALSE; /* allow another change */
9850 CheckCollision[dropx][dropy] = 2;
9853 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
9854 player->is_dropping = TRUE;
9856 player->drop_x = dropx;
9857 player->drop_y = dropy;
9862 /* ------------------------------------------------------------------------- */
9863 /* game sound playing functions */
9864 /* ------------------------------------------------------------------------- */
9866 static int *loop_sound_frame = NULL;
9867 static int *loop_sound_volume = NULL;
9869 void InitPlayLevelSound()
9871 int num_sounds = getSoundListSize();
9873 checked_free(loop_sound_frame);
9874 checked_free(loop_sound_volume);
9876 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9877 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9880 static void PlayLevelSound(int x, int y, int nr)
9882 int sx = SCREENX(x), sy = SCREENY(y);
9883 int volume, stereo_position;
9884 int max_distance = 8;
9885 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9887 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9888 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9891 if (!IN_LEV_FIELD(x, y) ||
9892 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9893 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9896 volume = SOUND_MAX_VOLUME;
9898 if (!IN_SCR_FIELD(sx, sy))
9900 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9901 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9903 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9906 stereo_position = (SOUND_MAX_LEFT +
9907 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9908 (SCR_FIELDX + 2 * max_distance));
9910 if (IS_LOOP_SOUND(nr))
9912 /* This assures that quieter loop sounds do not overwrite louder ones,
9913 while restarting sound volume comparison with each new game frame. */
9915 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9918 loop_sound_volume[nr] = volume;
9919 loop_sound_frame[nr] = FrameCounter;
9922 PlaySoundExt(nr, volume, stereo_position, type);
9925 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9927 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9928 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9929 y < LEVELY(BY1) ? LEVELY(BY1) :
9930 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9934 static void PlayLevelSoundAction(int x, int y, int action)
9936 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9939 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9941 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
9943 if (sound_effect != SND_UNDEFINED)
9944 PlayLevelSound(x, y, sound_effect);
9947 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9950 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
9952 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9953 PlayLevelSound(x, y, sound_effect);
9956 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9958 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
9960 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9961 PlayLevelSound(x, y, sound_effect);
9964 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9966 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
9968 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9969 StopSound(sound_effect);
9972 static void PlayLevelMusic()
9974 if (levelset.music[level_nr] != MUS_UNDEFINED)
9975 PlayMusic(levelset.music[level_nr]); /* from config file */
9977 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9980 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
9982 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
9987 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
9991 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9995 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
9999 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10003 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10007 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10011 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10014 case SAMPLE_android_clone:
10015 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10018 case SAMPLE_android_move:
10019 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10022 case SAMPLE_spring:
10023 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10027 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10031 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10034 case SAMPLE_eater_eat:
10035 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10039 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10042 case SAMPLE_collect:
10043 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10046 case SAMPLE_diamond:
10047 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10050 case SAMPLE_squash:
10051 /* !!! CHECK THIS !!! */
10053 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10055 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10059 case SAMPLE_wonderfall:
10060 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10064 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10068 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10072 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10076 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10080 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10084 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10087 case SAMPLE_wonder:
10088 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10092 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10095 case SAMPLE_exit_open:
10096 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10099 case SAMPLE_exit_leave:
10100 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10103 case SAMPLE_dynamite:
10104 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10108 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10112 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10116 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10120 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10124 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10128 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10132 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10137 void RaiseScore(int value)
10139 local_player->score += value;
10141 DrawGameValue_Score(local_player->score);
10144 void RaiseScoreElement(int element)
10149 case EL_BD_DIAMOND:
10150 case EL_EMERALD_YELLOW:
10151 case EL_EMERALD_RED:
10152 case EL_EMERALD_PURPLE:
10153 case EL_SP_INFOTRON:
10154 RaiseScore(level.score[SC_EMERALD]);
10157 RaiseScore(level.score[SC_DIAMOND]);
10160 RaiseScore(level.score[SC_CRYSTAL]);
10163 RaiseScore(level.score[SC_PEARL]);
10166 case EL_BD_BUTTERFLY:
10167 case EL_SP_ELECTRON:
10168 RaiseScore(level.score[SC_BUG]);
10171 case EL_BD_FIREFLY:
10172 case EL_SP_SNIKSNAK:
10173 RaiseScore(level.score[SC_SPACESHIP]);
10176 case EL_DARK_YAMYAM:
10177 RaiseScore(level.score[SC_YAMYAM]);
10180 RaiseScore(level.score[SC_ROBOT]);
10183 RaiseScore(level.score[SC_PACMAN]);
10186 RaiseScore(level.score[SC_NUT]);
10189 case EL_SP_DISK_RED:
10190 case EL_DYNABOMB_INCREASE_NUMBER:
10191 case EL_DYNABOMB_INCREASE_SIZE:
10192 case EL_DYNABOMB_INCREASE_POWER:
10193 RaiseScore(level.score[SC_DYNAMITE]);
10195 case EL_SHIELD_NORMAL:
10196 case EL_SHIELD_DEADLY:
10197 RaiseScore(level.score[SC_SHIELD]);
10199 case EL_EXTRA_TIME:
10200 RaiseScore(level.score[SC_TIME_BONUS]);
10214 RaiseScore(level.score[SC_KEY]);
10217 RaiseScore(element_info[element].collect_score);
10222 void RequestQuitGame(boolean ask_if_really_quit)
10224 if (AllPlayersGone ||
10225 !ask_if_really_quit ||
10226 level_editor_test_game ||
10227 Request("Do you really want to quit the game ?",
10228 REQ_ASK | REQ_STAY_CLOSED))
10230 #if defined(NETWORK_AVALIABLE)
10231 if (options.network)
10232 SendToServer_StopPlaying();
10236 game_status = GAME_MODE_MAIN;
10242 if (tape.playing && tape.deactivate_display)
10243 TapeDeactivateDisplayOff(TRUE);
10245 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10247 if (tape.playing && tape.deactivate_display)
10248 TapeDeactivateDisplayOn();
10253 /* ---------- new game button stuff ---------------------------------------- */
10255 /* graphic position values for game buttons */
10256 #define GAME_BUTTON_XSIZE 30
10257 #define GAME_BUTTON_YSIZE 30
10258 #define GAME_BUTTON_XPOS 5
10259 #define GAME_BUTTON_YPOS 215
10260 #define SOUND_BUTTON_XPOS 5
10261 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10263 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10264 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10265 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10266 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10267 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10268 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10275 } gamebutton_info[NUM_GAME_BUTTONS] =
10278 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10283 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10284 GAME_CTRL_ID_PAUSE,
10288 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10293 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10294 SOUND_CTRL_ID_MUSIC,
10295 "background music on/off"
10298 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10299 SOUND_CTRL_ID_LOOPS,
10300 "sound loops on/off"
10303 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10304 SOUND_CTRL_ID_SIMPLE,
10305 "normal sounds on/off"
10309 void CreateGameButtons()
10313 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10315 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10316 struct GadgetInfo *gi;
10319 unsigned long event_mask;
10320 int gd_xoffset, gd_yoffset;
10321 int gd_x1, gd_x2, gd_y1, gd_y2;
10324 gd_xoffset = gamebutton_info[i].x;
10325 gd_yoffset = gamebutton_info[i].y;
10326 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10327 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10329 if (id == GAME_CTRL_ID_STOP ||
10330 id == GAME_CTRL_ID_PAUSE ||
10331 id == GAME_CTRL_ID_PLAY)
10333 button_type = GD_TYPE_NORMAL_BUTTON;
10335 event_mask = GD_EVENT_RELEASED;
10336 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10337 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10341 button_type = GD_TYPE_CHECK_BUTTON;
10343 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10344 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10345 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10346 event_mask = GD_EVENT_PRESSED;
10347 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10348 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10351 gi = CreateGadget(GDI_CUSTOM_ID, id,
10352 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10353 GDI_X, DX + gd_xoffset,
10354 GDI_Y, DY + gd_yoffset,
10355 GDI_WIDTH, GAME_BUTTON_XSIZE,
10356 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10357 GDI_TYPE, button_type,
10358 GDI_STATE, GD_BUTTON_UNPRESSED,
10359 GDI_CHECKED, checked,
10360 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10361 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10362 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10363 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10364 GDI_EVENT_MASK, event_mask,
10365 GDI_CALLBACK_ACTION, HandleGameButtons,
10369 Error(ERR_EXIT, "cannot create gadget");
10371 game_gadget[id] = gi;
10375 void FreeGameButtons()
10379 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10380 FreeGadget(game_gadget[i]);
10383 static void MapGameButtons()
10387 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10388 MapGadget(game_gadget[i]);
10391 void UnmapGameButtons()
10395 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10396 UnmapGadget(game_gadget[i]);
10399 static void HandleGameButtons(struct GadgetInfo *gi)
10401 int id = gi->custom_id;
10403 if (game_status != GAME_MODE_PLAYING)
10408 case GAME_CTRL_ID_STOP:
10409 RequestQuitGame(TRUE);
10412 case GAME_CTRL_ID_PAUSE:
10413 if (options.network)
10415 #if defined(NETWORK_AVALIABLE)
10417 SendToServer_ContinuePlaying();
10419 SendToServer_PausePlaying();
10423 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10426 case GAME_CTRL_ID_PLAY:
10429 #if defined(NETWORK_AVALIABLE)
10430 if (options.network)
10431 SendToServer_ContinuePlaying();
10435 tape.pausing = FALSE;
10436 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10441 case SOUND_CTRL_ID_MUSIC:
10442 if (setup.sound_music)
10444 setup.sound_music = FALSE;
10447 else if (audio.music_available)
10449 setup.sound = setup.sound_music = TRUE;
10451 SetAudioMode(setup.sound);
10457 case SOUND_CTRL_ID_LOOPS:
10458 if (setup.sound_loops)
10459 setup.sound_loops = FALSE;
10460 else if (audio.loops_available)
10462 setup.sound = setup.sound_loops = TRUE;
10463 SetAudioMode(setup.sound);
10467 case SOUND_CTRL_ID_SIMPLE:
10468 if (setup.sound_simple)
10469 setup.sound_simple = FALSE;
10470 else if (audio.sound_available)
10472 setup.sound = setup.sound_simple = TRUE;
10473 SetAudioMode(setup.sound);