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 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
106 (CAN_MOVE_INTO_ACID(e) && \
107 Feld[x][y] == EL_ACID) || \
111 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
112 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 (DONT_COLLIDE_WITH(e) && \
116 !PLAYER_ENEMY_PROTECTED(x, y))))
118 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
119 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
121 (CAN_MOVE_INTO_ACID(e) && \
122 Feld[x][y] == EL_ACID) || \
123 (DONT_COLLIDE_WITH(e) && \
125 !PLAYER_ENEMY_PROTECTED(x, y))))
128 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
129 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
132 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
133 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
135 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
136 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
138 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
139 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
142 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
144 #define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0)
147 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
149 Feld[x][y] == EL_DIAMOND))
151 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
153 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
155 #define PACMAN_CAN_ENTER_FIELD(x, y) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
157 IS_AMOEBOID(Feld[x][y])))
159 #define PIG_CAN_ENTER_FIELD(x, y) \
160 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
161 IS_FOOD_PIG(Feld[x][y])))
163 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
164 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 IS_FOOD_PENGUIN(Feld[x][y]) || \
166 Feld[x][y] == EL_EXIT_OPEN || \
167 Feld[x][y] == EL_ACID))
169 #define GROUP_NR(e) ((e) - EL_GROUP_START)
170 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
171 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
172 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
174 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
175 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
178 #define CE_ENTER_FIELD_COND(e, x, y) \
179 (!IS_PLAYER(x, y) && \
180 (Feld[x][y] == EL_ACID || \
181 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
183 #define CE_ENTER_FIELD_COND(e, x, y) \
184 (!IS_PLAYER(x, y) && \
185 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
188 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
189 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
191 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
194 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
195 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
197 /* game button identifiers */
198 #define GAME_CTRL_ID_STOP 0
199 #define GAME_CTRL_ID_PAUSE 1
200 #define GAME_CTRL_ID_PLAY 2
201 #define SOUND_CTRL_ID_MUSIC 3
202 #define SOUND_CTRL_ID_LOOPS 4
203 #define SOUND_CTRL_ID_SIMPLE 5
205 #define NUM_GAME_BUTTONS 6
208 /* forward declaration for internal use */
210 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
211 static boolean MovePlayer(struct PlayerInfo *, int, int);
212 static void ScrollPlayer(struct PlayerInfo *, int);
213 static void ScrollScreen(struct PlayerInfo *, int);
215 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
217 static void InitBeltMovement(void);
218 static void CloseAllOpenTimegates(void);
219 static void CheckGravityMovement(struct PlayerInfo *);
220 static void KillHeroUnlessEnemyProtected(int, int);
221 static void KillHeroUnlessExplosionProtected(int, int);
223 static void TestIfPlayerTouchesCustomElement(int, int);
224 static void TestIfElementTouchesCustomElement(int, int);
225 static void TestIfElementHitsCustomElement(int, int, int);
227 static void ChangeElement(int, int, int);
228 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
229 static boolean CheckTriggeredElementChange(int, int, int, int);
230 static boolean CheckElementSideChange(int, int, int, int, int, int);
231 static boolean CheckElementChange(int, int, int, int);
233 static void PlayLevelSound(int, int, int);
234 static void PlayLevelSoundNearest(int, int, int);
235 static void PlayLevelSoundAction(int, int, int);
236 static void PlayLevelSoundElementAction(int, int, int, int);
237 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
238 static void PlayLevelSoundActionIfLoop(int, int, int);
239 static void StopLevelSoundActionIfLoop(int, int, int);
240 static void PlayLevelMusic();
242 static void MapGameButtons();
243 static void HandleGameButtons(struct GadgetInfo *);
245 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
248 /* ------------------------------------------------------------------------- */
249 /* definition of elements that automatically change to other elements after */
250 /* a specified time, eventually calling a function when changing */
251 /* ------------------------------------------------------------------------- */
253 /* forward declaration for changer functions */
254 static void InitBuggyBase(int x, int y);
255 static void WarnBuggyBase(int x, int y);
257 static void InitTrap(int x, int y);
258 static void ActivateTrap(int x, int y);
259 static void ChangeActiveTrap(int x, int y);
261 static void InitRobotWheel(int x, int y);
262 static void RunRobotWheel(int x, int y);
263 static void StopRobotWheel(int x, int y);
265 static void InitTimegateWheel(int x, int y);
266 static void RunTimegateWheel(int x, int y);
268 struct ChangingElementInfo
273 void (*pre_change_function)(int x, int y);
274 void (*change_function)(int x, int y);
275 void (*post_change_function)(int x, int y);
278 static struct ChangingElementInfo change_delay_list[] =
329 EL_SWITCHGATE_OPENING,
337 EL_SWITCHGATE_CLOSING,
338 EL_SWITCHGATE_CLOSED,
370 EL_ACID_SPLASH_RIGHT,
379 EL_SP_BUGGY_BASE_ACTIVATING,
386 EL_SP_BUGGY_BASE_ACTIVATING,
387 EL_SP_BUGGY_BASE_ACTIVE,
394 EL_SP_BUGGY_BASE_ACTIVE,
418 EL_ROBOT_WHEEL_ACTIVE,
426 EL_TIMEGATE_SWITCH_ACTIVE,
447 int push_delay_fixed, push_delay_random;
452 { EL_BALLOON, 0, 0 },
454 { EL_SOKOBAN_OBJECT, 2, 0 },
455 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
456 { EL_SATELLITE, 2, 0 },
457 { EL_SP_DISK_YELLOW, 2, 0 },
459 { EL_UNDEFINED, 0, 0 },
467 move_stepsize_list[] =
469 { EL_AMOEBA_DROP, 2 },
470 { EL_AMOEBA_DROPPING, 2 },
471 { EL_QUICKSAND_FILLING, 1 },
472 { EL_QUICKSAND_EMPTYING, 1 },
473 { EL_MAGIC_WALL_FILLING, 2 },
474 { EL_BD_MAGIC_WALL_FILLING, 2 },
475 { EL_MAGIC_WALL_EMPTYING, 2 },
476 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
486 collect_count_list[] =
489 { EL_BD_DIAMOND, 1 },
490 { EL_EMERALD_YELLOW, 1 },
491 { EL_EMERALD_RED, 1 },
492 { EL_EMERALD_PURPLE, 1 },
494 { EL_SP_INFOTRON, 1 },
508 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
509 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
510 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
511 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
512 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
513 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
514 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
515 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
516 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
517 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
518 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
523 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
525 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
526 CH_EVENT_BIT(CE_DELAY))
527 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
528 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
529 IS_JUST_CHANGING(x, y))
531 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
534 void GetPlayerConfig()
536 if (!audio.sound_available)
537 setup.sound_simple = FALSE;
539 if (!audio.loops_available)
540 setup.sound_loops = FALSE;
542 if (!audio.music_available)
543 setup.sound_music = FALSE;
545 if (!video.fullscreen_available)
546 setup.fullscreen = FALSE;
548 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
550 SetAudioMode(setup.sound);
554 static int getBeltNrFromBeltElement(int element)
556 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
557 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
558 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
561 static int getBeltNrFromBeltActiveElement(int element)
563 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
564 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
565 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
568 static int getBeltNrFromBeltSwitchElement(int element)
570 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
571 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
572 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
575 static int getBeltDirNrFromBeltSwitchElement(int element)
577 static int belt_base_element[4] =
579 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
580 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
581 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
582 EL_CONVEYOR_BELT_4_SWITCH_LEFT
585 int belt_nr = getBeltNrFromBeltSwitchElement(element);
586 int belt_dir_nr = element - belt_base_element[belt_nr];
588 return (belt_dir_nr % 3);
591 static int getBeltDirFromBeltSwitchElement(int element)
593 static int belt_move_dir[3] =
600 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
602 return belt_move_dir[belt_dir_nr];
605 static void InitPlayerField(int x, int y, int element, boolean init_game)
607 if (element == EL_SP_MURPHY)
611 if (stored_player[0].present)
613 Feld[x][y] = EL_SP_MURPHY_CLONE;
619 stored_player[0].use_murphy_graphic = TRUE;
622 Feld[x][y] = EL_PLAYER_1;
628 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
629 int jx = player->jx, jy = player->jy;
631 player->present = TRUE;
633 player->block_last_field = (element == EL_SP_MURPHY ?
634 level.sp_block_last_field :
635 level.block_last_field);
637 if (!options.network || player->connected)
639 player->active = TRUE;
641 /* remove potentially duplicate players */
642 if (StorePlayer[jx][jy] == Feld[x][y])
643 StorePlayer[jx][jy] = 0;
645 StorePlayer[x][y] = Feld[x][y];
649 printf("Player %d activated.\n", player->element_nr);
650 printf("[Local player is %d and currently %s.]\n",
651 local_player->element_nr,
652 local_player->active ? "active" : "not active");
656 Feld[x][y] = EL_EMPTY;
657 player->jx = player->last_jx = x;
658 player->jy = player->last_jy = y;
662 static void InitField(int x, int y, boolean init_game)
664 int element = Feld[x][y];
673 InitPlayerField(x, y, element, init_game);
676 case EL_SOKOBAN_FIELD_PLAYER:
677 element = Feld[x][y] = EL_PLAYER_1;
678 InitField(x, y, init_game);
680 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
681 InitField(x, y, init_game);
684 case EL_SOKOBAN_FIELD_EMPTY:
685 local_player->sokobanfields_still_needed++;
689 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
690 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
691 else if (x > 0 && Feld[x-1][y] == EL_ACID)
692 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
693 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
694 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
695 else if (y > 0 && Feld[x][y-1] == EL_ACID)
696 Feld[x][y] = EL_ACID_POOL_BOTTOM;
697 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
698 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
706 case EL_SPACESHIP_RIGHT:
707 case EL_SPACESHIP_UP:
708 case EL_SPACESHIP_LEFT:
709 case EL_SPACESHIP_DOWN:
711 case EL_BD_BUTTERFLY_RIGHT:
712 case EL_BD_BUTTERFLY_UP:
713 case EL_BD_BUTTERFLY_LEFT:
714 case EL_BD_BUTTERFLY_DOWN:
715 case EL_BD_BUTTERFLY:
716 case EL_BD_FIREFLY_RIGHT:
717 case EL_BD_FIREFLY_UP:
718 case EL_BD_FIREFLY_LEFT:
719 case EL_BD_FIREFLY_DOWN:
721 case EL_PACMAN_RIGHT:
745 if (y == lev_fieldy - 1)
747 Feld[x][y] = EL_AMOEBA_GROWING;
748 Store[x][y] = EL_AMOEBA_WET;
752 case EL_DYNAMITE_ACTIVE:
753 case EL_SP_DISK_RED_ACTIVE:
754 case EL_DYNABOMB_PLAYER_1_ACTIVE:
755 case EL_DYNABOMB_PLAYER_2_ACTIVE:
756 case EL_DYNABOMB_PLAYER_3_ACTIVE:
757 case EL_DYNABOMB_PLAYER_4_ACTIVE:
762 local_player->lights_still_needed++;
766 local_player->friends_still_needed++;
771 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
776 Feld[x][y] = EL_EMPTY;
781 case EL_EM_KEY_1_FILE:
782 Feld[x][y] = EL_EM_KEY_1;
784 case EL_EM_KEY_2_FILE:
785 Feld[x][y] = EL_EM_KEY_2;
787 case EL_EM_KEY_3_FILE:
788 Feld[x][y] = EL_EM_KEY_3;
790 case EL_EM_KEY_4_FILE:
791 Feld[x][y] = EL_EM_KEY_4;
795 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
796 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
797 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
798 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
799 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
800 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
801 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
802 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
803 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
804 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
805 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
806 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
809 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
810 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
811 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
813 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
815 game.belt_dir[belt_nr] = belt_dir;
816 game.belt_dir_nr[belt_nr] = belt_dir_nr;
818 else /* more than one switch -- set it like the first switch */
820 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
825 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
827 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
830 case EL_LIGHT_SWITCH_ACTIVE:
832 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
836 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
838 else if (IS_GROUP_ELEMENT(element))
840 struct ElementGroupInfo *group = element_info[element].group;
841 int last_anim_random_frame = gfx.anim_random_frame;
844 if (group->choice_mode == ANIM_RANDOM)
845 gfx.anim_random_frame = RND(group->num_elements_resolved);
847 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
848 group->choice_mode, 0,
851 if (group->choice_mode == ANIM_RANDOM)
852 gfx.anim_random_frame = last_anim_random_frame;
856 Feld[x][y] = group->element_resolved[element_pos];
858 InitField(x, y, init_game);
864 static inline void InitField_WithBug1(int x, int y, boolean init_game)
866 InitField(x, y, init_game);
868 /* not needed to call InitMovDir() -- already done by InitField()! */
869 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
870 CAN_MOVE(Feld[x][y]))
874 static inline void InitField_WithBug2(int x, int y, boolean init_game)
876 int old_element = Feld[x][y];
878 InitField(x, y, init_game);
880 /* not needed to call InitMovDir() -- already done by InitField()! */
881 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
882 CAN_MOVE(old_element) &&
883 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
886 /* this case is in fact a combination of not less than three bugs:
887 first, it calls InitMovDir() for elements that can move, although this is
888 already done by InitField(); then, it checks the element that was at this
889 field _before_ the call to InitField() (which can change it)
894 void DrawGameDoorValues()
898 for (i = 0; i < MAX_PLAYERS; i++)
899 for (j = 0; j < 4; j++)
900 if (stored_player[i].key[j])
901 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
902 el2edimg(EL_KEY_1 + j));
904 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
905 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
906 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
907 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
908 DrawText(DX + XX_SCORE, DY + YY_SCORE,
909 int2str(local_player->score, 5), FONT_TEXT_2);
910 DrawText(DX + XX_TIME, DY + YY_TIME,
911 int2str(TimeLeft, 3), FONT_TEXT_2);
914 static void resolve_group_element(int group_element, int recursion_depth)
917 static struct ElementGroupInfo *group;
918 struct ElementGroupInfo *actual_group = element_info[group_element].group;
921 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
923 Error(ERR_WARN, "recursion too deep when resolving group element %d",
924 group_element - EL_GROUP_START + 1);
926 /* replace element which caused too deep recursion by question mark */
927 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
932 if (recursion_depth == 0) /* initialization */
934 group = element_info[group_element].group;
935 group_nr = group_element - EL_GROUP_START;
937 group->num_elements_resolved = 0;
938 group->choice_pos = 0;
941 for (i = 0; i < actual_group->num_elements; i++)
943 int element = actual_group->element[i];
945 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
948 if (IS_GROUP_ELEMENT(element))
949 resolve_group_element(element, recursion_depth + 1);
952 group->element_resolved[group->num_elements_resolved++] = element;
953 element_info[element].in_group[group_nr] = TRUE;
958 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
960 printf("::: group %d: %d resolved elements\n",
961 group_element - EL_GROUP_START, group->num_elements_resolved);
962 for (i = 0; i < group->num_elements_resolved; i++)
963 printf("::: - %d ['%s']\n", group->element_resolved[i],
964 element_info[group->element_resolved[i]].token_name);
971 =============================================================================
973 -----------------------------------------------------------------------------
974 initialize game engine due to level / tape version number
975 =============================================================================
978 static void InitGameEngine()
982 /* set game engine from tape file when re-playing, else from level file */
983 game.engine_version = (tape.playing ? tape.engine_version :
986 /* dynamically adjust element properties according to game engine version */
987 InitElementPropertiesEngine(game.engine_version);
990 printf("level %d: level version == %06d\n", level_nr, level.game_version);
991 printf(" tape version == %06d [%s] [file: %06d]\n",
992 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
994 printf(" => game.engine_version == %06d\n", game.engine_version);
997 /* ---------- recursively resolve group elements ------------------------- */
999 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1000 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1001 element_info[i].in_group[j] = FALSE;
1003 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1004 resolve_group_element(EL_GROUP_START + i, 0);
1006 /* ---------- initialize player's initial move delay --------------------- */
1008 /* dynamically adjust player properties according to game engine version */
1009 game.initial_move_delay =
1010 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1011 INITIAL_MOVE_DELAY_OFF);
1013 /* dynamically adjust player properties according to level information */
1014 game.initial_move_delay_value =
1015 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1017 /* ---------- initialize player's initial push delay --------------------- */
1019 /* dynamically adjust player properties according to game engine version */
1020 game.initial_push_delay_value =
1021 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1023 /* ---------- initialize changing elements ------------------------------- */
1025 /* initialize changing elements information */
1026 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1028 struct ElementInfo *ei = &element_info[i];
1030 /* this pointer might have been changed in the level editor */
1031 ei->change = &ei->change_page[0];
1033 if (!IS_CUSTOM_ELEMENT(i))
1035 ei->change->target_element = EL_EMPTY_SPACE;
1036 ei->change->delay_fixed = 0;
1037 ei->change->delay_random = 0;
1038 ei->change->delay_frames = 1;
1041 ei->change_events = CE_BITMASK_DEFAULT;
1042 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1044 ei->event_page_nr[j] = 0;
1045 ei->event_page[j] = &ei->change_page[0];
1049 /* add changing elements from pre-defined list */
1050 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1052 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1053 struct ElementInfo *ei = &element_info[ch_delay->element];
1055 ei->change->target_element = ch_delay->target_element;
1056 ei->change->delay_fixed = ch_delay->change_delay;
1058 ei->change->pre_change_function = ch_delay->pre_change_function;
1059 ei->change->change_function = ch_delay->change_function;
1060 ei->change->post_change_function = ch_delay->post_change_function;
1062 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1066 /* add change events from custom element configuration */
1067 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1069 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1071 for (j = 0; j < ei->num_change_pages; j++)
1073 if (!ei->change_page[j].can_change)
1076 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1078 /* only add event page for the first page found with this event */
1079 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1080 !(ei->change_events & CH_EVENT_BIT(k)))
1082 ei->change_events |= CH_EVENT_BIT(k);
1083 ei->event_page_nr[k] = j;
1084 ei->event_page[k] = &ei->change_page[j];
1092 /* add change events from custom element configuration */
1093 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1095 int element = EL_CUSTOM_START + i;
1097 /* only add custom elements that change after fixed/random frame delay */
1098 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1099 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1103 /* ---------- initialize trigger events ---------------------------------- */
1105 /* initialize trigger events information */
1106 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1107 trigger_events[i] = EP_BITMASK_DEFAULT;
1110 /* add trigger events from element change event properties */
1111 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1113 struct ElementInfo *ei = &element_info[i];
1115 for (j = 0; j < ei->num_change_pages; j++)
1117 if (!ei->change_page[j].can_change)
1120 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1122 int trigger_element = ei->change_page[j].trigger_element;
1124 if (IS_GROUP_ELEMENT(trigger_element))
1126 struct ElementGroupInfo *group = element_info[trigger_element].group;
1128 for (k = 0; k < group->num_elements_resolved; k++)
1129 trigger_events[group->element_resolved[k]]
1130 |= ei->change_page[j].events;
1133 trigger_events[trigger_element] |= ei->change_page[j].events;
1138 /* add trigger events from element change event properties */
1139 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1140 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1141 trigger_events[element_info[i].change->trigger_element] |=
1142 element_info[i].change->events;
1145 /* ---------- initialize push delay -------------------------------------- */
1147 /* initialize push delay values to default */
1148 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1150 if (!IS_CUSTOM_ELEMENT(i))
1152 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1153 element_info[i].push_delay_random = game.default_push_delay_random;
1157 /* set push delay value for certain elements from pre-defined list */
1158 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1160 int e = push_delay_list[i].element;
1162 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1163 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1166 /* set push delay value for Supaplex elements for newer engine versions */
1167 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1171 if (IS_SP_ELEMENT(i))
1173 element_info[i].push_delay_fixed = 6;
1174 element_info[i].push_delay_random = 0;
1179 /* ---------- initialize move stepsize ----------------------------------- */
1181 /* initialize move stepsize values to default */
1182 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1183 if (!IS_CUSTOM_ELEMENT(i))
1184 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1186 /* set move stepsize value for certain elements from pre-defined list */
1187 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1189 int e = move_stepsize_list[i].element;
1191 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1194 /* ---------- initialize move dig/leave ---------------------------------- */
1196 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1198 element_info[i].can_leave_element = FALSE;
1199 element_info[i].can_leave_element_last = FALSE;
1202 /* ---------- initialize gem count --------------------------------------- */
1204 /* initialize gem count values for each element */
1205 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1206 if (!IS_CUSTOM_ELEMENT(i))
1207 element_info[i].collect_count = 0;
1209 /* add gem count values for all elements from pre-defined list */
1210 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1211 element_info[collect_count_list[i].element].collect_count =
1212 collect_count_list[i].count;
1214 /* ---------- initialize access direction -------------------------------- */
1216 /* initialize access direction values to default */
1217 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1218 if (!IS_CUSTOM_ELEMENT(i))
1219 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1221 /* set access direction value for certain elements from pre-defined list */
1222 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1223 element_info[tube_access[i].element].access_direction =
1224 tube_access[i].direction;
1229 =============================================================================
1231 -----------------------------------------------------------------------------
1232 initialize and start new game
1233 =============================================================================
1238 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1239 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1240 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1247 #if USE_NEW_AMOEBA_CODE
1248 printf("Using new amoeba code.\n");
1250 printf("Using old amoeba code.\n");
1255 /* don't play tapes over network */
1256 network_playing = (options.network && !tape.playing);
1258 for (i = 0; i < MAX_PLAYERS; i++)
1260 struct PlayerInfo *player = &stored_player[i];
1262 player->index_nr = i;
1263 player->element_nr = EL_PLAYER_1 + i;
1265 player->present = FALSE;
1266 player->active = FALSE;
1269 player->effective_action = 0;
1270 player->programmed_action = 0;
1273 player->gems_still_needed = level.gems_needed;
1274 player->sokobanfields_still_needed = 0;
1275 player->lights_still_needed = 0;
1276 player->friends_still_needed = 0;
1278 for (j = 0; j < 4; j++)
1279 player->key[j] = FALSE;
1281 player->dynabomb_count = 0;
1282 player->dynabomb_size = 1;
1283 player->dynabombs_left = 0;
1284 player->dynabomb_xl = FALSE;
1286 player->MovDir = MV_NO_MOVING;
1289 player->GfxDir = MV_NO_MOVING;
1290 player->GfxAction = ACTION_DEFAULT;
1292 player->StepFrame = 0;
1294 player->use_murphy_graphic = FALSE;
1296 player->block_last_field = FALSE;
1298 player->actual_frame_counter = 0;
1300 player->step_counter = 0;
1302 player->last_move_dir = MV_NO_MOVING;
1304 player->is_waiting = FALSE;
1305 player->is_moving = FALSE;
1306 player->is_digging = FALSE;
1307 player->is_snapping = FALSE;
1308 player->is_collecting = FALSE;
1309 player->is_pushing = FALSE;
1310 player->is_switching = FALSE;
1311 player->is_dropping = FALSE;
1313 player->is_bored = FALSE;
1314 player->is_sleeping = FALSE;
1316 player->frame_counter_bored = -1;
1317 player->frame_counter_sleeping = -1;
1319 player->anim_delay_counter = 0;
1320 player->post_delay_counter = 0;
1322 player->action_waiting = ACTION_DEFAULT;
1323 player->last_action_waiting = ACTION_DEFAULT;
1324 player->special_action_bored = ACTION_DEFAULT;
1325 player->special_action_sleeping = ACTION_DEFAULT;
1327 player->num_special_action_bored = 0;
1328 player->num_special_action_sleeping = 0;
1330 /* determine number of special actions for bored and sleeping animation */
1331 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1333 boolean found = FALSE;
1335 for (k = 0; k < NUM_DIRECTIONS; k++)
1336 if (el_act_dir2img(player->element_nr, j, k) !=
1337 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1341 player->num_special_action_bored++;
1345 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1347 boolean found = FALSE;
1349 for (k = 0; k < NUM_DIRECTIONS; k++)
1350 if (el_act_dir2img(player->element_nr, j, k) !=
1351 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1355 player->num_special_action_sleeping++;
1360 player->switch_x = -1;
1361 player->switch_y = -1;
1363 player->show_envelope = 0;
1365 player->move_delay = game.initial_move_delay;
1366 player->move_delay_value = game.initial_move_delay_value;
1368 player->move_delay_reset_counter = 0;
1370 player->push_delay = 0;
1371 player->push_delay_value = game.initial_push_delay_value;
1373 player->drop_delay = 0;
1375 player->last_jx = player->last_jy = 0;
1376 player->jx = player->jy = 0;
1378 player->shield_normal_time_left = 0;
1379 player->shield_deadly_time_left = 0;
1381 player->inventory_size = 0;
1383 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1384 SnapField(player, 0, 0);
1386 player->LevelSolved = FALSE;
1387 player->GameOver = FALSE;
1390 network_player_action_received = FALSE;
1392 #if defined(PLATFORM_UNIX)
1393 /* initial null action */
1394 if (network_playing)
1395 SendToServer_MovePlayer(MV_NO_MOVING);
1403 TimeLeft = level.time;
1405 ScreenMovDir = MV_NO_MOVING;
1409 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1411 AllPlayersGone = FALSE;
1413 game.yamyam_content_nr = 0;
1414 game.magic_wall_active = FALSE;
1415 game.magic_wall_time_left = 0;
1416 game.light_time_left = 0;
1417 game.timegate_time_left = 0;
1418 game.switchgate_pos = 0;
1419 game.balloon_dir = MV_NO_MOVING;
1420 game.gravity = level.initial_gravity;
1421 game.explosions_delayed = TRUE;
1423 game.envelope_active = FALSE;
1425 for (i = 0; i < 4; i++)
1427 game.belt_dir[i] = MV_NO_MOVING;
1428 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1431 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1432 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1434 for (x = 0; x < lev_fieldx; x++)
1436 for (y = 0; y < lev_fieldy; y++)
1438 Feld[x][y] = level.field[x][y];
1439 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1440 ChangeDelay[x][y] = 0;
1441 ChangePage[x][y] = -1;
1442 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1444 WasJustMoving[x][y] = 0;
1445 WasJustFalling[x][y] = 0;
1447 Pushed[x][y] = FALSE;
1449 Changed[x][y] = CE_BITMASK_DEFAULT;
1450 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1452 ExplodePhase[x][y] = 0;
1453 ExplodeDelay[x][y] = 0;
1454 ExplodeField[x][y] = EX_NO_EXPLOSION;
1456 RunnerVisit[x][y] = 0;
1457 PlayerVisit[x][y] = 0;
1460 GfxRandom[x][y] = INIT_GFX_RANDOM();
1461 GfxElement[x][y] = EL_UNDEFINED;
1462 GfxAction[x][y] = ACTION_DEFAULT;
1463 GfxDir[x][y] = MV_NO_MOVING;
1467 for (y = 0; y < lev_fieldy; y++)
1469 for (x = 0; x < lev_fieldx; x++)
1471 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1473 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1475 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1478 InitField(x, y, TRUE);
1484 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1485 emulate_sb ? EMU_SOKOBAN :
1486 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1488 /* initialize explosion and ignition delay */
1489 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1491 if (!IS_CUSTOM_ELEMENT(i))
1494 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1495 int last_phase = num_phase * delay;
1496 int half_phase = (num_phase / 2) * delay;
1498 element_info[i].explosion_delay = last_phase;
1499 element_info[i].ignition_delay = half_phase;
1501 if (i == EL_BLACK_ORB)
1502 element_info[i].ignition_delay = 1;
1505 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1506 element_info[i].explosion_delay = 2;
1508 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1509 element_info[i].ignition_delay = 1;
1512 /* correct non-moving belts to start moving left */
1513 for (i = 0; i < 4; i++)
1514 if (game.belt_dir[i] == MV_NO_MOVING)
1515 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1517 /* check if any connected player was not found in playfield */
1518 for (i = 0; i < MAX_PLAYERS; i++)
1520 struct PlayerInfo *player = &stored_player[i];
1522 if (player->connected && !player->present)
1524 for (j = 0; j < MAX_PLAYERS; j++)
1526 struct PlayerInfo *some_player = &stored_player[j];
1527 int jx = some_player->jx, jy = some_player->jy;
1529 /* assign first free player found that is present in the playfield */
1530 if (some_player->present && !some_player->connected)
1532 player->present = TRUE;
1533 player->active = TRUE;
1535 some_player->present = FALSE;
1536 some_player->active = FALSE;
1538 StorePlayer[jx][jy] = player->element_nr;
1539 player->jx = player->last_jx = jx;
1540 player->jy = player->last_jy = jy;
1550 /* when playing a tape, eliminate all players which do not participate */
1552 for (i = 0; i < MAX_PLAYERS; i++)
1554 if (stored_player[i].active && !tape.player_participates[i])
1556 struct PlayerInfo *player = &stored_player[i];
1557 int jx = player->jx, jy = player->jy;
1559 player->active = FALSE;
1560 StorePlayer[jx][jy] = 0;
1561 Feld[jx][jy] = EL_EMPTY;
1565 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1567 /* when in single player mode, eliminate all but the first active player */
1569 for (i = 0; i < MAX_PLAYERS; i++)
1571 if (stored_player[i].active)
1573 for (j = i + 1; j < MAX_PLAYERS; j++)
1575 if (stored_player[j].active)
1577 struct PlayerInfo *player = &stored_player[j];
1578 int jx = player->jx, jy = player->jy;
1580 player->active = FALSE;
1581 player->present = FALSE;
1583 StorePlayer[jx][jy] = 0;
1584 Feld[jx][jy] = EL_EMPTY;
1591 /* when recording the game, store which players take part in the game */
1594 for (i = 0; i < MAX_PLAYERS; i++)
1595 if (stored_player[i].active)
1596 tape.player_participates[i] = TRUE;
1601 for (i = 0; i < MAX_PLAYERS; i++)
1603 struct PlayerInfo *player = &stored_player[i];
1605 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1610 if (local_player == player)
1611 printf("Player %d is local player.\n", i+1);
1615 if (BorderElement == EL_EMPTY)
1618 SBX_Right = lev_fieldx - SCR_FIELDX;
1620 SBY_Lower = lev_fieldy - SCR_FIELDY;
1625 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1627 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1630 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1631 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1633 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1634 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1636 /* if local player not found, look for custom element that might create
1637 the player (make some assumptions about the right custom element) */
1638 if (!local_player->present)
1640 int start_x = 0, start_y = 0;
1641 int found_rating = 0;
1642 int found_element = EL_UNDEFINED;
1644 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1646 int element = Feld[x][y];
1651 if (!IS_CUSTOM_ELEMENT(element))
1654 if (CAN_CHANGE(element))
1656 for (i = 0; i < element_info[element].num_change_pages; i++)
1658 content = element_info[element].change_page[i].target_element;
1659 is_player = ELEM_IS_PLAYER(content);
1661 if (is_player && (found_rating < 3 || element < found_element))
1667 found_element = element;
1672 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1674 content = element_info[element].content[xx][yy];
1675 is_player = ELEM_IS_PLAYER(content);
1677 if (is_player && (found_rating < 2 || element < found_element))
1679 start_x = x + xx - 1;
1680 start_y = y + yy - 1;
1683 found_element = element;
1686 if (!CAN_CHANGE(element))
1689 for (i = 0; i < element_info[element].num_change_pages; i++)
1691 content = element_info[element].change_page[i].content[xx][yy];
1692 is_player = ELEM_IS_PLAYER(content);
1694 if (is_player && (found_rating < 1 || element < found_element))
1696 start_x = x + xx - 1;
1697 start_y = y + yy - 1;
1700 found_element = element;
1706 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1707 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1710 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1711 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1717 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1718 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1719 local_player->jx - MIDPOSX);
1721 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1722 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1723 local_player->jy - MIDPOSY);
1725 scroll_x = SBX_Left;
1726 scroll_y = SBY_Upper;
1727 if (local_player->jx >= SBX_Left + MIDPOSX)
1728 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1729 local_player->jx - MIDPOSX :
1731 if (local_player->jy >= SBY_Upper + MIDPOSY)
1732 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1733 local_player->jy - MIDPOSY :
1738 CloseDoor(DOOR_CLOSE_1);
1743 /* after drawing the level, correct some elements */
1744 if (game.timegate_time_left == 0)
1745 CloseAllOpenTimegates();
1747 if (setup.soft_scrolling)
1748 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1750 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1753 /* copy default game door content to main double buffer */
1754 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1755 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1758 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1761 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1762 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1763 BlitBitmap(drawto, drawto,
1764 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1765 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1766 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1767 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1770 DrawGameDoorValues();
1774 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1775 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1776 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1780 /* copy actual game door content to door double buffer for OpenDoor() */
1781 BlitBitmap(drawto, bitmap_db_door,
1782 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1784 OpenDoor(DOOR_OPEN_ALL);
1786 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1788 if (setup.sound_music)
1791 KeyboardAutoRepeatOffUnlessAutoplay();
1795 for (i = 0; i < 4; i++)
1796 printf("Player %d %sactive.\n",
1797 i + 1, (stored_player[i].active ? "" : "not "));
1801 printf("::: starting game [%d]\n", FrameCounter);
1805 void InitMovDir(int x, int y)
1807 int i, element = Feld[x][y];
1808 static int xy[4][2] =
1815 static int direction[3][4] =
1817 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1818 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1819 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1828 Feld[x][y] = EL_BUG;
1829 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1832 case EL_SPACESHIP_RIGHT:
1833 case EL_SPACESHIP_UP:
1834 case EL_SPACESHIP_LEFT:
1835 case EL_SPACESHIP_DOWN:
1836 Feld[x][y] = EL_SPACESHIP;
1837 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1840 case EL_BD_BUTTERFLY_RIGHT:
1841 case EL_BD_BUTTERFLY_UP:
1842 case EL_BD_BUTTERFLY_LEFT:
1843 case EL_BD_BUTTERFLY_DOWN:
1844 Feld[x][y] = EL_BD_BUTTERFLY;
1845 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1848 case EL_BD_FIREFLY_RIGHT:
1849 case EL_BD_FIREFLY_UP:
1850 case EL_BD_FIREFLY_LEFT:
1851 case EL_BD_FIREFLY_DOWN:
1852 Feld[x][y] = EL_BD_FIREFLY;
1853 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1856 case EL_PACMAN_RIGHT:
1858 case EL_PACMAN_LEFT:
1859 case EL_PACMAN_DOWN:
1860 Feld[x][y] = EL_PACMAN;
1861 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1864 case EL_SP_SNIKSNAK:
1865 MovDir[x][y] = MV_UP;
1868 case EL_SP_ELECTRON:
1869 MovDir[x][y] = MV_LEFT;
1876 Feld[x][y] = EL_MOLE;
1877 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1881 if (IS_CUSTOM_ELEMENT(element))
1883 struct ElementInfo *ei = &element_info[element];
1884 int move_direction_initial = ei->move_direction_initial;
1885 int move_pattern = ei->move_pattern;
1887 if (move_direction_initial == MV_START_PREVIOUS)
1889 if (MovDir[x][y] != MV_NO_MOVING)
1892 move_direction_initial = MV_START_AUTOMATIC;
1895 if (move_direction_initial == MV_START_RANDOM)
1896 MovDir[x][y] = 1 << RND(4);
1897 else if (move_direction_initial & MV_ANY_DIRECTION)
1898 MovDir[x][y] = move_direction_initial;
1899 else if (move_pattern == MV_ALL_DIRECTIONS ||
1900 move_pattern == MV_TURNING_LEFT ||
1901 move_pattern == MV_TURNING_RIGHT ||
1902 move_pattern == MV_TURNING_LEFT_RIGHT ||
1903 move_pattern == MV_TURNING_RIGHT_LEFT ||
1904 move_pattern == MV_TURNING_RANDOM)
1905 MovDir[x][y] = 1 << RND(4);
1906 else if (move_pattern == MV_HORIZONTAL)
1907 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1908 else if (move_pattern == MV_VERTICAL)
1909 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1910 else if (move_pattern & MV_ANY_DIRECTION)
1911 MovDir[x][y] = element_info[element].move_pattern;
1912 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1913 move_pattern == MV_ALONG_RIGHT_SIDE)
1915 for (i = 0; i < 4; i++)
1917 int x1 = x + xy[i][0];
1918 int y1 = y + xy[i][1];
1920 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1922 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1923 MovDir[x][y] = direction[0][i];
1925 MovDir[x][y] = direction[1][i];
1934 MovDir[x][y] = 1 << RND(4);
1936 if (element != EL_BUG &&
1937 element != EL_SPACESHIP &&
1938 element != EL_BD_BUTTERFLY &&
1939 element != EL_BD_FIREFLY)
1942 for (i = 0; i < 4; i++)
1944 int x1 = x + xy[i][0];
1945 int y1 = y + xy[i][1];
1947 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1949 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1951 MovDir[x][y] = direction[0][i];
1954 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1955 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1957 MovDir[x][y] = direction[1][i];
1966 GfxDir[x][y] = MovDir[x][y];
1969 void InitAmoebaNr(int x, int y)
1972 int group_nr = AmoebeNachbarNr(x, y);
1976 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1978 if (AmoebaCnt[i] == 0)
1986 AmoebaNr[x][y] = group_nr;
1987 AmoebaCnt[group_nr]++;
1988 AmoebaCnt2[group_nr]++;
1994 boolean raise_level = FALSE;
1996 if (local_player->MovPos)
2000 if (tape.auto_play) /* tape might already be stopped here */
2001 tape.auto_play_level_solved = TRUE;
2003 if (tape.playing && tape.auto_play)
2004 tape.auto_play_level_solved = TRUE;
2007 local_player->LevelSolved = FALSE;
2009 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2013 if (!tape.playing && setup.sound_loops)
2014 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2015 SND_CTRL_PLAY_LOOP);
2017 while (TimeLeft > 0)
2019 if (!tape.playing && !setup.sound_loops)
2020 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2021 if (TimeLeft > 0 && !(TimeLeft % 10))
2022 RaiseScore(level.score[SC_TIME_BONUS]);
2023 if (TimeLeft > 100 && !(TimeLeft % 10))
2027 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
2034 if (!tape.playing && setup.sound_loops)
2035 StopSound(SND_GAME_LEVELTIME_BONUS);
2037 else if (level.time == 0) /* level without time limit */
2039 if (!tape.playing && setup.sound_loops)
2040 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2041 SND_CTRL_PLAY_LOOP);
2043 while (TimePlayed < 999)
2045 if (!tape.playing && !setup.sound_loops)
2046 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2047 if (TimePlayed < 999 && !(TimePlayed % 10))
2048 RaiseScore(level.score[SC_TIME_BONUS]);
2049 if (TimePlayed < 900 && !(TimePlayed % 10))
2053 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
2060 if (!tape.playing && setup.sound_loops)
2061 StopSound(SND_GAME_LEVELTIME_BONUS);
2064 /* close exit door after last player */
2065 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2066 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2068 int element = Feld[ExitX][ExitY];
2070 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2071 EL_SP_EXIT_CLOSING);
2073 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2076 /* Hero disappears */
2077 DrawLevelField(ExitX, ExitY);
2083 CloseDoor(DOOR_CLOSE_1);
2088 SaveTape(tape.level_nr); /* Ask to save tape */
2091 if (level_nr == leveldir_current->handicap_level)
2093 leveldir_current->handicap_level++;
2094 SaveLevelSetup_SeriesInfo();
2097 if (level_editor_test_game)
2098 local_player->score = -1; /* no highscore when playing from editor */
2099 else if (level_nr < leveldir_current->last_level)
2100 raise_level = TRUE; /* advance to next level */
2102 if ((hi_pos = NewHiScore()) >= 0)
2104 game_status = GAME_MODE_SCORES;
2105 DrawHallOfFame(hi_pos);
2114 game_status = GAME_MODE_MAIN;
2131 LoadScore(level_nr);
2133 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2134 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2137 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2139 if (local_player->score > highscore[k].Score)
2141 /* player has made it to the hall of fame */
2143 if (k < MAX_SCORE_ENTRIES - 1)
2145 int m = MAX_SCORE_ENTRIES - 1;
2148 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2149 if (!strcmp(setup.player_name, highscore[l].Name))
2151 if (m == k) /* player's new highscore overwrites his old one */
2155 for (l = m; l > k; l--)
2157 strcpy(highscore[l].Name, highscore[l - 1].Name);
2158 highscore[l].Score = highscore[l - 1].Score;
2165 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2166 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2167 highscore[k].Score = local_player->score;
2173 else if (!strncmp(setup.player_name, highscore[k].Name,
2174 MAX_PLAYER_NAME_LEN))
2175 break; /* player already there with a higher score */
2181 SaveScore(level_nr);
2186 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2188 if (player->GfxAction != action || player->GfxDir != dir)
2191 printf("Player frame reset! (%d => %d, %d => %d)\n",
2192 player->GfxAction, action, player->GfxDir, dir);
2195 player->GfxAction = action;
2196 player->GfxDir = dir;
2198 player->StepFrame = 0;
2202 static void ResetRandomAnimationValue(int x, int y)
2204 GfxRandom[x][y] = INIT_GFX_RANDOM();
2207 static void ResetGfxAnimation(int x, int y)
2210 GfxAction[x][y] = ACTION_DEFAULT;
2211 GfxDir[x][y] = MovDir[x][y];
2214 void InitMovingField(int x, int y, int direction)
2216 int element = Feld[x][y];
2217 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2218 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2222 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2223 ResetGfxAnimation(x, y);
2225 MovDir[newx][newy] = MovDir[x][y] = direction;
2226 GfxDir[x][y] = direction;
2228 if (Feld[newx][newy] == EL_EMPTY)
2229 Feld[newx][newy] = EL_BLOCKED;
2231 if (direction == MV_DOWN && CAN_FALL(element))
2232 GfxAction[x][y] = ACTION_FALLING;
2234 GfxAction[x][y] = ACTION_MOVING;
2236 GfxFrame[newx][newy] = GfxFrame[x][y];
2237 GfxRandom[newx][newy] = GfxRandom[x][y];
2238 GfxAction[newx][newy] = GfxAction[x][y];
2239 GfxDir[newx][newy] = GfxDir[x][y];
2242 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2244 int direction = MovDir[x][y];
2245 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2246 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2252 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2254 int oldx = x, oldy = y;
2255 int direction = MovDir[x][y];
2257 if (direction == MV_LEFT)
2259 else if (direction == MV_RIGHT)
2261 else if (direction == MV_UP)
2263 else if (direction == MV_DOWN)
2266 *comes_from_x = oldx;
2267 *comes_from_y = oldy;
2270 int MovingOrBlocked2Element(int x, int y)
2272 int element = Feld[x][y];
2274 if (element == EL_BLOCKED)
2278 Blocked2Moving(x, y, &oldx, &oldy);
2279 return Feld[oldx][oldy];
2285 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2287 /* like MovingOrBlocked2Element(), but if element is moving
2288 and (x,y) is the field the moving element is just leaving,
2289 return EL_BLOCKED instead of the element value */
2290 int element = Feld[x][y];
2292 if (IS_MOVING(x, y))
2294 if (element == EL_BLOCKED)
2298 Blocked2Moving(x, y, &oldx, &oldy);
2299 return Feld[oldx][oldy];
2308 static void RemoveField(int x, int y)
2310 Feld[x][y] = EL_EMPTY;
2317 ChangeDelay[x][y] = 0;
2318 ChangePage[x][y] = -1;
2319 Pushed[x][y] = FALSE;
2321 GfxElement[x][y] = EL_UNDEFINED;
2322 GfxAction[x][y] = ACTION_DEFAULT;
2323 GfxDir[x][y] = MV_NO_MOVING;
2326 void RemoveMovingField(int x, int y)
2328 int oldx = x, oldy = y, newx = x, newy = y;
2329 int element = Feld[x][y];
2330 int next_element = EL_UNDEFINED;
2332 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2335 if (IS_MOVING(x, y))
2337 Moving2Blocked(x, y, &newx, &newy);
2339 if (Feld[newx][newy] != EL_BLOCKED)
2342 if (Feld[newx][newy] != EL_BLOCKED)
2344 /* element is moving, but target field is not free (blocked), but
2345 already occupied by something different (example: acid pool);
2346 in this case, only remove the moving field, but not the target */
2348 RemoveField(oldx, oldy);
2350 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2352 DrawLevelField(oldx, oldy);
2358 else if (element == EL_BLOCKED)
2360 Blocked2Moving(x, y, &oldx, &oldy);
2361 if (!IS_MOVING(oldx, oldy))
2365 if (element == EL_BLOCKED &&
2366 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2367 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2368 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2369 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2370 next_element = get_next_element(Feld[oldx][oldy]);
2372 RemoveField(oldx, oldy);
2373 RemoveField(newx, newy);
2375 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2377 if (next_element != EL_UNDEFINED)
2378 Feld[oldx][oldy] = next_element;
2380 DrawLevelField(oldx, oldy);
2381 DrawLevelField(newx, newy);
2384 void DrawDynamite(int x, int y)
2386 int sx = SCREENX(x), sy = SCREENY(y);
2387 int graphic = el2img(Feld[x][y]);
2390 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2393 if (IS_WALKABLE_INSIDE(Back[x][y]))
2397 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2398 else if (Store[x][y])
2399 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2401 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2404 if (Back[x][y] || Store[x][y])
2405 DrawGraphicThruMask(sx, sy, graphic, frame);
2407 DrawGraphic(sx, sy, graphic, frame);
2409 if (game.emulation == EMU_SUPAPLEX)
2410 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2411 else if (Store[x][y])
2412 DrawGraphicThruMask(sx, sy, graphic, frame);
2414 DrawGraphic(sx, sy, graphic, frame);
2418 void CheckDynamite(int x, int y)
2420 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2424 if (MovDelay[x][y] != 0)
2427 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2434 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2436 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2437 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2438 StopSound(SND_DYNAMITE_ACTIVE);
2440 StopSound(SND_DYNABOMB_ACTIVE);
2446 void RelocatePlayer(int x, int y, int element_raw)
2448 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2449 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2450 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2451 boolean no_delay = (tape.index_search);
2452 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2453 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2455 if (player->GameOver) /* do not reanimate dead player */
2459 RemoveField(x, y); /* temporarily remove newly placed player */
2460 DrawLevelField(x, y);
2463 if (player->present)
2465 while (player->MovPos)
2467 ScrollPlayer(player, SCROLL_GO_ON);
2468 ScrollScreen(NULL, SCROLL_GO_ON);
2474 Delay(wait_delay_value);
2477 DrawPlayer(player); /* needed here only to cleanup last field */
2478 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2480 player->is_moving = FALSE;
2483 Feld[x][y] = element;
2484 InitPlayerField(x, y, element, TRUE);
2486 if (player == local_player)
2488 int scroll_xx = -999, scroll_yy = -999;
2490 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2493 int fx = FX, fy = FY;
2495 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2496 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2497 local_player->jx - MIDPOSX);
2499 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2500 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2501 local_player->jy - MIDPOSY);
2503 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2504 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2509 fx += dx * TILEX / 2;
2510 fy += dy * TILEY / 2;
2512 ScrollLevel(dx, dy);
2515 /* scroll in two steps of half tile size to make things smoother */
2516 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2518 Delay(wait_delay_value);
2520 /* scroll second step to align at full tile size */
2522 Delay(wait_delay_value);
2527 void Explode(int ex, int ey, int phase, int mode)
2534 /* !!! eliminate this variable !!! */
2535 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2540 int last_phase = num_phase * delay;
2541 int half_phase = (num_phase / 2) * delay;
2542 int first_phase_after_start = EX_PHASE_START + 1;
2546 if (game.explosions_delayed)
2548 ExplodeField[ex][ey] = mode;
2552 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2554 int center_element = Feld[ex][ey];
2557 /* --- This is only really needed (and now handled) in "Impact()". --- */
2558 /* do not explode moving elements that left the explode field in time */
2559 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2560 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2564 if (mode == EX_NORMAL || mode == EX_CENTER)
2565 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2567 /* remove things displayed in background while burning dynamite */
2568 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2571 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2573 /* put moving element to center field (and let it explode there) */
2574 center_element = MovingOrBlocked2Element(ex, ey);
2575 RemoveMovingField(ex, ey);
2576 Feld[ex][ey] = center_element;
2580 last_phase = element_info[center_element].explosion_delay;
2583 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2585 int xx = x - ex + 1;
2586 int yy = y - ey + 1;
2589 if (!IN_LEV_FIELD(x, y) ||
2590 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2591 (x != ex || y != ey)))
2594 element = Feld[x][y];
2596 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2598 element = MovingOrBlocked2Element(x, y);
2600 if (!IS_EXPLOSION_PROOF(element))
2601 RemoveMovingField(x, y);
2607 if (IS_EXPLOSION_PROOF(element))
2610 /* indestructible elements can only explode in center (but not flames) */
2611 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2612 element == EL_FLAMES)
2617 if ((IS_INDESTRUCTIBLE(element) &&
2618 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2619 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2620 element == EL_FLAMES)
2624 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2626 if (IS_ACTIVE_BOMB(element))
2628 /* re-activate things under the bomb like gate or penguin */
2629 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2636 /* save walkable background elements while explosion on same tile */
2638 if (IS_INDESTRUCTIBLE(element))
2639 Back[x][y] = element;
2641 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2642 Back[x][y] = element;
2645 /* ignite explodable elements reached by other explosion */
2646 if (element == EL_EXPLOSION)
2647 element = Store2[x][y];
2650 if (AmoebaNr[x][y] &&
2651 (element == EL_AMOEBA_FULL ||
2652 element == EL_BD_AMOEBA ||
2653 element == EL_AMOEBA_GROWING))
2655 AmoebaCnt[AmoebaNr[x][y]]--;
2656 AmoebaCnt2[AmoebaNr[x][y]]--;
2662 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2664 switch(StorePlayer[ex][ey])
2667 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2670 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2673 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2677 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2681 if (game.emulation == EMU_SUPAPLEX)
2682 Store[x][y] = EL_EMPTY;
2684 else if (center_element == EL_MOLE)
2685 Store[x][y] = EL_EMERALD_RED;
2686 else if (center_element == EL_PENGUIN)
2687 Store[x][y] = EL_EMERALD_PURPLE;
2688 else if (center_element == EL_BUG)
2689 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2690 else if (center_element == EL_BD_BUTTERFLY)
2691 Store[x][y] = EL_BD_DIAMOND;
2692 else if (center_element == EL_SP_ELECTRON)
2693 Store[x][y] = EL_SP_INFOTRON;
2694 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2695 Store[x][y] = level.amoeba_content;
2696 else if (center_element == EL_YAMYAM)
2697 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2698 else if (IS_CUSTOM_ELEMENT(center_element) &&
2699 element_info[center_element].content[xx][yy] != EL_EMPTY)
2700 Store[x][y] = element_info[center_element].content[xx][yy];
2701 else if (element == EL_WALL_EMERALD)
2702 Store[x][y] = EL_EMERALD;
2703 else if (element == EL_WALL_DIAMOND)
2704 Store[x][y] = EL_DIAMOND;
2705 else if (element == EL_WALL_BD_DIAMOND)
2706 Store[x][y] = EL_BD_DIAMOND;
2707 else if (element == EL_WALL_EMERALD_YELLOW)
2708 Store[x][y] = EL_EMERALD_YELLOW;
2709 else if (element == EL_WALL_EMERALD_RED)
2710 Store[x][y] = EL_EMERALD_RED;
2711 else if (element == EL_WALL_EMERALD_PURPLE)
2712 Store[x][y] = EL_EMERALD_PURPLE;
2713 else if (element == EL_WALL_PEARL)
2714 Store[x][y] = EL_PEARL;
2715 else if (element == EL_WALL_CRYSTAL)
2716 Store[x][y] = EL_CRYSTAL;
2717 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2718 Store[x][y] = element_info[element].content[1][1];
2720 Store[x][y] = EL_EMPTY;
2722 if (x != ex || y != ey ||
2723 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2724 Store2[x][y] = element;
2727 if (AmoebaNr[x][y] &&
2728 (element == EL_AMOEBA_FULL ||
2729 element == EL_BD_AMOEBA ||
2730 element == EL_AMOEBA_GROWING))
2732 AmoebaCnt[AmoebaNr[x][y]]--;
2733 AmoebaCnt2[AmoebaNr[x][y]]--;
2739 MovDir[x][y] = MovPos[x][y] = 0;
2740 GfxDir[x][y] = MovDir[x][y];
2745 Feld[x][y] = EL_EXPLOSION;
2747 GfxElement[x][y] = center_element;
2749 GfxElement[x][y] = EL_UNDEFINED;
2752 ExplodePhase[x][y] = 1;
2754 ExplodeDelay[x][y] = last_phase;
2759 if (center_element == EL_YAMYAM)
2760 game.yamyam_content_nr =
2761 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2773 last_phase = ExplodeDelay[x][y];
2776 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2780 /* activate this even in non-DEBUG version until cause for crash in
2781 getGraphicAnimationFrame() (see below) is found and eliminated */
2785 if (GfxElement[x][y] == EL_UNDEFINED)
2788 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2789 printf("Explode(): This should never happen!\n");
2792 GfxElement[x][y] = EL_EMPTY;
2798 border_element = Store2[x][y];
2799 if (IS_PLAYER(x, y))
2800 border_element = StorePlayer[x][y];
2802 if (phase == element_info[border_element].ignition_delay ||
2803 phase == last_phase)
2805 boolean border_explosion = FALSE;
2808 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2810 if (IS_PLAYER(x, y))
2813 KillHeroUnlessExplosionProtected(x, y);
2814 border_explosion = TRUE;
2817 if (phase == last_phase)
2818 printf("::: IS_PLAYER\n");
2821 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2823 Feld[x][y] = Store2[x][y];
2826 border_explosion = TRUE;
2829 if (phase == last_phase)
2830 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2833 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2835 AmoebeUmwandeln(x, y);
2837 border_explosion = TRUE;
2840 if (phase == last_phase)
2841 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2842 element_info[border_element].explosion_delay,
2843 element_info[border_element].ignition_delay,
2849 /* if an element just explodes due to another explosion (chain-reaction),
2850 do not immediately end the new explosion when it was the last frame of
2851 the explosion (as it would be done in the following "if"-statement!) */
2852 if (border_explosion && phase == last_phase)
2859 if (phase == first_phase_after_start)
2861 int element = Store2[x][y];
2863 if (element == EL_BLACK_ORB)
2865 Feld[x][y] = Store2[x][y];
2870 else if (phase == half_phase)
2872 int element = Store2[x][y];
2874 if (IS_PLAYER(x, y))
2875 KillHeroUnlessExplosionProtected(x, y);
2876 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2878 Feld[x][y] = Store2[x][y];
2882 else if (element == EL_AMOEBA_TO_DIAMOND)
2883 AmoebeUmwandeln(x, y);
2887 if (phase == last_phase)
2891 element = Feld[x][y] = Store[x][y];
2892 Store[x][y] = Store2[x][y] = 0;
2893 GfxElement[x][y] = EL_UNDEFINED;
2895 /* player can escape from explosions and might therefore be still alive */
2896 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2897 element <= EL_PLAYER_IS_EXPLODING_4)
2898 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2900 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2901 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2902 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2905 /* restore probably existing indestructible background element */
2906 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2907 element = Feld[x][y] = Back[x][y];
2910 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2911 GfxDir[x][y] = MV_NO_MOVING;
2912 ChangeDelay[x][y] = 0;
2913 ChangePage[x][y] = -1;
2916 InitField_WithBug2(x, y, FALSE);
2918 InitField(x, y, FALSE);
2920 /* !!! not needed !!! */
2922 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
2923 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
2926 if (CAN_MOVE(element))
2931 DrawLevelField(x, y);
2933 TestIfElementTouchesCustomElement(x, y);
2935 if (GFX_CRUMBLED(element))
2936 DrawLevelFieldCrumbledSandNeighbours(x, y);
2938 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
2939 StorePlayer[x][y] = 0;
2941 if (ELEM_IS_PLAYER(element))
2942 RelocatePlayer(x, y, element);
2945 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2947 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2951 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2953 int stored = Store[x][y];
2954 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2955 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2958 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2961 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2962 element_info[GfxElement[x][y]].token_name,
2967 DrawLevelFieldCrumbledSand(x, y);
2969 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2971 DrawLevelElement(x, y, Back[x][y]);
2972 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2974 else if (IS_WALKABLE_UNDER(Back[x][y]))
2976 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2977 DrawLevelElementThruMask(x, y, Back[x][y]);
2979 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2980 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2984 void DynaExplode(int ex, int ey)
2987 int dynabomb_element = Feld[ex][ey];
2988 int dynabomb_size = 1;
2989 boolean dynabomb_xl = FALSE;
2990 struct PlayerInfo *player;
2991 static int xy[4][2] =
2999 if (IS_ACTIVE_BOMB(dynabomb_element))
3001 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3002 dynabomb_size = player->dynabomb_size;
3003 dynabomb_xl = player->dynabomb_xl;
3004 player->dynabombs_left++;
3007 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3009 for (i = 0; i < 4; i++)
3011 for (j = 1; j <= dynabomb_size; j++)
3013 int x = ex + j * xy[i % 4][0];
3014 int y = ey + j * xy[i % 4][1];
3017 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3020 element = Feld[x][y];
3022 /* do not restart explosions of fields with active bombs */
3023 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3026 Explode(x, y, EX_PHASE_START, EX_BORDER);
3028 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3029 if (element != EL_EMPTY &&
3030 element != EL_SAND &&
3031 element != EL_EXPLOSION &&
3038 void Bang(int x, int y)
3041 int element = MovingOrBlocked2Element(x, y);
3043 int element = Feld[x][y];
3047 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3049 if (IS_PLAYER(x, y))
3052 struct PlayerInfo *player = PLAYERINFO(x, y);
3054 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3055 player->element_nr);
3060 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3062 if (game.emulation == EMU_SUPAPLEX)
3063 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3065 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3070 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3078 case EL_BD_BUTTERFLY:
3081 case EL_DARK_YAMYAM:
3085 RaiseScoreElement(element);
3086 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3088 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3089 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3090 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3091 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3092 case EL_DYNABOMB_INCREASE_NUMBER:
3093 case EL_DYNABOMB_INCREASE_SIZE:
3094 case EL_DYNABOMB_INCREASE_POWER:
3099 case EL_LAMP_ACTIVE:
3100 if (IS_PLAYER(x, y))
3101 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3103 Explode(x, y, EX_PHASE_START, EX_CENTER);
3106 if (CAN_EXPLODE_DYNA(element))
3108 else if (CAN_EXPLODE_1X1(element))
3109 Explode(x, y, EX_PHASE_START, EX_CENTER);
3111 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3115 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3118 void SplashAcid(int x, int y)
3121 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3122 (!IN_LEV_FIELD(x - 1, y - 2) ||
3123 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3124 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3126 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3127 (!IN_LEV_FIELD(x + 1, y - 2) ||
3128 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3129 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3131 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3133 /* input: position of element entering acid (obsolete) */
3135 int element = Feld[x][y];
3137 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3140 if (element != EL_ACID_SPLASH_LEFT &&
3141 element != EL_ACID_SPLASH_RIGHT)
3143 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3145 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3146 (!IN_LEV_FIELD(x - 1, y - 1) ||
3147 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3148 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3150 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3151 (!IN_LEV_FIELD(x + 1, y - 1) ||
3152 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3153 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3158 static void InitBeltMovement()
3160 static int belt_base_element[4] =
3162 EL_CONVEYOR_BELT_1_LEFT,
3163 EL_CONVEYOR_BELT_2_LEFT,
3164 EL_CONVEYOR_BELT_3_LEFT,
3165 EL_CONVEYOR_BELT_4_LEFT
3167 static int belt_base_active_element[4] =
3169 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3170 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3171 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3172 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3177 /* set frame order for belt animation graphic according to belt direction */
3178 for (i = 0; i < 4; i++)
3182 for (j = 0; j < 3; j++)
3184 int element = belt_base_active_element[belt_nr] + j;
3185 int graphic = el2img(element);
3187 if (game.belt_dir[i] == MV_LEFT)
3188 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3190 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3194 for (y = 0; y < lev_fieldy; y++)
3196 for (x = 0; x < lev_fieldx; x++)
3198 int element = Feld[x][y];
3200 for (i = 0; i < 4; i++)
3202 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3204 int e_belt_nr = getBeltNrFromBeltElement(element);
3207 if (e_belt_nr == belt_nr)
3209 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3211 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3219 static void ToggleBeltSwitch(int x, int y)
3221 static int belt_base_element[4] =
3223 EL_CONVEYOR_BELT_1_LEFT,
3224 EL_CONVEYOR_BELT_2_LEFT,
3225 EL_CONVEYOR_BELT_3_LEFT,
3226 EL_CONVEYOR_BELT_4_LEFT
3228 static int belt_base_active_element[4] =
3230 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3231 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3232 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3233 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3235 static int belt_base_switch_element[4] =
3237 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3238 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3239 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3240 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3242 static int belt_move_dir[4] =
3250 int element = Feld[x][y];
3251 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3252 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3253 int belt_dir = belt_move_dir[belt_dir_nr];
3256 if (!IS_BELT_SWITCH(element))
3259 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3260 game.belt_dir[belt_nr] = belt_dir;
3262 if (belt_dir_nr == 3)
3265 /* set frame order for belt animation graphic according to belt direction */
3266 for (i = 0; i < 3; i++)
3268 int element = belt_base_active_element[belt_nr] + i;
3269 int graphic = el2img(element);
3271 if (belt_dir == MV_LEFT)
3272 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3274 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3277 for (yy = 0; yy < lev_fieldy; yy++)
3279 for (xx = 0; xx < lev_fieldx; xx++)
3281 int element = Feld[xx][yy];
3283 if (IS_BELT_SWITCH(element))
3285 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3287 if (e_belt_nr == belt_nr)
3289 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3290 DrawLevelField(xx, yy);
3293 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3295 int e_belt_nr = getBeltNrFromBeltElement(element);
3297 if (e_belt_nr == belt_nr)
3299 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3301 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3302 DrawLevelField(xx, yy);
3305 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3307 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3309 if (e_belt_nr == belt_nr)
3311 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3313 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3314 DrawLevelField(xx, yy);
3321 static void ToggleSwitchgateSwitch(int x, int y)
3325 game.switchgate_pos = !game.switchgate_pos;
3327 for (yy = 0; yy < lev_fieldy; yy++)
3329 for (xx = 0; xx < lev_fieldx; xx++)
3331 int element = Feld[xx][yy];
3333 if (element == EL_SWITCHGATE_SWITCH_UP ||
3334 element == EL_SWITCHGATE_SWITCH_DOWN)
3336 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3337 DrawLevelField(xx, yy);
3339 else if (element == EL_SWITCHGATE_OPEN ||
3340 element == EL_SWITCHGATE_OPENING)
3342 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3344 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3346 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3349 else if (element == EL_SWITCHGATE_CLOSED ||
3350 element == EL_SWITCHGATE_CLOSING)
3352 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3354 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3356 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3363 static int getInvisibleActiveFromInvisibleElement(int element)
3365 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3366 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3367 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3371 static int getInvisibleFromInvisibleActiveElement(int element)
3373 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3374 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3375 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3379 static void RedrawAllLightSwitchesAndInvisibleElements()
3383 for (y = 0; y < lev_fieldy; y++)
3385 for (x = 0; x < lev_fieldx; x++)
3387 int element = Feld[x][y];
3389 if (element == EL_LIGHT_SWITCH &&
3390 game.light_time_left > 0)
3392 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3393 DrawLevelField(x, y);
3395 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3396 game.light_time_left == 0)
3398 Feld[x][y] = EL_LIGHT_SWITCH;
3399 DrawLevelField(x, y);
3401 else if (element == EL_INVISIBLE_STEELWALL ||
3402 element == EL_INVISIBLE_WALL ||
3403 element == EL_INVISIBLE_SAND)
3405 if (game.light_time_left > 0)
3406 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3408 DrawLevelField(x, y);
3410 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3411 element == EL_INVISIBLE_WALL_ACTIVE ||
3412 element == EL_INVISIBLE_SAND_ACTIVE)
3414 if (game.light_time_left == 0)
3415 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3417 DrawLevelField(x, y);
3423 static void ToggleLightSwitch(int x, int y)
3425 int element = Feld[x][y];
3427 game.light_time_left =
3428 (element == EL_LIGHT_SWITCH ?
3429 level.time_light * FRAMES_PER_SECOND : 0);
3431 RedrawAllLightSwitchesAndInvisibleElements();
3434 static void ActivateTimegateSwitch(int x, int y)
3438 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3440 for (yy = 0; yy < lev_fieldy; yy++)
3442 for (xx = 0; xx < lev_fieldx; xx++)
3444 int element = Feld[xx][yy];
3446 if (element == EL_TIMEGATE_CLOSED ||
3447 element == EL_TIMEGATE_CLOSING)
3449 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3450 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3454 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3456 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3457 DrawLevelField(xx, yy);
3464 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3467 inline static int getElementMoveStepsize(int x, int y)
3469 int element = Feld[x][y];
3470 int direction = MovDir[x][y];
3471 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3472 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3473 int horiz_move = (dx != 0);
3474 int sign = (horiz_move ? dx : dy);
3475 int step = sign * element_info[element].move_stepsize;
3477 /* special values for move stepsize for spring and things on conveyor belt */
3481 if (element == EL_SPRING)
3482 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3483 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3484 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3485 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3487 if (CAN_FALL(element) &&
3488 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3489 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3490 else if (element == EL_SPRING)
3491 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3498 void Impact(int x, int y)
3500 boolean lastline = (y == lev_fieldy-1);
3501 boolean object_hit = FALSE;
3502 boolean impact = (lastline || object_hit);
3503 int element = Feld[x][y];
3504 int smashed = EL_UNDEFINED;
3506 if (!lastline) /* check if element below was hit */
3508 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3511 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3512 MovDir[x][y + 1] != MV_DOWN ||
3513 MovPos[x][y + 1] <= TILEY / 2));
3516 object_hit = !IS_FREE(x, y + 1);
3519 /* do not smash moving elements that left the smashed field in time */
3520 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3521 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3525 smashed = MovingOrBlocked2Element(x, y + 1);
3527 impact = (lastline || object_hit);
3530 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3532 SplashAcid(x, y + 1);
3536 /* only reset graphic animation if graphic really changes after impact */
3538 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3540 ResetGfxAnimation(x, y);
3541 DrawLevelField(x, y);
3544 if (impact && CAN_EXPLODE_IMPACT(element))
3549 else if (impact && element == EL_PEARL)
3551 Feld[x][y] = EL_PEARL_BREAKING;
3552 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3555 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3557 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3562 if (impact && element == EL_AMOEBA_DROP)
3564 if (object_hit && IS_PLAYER(x, y + 1))
3565 KillHeroUnlessEnemyProtected(x, y + 1);
3566 else if (object_hit && smashed == EL_PENGUIN)
3570 Feld[x][y] = EL_AMOEBA_GROWING;
3571 Store[x][y] = EL_AMOEBA_WET;
3573 ResetRandomAnimationValue(x, y);
3578 if (object_hit) /* check which object was hit */
3580 if (CAN_PASS_MAGIC_WALL(element) &&
3581 (smashed == EL_MAGIC_WALL ||
3582 smashed == EL_BD_MAGIC_WALL))
3585 int activated_magic_wall =
3586 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3587 EL_BD_MAGIC_WALL_ACTIVE);
3589 /* activate magic wall / mill */
3590 for (yy = 0; yy < lev_fieldy; yy++)
3591 for (xx = 0; xx < lev_fieldx; xx++)
3592 if (Feld[xx][yy] == smashed)
3593 Feld[xx][yy] = activated_magic_wall;
3595 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3596 game.magic_wall_active = TRUE;
3598 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3599 SND_MAGIC_WALL_ACTIVATING :
3600 SND_BD_MAGIC_WALL_ACTIVATING));
3603 if (IS_PLAYER(x, y + 1))
3605 if (CAN_SMASH_PLAYER(element))
3607 KillHeroUnlessEnemyProtected(x, y + 1);
3611 else if (smashed == EL_PENGUIN)
3613 if (CAN_SMASH_PLAYER(element))
3619 else if (element == EL_BD_DIAMOND)
3621 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3627 else if (((element == EL_SP_INFOTRON ||
3628 element == EL_SP_ZONK) &&
3629 (smashed == EL_SP_SNIKSNAK ||
3630 smashed == EL_SP_ELECTRON ||
3631 smashed == EL_SP_DISK_ORANGE)) ||
3632 (element == EL_SP_INFOTRON &&
3633 smashed == EL_SP_DISK_YELLOW))
3639 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3645 else if (CAN_SMASH_EVERYTHING(element))
3647 if (IS_CLASSIC_ENEMY(smashed) ||
3648 CAN_EXPLODE_SMASHED(smashed))
3653 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3655 if (smashed == EL_LAMP ||
3656 smashed == EL_LAMP_ACTIVE)
3661 else if (smashed == EL_NUT)
3663 Feld[x][y + 1] = EL_NUT_BREAKING;
3664 PlayLevelSound(x, y, SND_NUT_BREAKING);
3665 RaiseScoreElement(EL_NUT);
3668 else if (smashed == EL_PEARL)
3670 Feld[x][y + 1] = EL_PEARL_BREAKING;
3671 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3674 else if (smashed == EL_DIAMOND)
3676 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3677 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3680 else if (IS_BELT_SWITCH(smashed))
3682 ToggleBeltSwitch(x, y + 1);
3684 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3685 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3687 ToggleSwitchgateSwitch(x, y + 1);
3689 else if (smashed == EL_LIGHT_SWITCH ||
3690 smashed == EL_LIGHT_SWITCH_ACTIVE)
3692 ToggleLightSwitch(x, y + 1);
3696 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3698 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3699 CE_OTHER_IS_SWITCHING);
3700 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3706 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3711 /* play sound of magic wall / mill */
3713 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3714 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3716 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3717 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3718 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3719 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3724 /* play sound of object that hits the ground */
3725 if (lastline || object_hit)
3726 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3729 inline static void TurnRoundExt(int x, int y)
3741 { 0, 0 }, { 0, 0 }, { 0, 0 },
3746 int left, right, back;
3750 { MV_DOWN, MV_UP, MV_RIGHT },
3751 { MV_UP, MV_DOWN, MV_LEFT },
3753 { MV_LEFT, MV_RIGHT, MV_DOWN },
3757 { MV_RIGHT, MV_LEFT, MV_UP }
3760 int element = Feld[x][y];
3761 int move_pattern = element_info[element].move_pattern;
3763 int old_move_dir = MovDir[x][y];
3764 int left_dir = turn[old_move_dir].left;
3765 int right_dir = turn[old_move_dir].right;
3766 int back_dir = turn[old_move_dir].back;
3768 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3769 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3770 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3771 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3773 int left_x = x + left_dx, left_y = y + left_dy;
3774 int right_x = x + right_dx, right_y = y + right_dy;
3775 int move_x = x + move_dx, move_y = y + move_dy;
3779 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3781 TestIfBadThingTouchesOtherBadThing(x, y);
3783 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3784 MovDir[x][y] = right_dir;
3785 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3786 MovDir[x][y] = left_dir;
3788 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3790 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3793 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3794 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3796 TestIfBadThingTouchesOtherBadThing(x, y);
3798 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3799 MovDir[x][y] = left_dir;
3800 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3801 MovDir[x][y] = right_dir;
3803 if ((element == EL_SPACESHIP ||
3804 element == EL_SP_SNIKSNAK ||
3805 element == EL_SP_ELECTRON)
3806 && MovDir[x][y] != old_move_dir)
3808 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3811 else if (element == EL_YAMYAM)
3813 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3814 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3816 if (can_turn_left && can_turn_right)
3817 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3818 else if (can_turn_left)
3819 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3820 else if (can_turn_right)
3821 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3823 MovDir[x][y] = back_dir;
3825 MovDelay[x][y] = 16 + 16 * RND(3);
3827 else if (element == EL_DARK_YAMYAM)
3829 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3830 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3832 if (can_turn_left && can_turn_right)
3833 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3834 else if (can_turn_left)
3835 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3836 else if (can_turn_right)
3837 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3839 MovDir[x][y] = back_dir;
3841 MovDelay[x][y] = 16 + 16 * RND(3);
3843 else if (element == EL_PACMAN)
3845 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3846 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3848 if (can_turn_left && can_turn_right)
3849 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3850 else if (can_turn_left)
3851 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3852 else if (can_turn_right)
3853 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3855 MovDir[x][y] = back_dir;
3857 MovDelay[x][y] = 6 + RND(40);
3859 else if (element == EL_PIG)
3861 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3862 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3863 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3864 boolean should_turn_left, should_turn_right, should_move_on;
3866 int rnd = RND(rnd_value);
3868 should_turn_left = (can_turn_left &&
3870 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3871 y + back_dy + left_dy)));
3872 should_turn_right = (can_turn_right &&
3874 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3875 y + back_dy + right_dy)));
3876 should_move_on = (can_move_on &&
3879 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3880 y + move_dy + left_dy) ||
3881 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3882 y + move_dy + right_dy)));
3884 if (should_turn_left || should_turn_right || should_move_on)
3886 if (should_turn_left && should_turn_right && should_move_on)
3887 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3888 rnd < 2 * rnd_value / 3 ? right_dir :
3890 else if (should_turn_left && should_turn_right)
3891 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3892 else if (should_turn_left && should_move_on)
3893 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3894 else if (should_turn_right && should_move_on)
3895 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3896 else if (should_turn_left)
3897 MovDir[x][y] = left_dir;
3898 else if (should_turn_right)
3899 MovDir[x][y] = right_dir;
3900 else if (should_move_on)
3901 MovDir[x][y] = old_move_dir;
3903 else if (can_move_on && rnd > rnd_value / 8)
3904 MovDir[x][y] = old_move_dir;
3905 else if (can_turn_left && can_turn_right)
3906 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3907 else if (can_turn_left && rnd > rnd_value / 8)
3908 MovDir[x][y] = left_dir;
3909 else if (can_turn_right && rnd > rnd_value/8)
3910 MovDir[x][y] = right_dir;
3912 MovDir[x][y] = back_dir;
3914 xx = x + move_xy[MovDir[x][y]].x;
3915 yy = y + move_xy[MovDir[x][y]].y;
3917 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3918 MovDir[x][y] = old_move_dir;
3922 else if (element == EL_DRAGON)
3924 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3925 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3926 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3928 int rnd = RND(rnd_value);
3931 if (FrameCounter < 1 && x == 0 && y == 29)
3932 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3935 if (can_move_on && rnd > rnd_value / 8)
3936 MovDir[x][y] = old_move_dir;
3937 else if (can_turn_left && can_turn_right)
3938 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3939 else if (can_turn_left && rnd > rnd_value / 8)
3940 MovDir[x][y] = left_dir;
3941 else if (can_turn_right && rnd > rnd_value / 8)
3942 MovDir[x][y] = right_dir;
3944 MovDir[x][y] = back_dir;
3946 xx = x + move_xy[MovDir[x][y]].x;
3947 yy = y + move_xy[MovDir[x][y]].y;
3950 if (FrameCounter < 1 && x == 0 && y == 29)
3951 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3952 xx, yy, Feld[xx][yy],
3957 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3958 MovDir[x][y] = old_move_dir;
3960 if (!IS_FREE(xx, yy))
3961 MovDir[x][y] = old_move_dir;
3965 if (FrameCounter < 1 && x == 0 && y == 29)
3966 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3971 else if (element == EL_MOLE)
3973 boolean can_move_on =
3974 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3975 IS_AMOEBOID(Feld[move_x][move_y]) ||
3976 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3979 boolean can_turn_left =
3980 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3981 IS_AMOEBOID(Feld[left_x][left_y])));
3983 boolean can_turn_right =
3984 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3985 IS_AMOEBOID(Feld[right_x][right_y])));
3987 if (can_turn_left && can_turn_right)
3988 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3989 else if (can_turn_left)
3990 MovDir[x][y] = left_dir;
3992 MovDir[x][y] = right_dir;
3995 if (MovDir[x][y] != old_move_dir)
3998 else if (element == EL_BALLOON)
4000 MovDir[x][y] = game.balloon_dir;
4003 else if (element == EL_SPRING)
4005 if (MovDir[x][y] & MV_HORIZONTAL &&
4006 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
4007 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
4008 MovDir[x][y] = MV_NO_MOVING;
4012 else if (element == EL_ROBOT ||
4013 element == EL_SATELLITE ||
4014 element == EL_PENGUIN)
4016 int attr_x = -1, attr_y = -1;
4027 for (i = 0; i < MAX_PLAYERS; i++)
4029 struct PlayerInfo *player = &stored_player[i];
4030 int jx = player->jx, jy = player->jy;
4032 if (!player->active)
4036 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4044 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4050 if (element == EL_PENGUIN)
4053 static int xy[4][2] =
4061 for (i = 0; i < 4; i++)
4063 int ex = x + xy[i % 4][0];
4064 int ey = y + xy[i % 4][1];
4066 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4075 MovDir[x][y] = MV_NO_MOVING;
4077 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4078 else if (attr_x > x)
4079 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4081 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4082 else if (attr_y > y)
4083 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4085 if (element == EL_ROBOT)
4089 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4090 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4091 Moving2Blocked(x, y, &newx, &newy);
4093 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4094 MovDelay[x][y] = 8 + 8 * !RND(3);
4096 MovDelay[x][y] = 16;
4098 else if (element == EL_PENGUIN)
4104 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4106 boolean first_horiz = RND(2);
4107 int new_move_dir = MovDir[x][y];
4110 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4111 Moving2Blocked(x, y, &newx, &newy);
4113 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4117 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4118 Moving2Blocked(x, y, &newx, &newy);
4120 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4123 MovDir[x][y] = old_move_dir;
4127 else /* (element == EL_SATELLITE) */
4133 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4135 boolean first_horiz = RND(2);
4136 int new_move_dir = MovDir[x][y];
4139 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4140 Moving2Blocked(x, y, &newx, &newy);
4142 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4146 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4147 Moving2Blocked(x, y, &newx, &newy);
4149 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4152 MovDir[x][y] = old_move_dir;
4157 else if (move_pattern == MV_TURNING_LEFT ||
4158 move_pattern == MV_TURNING_RIGHT ||
4159 move_pattern == MV_TURNING_LEFT_RIGHT ||
4160 move_pattern == MV_TURNING_RIGHT_LEFT ||
4161 move_pattern == MV_TURNING_RANDOM ||
4162 move_pattern == MV_ALL_DIRECTIONS)
4164 boolean can_turn_left =
4165 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4166 boolean can_turn_right =
4167 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4169 if (move_pattern == MV_TURNING_LEFT)
4170 MovDir[x][y] = left_dir;
4171 else if (move_pattern == MV_TURNING_RIGHT)
4172 MovDir[x][y] = right_dir;
4173 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4174 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4175 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4176 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4177 else if (move_pattern == MV_TURNING_RANDOM)
4178 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4179 can_turn_right && !can_turn_left ? right_dir :
4180 RND(2) ? left_dir : right_dir);
4181 else if (can_turn_left && can_turn_right)
4182 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4183 else if (can_turn_left)
4184 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4185 else if (can_turn_right)
4186 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4188 MovDir[x][y] = back_dir;
4190 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4192 else if (move_pattern == MV_HORIZONTAL ||
4193 move_pattern == MV_VERTICAL)
4195 if (move_pattern & old_move_dir)
4196 MovDir[x][y] = back_dir;
4197 else if (move_pattern == MV_HORIZONTAL)
4198 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4199 else if (move_pattern == MV_VERTICAL)
4200 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4202 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4204 else if (move_pattern & MV_ANY_DIRECTION)
4206 MovDir[x][y] = move_pattern;
4207 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4209 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4211 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4212 MovDir[x][y] = left_dir;
4213 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4214 MovDir[x][y] = right_dir;
4216 if (MovDir[x][y] != old_move_dir)
4217 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4219 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4221 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4222 MovDir[x][y] = right_dir;
4223 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4224 MovDir[x][y] = left_dir;
4226 if (MovDir[x][y] != old_move_dir)
4227 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4229 else if (move_pattern == MV_TOWARDS_PLAYER ||
4230 move_pattern == MV_AWAY_FROM_PLAYER)
4232 int attr_x = -1, attr_y = -1;
4234 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4245 for (i = 0; i < MAX_PLAYERS; i++)
4247 struct PlayerInfo *player = &stored_player[i];
4248 int jx = player->jx, jy = player->jy;
4250 if (!player->active)
4254 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4262 MovDir[x][y] = MV_NO_MOVING;
4264 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4265 else if (attr_x > x)
4266 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4268 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4269 else if (attr_y > y)
4270 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4272 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4274 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4276 boolean first_horiz = RND(2);
4277 int new_move_dir = MovDir[x][y];
4280 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4281 Moving2Blocked(x, y, &newx, &newy);
4283 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4287 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4288 Moving2Blocked(x, y, &newx, &newy);
4290 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4293 MovDir[x][y] = old_move_dir;
4296 else if (move_pattern == MV_WHEN_PUSHED ||
4297 move_pattern == MV_WHEN_DROPPED)
4299 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4300 MovDir[x][y] = MV_NO_MOVING;
4304 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4306 static int test_xy[7][2] =
4316 static int test_dir[7] =
4326 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4327 int move_preference = -1000000; /* start with very low preference */
4328 int new_move_dir = MV_NO_MOVING;
4329 int start_test = RND(4);
4332 for (i = 0; i < 4; i++)
4334 int move_dir = test_dir[start_test + i];
4335 int move_dir_preference;
4337 xx = x + test_xy[start_test + i][0];
4338 yy = y + test_xy[start_test + i][1];
4340 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4341 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4343 new_move_dir = move_dir;
4348 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4351 move_dir_preference = -1 * RunnerVisit[xx][yy];
4352 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4353 move_dir_preference = PlayerVisit[xx][yy];
4355 if (move_dir_preference > move_preference)
4357 /* prefer field that has not been visited for the longest time */
4358 move_preference = move_dir_preference;
4359 new_move_dir = move_dir;
4361 else if (move_dir_preference == move_preference &&
4362 move_dir == old_move_dir)
4364 /* prefer last direction when all directions are preferred equally */
4365 move_preference = move_dir_preference;
4366 new_move_dir = move_dir;
4370 MovDir[x][y] = new_move_dir;
4371 if (old_move_dir != new_move_dir)
4376 static void TurnRound(int x, int y)
4378 int direction = MovDir[x][y];
4381 GfxDir[x][y] = MovDir[x][y];
4387 GfxDir[x][y] = MovDir[x][y];
4390 if (direction != MovDir[x][y])
4395 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4398 GfxAction[x][y] = ACTION_WAITING;
4402 static boolean JustBeingPushed(int x, int y)
4406 for (i = 0; i < MAX_PLAYERS; i++)
4408 struct PlayerInfo *player = &stored_player[i];
4410 if (player->active && player->is_pushing && player->MovPos)
4412 int next_jx = player->jx + (player->jx - player->last_jx);
4413 int next_jy = player->jy + (player->jy - player->last_jy);
4415 if (x == next_jx && y == next_jy)
4423 void StartMoving(int x, int y)
4426 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4428 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4429 int element = Feld[x][y];
4435 if (MovDelay[x][y] == 0)
4436 GfxAction[x][y] = ACTION_DEFAULT;
4438 /* !!! this should be handled more generic (not only for mole) !!! */
4439 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4440 GfxAction[x][y] = ACTION_DEFAULT;
4443 if (CAN_FALL(element) && y < lev_fieldy - 1)
4445 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4446 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4447 if (JustBeingPushed(x, y))
4450 if (element == EL_QUICKSAND_FULL)
4452 if (IS_FREE(x, y + 1))
4454 InitMovingField(x, y, MV_DOWN);
4455 started_moving = TRUE;
4457 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4458 Store[x][y] = EL_ROCK;
4460 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4462 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4465 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4467 if (!MovDelay[x][y])
4468 MovDelay[x][y] = TILEY + 1;
4477 Feld[x][y] = EL_QUICKSAND_EMPTY;
4478 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4479 Store[x][y + 1] = Store[x][y];
4482 PlayLevelSoundAction(x, y, ACTION_FILLING);
4484 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4488 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4489 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4491 InitMovingField(x, y, MV_DOWN);
4492 started_moving = TRUE;
4494 Feld[x][y] = EL_QUICKSAND_FILLING;
4495 Store[x][y] = element;
4497 PlayLevelSoundAction(x, y, ACTION_FILLING);
4499 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4502 else if (element == EL_MAGIC_WALL_FULL)
4504 if (IS_FREE(x, y + 1))
4506 InitMovingField(x, y, MV_DOWN);
4507 started_moving = TRUE;
4509 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4510 Store[x][y] = EL_CHANGED(Store[x][y]);
4512 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4514 if (!MovDelay[x][y])
4515 MovDelay[x][y] = TILEY/4 + 1;
4524 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4525 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4526 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4530 else if (element == EL_BD_MAGIC_WALL_FULL)
4532 if (IS_FREE(x, y + 1))
4534 InitMovingField(x, y, MV_DOWN);
4535 started_moving = TRUE;
4537 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4538 Store[x][y] = EL_CHANGED2(Store[x][y]);
4540 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4542 if (!MovDelay[x][y])
4543 MovDelay[x][y] = TILEY/4 + 1;
4552 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4553 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4554 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4558 else if (CAN_PASS_MAGIC_WALL(element) &&
4559 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4560 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4562 InitMovingField(x, y, MV_DOWN);
4563 started_moving = TRUE;
4566 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4567 EL_BD_MAGIC_WALL_FILLING);
4568 Store[x][y] = element;
4571 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4573 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4576 SplashAcid(x, y + 1);
4578 InitMovingField(x, y, MV_DOWN);
4579 started_moving = TRUE;
4581 Store[x][y] = EL_ACID;
4583 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4584 GfxAction[x][y + 1] = ACTION_ACTIVE;
4588 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4589 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4590 (Feld[x][y + 1] == EL_BLOCKED)) ||
4591 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4592 CAN_SMASH(element) && WasJustFalling[x][y] &&
4593 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4597 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4598 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4599 WasJustMoving[x][y] && !Pushed[x][y + 1])
4601 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4602 WasJustMoving[x][y])
4607 /* this is needed for a special case not covered by calling "Impact()"
4608 from "ContinueMoving()": if an element moves to a tile directly below
4609 another element which was just falling on that tile (which was empty
4610 in the previous frame), the falling element above would just stop
4611 instead of smashing the element below (in previous version, the above
4612 element was just checked for "moving" instead of "falling", resulting
4613 in incorrect smashes caused by horizontal movement of the above
4614 element; also, the case of the player being the element to smash was
4615 simply not covered here... :-/ ) */
4618 WasJustMoving[x][y] = 0;
4619 WasJustFalling[x][y] = 0;
4624 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4626 if (MovDir[x][y] == MV_NO_MOVING)
4628 InitMovingField(x, y, MV_DOWN);
4629 started_moving = TRUE;
4632 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4634 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4635 MovDir[x][y] = MV_DOWN;
4637 InitMovingField(x, y, MV_DOWN);
4638 started_moving = TRUE;
4640 else if (element == EL_AMOEBA_DROP)
4642 Feld[x][y] = EL_AMOEBA_GROWING;
4643 Store[x][y] = EL_AMOEBA_WET;
4645 /* Store[x][y + 1] must be zero, because:
4646 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4649 #if OLD_GAME_BEHAVIOUR
4650 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4652 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4653 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4654 element != EL_DX_SUPABOMB)
4657 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4658 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4659 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4660 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4663 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4664 (IS_FREE(x - 1, y + 1) ||
4665 Feld[x - 1][y + 1] == EL_ACID));
4666 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4667 (IS_FREE(x + 1, y + 1) ||
4668 Feld[x + 1][y + 1] == EL_ACID));
4669 boolean can_fall_any = (can_fall_left || can_fall_right);
4670 boolean can_fall_both = (can_fall_left && can_fall_right);
4672 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4674 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4676 if (slippery_type == SLIPPERY_ONLY_LEFT)
4677 can_fall_right = FALSE;
4678 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4679 can_fall_left = FALSE;
4680 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4681 can_fall_right = FALSE;
4682 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4683 can_fall_left = FALSE;
4685 can_fall_any = (can_fall_left || can_fall_right);
4686 can_fall_both = (can_fall_left && can_fall_right);
4691 if (can_fall_both &&
4692 (game.emulation != EMU_BOULDERDASH &&
4693 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4694 can_fall_left = !(can_fall_right = RND(2));
4696 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4697 started_moving = TRUE;
4701 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4703 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4706 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4707 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4708 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4709 int belt_dir = game.belt_dir[belt_nr];
4711 if ((belt_dir == MV_LEFT && left_is_free) ||
4712 (belt_dir == MV_RIGHT && right_is_free))
4715 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4718 InitMovingField(x, y, belt_dir);
4719 started_moving = TRUE;
4722 Pushed[x][y] = TRUE;
4723 Pushed[nextx][y] = TRUE;
4726 GfxAction[x][y] = ACTION_DEFAULT;
4730 MovDir[x][y] = 0; /* if element was moving, stop it */
4735 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4736 if (CAN_MOVE(element) && !started_moving)
4738 int move_pattern = element_info[element].move_pattern;
4741 Moving2Blocked(x, y, &newx, &newy);
4744 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4747 if ((element == EL_SATELLITE ||
4748 element == EL_BALLOON ||
4749 element == EL_SPRING)
4750 && JustBeingPushed(x, y))
4755 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4756 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4757 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4760 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4761 element, element_info[element].token_name,
4762 WasJustMoving[x][y],
4763 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4764 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4765 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4766 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4770 WasJustMoving[x][y] = 0;
4773 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4776 if (Feld[x][y] != element) /* element has changed */
4778 element = Feld[x][y];
4779 move_pattern = element_info[element].move_pattern;
4781 if (!CAN_MOVE(element))
4785 if (Feld[x][y] != element) /* element has changed */
4793 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4794 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4796 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4798 Moving2Blocked(x, y, &newx, &newy);
4799 if (Feld[newx][newy] == EL_BLOCKED)
4800 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4806 if (FrameCounter < 1 && x == 0 && y == 29)
4807 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4810 if (!MovDelay[x][y]) /* start new movement phase */
4812 /* all objects that can change their move direction after each step
4813 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4815 if (element != EL_YAMYAM &&
4816 element != EL_DARK_YAMYAM &&
4817 element != EL_PACMAN &&
4818 !(move_pattern & MV_ANY_DIRECTION) &&
4819 move_pattern != MV_TURNING_LEFT &&
4820 move_pattern != MV_TURNING_RIGHT &&
4821 move_pattern != MV_TURNING_LEFT_RIGHT &&
4822 move_pattern != MV_TURNING_RIGHT_LEFT &&
4823 move_pattern != MV_TURNING_RANDOM)
4828 if (FrameCounter < 1 && x == 0 && y == 29)
4829 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4832 if (MovDelay[x][y] && (element == EL_BUG ||
4833 element == EL_SPACESHIP ||
4834 element == EL_SP_SNIKSNAK ||
4835 element == EL_SP_ELECTRON ||
4836 element == EL_MOLE))
4837 DrawLevelField(x, y);
4841 if (MovDelay[x][y]) /* wait some time before next movement */
4846 if (element == EL_YAMYAM)
4849 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4850 DrawLevelElementAnimation(x, y, element);
4854 if (MovDelay[x][y]) /* element still has to wait some time */
4857 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4858 ResetGfxAnimation(x, y);
4862 if (GfxAction[x][y] != ACTION_WAITING)
4863 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4865 GfxAction[x][y] = ACTION_WAITING;
4869 if (element == EL_ROBOT ||
4871 element == EL_PACMAN ||
4873 element == EL_YAMYAM ||
4874 element == EL_DARK_YAMYAM)
4877 DrawLevelElementAnimation(x, y, element);
4879 DrawLevelElementAnimationIfNeeded(x, y, element);
4881 PlayLevelSoundAction(x, y, ACTION_WAITING);
4883 else if (element == EL_SP_ELECTRON)
4884 DrawLevelElementAnimationIfNeeded(x, y, element);
4885 else if (element == EL_DRAGON)
4888 int dir = MovDir[x][y];
4889 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4890 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4891 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4892 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4893 dir == MV_UP ? IMG_FLAMES_1_UP :
4894 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4895 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4898 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4901 GfxAction[x][y] = ACTION_ATTACKING;
4903 if (IS_PLAYER(x, y))
4904 DrawPlayerField(x, y);
4906 DrawLevelField(x, y);
4908 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4910 for (i = 1; i <= 3; i++)
4912 int xx = x + i * dx;
4913 int yy = y + i * dy;
4914 int sx = SCREENX(xx);
4915 int sy = SCREENY(yy);
4916 int flame_graphic = graphic + (i - 1);
4918 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4923 int flamed = MovingOrBlocked2Element(xx, yy);
4925 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4928 RemoveMovingField(xx, yy);
4930 Feld[xx][yy] = EL_FLAMES;
4931 if (IN_SCR_FIELD(sx, sy))
4933 DrawLevelFieldCrumbledSand(xx, yy);
4934 DrawGraphic(sx, sy, flame_graphic, frame);
4939 if (Feld[xx][yy] == EL_FLAMES)
4940 Feld[xx][yy] = EL_EMPTY;
4941 DrawLevelField(xx, yy);
4946 if (MovDelay[x][y]) /* element still has to wait some time */
4948 PlayLevelSoundAction(x, y, ACTION_WAITING);
4954 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4955 for all other elements GfxAction will be set by InitMovingField() */
4956 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4957 GfxAction[x][y] = ACTION_MOVING;
4961 /* now make next step */
4963 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4965 if (DONT_COLLIDE_WITH(element) &&
4966 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4967 !PLAYER_ENEMY_PROTECTED(newx, newy))
4970 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4974 /* player killed by element which is deadly when colliding with */
4976 KillHero(PLAYERINFO(newx, newy));
4983 else if (CAN_MOVE_INTO_ACID(element) &&
4984 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
4985 (MovDir[x][y] == MV_DOWN ||
4986 game.engine_version > VERSION_IDENT(3,0,8,0)))
4988 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
4989 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
4993 else if ((element == EL_PENGUIN ||
4994 element == EL_ROBOT ||
4995 element == EL_SATELLITE ||
4996 element == EL_BALLOON ||
4997 IS_CUSTOM_ELEMENT(element)) &&
4998 IN_LEV_FIELD(newx, newy) &&
4999 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5002 SplashAcid(newx, newy);
5003 Store[x][y] = EL_ACID;
5005 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5007 if (Feld[newx][newy] == EL_EXIT_OPEN)
5011 DrawLevelField(x, y);
5013 Feld[x][y] = EL_EMPTY;
5014 DrawLevelField(x, y);
5017 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5018 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5019 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5021 local_player->friends_still_needed--;
5022 if (!local_player->friends_still_needed &&
5023 !local_player->GameOver && AllPlayersGone)
5024 local_player->LevelSolved = local_player->GameOver = TRUE;
5028 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5030 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5031 DrawLevelField(newx, newy);
5033 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5035 else if (!IS_FREE(newx, newy))
5037 GfxAction[x][y] = ACTION_WAITING;
5039 if (IS_PLAYER(x, y))
5040 DrawPlayerField(x, y);
5042 DrawLevelField(x, y);
5047 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5049 if (IS_FOOD_PIG(Feld[newx][newy]))
5051 if (IS_MOVING(newx, newy))
5052 RemoveMovingField(newx, newy);
5055 Feld[newx][newy] = EL_EMPTY;
5056 DrawLevelField(newx, newy);
5059 PlayLevelSound(x, y, SND_PIG_DIGGING);
5061 else if (!IS_FREE(newx, newy))
5063 if (IS_PLAYER(x, y))
5064 DrawPlayerField(x, y);
5066 DrawLevelField(x, y);
5075 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5078 else if (IS_CUSTOM_ELEMENT(element) &&
5079 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5083 !IS_FREE(newx, newy)
5088 int new_element = Feld[newx][newy];
5091 printf("::: '%s' digs '%s' [%d]\n",
5092 element_info[element].token_name,
5093 element_info[Feld[newx][newy]].token_name,
5094 StorePlayer[newx][newy]);
5097 if (!IS_FREE(newx, newy))
5099 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5100 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5103 /* no element can dig solid indestructible elements */
5104 if (IS_INDESTRUCTIBLE(new_element) &&
5105 !IS_DIGGABLE(new_element) &&
5106 !IS_COLLECTIBLE(new_element))
5109 if (AmoebaNr[newx][newy] &&
5110 (new_element == EL_AMOEBA_FULL ||
5111 new_element == EL_BD_AMOEBA ||
5112 new_element == EL_AMOEBA_GROWING))
5114 AmoebaCnt[AmoebaNr[newx][newy]]--;
5115 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5118 if (IS_MOVING(newx, newy))
5119 RemoveMovingField(newx, newy);
5122 RemoveField(newx, newy);
5123 DrawLevelField(newx, newy);
5126 PlayLevelSoundAction(x, y, action);
5129 if (new_element == element_info[element].move_enter_element)
5130 element_info[element].can_leave_element = TRUE;
5132 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5134 RunnerVisit[x][y] = FrameCounter;
5135 PlayerVisit[x][y] /= 8; /* expire player visit path */
5141 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5143 if (!IS_FREE(newx, newy))
5145 if (IS_PLAYER(x, y))
5146 DrawPlayerField(x, y);
5148 DrawLevelField(x, y);
5154 boolean wanna_flame = !RND(10);
5155 int dx = newx - x, dy = newy - y;
5156 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5157 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5158 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5159 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5160 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5161 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5164 IS_CLASSIC_ENEMY(element1) ||
5165 IS_CLASSIC_ENEMY(element2)) &&
5166 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5167 element1 != EL_FLAMES && element2 != EL_FLAMES)
5170 ResetGfxAnimation(x, y);
5171 GfxAction[x][y] = ACTION_ATTACKING;
5174 if (IS_PLAYER(x, y))
5175 DrawPlayerField(x, y);
5177 DrawLevelField(x, y);
5179 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5181 MovDelay[x][y] = 50;
5183 Feld[newx][newy] = EL_FLAMES;
5184 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5185 Feld[newx1][newy1] = EL_FLAMES;
5186 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5187 Feld[newx2][newy2] = EL_FLAMES;
5193 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5194 Feld[newx][newy] == EL_DIAMOND)
5196 if (IS_MOVING(newx, newy))
5197 RemoveMovingField(newx, newy);
5200 Feld[newx][newy] = EL_EMPTY;
5201 DrawLevelField(newx, newy);
5204 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5206 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5207 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5209 if (AmoebaNr[newx][newy])
5211 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5212 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5213 Feld[newx][newy] == EL_BD_AMOEBA)
5214 AmoebaCnt[AmoebaNr[newx][newy]]--;
5219 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5221 if (IS_MOVING(newx, newy))
5224 RemoveMovingField(newx, newy);
5228 Feld[newx][newy] = EL_EMPTY;
5229 DrawLevelField(newx, newy);
5232 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5234 else if ((element == EL_PACMAN || element == EL_MOLE)
5235 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5237 if (AmoebaNr[newx][newy])
5239 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5240 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5241 Feld[newx][newy] == EL_BD_AMOEBA)
5242 AmoebaCnt[AmoebaNr[newx][newy]]--;
5245 if (element == EL_MOLE)
5247 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5248 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5250 ResetGfxAnimation(x, y);
5251 GfxAction[x][y] = ACTION_DIGGING;
5252 DrawLevelField(x, y);
5254 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5256 return; /* wait for shrinking amoeba */
5258 else /* element == EL_PACMAN */
5260 Feld[newx][newy] = EL_EMPTY;
5261 DrawLevelField(newx, newy);
5262 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5265 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5266 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5267 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5269 /* wait for shrinking amoeba to completely disappear */
5272 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5274 /* object was running against a wall */
5279 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5280 DrawLevelElementAnimation(x, y, element);
5282 if (element == EL_BUG ||
5283 element == EL_SPACESHIP ||
5284 element == EL_SP_SNIKSNAK)
5285 DrawLevelField(x, y);
5286 else if (element == EL_MOLE)
5287 DrawLevelField(x, y);
5288 else if (element == EL_BD_BUTTERFLY ||
5289 element == EL_BD_FIREFLY)
5290 DrawLevelElementAnimationIfNeeded(x, y, element);
5291 else if (element == EL_SATELLITE)
5292 DrawLevelElementAnimationIfNeeded(x, y, element);
5293 else if (element == EL_SP_ELECTRON)
5294 DrawLevelElementAnimationIfNeeded(x, y, element);
5297 if (DONT_TOUCH(element))
5298 TestIfBadThingTouchesHero(x, y);
5301 PlayLevelSoundAction(x, y, ACTION_WAITING);
5307 InitMovingField(x, y, MovDir[x][y]);
5309 PlayLevelSoundAction(x, y, ACTION_MOVING);
5313 ContinueMoving(x, y);
5316 void ContinueMoving(int x, int y)
5318 int element = Feld[x][y];
5319 struct ElementInfo *ei = &element_info[element];
5320 int direction = MovDir[x][y];
5321 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5322 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5323 int newx = x + dx, newy = y + dy;
5325 int nextx = newx + dx, nexty = newy + dy;
5328 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5329 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5331 boolean pushed_by_player = Pushed[x][y];
5334 MovPos[x][y] += getElementMoveStepsize(x, y);
5337 if (pushed_by_player && IS_PLAYER(x, y))
5339 /* special case: moving object pushed by player */
5340 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5343 if (pushed_by_player) /* special case: moving object pushed by player */
5344 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5347 if (ABS(MovPos[x][y]) < TILEX)
5349 DrawLevelField(x, y);
5351 return; /* element is still moving */
5354 /* element reached destination field */
5356 Feld[x][y] = EL_EMPTY;
5357 Feld[newx][newy] = element;
5358 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5360 if (element == EL_MOLE)
5362 Feld[x][y] = EL_SAND;
5364 DrawLevelFieldCrumbledSandNeighbours(x, y);
5366 else if (element == EL_QUICKSAND_FILLING)
5368 element = Feld[newx][newy] = get_next_element(element);
5369 Store[newx][newy] = Store[x][y];
5371 else if (element == EL_QUICKSAND_EMPTYING)
5373 Feld[x][y] = get_next_element(element);
5374 element = Feld[newx][newy] = Store[x][y];
5376 else if (element == EL_MAGIC_WALL_FILLING)
5378 element = Feld[newx][newy] = get_next_element(element);
5379 if (!game.magic_wall_active)
5380 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5381 Store[newx][newy] = Store[x][y];
5383 else if (element == EL_MAGIC_WALL_EMPTYING)
5385 Feld[x][y] = get_next_element(element);
5386 if (!game.magic_wall_active)
5387 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5388 element = Feld[newx][newy] = Store[x][y];
5390 else if (element == EL_BD_MAGIC_WALL_FILLING)
5392 element = Feld[newx][newy] = get_next_element(element);
5393 if (!game.magic_wall_active)
5394 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5395 Store[newx][newy] = Store[x][y];
5397 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5399 Feld[x][y] = get_next_element(element);
5400 if (!game.magic_wall_active)
5401 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5402 element = Feld[newx][newy] = Store[x][y];
5404 else if (element == EL_AMOEBA_DROPPING)
5406 Feld[x][y] = get_next_element(element);
5407 element = Feld[newx][newy] = Store[x][y];
5409 else if (element == EL_SOKOBAN_OBJECT)
5412 Feld[x][y] = Back[x][y];
5414 if (Back[newx][newy])
5415 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5417 Back[x][y] = Back[newx][newy] = 0;
5419 else if (Store[x][y] == EL_ACID)
5421 element = Feld[newx][newy] = EL_ACID;
5425 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5426 MovDelay[newx][newy] = 0;
5428 /* copy element change control values to new field */
5429 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5430 ChangePage[newx][newy] = ChangePage[x][y];
5431 Changed[newx][newy] = Changed[x][y];
5432 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5434 ChangeDelay[x][y] = 0;
5435 ChangePage[x][y] = -1;
5436 Changed[x][y] = CE_BITMASK_DEFAULT;
5437 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5439 /* copy animation control values to new field */
5440 GfxFrame[newx][newy] = GfxFrame[x][y];
5441 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5442 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5443 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5445 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5447 ResetGfxAnimation(x, y); /* reset animation values for old field */
5450 /* some elements can leave other elements behind after moving */
5451 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5452 ei->move_leave_element != EL_EMPTY &&
5453 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5454 ei->can_leave_element_last))
5456 Feld[x][y] = ei->move_leave_element;
5457 InitField(x, y, FALSE);
5459 if (GFX_CRUMBLED(Feld[x][y]))
5460 DrawLevelFieldCrumbledSandNeighbours(x, y);
5463 ei->can_leave_element_last = ei->can_leave_element;
5464 ei->can_leave_element = FALSE;
5468 /* 2.1.1 (does not work correctly for spring) */
5469 if (!CAN_MOVE(element))
5470 MovDir[newx][newy] = 0;
5474 /* (does not work for falling objects that slide horizontally) */
5475 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5476 MovDir[newx][newy] = 0;
5479 if (!CAN_MOVE(element) ||
5480 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5481 MovDir[newx][newy] = 0;
5484 if (!CAN_MOVE(element) ||
5485 (CAN_FALL(element) && direction == MV_DOWN))
5486 GfxDir[x][y] = MovDir[newx][newy] = 0;
5491 DrawLevelField(x, y);
5492 DrawLevelField(newx, newy);
5494 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5496 /* prevent pushed element from moving on in pushed direction */
5497 if (pushed_by_player && CAN_MOVE(element) &&
5498 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5499 !(element_info[element].move_pattern & direction))
5500 TurnRound(newx, newy);
5503 /* prevent elements on conveyor belt from moving on in last direction */
5504 if (pushed_by_conveyor && CAN_FALL(element) &&
5505 direction & MV_HORIZONTAL)
5506 MovDir[newx][newy] = 0;
5509 if (!pushed_by_player)
5511 WasJustMoving[newx][newy] = 3;
5513 if (CAN_FALL(element) && direction == MV_DOWN)
5514 WasJustFalling[newx][newy] = 3;
5517 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5519 TestIfBadThingTouchesHero(newx, newy);
5520 TestIfBadThingTouchesFriend(newx, newy);
5522 if (!IS_CUSTOM_ELEMENT(element))
5523 TestIfBadThingTouchesOtherBadThing(newx, newy);
5525 else if (element == EL_PENGUIN)
5526 TestIfFriendTouchesBadThing(newx, newy);
5528 if (CAN_FALL(element) && direction == MV_DOWN &&
5529 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5533 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5537 if (ChangePage[newx][newy] != -1) /* delayed change */
5538 ChangeElement(newx, newy, ChangePage[newx][newy]);
5543 TestIfElementHitsCustomElement(newx, newy, direction);
5547 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5549 int hitting_element = Feld[newx][newy];
5551 /* !!! fix side (direction) orientation here and elsewhere !!! */
5552 CheckElementSideChange(newx, newy, hitting_element,
5553 direction, CE_HITTING_SOMETHING, -1);
5556 if (IN_LEV_FIELD(nextx, nexty))
5558 int opposite_direction = MV_DIR_OPPOSITE(direction);
5559 int hitting_side = direction;
5560 int touched_side = opposite_direction;
5561 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5562 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5563 MovDir[nextx][nexty] != direction ||
5564 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5570 CheckElementSideChange(nextx, nexty, touched_element,
5571 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5573 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5574 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5576 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5578 struct ElementChangeInfo *change =
5579 &element_info[hitting_element].change_page[i];
5581 if (change->can_change &&
5582 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5583 change->sides & touched_side &&
5584 change->trigger_element == touched_element)
5586 CheckElementSideChange(newx, newy, hitting_element,
5587 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5593 if (IS_CUSTOM_ELEMENT(touched_element) &&
5594 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5596 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5598 struct ElementChangeInfo *change =
5599 &element_info[touched_element].change_page[i];
5601 if (change->can_change &&
5602 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5603 change->sides & hitting_side &&
5604 change->trigger_element == hitting_element)
5606 CheckElementSideChange(nextx, nexty, touched_element,
5607 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5618 TestIfPlayerTouchesCustomElement(newx, newy);
5619 TestIfElementTouchesCustomElement(newx, newy);
5622 int AmoebeNachbarNr(int ax, int ay)
5625 int element = Feld[ax][ay];
5627 static int xy[4][2] =
5635 for (i = 0; i < 4; i++)
5637 int x = ax + xy[i][0];
5638 int y = ay + xy[i][1];
5640 if (!IN_LEV_FIELD(x, y))
5643 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5644 group_nr = AmoebaNr[x][y];
5650 void AmoebenVereinigen(int ax, int ay)
5652 int i, x, y, xx, yy;
5653 int new_group_nr = AmoebaNr[ax][ay];
5654 static int xy[4][2] =
5662 if (new_group_nr == 0)
5665 for (i = 0; i < 4; i++)
5670 if (!IN_LEV_FIELD(x, y))
5673 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5674 Feld[x][y] == EL_BD_AMOEBA ||
5675 Feld[x][y] == EL_AMOEBA_DEAD) &&
5676 AmoebaNr[x][y] != new_group_nr)
5678 int old_group_nr = AmoebaNr[x][y];
5680 if (old_group_nr == 0)
5683 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5684 AmoebaCnt[old_group_nr] = 0;
5685 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5686 AmoebaCnt2[old_group_nr] = 0;
5688 for (yy = 0; yy < lev_fieldy; yy++)
5690 for (xx = 0; xx < lev_fieldx; xx++)
5692 if (AmoebaNr[xx][yy] == old_group_nr)
5693 AmoebaNr[xx][yy] = new_group_nr;
5700 void AmoebeUmwandeln(int ax, int ay)
5704 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5706 int group_nr = AmoebaNr[ax][ay];
5711 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5712 printf("AmoebeUmwandeln(): This should never happen!\n");
5717 for (y = 0; y < lev_fieldy; y++)
5719 for (x = 0; x < lev_fieldx; x++)
5721 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5724 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5728 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5729 SND_AMOEBA_TURNING_TO_GEM :
5730 SND_AMOEBA_TURNING_TO_ROCK));
5735 static int xy[4][2] =
5743 for (i = 0; i < 4; i++)
5748 if (!IN_LEV_FIELD(x, y))
5751 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5753 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5754 SND_AMOEBA_TURNING_TO_GEM :
5755 SND_AMOEBA_TURNING_TO_ROCK));
5762 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5765 int group_nr = AmoebaNr[ax][ay];
5766 boolean done = FALSE;
5771 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5772 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5777 for (y = 0; y < lev_fieldy; y++)
5779 for (x = 0; x < lev_fieldx; x++)
5781 if (AmoebaNr[x][y] == group_nr &&
5782 (Feld[x][y] == EL_AMOEBA_DEAD ||
5783 Feld[x][y] == EL_BD_AMOEBA ||
5784 Feld[x][y] == EL_AMOEBA_GROWING))
5787 Feld[x][y] = new_element;
5788 InitField(x, y, FALSE);
5789 DrawLevelField(x, y);
5796 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5797 SND_BD_AMOEBA_TURNING_TO_ROCK :
5798 SND_BD_AMOEBA_TURNING_TO_GEM));
5801 void AmoebeWaechst(int x, int y)
5803 static unsigned long sound_delay = 0;
5804 static unsigned long sound_delay_value = 0;
5806 if (!MovDelay[x][y]) /* start new growing cycle */
5810 if (DelayReached(&sound_delay, sound_delay_value))
5813 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5815 if (Store[x][y] == EL_BD_AMOEBA)
5816 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5818 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5820 sound_delay_value = 30;
5824 if (MovDelay[x][y]) /* wait some time before growing bigger */
5827 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5829 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5830 6 - MovDelay[x][y]);
5832 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5835 if (!MovDelay[x][y])
5837 Feld[x][y] = Store[x][y];
5839 DrawLevelField(x, y);
5844 void AmoebaDisappearing(int x, int y)
5846 static unsigned long sound_delay = 0;
5847 static unsigned long sound_delay_value = 0;
5849 if (!MovDelay[x][y]) /* start new shrinking cycle */
5853 if (DelayReached(&sound_delay, sound_delay_value))
5854 sound_delay_value = 30;
5857 if (MovDelay[x][y]) /* wait some time before shrinking */
5860 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5862 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5863 6 - MovDelay[x][y]);
5865 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5868 if (!MovDelay[x][y])
5870 Feld[x][y] = EL_EMPTY;
5871 DrawLevelField(x, y);
5873 /* don't let mole enter this field in this cycle;
5874 (give priority to objects falling to this field from above) */
5880 void AmoebeAbleger(int ax, int ay)
5883 int element = Feld[ax][ay];
5884 int graphic = el2img(element);
5885 int newax = ax, neway = ay;
5886 static int xy[4][2] =
5894 if (!level.amoeba_speed)
5896 Feld[ax][ay] = EL_AMOEBA_DEAD;
5897 DrawLevelField(ax, ay);
5901 if (IS_ANIMATED(graphic))
5902 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5904 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5905 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5907 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5910 if (MovDelay[ax][ay])
5914 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5917 int x = ax + xy[start][0];
5918 int y = ay + xy[start][1];
5920 if (!IN_LEV_FIELD(x, y))
5923 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5924 if (IS_FREE(x, y) ||
5925 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5931 if (newax == ax && neway == ay)
5934 else /* normal or "filled" (BD style) amoeba */
5937 boolean waiting_for_player = FALSE;
5939 for (i = 0; i < 4; i++)
5941 int j = (start + i) % 4;
5942 int x = ax + xy[j][0];
5943 int y = ay + xy[j][1];
5945 if (!IN_LEV_FIELD(x, y))
5948 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5949 if (IS_FREE(x, y) ||
5950 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5956 else if (IS_PLAYER(x, y))
5957 waiting_for_player = TRUE;
5960 if (newax == ax && neway == ay) /* amoeba cannot grow */
5962 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5964 Feld[ax][ay] = EL_AMOEBA_DEAD;
5965 DrawLevelField(ax, ay);
5966 AmoebaCnt[AmoebaNr[ax][ay]]--;
5968 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5970 if (element == EL_AMOEBA_FULL)
5971 AmoebeUmwandeln(ax, ay);
5972 else if (element == EL_BD_AMOEBA)
5973 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5978 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5980 /* amoeba gets larger by growing in some direction */
5982 int new_group_nr = AmoebaNr[ax][ay];
5985 if (new_group_nr == 0)
5987 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5988 printf("AmoebeAbleger(): This should never happen!\n");
5993 AmoebaNr[newax][neway] = new_group_nr;
5994 AmoebaCnt[new_group_nr]++;
5995 AmoebaCnt2[new_group_nr]++;
5997 /* if amoeba touches other amoeba(s) after growing, unify them */
5998 AmoebenVereinigen(newax, neway);
6000 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6002 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6008 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6009 (neway == lev_fieldy - 1 && newax != ax))
6011 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6012 Store[newax][neway] = element;
6014 else if (neway == ay)
6016 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6018 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6020 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6025 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6026 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6027 Store[ax][ay] = EL_AMOEBA_DROP;
6028 ContinueMoving(ax, ay);
6032 DrawLevelField(newax, neway);
6035 void Life(int ax, int ay)
6038 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6040 int element = Feld[ax][ay];
6041 int graphic = el2img(element);
6042 boolean changed = FALSE;
6044 if (IS_ANIMATED(graphic))
6045 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6050 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6051 MovDelay[ax][ay] = life_time;
6053 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6056 if (MovDelay[ax][ay])
6060 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6062 int xx = ax+x1, yy = ay+y1;
6065 if (!IN_LEV_FIELD(xx, yy))
6068 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6070 int x = xx+x2, y = yy+y2;
6072 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6075 if (((Feld[x][y] == element ||
6076 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6078 (IS_FREE(x, y) && Stop[x][y]))
6082 if (xx == ax && yy == ay) /* field in the middle */
6084 if (nachbarn < life[0] || nachbarn > life[1])
6086 Feld[xx][yy] = EL_EMPTY;
6088 DrawLevelField(xx, yy);
6089 Stop[xx][yy] = TRUE;
6093 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6094 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6095 { /* free border field */
6096 if (nachbarn >= life[2] && nachbarn <= life[3])
6098 Feld[xx][yy] = element;
6099 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6101 DrawLevelField(xx, yy);
6102 Stop[xx][yy] = TRUE;
6109 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6110 SND_GAME_OF_LIFE_GROWING);
6113 static void InitRobotWheel(int x, int y)
6115 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6118 static void RunRobotWheel(int x, int y)
6120 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6123 static void StopRobotWheel(int x, int y)
6125 if (ZX == x && ZY == y)
6129 static void InitTimegateWheel(int x, int y)
6131 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6134 static void RunTimegateWheel(int x, int y)
6136 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6139 void CheckExit(int x, int y)
6141 if (local_player->gems_still_needed > 0 ||
6142 local_player->sokobanfields_still_needed > 0 ||
6143 local_player->lights_still_needed > 0)
6145 int element = Feld[x][y];
6146 int graphic = el2img(element);
6148 if (IS_ANIMATED(graphic))
6149 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6154 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6157 Feld[x][y] = EL_EXIT_OPENING;
6159 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6162 void CheckExitSP(int x, int y)
6164 if (local_player->gems_still_needed > 0)
6166 int element = Feld[x][y];
6167 int graphic = el2img(element);
6169 if (IS_ANIMATED(graphic))
6170 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6175 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6178 Feld[x][y] = EL_SP_EXIT_OPENING;
6180 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6183 static void CloseAllOpenTimegates()
6187 for (y = 0; y < lev_fieldy; y++)
6189 for (x = 0; x < lev_fieldx; x++)
6191 int element = Feld[x][y];
6193 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6195 Feld[x][y] = EL_TIMEGATE_CLOSING;
6197 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6199 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6206 void EdelsteinFunkeln(int x, int y)
6208 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6211 if (Feld[x][y] == EL_BD_DIAMOND)
6214 if (MovDelay[x][y] == 0) /* next animation frame */
6215 MovDelay[x][y] = 11 * !SimpleRND(500);
6217 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6221 if (setup.direct_draw && MovDelay[x][y])
6222 SetDrawtoField(DRAW_BUFFERED);
6224 DrawLevelElementAnimation(x, y, Feld[x][y]);
6226 if (MovDelay[x][y] != 0)
6228 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6229 10 - MovDelay[x][y]);
6231 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6233 if (setup.direct_draw)
6237 dest_x = FX + SCREENX(x) * TILEX;
6238 dest_y = FY + SCREENY(y) * TILEY;
6240 BlitBitmap(drawto_field, window,
6241 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6242 SetDrawtoField(DRAW_DIRECT);
6248 void MauerWaechst(int x, int y)
6252 if (!MovDelay[x][y]) /* next animation frame */
6253 MovDelay[x][y] = 3 * delay;
6255 if (MovDelay[x][y]) /* wait some time before next frame */
6259 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6261 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6262 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6264 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6267 if (!MovDelay[x][y])
6269 if (MovDir[x][y] == MV_LEFT)
6271 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6272 DrawLevelField(x - 1, y);
6274 else if (MovDir[x][y] == MV_RIGHT)
6276 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6277 DrawLevelField(x + 1, y);
6279 else if (MovDir[x][y] == MV_UP)
6281 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6282 DrawLevelField(x, y - 1);
6286 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6287 DrawLevelField(x, y + 1);
6290 Feld[x][y] = Store[x][y];
6292 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6293 DrawLevelField(x, y);
6298 void MauerAbleger(int ax, int ay)
6300 int element = Feld[ax][ay];
6301 int graphic = el2img(element);
6302 boolean oben_frei = FALSE, unten_frei = FALSE;
6303 boolean links_frei = FALSE, rechts_frei = FALSE;
6304 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6305 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6306 boolean new_wall = FALSE;
6308 if (IS_ANIMATED(graphic))
6309 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6311 if (!MovDelay[ax][ay]) /* start building new wall */
6312 MovDelay[ax][ay] = 6;
6314 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6317 if (MovDelay[ax][ay])
6321 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6323 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6325 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6327 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6330 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6331 element == EL_EXPANDABLE_WALL_ANY)
6335 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6336 Store[ax][ay-1] = element;
6337 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6338 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6339 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6340 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6345 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6346 Store[ax][ay+1] = element;
6347 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6348 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6349 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6350 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6355 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6356 element == EL_EXPANDABLE_WALL_ANY ||
6357 element == EL_EXPANDABLE_WALL)
6361 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6362 Store[ax-1][ay] = element;
6363 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6364 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6365 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6366 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6372 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6373 Store[ax+1][ay] = element;
6374 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6375 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6376 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6377 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6382 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6383 DrawLevelField(ax, ay);
6385 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6387 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6388 unten_massiv = TRUE;
6389 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6390 links_massiv = TRUE;
6391 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6392 rechts_massiv = TRUE;
6394 if (((oben_massiv && unten_massiv) ||
6395 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6396 element == EL_EXPANDABLE_WALL) &&
6397 ((links_massiv && rechts_massiv) ||
6398 element == EL_EXPANDABLE_WALL_VERTICAL))
6399 Feld[ax][ay] = EL_WALL;
6403 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6405 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6409 void CheckForDragon(int x, int y)
6412 boolean dragon_found = FALSE;
6413 static int xy[4][2] =
6421 for (i = 0; i < 4; i++)
6423 for (j = 0; j < 4; j++)
6425 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6427 if (IN_LEV_FIELD(xx, yy) &&
6428 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6430 if (Feld[xx][yy] == EL_DRAGON)
6431 dragon_found = TRUE;
6440 for (i = 0; i < 4; i++)
6442 for (j = 0; j < 3; j++)
6444 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6446 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6448 Feld[xx][yy] = EL_EMPTY;
6449 DrawLevelField(xx, yy);
6458 static void InitBuggyBase(int x, int y)
6460 int element = Feld[x][y];
6461 int activating_delay = FRAMES_PER_SECOND / 4;
6464 (element == EL_SP_BUGGY_BASE ?
6465 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6466 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6468 element == EL_SP_BUGGY_BASE_ACTIVE ?
6469 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6472 static void WarnBuggyBase(int x, int y)
6475 static int xy[4][2] =
6483 for (i = 0; i < 4; i++)
6485 int xx = x + xy[i][0], yy = y + xy[i][1];
6487 if (IS_PLAYER(xx, yy))
6489 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6496 static void InitTrap(int x, int y)
6498 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6501 static void ActivateTrap(int x, int y)
6503 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6506 static void ChangeActiveTrap(int x, int y)
6508 int graphic = IMG_TRAP_ACTIVE;
6510 /* if new animation frame was drawn, correct crumbled sand border */
6511 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6512 DrawLevelFieldCrumbledSand(x, y);
6515 static void ChangeElementNowExt(int x, int y, int target_element)
6517 int previous_move_direction = MovDir[x][y];
6519 /* check if element under player changes from accessible to unaccessible
6520 (needed for special case of dropping element which then changes) */
6521 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6522 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6529 Feld[x][y] = target_element;
6531 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6533 ResetGfxAnimation(x, y);
6534 ResetRandomAnimationValue(x, y);
6536 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6537 MovDir[x][y] = previous_move_direction;
6540 InitField_WithBug1(x, y, FALSE);
6542 InitField(x, y, FALSE);
6543 if (CAN_MOVE(Feld[x][y]))
6547 DrawLevelField(x, y);
6549 if (GFX_CRUMBLED(Feld[x][y]))
6550 DrawLevelFieldCrumbledSandNeighbours(x, y);
6552 TestIfBadThingTouchesHero(x, y);
6553 TestIfPlayerTouchesCustomElement(x, y);
6554 TestIfElementTouchesCustomElement(x, y);
6556 if (ELEM_IS_PLAYER(target_element))
6557 RelocatePlayer(x, y, target_element);
6560 static boolean ChangeElementNow(int x, int y, int element, int page)
6562 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6564 /* always use default change event to prevent running into a loop */
6565 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6566 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6568 /* do not change already changed elements with same change event */
6570 if (Changed[x][y] & ChangeEvent[x][y])
6577 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6579 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6581 if (change->explode)
6588 if (change->use_content)
6590 boolean complete_change = TRUE;
6591 boolean can_change[3][3];
6594 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6596 boolean half_destructible;
6597 int ex = x + xx - 1;
6598 int ey = y + yy - 1;
6601 can_change[xx][yy] = TRUE;
6603 if (ex == x && ey == y) /* do not check changing element itself */
6606 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6608 can_change[xx][yy] = FALSE; /* do not change empty borders */
6613 if (!IN_LEV_FIELD(ex, ey))
6615 can_change[xx][yy] = FALSE;
6616 complete_change = FALSE;
6623 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6624 e = MovingOrBlocked2Element(ex, ey);
6626 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6628 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6629 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6630 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6632 can_change[xx][yy] = FALSE;
6633 complete_change = FALSE;
6637 if (!change->only_complete || complete_change)
6639 boolean something_has_changed = FALSE;
6641 if (change->only_complete && change->use_random_change &&
6642 RND(100) < change->random)
6645 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6647 int ex = x + xx - 1;
6648 int ey = y + yy - 1;
6650 if (can_change[xx][yy] && (!change->use_random_change ||
6651 RND(100) < change->random))
6653 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6654 RemoveMovingField(ex, ey);
6656 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6658 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6660 something_has_changed = TRUE;
6662 /* for symmetry reasons, freeze newly created border elements */
6663 if (ex != x || ey != y)
6664 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6668 if (something_has_changed)
6669 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6674 ChangeElementNowExt(x, y, change->target_element);
6676 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6682 static void ChangeElement(int x, int y, int page)
6684 int element = MovingOrBlocked2Element(x, y);
6685 struct ElementInfo *ei = &element_info[element];
6686 struct ElementChangeInfo *change = &ei->change_page[page];
6690 if (!CAN_CHANGE(element))
6693 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6694 x, y, element, element_info[element].token_name);
6695 printf("ChangeElement(): This should never happen!\n");
6701 if (ChangeDelay[x][y] == 0) /* initialize element change */
6703 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6704 RND(change->delay_random * change->delay_frames)) + 1;
6706 ResetGfxAnimation(x, y);
6707 ResetRandomAnimationValue(x, y);
6709 if (change->pre_change_function)
6710 change->pre_change_function(x, y);
6713 ChangeDelay[x][y]--;
6715 if (ChangeDelay[x][y] != 0) /* continue element change */
6717 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6719 if (IS_ANIMATED(graphic))
6720 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6722 if (change->change_function)
6723 change->change_function(x, y);
6725 else /* finish element change */
6727 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6729 page = ChangePage[x][y];
6730 ChangePage[x][y] = -1;
6734 if (IS_MOVING(x, y) && !change->explode)
6736 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6739 ChangeDelay[x][y] = 1; /* try change after next move step */
6740 ChangePage[x][y] = page; /* remember page to use for change */
6745 if (ChangeElementNow(x, y, element, page))
6747 if (change->post_change_function)
6748 change->post_change_function(x, y);
6753 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6754 int trigger_element,
6760 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6763 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6765 int element = EL_CUSTOM_START + i;
6767 boolean change_element = FALSE;
6770 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6773 for (j = 0; j < element_info[element].num_change_pages; j++)
6775 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6777 if (change->can_change &&
6779 change->events & CH_EVENT_BIT(trigger_event) &&
6781 change->sides & trigger_side &&
6783 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6785 change->trigger_element == trigger_element
6790 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6791 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6792 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6795 change_element = TRUE;
6802 if (!change_element)
6805 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6808 if (x == lx && y == ly) /* do not change trigger element itself */
6812 if (Feld[x][y] == element)
6814 ChangeDelay[x][y] = 1;
6815 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6816 ChangeElement(x, y, page);
6824 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6827 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6831 static boolean CheckElementSideChange(int x, int y, int element, int side,
6832 int trigger_event, int page)
6834 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6837 if (Feld[x][y] == EL_BLOCKED)
6839 Blocked2Moving(x, y, &x, &y);
6840 element = Feld[x][y];
6846 boolean change_element = FALSE;
6849 for (i = 0; i < element_info[element].num_change_pages; i++)
6851 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6853 if (change->can_change &&
6854 change->events & CH_EVENT_BIT(trigger_event) &&
6855 change->sides & side)
6857 change_element = TRUE;
6864 if (!change_element)
6870 /* !!! this check misses pages with same event, but different side !!! */
6873 page = element_info[element].event_page_nr[trigger_event];
6875 if (!(element_info[element].change_page[page].sides & side))
6879 ChangeDelay[x][y] = 1;
6880 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6881 ChangeElement(x, y, page);
6886 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6888 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6891 static void PlayPlayerSound(struct PlayerInfo *player)
6893 int jx = player->jx, jy = player->jy;
6894 int element = player->element_nr;
6895 int last_action = player->last_action_waiting;
6896 int action = player->action_waiting;
6898 if (player->is_waiting)
6900 if (action != last_action)
6901 PlayLevelSoundElementAction(jx, jy, element, action);
6903 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6907 if (action != last_action)
6908 StopSound(element_info[element].sound[last_action]);
6910 if (last_action == ACTION_SLEEPING)
6911 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6915 static void PlayAllPlayersSound()
6919 for (i = 0; i < MAX_PLAYERS; i++)
6920 if (stored_player[i].active)
6921 PlayPlayerSound(&stored_player[i]);
6924 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6926 boolean last_waiting = player->is_waiting;
6927 int move_dir = player->MovDir;
6929 player->last_action_waiting = player->action_waiting;
6933 if (!last_waiting) /* not waiting -> waiting */
6935 player->is_waiting = TRUE;
6937 player->frame_counter_bored =
6939 game.player_boring_delay_fixed +
6940 SimpleRND(game.player_boring_delay_random);
6941 player->frame_counter_sleeping =
6943 game.player_sleeping_delay_fixed +
6944 SimpleRND(game.player_sleeping_delay_random);
6946 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6949 if (game.player_sleeping_delay_fixed +
6950 game.player_sleeping_delay_random > 0 &&
6951 player->anim_delay_counter == 0 &&
6952 player->post_delay_counter == 0 &&
6953 FrameCounter >= player->frame_counter_sleeping)
6954 player->is_sleeping = TRUE;
6955 else if (game.player_boring_delay_fixed +
6956 game.player_boring_delay_random > 0 &&
6957 FrameCounter >= player->frame_counter_bored)
6958 player->is_bored = TRUE;
6960 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6961 player->is_bored ? ACTION_BORING :
6964 if (player->is_sleeping)
6966 if (player->num_special_action_sleeping > 0)
6968 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6970 int last_special_action = player->special_action_sleeping;
6971 int num_special_action = player->num_special_action_sleeping;
6972 int special_action =
6973 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6974 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6975 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6976 last_special_action + 1 : ACTION_SLEEPING);
6977 int special_graphic =
6978 el_act_dir2img(player->element_nr, special_action, move_dir);
6980 player->anim_delay_counter =
6981 graphic_info[special_graphic].anim_delay_fixed +
6982 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6983 player->post_delay_counter =
6984 graphic_info[special_graphic].post_delay_fixed +
6985 SimpleRND(graphic_info[special_graphic].post_delay_random);
6987 player->special_action_sleeping = special_action;
6990 if (player->anim_delay_counter > 0)
6992 player->action_waiting = player->special_action_sleeping;
6993 player->anim_delay_counter--;
6995 else if (player->post_delay_counter > 0)
6997 player->post_delay_counter--;
7001 else if (player->is_bored)
7003 if (player->num_special_action_bored > 0)
7005 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7007 int special_action =
7008 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7009 int special_graphic =
7010 el_act_dir2img(player->element_nr, special_action, move_dir);
7012 player->anim_delay_counter =
7013 graphic_info[special_graphic].anim_delay_fixed +
7014 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7015 player->post_delay_counter =
7016 graphic_info[special_graphic].post_delay_fixed +
7017 SimpleRND(graphic_info[special_graphic].post_delay_random);
7019 player->special_action_bored = special_action;
7022 if (player->anim_delay_counter > 0)
7024 player->action_waiting = player->special_action_bored;
7025 player->anim_delay_counter--;
7027 else if (player->post_delay_counter > 0)
7029 player->post_delay_counter--;
7034 else if (last_waiting) /* waiting -> not waiting */
7036 player->is_waiting = FALSE;
7037 player->is_bored = FALSE;
7038 player->is_sleeping = FALSE;
7040 player->frame_counter_bored = -1;
7041 player->frame_counter_sleeping = -1;
7043 player->anim_delay_counter = 0;
7044 player->post_delay_counter = 0;
7046 player->action_waiting = ACTION_DEFAULT;
7048 player->special_action_bored = ACTION_DEFAULT;
7049 player->special_action_sleeping = ACTION_DEFAULT;
7054 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7057 static byte stored_player_action[MAX_PLAYERS];
7058 static int num_stored_actions = 0;
7060 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7061 int left = player_action & JOY_LEFT;
7062 int right = player_action & JOY_RIGHT;
7063 int up = player_action & JOY_UP;
7064 int down = player_action & JOY_DOWN;
7065 int button1 = player_action & JOY_BUTTON_1;
7066 int button2 = player_action & JOY_BUTTON_2;
7067 int dx = (left ? -1 : right ? 1 : 0);
7068 int dy = (up ? -1 : down ? 1 : 0);
7071 stored_player_action[player->index_nr] = 0;
7072 num_stored_actions++;
7076 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7079 if (!player->active || tape.pausing)
7083 printf("::: [%d %d %d %d] [%d %d]\n",
7084 left, right, up, down, button1, button2);
7090 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7094 snapped = SnapField(player, dx, dy);
7098 dropped = DropElement(player);
7100 moved = MovePlayer(player, dx, dy);
7103 if (tape.single_step && tape.recording && !tape.pausing)
7105 if (button1 || (dropped && !moved))
7107 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7108 SnapField(player, 0, 0); /* stop snapping */
7112 SetPlayerWaiting(player, FALSE);
7115 return player_action;
7117 stored_player_action[player->index_nr] = player_action;
7123 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7126 /* no actions for this player (no input at player's configured device) */
7128 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7129 SnapField(player, 0, 0);
7130 CheckGravityMovement(player);
7132 if (player->MovPos == 0)
7133 SetPlayerWaiting(player, TRUE);
7135 if (player->MovPos == 0) /* needed for tape.playing */
7136 player->is_moving = FALSE;
7138 player->is_dropping = FALSE;
7144 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7146 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7148 TapeRecordAction(stored_player_action);
7149 num_stored_actions = 0;
7156 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7158 static byte stored_player_action[MAX_PLAYERS];
7159 static int num_stored_actions = 0;
7160 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7161 int left = player_action & JOY_LEFT;
7162 int right = player_action & JOY_RIGHT;
7163 int up = player_action & JOY_UP;
7164 int down = player_action & JOY_DOWN;
7165 int button1 = player_action & JOY_BUTTON_1;
7166 int button2 = player_action & JOY_BUTTON_2;
7167 int dx = (left ? -1 : right ? 1 : 0);
7168 int dy = (up ? -1 : down ? 1 : 0);
7170 stored_player_action[player->index_nr] = 0;
7171 num_stored_actions++;
7173 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7175 if (!player->active || tape.pausing)
7180 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7183 snapped = SnapField(player, dx, dy);
7187 dropped = DropElement(player);
7189 moved = MovePlayer(player, dx, dy);
7192 if (tape.single_step && tape.recording && !tape.pausing)
7194 if (button1 || (dropped && !moved))
7196 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7197 SnapField(player, 0, 0); /* stop snapping */
7201 stored_player_action[player->index_nr] = player_action;
7205 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7207 /* no actions for this player (no input at player's configured device) */
7209 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7210 SnapField(player, 0, 0);
7211 CheckGravityMovement(player);
7213 if (player->MovPos == 0)
7214 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7216 if (player->MovPos == 0) /* needed for tape.playing */
7217 player->is_moving = FALSE;
7220 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7222 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7224 TapeRecordAction(stored_player_action);
7225 num_stored_actions = 0;
7232 static unsigned long action_delay = 0;
7233 unsigned long action_delay_value;
7234 int magic_wall_x = 0, magic_wall_y = 0;
7235 int i, x, y, element, graphic;
7236 byte *recorded_player_action;
7237 byte summarized_player_action = 0;
7239 byte tape_action[MAX_PLAYERS];
7242 if (game_status != GAME_MODE_PLAYING)
7245 action_delay_value =
7246 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7248 if (tape.playing && tape.index_search && !tape.pausing)
7249 action_delay_value = 0;
7251 /* ---------- main game synchronization point ---------- */
7253 WaitUntilDelayReached(&action_delay, action_delay_value);
7255 if (network_playing && !network_player_action_received)
7259 printf("DEBUG: try to get network player actions in time\n");
7263 #if defined(PLATFORM_UNIX)
7264 /* last chance to get network player actions without main loop delay */
7268 if (game_status != GAME_MODE_PLAYING)
7271 if (!network_player_action_received)
7275 printf("DEBUG: failed to get network player actions in time\n");
7286 printf("::: getting new tape action [%d]\n", FrameCounter);
7289 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7291 for (i = 0; i < MAX_PLAYERS; i++)
7293 summarized_player_action |= stored_player[i].action;
7295 if (!network_playing)
7296 stored_player[i].effective_action = stored_player[i].action;
7299 #if defined(PLATFORM_UNIX)
7300 if (network_playing)
7301 SendToServer_MovePlayer(summarized_player_action);
7304 if (!options.network && !setup.team_mode)
7305 local_player->effective_action = summarized_player_action;
7307 for (i = 0; i < MAX_PLAYERS; i++)
7309 int actual_player_action = stored_player[i].effective_action;
7311 if (stored_player[i].programmed_action)
7312 actual_player_action = stored_player[i].programmed_action;
7314 if (recorded_player_action)
7315 actual_player_action = recorded_player_action[i];
7317 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7319 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7320 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7322 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7327 TapeRecordAction(tape_action);
7330 network_player_action_received = FALSE;
7332 ScrollScreen(NULL, SCROLL_GO_ON);
7338 for (i = 0; i < MAX_PLAYERS; i++)
7339 stored_player[i].Frame++;
7343 /* for downwards compatibility, the following code emulates a fixed bug that
7344 occured when pushing elements (causing elements that just made their last
7345 pushing step to already (if possible) make their first falling step in the
7346 same game frame, which is bad); this code is also needed to use the famous
7347 "spring push bug" which is used in older levels and might be wanted to be
7348 used also in newer levels, but in this case the buggy pushing code is only
7349 affecting the "spring" element and no other elements */
7352 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7354 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7357 for (i = 0; i < MAX_PLAYERS; i++)
7359 struct PlayerInfo *player = &stored_player[i];
7364 if (player->active && player->is_pushing && player->is_moving &&
7366 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7367 Feld[x][y] == EL_SPRING))
7369 if (player->active && player->is_pushing && player->is_moving &&
7373 ContinueMoving(x, y);
7375 /* continue moving after pushing (this is actually a bug) */
7376 if (!IS_MOVING(x, y))
7385 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7387 Changed[x][y] = CE_BITMASK_DEFAULT;
7388 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7391 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7393 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7394 printf("GameActions(): This should never happen!\n");
7396 ChangePage[x][y] = -1;
7401 if (WasJustMoving[x][y] > 0)
7402 WasJustMoving[x][y]--;
7403 if (WasJustFalling[x][y] > 0)
7404 WasJustFalling[x][y]--;
7409 /* reset finished pushing action (not done in ContinueMoving() to allow
7410 continous pushing animation for elements with zero push delay) */
7411 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7413 ResetGfxAnimation(x, y);
7414 DrawLevelField(x, y);
7419 if (IS_BLOCKED(x, y))
7423 Blocked2Moving(x, y, &oldx, &oldy);
7424 if (!IS_MOVING(oldx, oldy))
7426 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7427 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7428 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7429 printf("GameActions(): This should never happen!\n");
7435 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7437 element = Feld[x][y];
7439 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7441 graphic = el2img(element);
7447 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7449 element = graphic = 0;
7453 if (graphic_info[graphic].anim_global_sync)
7454 GfxFrame[x][y] = FrameCounter;
7456 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7457 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7458 ResetRandomAnimationValue(x, y);
7460 SetRandomAnimationValue(x, y);
7463 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7466 if (IS_INACTIVE(element))
7468 if (IS_ANIMATED(graphic))
7469 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7475 /* this may take place after moving, so 'element' may have changed */
7477 if (IS_CHANGING(x, y))
7479 if (IS_CHANGING(x, y) &&
7480 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7484 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7485 element_info[element].event_page_nr[CE_DELAY]);
7487 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7490 element = Feld[x][y];
7491 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7495 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7500 element = Feld[x][y];
7501 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7503 if (element == EL_MOLE)
7504 printf("::: %d, %d, %d [%d]\n",
7505 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7509 if (element == EL_YAMYAM)
7510 printf("::: %d, %d, %d\n",
7511 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7515 if (IS_ANIMATED(graphic) &&
7519 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7522 if (element == EL_BUG)
7523 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7527 if (element == EL_MOLE)
7528 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7532 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7533 EdelsteinFunkeln(x, y);
7535 else if ((element == EL_ACID ||
7536 element == EL_EXIT_OPEN ||
7537 element == EL_SP_EXIT_OPEN ||
7538 element == EL_SP_TERMINAL ||
7539 element == EL_SP_TERMINAL_ACTIVE ||
7540 element == EL_EXTRA_TIME ||
7541 element == EL_SHIELD_NORMAL ||
7542 element == EL_SHIELD_DEADLY) &&
7543 IS_ANIMATED(graphic))
7544 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7545 else if (IS_MOVING(x, y))
7546 ContinueMoving(x, y);
7547 else if (IS_ACTIVE_BOMB(element))
7548 CheckDynamite(x, y);
7550 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7551 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7553 else if (element == EL_AMOEBA_GROWING)
7554 AmoebeWaechst(x, y);
7555 else if (element == EL_AMOEBA_SHRINKING)
7556 AmoebaDisappearing(x, y);
7558 #if !USE_NEW_AMOEBA_CODE
7559 else if (IS_AMOEBALIVE(element))
7560 AmoebeAbleger(x, y);
7563 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7565 else if (element == EL_EXIT_CLOSED)
7567 else if (element == EL_SP_EXIT_CLOSED)
7569 else if (element == EL_EXPANDABLE_WALL_GROWING)
7571 else if (element == EL_EXPANDABLE_WALL ||
7572 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7573 element == EL_EXPANDABLE_WALL_VERTICAL ||
7574 element == EL_EXPANDABLE_WALL_ANY)
7576 else if (element == EL_FLAMES)
7577 CheckForDragon(x, y);
7579 else if (IS_AUTO_CHANGING(element))
7580 ChangeElement(x, y);
7582 else if (element == EL_EXPLOSION)
7583 ; /* drawing of correct explosion animation is handled separately */
7584 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7585 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7588 /* this may take place after moving, so 'element' may have changed */
7589 if (IS_AUTO_CHANGING(Feld[x][y]))
7590 ChangeElement(x, y);
7593 if (IS_BELT_ACTIVE(element))
7594 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7596 if (game.magic_wall_active)
7598 int jx = local_player->jx, jy = local_player->jy;
7600 /* play the element sound at the position nearest to the player */
7601 if ((element == EL_MAGIC_WALL_FULL ||
7602 element == EL_MAGIC_WALL_ACTIVE ||
7603 element == EL_MAGIC_WALL_EMPTYING ||
7604 element == EL_BD_MAGIC_WALL_FULL ||
7605 element == EL_BD_MAGIC_WALL_ACTIVE ||
7606 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7607 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7615 #if USE_NEW_AMOEBA_CODE
7616 /* new experimental amoeba growth stuff */
7618 if (!(FrameCounter % 8))
7621 static unsigned long random = 1684108901;
7623 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7626 x = (random >> 10) % lev_fieldx;
7627 y = (random >> 20) % lev_fieldy;
7629 x = RND(lev_fieldx);
7630 y = RND(lev_fieldy);
7632 element = Feld[x][y];
7634 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7635 if (!IS_PLAYER(x,y) &&
7636 (element == EL_EMPTY ||
7637 element == EL_SAND ||
7638 element == EL_QUICKSAND_EMPTY ||
7639 element == EL_ACID_SPLASH_LEFT ||
7640 element == EL_ACID_SPLASH_RIGHT))
7642 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7643 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7644 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7645 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7646 Feld[x][y] = EL_AMOEBA_DROP;
7649 random = random * 129 + 1;
7655 if (game.explosions_delayed)
7658 game.explosions_delayed = FALSE;
7660 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7662 element = Feld[x][y];
7664 if (ExplodeField[x][y])
7665 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7666 else if (element == EL_EXPLOSION)
7667 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7669 ExplodeField[x][y] = EX_NO_EXPLOSION;
7672 game.explosions_delayed = TRUE;
7675 if (game.magic_wall_active)
7677 if (!(game.magic_wall_time_left % 4))
7679 int element = Feld[magic_wall_x][magic_wall_y];
7681 if (element == EL_BD_MAGIC_WALL_FULL ||
7682 element == EL_BD_MAGIC_WALL_ACTIVE ||
7683 element == EL_BD_MAGIC_WALL_EMPTYING)
7684 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7686 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7689 if (game.magic_wall_time_left > 0)
7691 game.magic_wall_time_left--;
7692 if (!game.magic_wall_time_left)
7694 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7696 element = Feld[x][y];
7698 if (element == EL_MAGIC_WALL_ACTIVE ||
7699 element == EL_MAGIC_WALL_FULL)
7701 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7702 DrawLevelField(x, y);
7704 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7705 element == EL_BD_MAGIC_WALL_FULL)
7707 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7708 DrawLevelField(x, y);
7712 game.magic_wall_active = FALSE;
7717 if (game.light_time_left > 0)
7719 game.light_time_left--;
7721 if (game.light_time_left == 0)
7722 RedrawAllLightSwitchesAndInvisibleElements();
7725 if (game.timegate_time_left > 0)
7727 game.timegate_time_left--;
7729 if (game.timegate_time_left == 0)
7730 CloseAllOpenTimegates();
7733 for (i = 0; i < MAX_PLAYERS; i++)
7735 struct PlayerInfo *player = &stored_player[i];
7737 if (SHIELD_ON(player))
7739 if (player->shield_deadly_time_left)
7740 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7741 else if (player->shield_normal_time_left)
7742 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7746 if (TimeFrames >= FRAMES_PER_SECOND)
7751 for (i = 0; i < MAX_PLAYERS; i++)
7753 struct PlayerInfo *player = &stored_player[i];
7755 if (SHIELD_ON(player))
7757 player->shield_normal_time_left--;
7759 if (player->shield_deadly_time_left > 0)
7760 player->shield_deadly_time_left--;
7764 if (tape.recording || tape.playing)
7765 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7771 if (TimeLeft <= 10 && setup.time_limit)
7772 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7774 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7776 if (!TimeLeft && setup.time_limit)
7777 for (i = 0; i < MAX_PLAYERS; i++)
7778 KillHero(&stored_player[i]);
7780 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7781 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7785 PlayAllPlayersSound();
7787 if (options.debug) /* calculate frames per second */
7789 static unsigned long fps_counter = 0;
7790 static int fps_frames = 0;
7791 unsigned long fps_delay_ms = Counter() - fps_counter;
7795 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7797 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7800 fps_counter = Counter();
7803 redraw_mask |= REDRAW_FPS;
7807 if (stored_player[0].jx != stored_player[0].last_jx ||
7808 stored_player[0].jy != stored_player[0].last_jy)
7809 printf("::: %d, %d, %d, %d, %d\n",
7810 stored_player[0].MovDir,
7811 stored_player[0].MovPos,
7812 stored_player[0].GfxPos,
7813 stored_player[0].Frame,
7814 stored_player[0].StepFrame);
7821 for (i = 0; i < MAX_PLAYERS; i++)
7824 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7826 stored_player[i].Frame += move_frames;
7828 if (stored_player[i].MovPos != 0)
7829 stored_player[i].StepFrame += move_frames;
7831 if (stored_player[i].drop_delay > 0)
7832 stored_player[i].drop_delay--;
7837 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7839 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7841 local_player->show_envelope = 0;
7846 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7848 int min_x = x, min_y = y, max_x = x, max_y = y;
7851 for (i = 0; i < MAX_PLAYERS; i++)
7853 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7855 if (!stored_player[i].active || &stored_player[i] == player)
7858 min_x = MIN(min_x, jx);
7859 min_y = MIN(min_y, jy);
7860 max_x = MAX(max_x, jx);
7861 max_y = MAX(max_y, jy);
7864 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7867 static boolean AllPlayersInVisibleScreen()
7871 for (i = 0; i < MAX_PLAYERS; i++)
7873 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7875 if (!stored_player[i].active)
7878 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7885 void ScrollLevel(int dx, int dy)
7887 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7890 BlitBitmap(drawto_field, drawto_field,
7891 FX + TILEX * (dx == -1) - softscroll_offset,
7892 FY + TILEY * (dy == -1) - softscroll_offset,
7893 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7894 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7895 FX + TILEX * (dx == 1) - softscroll_offset,
7896 FY + TILEY * (dy == 1) - softscroll_offset);
7900 x = (dx == 1 ? BX1 : BX2);
7901 for (y = BY1; y <= BY2; y++)
7902 DrawScreenField(x, y);
7907 y = (dy == 1 ? BY1 : BY2);
7908 for (x = BX1; x <= BX2; x++)
7909 DrawScreenField(x, y);
7912 redraw_mask |= REDRAW_FIELD;
7915 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7917 int nextx = x + dx, nexty = y + dy;
7918 int element = Feld[x][y];
7921 element != EL_SP_PORT_LEFT &&
7922 element != EL_SP_GRAVITY_PORT_LEFT &&
7923 element != EL_SP_PORT_HORIZONTAL &&
7924 element != EL_SP_PORT_ANY) ||
7926 element != EL_SP_PORT_RIGHT &&
7927 element != EL_SP_GRAVITY_PORT_RIGHT &&
7928 element != EL_SP_PORT_HORIZONTAL &&
7929 element != EL_SP_PORT_ANY) ||
7931 element != EL_SP_PORT_UP &&
7932 element != EL_SP_GRAVITY_PORT_UP &&
7933 element != EL_SP_PORT_VERTICAL &&
7934 element != EL_SP_PORT_ANY) ||
7936 element != EL_SP_PORT_DOWN &&
7937 element != EL_SP_GRAVITY_PORT_DOWN &&
7938 element != EL_SP_PORT_VERTICAL &&
7939 element != EL_SP_PORT_ANY) ||
7940 !IN_LEV_FIELD(nextx, nexty) ||
7941 !IS_FREE(nextx, nexty))
7947 static void CheckGravityMovement(struct PlayerInfo *player)
7949 if (game.gravity && !player->programmed_action)
7951 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7952 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7954 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7955 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7956 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7957 int jx = player->jx, jy = player->jy;
7958 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7959 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7960 int new_jx = jx + dx, new_jy = jy + dy;
7961 boolean field_under_player_is_free =
7962 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7963 boolean player_is_moving_to_valid_field =
7964 (IN_LEV_FIELD(new_jx, new_jy) &&
7965 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7966 Feld[new_jx][new_jy] == EL_SAND ||
7967 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7968 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7969 /* !!! extend EL_SAND to anything diggable !!! */
7971 boolean player_is_standing_on_valid_field =
7972 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
7973 (IS_WALKABLE(Feld[jx][jy]) &&
7974 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
7976 if (field_under_player_is_free &&
7977 !player_is_standing_on_valid_field &&
7978 !player_is_moving_to_valid_field)
7979 player->programmed_action = MV_DOWN;
7985 -----------------------------------------------------------------------------
7986 dx, dy: direction (non-diagonal) to try to move the player to
7987 real_dx, real_dy: direction as read from input device (can be diagonal)
7990 boolean MovePlayerOneStep(struct PlayerInfo *player,
7991 int dx, int dy, int real_dx, int real_dy)
7994 static int change_sides[4][2] =
7996 /* enter side leave side */
7997 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7998 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7999 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8000 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8002 int move_direction = (dx == -1 ? MV_LEFT :
8003 dx == +1 ? MV_RIGHT :
8005 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8006 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8007 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8009 int jx = player->jx, jy = player->jy;
8010 int new_jx = jx + dx, new_jy = jy + dy;
8014 if (!player->active || (!dx && !dy))
8015 return MF_NO_ACTION;
8017 player->MovDir = (dx < 0 ? MV_LEFT :
8020 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8022 if (!IN_LEV_FIELD(new_jx, new_jy))
8023 return MF_NO_ACTION;
8025 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8026 return MF_NO_ACTION;
8029 element = MovingOrBlocked2Element(new_jx, new_jy);
8031 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8034 if (DONT_RUN_INTO(element))
8036 if (element == EL_ACID && dx == 0 && dy == 1)
8038 SplashAcid(new_jx, new_jy);
8039 Feld[jx][jy] = EL_PLAYER_1;
8040 InitMovingField(jx, jy, MV_DOWN);
8041 Store[jx][jy] = EL_ACID;
8042 ContinueMoving(jx, jy);
8046 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8051 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8052 if (can_move != MF_MOVING)
8055 /* check if DigField() has caused relocation of the player */
8056 if (player->jx != jx || player->jy != jy)
8057 return MF_NO_ACTION;
8059 StorePlayer[jx][jy] = 0;
8060 player->last_jx = jx;
8061 player->last_jy = jy;
8062 player->jx = new_jx;
8063 player->jy = new_jy;
8064 StorePlayer[new_jx][new_jy] = player->element_nr;
8067 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8069 player->step_counter++;
8071 player->drop_delay = 0;
8073 PlayerVisit[jx][jy] = FrameCounter;
8075 ScrollPlayer(player, SCROLL_INIT);
8078 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8080 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8081 CE_OTHER_GETS_LEFT);
8082 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8083 CE_LEFT_BY_PLAYER, -1);
8086 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8088 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
8089 enter_side, CE_OTHER_GETS_ENTERED);
8090 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
8091 CE_ENTERED_BY_PLAYER, -1);
8098 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8100 int jx = player->jx, jy = player->jy;
8101 int old_jx = jx, old_jy = jy;
8102 int moved = MF_NO_ACTION;
8105 if (!player->active)
8110 if (player->MovPos == 0)
8112 player->is_moving = FALSE;
8113 player->is_digging = FALSE;
8114 player->is_collecting = FALSE;
8115 player->is_snapping = FALSE;
8116 player->is_pushing = FALSE;
8122 if (!player->active || (!dx && !dy))
8127 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8131 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8132 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8136 /* remove the last programmed player action */
8137 player->programmed_action = 0;
8141 /* should only happen if pre-1.2 tape recordings are played */
8142 /* this is only for backward compatibility */
8144 int original_move_delay_value = player->move_delay_value;
8147 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8151 /* scroll remaining steps with finest movement resolution */
8152 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8154 while (player->MovPos)
8156 ScrollPlayer(player, SCROLL_GO_ON);
8157 ScrollScreen(NULL, SCROLL_GO_ON);
8163 player->move_delay_value = original_move_delay_value;
8166 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8168 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8169 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8173 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8174 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8180 if (moved & MF_MOVING && !ScreenMovPos &&
8181 (player == local_player || !options.network))
8183 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8184 int offset = (setup.scroll_delay ? 3 : 0);
8186 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8188 /* actual player has left the screen -- scroll in that direction */
8189 if (jx != old_jx) /* player has moved horizontally */
8190 scroll_x += (jx - old_jx);
8191 else /* player has moved vertically */
8192 scroll_y += (jy - old_jy);
8196 if (jx != old_jx) /* player has moved horizontally */
8198 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8199 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8200 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8202 /* don't scroll over playfield boundaries */
8203 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8204 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8206 /* don't scroll more than one field at a time */
8207 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8209 /* don't scroll against the player's moving direction */
8210 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8211 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8212 scroll_x = old_scroll_x;
8214 else /* player has moved vertically */
8216 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8217 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8218 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8220 /* don't scroll over playfield boundaries */
8221 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8222 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8224 /* don't scroll more than one field at a time */
8225 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8227 /* don't scroll against the player's moving direction */
8228 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8229 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8230 scroll_y = old_scroll_y;
8234 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8236 if (!options.network && !AllPlayersInVisibleScreen())
8238 scroll_x = old_scroll_x;
8239 scroll_y = old_scroll_y;
8243 ScrollScreen(player, SCROLL_INIT);
8244 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8251 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8253 if (!(moved & MF_MOVING) && !player->is_pushing)
8258 player->StepFrame = 0;
8260 if (moved & MF_MOVING)
8262 if (old_jx != jx && old_jy == jy)
8263 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8264 else if (old_jx == jx && old_jy != jy)
8265 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8267 DrawLevelField(jx, jy); /* for "crumbled sand" */
8269 player->last_move_dir = player->MovDir;
8270 player->is_moving = TRUE;
8272 player->is_snapping = FALSE;
8276 player->is_switching = FALSE;
8279 player->is_dropping = FALSE;
8284 static int change_sides[4][2] =
8286 /* enter side leave side */
8287 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8288 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8289 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8290 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8292 int move_direction = player->MovDir;
8293 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8294 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8297 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8299 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8300 leave_side, CE_OTHER_GETS_LEFT);
8301 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8302 leave_side, CE_LEFT_BY_PLAYER, -1);
8305 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8307 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8308 enter_side, CE_OTHER_GETS_ENTERED);
8309 CheckElementSideChange(jx, jy, Feld[jx][jy],
8310 enter_side, CE_ENTERED_BY_PLAYER, -1);
8321 CheckGravityMovement(player);
8324 player->last_move_dir = MV_NO_MOVING;
8326 player->is_moving = FALSE;
8329 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8331 TestIfHeroTouchesBadThing(jx, jy);
8332 TestIfPlayerTouchesCustomElement(jx, jy);
8335 if (!player->active)
8341 void ScrollPlayer(struct PlayerInfo *player, int mode)
8343 int jx = player->jx, jy = player->jy;
8344 int last_jx = player->last_jx, last_jy = player->last_jy;
8345 int move_stepsize = TILEX / player->move_delay_value;
8347 if (!player->active || !player->MovPos)
8350 if (mode == SCROLL_INIT)
8352 player->actual_frame_counter = FrameCounter;
8353 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8355 if (Feld[last_jx][last_jy] == EL_EMPTY)
8356 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8364 else if (!FrameReached(&player->actual_frame_counter, 1))
8367 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8368 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8370 if (!player->block_last_field &&
8371 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8372 Feld[last_jx][last_jy] = EL_EMPTY;
8374 /* before DrawPlayer() to draw correct player graphic for this case */
8375 if (player->MovPos == 0)
8376 CheckGravityMovement(player);
8379 DrawPlayer(player); /* needed here only to cleanup last field */
8382 if (player->MovPos == 0) /* player reached destination field */
8385 if (player->move_delay_reset_counter > 0)
8387 player->move_delay_reset_counter--;
8389 if (player->move_delay_reset_counter == 0)
8391 /* continue with normal speed after quickly moving through gate */
8392 HALVE_PLAYER_SPEED(player);
8394 /* be able to make the next move without delay */
8395 player->move_delay = 0;
8399 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8401 /* continue with normal speed after quickly moving through gate */
8402 HALVE_PLAYER_SPEED(player);
8404 /* be able to make the next move without delay */
8405 player->move_delay = 0;
8409 if (player->block_last_field &&
8410 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8411 Feld[last_jx][last_jy] = EL_EMPTY;
8413 player->last_jx = jx;
8414 player->last_jy = jy;
8416 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8417 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8418 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8420 DrawPlayer(player); /* needed here only to cleanup last field */
8423 if (local_player->friends_still_needed == 0 ||
8424 IS_SP_ELEMENT(Feld[jx][jy]))
8425 player->LevelSolved = player->GameOver = TRUE;
8428 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8430 TestIfHeroTouchesBadThing(jx, jy);
8431 TestIfPlayerTouchesCustomElement(jx, jy);
8433 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8436 if (!player->active)
8440 if (tape.single_step && tape.recording && !tape.pausing &&
8441 !player->programmed_action)
8442 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8446 void ScrollScreen(struct PlayerInfo *player, int mode)
8448 static unsigned long screen_frame_counter = 0;
8450 if (mode == SCROLL_INIT)
8452 /* set scrolling step size according to actual player's moving speed */
8453 ScrollStepSize = TILEX / player->move_delay_value;
8455 screen_frame_counter = FrameCounter;
8456 ScreenMovDir = player->MovDir;
8457 ScreenMovPos = player->MovPos;
8458 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8461 else if (!FrameReached(&screen_frame_counter, 1))
8466 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8467 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8468 redraw_mask |= REDRAW_FIELD;
8471 ScreenMovDir = MV_NO_MOVING;
8474 void TestIfPlayerTouchesCustomElement(int x, int y)
8476 static int xy[4][2] =
8483 static int change_sides[4][2] =
8485 /* center side border side */
8486 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8487 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8488 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8489 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8491 static int touch_dir[4] =
8498 int center_element = Feld[x][y]; /* should always be non-moving! */
8501 for (i = 0; i < 4; i++)
8503 int xx = x + xy[i][0];
8504 int yy = y + xy[i][1];
8505 int center_side = change_sides[i][0];
8506 int border_side = change_sides[i][1];
8509 if (!IN_LEV_FIELD(xx, yy))
8512 if (IS_PLAYER(x, y))
8514 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8515 border_element = Feld[xx][yy]; /* may be moving! */
8516 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8517 border_element = Feld[xx][yy];
8518 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8519 border_element = MovingOrBlocked2Element(xx, yy);
8521 continue; /* center and border element do not touch */
8523 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8524 CE_OTHER_GETS_TOUCHED);
8525 CheckElementSideChange(xx, yy, border_element, border_side,
8526 CE_TOUCHED_BY_PLAYER, -1);
8528 else if (IS_PLAYER(xx, yy))
8530 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8532 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8534 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8535 continue; /* center and border element do not touch */
8538 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8539 CE_OTHER_GETS_TOUCHED);
8540 CheckElementSideChange(x, y, center_element, center_side,
8541 CE_TOUCHED_BY_PLAYER, -1);
8548 void TestIfElementTouchesCustomElement(int x, int y)
8550 static int xy[4][2] =
8557 static int change_sides[4][2] =
8559 /* center side border side */
8560 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8561 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8562 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8563 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8565 static int touch_dir[4] =
8572 boolean change_center_element = FALSE;
8573 int center_element_change_page = 0;
8574 int center_element = Feld[x][y]; /* should always be non-moving! */
8577 for (i = 0; i < 4; i++)
8579 int xx = x + xy[i][0];
8580 int yy = y + xy[i][1];
8581 int center_side = change_sides[i][0];
8582 int border_side = change_sides[i][1];
8585 if (!IN_LEV_FIELD(xx, yy))
8588 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8589 border_element = Feld[xx][yy]; /* may be moving! */
8590 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8591 border_element = Feld[xx][yy];
8592 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8593 border_element = MovingOrBlocked2Element(xx, yy);
8595 continue; /* center and border element do not touch */
8597 /* check for change of center element (but change it only once) */
8598 if (IS_CUSTOM_ELEMENT(center_element) &&
8599 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8600 !change_center_element)
8602 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8604 struct ElementChangeInfo *change =
8605 &element_info[center_element].change_page[j];
8607 if (change->can_change &&
8608 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8609 change->sides & border_side &&
8611 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8613 change->trigger_element == border_element
8617 change_center_element = TRUE;
8618 center_element_change_page = j;
8625 /* check for change of border element */
8626 if (IS_CUSTOM_ELEMENT(border_element) &&
8627 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8629 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8631 struct ElementChangeInfo *change =
8632 &element_info[border_element].change_page[j];
8634 if (change->can_change &&
8635 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8636 change->sides & center_side &&
8638 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8640 change->trigger_element == center_element
8644 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8645 CE_OTHER_IS_TOUCHING, j);
8652 if (change_center_element)
8653 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8654 CE_OTHER_IS_TOUCHING, center_element_change_page);
8657 void TestIfElementHitsCustomElement(int x, int y, int direction)
8659 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8660 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8661 int hitx = x + dx, hity = y + dy;
8662 int hitting_element = Feld[x][y];
8664 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8665 !IS_FREE(hitx, hity) &&
8666 (!IS_MOVING(hitx, hity) ||
8667 MovDir[hitx][hity] != direction ||
8668 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8671 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8675 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8679 CheckElementSideChange(x, y, hitting_element,
8680 direction, CE_HITTING_SOMETHING, -1);
8682 if (IN_LEV_FIELD(hitx, hity))
8684 int opposite_direction = MV_DIR_OPPOSITE(direction);
8685 int hitting_side = direction;
8686 int touched_side = opposite_direction;
8687 int touched_element = MovingOrBlocked2Element(hitx, hity);
8689 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8690 MovDir[hitx][hity] != direction ||
8691 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8700 CheckElementSideChange(hitx, hity, touched_element,
8701 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8703 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8704 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8706 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8708 struct ElementChangeInfo *change =
8709 &element_info[hitting_element].change_page[i];
8711 if (change->can_change &&
8712 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8713 change->sides & touched_side &&
8716 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8718 change->trigger_element == touched_element
8722 CheckElementSideChange(x, y, hitting_element,
8723 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8729 if (IS_CUSTOM_ELEMENT(touched_element) &&
8730 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8732 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8734 struct ElementChangeInfo *change =
8735 &element_info[touched_element].change_page[i];
8737 if (change->can_change &&
8738 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8739 change->sides & hitting_side &&
8741 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8743 change->trigger_element == hitting_element
8747 CheckElementSideChange(hitx, hity, touched_element,
8748 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8757 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8759 int i, kill_x = -1, kill_y = -1;
8760 static int test_xy[4][2] =
8767 static int test_dir[4] =
8775 for (i = 0; i < 4; i++)
8777 int test_x, test_y, test_move_dir, test_element;
8779 test_x = good_x + test_xy[i][0];
8780 test_y = good_y + test_xy[i][1];
8781 if (!IN_LEV_FIELD(test_x, test_y))
8785 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8788 test_element = Feld[test_x][test_y];
8790 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8793 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8794 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8796 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8797 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8805 if (kill_x != -1 || kill_y != -1)
8807 if (IS_PLAYER(good_x, good_y))
8809 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8811 if (player->shield_deadly_time_left > 0)
8812 Bang(kill_x, kill_y);
8813 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8817 Bang(good_x, good_y);
8821 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8823 int i, kill_x = -1, kill_y = -1;
8824 int bad_element = Feld[bad_x][bad_y];
8825 static int test_xy[4][2] =
8832 static int touch_dir[4] =
8839 static int test_dir[4] =
8847 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8850 for (i = 0; i < 4; i++)
8852 int test_x, test_y, test_move_dir, test_element;
8854 test_x = bad_x + test_xy[i][0];
8855 test_y = bad_y + test_xy[i][1];
8856 if (!IN_LEV_FIELD(test_x, test_y))
8860 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8862 test_element = Feld[test_x][test_y];
8864 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8865 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8867 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8868 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8870 /* good thing is player or penguin that does not move away */
8871 if (IS_PLAYER(test_x, test_y))
8873 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8875 if (bad_element == EL_ROBOT && player->is_moving)
8876 continue; /* robot does not kill player if he is moving */
8878 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8880 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8881 continue; /* center and border element do not touch */
8888 else if (test_element == EL_PENGUIN)
8897 if (kill_x != -1 || kill_y != -1)
8899 if (IS_PLAYER(kill_x, kill_y))
8901 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8903 if (player->shield_deadly_time_left > 0)
8905 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8909 Bang(kill_x, kill_y);
8913 void TestIfHeroTouchesBadThing(int x, int y)
8915 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8918 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8920 TestIfGoodThingHitsBadThing(x, y, move_dir);
8923 void TestIfBadThingTouchesHero(int x, int y)
8925 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8928 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8930 TestIfBadThingHitsGoodThing(x, y, move_dir);
8933 void TestIfFriendTouchesBadThing(int x, int y)
8935 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8938 void TestIfBadThingTouchesFriend(int x, int y)
8940 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8943 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8945 int i, kill_x = bad_x, kill_y = bad_y;
8946 static int xy[4][2] =
8954 for (i = 0; i < 4; i++)
8958 x = bad_x + xy[i][0];
8959 y = bad_y + xy[i][1];
8960 if (!IN_LEV_FIELD(x, y))
8963 element = Feld[x][y];
8964 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8965 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8973 if (kill_x != bad_x || kill_y != bad_y)
8977 void KillHero(struct PlayerInfo *player)
8979 int jx = player->jx, jy = player->jy;
8981 if (!player->active)
8984 /* remove accessible field at the player's position */
8985 Feld[jx][jy] = EL_EMPTY;
8987 /* deactivate shield (else Bang()/Explode() would not work right) */
8988 player->shield_normal_time_left = 0;
8989 player->shield_deadly_time_left = 0;
8995 static void KillHeroUnlessEnemyProtected(int x, int y)
8997 if (!PLAYER_ENEMY_PROTECTED(x, y))
8998 KillHero(PLAYERINFO(x, y));
9001 static void KillHeroUnlessExplosionProtected(int x, int y)
9003 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9004 KillHero(PLAYERINFO(x, y));
9007 void BuryHero(struct PlayerInfo *player)
9009 int jx = player->jx, jy = player->jy;
9011 if (!player->active)
9015 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9017 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9019 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9021 player->GameOver = TRUE;
9025 void RemoveHero(struct PlayerInfo *player)
9027 int jx = player->jx, jy = player->jy;
9028 int i, found = FALSE;
9030 player->present = FALSE;
9031 player->active = FALSE;
9033 if (!ExplodeField[jx][jy])
9034 StorePlayer[jx][jy] = 0;
9036 for (i = 0; i < MAX_PLAYERS; i++)
9037 if (stored_player[i].active)
9041 AllPlayersGone = TRUE;
9048 =============================================================================
9049 checkDiagonalPushing()
9050 -----------------------------------------------------------------------------
9051 check if diagonal input device direction results in pushing of object
9052 (by checking if the alternative direction is walkable, diggable, ...)
9053 =============================================================================
9056 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9057 int x, int y, int real_dx, int real_dy)
9059 int jx, jy, dx, dy, xx, yy;
9061 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9064 /* diagonal direction: check alternative direction */
9069 xx = jx + (dx == 0 ? real_dx : 0);
9070 yy = jy + (dy == 0 ? real_dy : 0);
9072 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9076 =============================================================================
9078 -----------------------------------------------------------------------------
9079 x, y: field next to player (non-diagonal) to try to dig to
9080 real_dx, real_dy: direction as read from input device (can be diagonal)
9081 =============================================================================
9084 int DigField(struct PlayerInfo *player,
9085 int oldx, int oldy, int x, int y,
9086 int real_dx, int real_dy, int mode)
9088 static int change_sides[4] =
9090 CH_SIDE_RIGHT, /* moving left */
9091 CH_SIDE_LEFT, /* moving right */
9092 CH_SIDE_BOTTOM, /* moving up */
9093 CH_SIDE_TOP, /* moving down */
9096 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9098 int jx = oldx, jy = oldy;
9099 int dx = x - jx, dy = y - jy;
9100 int nextx = x + dx, nexty = y + dy;
9101 int move_direction = (dx == -1 ? MV_LEFT :
9102 dx == +1 ? MV_RIGHT :
9104 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9105 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9106 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
9107 int old_element = Feld[jx][jy];
9110 if (player->MovPos == 0)
9112 player->is_digging = FALSE;
9113 player->is_collecting = FALSE;
9116 if (player->MovPos == 0) /* last pushing move finished */
9117 player->is_pushing = FALSE;
9119 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9121 player->is_switching = FALSE;
9122 player->push_delay = 0;
9124 return MF_NO_ACTION;
9127 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9128 return MF_NO_ACTION;
9133 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9135 if (IS_TUBE(Feld[jx][jy]) ||
9136 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9140 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9141 int tube_leave_directions[][2] =
9143 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9144 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9145 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9146 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9147 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9148 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9149 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9150 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9151 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9152 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9153 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9154 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9157 while (tube_leave_directions[i][0] != tube_element)
9160 if (tube_leave_directions[i][0] == -1) /* should not happen */
9164 if (!(tube_leave_directions[i][1] & move_direction))
9165 return MF_NO_ACTION; /* tube has no opening in this direction */
9170 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9171 old_element = Back[jx][jy];
9175 if (IS_WALKABLE(old_element) &&
9176 !(element_info[old_element].access_direction & move_direction))
9177 return MF_NO_ACTION; /* field has no opening in this direction */
9179 element = Feld[x][y];
9181 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9182 game.engine_version >= VERSION_IDENT(2,2,0,0))
9183 return MF_NO_ACTION;
9187 case EL_SP_PORT_LEFT:
9188 case EL_SP_PORT_RIGHT:
9190 case EL_SP_PORT_DOWN:
9191 case EL_SP_PORT_HORIZONTAL:
9192 case EL_SP_PORT_VERTICAL:
9193 case EL_SP_PORT_ANY:
9194 case EL_SP_GRAVITY_PORT_LEFT:
9195 case EL_SP_GRAVITY_PORT_RIGHT:
9196 case EL_SP_GRAVITY_PORT_UP:
9197 case EL_SP_GRAVITY_PORT_DOWN:
9199 if (!canEnterSupaplexPort(x, y, dx, dy))
9200 return MF_NO_ACTION;
9203 element != EL_SP_PORT_LEFT &&
9204 element != EL_SP_GRAVITY_PORT_LEFT &&
9205 element != EL_SP_PORT_HORIZONTAL &&
9206 element != EL_SP_PORT_ANY) ||
9208 element != EL_SP_PORT_RIGHT &&
9209 element != EL_SP_GRAVITY_PORT_RIGHT &&
9210 element != EL_SP_PORT_HORIZONTAL &&
9211 element != EL_SP_PORT_ANY) ||
9213 element != EL_SP_PORT_UP &&
9214 element != EL_SP_GRAVITY_PORT_UP &&
9215 element != EL_SP_PORT_VERTICAL &&
9216 element != EL_SP_PORT_ANY) ||
9218 element != EL_SP_PORT_DOWN &&
9219 element != EL_SP_GRAVITY_PORT_DOWN &&
9220 element != EL_SP_PORT_VERTICAL &&
9221 element != EL_SP_PORT_ANY) ||
9222 !IN_LEV_FIELD(nextx, nexty) ||
9223 !IS_FREE(nextx, nexty))
9224 return MF_NO_ACTION;
9227 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9228 element == EL_SP_GRAVITY_PORT_RIGHT ||
9229 element == EL_SP_GRAVITY_PORT_UP ||
9230 element == EL_SP_GRAVITY_PORT_DOWN)
9231 game.gravity = !game.gravity;
9233 /* automatically move to the next field with double speed */
9234 player->programmed_action = move_direction;
9236 if (player->move_delay_reset_counter == 0)
9238 player->move_delay_reset_counter = 2; /* two double speed steps */
9240 DOUBLE_PLAYER_SPEED(player);
9243 player->move_delay_reset_counter = 2;
9245 DOUBLE_PLAYER_SPEED(player);
9248 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9253 case EL_TUBE_VERTICAL:
9254 case EL_TUBE_HORIZONTAL:
9255 case EL_TUBE_VERTICAL_LEFT:
9256 case EL_TUBE_VERTICAL_RIGHT:
9257 case EL_TUBE_HORIZONTAL_UP:
9258 case EL_TUBE_HORIZONTAL_DOWN:
9259 case EL_TUBE_LEFT_UP:
9260 case EL_TUBE_LEFT_DOWN:
9261 case EL_TUBE_RIGHT_UP:
9262 case EL_TUBE_RIGHT_DOWN:
9265 int tube_enter_directions[][2] =
9267 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9268 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9269 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9270 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9271 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9272 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9273 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9274 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9275 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9276 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9277 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9278 { -1, MV_NO_MOVING }
9281 while (tube_enter_directions[i][0] != element)
9284 if (tube_enter_directions[i][0] == -1) /* should not happen */
9288 if (!(tube_enter_directions[i][1] & move_direction))
9289 return MF_NO_ACTION; /* tube has no opening in this direction */
9291 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9298 if (IS_WALKABLE(element))
9300 int sound_action = ACTION_WALKING;
9302 if (!(element_info[element].access_direction & opposite_direction))
9303 return MF_NO_ACTION; /* field not accessible from this direction */
9305 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9307 if (!player->key[element - EL_GATE_1])
9308 return MF_NO_ACTION;
9310 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9312 if (!player->key[element - EL_GATE_1_GRAY])
9313 return MF_NO_ACTION;
9315 else if (element == EL_EXIT_OPEN ||
9316 element == EL_SP_EXIT_OPEN ||
9317 element == EL_SP_EXIT_OPENING)
9319 sound_action = ACTION_PASSING; /* player is passing exit */
9321 else if (element == EL_EMPTY)
9323 sound_action = ACTION_MOVING; /* nothing to walk on */
9326 /* play sound from background or player, whatever is available */
9327 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9328 PlayLevelSoundElementAction(x, y, element, sound_action);
9330 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9334 else if (IS_PASSABLE(element))
9336 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9337 return MF_NO_ACTION;
9339 if (IS_CUSTOM_ELEMENT(element) &&
9340 !(element_info[element].access_direction & opposite_direction))
9341 return MF_NO_ACTION; /* field not accessible from this direction */
9344 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9345 return MF_NO_ACTION;
9348 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9350 if (!player->key[element - EL_EM_GATE_1])
9351 return MF_NO_ACTION;
9353 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9355 if (!player->key[element - EL_EM_GATE_1_GRAY])
9356 return MF_NO_ACTION;
9359 /* automatically move to the next field with double speed */
9360 player->programmed_action = move_direction;
9362 if (player->move_delay_reset_counter == 0)
9364 player->move_delay_reset_counter = 2; /* two double speed steps */
9366 DOUBLE_PLAYER_SPEED(player);
9369 player->move_delay_reset_counter = 2;
9371 DOUBLE_PLAYER_SPEED(player);
9374 PlayLevelSoundAction(x, y, ACTION_PASSING);
9378 else if (IS_DIGGABLE(element))
9382 if (mode != DF_SNAP)
9385 GfxElement[x][y] = GFX_ELEMENT(element);
9388 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9390 player->is_digging = TRUE;
9393 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9395 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9398 if (mode == DF_SNAP)
9399 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9404 else if (IS_COLLECTIBLE(element))
9408 if (mode != DF_SNAP)
9410 GfxElement[x][y] = element;
9411 player->is_collecting = TRUE;
9414 if (element == EL_SPEED_PILL)
9415 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9416 else if (element == EL_EXTRA_TIME && level.time > 0)
9419 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9421 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9423 player->shield_normal_time_left += 10;
9424 if (element == EL_SHIELD_DEADLY)
9425 player->shield_deadly_time_left += 10;
9427 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9429 if (player->inventory_size < MAX_INVENTORY_SIZE)
9430 player->inventory_element[player->inventory_size++] = element;
9432 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9433 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9435 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9437 player->dynabomb_count++;
9438 player->dynabombs_left++;
9440 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9442 player->dynabomb_size++;
9444 else if (element == EL_DYNABOMB_INCREASE_POWER)
9446 player->dynabomb_xl = TRUE;
9448 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9449 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9451 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9452 element - EL_KEY_1 : element - EL_EM_KEY_1);
9454 player->key[key_nr] = TRUE;
9456 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9457 el2edimg(EL_KEY_1 + key_nr));
9458 redraw_mask |= REDRAW_DOOR_1;
9460 else if (IS_ENVELOPE(element))
9463 player->show_envelope = element;
9465 ShowEnvelope(element - EL_ENVELOPE_1);
9468 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9472 for (i = 0; i < element_info[element].collect_count; i++)
9473 if (player->inventory_size < MAX_INVENTORY_SIZE)
9474 player->inventory_element[player->inventory_size++] = element;
9476 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9477 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9479 else if (element_info[element].collect_count > 0)
9481 local_player->gems_still_needed -=
9482 element_info[element].collect_count;
9483 if (local_player->gems_still_needed < 0)
9484 local_player->gems_still_needed = 0;
9486 DrawText(DX_EMERALDS, DY_EMERALDS,
9487 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9490 RaiseScoreElement(element);
9491 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9493 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9496 if (mode == DF_SNAP)
9497 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9502 else if (IS_PUSHABLE(element))
9504 if (mode == DF_SNAP && element != EL_BD_ROCK)
9505 return MF_NO_ACTION;
9507 if (CAN_FALL(element) && dy)
9508 return MF_NO_ACTION;
9510 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9511 !(element == EL_SPRING && level.use_spring_bug))
9512 return MF_NO_ACTION;
9515 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9516 ((move_direction & MV_VERTICAL &&
9517 ((element_info[element].move_pattern & MV_LEFT &&
9518 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9519 (element_info[element].move_pattern & MV_RIGHT &&
9520 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9521 (move_direction & MV_HORIZONTAL &&
9522 ((element_info[element].move_pattern & MV_UP &&
9523 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9524 (element_info[element].move_pattern & MV_DOWN &&
9525 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9526 return MF_NO_ACTION;
9530 /* do not push elements already moving away faster than player */
9531 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9532 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9533 return MF_NO_ACTION;
9535 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9536 return MF_NO_ACTION;
9540 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9542 if (player->push_delay_value == -1)
9543 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9545 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9547 if (!player->is_pushing)
9548 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9552 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9553 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9554 !player_is_pushing))
9555 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9558 if (!player->is_pushing &&
9559 game.engine_version >= VERSION_IDENT(2,2,0,7))
9560 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9564 printf("::: push delay: %ld [%d, %d] [%d]\n",
9565 player->push_delay_value, FrameCounter, game.engine_version,
9566 player->is_pushing);
9569 player->is_pushing = TRUE;
9571 if (!(IN_LEV_FIELD(nextx, nexty) &&
9572 (IS_FREE(nextx, nexty) ||
9573 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9574 IS_SB_ELEMENT(element)))))
9575 return MF_NO_ACTION;
9577 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9578 return MF_NO_ACTION;
9580 if (player->push_delay == 0) /* new pushing; restart delay */
9581 player->push_delay = FrameCounter;
9583 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9584 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9585 element != EL_SPRING && element != EL_BALLOON)
9587 /* make sure that there is no move delay before next try to push */
9588 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9589 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9591 return MF_NO_ACTION;
9595 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9598 if (IS_SB_ELEMENT(element))
9600 if (element == EL_SOKOBAN_FIELD_FULL)
9602 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9603 local_player->sokobanfields_still_needed++;
9606 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9608 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9609 local_player->sokobanfields_still_needed--;
9612 Feld[x][y] = EL_SOKOBAN_OBJECT;
9614 if (Back[x][y] == Back[nextx][nexty])
9615 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9616 else if (Back[x][y] != 0)
9617 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9620 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9623 if (local_player->sokobanfields_still_needed == 0 &&
9624 game.emulation == EMU_SOKOBAN)
9626 player->LevelSolved = player->GameOver = TRUE;
9627 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9631 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9633 InitMovingField(x, y, move_direction);
9634 GfxAction[x][y] = ACTION_PUSHING;
9636 if (mode == DF_SNAP)
9637 ContinueMoving(x, y);
9639 MovPos[x][y] = (dx != 0 ? dx : dy);
9641 Pushed[x][y] = TRUE;
9642 Pushed[nextx][nexty] = TRUE;
9644 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9645 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9647 player->push_delay_value = -1; /* get new value later */
9649 CheckTriggeredElementSideChange(x, y, element, dig_side,
9650 CE_OTHER_GETS_PUSHED);
9651 CheckElementSideChange(x, y, element, dig_side,
9652 CE_PUSHED_BY_PLAYER, -1);
9656 else if (IS_SWITCHABLE(element))
9658 if (PLAYER_SWITCHING(player, x, y))
9661 player->is_switching = TRUE;
9662 player->switch_x = x;
9663 player->switch_y = y;
9665 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9667 if (element == EL_ROBOT_WHEEL)
9669 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9673 DrawLevelField(x, y);
9675 else if (element == EL_SP_TERMINAL)
9679 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9681 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9683 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9684 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9687 else if (IS_BELT_SWITCH(element))
9689 ToggleBeltSwitch(x, y);
9691 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9692 element == EL_SWITCHGATE_SWITCH_DOWN)
9694 ToggleSwitchgateSwitch(x, y);
9696 else if (element == EL_LIGHT_SWITCH ||
9697 element == EL_LIGHT_SWITCH_ACTIVE)
9699 ToggleLightSwitch(x, y);
9702 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9703 SND_LIGHT_SWITCH_ACTIVATING :
9704 SND_LIGHT_SWITCH_DEACTIVATING);
9707 else if (element == EL_TIMEGATE_SWITCH)
9709 ActivateTimegateSwitch(x, y);
9711 else if (element == EL_BALLOON_SWITCH_LEFT ||
9712 element == EL_BALLOON_SWITCH_RIGHT ||
9713 element == EL_BALLOON_SWITCH_UP ||
9714 element == EL_BALLOON_SWITCH_DOWN ||
9715 element == EL_BALLOON_SWITCH_ANY)
9717 if (element == EL_BALLOON_SWITCH_ANY)
9718 game.balloon_dir = move_direction;
9720 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9721 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9722 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9723 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9726 else if (element == EL_LAMP)
9728 Feld[x][y] = EL_LAMP_ACTIVE;
9729 local_player->lights_still_needed--;
9731 DrawLevelField(x, y);
9733 else if (element == EL_TIME_ORB_FULL)
9735 Feld[x][y] = EL_TIME_ORB_EMPTY;
9737 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9739 DrawLevelField(x, y);
9742 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9750 if (!PLAYER_SWITCHING(player, x, y))
9752 player->is_switching = TRUE;
9753 player->switch_x = x;
9754 player->switch_y = y;
9756 CheckTriggeredElementSideChange(x, y, element, dig_side,
9757 CE_OTHER_IS_SWITCHING);
9758 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9761 CheckTriggeredElementSideChange(x, y, element, dig_side,
9762 CE_OTHER_GETS_PRESSED);
9763 CheckElementSideChange(x, y, element, dig_side,
9764 CE_PRESSED_BY_PLAYER, -1);
9767 return MF_NO_ACTION;
9770 player->push_delay = 0;
9772 if (Feld[x][y] != element) /* really digged/collected something */
9773 player->is_collecting = !player->is_digging;
9778 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9780 int jx = player->jx, jy = player->jy;
9781 int x = jx + dx, y = jy + dy;
9782 int snap_direction = (dx == -1 ? MV_LEFT :
9783 dx == +1 ? MV_RIGHT :
9785 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9787 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9790 if (!player->active || !IN_LEV_FIELD(x, y))
9798 if (player->MovPos == 0)
9799 player->is_pushing = FALSE;
9801 player->is_snapping = FALSE;
9803 if (player->MovPos == 0)
9805 player->is_moving = FALSE;
9806 player->is_digging = FALSE;
9807 player->is_collecting = FALSE;
9813 if (player->is_snapping)
9816 player->MovDir = snap_direction;
9819 if (player->MovPos == 0)
9822 player->is_moving = FALSE;
9823 player->is_digging = FALSE;
9824 player->is_collecting = FALSE;
9827 player->is_dropping = FALSE;
9829 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9832 player->is_snapping = TRUE;
9835 if (player->MovPos == 0)
9838 player->is_moving = FALSE;
9839 player->is_digging = FALSE;
9840 player->is_collecting = FALSE;
9843 DrawLevelField(x, y);
9849 boolean DropElement(struct PlayerInfo *player)
9851 int jx = player->jx, jy = player->jy;
9852 int old_element = Feld[jx][jy];
9855 /* check if player is active, not moving and ready to drop */
9856 if (!player->active || player->MovPos || player->drop_delay > 0)
9859 /* check if player has anything that can be dropped */
9860 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9863 /* check if anything can be dropped at the current position */
9864 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9867 /* collected custom elements can only be dropped on empty fields */
9868 if (player->inventory_size > 0 &&
9869 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9870 && old_element != EL_EMPTY)
9873 if (old_element != EL_EMPTY)
9874 Back[jx][jy] = old_element; /* store old element on this field */
9876 ResetGfxAnimation(jx, jy);
9877 ResetRandomAnimationValue(jx, jy);
9879 if (player->inventory_size > 0)
9881 player->inventory_size--;
9882 new_element = player->inventory_element[player->inventory_size];
9884 if (new_element == EL_DYNAMITE)
9885 new_element = EL_DYNAMITE_ACTIVE;
9886 else if (new_element == EL_SP_DISK_RED)
9887 new_element = EL_SP_DISK_RED_ACTIVE;
9889 Feld[jx][jy] = new_element;
9891 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9892 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9894 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9895 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9897 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9900 /* needed if previous element just changed to "empty" in the last frame */
9901 Changed[jx][jy] = 0; /* allow another change */
9904 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9905 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9907 TestIfElementTouchesCustomElement(jx, jy);
9909 else /* player is dropping a dyna bomb */
9911 player->dynabombs_left--;
9912 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9914 Feld[jx][jy] = new_element;
9916 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9917 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9919 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9926 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9929 InitField_WithBug1(jx, jy, FALSE);
9931 InitField(jx, jy, FALSE);
9932 if (CAN_MOVE(Feld[jx][jy]))
9937 new_element = Feld[jx][jy];
9939 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9940 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9942 int move_stepsize = element_info[new_element].move_stepsize;
9943 int direction, dx, dy, nextx, nexty;
9945 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9946 MovDir[jx][jy] = player->MovDir;
9948 direction = MovDir[jx][jy];
9949 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9950 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9954 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9957 WasJustMoving[jx][jy] = 3;
9959 InitMovingField(jx, jy, direction);
9960 ContinueMoving(jx, jy);
9965 Changed[jx][jy] = 0; /* allow another change */
9968 TestIfElementHitsCustomElement(jx, jy, direction);
9970 CheckElementSideChange(jx, jy, new_element,
9971 direction, CE_HITTING_SOMETHING, -1);
9975 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9979 player->drop_delay = 8 + 8 + 8;
9984 player->is_dropping = TRUE;
9990 /* ------------------------------------------------------------------------- */
9991 /* game sound playing functions */
9992 /* ------------------------------------------------------------------------- */
9994 static int *loop_sound_frame = NULL;
9995 static int *loop_sound_volume = NULL;
9997 void InitPlayLevelSound()
9999 int num_sounds = getSoundListSize();
10001 checked_free(loop_sound_frame);
10002 checked_free(loop_sound_volume);
10004 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10005 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10008 static void PlayLevelSound(int x, int y, int nr)
10010 int sx = SCREENX(x), sy = SCREENY(y);
10011 int volume, stereo_position;
10012 int max_distance = 8;
10013 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10015 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10016 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10019 if (!IN_LEV_FIELD(x, y) ||
10020 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10021 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10024 volume = SOUND_MAX_VOLUME;
10026 if (!IN_SCR_FIELD(sx, sy))
10028 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10029 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10031 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10034 stereo_position = (SOUND_MAX_LEFT +
10035 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10036 (SCR_FIELDX + 2 * max_distance));
10038 if (IS_LOOP_SOUND(nr))
10040 /* This assures that quieter loop sounds do not overwrite louder ones,
10041 while restarting sound volume comparison with each new game frame. */
10043 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10046 loop_sound_volume[nr] = volume;
10047 loop_sound_frame[nr] = FrameCounter;
10050 PlaySoundExt(nr, volume, stereo_position, type);
10053 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10055 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10056 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10057 y < LEVELY(BY1) ? LEVELY(BY1) :
10058 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10062 static void PlayLevelSoundAction(int x, int y, int action)
10064 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10067 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10069 int sound_effect = element_info[element].sound[action];
10071 if (sound_effect != SND_UNDEFINED)
10072 PlayLevelSound(x, y, sound_effect);
10075 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10078 int sound_effect = element_info[element].sound[action];
10080 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10081 PlayLevelSound(x, y, sound_effect);
10084 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10086 int sound_effect = element_info[Feld[x][y]].sound[action];
10088 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10089 PlayLevelSound(x, y, sound_effect);
10092 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10094 int sound_effect = element_info[Feld[x][y]].sound[action];
10096 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10097 StopSound(sound_effect);
10100 static void PlayLevelMusic()
10102 if (levelset.music[level_nr] != MUS_UNDEFINED)
10103 PlayMusic(levelset.music[level_nr]); /* from config file */
10105 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10108 void RaiseScore(int value)
10110 local_player->score += value;
10111 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
10114 void RaiseScoreElement(int element)
10119 case EL_BD_DIAMOND:
10120 case EL_EMERALD_YELLOW:
10121 case EL_EMERALD_RED:
10122 case EL_EMERALD_PURPLE:
10123 case EL_SP_INFOTRON:
10124 RaiseScore(level.score[SC_EMERALD]);
10127 RaiseScore(level.score[SC_DIAMOND]);
10130 RaiseScore(level.score[SC_CRYSTAL]);
10133 RaiseScore(level.score[SC_PEARL]);
10136 case EL_BD_BUTTERFLY:
10137 case EL_SP_ELECTRON:
10138 RaiseScore(level.score[SC_BUG]);
10141 case EL_BD_FIREFLY:
10142 case EL_SP_SNIKSNAK:
10143 RaiseScore(level.score[SC_SPACESHIP]);
10146 case EL_DARK_YAMYAM:
10147 RaiseScore(level.score[SC_YAMYAM]);
10150 RaiseScore(level.score[SC_ROBOT]);
10153 RaiseScore(level.score[SC_PACMAN]);
10156 RaiseScore(level.score[SC_NUT]);
10159 case EL_SP_DISK_RED:
10160 case EL_DYNABOMB_INCREASE_NUMBER:
10161 case EL_DYNABOMB_INCREASE_SIZE:
10162 case EL_DYNABOMB_INCREASE_POWER:
10163 RaiseScore(level.score[SC_DYNAMITE]);
10165 case EL_SHIELD_NORMAL:
10166 case EL_SHIELD_DEADLY:
10167 RaiseScore(level.score[SC_SHIELD]);
10169 case EL_EXTRA_TIME:
10170 RaiseScore(level.score[SC_TIME_BONUS]);
10176 RaiseScore(level.score[SC_KEY]);
10179 RaiseScore(element_info[element].collect_score);
10184 void RequestQuitGame(boolean ask_if_really_quit)
10186 if (AllPlayersGone ||
10187 !ask_if_really_quit ||
10188 level_editor_test_game ||
10189 Request("Do you really want to quit the game ?",
10190 REQ_ASK | REQ_STAY_CLOSED))
10192 #if defined(PLATFORM_UNIX)
10193 if (options.network)
10194 SendToServer_StopPlaying();
10198 game_status = GAME_MODE_MAIN;
10204 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10209 /* ---------- new game button stuff ---------------------------------------- */
10211 /* graphic position values for game buttons */
10212 #define GAME_BUTTON_XSIZE 30
10213 #define GAME_BUTTON_YSIZE 30
10214 #define GAME_BUTTON_XPOS 5
10215 #define GAME_BUTTON_YPOS 215
10216 #define SOUND_BUTTON_XPOS 5
10217 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10219 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10220 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10221 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10222 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10223 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10224 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10231 } gamebutton_info[NUM_GAME_BUTTONS] =
10234 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10239 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10240 GAME_CTRL_ID_PAUSE,
10244 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10249 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10250 SOUND_CTRL_ID_MUSIC,
10251 "background music on/off"
10254 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10255 SOUND_CTRL_ID_LOOPS,
10256 "sound loops on/off"
10259 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10260 SOUND_CTRL_ID_SIMPLE,
10261 "normal sounds on/off"
10265 void CreateGameButtons()
10269 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10271 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10272 struct GadgetInfo *gi;
10275 unsigned long event_mask;
10276 int gd_xoffset, gd_yoffset;
10277 int gd_x1, gd_x2, gd_y1, gd_y2;
10280 gd_xoffset = gamebutton_info[i].x;
10281 gd_yoffset = gamebutton_info[i].y;
10282 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10283 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10285 if (id == GAME_CTRL_ID_STOP ||
10286 id == GAME_CTRL_ID_PAUSE ||
10287 id == GAME_CTRL_ID_PLAY)
10289 button_type = GD_TYPE_NORMAL_BUTTON;
10291 event_mask = GD_EVENT_RELEASED;
10292 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10293 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10297 button_type = GD_TYPE_CHECK_BUTTON;
10299 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10300 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10301 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10302 event_mask = GD_EVENT_PRESSED;
10303 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10304 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10307 gi = CreateGadget(GDI_CUSTOM_ID, id,
10308 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10309 GDI_X, DX + gd_xoffset,
10310 GDI_Y, DY + gd_yoffset,
10311 GDI_WIDTH, GAME_BUTTON_XSIZE,
10312 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10313 GDI_TYPE, button_type,
10314 GDI_STATE, GD_BUTTON_UNPRESSED,
10315 GDI_CHECKED, checked,
10316 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10317 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10318 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10319 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10320 GDI_EVENT_MASK, event_mask,
10321 GDI_CALLBACK_ACTION, HandleGameButtons,
10325 Error(ERR_EXIT, "cannot create gadget");
10327 game_gadget[id] = gi;
10331 void FreeGameButtons()
10335 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10336 FreeGadget(game_gadget[i]);
10339 static void MapGameButtons()
10343 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10344 MapGadget(game_gadget[i]);
10347 void UnmapGameButtons()
10351 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10352 UnmapGadget(game_gadget[i]);
10355 static void HandleGameButtons(struct GadgetInfo *gi)
10357 int id = gi->custom_id;
10359 if (game_status != GAME_MODE_PLAYING)
10364 case GAME_CTRL_ID_STOP:
10365 RequestQuitGame(TRUE);
10368 case GAME_CTRL_ID_PAUSE:
10369 if (options.network)
10371 #if defined(PLATFORM_UNIX)
10373 SendToServer_ContinuePlaying();
10375 SendToServer_PausePlaying();
10379 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10382 case GAME_CTRL_ID_PLAY:
10385 #if defined(PLATFORM_UNIX)
10386 if (options.network)
10387 SendToServer_ContinuePlaying();
10391 tape.pausing = FALSE;
10392 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10397 case SOUND_CTRL_ID_MUSIC:
10398 if (setup.sound_music)
10400 setup.sound_music = FALSE;
10403 else if (audio.music_available)
10405 setup.sound = setup.sound_music = TRUE;
10407 SetAudioMode(setup.sound);
10413 case SOUND_CTRL_ID_LOOPS:
10414 if (setup.sound_loops)
10415 setup.sound_loops = FALSE;
10416 else if (audio.loops_available)
10418 setup.sound = setup.sound_loops = TRUE;
10419 SetAudioMode(setup.sound);
10423 case SOUND_CTRL_ID_SIMPLE:
10424 if (setup.sound_simple)
10425 setup.sound_simple = FALSE;
10426 else if (audio.sound_available)
10428 setup.sound = setup.sound_simple = TRUE;
10429 SetAudioMode(setup.sound);