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))
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
106 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (DONT_COLLIDE_WITH(e) && \
110 !PLAYER_PROTECTED(x, y))))
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
113 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
115 (DONT_COLLIDE_WITH(e) && \
116 IS_FREE_OR_PLAYER(x, y))))
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
120 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
124 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
127 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
130 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
134 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
135 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
136 Feld[x][y] == EL_DIAMOND))
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
142 #define PACMAN_CAN_ENTER_FIELD(x, y) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
144 IS_AMOEBOID(Feld[x][y])))
146 #define PIG_CAN_ENTER_FIELD(x, y) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 IS_FOOD_PIG(Feld[x][y])))
150 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 IS_FOOD_PENGUIN(Feld[x][y]) || \
153 Feld[x][y] == EL_EXIT_OPEN || \
154 Feld[x][y] == EL_ACID))
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
159 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
167 #define GROUP_NR(e) ((e) - EL_GROUP_START)
168 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
169 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
170 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
172 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
173 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
176 #define CE_ENTER_FIELD_COND(e, x, y) \
177 (!IS_PLAYER(x, y) && \
178 (Feld[x][y] == EL_ACID || \
179 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
181 #define CE_ENTER_FIELD_COND(e, x, y) \
182 (!IS_PLAYER(x, y) && \
183 (Feld[x][y] == EL_ACID || \
184 Feld[x][y] == MOVE_ENTER_EL(e) || \
185 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
186 IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
189 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
192 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
193 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
195 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
196 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
198 /* game button identifiers */
199 #define GAME_CTRL_ID_STOP 0
200 #define GAME_CTRL_ID_PAUSE 1
201 #define GAME_CTRL_ID_PLAY 2
202 #define SOUND_CTRL_ID_MUSIC 3
203 #define SOUND_CTRL_ID_LOOPS 4
204 #define SOUND_CTRL_ID_SIMPLE 5
206 #define NUM_GAME_BUTTONS 6
209 /* forward declaration for internal use */
211 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
212 static boolean MovePlayer(struct PlayerInfo *, int, int);
213 static void ScrollPlayer(struct PlayerInfo *, int);
214 static void ScrollScreen(struct PlayerInfo *, int);
216 static void InitBeltMovement(void);
217 static void CloseAllOpenTimegates(void);
218 static void CheckGravityMovement(struct PlayerInfo *);
219 static void KillHeroUnlessProtected(int, int);
221 static void TestIfPlayerTouchesCustomElement(int, int);
222 static void TestIfElementTouchesCustomElement(int, int);
223 static void TestIfElementHitsCustomElement(int, int, int);
225 static void ChangeElement(int, int, int);
226 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
227 static boolean CheckTriggeredElementChange(int, int, int, int);
228 static boolean CheckElementSideChange(int, int, int, int, int, int);
229 static boolean CheckElementChange(int, int, int, int);
231 static void PlayLevelSound(int, int, int);
232 static void PlayLevelSoundNearest(int, int, int);
233 static void PlayLevelSoundAction(int, int, int);
234 static void PlayLevelSoundElementAction(int, int, int, int);
235 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
236 static void PlayLevelSoundActionIfLoop(int, int, int);
237 static void StopLevelSoundActionIfLoop(int, int, int);
238 static void PlayLevelMusic();
240 static void MapGameButtons();
241 static void HandleGameButtons(struct GadgetInfo *);
243 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
246 /* ------------------------------------------------------------------------- */
247 /* definition of elements that automatically change to other elements after */
248 /* a specified time, eventually calling a function when changing */
249 /* ------------------------------------------------------------------------- */
251 /* forward declaration for changer functions */
252 static void InitBuggyBase(int x, int y);
253 static void WarnBuggyBase(int x, int y);
255 static void InitTrap(int x, int y);
256 static void ActivateTrap(int x, int y);
257 static void ChangeActiveTrap(int x, int y);
259 static void InitRobotWheel(int x, int y);
260 static void RunRobotWheel(int x, int y);
261 static void StopRobotWheel(int x, int y);
263 static void InitTimegateWheel(int x, int y);
264 static void RunTimegateWheel(int x, int y);
266 struct ChangingElementInfo
271 void (*pre_change_function)(int x, int y);
272 void (*change_function)(int x, int y);
273 void (*post_change_function)(int x, int y);
276 static struct ChangingElementInfo change_delay_list[] =
327 EL_SWITCHGATE_OPENING,
335 EL_SWITCHGATE_CLOSING,
336 EL_SWITCHGATE_CLOSED,
368 EL_ACID_SPLASH_RIGHT,
377 EL_SP_BUGGY_BASE_ACTIVATING,
384 EL_SP_BUGGY_BASE_ACTIVATING,
385 EL_SP_BUGGY_BASE_ACTIVE,
392 EL_SP_BUGGY_BASE_ACTIVE,
416 EL_ROBOT_WHEEL_ACTIVE,
424 EL_TIMEGATE_SWITCH_ACTIVE,
445 int push_delay_fixed, push_delay_random;
450 { EL_BALLOON, 0, 0 },
452 { EL_SOKOBAN_OBJECT, 2, 0 },
453 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
454 { EL_SATELLITE, 2, 0 },
455 { EL_SP_DISK_YELLOW, 2, 0 },
457 { EL_UNDEFINED, 0, 0 },
465 move_stepsize_list[] =
467 { EL_AMOEBA_DROP, 2 },
468 { EL_AMOEBA_DROPPING, 2 },
469 { EL_QUICKSAND_FILLING, 1 },
470 { EL_QUICKSAND_EMPTYING, 1 },
471 { EL_MAGIC_WALL_FILLING, 2 },
472 { EL_BD_MAGIC_WALL_FILLING, 2 },
473 { EL_MAGIC_WALL_EMPTYING, 2 },
474 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
484 collect_count_list[] =
487 { EL_BD_DIAMOND, 1 },
488 { EL_EMERALD_YELLOW, 1 },
489 { EL_EMERALD_RED, 1 },
490 { EL_EMERALD_PURPLE, 1 },
492 { EL_SP_INFOTRON, 1 },
499 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
501 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
502 CH_EVENT_BIT(CE_DELAY))
503 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
504 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
505 IS_JUST_CHANGING(x, y))
507 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
510 void GetPlayerConfig()
512 if (!audio.sound_available)
513 setup.sound_simple = FALSE;
515 if (!audio.loops_available)
516 setup.sound_loops = FALSE;
518 if (!audio.music_available)
519 setup.sound_music = FALSE;
521 if (!video.fullscreen_available)
522 setup.fullscreen = FALSE;
524 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
526 SetAudioMode(setup.sound);
530 static int getBeltNrFromBeltElement(int element)
532 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
533 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
534 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
537 static int getBeltNrFromBeltActiveElement(int element)
539 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
540 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
541 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
544 static int getBeltNrFromBeltSwitchElement(int element)
546 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
547 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
548 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
551 static int getBeltDirNrFromBeltSwitchElement(int element)
553 static int belt_base_element[4] =
555 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
556 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
557 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
558 EL_CONVEYOR_BELT_4_SWITCH_LEFT
561 int belt_nr = getBeltNrFromBeltSwitchElement(element);
562 int belt_dir_nr = element - belt_base_element[belt_nr];
564 return (belt_dir_nr % 3);
567 static int getBeltDirFromBeltSwitchElement(int element)
569 static int belt_move_dir[3] =
576 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
578 return belt_move_dir[belt_dir_nr];
581 static void InitPlayerField(int x, int y, int element, boolean init_game)
583 if (element == EL_SP_MURPHY)
587 if (stored_player[0].present)
589 Feld[x][y] = EL_SP_MURPHY_CLONE;
595 stored_player[0].use_murphy_graphic = TRUE;
598 Feld[x][y] = EL_PLAYER_1;
604 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
605 int jx = player->jx, jy = player->jy;
607 player->present = TRUE;
609 player->block_last_field = (element == EL_SP_MURPHY ?
610 level.sp_block_last_field :
611 level.block_last_field);
613 if (!options.network || player->connected)
615 player->active = TRUE;
617 /* remove potentially duplicate players */
618 if (StorePlayer[jx][jy] == Feld[x][y])
619 StorePlayer[jx][jy] = 0;
621 StorePlayer[x][y] = Feld[x][y];
625 printf("Player %d activated.\n", player->element_nr);
626 printf("[Local player is %d and currently %s.]\n",
627 local_player->element_nr,
628 local_player->active ? "active" : "not active");
632 Feld[x][y] = EL_EMPTY;
633 player->jx = player->last_jx = x;
634 player->jy = player->last_jy = y;
638 static void InitField(int x, int y, boolean init_game)
640 int element = Feld[x][y];
649 InitPlayerField(x, y, element, init_game);
652 case EL_SOKOBAN_FIELD_PLAYER:
653 element = Feld[x][y] = EL_PLAYER_1;
654 InitField(x, y, init_game);
656 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
657 InitField(x, y, init_game);
660 case EL_SOKOBAN_FIELD_EMPTY:
661 local_player->sokobanfields_still_needed++;
665 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
666 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
667 else if (x > 0 && Feld[x-1][y] == EL_ACID)
668 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
669 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
670 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
671 else if (y > 0 && Feld[x][y-1] == EL_ACID)
672 Feld[x][y] = EL_ACID_POOL_BOTTOM;
673 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
674 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
682 case EL_SPACESHIP_RIGHT:
683 case EL_SPACESHIP_UP:
684 case EL_SPACESHIP_LEFT:
685 case EL_SPACESHIP_DOWN:
687 case EL_BD_BUTTERFLY_RIGHT:
688 case EL_BD_BUTTERFLY_UP:
689 case EL_BD_BUTTERFLY_LEFT:
690 case EL_BD_BUTTERFLY_DOWN:
691 case EL_BD_BUTTERFLY:
692 case EL_BD_FIREFLY_RIGHT:
693 case EL_BD_FIREFLY_UP:
694 case EL_BD_FIREFLY_LEFT:
695 case EL_BD_FIREFLY_DOWN:
697 case EL_PACMAN_RIGHT:
721 if (y == lev_fieldy - 1)
723 Feld[x][y] = EL_AMOEBA_GROWING;
724 Store[x][y] = EL_AMOEBA_WET;
728 case EL_DYNAMITE_ACTIVE:
729 case EL_SP_DISK_RED_ACTIVE:
730 case EL_DYNABOMB_PLAYER_1_ACTIVE:
731 case EL_DYNABOMB_PLAYER_2_ACTIVE:
732 case EL_DYNABOMB_PLAYER_3_ACTIVE:
733 case EL_DYNABOMB_PLAYER_4_ACTIVE:
738 local_player->lights_still_needed++;
742 local_player->friends_still_needed++;
747 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
752 Feld[x][y] = EL_EMPTY;
757 case EL_EM_KEY_1_FILE:
758 Feld[x][y] = EL_EM_KEY_1;
760 case EL_EM_KEY_2_FILE:
761 Feld[x][y] = EL_EM_KEY_2;
763 case EL_EM_KEY_3_FILE:
764 Feld[x][y] = EL_EM_KEY_3;
766 case EL_EM_KEY_4_FILE:
767 Feld[x][y] = EL_EM_KEY_4;
771 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
772 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
773 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
774 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
775 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
776 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
777 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
778 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
779 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
780 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
781 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
782 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
785 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
786 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
787 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
789 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
791 game.belt_dir[belt_nr] = belt_dir;
792 game.belt_dir_nr[belt_nr] = belt_dir_nr;
794 else /* more than one switch -- set it like the first switch */
796 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
801 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
803 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
806 case EL_LIGHT_SWITCH_ACTIVE:
808 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
812 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
814 else if (IS_GROUP_ELEMENT(element))
816 struct ElementGroupInfo *group = element_info[element].group;
817 int last_anim_random_frame = gfx.anim_random_frame;
820 if (group->choice_mode == ANIM_RANDOM)
821 gfx.anim_random_frame = RND(group->num_elements_resolved);
823 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
824 group->choice_mode, 0,
827 if (group->choice_mode == ANIM_RANDOM)
828 gfx.anim_random_frame = last_anim_random_frame;
832 Feld[x][y] = group->element_resolved[element_pos];
834 InitField(x, y, init_game);
840 void DrawGameDoorValues()
844 for (i = 0; i < MAX_PLAYERS; i++)
845 for (j = 0; j < 4; j++)
846 if (stored_player[i].key[j])
847 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
848 el2edimg(EL_KEY_1 + j));
850 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
851 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
852 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
853 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
854 DrawText(DX + XX_SCORE, DY + YY_SCORE,
855 int2str(local_player->score, 5), FONT_TEXT_2);
856 DrawText(DX + XX_TIME, DY + YY_TIME,
857 int2str(TimeLeft, 3), FONT_TEXT_2);
860 static void resolve_group_element(int group_element, int recursion_depth)
863 static struct ElementGroupInfo *group;
864 struct ElementGroupInfo *actual_group = element_info[group_element].group;
867 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
869 Error(ERR_WARN, "recursion too deep when resolving group element %d",
870 group_element - EL_GROUP_START + 1);
872 /* replace element which caused too deep recursion by question mark */
873 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
878 if (recursion_depth == 0) /* initialization */
880 group = element_info[group_element].group;
881 group_nr = group_element - EL_GROUP_START;
883 group->num_elements_resolved = 0;
884 group->choice_pos = 0;
887 for (i = 0; i < actual_group->num_elements; i++)
889 int element = actual_group->element[i];
891 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
894 if (IS_GROUP_ELEMENT(element))
895 resolve_group_element(element, recursion_depth + 1);
898 group->element_resolved[group->num_elements_resolved++] = element;
899 element_info[element].in_group[group_nr] = TRUE;
904 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
906 printf("::: group %d: %d resolved elements\n",
907 group_element - EL_GROUP_START, group->num_elements_resolved);
908 for (i = 0; i < group->num_elements_resolved; i++)
909 printf("::: - %d ['%s']\n", group->element_resolved[i],
910 element_info[group->element_resolved[i]].token_name);
917 =============================================================================
919 -----------------------------------------------------------------------------
920 initialize game engine due to level / tape version number
921 =============================================================================
924 static void InitGameEngine()
928 /* set game engine from tape file when re-playing, else from level file */
929 game.engine_version = (tape.playing ? tape.engine_version :
932 /* dynamically adjust element properties according to game engine version */
933 InitElementPropertiesEngine(game.engine_version);
936 printf("level %d: level version == %06d\n", level_nr, level.game_version);
937 printf(" tape version == %06d [%s] [file: %06d]\n",
938 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
940 printf(" => game.engine_version == %06d\n", game.engine_version);
943 /* ---------- recursively resolve group elements ------------------------- */
945 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
946 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
947 element_info[i].in_group[j] = FALSE;
949 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
950 resolve_group_element(EL_GROUP_START + i, 0);
952 /* ---------- initialize player's initial move delay --------------------- */
954 /* dynamically adjust player properties according to game engine version */
955 game.initial_move_delay =
956 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
957 INITIAL_MOVE_DELAY_OFF);
959 /* dynamically adjust player properties according to level information */
960 game.initial_move_delay_value =
961 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
963 /* ---------- initialize player's initial push delay --------------------- */
965 /* dynamically adjust player properties according to game engine version */
966 game.initial_push_delay_value =
967 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
969 /* ---------- initialize changing elements ------------------------------- */
971 /* initialize changing elements information */
972 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
974 struct ElementInfo *ei = &element_info[i];
976 /* this pointer might have been changed in the level editor */
977 ei->change = &ei->change_page[0];
979 if (!IS_CUSTOM_ELEMENT(i))
981 ei->change->target_element = EL_EMPTY_SPACE;
982 ei->change->delay_fixed = 0;
983 ei->change->delay_random = 0;
984 ei->change->delay_frames = 1;
987 ei->change_events = CE_BITMASK_DEFAULT;
988 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
990 ei->event_page_nr[j] = 0;
991 ei->event_page[j] = &ei->change_page[0];
995 /* add changing elements from pre-defined list */
996 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
998 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
999 struct ElementInfo *ei = &element_info[ch_delay->element];
1001 ei->change->target_element = ch_delay->target_element;
1002 ei->change->delay_fixed = ch_delay->change_delay;
1004 ei->change->pre_change_function = ch_delay->pre_change_function;
1005 ei->change->change_function = ch_delay->change_function;
1006 ei->change->post_change_function = ch_delay->post_change_function;
1008 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1012 /* add change events from custom element configuration */
1013 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1015 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1017 for (j = 0; j < ei->num_change_pages; j++)
1019 if (!ei->change_page[j].can_change)
1022 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1024 /* only add event page for the first page found with this event */
1025 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1026 !(ei->change_events & CH_EVENT_BIT(k)))
1028 ei->change_events |= CH_EVENT_BIT(k);
1029 ei->event_page_nr[k] = j;
1030 ei->event_page[k] = &ei->change_page[j];
1038 /* add change events from custom element configuration */
1039 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1041 int element = EL_CUSTOM_START + i;
1043 /* only add custom elements that change after fixed/random frame delay */
1044 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1045 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1049 /* ---------- initialize trigger events ---------------------------------- */
1051 /* initialize trigger events information */
1052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1053 trigger_events[i] = EP_BITMASK_DEFAULT;
1056 /* add trigger events from element change event properties */
1057 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1059 struct ElementInfo *ei = &element_info[i];
1061 for (j = 0; j < ei->num_change_pages; j++)
1063 if (!ei->change_page[j].can_change)
1066 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1068 int trigger_element = ei->change_page[j].trigger_element;
1070 if (IS_GROUP_ELEMENT(trigger_element))
1072 struct ElementGroupInfo *group = element_info[trigger_element].group;
1074 for (k = 0; k < group->num_elements_resolved; k++)
1075 trigger_events[group->element_resolved[k]]
1076 |= ei->change_page[j].events;
1079 trigger_events[trigger_element] |= ei->change_page[j].events;
1084 /* add trigger events from element change event properties */
1085 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1086 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1087 trigger_events[element_info[i].change->trigger_element] |=
1088 element_info[i].change->events;
1091 /* ---------- initialize push delay -------------------------------------- */
1093 /* initialize push delay values to default */
1094 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1096 if (!IS_CUSTOM_ELEMENT(i))
1098 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1099 element_info[i].push_delay_random = game.default_push_delay_random;
1103 /* set push delay value for certain elements from pre-defined list */
1104 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1106 int e = push_delay_list[i].element;
1108 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1109 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1112 /* set push delay value for Supaplex elements for newer engine versions */
1113 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1115 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1117 if (IS_SP_ELEMENT(i))
1119 element_info[i].push_delay_fixed = 6;
1120 element_info[i].push_delay_random = 0;
1125 /* ---------- initialize move stepsize ----------------------------------- */
1127 /* initialize move stepsize values to default */
1128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1129 if (!IS_CUSTOM_ELEMENT(i))
1130 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1132 /* set move stepsize value for certain elements from pre-defined list */
1133 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1135 int e = move_stepsize_list[i].element;
1137 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1140 /* ---------- initialize move dig/leave ---------------------------------- */
1142 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1144 element_info[i].can_leave_element = FALSE;
1145 element_info[i].can_leave_element_last = FALSE;
1148 /* ---------- initialize gem count --------------------------------------- */
1150 /* initialize gem count values for each element */
1151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1152 if (!IS_CUSTOM_ELEMENT(i))
1153 element_info[i].collect_count = 0;
1155 /* add gem count values for all elements from pre-defined list */
1156 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1157 element_info[collect_count_list[i].element].collect_count =
1158 collect_count_list[i].count;
1163 =============================================================================
1165 -----------------------------------------------------------------------------
1166 initialize and start new game
1167 =============================================================================
1172 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1173 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1174 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1181 #if USE_NEW_AMOEBA_CODE
1182 printf("Using new amoeba code.\n");
1184 printf("Using old amoeba code.\n");
1189 /* don't play tapes over network */
1190 network_playing = (options.network && !tape.playing);
1192 for (i = 0; i < MAX_PLAYERS; i++)
1194 struct PlayerInfo *player = &stored_player[i];
1196 player->index_nr = i;
1197 player->element_nr = EL_PLAYER_1 + i;
1199 player->present = FALSE;
1200 player->active = FALSE;
1203 player->effective_action = 0;
1204 player->programmed_action = 0;
1207 player->gems_still_needed = level.gems_needed;
1208 player->sokobanfields_still_needed = 0;
1209 player->lights_still_needed = 0;
1210 player->friends_still_needed = 0;
1212 for (j = 0; j < 4; j++)
1213 player->key[j] = FALSE;
1215 player->dynabomb_count = 0;
1216 player->dynabomb_size = 1;
1217 player->dynabombs_left = 0;
1218 player->dynabomb_xl = FALSE;
1220 player->MovDir = MV_NO_MOVING;
1223 player->GfxDir = MV_NO_MOVING;
1224 player->GfxAction = ACTION_DEFAULT;
1226 player->StepFrame = 0;
1228 player->use_murphy_graphic = FALSE;
1230 player->block_last_field = FALSE;
1232 player->actual_frame_counter = 0;
1234 player->step_counter = 0;
1236 player->last_move_dir = MV_NO_MOVING;
1238 player->is_waiting = FALSE;
1239 player->is_moving = FALSE;
1240 player->is_digging = FALSE;
1241 player->is_snapping = FALSE;
1242 player->is_collecting = FALSE;
1243 player->is_pushing = FALSE;
1244 player->is_switching = FALSE;
1245 player->is_dropping = FALSE;
1247 player->is_bored = FALSE;
1248 player->is_sleeping = FALSE;
1250 player->frame_counter_bored = -1;
1251 player->frame_counter_sleeping = -1;
1253 player->anim_delay_counter = 0;
1254 player->post_delay_counter = 0;
1256 player->action_waiting = ACTION_DEFAULT;
1257 player->last_action_waiting = ACTION_DEFAULT;
1258 player->special_action_bored = ACTION_DEFAULT;
1259 player->special_action_sleeping = ACTION_DEFAULT;
1261 player->num_special_action_bored = 0;
1262 player->num_special_action_sleeping = 0;
1264 /* determine number of special actions for bored and sleeping animation */
1265 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1267 boolean found = FALSE;
1269 for (k = 0; k < NUM_DIRECTIONS; k++)
1270 if (el_act_dir2img(player->element_nr, j, k) !=
1271 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1275 player->num_special_action_bored++;
1279 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1281 boolean found = FALSE;
1283 for (k = 0; k < NUM_DIRECTIONS; k++)
1284 if (el_act_dir2img(player->element_nr, j, k) !=
1285 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1289 player->num_special_action_sleeping++;
1294 player->switch_x = -1;
1295 player->switch_y = -1;
1297 player->show_envelope = 0;
1299 player->move_delay = game.initial_move_delay;
1300 player->move_delay_value = game.initial_move_delay_value;
1302 player->move_delay_reset_counter = 0;
1304 player->push_delay = 0;
1305 player->push_delay_value = game.initial_push_delay_value;
1307 player->drop_delay = 0;
1309 player->last_jx = player->last_jy = 0;
1310 player->jx = player->jy = 0;
1312 player->shield_normal_time_left = 0;
1313 player->shield_deadly_time_left = 0;
1315 player->inventory_size = 0;
1317 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1318 SnapField(player, 0, 0);
1320 player->LevelSolved = FALSE;
1321 player->GameOver = FALSE;
1324 network_player_action_received = FALSE;
1326 #if defined(PLATFORM_UNIX)
1327 /* initial null action */
1328 if (network_playing)
1329 SendToServer_MovePlayer(MV_NO_MOVING);
1337 TimeLeft = level.time;
1339 ScreenMovDir = MV_NO_MOVING;
1343 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1345 AllPlayersGone = FALSE;
1347 game.yamyam_content_nr = 0;
1348 game.magic_wall_active = FALSE;
1349 game.magic_wall_time_left = 0;
1350 game.light_time_left = 0;
1351 game.timegate_time_left = 0;
1352 game.switchgate_pos = 0;
1353 game.balloon_dir = MV_NO_MOVING;
1354 game.gravity = level.initial_gravity;
1355 game.explosions_delayed = TRUE;
1357 game.envelope_active = FALSE;
1359 for (i = 0; i < 4; i++)
1361 game.belt_dir[i] = MV_NO_MOVING;
1362 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1365 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1366 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1368 for (x = 0; x < lev_fieldx; x++)
1370 for (y = 0; y < lev_fieldy; y++)
1372 Feld[x][y] = level.field[x][y];
1373 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1374 ChangeDelay[x][y] = 0;
1375 ChangePage[x][y] = -1;
1376 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1378 WasJustMoving[x][y] = 0;
1379 WasJustFalling[x][y] = 0;
1381 Pushed[x][y] = FALSE;
1383 Changed[x][y] = CE_BITMASK_DEFAULT;
1384 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1386 ExplodePhase[x][y] = 0;
1387 ExplodeField[x][y] = EX_NO_EXPLOSION;
1389 RunnerVisit[x][y] = 0;
1390 PlayerVisit[x][y] = 0;
1393 GfxRandom[x][y] = INIT_GFX_RANDOM();
1394 GfxElement[x][y] = EL_UNDEFINED;
1395 GfxAction[x][y] = ACTION_DEFAULT;
1396 GfxDir[x][y] = MV_NO_MOVING;
1400 for (y = 0; y < lev_fieldy; y++)
1402 for (x = 0; x < lev_fieldx; x++)
1404 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1406 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1408 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1411 InitField(x, y, TRUE);
1417 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1418 emulate_sb ? EMU_SOKOBAN :
1419 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1421 /* correct non-moving belts to start moving left */
1422 for (i = 0; i < 4; i++)
1423 if (game.belt_dir[i] == MV_NO_MOVING)
1424 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1426 /* check if any connected player was not found in playfield */
1427 for (i = 0; i < MAX_PLAYERS; i++)
1429 struct PlayerInfo *player = &stored_player[i];
1431 if (player->connected && !player->present)
1433 for (j = 0; j < MAX_PLAYERS; j++)
1435 struct PlayerInfo *some_player = &stored_player[j];
1436 int jx = some_player->jx, jy = some_player->jy;
1438 /* assign first free player found that is present in the playfield */
1439 if (some_player->present && !some_player->connected)
1441 player->present = TRUE;
1442 player->active = TRUE;
1444 some_player->present = FALSE;
1445 some_player->active = FALSE;
1447 StorePlayer[jx][jy] = player->element_nr;
1448 player->jx = player->last_jx = jx;
1449 player->jy = player->last_jy = jy;
1459 /* when playing a tape, eliminate all players which do not participate */
1461 for (i = 0; i < MAX_PLAYERS; i++)
1463 if (stored_player[i].active && !tape.player_participates[i])
1465 struct PlayerInfo *player = &stored_player[i];
1466 int jx = player->jx, jy = player->jy;
1468 player->active = FALSE;
1469 StorePlayer[jx][jy] = 0;
1470 Feld[jx][jy] = EL_EMPTY;
1474 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1476 /* when in single player mode, eliminate all but the first active player */
1478 for (i = 0; i < MAX_PLAYERS; i++)
1480 if (stored_player[i].active)
1482 for (j = i + 1; j < MAX_PLAYERS; j++)
1484 if (stored_player[j].active)
1486 struct PlayerInfo *player = &stored_player[j];
1487 int jx = player->jx, jy = player->jy;
1489 player->active = FALSE;
1490 player->present = FALSE;
1492 StorePlayer[jx][jy] = 0;
1493 Feld[jx][jy] = EL_EMPTY;
1500 /* when recording the game, store which players take part in the game */
1503 for (i = 0; i < MAX_PLAYERS; i++)
1504 if (stored_player[i].active)
1505 tape.player_participates[i] = TRUE;
1510 for (i = 0; i < MAX_PLAYERS; i++)
1512 struct PlayerInfo *player = &stored_player[i];
1514 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1519 if (local_player == player)
1520 printf("Player %d is local player.\n", i+1);
1524 if (BorderElement == EL_EMPTY)
1527 SBX_Right = lev_fieldx - SCR_FIELDX;
1529 SBY_Lower = lev_fieldy - SCR_FIELDY;
1534 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1536 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1539 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1540 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1542 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1543 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1545 /* if local player not found, look for custom element that might create
1546 the player (make some assumptions about the right custom element) */
1547 if (!local_player->present)
1549 int start_x = 0, start_y = 0;
1550 int found_rating = 0;
1551 int found_element = EL_UNDEFINED;
1553 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1555 int element = Feld[x][y];
1560 if (!IS_CUSTOM_ELEMENT(element))
1563 if (CAN_CHANGE(element))
1565 for (i = 0; i < element_info[element].num_change_pages; i++)
1567 content = element_info[element].change_page[i].target_element;
1568 is_player = ELEM_IS_PLAYER(content);
1570 if (is_player && (found_rating < 3 || element < found_element))
1576 found_element = element;
1581 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1583 content = element_info[element].content[xx][yy];
1584 is_player = ELEM_IS_PLAYER(content);
1586 if (is_player && (found_rating < 2 || element < found_element))
1588 start_x = x + xx - 1;
1589 start_y = y + yy - 1;
1592 found_element = element;
1595 if (!CAN_CHANGE(element))
1598 for (i = 0; i < element_info[element].num_change_pages; i++)
1600 content = element_info[element].change_page[i].content[xx][yy];
1601 is_player = ELEM_IS_PLAYER(content);
1603 if (is_player && (found_rating < 1 || element < found_element))
1605 start_x = x + xx - 1;
1606 start_y = y + yy - 1;
1609 found_element = element;
1615 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1616 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1619 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1620 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1626 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1627 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1628 local_player->jx - MIDPOSX);
1630 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1631 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1632 local_player->jy - MIDPOSY);
1634 scroll_x = SBX_Left;
1635 scroll_y = SBY_Upper;
1636 if (local_player->jx >= SBX_Left + MIDPOSX)
1637 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1638 local_player->jx - MIDPOSX :
1640 if (local_player->jy >= SBY_Upper + MIDPOSY)
1641 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1642 local_player->jy - MIDPOSY :
1647 CloseDoor(DOOR_CLOSE_1);
1652 /* after drawing the level, correct some elements */
1653 if (game.timegate_time_left == 0)
1654 CloseAllOpenTimegates();
1656 if (setup.soft_scrolling)
1657 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1659 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1662 /* copy default game door content to main double buffer */
1663 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1664 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1667 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1670 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1671 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1672 BlitBitmap(drawto, drawto,
1673 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1674 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1675 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1676 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1679 DrawGameDoorValues();
1683 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1684 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1685 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1689 /* copy actual game door content to door double buffer for OpenDoor() */
1690 BlitBitmap(drawto, bitmap_db_door,
1691 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1693 OpenDoor(DOOR_OPEN_ALL);
1695 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1697 if (setup.sound_music)
1700 KeyboardAutoRepeatOffUnlessAutoplay();
1704 for (i = 0; i < 4; i++)
1705 printf("Player %d %sactive.\n",
1706 i + 1, (stored_player[i].active ? "" : "not "));
1710 printf("::: starting game [%d]\n", FrameCounter);
1714 void InitMovDir(int x, int y)
1716 int i, element = Feld[x][y];
1717 static int xy[4][2] =
1724 static int direction[3][4] =
1726 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1727 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1728 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1737 Feld[x][y] = EL_BUG;
1738 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1741 case EL_SPACESHIP_RIGHT:
1742 case EL_SPACESHIP_UP:
1743 case EL_SPACESHIP_LEFT:
1744 case EL_SPACESHIP_DOWN:
1745 Feld[x][y] = EL_SPACESHIP;
1746 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1749 case EL_BD_BUTTERFLY_RIGHT:
1750 case EL_BD_BUTTERFLY_UP:
1751 case EL_BD_BUTTERFLY_LEFT:
1752 case EL_BD_BUTTERFLY_DOWN:
1753 Feld[x][y] = EL_BD_BUTTERFLY;
1754 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1757 case EL_BD_FIREFLY_RIGHT:
1758 case EL_BD_FIREFLY_UP:
1759 case EL_BD_FIREFLY_LEFT:
1760 case EL_BD_FIREFLY_DOWN:
1761 Feld[x][y] = EL_BD_FIREFLY;
1762 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1765 case EL_PACMAN_RIGHT:
1767 case EL_PACMAN_LEFT:
1768 case EL_PACMAN_DOWN:
1769 Feld[x][y] = EL_PACMAN;
1770 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1773 case EL_SP_SNIKSNAK:
1774 MovDir[x][y] = MV_UP;
1777 case EL_SP_ELECTRON:
1778 MovDir[x][y] = MV_LEFT;
1785 Feld[x][y] = EL_MOLE;
1786 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1790 if (IS_CUSTOM_ELEMENT(element))
1792 struct ElementInfo *ei = &element_info[element];
1793 int move_direction_initial = ei->move_direction_initial;
1794 int move_pattern = ei->move_pattern;
1796 if (move_direction_initial == MV_PREVIOUS)
1798 if (MovDir[x][y] != MV_NO_MOVING)
1801 move_direction_initial = MV_AUTOMATIC;
1804 if (move_direction_initial == MV_RANDOM)
1805 MovDir[x][y] = 1 << RND(4);
1806 else if (move_direction_initial & MV_ANY_DIRECTION)
1807 MovDir[x][y] = move_direction_initial;
1808 else if (move_pattern == MV_ALL_DIRECTIONS ||
1809 move_pattern == MV_TURNING_LEFT ||
1810 move_pattern == MV_TURNING_RIGHT ||
1811 move_pattern == MV_TURNING_LEFT_RIGHT ||
1812 move_pattern == MV_TURNING_RIGHT_LEFT ||
1813 move_pattern == MV_TURNING_RANDOM)
1814 MovDir[x][y] = 1 << RND(4);
1815 else if (move_pattern == MV_HORIZONTAL)
1816 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1817 else if (move_pattern == MV_VERTICAL)
1818 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1819 else if (move_pattern & MV_ANY_DIRECTION)
1820 MovDir[x][y] = element_info[element].move_pattern;
1821 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1822 move_pattern == MV_ALONG_RIGHT_SIDE)
1824 for (i = 0; i < 4; i++)
1826 int x1 = x + xy[i][0];
1827 int y1 = y + xy[i][1];
1829 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1831 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1832 MovDir[x][y] = direction[0][i];
1834 MovDir[x][y] = direction[1][i];
1843 MovDir[x][y] = 1 << RND(4);
1845 if (element != EL_BUG &&
1846 element != EL_SPACESHIP &&
1847 element != EL_BD_BUTTERFLY &&
1848 element != EL_BD_FIREFLY)
1851 for (i = 0; i < 4; i++)
1853 int x1 = x + xy[i][0];
1854 int y1 = y + xy[i][1];
1856 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1858 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1860 MovDir[x][y] = direction[0][i];
1863 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1864 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1866 MovDir[x][y] = direction[1][i];
1875 GfxDir[x][y] = MovDir[x][y];
1878 void InitAmoebaNr(int x, int y)
1881 int group_nr = AmoebeNachbarNr(x, y);
1885 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1887 if (AmoebaCnt[i] == 0)
1895 AmoebaNr[x][y] = group_nr;
1896 AmoebaCnt[group_nr]++;
1897 AmoebaCnt2[group_nr]++;
1903 boolean raise_level = FALSE;
1905 if (local_player->MovPos)
1909 if (tape.auto_play) /* tape might already be stopped here */
1910 tape.auto_play_level_solved = TRUE;
1912 if (tape.playing && tape.auto_play)
1913 tape.auto_play_level_solved = TRUE;
1916 local_player->LevelSolved = FALSE;
1918 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1922 if (!tape.playing && setup.sound_loops)
1923 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1924 SND_CTRL_PLAY_LOOP);
1926 while (TimeLeft > 0)
1928 if (!tape.playing && !setup.sound_loops)
1929 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1930 if (TimeLeft > 0 && !(TimeLeft % 10))
1931 RaiseScore(level.score[SC_TIME_BONUS]);
1932 if (TimeLeft > 100 && !(TimeLeft % 10))
1936 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1943 if (!tape.playing && setup.sound_loops)
1944 StopSound(SND_GAME_LEVELTIME_BONUS);
1946 else if (level.time == 0) /* level without time limit */
1948 if (!tape.playing && setup.sound_loops)
1949 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1950 SND_CTRL_PLAY_LOOP);
1952 while (TimePlayed < 999)
1954 if (!tape.playing && !setup.sound_loops)
1955 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1956 if (TimePlayed < 999 && !(TimePlayed % 10))
1957 RaiseScore(level.score[SC_TIME_BONUS]);
1958 if (TimePlayed < 900 && !(TimePlayed % 10))
1962 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1969 if (!tape.playing && setup.sound_loops)
1970 StopSound(SND_GAME_LEVELTIME_BONUS);
1973 /* close exit door after last player */
1974 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1975 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1977 int element = Feld[ExitX][ExitY];
1979 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1980 EL_SP_EXIT_CLOSING);
1982 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1985 /* Hero disappears */
1986 DrawLevelField(ExitX, ExitY);
1992 CloseDoor(DOOR_CLOSE_1);
1997 SaveTape(tape.level_nr); /* Ask to save tape */
2000 if (level_nr == leveldir_current->handicap_level)
2002 leveldir_current->handicap_level++;
2003 SaveLevelSetup_SeriesInfo();
2006 if (level_editor_test_game)
2007 local_player->score = -1; /* no highscore when playing from editor */
2008 else if (level_nr < leveldir_current->last_level)
2009 raise_level = TRUE; /* advance to next level */
2011 if ((hi_pos = NewHiScore()) >= 0)
2013 game_status = GAME_MODE_SCORES;
2014 DrawHallOfFame(hi_pos);
2023 game_status = GAME_MODE_MAIN;
2040 LoadScore(level_nr);
2042 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2043 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2046 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2048 if (local_player->score > highscore[k].Score)
2050 /* player has made it to the hall of fame */
2052 if (k < MAX_SCORE_ENTRIES - 1)
2054 int m = MAX_SCORE_ENTRIES - 1;
2057 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2058 if (!strcmp(setup.player_name, highscore[l].Name))
2060 if (m == k) /* player's new highscore overwrites his old one */
2064 for (l = m; l > k; l--)
2066 strcpy(highscore[l].Name, highscore[l - 1].Name);
2067 highscore[l].Score = highscore[l - 1].Score;
2074 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2075 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2076 highscore[k].Score = local_player->score;
2082 else if (!strncmp(setup.player_name, highscore[k].Name,
2083 MAX_PLAYER_NAME_LEN))
2084 break; /* player already there with a higher score */
2090 SaveScore(level_nr);
2095 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2097 if (player->GfxAction != action || player->GfxDir != dir)
2100 printf("Player frame reset! (%d => %d, %d => %d)\n",
2101 player->GfxAction, action, player->GfxDir, dir);
2104 player->GfxAction = action;
2105 player->GfxDir = dir;
2107 player->StepFrame = 0;
2111 static void ResetRandomAnimationValue(int x, int y)
2113 GfxRandom[x][y] = INIT_GFX_RANDOM();
2116 static void ResetGfxAnimation(int x, int y)
2119 GfxAction[x][y] = ACTION_DEFAULT;
2120 GfxDir[x][y] = MovDir[x][y];
2123 void InitMovingField(int x, int y, int direction)
2125 int element = Feld[x][y];
2126 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2127 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2131 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2132 ResetGfxAnimation(x, y);
2134 MovDir[newx][newy] = MovDir[x][y] = direction;
2135 GfxDir[x][y] = direction;
2137 if (Feld[newx][newy] == EL_EMPTY)
2138 Feld[newx][newy] = EL_BLOCKED;
2140 if (direction == MV_DOWN && CAN_FALL(element))
2141 GfxAction[x][y] = ACTION_FALLING;
2143 GfxAction[x][y] = ACTION_MOVING;
2145 GfxFrame[newx][newy] = GfxFrame[x][y];
2146 GfxRandom[newx][newy] = GfxRandom[x][y];
2147 GfxAction[newx][newy] = GfxAction[x][y];
2148 GfxDir[newx][newy] = GfxDir[x][y];
2151 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2153 int direction = MovDir[x][y];
2154 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2155 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2161 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2163 int oldx = x, oldy = y;
2164 int direction = MovDir[x][y];
2166 if (direction == MV_LEFT)
2168 else if (direction == MV_RIGHT)
2170 else if (direction == MV_UP)
2172 else if (direction == MV_DOWN)
2175 *comes_from_x = oldx;
2176 *comes_from_y = oldy;
2179 int MovingOrBlocked2Element(int x, int y)
2181 int element = Feld[x][y];
2183 if (element == EL_BLOCKED)
2187 Blocked2Moving(x, y, &oldx, &oldy);
2188 return Feld[oldx][oldy];
2194 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2196 /* like MovingOrBlocked2Element(), but if element is moving
2197 and (x,y) is the field the moving element is just leaving,
2198 return EL_BLOCKED instead of the element value */
2199 int element = Feld[x][y];
2201 if (IS_MOVING(x, y))
2203 if (element == EL_BLOCKED)
2207 Blocked2Moving(x, y, &oldx, &oldy);
2208 return Feld[oldx][oldy];
2217 static void RemoveField(int x, int y)
2219 Feld[x][y] = EL_EMPTY;
2226 ChangeDelay[x][y] = 0;
2227 ChangePage[x][y] = -1;
2228 Pushed[x][y] = FALSE;
2230 GfxElement[x][y] = EL_UNDEFINED;
2231 GfxAction[x][y] = ACTION_DEFAULT;
2232 GfxDir[x][y] = MV_NO_MOVING;
2235 void RemoveMovingField(int x, int y)
2237 int oldx = x, oldy = y, newx = x, newy = y;
2238 int element = Feld[x][y];
2239 int next_element = EL_UNDEFINED;
2241 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2244 if (IS_MOVING(x, y))
2246 Moving2Blocked(x, y, &newx, &newy);
2247 if (Feld[newx][newy] != EL_BLOCKED)
2250 else if (element == EL_BLOCKED)
2252 Blocked2Moving(x, y, &oldx, &oldy);
2253 if (!IS_MOVING(oldx, oldy))
2257 if (element == EL_BLOCKED &&
2258 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2259 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2260 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2261 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2262 next_element = get_next_element(Feld[oldx][oldy]);
2264 RemoveField(oldx, oldy);
2265 RemoveField(newx, newy);
2267 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2269 if (next_element != EL_UNDEFINED)
2270 Feld[oldx][oldy] = next_element;
2272 DrawLevelField(oldx, oldy);
2273 DrawLevelField(newx, newy);
2276 void DrawDynamite(int x, int y)
2278 int sx = SCREENX(x), sy = SCREENY(y);
2279 int graphic = el2img(Feld[x][y]);
2282 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2285 if (IS_WALKABLE_INSIDE(Back[x][y]))
2289 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2290 else if (Store[x][y])
2291 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2293 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2296 if (Back[x][y] || Store[x][y])
2297 DrawGraphicThruMask(sx, sy, graphic, frame);
2299 DrawGraphic(sx, sy, graphic, frame);
2301 if (game.emulation == EMU_SUPAPLEX)
2302 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2303 else if (Store[x][y])
2304 DrawGraphicThruMask(sx, sy, graphic, frame);
2306 DrawGraphic(sx, sy, graphic, frame);
2310 void CheckDynamite(int x, int y)
2312 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2316 if (MovDelay[x][y] != 0)
2319 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2326 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2328 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2329 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2330 StopSound(SND_DYNAMITE_ACTIVE);
2332 StopSound(SND_DYNABOMB_ACTIVE);
2338 void RelocatePlayer(int x, int y, int element)
2340 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2341 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2342 boolean no_delay = (tape.index_search);
2343 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2344 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2346 if (player->GameOver) /* do not reanimate dead player */
2350 RemoveField(x, y); /* temporarily remove newly placed player */
2351 DrawLevelField(x, y);
2354 if (player->present)
2356 while (player->MovPos)
2358 ScrollPlayer(player, SCROLL_GO_ON);
2359 ScrollScreen(NULL, SCROLL_GO_ON);
2365 Delay(wait_delay_value);
2368 DrawPlayer(player); /* needed here only to cleanup last field */
2369 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2371 player->is_moving = FALSE;
2374 Feld[x][y] = element;
2375 InitPlayerField(x, y, element, TRUE);
2377 if (player == local_player)
2379 int scroll_xx = -999, scroll_yy = -999;
2381 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2384 int fx = FX, fy = FY;
2386 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2387 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2388 local_player->jx - MIDPOSX);
2390 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2391 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2392 local_player->jy - MIDPOSY);
2394 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2395 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2400 fx += dx * TILEX / 2;
2401 fy += dy * TILEY / 2;
2403 ScrollLevel(dx, dy);
2406 /* scroll in two steps of half tile size to make things smoother */
2407 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2409 Delay(wait_delay_value);
2411 /* scroll second step to align at full tile size */
2413 Delay(wait_delay_value);
2418 void Explode(int ex, int ey, int phase, int mode)
2422 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2423 int last_phase = num_phase * delay;
2424 int half_phase = (num_phase / 2) * delay;
2425 int first_phase_after_start = EX_PHASE_START + 1;
2427 if (game.explosions_delayed)
2429 ExplodeField[ex][ey] = mode;
2433 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2435 int center_element = Feld[ex][ey];
2438 /* --- This is only really needed (and now handled) in "Impact()". --- */
2439 /* do not explode moving elements that left the explode field in time */
2440 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2441 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2445 if (mode == EX_NORMAL || mode == EX_CENTER)
2446 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2448 /* remove things displayed in background while burning dynamite */
2449 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2452 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2454 /* put moving element to center field (and let it explode there) */
2455 center_element = MovingOrBlocked2Element(ex, ey);
2456 RemoveMovingField(ex, ey);
2457 Feld[ex][ey] = center_element;
2460 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2462 int xx = x - ex + 1;
2463 int yy = y - ey + 1;
2466 if (!IN_LEV_FIELD(x, y) ||
2467 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2468 (x != ex || y != ey)))
2471 element = Feld[x][y];
2473 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2475 element = MovingOrBlocked2Element(x, y);
2477 if (!IS_EXPLOSION_PROOF(element))
2478 RemoveMovingField(x, y);
2484 if (IS_EXPLOSION_PROOF(element))
2487 /* indestructible elements can only explode in center (but not flames) */
2488 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2489 element == EL_FLAMES)
2494 if ((IS_INDESTRUCTIBLE(element) &&
2495 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2496 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2497 element == EL_FLAMES)
2501 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2503 if (IS_ACTIVE_BOMB(element))
2505 /* re-activate things under the bomb like gate or penguin */
2506 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2513 /* save walkable background elements while explosion on same tile */
2515 if (IS_INDESTRUCTIBLE(element))
2516 Back[x][y] = element;
2518 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2519 Back[x][y] = element;
2522 /* ignite explodable elements reached by other explosion */
2523 if (element == EL_EXPLOSION)
2524 element = Store2[x][y];
2527 if (AmoebaNr[x][y] &&
2528 (element == EL_AMOEBA_FULL ||
2529 element == EL_BD_AMOEBA ||
2530 element == EL_AMOEBA_GROWING))
2532 AmoebaCnt[AmoebaNr[x][y]]--;
2533 AmoebaCnt2[AmoebaNr[x][y]]--;
2539 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2541 switch(StorePlayer[ex][ey])
2544 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2547 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2550 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2554 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2558 if (game.emulation == EMU_SUPAPLEX)
2559 Store[x][y] = EL_EMPTY;
2561 else if (center_element == EL_MOLE)
2562 Store[x][y] = EL_EMERALD_RED;
2563 else if (center_element == EL_PENGUIN)
2564 Store[x][y] = EL_EMERALD_PURPLE;
2565 else if (center_element == EL_BUG)
2566 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2567 else if (center_element == EL_BD_BUTTERFLY)
2568 Store[x][y] = EL_BD_DIAMOND;
2569 else if (center_element == EL_SP_ELECTRON)
2570 Store[x][y] = EL_SP_INFOTRON;
2571 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2572 Store[x][y] = level.amoeba_content;
2573 else if (center_element == EL_YAMYAM)
2574 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2575 else if (IS_CUSTOM_ELEMENT(center_element) &&
2576 element_info[center_element].content[xx][yy] != EL_EMPTY)
2577 Store[x][y] = element_info[center_element].content[xx][yy];
2578 else if (element == EL_WALL_EMERALD)
2579 Store[x][y] = EL_EMERALD;
2580 else if (element == EL_WALL_DIAMOND)
2581 Store[x][y] = EL_DIAMOND;
2582 else if (element == EL_WALL_BD_DIAMOND)
2583 Store[x][y] = EL_BD_DIAMOND;
2584 else if (element == EL_WALL_EMERALD_YELLOW)
2585 Store[x][y] = EL_EMERALD_YELLOW;
2586 else if (element == EL_WALL_EMERALD_RED)
2587 Store[x][y] = EL_EMERALD_RED;
2588 else if (element == EL_WALL_EMERALD_PURPLE)
2589 Store[x][y] = EL_EMERALD_PURPLE;
2590 else if (element == EL_WALL_PEARL)
2591 Store[x][y] = EL_PEARL;
2592 else if (element == EL_WALL_CRYSTAL)
2593 Store[x][y] = EL_CRYSTAL;
2594 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2595 Store[x][y] = element_info[element].content[1][1];
2597 Store[x][y] = EL_EMPTY;
2599 if (x != ex || y != ey ||
2600 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2601 Store2[x][y] = element;
2604 if (AmoebaNr[x][y] &&
2605 (element == EL_AMOEBA_FULL ||
2606 element == EL_BD_AMOEBA ||
2607 element == EL_AMOEBA_GROWING))
2609 AmoebaCnt[AmoebaNr[x][y]]--;
2610 AmoebaCnt2[AmoebaNr[x][y]]--;
2616 MovDir[x][y] = MovPos[x][y] = 0;
2617 GfxDir[x][y] = MovDir[x][y];
2622 Feld[x][y] = EL_EXPLOSION;
2624 GfxElement[x][y] = center_element;
2626 GfxElement[x][y] = EL_UNDEFINED;
2629 ExplodePhase[x][y] = 1;
2633 if (center_element == EL_YAMYAM)
2634 game.yamyam_content_nr =
2635 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2646 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2650 /* activate this even in non-DEBUG version until cause for crash in
2651 getGraphicAnimationFrame() (see below) is found and eliminated */
2655 if (GfxElement[x][y] == EL_UNDEFINED)
2658 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2659 printf("Explode(): This should never happen!\n");
2662 GfxElement[x][y] = EL_EMPTY;
2666 if (phase == first_phase_after_start)
2668 int element = Store2[x][y];
2670 if (element == EL_BLACK_ORB)
2672 Feld[x][y] = Store2[x][y];
2677 else if (phase == half_phase)
2679 int element = Store2[x][y];
2681 if (IS_PLAYER(x, y))
2682 KillHeroUnlessProtected(x, y);
2683 else if (CAN_EXPLODE_BY_FIRE(element))
2685 Feld[x][y] = Store2[x][y];
2689 else if (element == EL_AMOEBA_TO_DIAMOND)
2690 AmoebeUmwandeln(x, y);
2693 if (phase == last_phase)
2697 element = Feld[x][y] = Store[x][y];
2698 Store[x][y] = Store2[x][y] = 0;
2699 GfxElement[x][y] = EL_UNDEFINED;
2701 /* player can escape from explosions and might therefore be still alive */
2702 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2703 element <= EL_PLAYER_IS_EXPLODING_4)
2704 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2706 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2707 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2708 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2711 /* restore probably existing indestructible background element */
2712 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2713 element = Feld[x][y] = Back[x][y];
2716 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2717 GfxDir[x][y] = MV_NO_MOVING;
2718 ChangeDelay[x][y] = 0;
2719 ChangePage[x][y] = -1;
2721 InitField(x, y, FALSE);
2723 /* !!! not needed !!! */
2724 if (CAN_MOVE(element))
2727 DrawLevelField(x, y);
2729 TestIfElementTouchesCustomElement(x, y);
2731 if (GFX_CRUMBLED(element))
2732 DrawLevelFieldCrumbledSandNeighbours(x, y);
2734 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2735 StorePlayer[x][y] = 0;
2737 if (ELEM_IS_PLAYER(element))
2738 RelocatePlayer(x, y, element);
2740 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2743 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2745 int stored = Store[x][y];
2746 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2747 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2750 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2753 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2754 element_info[GfxElement[x][y]].token_name,
2759 DrawLevelFieldCrumbledSand(x, y);
2761 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2763 DrawLevelElement(x, y, Back[x][y]);
2764 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2766 else if (IS_WALKABLE_UNDER(Back[x][y]))
2768 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2769 DrawLevelElementThruMask(x, y, Back[x][y]);
2771 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2772 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2776 void DynaExplode(int ex, int ey)
2779 int dynabomb_element = Feld[ex][ey];
2780 int dynabomb_size = 1;
2781 boolean dynabomb_xl = FALSE;
2782 struct PlayerInfo *player;
2783 static int xy[4][2] =
2791 if (IS_ACTIVE_BOMB(dynabomb_element))
2793 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2794 dynabomb_size = player->dynabomb_size;
2795 dynabomb_xl = player->dynabomb_xl;
2796 player->dynabombs_left++;
2799 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2801 for (i = 0; i < 4; i++)
2803 for (j = 1; j <= dynabomb_size; j++)
2805 int x = ex + j * xy[i % 4][0];
2806 int y = ey + j * xy[i % 4][1];
2809 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2812 element = Feld[x][y];
2814 /* do not restart explosions of fields with active bombs */
2815 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2818 Explode(x, y, EX_PHASE_START, EX_BORDER);
2820 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2821 if (element != EL_EMPTY &&
2822 element != EL_SAND &&
2823 element != EL_EXPLOSION &&
2830 void Bang(int x, int y)
2833 int element = MovingOrBlocked2Element(x, y);
2835 int element = Feld[x][y];
2839 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2841 if (IS_PLAYER(x, y))
2844 struct PlayerInfo *player = PLAYERINFO(x, y);
2846 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2847 player->element_nr);
2852 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2854 if (game.emulation == EMU_SUPAPLEX)
2855 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2857 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2862 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2870 case EL_BD_BUTTERFLY:
2873 case EL_DARK_YAMYAM:
2877 RaiseScoreElement(element);
2878 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2880 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2881 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2882 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2883 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2884 case EL_DYNABOMB_INCREASE_NUMBER:
2885 case EL_DYNABOMB_INCREASE_SIZE:
2886 case EL_DYNABOMB_INCREASE_POWER:
2891 case EL_LAMP_ACTIVE:
2892 if (IS_PLAYER(x, y))
2893 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2895 Explode(x, y, EX_PHASE_START, EX_CENTER);
2898 if (CAN_EXPLODE_DYNA(element))
2900 else if (CAN_EXPLODE_1X1(element))
2901 Explode(x, y, EX_PHASE_START, EX_CENTER);
2903 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2907 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2910 void SplashAcid(int x, int y)
2912 int element = Feld[x][y];
2914 if (element != EL_ACID_SPLASH_LEFT &&
2915 element != EL_ACID_SPLASH_RIGHT)
2917 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2919 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2920 (!IN_LEV_FIELD(x-1, y-1) ||
2921 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2922 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2924 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2925 (!IN_LEV_FIELD(x+1, y-1) ||
2926 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2927 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2931 static void InitBeltMovement()
2933 static int belt_base_element[4] =
2935 EL_CONVEYOR_BELT_1_LEFT,
2936 EL_CONVEYOR_BELT_2_LEFT,
2937 EL_CONVEYOR_BELT_3_LEFT,
2938 EL_CONVEYOR_BELT_4_LEFT
2940 static int belt_base_active_element[4] =
2942 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2943 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2944 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2945 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2950 /* set frame order for belt animation graphic according to belt direction */
2951 for (i = 0; i < 4; i++)
2955 for (j = 0; j < 3; j++)
2957 int element = belt_base_active_element[belt_nr] + j;
2958 int graphic = el2img(element);
2960 if (game.belt_dir[i] == MV_LEFT)
2961 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2963 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2967 for (y = 0; y < lev_fieldy; y++)
2969 for (x = 0; x < lev_fieldx; x++)
2971 int element = Feld[x][y];
2973 for (i = 0; i < 4; i++)
2975 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2977 int e_belt_nr = getBeltNrFromBeltElement(element);
2980 if (e_belt_nr == belt_nr)
2982 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2984 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2992 static void ToggleBeltSwitch(int x, int y)
2994 static int belt_base_element[4] =
2996 EL_CONVEYOR_BELT_1_LEFT,
2997 EL_CONVEYOR_BELT_2_LEFT,
2998 EL_CONVEYOR_BELT_3_LEFT,
2999 EL_CONVEYOR_BELT_4_LEFT
3001 static int belt_base_active_element[4] =
3003 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3004 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3005 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3006 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3008 static int belt_base_switch_element[4] =
3010 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3011 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3012 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3013 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3015 static int belt_move_dir[4] =
3023 int element = Feld[x][y];
3024 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3025 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3026 int belt_dir = belt_move_dir[belt_dir_nr];
3029 if (!IS_BELT_SWITCH(element))
3032 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3033 game.belt_dir[belt_nr] = belt_dir;
3035 if (belt_dir_nr == 3)
3038 /* set frame order for belt animation graphic according to belt direction */
3039 for (i = 0; i < 3; i++)
3041 int element = belt_base_active_element[belt_nr] + i;
3042 int graphic = el2img(element);
3044 if (belt_dir == MV_LEFT)
3045 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3047 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3050 for (yy = 0; yy < lev_fieldy; yy++)
3052 for (xx = 0; xx < lev_fieldx; xx++)
3054 int element = Feld[xx][yy];
3056 if (IS_BELT_SWITCH(element))
3058 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3060 if (e_belt_nr == belt_nr)
3062 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3063 DrawLevelField(xx, yy);
3066 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3068 int e_belt_nr = getBeltNrFromBeltElement(element);
3070 if (e_belt_nr == belt_nr)
3072 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3074 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3075 DrawLevelField(xx, yy);
3078 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3080 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3082 if (e_belt_nr == belt_nr)
3084 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3086 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3087 DrawLevelField(xx, yy);
3094 static void ToggleSwitchgateSwitch(int x, int y)
3098 game.switchgate_pos = !game.switchgate_pos;
3100 for (yy = 0; yy < lev_fieldy; yy++)
3102 for (xx = 0; xx < lev_fieldx; xx++)
3104 int element = Feld[xx][yy];
3106 if (element == EL_SWITCHGATE_SWITCH_UP ||
3107 element == EL_SWITCHGATE_SWITCH_DOWN)
3109 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3110 DrawLevelField(xx, yy);
3112 else if (element == EL_SWITCHGATE_OPEN ||
3113 element == EL_SWITCHGATE_OPENING)
3115 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3117 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3119 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3122 else if (element == EL_SWITCHGATE_CLOSED ||
3123 element == EL_SWITCHGATE_CLOSING)
3125 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3127 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3129 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3136 static int getInvisibleActiveFromInvisibleElement(int element)
3138 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3139 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3140 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3144 static int getInvisibleFromInvisibleActiveElement(int element)
3146 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3147 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3148 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3152 static void RedrawAllLightSwitchesAndInvisibleElements()
3156 for (y = 0; y < lev_fieldy; y++)
3158 for (x = 0; x < lev_fieldx; x++)
3160 int element = Feld[x][y];
3162 if (element == EL_LIGHT_SWITCH &&
3163 game.light_time_left > 0)
3165 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3166 DrawLevelField(x, y);
3168 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3169 game.light_time_left == 0)
3171 Feld[x][y] = EL_LIGHT_SWITCH;
3172 DrawLevelField(x, y);
3174 else if (element == EL_INVISIBLE_STEELWALL ||
3175 element == EL_INVISIBLE_WALL ||
3176 element == EL_INVISIBLE_SAND)
3178 if (game.light_time_left > 0)
3179 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3181 DrawLevelField(x, y);
3183 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3184 element == EL_INVISIBLE_WALL_ACTIVE ||
3185 element == EL_INVISIBLE_SAND_ACTIVE)
3187 if (game.light_time_left == 0)
3188 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3190 DrawLevelField(x, y);
3196 static void ToggleLightSwitch(int x, int y)
3198 int element = Feld[x][y];
3200 game.light_time_left =
3201 (element == EL_LIGHT_SWITCH ?
3202 level.time_light * FRAMES_PER_SECOND : 0);
3204 RedrawAllLightSwitchesAndInvisibleElements();
3207 static void ActivateTimegateSwitch(int x, int y)
3211 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3213 for (yy = 0; yy < lev_fieldy; yy++)
3215 for (xx = 0; xx < lev_fieldx; xx++)
3217 int element = Feld[xx][yy];
3219 if (element == EL_TIMEGATE_CLOSED ||
3220 element == EL_TIMEGATE_CLOSING)
3222 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3223 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3227 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3229 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3230 DrawLevelField(xx, yy);
3237 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3240 inline static int getElementMoveStepsize(int x, int y)
3242 int element = Feld[x][y];
3243 int direction = MovDir[x][y];
3244 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3245 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3246 int horiz_move = (dx != 0);
3247 int sign = (horiz_move ? dx : dy);
3248 int step = sign * element_info[element].move_stepsize;
3250 /* special values for move stepsize for spring and things on conveyor belt */
3253 if (CAN_FALL(element) &&
3254 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3255 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3256 else if (element == EL_SPRING)
3257 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3263 void Impact(int x, int y)
3265 boolean lastline = (y == lev_fieldy-1);
3266 boolean object_hit = FALSE;
3267 boolean impact = (lastline || object_hit);
3268 int element = Feld[x][y];
3269 int smashed = EL_UNDEFINED;
3271 if (!lastline) /* check if element below was hit */
3273 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3276 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3277 MovDir[x][y + 1] != MV_DOWN ||
3278 MovPos[x][y + 1] <= TILEY / 2));
3281 object_hit = !IS_FREE(x, y + 1);
3284 /* do not smash moving elements that left the smashed field in time */
3285 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3286 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3290 smashed = MovingOrBlocked2Element(x, y + 1);
3292 impact = (lastline || object_hit);
3295 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3301 /* only reset graphic animation if graphic really changes after impact */
3303 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3305 ResetGfxAnimation(x, y);
3306 DrawLevelField(x, y);
3309 if (impact && CAN_EXPLODE_IMPACT(element))
3314 else if (impact && element == EL_PEARL)
3316 Feld[x][y] = EL_PEARL_BREAKING;
3317 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3320 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3322 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3327 if (impact && element == EL_AMOEBA_DROP)
3329 if (object_hit && IS_PLAYER(x, y + 1))
3330 KillHeroUnlessProtected(x, y + 1);
3331 else if (object_hit && smashed == EL_PENGUIN)
3335 Feld[x][y] = EL_AMOEBA_GROWING;
3336 Store[x][y] = EL_AMOEBA_WET;
3338 ResetRandomAnimationValue(x, y);
3343 if (object_hit) /* check which object was hit */
3345 if (CAN_PASS_MAGIC_WALL(element) &&
3346 (smashed == EL_MAGIC_WALL ||
3347 smashed == EL_BD_MAGIC_WALL))
3350 int activated_magic_wall =
3351 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3352 EL_BD_MAGIC_WALL_ACTIVE);
3354 /* activate magic wall / mill */
3355 for (yy = 0; yy < lev_fieldy; yy++)
3356 for (xx = 0; xx < lev_fieldx; xx++)
3357 if (Feld[xx][yy] == smashed)
3358 Feld[xx][yy] = activated_magic_wall;
3360 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3361 game.magic_wall_active = TRUE;
3363 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3364 SND_MAGIC_WALL_ACTIVATING :
3365 SND_BD_MAGIC_WALL_ACTIVATING));
3368 if (IS_PLAYER(x, y + 1))
3370 if (CAN_SMASH_PLAYER(element))
3372 KillHeroUnlessProtected(x, y + 1);
3376 else if (smashed == EL_PENGUIN)
3378 if (CAN_SMASH_PLAYER(element))
3384 else if (element == EL_BD_DIAMOND)
3386 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3392 else if (((element == EL_SP_INFOTRON ||
3393 element == EL_SP_ZONK) &&
3394 (smashed == EL_SP_SNIKSNAK ||
3395 smashed == EL_SP_ELECTRON ||
3396 smashed == EL_SP_DISK_ORANGE)) ||
3397 (element == EL_SP_INFOTRON &&
3398 smashed == EL_SP_DISK_YELLOW))
3404 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3410 else if (CAN_SMASH_EVERYTHING(element))
3412 if (IS_CLASSIC_ENEMY(smashed) ||
3413 CAN_EXPLODE_SMASHED(smashed))
3418 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3420 if (smashed == EL_LAMP ||
3421 smashed == EL_LAMP_ACTIVE)
3426 else if (smashed == EL_NUT)
3428 Feld[x][y + 1] = EL_NUT_BREAKING;
3429 PlayLevelSound(x, y, SND_NUT_BREAKING);
3430 RaiseScoreElement(EL_NUT);
3433 else if (smashed == EL_PEARL)
3435 Feld[x][y + 1] = EL_PEARL_BREAKING;
3436 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3439 else if (smashed == EL_DIAMOND)
3441 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3442 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3445 else if (IS_BELT_SWITCH(smashed))
3447 ToggleBeltSwitch(x, y + 1);
3449 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3450 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3452 ToggleSwitchgateSwitch(x, y + 1);
3454 else if (smashed == EL_LIGHT_SWITCH ||
3455 smashed == EL_LIGHT_SWITCH_ACTIVE)
3457 ToggleLightSwitch(x, y + 1);
3461 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3463 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3464 CE_OTHER_IS_SWITCHING);
3465 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3471 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3476 /* play sound of magic wall / mill */
3478 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3479 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3481 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3482 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3483 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3484 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3489 /* play sound of object that hits the ground */
3490 if (lastline || object_hit)
3491 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3494 inline static void TurnRoundExt(int x, int y)
3506 { 0, 0 }, { 0, 0 }, { 0, 0 },
3511 int left, right, back;
3515 { MV_DOWN, MV_UP, MV_RIGHT },
3516 { MV_UP, MV_DOWN, MV_LEFT },
3518 { MV_LEFT, MV_RIGHT, MV_DOWN },
3522 { MV_RIGHT, MV_LEFT, MV_UP }
3525 int element = Feld[x][y];
3526 int move_pattern = element_info[element].move_pattern;
3528 int old_move_dir = MovDir[x][y];
3529 int left_dir = turn[old_move_dir].left;
3530 int right_dir = turn[old_move_dir].right;
3531 int back_dir = turn[old_move_dir].back;
3533 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3534 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3535 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3536 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3538 int left_x = x + left_dx, left_y = y + left_dy;
3539 int right_x = x + right_dx, right_y = y + right_dy;
3540 int move_x = x + move_dx, move_y = y + move_dy;
3544 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3546 TestIfBadThingTouchesOtherBadThing(x, y);
3548 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3549 MovDir[x][y] = right_dir;
3550 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3551 MovDir[x][y] = left_dir;
3553 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3555 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3558 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3559 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3561 TestIfBadThingTouchesOtherBadThing(x, y);
3563 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3564 MovDir[x][y] = left_dir;
3565 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3566 MovDir[x][y] = right_dir;
3568 if ((element == EL_SPACESHIP ||
3569 element == EL_SP_SNIKSNAK ||
3570 element == EL_SP_ELECTRON)
3571 && MovDir[x][y] != old_move_dir)
3573 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3576 else if (element == EL_YAMYAM)
3578 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3579 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3581 if (can_turn_left && can_turn_right)
3582 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3583 else if (can_turn_left)
3584 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3585 else if (can_turn_right)
3586 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3588 MovDir[x][y] = back_dir;
3590 MovDelay[x][y] = 16 + 16 * RND(3);
3592 else if (element == EL_DARK_YAMYAM)
3594 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3595 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3597 if (can_turn_left && can_turn_right)
3598 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3599 else if (can_turn_left)
3600 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3601 else if (can_turn_right)
3602 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3604 MovDir[x][y] = back_dir;
3606 MovDelay[x][y] = 16 + 16 * RND(3);
3608 else if (element == EL_PACMAN)
3610 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3611 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3613 if (can_turn_left && can_turn_right)
3614 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3615 else if (can_turn_left)
3616 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3617 else if (can_turn_right)
3618 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3620 MovDir[x][y] = back_dir;
3622 MovDelay[x][y] = 6 + RND(40);
3624 else if (element == EL_PIG)
3626 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3627 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3628 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3629 boolean should_turn_left, should_turn_right, should_move_on;
3631 int rnd = RND(rnd_value);
3633 should_turn_left = (can_turn_left &&
3635 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3636 y + back_dy + left_dy)));
3637 should_turn_right = (can_turn_right &&
3639 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3640 y + back_dy + right_dy)));
3641 should_move_on = (can_move_on &&
3644 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3645 y + move_dy + left_dy) ||
3646 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3647 y + move_dy + right_dy)));
3649 if (should_turn_left || should_turn_right || should_move_on)
3651 if (should_turn_left && should_turn_right && should_move_on)
3652 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3653 rnd < 2 * rnd_value / 3 ? right_dir :
3655 else if (should_turn_left && should_turn_right)
3656 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3657 else if (should_turn_left && should_move_on)
3658 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3659 else if (should_turn_right && should_move_on)
3660 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3661 else if (should_turn_left)
3662 MovDir[x][y] = left_dir;
3663 else if (should_turn_right)
3664 MovDir[x][y] = right_dir;
3665 else if (should_move_on)
3666 MovDir[x][y] = old_move_dir;
3668 else if (can_move_on && rnd > rnd_value / 8)
3669 MovDir[x][y] = old_move_dir;
3670 else if (can_turn_left && can_turn_right)
3671 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3672 else if (can_turn_left && rnd > rnd_value / 8)
3673 MovDir[x][y] = left_dir;
3674 else if (can_turn_right && rnd > rnd_value/8)
3675 MovDir[x][y] = right_dir;
3677 MovDir[x][y] = back_dir;
3679 xx = x + move_xy[MovDir[x][y]].x;
3680 yy = y + move_xy[MovDir[x][y]].y;
3682 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3683 MovDir[x][y] = old_move_dir;
3687 else if (element == EL_DRAGON)
3689 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3690 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3691 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3693 int rnd = RND(rnd_value);
3696 if (FrameCounter < 1 && x == 0 && y == 29)
3697 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3700 if (can_move_on && rnd > rnd_value / 8)
3701 MovDir[x][y] = old_move_dir;
3702 else if (can_turn_left && can_turn_right)
3703 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3704 else if (can_turn_left && rnd > rnd_value / 8)
3705 MovDir[x][y] = left_dir;
3706 else if (can_turn_right && rnd > rnd_value / 8)
3707 MovDir[x][y] = right_dir;
3709 MovDir[x][y] = back_dir;
3711 xx = x + move_xy[MovDir[x][y]].x;
3712 yy = y + move_xy[MovDir[x][y]].y;
3715 if (FrameCounter < 1 && x == 0 && y == 29)
3716 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3717 xx, yy, Feld[xx][yy],
3722 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3723 MovDir[x][y] = old_move_dir;
3725 if (!IS_FREE(xx, yy))
3726 MovDir[x][y] = old_move_dir;
3730 if (FrameCounter < 1 && x == 0 && y == 29)
3731 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3736 else if (element == EL_MOLE)
3738 boolean can_move_on =
3739 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3740 IS_AMOEBOID(Feld[move_x][move_y]) ||
3741 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3744 boolean can_turn_left =
3745 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3746 IS_AMOEBOID(Feld[left_x][left_y])));
3748 boolean can_turn_right =
3749 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3750 IS_AMOEBOID(Feld[right_x][right_y])));
3752 if (can_turn_left && can_turn_right)
3753 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3754 else if (can_turn_left)
3755 MovDir[x][y] = left_dir;
3757 MovDir[x][y] = right_dir;
3760 if (MovDir[x][y] != old_move_dir)
3763 else if (element == EL_BALLOON)
3765 MovDir[x][y] = game.balloon_dir;
3768 else if (element == EL_SPRING)
3770 if (MovDir[x][y] & MV_HORIZONTAL &&
3771 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3772 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3773 MovDir[x][y] = MV_NO_MOVING;
3777 else if (element == EL_ROBOT ||
3778 element == EL_SATELLITE ||
3779 element == EL_PENGUIN)
3781 int attr_x = -1, attr_y = -1;
3792 for (i = 0; i < MAX_PLAYERS; i++)
3794 struct PlayerInfo *player = &stored_player[i];
3795 int jx = player->jx, jy = player->jy;
3797 if (!player->active)
3801 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3809 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3815 if (element == EL_PENGUIN)
3818 static int xy[4][2] =
3826 for (i = 0; i < 4; i++)
3828 int ex = x + xy[i % 4][0];
3829 int ey = y + xy[i % 4][1];
3831 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3840 MovDir[x][y] = MV_NO_MOVING;
3842 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3843 else if (attr_x > x)
3844 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3846 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3847 else if (attr_y > y)
3848 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3850 if (element == EL_ROBOT)
3854 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3855 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3856 Moving2Blocked(x, y, &newx, &newy);
3858 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3859 MovDelay[x][y] = 8 + 8 * !RND(3);
3861 MovDelay[x][y] = 16;
3863 else if (element == EL_PENGUIN)
3869 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3871 boolean first_horiz = RND(2);
3872 int new_move_dir = MovDir[x][y];
3875 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3876 Moving2Blocked(x, y, &newx, &newy);
3878 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3882 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3883 Moving2Blocked(x, y, &newx, &newy);
3885 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3888 MovDir[x][y] = old_move_dir;
3892 else /* (element == EL_SATELLITE) */
3898 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3900 boolean first_horiz = RND(2);
3901 int new_move_dir = MovDir[x][y];
3904 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3905 Moving2Blocked(x, y, &newx, &newy);
3907 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3911 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3912 Moving2Blocked(x, y, &newx, &newy);
3914 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3917 MovDir[x][y] = old_move_dir;
3922 else if (move_pattern == MV_TURNING_LEFT ||
3923 move_pattern == MV_TURNING_RIGHT ||
3924 move_pattern == MV_TURNING_LEFT_RIGHT ||
3925 move_pattern == MV_TURNING_RIGHT_LEFT ||
3926 move_pattern == MV_TURNING_RANDOM ||
3927 move_pattern == MV_ALL_DIRECTIONS)
3929 boolean can_turn_left =
3930 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3931 boolean can_turn_right =
3932 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3934 if (move_pattern == MV_TURNING_LEFT)
3935 MovDir[x][y] = left_dir;
3936 else if (move_pattern == MV_TURNING_RIGHT)
3937 MovDir[x][y] = right_dir;
3938 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3939 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3940 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3941 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3942 else if (move_pattern == MV_TURNING_RANDOM)
3943 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3944 can_turn_right && !can_turn_left ? right_dir :
3945 RND(2) ? left_dir : right_dir);
3946 else if (can_turn_left && can_turn_right)
3947 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3948 else if (can_turn_left)
3949 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3950 else if (can_turn_right)
3951 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3953 MovDir[x][y] = back_dir;
3955 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3957 else if (move_pattern == MV_HORIZONTAL ||
3958 move_pattern == MV_VERTICAL)
3960 if (move_pattern & old_move_dir)
3961 MovDir[x][y] = back_dir;
3962 else if (move_pattern == MV_HORIZONTAL)
3963 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3964 else if (move_pattern == MV_VERTICAL)
3965 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3967 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3969 else if (move_pattern & MV_ANY_DIRECTION)
3971 MovDir[x][y] = move_pattern;
3972 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3974 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3976 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3977 MovDir[x][y] = left_dir;
3978 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3979 MovDir[x][y] = right_dir;
3981 if (MovDir[x][y] != old_move_dir)
3982 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3984 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3986 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3987 MovDir[x][y] = right_dir;
3988 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3989 MovDir[x][y] = left_dir;
3991 if (MovDir[x][y] != old_move_dir)
3992 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3994 else if (move_pattern == MV_TOWARDS_PLAYER ||
3995 move_pattern == MV_AWAY_FROM_PLAYER)
3997 int attr_x = -1, attr_y = -1;
3999 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4010 for (i = 0; i < MAX_PLAYERS; i++)
4012 struct PlayerInfo *player = &stored_player[i];
4013 int jx = player->jx, jy = player->jy;
4015 if (!player->active)
4019 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4027 MovDir[x][y] = MV_NO_MOVING;
4029 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4030 else if (attr_x > x)
4031 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4033 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4034 else if (attr_y > y)
4035 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4037 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4039 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4041 boolean first_horiz = RND(2);
4042 int new_move_dir = MovDir[x][y];
4045 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4046 Moving2Blocked(x, y, &newx, &newy);
4048 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4052 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4053 Moving2Blocked(x, y, &newx, &newy);
4055 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4058 MovDir[x][y] = old_move_dir;
4061 else if (move_pattern == MV_WHEN_PUSHED ||
4062 move_pattern == MV_WHEN_DROPPED)
4064 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4065 MovDir[x][y] = MV_NO_MOVING;
4069 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4071 static int test_xy[7][2] =
4081 static int test_dir[7] =
4091 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4092 int move_preference = -1000000; /* start with very low preference */
4093 int new_move_dir = MV_NO_MOVING;
4094 int start_test = RND(4);
4097 for (i = 0; i < 4; i++)
4099 int move_dir = test_dir[start_test + i];
4100 int move_dir_preference;
4102 xx = x + test_xy[start_test + i][0];
4103 yy = y + test_xy[start_test + i][1];
4105 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4106 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4108 new_move_dir = move_dir;
4113 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4116 move_dir_preference = -1 * RunnerVisit[xx][yy];
4117 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4118 move_dir_preference = PlayerVisit[xx][yy];
4120 if (move_dir_preference > move_preference)
4122 /* prefer field that has not been visited for the longest time */
4123 move_preference = move_dir_preference;
4124 new_move_dir = move_dir;
4126 else if (move_dir_preference == move_preference &&
4127 move_dir == old_move_dir)
4129 /* prefer last direction when all directions are preferred equally */
4130 move_preference = move_dir_preference;
4131 new_move_dir = move_dir;
4135 MovDir[x][y] = new_move_dir;
4136 if (old_move_dir != new_move_dir)
4141 static void TurnRound(int x, int y)
4143 int direction = MovDir[x][y];
4146 GfxDir[x][y] = MovDir[x][y];
4152 GfxDir[x][y] = MovDir[x][y];
4155 if (direction != MovDir[x][y])
4160 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4163 GfxAction[x][y] = ACTION_WAITING;
4167 static boolean JustBeingPushed(int x, int y)
4171 for (i = 0; i < MAX_PLAYERS; i++)
4173 struct PlayerInfo *player = &stored_player[i];
4175 if (player->active && player->is_pushing && player->MovPos)
4177 int next_jx = player->jx + (player->jx - player->last_jx);
4178 int next_jy = player->jy + (player->jy - player->last_jy);
4180 if (x == next_jx && y == next_jy)
4188 void StartMoving(int x, int y)
4190 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4191 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4192 int element = Feld[x][y];
4198 if (MovDelay[x][y] == 0)
4199 GfxAction[x][y] = ACTION_DEFAULT;
4201 /* !!! this should be handled more generic (not only for mole) !!! */
4202 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4203 GfxAction[x][y] = ACTION_DEFAULT;
4206 if (CAN_FALL(element) && y < lev_fieldy - 1)
4208 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4209 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4210 if (JustBeingPushed(x, y))
4213 if (element == EL_QUICKSAND_FULL)
4215 if (IS_FREE(x, y + 1))
4217 InitMovingField(x, y, MV_DOWN);
4218 started_moving = TRUE;
4220 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4221 Store[x][y] = EL_ROCK;
4223 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4225 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4228 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4230 if (!MovDelay[x][y])
4231 MovDelay[x][y] = TILEY + 1;
4240 Feld[x][y] = EL_QUICKSAND_EMPTY;
4241 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4242 Store[x][y + 1] = Store[x][y];
4245 PlayLevelSoundAction(x, y, ACTION_FILLING);
4247 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4251 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4252 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4254 InitMovingField(x, y, MV_DOWN);
4255 started_moving = TRUE;
4257 Feld[x][y] = EL_QUICKSAND_FILLING;
4258 Store[x][y] = element;
4260 PlayLevelSoundAction(x, y, ACTION_FILLING);
4262 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4265 else if (element == EL_MAGIC_WALL_FULL)
4267 if (IS_FREE(x, y + 1))
4269 InitMovingField(x, y, MV_DOWN);
4270 started_moving = TRUE;
4272 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4273 Store[x][y] = EL_CHANGED(Store[x][y]);
4275 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4277 if (!MovDelay[x][y])
4278 MovDelay[x][y] = TILEY/4 + 1;
4287 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4288 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4289 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4293 else if (element == EL_BD_MAGIC_WALL_FULL)
4295 if (IS_FREE(x, y + 1))
4297 InitMovingField(x, y, MV_DOWN);
4298 started_moving = TRUE;
4300 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4301 Store[x][y] = EL_CHANGED2(Store[x][y]);
4303 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4305 if (!MovDelay[x][y])
4306 MovDelay[x][y] = TILEY/4 + 1;
4315 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4316 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4317 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4321 else if (CAN_PASS_MAGIC_WALL(element) &&
4322 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4323 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4325 InitMovingField(x, y, MV_DOWN);
4326 started_moving = TRUE;
4329 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4330 EL_BD_MAGIC_WALL_FILLING);
4331 Store[x][y] = element;
4334 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4336 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4341 InitMovingField(x, y, MV_DOWN);
4342 started_moving = TRUE;
4344 Store[x][y] = EL_ACID;
4346 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4347 GfxAction[x][y + 1] = ACTION_ACTIVE;
4351 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4352 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4353 (Feld[x][y + 1] == EL_BLOCKED)) ||
4354 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4355 CAN_SMASH(element) && WasJustFalling[x][y] &&
4356 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4360 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4361 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4362 WasJustMoving[x][y] && !Pushed[x][y + 1])
4364 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4365 WasJustMoving[x][y])
4370 /* this is needed for a special case not covered by calling "Impact()"
4371 from "ContinueMoving()": if an element moves to a tile directly below
4372 another element which was just falling on that tile (which was empty
4373 in the previous frame), the falling element above would just stop
4374 instead of smashing the element below (in previous version, the above
4375 element was just checked for "moving" instead of "falling", resulting
4376 in incorrect smashes caused by horizontal movement of the above
4377 element; also, the case of the player being the element to smash was
4378 simply not covered here... :-/ ) */
4381 WasJustMoving[x][y] = 0;
4382 WasJustFalling[x][y] = 0;
4387 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4389 if (MovDir[x][y] == MV_NO_MOVING)
4391 InitMovingField(x, y, MV_DOWN);
4392 started_moving = TRUE;
4395 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4397 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4398 MovDir[x][y] = MV_DOWN;
4400 InitMovingField(x, y, MV_DOWN);
4401 started_moving = TRUE;
4403 else if (element == EL_AMOEBA_DROP)
4405 Feld[x][y] = EL_AMOEBA_GROWING;
4406 Store[x][y] = EL_AMOEBA_WET;
4408 /* Store[x][y + 1] must be zero, because:
4409 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4412 #if OLD_GAME_BEHAVIOUR
4413 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4415 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4416 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4417 element != EL_DX_SUPABOMB)
4420 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4421 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4422 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4423 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4426 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4427 (IS_FREE(x - 1, y + 1) ||
4428 Feld[x - 1][y + 1] == EL_ACID));
4429 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4430 (IS_FREE(x + 1, y + 1) ||
4431 Feld[x + 1][y + 1] == EL_ACID));
4432 boolean can_fall_any = (can_fall_left || can_fall_right);
4433 boolean can_fall_both = (can_fall_left && can_fall_right);
4435 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4437 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4439 if (slippery_type == SLIPPERY_ONLY_LEFT)
4440 can_fall_right = FALSE;
4441 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4442 can_fall_left = FALSE;
4443 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4444 can_fall_right = FALSE;
4445 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4446 can_fall_left = FALSE;
4448 can_fall_any = (can_fall_left || can_fall_right);
4449 can_fall_both = (can_fall_left && can_fall_right);
4454 if (can_fall_both &&
4455 (game.emulation != EMU_BOULDERDASH &&
4456 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4457 can_fall_left = !(can_fall_right = RND(2));
4459 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4460 started_moving = TRUE;
4463 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4465 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4466 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4467 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4468 int belt_dir = game.belt_dir[belt_nr];
4470 if ((belt_dir == MV_LEFT && left_is_free) ||
4471 (belt_dir == MV_RIGHT && right_is_free))
4473 InitMovingField(x, y, belt_dir);
4474 started_moving = TRUE;
4476 GfxAction[x][y] = ACTION_DEFAULT;
4481 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4482 if (CAN_MOVE(element) && !started_moving)
4484 int move_pattern = element_info[element].move_pattern;
4487 Moving2Blocked(x, y, &newx, &newy);
4490 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4493 if ((element == EL_SATELLITE ||
4494 element == EL_BALLOON ||
4495 element == EL_SPRING)
4496 && JustBeingPushed(x, y))
4501 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4502 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4503 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4506 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4507 element, element_info[element].token_name,
4508 WasJustMoving[x][y],
4509 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4510 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4511 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4512 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4516 WasJustMoving[x][y] = 0;
4519 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4522 if (Feld[x][y] != element) /* element has changed */
4524 element = Feld[x][y];
4525 move_pattern = element_info[element].move_pattern;
4527 if (!CAN_MOVE(element))
4531 if (Feld[x][y] != element) /* element has changed */
4539 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4540 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4542 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4544 Moving2Blocked(x, y, &newx, &newy);
4545 if (Feld[newx][newy] == EL_BLOCKED)
4546 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4552 if (FrameCounter < 1 && x == 0 && y == 29)
4553 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4556 if (!MovDelay[x][y]) /* start new movement phase */
4558 /* all objects that can change their move direction after each step
4559 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4561 if (element != EL_YAMYAM &&
4562 element != EL_DARK_YAMYAM &&
4563 element != EL_PACMAN &&
4564 !(move_pattern & MV_ANY_DIRECTION) &&
4565 move_pattern != MV_TURNING_LEFT &&
4566 move_pattern != MV_TURNING_RIGHT &&
4567 move_pattern != MV_TURNING_LEFT_RIGHT &&
4568 move_pattern != MV_TURNING_RIGHT_LEFT &&
4569 move_pattern != MV_TURNING_RANDOM)
4574 if (FrameCounter < 1 && x == 0 && y == 29)
4575 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4578 if (MovDelay[x][y] && (element == EL_BUG ||
4579 element == EL_SPACESHIP ||
4580 element == EL_SP_SNIKSNAK ||
4581 element == EL_SP_ELECTRON ||
4582 element == EL_MOLE))
4583 DrawLevelField(x, y);
4587 if (MovDelay[x][y]) /* wait some time before next movement */
4592 if (element == EL_YAMYAM)
4595 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4596 DrawLevelElementAnimation(x, y, element);
4600 if (MovDelay[x][y]) /* element still has to wait some time */
4603 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4604 ResetGfxAnimation(x, y);
4608 if (GfxAction[x][y] != ACTION_WAITING)
4609 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4611 GfxAction[x][y] = ACTION_WAITING;
4615 if (element == EL_ROBOT ||
4617 element == EL_PACMAN ||
4619 element == EL_YAMYAM ||
4620 element == EL_DARK_YAMYAM)
4623 DrawLevelElementAnimation(x, y, element);
4625 DrawLevelElementAnimationIfNeeded(x, y, element);
4627 PlayLevelSoundAction(x, y, ACTION_WAITING);
4629 else if (element == EL_SP_ELECTRON)
4630 DrawLevelElementAnimationIfNeeded(x, y, element);
4631 else if (element == EL_DRAGON)
4634 int dir = MovDir[x][y];
4635 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4636 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4637 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4638 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4639 dir == MV_UP ? IMG_FLAMES_1_UP :
4640 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4641 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4644 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4647 GfxAction[x][y] = ACTION_ATTACKING;
4649 if (IS_PLAYER(x, y))
4650 DrawPlayerField(x, y);
4652 DrawLevelField(x, y);
4654 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4656 for (i = 1; i <= 3; i++)
4658 int xx = x + i * dx;
4659 int yy = y + i * dy;
4660 int sx = SCREENX(xx);
4661 int sy = SCREENY(yy);
4662 int flame_graphic = graphic + (i - 1);
4664 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4669 int flamed = MovingOrBlocked2Element(xx, yy);
4671 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4674 RemoveMovingField(xx, yy);
4676 Feld[xx][yy] = EL_FLAMES;
4677 if (IN_SCR_FIELD(sx, sy))
4679 DrawLevelFieldCrumbledSand(xx, yy);
4680 DrawGraphic(sx, sy, flame_graphic, frame);
4685 if (Feld[xx][yy] == EL_FLAMES)
4686 Feld[xx][yy] = EL_EMPTY;
4687 DrawLevelField(xx, yy);
4692 if (MovDelay[x][y]) /* element still has to wait some time */
4694 PlayLevelSoundAction(x, y, ACTION_WAITING);
4700 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4701 for all other elements GfxAction will be set by InitMovingField() */
4702 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4703 GfxAction[x][y] = ACTION_MOVING;
4707 /* now make next step */
4709 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4711 if (DONT_COLLIDE_WITH(element) &&
4712 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4713 !PLAYER_PROTECTED(newx, newy))
4716 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4720 /* player killed by element which is deadly when colliding with */
4722 KillHero(PLAYERINFO(newx, newy));
4727 else if ((element == EL_PENGUIN ||
4728 element == EL_ROBOT ||
4729 element == EL_SATELLITE ||
4730 element == EL_BALLOON ||
4731 IS_CUSTOM_ELEMENT(element)) &&
4732 IN_LEV_FIELD(newx, newy) &&
4733 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4736 Store[x][y] = EL_ACID;
4738 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4740 if (Feld[newx][newy] == EL_EXIT_OPEN)
4744 DrawLevelField(x, y);
4746 Feld[x][y] = EL_EMPTY;
4747 DrawLevelField(x, y);
4750 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4751 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4752 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4754 local_player->friends_still_needed--;
4755 if (!local_player->friends_still_needed &&
4756 !local_player->GameOver && AllPlayersGone)
4757 local_player->LevelSolved = local_player->GameOver = TRUE;
4761 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4763 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4764 DrawLevelField(newx, newy);
4766 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4768 else if (!IS_FREE(newx, newy))
4770 GfxAction[x][y] = ACTION_WAITING;
4772 if (IS_PLAYER(x, y))
4773 DrawPlayerField(x, y);
4775 DrawLevelField(x, y);
4780 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4782 if (IS_FOOD_PIG(Feld[newx][newy]))
4784 if (IS_MOVING(newx, newy))
4785 RemoveMovingField(newx, newy);
4788 Feld[newx][newy] = EL_EMPTY;
4789 DrawLevelField(newx, newy);
4792 PlayLevelSound(x, y, SND_PIG_DIGGING);
4794 else if (!IS_FREE(newx, newy))
4796 if (IS_PLAYER(x, y))
4797 DrawPlayerField(x, y);
4799 DrawLevelField(x, y);
4808 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4811 else if (IS_CUSTOM_ELEMENT(element) &&
4812 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4816 !IS_FREE(newx, newy)
4821 int new_element = Feld[newx][newy];
4824 printf("::: '%s' digs '%s' [%d]\n",
4825 element_info[element].token_name,
4826 element_info[Feld[newx][newy]].token_name,
4827 StorePlayer[newx][newy]);
4830 if (!IS_FREE(newx, newy))
4832 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4833 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4836 /* no element can dig solid indestructible elements */
4837 if (IS_INDESTRUCTIBLE(new_element) &&
4838 !IS_DIGGABLE(new_element) &&
4839 !IS_COLLECTIBLE(new_element))
4842 if (AmoebaNr[newx][newy] &&
4843 (new_element == EL_AMOEBA_FULL ||
4844 new_element == EL_BD_AMOEBA ||
4845 new_element == EL_AMOEBA_GROWING))
4847 AmoebaCnt[AmoebaNr[newx][newy]]--;
4848 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4851 if (IS_MOVING(newx, newy))
4852 RemoveMovingField(newx, newy);
4855 RemoveField(newx, newy);
4856 DrawLevelField(newx, newy);
4859 PlayLevelSoundAction(x, y, action);
4862 if (new_element == element_info[element].move_enter_element)
4863 element_info[element].can_leave_element = TRUE;
4865 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4867 RunnerVisit[x][y] = FrameCounter;
4868 PlayerVisit[x][y] /= 8; /* expire player visit path */
4874 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4876 if (!IS_FREE(newx, newy))
4878 if (IS_PLAYER(x, y))
4879 DrawPlayerField(x, y);
4881 DrawLevelField(x, y);
4887 boolean wanna_flame = !RND(10);
4888 int dx = newx - x, dy = newy - y;
4889 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4890 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4891 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4892 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4893 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4894 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4897 IS_CLASSIC_ENEMY(element1) ||
4898 IS_CLASSIC_ENEMY(element2)) &&
4899 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4900 element1 != EL_FLAMES && element2 != EL_FLAMES)
4903 ResetGfxAnimation(x, y);
4904 GfxAction[x][y] = ACTION_ATTACKING;
4907 if (IS_PLAYER(x, y))
4908 DrawPlayerField(x, y);
4910 DrawLevelField(x, y);
4912 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4914 MovDelay[x][y] = 50;
4916 Feld[newx][newy] = EL_FLAMES;
4917 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4918 Feld[newx1][newy1] = EL_FLAMES;
4919 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4920 Feld[newx2][newy2] = EL_FLAMES;
4926 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4927 Feld[newx][newy] == EL_DIAMOND)
4929 if (IS_MOVING(newx, newy))
4930 RemoveMovingField(newx, newy);
4933 Feld[newx][newy] = EL_EMPTY;
4934 DrawLevelField(newx, newy);
4937 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4939 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4940 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4942 if (AmoebaNr[newx][newy])
4944 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4945 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4946 Feld[newx][newy] == EL_BD_AMOEBA)
4947 AmoebaCnt[AmoebaNr[newx][newy]]--;
4950 if (IS_MOVING(newx, newy))
4951 RemoveMovingField(newx, newy);
4954 Feld[newx][newy] = EL_EMPTY;
4955 DrawLevelField(newx, newy);
4958 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4960 else if ((element == EL_PACMAN || element == EL_MOLE)
4961 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4963 if (AmoebaNr[newx][newy])
4965 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4966 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4967 Feld[newx][newy] == EL_BD_AMOEBA)
4968 AmoebaCnt[AmoebaNr[newx][newy]]--;
4971 if (element == EL_MOLE)
4973 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4974 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4976 ResetGfxAnimation(x, y);
4977 GfxAction[x][y] = ACTION_DIGGING;
4978 DrawLevelField(x, y);
4980 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4982 return; /* wait for shrinking amoeba */
4984 else /* element == EL_PACMAN */
4986 Feld[newx][newy] = EL_EMPTY;
4987 DrawLevelField(newx, newy);
4988 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4991 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4992 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4993 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4995 /* wait for shrinking amoeba to completely disappear */
4998 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5000 /* object was running against a wall */
5005 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5006 DrawLevelElementAnimation(x, y, element);
5008 if (element == EL_BUG ||
5009 element == EL_SPACESHIP ||
5010 element == EL_SP_SNIKSNAK)
5011 DrawLevelField(x, y);
5012 else if (element == EL_MOLE)
5013 DrawLevelField(x, y);
5014 else if (element == EL_BD_BUTTERFLY ||
5015 element == EL_BD_FIREFLY)
5016 DrawLevelElementAnimationIfNeeded(x, y, element);
5017 else if (element == EL_SATELLITE)
5018 DrawLevelElementAnimationIfNeeded(x, y, element);
5019 else if (element == EL_SP_ELECTRON)
5020 DrawLevelElementAnimationIfNeeded(x, y, element);
5023 if (DONT_TOUCH(element))
5024 TestIfBadThingTouchesHero(x, y);
5027 PlayLevelSoundAction(x, y, ACTION_WAITING);
5033 InitMovingField(x, y, MovDir[x][y]);
5035 PlayLevelSoundAction(x, y, ACTION_MOVING);
5039 ContinueMoving(x, y);
5042 void ContinueMoving(int x, int y)
5044 int element = Feld[x][y];
5045 struct ElementInfo *ei = &element_info[element];
5046 int direction = MovDir[x][y];
5047 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5048 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5049 int newx = x + dx, newy = y + dy;
5051 int nextx = newx + dx, nexty = newy + dy;
5053 boolean pushed = Pushed[x][y];
5055 MovPos[x][y] += getElementMoveStepsize(x, y);
5057 if (pushed) /* special case: moving object pushed by player */
5058 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5060 if (ABS(MovPos[x][y]) < TILEX)
5062 DrawLevelField(x, y);
5064 return; /* element is still moving */
5067 /* element reached destination field */
5069 Feld[x][y] = EL_EMPTY;
5070 Feld[newx][newy] = element;
5071 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5073 if (element == EL_MOLE)
5075 Feld[x][y] = EL_SAND;
5077 DrawLevelFieldCrumbledSandNeighbours(x, y);
5079 else if (element == EL_QUICKSAND_FILLING)
5081 element = Feld[newx][newy] = get_next_element(element);
5082 Store[newx][newy] = Store[x][y];
5084 else if (element == EL_QUICKSAND_EMPTYING)
5086 Feld[x][y] = get_next_element(element);
5087 element = Feld[newx][newy] = Store[x][y];
5089 else if (element == EL_MAGIC_WALL_FILLING)
5091 element = Feld[newx][newy] = get_next_element(element);
5092 if (!game.magic_wall_active)
5093 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5094 Store[newx][newy] = Store[x][y];
5096 else if (element == EL_MAGIC_WALL_EMPTYING)
5098 Feld[x][y] = get_next_element(element);
5099 if (!game.magic_wall_active)
5100 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5101 element = Feld[newx][newy] = Store[x][y];
5103 else if (element == EL_BD_MAGIC_WALL_FILLING)
5105 element = Feld[newx][newy] = get_next_element(element);
5106 if (!game.magic_wall_active)
5107 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5108 Store[newx][newy] = Store[x][y];
5110 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5112 Feld[x][y] = get_next_element(element);
5113 if (!game.magic_wall_active)
5114 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5115 element = Feld[newx][newy] = Store[x][y];
5117 else if (element == EL_AMOEBA_DROPPING)
5119 Feld[x][y] = get_next_element(element);
5120 element = Feld[newx][newy] = Store[x][y];
5122 else if (element == EL_SOKOBAN_OBJECT)
5125 Feld[x][y] = Back[x][y];
5127 if (Back[newx][newy])
5128 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5130 Back[x][y] = Back[newx][newy] = 0;
5132 else if (Store[x][y] == EL_ACID)
5134 element = Feld[newx][newy] = EL_ACID;
5138 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5139 MovDelay[newx][newy] = 0;
5141 /* copy element change control values to new field */
5142 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5143 ChangePage[newx][newy] = ChangePage[x][y];
5144 Changed[newx][newy] = Changed[x][y];
5145 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5147 ChangeDelay[x][y] = 0;
5148 ChangePage[x][y] = -1;
5149 Changed[x][y] = CE_BITMASK_DEFAULT;
5150 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5152 /* copy animation control values to new field */
5153 GfxFrame[newx][newy] = GfxFrame[x][y];
5154 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5155 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5156 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5158 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5160 ResetGfxAnimation(x, y); /* reset animation values for old field */
5163 /* some elements can leave other elements behind after moving */
5164 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5165 ei->move_leave_element != EL_EMPTY &&
5166 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5167 ei->can_leave_element_last))
5169 Feld[x][y] = ei->move_leave_element;
5170 InitField(x, y, FALSE);
5172 if (GFX_CRUMBLED(Feld[x][y]))
5173 DrawLevelFieldCrumbledSandNeighbours(x, y);
5176 ei->can_leave_element_last = ei->can_leave_element;
5177 ei->can_leave_element = FALSE;
5181 /* 2.1.1 (does not work correctly for spring) */
5182 if (!CAN_MOVE(element))
5183 MovDir[newx][newy] = 0;
5187 /* (does not work for falling objects that slide horizontally) */
5188 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5189 MovDir[newx][newy] = 0;
5192 if (!CAN_MOVE(element) ||
5193 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5194 MovDir[newx][newy] = 0;
5197 if (!CAN_MOVE(element) ||
5198 (CAN_FALL(element) && direction == MV_DOWN))
5199 GfxDir[x][y] = MovDir[newx][newy] = 0;
5204 DrawLevelField(x, y);
5205 DrawLevelField(newx, newy);
5207 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5209 /* prevent pushed element from moving on in pushed direction */
5210 if (pushed && CAN_MOVE(element) &&
5211 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5212 !(element_info[element].move_pattern & direction))
5213 TurnRound(newx, newy);
5215 if (!pushed) /* special case: moving object pushed by player */
5217 WasJustMoving[newx][newy] = 3;
5219 if (CAN_FALL(element) && direction == MV_DOWN)
5220 WasJustFalling[newx][newy] = 3;
5223 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5225 TestIfBadThingTouchesHero(newx, newy);
5226 TestIfBadThingTouchesFriend(newx, newy);
5228 if (!IS_CUSTOM_ELEMENT(element))
5229 TestIfBadThingTouchesOtherBadThing(newx, newy);
5231 else if (element == EL_PENGUIN)
5232 TestIfFriendTouchesBadThing(newx, newy);
5234 if (CAN_FALL(element) && direction == MV_DOWN &&
5235 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5239 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5243 if (ChangePage[newx][newy] != -1) /* delayed change */
5244 ChangeElement(newx, newy, ChangePage[newx][newy]);
5249 TestIfElementHitsCustomElement(newx, newy, direction);
5253 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5255 int hitting_element = Feld[newx][newy];
5257 /* !!! fix side (direction) orientation here and elsewhere !!! */
5258 CheckElementSideChange(newx, newy, hitting_element,
5259 direction, CE_HITTING_SOMETHING, -1);
5262 if (IN_LEV_FIELD(nextx, nexty))
5264 static int opposite_directions[] =
5271 int move_dir_bit = MV_DIR_BIT(direction);
5272 int opposite_direction = opposite_directions[move_dir_bit];
5273 int hitting_side = direction;
5274 int touched_side = opposite_direction;
5275 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5276 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5277 MovDir[nextx][nexty] != direction ||
5278 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5284 CheckElementSideChange(nextx, nexty, touched_element,
5285 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5287 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5288 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5290 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5292 struct ElementChangeInfo *change =
5293 &element_info[hitting_element].change_page[i];
5295 if (change->can_change &&
5296 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5297 change->sides & touched_side &&
5298 change->trigger_element == touched_element)
5300 CheckElementSideChange(newx, newy, hitting_element,
5301 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5307 if (IS_CUSTOM_ELEMENT(touched_element) &&
5308 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5310 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5312 struct ElementChangeInfo *change =
5313 &element_info[touched_element].change_page[i];
5315 if (change->can_change &&
5316 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5317 change->sides & hitting_side &&
5318 change->trigger_element == hitting_element)
5320 CheckElementSideChange(nextx, nexty, touched_element,
5321 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5332 TestIfPlayerTouchesCustomElement(newx, newy);
5333 TestIfElementTouchesCustomElement(newx, newy);
5336 int AmoebeNachbarNr(int ax, int ay)
5339 int element = Feld[ax][ay];
5341 static int xy[4][2] =
5349 for (i = 0; i < 4; i++)
5351 int x = ax + xy[i][0];
5352 int y = ay + xy[i][1];
5354 if (!IN_LEV_FIELD(x, y))
5357 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5358 group_nr = AmoebaNr[x][y];
5364 void AmoebenVereinigen(int ax, int ay)
5366 int i, x, y, xx, yy;
5367 int new_group_nr = AmoebaNr[ax][ay];
5368 static int xy[4][2] =
5376 if (new_group_nr == 0)
5379 for (i = 0; i < 4; i++)
5384 if (!IN_LEV_FIELD(x, y))
5387 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5388 Feld[x][y] == EL_BD_AMOEBA ||
5389 Feld[x][y] == EL_AMOEBA_DEAD) &&
5390 AmoebaNr[x][y] != new_group_nr)
5392 int old_group_nr = AmoebaNr[x][y];
5394 if (old_group_nr == 0)
5397 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5398 AmoebaCnt[old_group_nr] = 0;
5399 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5400 AmoebaCnt2[old_group_nr] = 0;
5402 for (yy = 0; yy < lev_fieldy; yy++)
5404 for (xx = 0; xx < lev_fieldx; xx++)
5406 if (AmoebaNr[xx][yy] == old_group_nr)
5407 AmoebaNr[xx][yy] = new_group_nr;
5414 void AmoebeUmwandeln(int ax, int ay)
5418 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5420 int group_nr = AmoebaNr[ax][ay];
5425 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5426 printf("AmoebeUmwandeln(): This should never happen!\n");
5431 for (y = 0; y < lev_fieldy; y++)
5433 for (x = 0; x < lev_fieldx; x++)
5435 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5438 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5442 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5443 SND_AMOEBA_TURNING_TO_GEM :
5444 SND_AMOEBA_TURNING_TO_ROCK));
5449 static int xy[4][2] =
5457 for (i = 0; i < 4; i++)
5462 if (!IN_LEV_FIELD(x, y))
5465 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5467 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5468 SND_AMOEBA_TURNING_TO_GEM :
5469 SND_AMOEBA_TURNING_TO_ROCK));
5476 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5479 int group_nr = AmoebaNr[ax][ay];
5480 boolean done = FALSE;
5485 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5486 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5491 for (y = 0; y < lev_fieldy; y++)
5493 for (x = 0; x < lev_fieldx; x++)
5495 if (AmoebaNr[x][y] == group_nr &&
5496 (Feld[x][y] == EL_AMOEBA_DEAD ||
5497 Feld[x][y] == EL_BD_AMOEBA ||
5498 Feld[x][y] == EL_AMOEBA_GROWING))
5501 Feld[x][y] = new_element;
5502 InitField(x, y, FALSE);
5503 DrawLevelField(x, y);
5510 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5511 SND_BD_AMOEBA_TURNING_TO_ROCK :
5512 SND_BD_AMOEBA_TURNING_TO_GEM));
5515 void AmoebeWaechst(int x, int y)
5517 static unsigned long sound_delay = 0;
5518 static unsigned long sound_delay_value = 0;
5520 if (!MovDelay[x][y]) /* start new growing cycle */
5524 if (DelayReached(&sound_delay, sound_delay_value))
5527 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5529 if (Store[x][y] == EL_BD_AMOEBA)
5530 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5532 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5534 sound_delay_value = 30;
5538 if (MovDelay[x][y]) /* wait some time before growing bigger */
5541 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5543 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5544 6 - MovDelay[x][y]);
5546 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5549 if (!MovDelay[x][y])
5551 Feld[x][y] = Store[x][y];
5553 DrawLevelField(x, y);
5558 void AmoebaDisappearing(int x, int y)
5560 static unsigned long sound_delay = 0;
5561 static unsigned long sound_delay_value = 0;
5563 if (!MovDelay[x][y]) /* start new shrinking cycle */
5567 if (DelayReached(&sound_delay, sound_delay_value))
5568 sound_delay_value = 30;
5571 if (MovDelay[x][y]) /* wait some time before shrinking */
5574 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5576 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5577 6 - MovDelay[x][y]);
5579 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5582 if (!MovDelay[x][y])
5584 Feld[x][y] = EL_EMPTY;
5585 DrawLevelField(x, y);
5587 /* don't let mole enter this field in this cycle;
5588 (give priority to objects falling to this field from above) */
5594 void AmoebeAbleger(int ax, int ay)
5597 int element = Feld[ax][ay];
5598 int graphic = el2img(element);
5599 int newax = ax, neway = ay;
5600 static int xy[4][2] =
5608 if (!level.amoeba_speed)
5610 Feld[ax][ay] = EL_AMOEBA_DEAD;
5611 DrawLevelField(ax, ay);
5615 if (IS_ANIMATED(graphic))
5616 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5618 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5619 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5621 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5624 if (MovDelay[ax][ay])
5628 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5631 int x = ax + xy[start][0];
5632 int y = ay + xy[start][1];
5634 if (!IN_LEV_FIELD(x, y))
5637 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5638 if (IS_FREE(x, y) ||
5639 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5645 if (newax == ax && neway == ay)
5648 else /* normal or "filled" (BD style) amoeba */
5651 boolean waiting_for_player = FALSE;
5653 for (i = 0; i < 4; i++)
5655 int j = (start + i) % 4;
5656 int x = ax + xy[j][0];
5657 int y = ay + xy[j][1];
5659 if (!IN_LEV_FIELD(x, y))
5662 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5663 if (IS_FREE(x, y) ||
5664 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5670 else if (IS_PLAYER(x, y))
5671 waiting_for_player = TRUE;
5674 if (newax == ax && neway == ay) /* amoeba cannot grow */
5676 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5678 Feld[ax][ay] = EL_AMOEBA_DEAD;
5679 DrawLevelField(ax, ay);
5680 AmoebaCnt[AmoebaNr[ax][ay]]--;
5682 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5684 if (element == EL_AMOEBA_FULL)
5685 AmoebeUmwandeln(ax, ay);
5686 else if (element == EL_BD_AMOEBA)
5687 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5692 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5694 /* amoeba gets larger by growing in some direction */
5696 int new_group_nr = AmoebaNr[ax][ay];
5699 if (new_group_nr == 0)
5701 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5702 printf("AmoebeAbleger(): This should never happen!\n");
5707 AmoebaNr[newax][neway] = new_group_nr;
5708 AmoebaCnt[new_group_nr]++;
5709 AmoebaCnt2[new_group_nr]++;
5711 /* if amoeba touches other amoeba(s) after growing, unify them */
5712 AmoebenVereinigen(newax, neway);
5714 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5716 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5722 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5723 (neway == lev_fieldy - 1 && newax != ax))
5725 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5726 Store[newax][neway] = element;
5728 else if (neway == ay)
5730 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5732 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5734 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5739 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5740 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5741 Store[ax][ay] = EL_AMOEBA_DROP;
5742 ContinueMoving(ax, ay);
5746 DrawLevelField(newax, neway);
5749 void Life(int ax, int ay)
5752 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5754 int element = Feld[ax][ay];
5755 int graphic = el2img(element);
5756 boolean changed = FALSE;
5758 if (IS_ANIMATED(graphic))
5759 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5764 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5765 MovDelay[ax][ay] = life_time;
5767 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5770 if (MovDelay[ax][ay])
5774 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5776 int xx = ax+x1, yy = ay+y1;
5779 if (!IN_LEV_FIELD(xx, yy))
5782 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5784 int x = xx+x2, y = yy+y2;
5786 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5789 if (((Feld[x][y] == element ||
5790 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5792 (IS_FREE(x, y) && Stop[x][y]))
5796 if (xx == ax && yy == ay) /* field in the middle */
5798 if (nachbarn < life[0] || nachbarn > life[1])
5800 Feld[xx][yy] = EL_EMPTY;
5802 DrawLevelField(xx, yy);
5803 Stop[xx][yy] = TRUE;
5807 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5808 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5809 { /* free border field */
5810 if (nachbarn >= life[2] && nachbarn <= life[3])
5812 Feld[xx][yy] = element;
5813 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5815 DrawLevelField(xx, yy);
5816 Stop[xx][yy] = TRUE;
5823 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5824 SND_GAME_OF_LIFE_GROWING);
5827 static void InitRobotWheel(int x, int y)
5829 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5832 static void RunRobotWheel(int x, int y)
5834 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5837 static void StopRobotWheel(int x, int y)
5839 if (ZX == x && ZY == y)
5843 static void InitTimegateWheel(int x, int y)
5845 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5848 static void RunTimegateWheel(int x, int y)
5850 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5853 void CheckExit(int x, int y)
5855 if (local_player->gems_still_needed > 0 ||
5856 local_player->sokobanfields_still_needed > 0 ||
5857 local_player->lights_still_needed > 0)
5859 int element = Feld[x][y];
5860 int graphic = el2img(element);
5862 if (IS_ANIMATED(graphic))
5863 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5868 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5871 Feld[x][y] = EL_EXIT_OPENING;
5873 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5876 void CheckExitSP(int x, int y)
5878 if (local_player->gems_still_needed > 0)
5880 int element = Feld[x][y];
5881 int graphic = el2img(element);
5883 if (IS_ANIMATED(graphic))
5884 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5889 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5892 Feld[x][y] = EL_SP_EXIT_OPENING;
5894 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5897 static void CloseAllOpenTimegates()
5901 for (y = 0; y < lev_fieldy; y++)
5903 for (x = 0; x < lev_fieldx; x++)
5905 int element = Feld[x][y];
5907 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5909 Feld[x][y] = EL_TIMEGATE_CLOSING;
5911 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5913 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5920 void EdelsteinFunkeln(int x, int y)
5922 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5925 if (Feld[x][y] == EL_BD_DIAMOND)
5928 if (MovDelay[x][y] == 0) /* next animation frame */
5929 MovDelay[x][y] = 11 * !SimpleRND(500);
5931 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5935 if (setup.direct_draw && MovDelay[x][y])
5936 SetDrawtoField(DRAW_BUFFERED);
5938 DrawLevelElementAnimation(x, y, Feld[x][y]);
5940 if (MovDelay[x][y] != 0)
5942 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5943 10 - MovDelay[x][y]);
5945 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5947 if (setup.direct_draw)
5951 dest_x = FX + SCREENX(x) * TILEX;
5952 dest_y = FY + SCREENY(y) * TILEY;
5954 BlitBitmap(drawto_field, window,
5955 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5956 SetDrawtoField(DRAW_DIRECT);
5962 void MauerWaechst(int x, int y)
5966 if (!MovDelay[x][y]) /* next animation frame */
5967 MovDelay[x][y] = 3 * delay;
5969 if (MovDelay[x][y]) /* wait some time before next frame */
5973 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5975 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5976 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5978 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5981 if (!MovDelay[x][y])
5983 if (MovDir[x][y] == MV_LEFT)
5985 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5986 DrawLevelField(x - 1, y);
5988 else if (MovDir[x][y] == MV_RIGHT)
5990 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5991 DrawLevelField(x + 1, y);
5993 else if (MovDir[x][y] == MV_UP)
5995 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5996 DrawLevelField(x, y - 1);
6000 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6001 DrawLevelField(x, y + 1);
6004 Feld[x][y] = Store[x][y];
6006 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6007 DrawLevelField(x, y);
6012 void MauerAbleger(int ax, int ay)
6014 int element = Feld[ax][ay];
6015 int graphic = el2img(element);
6016 boolean oben_frei = FALSE, unten_frei = FALSE;
6017 boolean links_frei = FALSE, rechts_frei = FALSE;
6018 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6019 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6020 boolean new_wall = FALSE;
6022 if (IS_ANIMATED(graphic))
6023 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6025 if (!MovDelay[ax][ay]) /* start building new wall */
6026 MovDelay[ax][ay] = 6;
6028 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6031 if (MovDelay[ax][ay])
6035 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6037 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6039 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6041 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6044 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6045 element == EL_EXPANDABLE_WALL_ANY)
6049 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6050 Store[ax][ay-1] = element;
6051 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6052 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6053 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6054 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6059 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6060 Store[ax][ay+1] = element;
6061 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6062 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6063 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6064 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6069 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6070 element == EL_EXPANDABLE_WALL_ANY ||
6071 element == EL_EXPANDABLE_WALL)
6075 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6076 Store[ax-1][ay] = element;
6077 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6078 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6079 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6080 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6086 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6087 Store[ax+1][ay] = element;
6088 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6089 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6090 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6091 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6096 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6097 DrawLevelField(ax, ay);
6099 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6101 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6102 unten_massiv = TRUE;
6103 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6104 links_massiv = TRUE;
6105 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6106 rechts_massiv = TRUE;
6108 if (((oben_massiv && unten_massiv) ||
6109 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6110 element == EL_EXPANDABLE_WALL) &&
6111 ((links_massiv && rechts_massiv) ||
6112 element == EL_EXPANDABLE_WALL_VERTICAL))
6113 Feld[ax][ay] = EL_WALL;
6117 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6119 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6123 void CheckForDragon(int x, int y)
6126 boolean dragon_found = FALSE;
6127 static int xy[4][2] =
6135 for (i = 0; i < 4; i++)
6137 for (j = 0; j < 4; j++)
6139 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6141 if (IN_LEV_FIELD(xx, yy) &&
6142 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6144 if (Feld[xx][yy] == EL_DRAGON)
6145 dragon_found = TRUE;
6154 for (i = 0; i < 4; i++)
6156 for (j = 0; j < 3; j++)
6158 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6160 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6162 Feld[xx][yy] = EL_EMPTY;
6163 DrawLevelField(xx, yy);
6172 static void InitBuggyBase(int x, int y)
6174 int element = Feld[x][y];
6175 int activating_delay = FRAMES_PER_SECOND / 4;
6178 (element == EL_SP_BUGGY_BASE ?
6179 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6180 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6182 element == EL_SP_BUGGY_BASE_ACTIVE ?
6183 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6186 static void WarnBuggyBase(int x, int y)
6189 static int xy[4][2] =
6197 for (i = 0; i < 4; i++)
6199 int xx = x + xy[i][0], yy = y + xy[i][1];
6201 if (IS_PLAYER(xx, yy))
6203 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6210 static void InitTrap(int x, int y)
6212 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6215 static void ActivateTrap(int x, int y)
6217 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6220 static void ChangeActiveTrap(int x, int y)
6222 int graphic = IMG_TRAP_ACTIVE;
6224 /* if new animation frame was drawn, correct crumbled sand border */
6225 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6226 DrawLevelFieldCrumbledSand(x, y);
6229 static void ChangeElementNowExt(int x, int y, int target_element)
6231 int previous_move_direction = MovDir[x][y];
6233 /* check if element under player changes from accessible to unaccessible
6234 (needed for special case of dropping element which then changes) */
6235 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6236 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6243 Feld[x][y] = target_element;
6245 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6247 ResetGfxAnimation(x, y);
6248 ResetRandomAnimationValue(x, y);
6250 if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
6251 MovDir[x][y] = previous_move_direction;
6253 InitField(x, y, FALSE);
6254 if (CAN_MOVE(Feld[x][y]))
6257 DrawLevelField(x, y);
6259 if (GFX_CRUMBLED(Feld[x][y]))
6260 DrawLevelFieldCrumbledSandNeighbours(x, y);
6262 TestIfBadThingTouchesHero(x, y);
6263 TestIfPlayerTouchesCustomElement(x, y);
6264 TestIfElementTouchesCustomElement(x, y);
6266 if (ELEM_IS_PLAYER(target_element))
6267 RelocatePlayer(x, y, target_element);
6270 static boolean ChangeElementNow(int x, int y, int element, int page)
6272 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6274 /* always use default change event to prevent running into a loop */
6275 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6276 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6278 /* do not change already changed elements with same change event */
6280 if (Changed[x][y] & ChangeEvent[x][y])
6287 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6289 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6291 if (change->explode)
6298 if (change->use_content)
6300 boolean complete_change = TRUE;
6301 boolean can_change[3][3];
6304 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6306 boolean half_destructible;
6307 int ex = x + xx - 1;
6308 int ey = y + yy - 1;
6311 can_change[xx][yy] = TRUE;
6313 if (ex == x && ey == y) /* do not check changing element itself */
6316 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6318 can_change[xx][yy] = FALSE; /* do not change empty borders */
6323 if (!IN_LEV_FIELD(ex, ey))
6325 can_change[xx][yy] = FALSE;
6326 complete_change = FALSE;
6333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6334 e = MovingOrBlocked2Element(ex, ey);
6336 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6338 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6339 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6340 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6342 can_change[xx][yy] = FALSE;
6343 complete_change = FALSE;
6347 if (!change->only_complete || complete_change)
6349 boolean something_has_changed = FALSE;
6351 if (change->only_complete && change->use_random_change &&
6352 RND(100) < change->random)
6355 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6357 int ex = x + xx - 1;
6358 int ey = y + yy - 1;
6360 if (can_change[xx][yy] && (!change->use_random_change ||
6361 RND(100) < change->random))
6363 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6364 RemoveMovingField(ex, ey);
6366 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6368 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6370 something_has_changed = TRUE;
6372 /* for symmetry reasons, freeze newly created border elements */
6373 if (ex != x || ey != y)
6374 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6378 if (something_has_changed)
6379 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6384 ChangeElementNowExt(x, y, change->target_element);
6386 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6392 static void ChangeElement(int x, int y, int page)
6394 int element = MovingOrBlocked2Element(x, y);
6395 struct ElementInfo *ei = &element_info[element];
6396 struct ElementChangeInfo *change = &ei->change_page[page];
6400 if (!CAN_CHANGE(element))
6403 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6404 x, y, element, element_info[element].token_name);
6405 printf("ChangeElement(): This should never happen!\n");
6411 if (ChangeDelay[x][y] == 0) /* initialize element change */
6413 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6414 RND(change->delay_random * change->delay_frames)) + 1;
6416 ResetGfxAnimation(x, y);
6417 ResetRandomAnimationValue(x, y);
6419 if (change->pre_change_function)
6420 change->pre_change_function(x, y);
6423 ChangeDelay[x][y]--;
6425 if (ChangeDelay[x][y] != 0) /* continue element change */
6427 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6429 if (IS_ANIMATED(graphic))
6430 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6432 if (change->change_function)
6433 change->change_function(x, y);
6435 else /* finish element change */
6437 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6439 page = ChangePage[x][y];
6440 ChangePage[x][y] = -1;
6444 if (IS_MOVING(x, y) && !change->explode)
6446 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6449 ChangeDelay[x][y] = 1; /* try change after next move step */
6450 ChangePage[x][y] = page; /* remember page to use for change */
6455 if (ChangeElementNow(x, y, element, page))
6457 if (change->post_change_function)
6458 change->post_change_function(x, y);
6463 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6464 int trigger_element,
6470 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6473 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6475 int element = EL_CUSTOM_START + i;
6477 boolean change_element = FALSE;
6480 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6483 for (j = 0; j < element_info[element].num_change_pages; j++)
6485 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6487 if (change->can_change &&
6489 change->events & CH_EVENT_BIT(trigger_event) &&
6491 change->sides & trigger_side &&
6493 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6495 change->trigger_element == trigger_element
6500 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6501 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6502 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6505 change_element = TRUE;
6512 if (!change_element)
6515 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6518 if (x == lx && y == ly) /* do not change trigger element itself */
6522 if (Feld[x][y] == element)
6524 ChangeDelay[x][y] = 1;
6525 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6526 ChangeElement(x, y, page);
6534 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6537 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6541 static boolean CheckElementSideChange(int x, int y, int element, int side,
6542 int trigger_event, int page)
6544 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6547 if (Feld[x][y] == EL_BLOCKED)
6549 Blocked2Moving(x, y, &x, &y);
6550 element = Feld[x][y];
6554 page = element_info[element].event_page_nr[trigger_event];
6556 if (!(element_info[element].change_page[page].sides & side))
6559 ChangeDelay[x][y] = 1;
6560 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6561 ChangeElement(x, y, page);
6566 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6568 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6571 static void PlayPlayerSound(struct PlayerInfo *player)
6573 int jx = player->jx, jy = player->jy;
6574 int element = player->element_nr;
6575 int last_action = player->last_action_waiting;
6576 int action = player->action_waiting;
6578 if (player->is_waiting)
6580 if (action != last_action)
6581 PlayLevelSoundElementAction(jx, jy, element, action);
6583 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6587 if (action != last_action)
6588 StopSound(element_info[element].sound[last_action]);
6590 if (last_action == ACTION_SLEEPING)
6591 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6595 static void PlayAllPlayersSound()
6599 for (i = 0; i < MAX_PLAYERS; i++)
6600 if (stored_player[i].active)
6601 PlayPlayerSound(&stored_player[i]);
6604 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6606 boolean last_waiting = player->is_waiting;
6607 int move_dir = player->MovDir;
6609 player->last_action_waiting = player->action_waiting;
6613 if (!last_waiting) /* not waiting -> waiting */
6615 player->is_waiting = TRUE;
6617 player->frame_counter_bored =
6619 game.player_boring_delay_fixed +
6620 SimpleRND(game.player_boring_delay_random);
6621 player->frame_counter_sleeping =
6623 game.player_sleeping_delay_fixed +
6624 SimpleRND(game.player_sleeping_delay_random);
6626 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6629 if (game.player_sleeping_delay_fixed +
6630 game.player_sleeping_delay_random > 0 &&
6631 player->anim_delay_counter == 0 &&
6632 player->post_delay_counter == 0 &&
6633 FrameCounter >= player->frame_counter_sleeping)
6634 player->is_sleeping = TRUE;
6635 else if (game.player_boring_delay_fixed +
6636 game.player_boring_delay_random > 0 &&
6637 FrameCounter >= player->frame_counter_bored)
6638 player->is_bored = TRUE;
6640 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6641 player->is_bored ? ACTION_BORING :
6644 if (player->is_sleeping)
6646 if (player->num_special_action_sleeping > 0)
6648 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6650 int last_special_action = player->special_action_sleeping;
6651 int num_special_action = player->num_special_action_sleeping;
6652 int special_action =
6653 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6654 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6655 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6656 last_special_action + 1 : ACTION_SLEEPING);
6657 int special_graphic =
6658 el_act_dir2img(player->element_nr, special_action, move_dir);
6660 player->anim_delay_counter =
6661 graphic_info[special_graphic].anim_delay_fixed +
6662 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6663 player->post_delay_counter =
6664 graphic_info[special_graphic].post_delay_fixed +
6665 SimpleRND(graphic_info[special_graphic].post_delay_random);
6667 player->special_action_sleeping = special_action;
6670 if (player->anim_delay_counter > 0)
6672 player->action_waiting = player->special_action_sleeping;
6673 player->anim_delay_counter--;
6675 else if (player->post_delay_counter > 0)
6677 player->post_delay_counter--;
6681 else if (player->is_bored)
6683 if (player->num_special_action_bored > 0)
6685 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6687 int special_action =
6688 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6689 int special_graphic =
6690 el_act_dir2img(player->element_nr, special_action, move_dir);
6692 player->anim_delay_counter =
6693 graphic_info[special_graphic].anim_delay_fixed +
6694 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6695 player->post_delay_counter =
6696 graphic_info[special_graphic].post_delay_fixed +
6697 SimpleRND(graphic_info[special_graphic].post_delay_random);
6699 player->special_action_bored = special_action;
6702 if (player->anim_delay_counter > 0)
6704 player->action_waiting = player->special_action_bored;
6705 player->anim_delay_counter--;
6707 else if (player->post_delay_counter > 0)
6709 player->post_delay_counter--;
6714 else if (last_waiting) /* waiting -> not waiting */
6716 player->is_waiting = FALSE;
6717 player->is_bored = FALSE;
6718 player->is_sleeping = FALSE;
6720 player->frame_counter_bored = -1;
6721 player->frame_counter_sleeping = -1;
6723 player->anim_delay_counter = 0;
6724 player->post_delay_counter = 0;
6726 player->action_waiting = ACTION_DEFAULT;
6728 player->special_action_bored = ACTION_DEFAULT;
6729 player->special_action_sleeping = ACTION_DEFAULT;
6734 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6737 static byte stored_player_action[MAX_PLAYERS];
6738 static int num_stored_actions = 0;
6740 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6741 int left = player_action & JOY_LEFT;
6742 int right = player_action & JOY_RIGHT;
6743 int up = player_action & JOY_UP;
6744 int down = player_action & JOY_DOWN;
6745 int button1 = player_action & JOY_BUTTON_1;
6746 int button2 = player_action & JOY_BUTTON_2;
6747 int dx = (left ? -1 : right ? 1 : 0);
6748 int dy = (up ? -1 : down ? 1 : 0);
6751 stored_player_action[player->index_nr] = 0;
6752 num_stored_actions++;
6756 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6759 if (!player->active || tape.pausing)
6763 printf("::: [%d %d %d %d] [%d %d]\n",
6764 left, right, up, down, button1, button2);
6770 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6774 snapped = SnapField(player, dx, dy);
6778 dropped = DropElement(player);
6780 moved = MovePlayer(player, dx, dy);
6783 if (tape.single_step && tape.recording && !tape.pausing)
6785 if (button1 || (dropped && !moved))
6787 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6788 SnapField(player, 0, 0); /* stop snapping */
6792 SetPlayerWaiting(player, FALSE);
6795 return player_action;
6797 stored_player_action[player->index_nr] = player_action;
6803 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6806 /* no actions for this player (no input at player's configured device) */
6808 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6809 SnapField(player, 0, 0);
6810 CheckGravityMovement(player);
6812 if (player->MovPos == 0)
6813 SetPlayerWaiting(player, TRUE);
6815 if (player->MovPos == 0) /* needed for tape.playing */
6816 player->is_moving = FALSE;
6818 player->is_dropping = FALSE;
6824 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6826 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6828 TapeRecordAction(stored_player_action);
6829 num_stored_actions = 0;
6836 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6838 static byte stored_player_action[MAX_PLAYERS];
6839 static int num_stored_actions = 0;
6840 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6841 int left = player_action & JOY_LEFT;
6842 int right = player_action & JOY_RIGHT;
6843 int up = player_action & JOY_UP;
6844 int down = player_action & JOY_DOWN;
6845 int button1 = player_action & JOY_BUTTON_1;
6846 int button2 = player_action & JOY_BUTTON_2;
6847 int dx = (left ? -1 : right ? 1 : 0);
6848 int dy = (up ? -1 : down ? 1 : 0);
6850 stored_player_action[player->index_nr] = 0;
6851 num_stored_actions++;
6853 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6855 if (!player->active || tape.pausing)
6860 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6863 snapped = SnapField(player, dx, dy);
6867 dropped = DropElement(player);
6869 moved = MovePlayer(player, dx, dy);
6872 if (tape.single_step && tape.recording && !tape.pausing)
6874 if (button1 || (dropped && !moved))
6876 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6877 SnapField(player, 0, 0); /* stop snapping */
6881 stored_player_action[player->index_nr] = player_action;
6885 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6887 /* no actions for this player (no input at player's configured device) */
6889 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6890 SnapField(player, 0, 0);
6891 CheckGravityMovement(player);
6893 if (player->MovPos == 0)
6894 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6896 if (player->MovPos == 0) /* needed for tape.playing */
6897 player->is_moving = FALSE;
6900 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6902 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6904 TapeRecordAction(stored_player_action);
6905 num_stored_actions = 0;
6912 static unsigned long action_delay = 0;
6913 unsigned long action_delay_value;
6914 int magic_wall_x = 0, magic_wall_y = 0;
6915 int i, x, y, element, graphic;
6916 byte *recorded_player_action;
6917 byte summarized_player_action = 0;
6919 byte tape_action[MAX_PLAYERS];
6922 if (game_status != GAME_MODE_PLAYING)
6925 action_delay_value =
6926 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6928 if (tape.playing && tape.index_search && !tape.pausing)
6929 action_delay_value = 0;
6931 /* ---------- main game synchronization point ---------- */
6933 WaitUntilDelayReached(&action_delay, action_delay_value);
6935 if (network_playing && !network_player_action_received)
6939 printf("DEBUG: try to get network player actions in time\n");
6943 #if defined(PLATFORM_UNIX)
6944 /* last chance to get network player actions without main loop delay */
6948 if (game_status != GAME_MODE_PLAYING)
6951 if (!network_player_action_received)
6955 printf("DEBUG: failed to get network player actions in time\n");
6966 printf("::: getting new tape action [%d]\n", FrameCounter);
6969 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6971 for (i = 0; i < MAX_PLAYERS; i++)
6973 summarized_player_action |= stored_player[i].action;
6975 if (!network_playing)
6976 stored_player[i].effective_action = stored_player[i].action;
6979 #if defined(PLATFORM_UNIX)
6980 if (network_playing)
6981 SendToServer_MovePlayer(summarized_player_action);
6984 if (!options.network && !setup.team_mode)
6985 local_player->effective_action = summarized_player_action;
6987 for (i = 0; i < MAX_PLAYERS; i++)
6989 int actual_player_action = stored_player[i].effective_action;
6991 if (stored_player[i].programmed_action)
6992 actual_player_action = stored_player[i].programmed_action;
6994 if (recorded_player_action)
6995 actual_player_action = recorded_player_action[i];
6997 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6999 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7000 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7002 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7007 TapeRecordAction(tape_action);
7010 network_player_action_received = FALSE;
7012 ScrollScreen(NULL, SCROLL_GO_ON);
7018 for (i = 0; i < MAX_PLAYERS; i++)
7019 stored_player[i].Frame++;
7023 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7025 for (i = 0; i < MAX_PLAYERS; i++)
7027 struct PlayerInfo *player = &stored_player[i];
7031 if (player->active && player->is_pushing && player->is_moving &&
7034 ContinueMoving(x, y);
7036 /* continue moving after pushing (this is actually a bug) */
7037 if (!IS_MOVING(x, y))
7046 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7048 Changed[x][y] = CE_BITMASK_DEFAULT;
7049 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7052 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7054 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7055 printf("GameActions(): This should never happen!\n");
7057 ChangePage[x][y] = -1;
7062 if (WasJustMoving[x][y] > 0)
7063 WasJustMoving[x][y]--;
7064 if (WasJustFalling[x][y] > 0)
7065 WasJustFalling[x][y]--;
7070 /* reset finished pushing action (not done in ContinueMoving() to allow
7071 continous pushing animation for elements with zero push delay) */
7072 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7074 ResetGfxAnimation(x, y);
7075 DrawLevelField(x, y);
7080 if (IS_BLOCKED(x, y))
7084 Blocked2Moving(x, y, &oldx, &oldy);
7085 if (!IS_MOVING(oldx, oldy))
7087 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7088 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7089 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7090 printf("GameActions(): This should never happen!\n");
7096 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7098 element = Feld[x][y];
7100 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7102 graphic = el2img(element);
7108 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7110 element = graphic = 0;
7114 if (graphic_info[graphic].anim_global_sync)
7115 GfxFrame[x][y] = FrameCounter;
7117 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7118 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7119 ResetRandomAnimationValue(x, y);
7121 SetRandomAnimationValue(x, y);
7124 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7127 if (IS_INACTIVE(element))
7129 if (IS_ANIMATED(graphic))
7130 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7136 /* this may take place after moving, so 'element' may have changed */
7138 if (IS_CHANGING(x, y))
7140 if (IS_CHANGING(x, y) &&
7141 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7145 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7146 element_info[element].event_page_nr[CE_DELAY]);
7148 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7151 element = Feld[x][y];
7152 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7156 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7161 element = Feld[x][y];
7162 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7164 if (element == EL_MOLE)
7165 printf("::: %d, %d, %d [%d]\n",
7166 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7170 if (element == EL_YAMYAM)
7171 printf("::: %d, %d, %d\n",
7172 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7176 if (IS_ANIMATED(graphic) &&
7180 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7183 if (element == EL_BUG)
7184 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7188 if (element == EL_MOLE)
7189 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7193 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7194 EdelsteinFunkeln(x, y);
7196 else if ((element == EL_ACID ||
7197 element == EL_EXIT_OPEN ||
7198 element == EL_SP_EXIT_OPEN ||
7199 element == EL_SP_TERMINAL ||
7200 element == EL_SP_TERMINAL_ACTIVE ||
7201 element == EL_EXTRA_TIME ||
7202 element == EL_SHIELD_NORMAL ||
7203 element == EL_SHIELD_DEADLY) &&
7204 IS_ANIMATED(graphic))
7205 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7206 else if (IS_MOVING(x, y))
7207 ContinueMoving(x, y);
7208 else if (IS_ACTIVE_BOMB(element))
7209 CheckDynamite(x, y);
7211 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7212 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7214 else if (element == EL_AMOEBA_GROWING)
7215 AmoebeWaechst(x, y);
7216 else if (element == EL_AMOEBA_SHRINKING)
7217 AmoebaDisappearing(x, y);
7219 #if !USE_NEW_AMOEBA_CODE
7220 else if (IS_AMOEBALIVE(element))
7221 AmoebeAbleger(x, y);
7224 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7226 else if (element == EL_EXIT_CLOSED)
7228 else if (element == EL_SP_EXIT_CLOSED)
7230 else if (element == EL_EXPANDABLE_WALL_GROWING)
7232 else if (element == EL_EXPANDABLE_WALL ||
7233 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7234 element == EL_EXPANDABLE_WALL_VERTICAL ||
7235 element == EL_EXPANDABLE_WALL_ANY)
7237 else if (element == EL_FLAMES)
7238 CheckForDragon(x, y);
7240 else if (IS_AUTO_CHANGING(element))
7241 ChangeElement(x, y);
7243 else if (element == EL_EXPLOSION)
7244 ; /* drawing of correct explosion animation is handled separately */
7245 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7246 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7249 /* this may take place after moving, so 'element' may have changed */
7250 if (IS_AUTO_CHANGING(Feld[x][y]))
7251 ChangeElement(x, y);
7254 if (IS_BELT_ACTIVE(element))
7255 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7257 if (game.magic_wall_active)
7259 int jx = local_player->jx, jy = local_player->jy;
7261 /* play the element sound at the position nearest to the player */
7262 if ((element == EL_MAGIC_WALL_FULL ||
7263 element == EL_MAGIC_WALL_ACTIVE ||
7264 element == EL_MAGIC_WALL_EMPTYING ||
7265 element == EL_BD_MAGIC_WALL_FULL ||
7266 element == EL_BD_MAGIC_WALL_ACTIVE ||
7267 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7268 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7276 #if USE_NEW_AMOEBA_CODE
7277 /* new experimental amoeba growth stuff */
7279 if (!(FrameCounter % 8))
7282 static unsigned long random = 1684108901;
7284 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7287 x = (random >> 10) % lev_fieldx;
7288 y = (random >> 20) % lev_fieldy;
7290 x = RND(lev_fieldx);
7291 y = RND(lev_fieldy);
7293 element = Feld[x][y];
7295 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7296 if (!IS_PLAYER(x,y) &&
7297 (element == EL_EMPTY ||
7298 element == EL_SAND ||
7299 element == EL_QUICKSAND_EMPTY ||
7300 element == EL_ACID_SPLASH_LEFT ||
7301 element == EL_ACID_SPLASH_RIGHT))
7303 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7304 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7305 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7306 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7307 Feld[x][y] = EL_AMOEBA_DROP;
7310 random = random * 129 + 1;
7316 if (game.explosions_delayed)
7319 game.explosions_delayed = FALSE;
7321 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7323 element = Feld[x][y];
7325 if (ExplodeField[x][y])
7326 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7327 else if (element == EL_EXPLOSION)
7328 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7330 ExplodeField[x][y] = EX_NO_EXPLOSION;
7333 game.explosions_delayed = TRUE;
7336 if (game.magic_wall_active)
7338 if (!(game.magic_wall_time_left % 4))
7340 int element = Feld[magic_wall_x][magic_wall_y];
7342 if (element == EL_BD_MAGIC_WALL_FULL ||
7343 element == EL_BD_MAGIC_WALL_ACTIVE ||
7344 element == EL_BD_MAGIC_WALL_EMPTYING)
7345 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7347 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7350 if (game.magic_wall_time_left > 0)
7352 game.magic_wall_time_left--;
7353 if (!game.magic_wall_time_left)
7355 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7357 element = Feld[x][y];
7359 if (element == EL_MAGIC_WALL_ACTIVE ||
7360 element == EL_MAGIC_WALL_FULL)
7362 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7363 DrawLevelField(x, y);
7365 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7366 element == EL_BD_MAGIC_WALL_FULL)
7368 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7369 DrawLevelField(x, y);
7373 game.magic_wall_active = FALSE;
7378 if (game.light_time_left > 0)
7380 game.light_time_left--;
7382 if (game.light_time_left == 0)
7383 RedrawAllLightSwitchesAndInvisibleElements();
7386 if (game.timegate_time_left > 0)
7388 game.timegate_time_left--;
7390 if (game.timegate_time_left == 0)
7391 CloseAllOpenTimegates();
7394 for (i = 0; i < MAX_PLAYERS; i++)
7396 struct PlayerInfo *player = &stored_player[i];
7398 if (SHIELD_ON(player))
7400 if (player->shield_deadly_time_left)
7401 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7402 else if (player->shield_normal_time_left)
7403 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7407 if (TimeFrames >= FRAMES_PER_SECOND)
7412 for (i = 0; i < MAX_PLAYERS; i++)
7414 struct PlayerInfo *player = &stored_player[i];
7416 if (SHIELD_ON(player))
7418 player->shield_normal_time_left--;
7420 if (player->shield_deadly_time_left > 0)
7421 player->shield_deadly_time_left--;
7425 if (tape.recording || tape.playing)
7426 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7432 if (TimeLeft <= 10 && setup.time_limit)
7433 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7435 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7437 if (!TimeLeft && setup.time_limit)
7438 for (i = 0; i < MAX_PLAYERS; i++)
7439 KillHero(&stored_player[i]);
7441 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7442 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7446 PlayAllPlayersSound();
7448 if (options.debug) /* calculate frames per second */
7450 static unsigned long fps_counter = 0;
7451 static int fps_frames = 0;
7452 unsigned long fps_delay_ms = Counter() - fps_counter;
7456 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7458 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7461 fps_counter = Counter();
7464 redraw_mask |= REDRAW_FPS;
7468 if (stored_player[0].jx != stored_player[0].last_jx ||
7469 stored_player[0].jy != stored_player[0].last_jy)
7470 printf("::: %d, %d, %d, %d, %d\n",
7471 stored_player[0].MovDir,
7472 stored_player[0].MovPos,
7473 stored_player[0].GfxPos,
7474 stored_player[0].Frame,
7475 stored_player[0].StepFrame);
7482 for (i = 0; i < MAX_PLAYERS; i++)
7485 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7487 stored_player[i].Frame += move_frames;
7489 if (stored_player[i].MovPos != 0)
7490 stored_player[i].StepFrame += move_frames;
7492 if (stored_player[i].drop_delay > 0)
7493 stored_player[i].drop_delay--;
7498 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7500 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7502 local_player->show_envelope = 0;
7507 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7509 int min_x = x, min_y = y, max_x = x, max_y = y;
7512 for (i = 0; i < MAX_PLAYERS; i++)
7514 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7516 if (!stored_player[i].active || &stored_player[i] == player)
7519 min_x = MIN(min_x, jx);
7520 min_y = MIN(min_y, jy);
7521 max_x = MAX(max_x, jx);
7522 max_y = MAX(max_y, jy);
7525 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7528 static boolean AllPlayersInVisibleScreen()
7532 for (i = 0; i < MAX_PLAYERS; i++)
7534 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7536 if (!stored_player[i].active)
7539 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7546 void ScrollLevel(int dx, int dy)
7548 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7551 BlitBitmap(drawto_field, drawto_field,
7552 FX + TILEX * (dx == -1) - softscroll_offset,
7553 FY + TILEY * (dy == -1) - softscroll_offset,
7554 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7555 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7556 FX + TILEX * (dx == 1) - softscroll_offset,
7557 FY + TILEY * (dy == 1) - softscroll_offset);
7561 x = (dx == 1 ? BX1 : BX2);
7562 for (y = BY1; y <= BY2; y++)
7563 DrawScreenField(x, y);
7568 y = (dy == 1 ? BY1 : BY2);
7569 for (x = BX1; x <= BX2; x++)
7570 DrawScreenField(x, y);
7573 redraw_mask |= REDRAW_FIELD;
7576 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7578 int nextx = x + dx, nexty = y + dy;
7579 int element = Feld[x][y];
7582 element != EL_SP_PORT_LEFT &&
7583 element != EL_SP_GRAVITY_PORT_LEFT &&
7584 element != EL_SP_PORT_HORIZONTAL &&
7585 element != EL_SP_PORT_ANY) ||
7587 element != EL_SP_PORT_RIGHT &&
7588 element != EL_SP_GRAVITY_PORT_RIGHT &&
7589 element != EL_SP_PORT_HORIZONTAL &&
7590 element != EL_SP_PORT_ANY) ||
7592 element != EL_SP_PORT_UP &&
7593 element != EL_SP_GRAVITY_PORT_UP &&
7594 element != EL_SP_PORT_VERTICAL &&
7595 element != EL_SP_PORT_ANY) ||
7597 element != EL_SP_PORT_DOWN &&
7598 element != EL_SP_GRAVITY_PORT_DOWN &&
7599 element != EL_SP_PORT_VERTICAL &&
7600 element != EL_SP_PORT_ANY) ||
7601 !IN_LEV_FIELD(nextx, nexty) ||
7602 !IS_FREE(nextx, nexty))
7608 static void CheckGravityMovement(struct PlayerInfo *player)
7610 if (game.gravity && !player->programmed_action)
7612 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7613 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7615 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7616 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7617 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7618 int jx = player->jx, jy = player->jy;
7619 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7620 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7621 int new_jx = jx + dx, new_jy = jy + dy;
7622 boolean field_under_player_is_free =
7623 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7624 boolean player_is_moving_to_valid_field =
7625 (IN_LEV_FIELD(new_jx, new_jy) &&
7626 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7627 Feld[new_jx][new_jy] == EL_SAND ||
7628 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7629 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7630 /* !!! extend EL_SAND to anything diggable !!! */
7632 if (field_under_player_is_free &&
7633 !player_is_moving_to_valid_field &&
7634 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7635 player->programmed_action = MV_DOWN;
7641 -----------------------------------------------------------------------------
7642 dx, dy: direction (non-diagonal) to try to move the player to
7643 real_dx, real_dy: direction as read from input device (can be diagonal)
7646 boolean MovePlayerOneStep(struct PlayerInfo *player,
7647 int dx, int dy, int real_dx, int real_dy)
7650 static int change_sides[4][2] =
7652 /* enter side leave side */
7653 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7654 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7655 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7656 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7658 int move_direction = (dx == -1 ? MV_LEFT :
7659 dx == +1 ? MV_RIGHT :
7661 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7662 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7663 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7665 int jx = player->jx, jy = player->jy;
7666 int new_jx = jx + dx, new_jy = jy + dy;
7670 if (!player->active || (!dx && !dy))
7671 return MF_NO_ACTION;
7673 player->MovDir = (dx < 0 ? MV_LEFT :
7676 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7678 if (!IN_LEV_FIELD(new_jx, new_jy))
7679 return MF_NO_ACTION;
7681 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7682 return MF_NO_ACTION;
7685 element = MovingOrBlocked2Element(new_jx, new_jy);
7687 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7690 if (DONT_RUN_INTO(element))
7692 if (element == EL_ACID && dx == 0 && dy == 1)
7695 Feld[jx][jy] = EL_PLAYER_1;
7696 InitMovingField(jx, jy, MV_DOWN);
7697 Store[jx][jy] = EL_ACID;
7698 ContinueMoving(jx, jy);
7702 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7707 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7708 if (can_move != MF_MOVING)
7711 /* check if DigField() has caused relocation of the player */
7712 if (player->jx != jx || player->jy != jy)
7713 return MF_NO_ACTION;
7715 StorePlayer[jx][jy] = 0;
7716 player->last_jx = jx;
7717 player->last_jy = jy;
7718 player->jx = new_jx;
7719 player->jy = new_jy;
7720 StorePlayer[new_jx][new_jy] = player->element_nr;
7723 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7725 player->step_counter++;
7727 player->drop_delay = 0;
7729 PlayerVisit[jx][jy] = FrameCounter;
7731 ScrollPlayer(player, SCROLL_INIT);
7734 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7736 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7737 CE_OTHER_GETS_LEFT);
7738 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7739 CE_LEFT_BY_PLAYER, -1);
7742 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7744 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7745 enter_side, CE_OTHER_GETS_ENTERED);
7746 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7747 CE_ENTERED_BY_PLAYER, -1);
7754 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7756 int jx = player->jx, jy = player->jy;
7757 int old_jx = jx, old_jy = jy;
7758 int moved = MF_NO_ACTION;
7761 if (!player->active)
7766 if (player->MovPos == 0)
7768 player->is_moving = FALSE;
7769 player->is_digging = FALSE;
7770 player->is_collecting = FALSE;
7771 player->is_snapping = FALSE;
7772 player->is_pushing = FALSE;
7778 if (!player->active || (!dx && !dy))
7783 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7787 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7788 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7792 /* remove the last programmed player action */
7793 player->programmed_action = 0;
7797 /* should only happen if pre-1.2 tape recordings are played */
7798 /* this is only for backward compatibility */
7800 int original_move_delay_value = player->move_delay_value;
7803 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7807 /* scroll remaining steps with finest movement resolution */
7808 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7810 while (player->MovPos)
7812 ScrollPlayer(player, SCROLL_GO_ON);
7813 ScrollScreen(NULL, SCROLL_GO_ON);
7819 player->move_delay_value = original_move_delay_value;
7822 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7824 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7825 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7829 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7830 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7836 if (moved & MF_MOVING && !ScreenMovPos &&
7837 (player == local_player || !options.network))
7839 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7840 int offset = (setup.scroll_delay ? 3 : 0);
7842 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7844 /* actual player has left the screen -- scroll in that direction */
7845 if (jx != old_jx) /* player has moved horizontally */
7846 scroll_x += (jx - old_jx);
7847 else /* player has moved vertically */
7848 scroll_y += (jy - old_jy);
7852 if (jx != old_jx) /* player has moved horizontally */
7854 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7855 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7856 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7858 /* don't scroll over playfield boundaries */
7859 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7860 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7862 /* don't scroll more than one field at a time */
7863 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7865 /* don't scroll against the player's moving direction */
7866 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7867 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7868 scroll_x = old_scroll_x;
7870 else /* player has moved vertically */
7872 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7873 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7874 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7876 /* don't scroll over playfield boundaries */
7877 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7878 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7880 /* don't scroll more than one field at a time */
7881 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7883 /* don't scroll against the player's moving direction */
7884 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7885 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7886 scroll_y = old_scroll_y;
7890 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7892 if (!options.network && !AllPlayersInVisibleScreen())
7894 scroll_x = old_scroll_x;
7895 scroll_y = old_scroll_y;
7899 ScrollScreen(player, SCROLL_INIT);
7900 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7907 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7909 if (!(moved & MF_MOVING) && !player->is_pushing)
7914 player->StepFrame = 0;
7916 if (moved & MF_MOVING)
7918 if (old_jx != jx && old_jy == jy)
7919 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7920 else if (old_jx == jx && old_jy != jy)
7921 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7923 DrawLevelField(jx, jy); /* for "crumbled sand" */
7925 player->last_move_dir = player->MovDir;
7926 player->is_moving = TRUE;
7928 player->is_snapping = FALSE;
7932 player->is_switching = FALSE;
7935 player->is_dropping = FALSE;
7940 static int change_sides[4][2] =
7942 /* enter side leave side */
7943 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7944 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7945 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7946 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7948 int move_direction = player->MovDir;
7949 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7950 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7953 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7955 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7956 leave_side, CE_OTHER_GETS_LEFT);
7957 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7958 leave_side, CE_LEFT_BY_PLAYER, -1);
7961 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7963 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7964 enter_side, CE_OTHER_GETS_ENTERED);
7965 CheckElementSideChange(jx, jy, Feld[jx][jy],
7966 enter_side, CE_ENTERED_BY_PLAYER, -1);
7977 CheckGravityMovement(player);
7980 player->last_move_dir = MV_NO_MOVING;
7982 player->is_moving = FALSE;
7985 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7987 TestIfHeroTouchesBadThing(jx, jy);
7988 TestIfPlayerTouchesCustomElement(jx, jy);
7991 if (!player->active)
7997 void ScrollPlayer(struct PlayerInfo *player, int mode)
7999 int jx = player->jx, jy = player->jy;
8000 int last_jx = player->last_jx, last_jy = player->last_jy;
8001 int move_stepsize = TILEX / player->move_delay_value;
8003 if (!player->active || !player->MovPos)
8006 if (mode == SCROLL_INIT)
8008 player->actual_frame_counter = FrameCounter;
8009 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8011 if (Feld[last_jx][last_jy] == EL_EMPTY)
8012 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8020 else if (!FrameReached(&player->actual_frame_counter, 1))
8023 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8024 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8026 if (!player->block_last_field &&
8027 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8028 Feld[last_jx][last_jy] = EL_EMPTY;
8030 /* before DrawPlayer() to draw correct player graphic for this case */
8031 if (player->MovPos == 0)
8032 CheckGravityMovement(player);
8035 DrawPlayer(player); /* needed here only to cleanup last field */
8038 if (player->MovPos == 0) /* player reached destination field */
8041 if (player->move_delay_reset_counter > 0)
8043 player->move_delay_reset_counter--;
8045 if (player->move_delay_reset_counter == 0)
8047 /* continue with normal speed after quickly moving through gate */
8048 HALVE_PLAYER_SPEED(player);
8050 /* be able to make the next move without delay */
8051 player->move_delay = 0;
8055 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8057 /* continue with normal speed after quickly moving through gate */
8058 HALVE_PLAYER_SPEED(player);
8060 /* be able to make the next move without delay */
8061 player->move_delay = 0;
8065 if (player->block_last_field &&
8066 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8067 Feld[last_jx][last_jy] = EL_EMPTY;
8069 player->last_jx = jx;
8070 player->last_jy = jy;
8072 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8073 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8074 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8076 DrawPlayer(player); /* needed here only to cleanup last field */
8079 if (local_player->friends_still_needed == 0 ||
8080 IS_SP_ELEMENT(Feld[jx][jy]))
8081 player->LevelSolved = player->GameOver = TRUE;
8084 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8086 TestIfHeroTouchesBadThing(jx, jy);
8087 TestIfPlayerTouchesCustomElement(jx, jy);
8089 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8092 if (!player->active)
8096 if (tape.single_step && tape.recording && !tape.pausing &&
8097 !player->programmed_action)
8098 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8102 void ScrollScreen(struct PlayerInfo *player, int mode)
8104 static unsigned long screen_frame_counter = 0;
8106 if (mode == SCROLL_INIT)
8108 /* set scrolling step size according to actual player's moving speed */
8109 ScrollStepSize = TILEX / player->move_delay_value;
8111 screen_frame_counter = FrameCounter;
8112 ScreenMovDir = player->MovDir;
8113 ScreenMovPos = player->MovPos;
8114 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8117 else if (!FrameReached(&screen_frame_counter, 1))
8122 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8123 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8124 redraw_mask |= REDRAW_FIELD;
8127 ScreenMovDir = MV_NO_MOVING;
8130 void TestIfPlayerTouchesCustomElement(int x, int y)
8132 static int xy[4][2] =
8139 static int change_sides[4][2] =
8141 /* center side border side */
8142 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8143 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8144 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8145 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8147 static int touch_dir[4] =
8154 int center_element = Feld[x][y]; /* should always be non-moving! */
8157 for (i = 0; i < 4; i++)
8159 int xx = x + xy[i][0];
8160 int yy = y + xy[i][1];
8161 int center_side = change_sides[i][0];
8162 int border_side = change_sides[i][1];
8165 if (!IN_LEV_FIELD(xx, yy))
8168 if (IS_PLAYER(x, y))
8170 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8171 border_element = Feld[xx][yy]; /* may be moving! */
8172 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8173 border_element = Feld[xx][yy];
8174 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8175 border_element = MovingOrBlocked2Element(xx, yy);
8177 continue; /* center and border element do not touch */
8179 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8180 CE_OTHER_GETS_TOUCHED);
8181 CheckElementSideChange(xx, yy, border_element, border_side,
8182 CE_TOUCHED_BY_PLAYER, -1);
8184 else if (IS_PLAYER(xx, yy))
8186 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8188 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8190 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8191 continue; /* center and border element do not touch */
8194 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8195 CE_OTHER_GETS_TOUCHED);
8196 CheckElementSideChange(x, y, center_element, center_side,
8197 CE_TOUCHED_BY_PLAYER, -1);
8204 void TestIfElementTouchesCustomElement(int x, int y)
8206 static int xy[4][2] =
8213 static int change_sides[4][2] =
8215 /* center side border side */
8216 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8217 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8218 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8219 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8221 static int touch_dir[4] =
8228 boolean change_center_element = FALSE;
8229 int center_element_change_page = 0;
8230 int center_element = Feld[x][y]; /* should always be non-moving! */
8233 for (i = 0; i < 4; i++)
8235 int xx = x + xy[i][0];
8236 int yy = y + xy[i][1];
8237 int center_side = change_sides[i][0];
8238 int border_side = change_sides[i][1];
8241 if (!IN_LEV_FIELD(xx, yy))
8244 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8245 border_element = Feld[xx][yy]; /* may be moving! */
8246 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8247 border_element = Feld[xx][yy];
8248 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8249 border_element = MovingOrBlocked2Element(xx, yy);
8251 continue; /* center and border element do not touch */
8253 /* check for change of center element (but change it only once) */
8254 if (IS_CUSTOM_ELEMENT(center_element) &&
8255 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8256 !change_center_element)
8258 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8260 struct ElementChangeInfo *change =
8261 &element_info[center_element].change_page[j];
8263 if (change->can_change &&
8264 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8265 change->sides & border_side &&
8267 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8269 change->trigger_element == border_element
8273 change_center_element = TRUE;
8274 center_element_change_page = j;
8281 /* check for change of border element */
8282 if (IS_CUSTOM_ELEMENT(border_element) &&
8283 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8285 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8287 struct ElementChangeInfo *change =
8288 &element_info[border_element].change_page[j];
8290 if (change->can_change &&
8291 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8292 change->sides & center_side &&
8294 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8296 change->trigger_element == center_element
8300 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8301 CE_OTHER_IS_TOUCHING, j);
8308 if (change_center_element)
8309 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8310 CE_OTHER_IS_TOUCHING, center_element_change_page);
8313 void TestIfElementHitsCustomElement(int x, int y, int direction)
8315 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8316 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8317 int hitx = x + dx, hity = y + dy;
8318 int hitting_element = Feld[x][y];
8320 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8321 !IS_FREE(hitx, hity) &&
8322 (!IS_MOVING(hitx, hity) ||
8323 MovDir[hitx][hity] != direction ||
8324 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8327 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8331 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8335 CheckElementSideChange(x, y, hitting_element,
8336 direction, CE_HITTING_SOMETHING, -1);
8338 if (IN_LEV_FIELD(hitx, hity))
8340 static int opposite_directions[] =
8347 int move_dir_bit = MV_DIR_BIT(direction);
8348 int opposite_direction = opposite_directions[move_dir_bit];
8349 int hitting_side = direction;
8350 int touched_side = opposite_direction;
8351 int touched_element = MovingOrBlocked2Element(hitx, hity);
8353 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8354 MovDir[hitx][hity] != direction ||
8355 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8364 CheckElementSideChange(hitx, hity, touched_element,
8365 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8367 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8368 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8370 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8372 struct ElementChangeInfo *change =
8373 &element_info[hitting_element].change_page[i];
8375 if (change->can_change &&
8376 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8377 change->sides & touched_side &&
8380 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8382 change->trigger_element == touched_element
8386 CheckElementSideChange(x, y, hitting_element,
8387 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8393 if (IS_CUSTOM_ELEMENT(touched_element) &&
8394 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8396 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8398 struct ElementChangeInfo *change =
8399 &element_info[touched_element].change_page[i];
8401 if (change->can_change &&
8402 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8403 change->sides & hitting_side &&
8405 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8407 change->trigger_element == hitting_element
8411 CheckElementSideChange(hitx, hity, touched_element,
8412 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8421 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8423 int i, kill_x = -1, kill_y = -1;
8424 static int test_xy[4][2] =
8431 static int test_dir[4] =
8439 for (i = 0; i < 4; i++)
8441 int test_x, test_y, test_move_dir, test_element;
8443 test_x = good_x + test_xy[i][0];
8444 test_y = good_y + test_xy[i][1];
8445 if (!IN_LEV_FIELD(test_x, test_y))
8449 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8452 test_element = Feld[test_x][test_y];
8454 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8457 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8458 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8460 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8461 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8469 if (kill_x != -1 || kill_y != -1)
8471 if (IS_PLAYER(good_x, good_y))
8473 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8475 if (player->shield_deadly_time_left > 0)
8476 Bang(kill_x, kill_y);
8477 else if (!PLAYER_PROTECTED(good_x, good_y))
8481 Bang(good_x, good_y);
8485 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8487 int i, kill_x = -1, kill_y = -1;
8488 int bad_element = Feld[bad_x][bad_y];
8489 static int test_xy[4][2] =
8496 static int touch_dir[4] =
8503 static int test_dir[4] =
8511 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8514 for (i = 0; i < 4; i++)
8516 int test_x, test_y, test_move_dir, test_element;
8518 test_x = bad_x + test_xy[i][0];
8519 test_y = bad_y + test_xy[i][1];
8520 if (!IN_LEV_FIELD(test_x, test_y))
8524 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8526 test_element = Feld[test_x][test_y];
8528 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8529 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8531 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8532 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8534 /* good thing is player or penguin that does not move away */
8535 if (IS_PLAYER(test_x, test_y))
8537 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8539 if (bad_element == EL_ROBOT && player->is_moving)
8540 continue; /* robot does not kill player if he is moving */
8542 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8544 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8545 continue; /* center and border element do not touch */
8552 else if (test_element == EL_PENGUIN)
8561 if (kill_x != -1 || kill_y != -1)
8563 if (IS_PLAYER(kill_x, kill_y))
8565 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8567 if (player->shield_deadly_time_left > 0)
8569 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8573 Bang(kill_x, kill_y);
8577 void TestIfHeroTouchesBadThing(int x, int y)
8579 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8582 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8584 TestIfGoodThingHitsBadThing(x, y, move_dir);
8587 void TestIfBadThingTouchesHero(int x, int y)
8589 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8592 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8594 TestIfBadThingHitsGoodThing(x, y, move_dir);
8597 void TestIfFriendTouchesBadThing(int x, int y)
8599 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8602 void TestIfBadThingTouchesFriend(int x, int y)
8604 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8607 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8609 int i, kill_x = bad_x, kill_y = bad_y;
8610 static int xy[4][2] =
8618 for (i = 0; i < 4; i++)
8622 x = bad_x + xy[i][0];
8623 y = bad_y + xy[i][1];
8624 if (!IN_LEV_FIELD(x, y))
8627 element = Feld[x][y];
8628 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8629 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8637 if (kill_x != bad_x || kill_y != bad_y)
8641 void KillHero(struct PlayerInfo *player)
8643 int jx = player->jx, jy = player->jy;
8645 if (!player->active)
8648 /* remove accessible field at the player's position */
8649 Feld[jx][jy] = EL_EMPTY;
8651 /* deactivate shield (else Bang()/Explode() would not work right) */
8652 player->shield_normal_time_left = 0;
8653 player->shield_deadly_time_left = 0;
8659 static void KillHeroUnlessProtected(int x, int y)
8661 if (!PLAYER_PROTECTED(x, y))
8662 KillHero(PLAYERINFO(x, y));
8665 void BuryHero(struct PlayerInfo *player)
8667 int jx = player->jx, jy = player->jy;
8669 if (!player->active)
8673 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8675 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8677 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8679 player->GameOver = TRUE;
8683 void RemoveHero(struct PlayerInfo *player)
8685 int jx = player->jx, jy = player->jy;
8686 int i, found = FALSE;
8688 player->present = FALSE;
8689 player->active = FALSE;
8691 if (!ExplodeField[jx][jy])
8692 StorePlayer[jx][jy] = 0;
8694 for (i = 0; i < MAX_PLAYERS; i++)
8695 if (stored_player[i].active)
8699 AllPlayersGone = TRUE;
8706 =============================================================================
8707 checkDiagonalPushing()
8708 -----------------------------------------------------------------------------
8709 check if diagonal input device direction results in pushing of object
8710 (by checking if the alternative direction is walkable, diggable, ...)
8711 =============================================================================
8714 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8715 int x, int y, int real_dx, int real_dy)
8717 int jx, jy, dx, dy, xx, yy;
8719 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8722 /* diagonal direction: check alternative direction */
8727 xx = jx + (dx == 0 ? real_dx : 0);
8728 yy = jy + (dy == 0 ? real_dy : 0);
8730 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8734 =============================================================================
8736 -----------------------------------------------------------------------------
8737 x, y: field next to player (non-diagonal) to try to dig to
8738 real_dx, real_dy: direction as read from input device (can be diagonal)
8739 =============================================================================
8742 int DigField(struct PlayerInfo *player,
8743 int x, int y, int real_dx, int real_dy, int mode)
8745 static int change_sides[4] =
8747 CH_SIDE_RIGHT, /* moving left */
8748 CH_SIDE_LEFT, /* moving right */
8749 CH_SIDE_BOTTOM, /* moving up */
8750 CH_SIDE_TOP, /* moving down */
8752 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8753 int jx = player->jx, jy = player->jy;
8754 int dx = x - jx, dy = y - jy;
8755 int nextx = x + dx, nexty = y + dy;
8756 int move_direction = (dx == -1 ? MV_LEFT :
8757 dx == +1 ? MV_RIGHT :
8759 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8760 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8763 if (player->MovPos == 0)
8765 player->is_digging = FALSE;
8766 player->is_collecting = FALSE;
8769 if (player->MovPos == 0) /* last pushing move finished */
8770 player->is_pushing = FALSE;
8772 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8774 player->is_switching = FALSE;
8775 player->push_delay = 0;
8777 return MF_NO_ACTION;
8780 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8781 return MF_NO_ACTION;
8784 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8786 if (IS_TUBE(Feld[jx][jy]) ||
8787 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8791 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8792 int tube_leave_directions[][2] =
8794 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8795 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8796 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8797 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8798 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8799 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8800 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8801 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8802 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8803 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8804 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8805 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8808 while (tube_leave_directions[i][0] != tube_element)
8811 if (tube_leave_directions[i][0] == -1) /* should not happen */
8815 if (!(tube_leave_directions[i][1] & move_direction))
8816 return MF_NO_ACTION; /* tube has no opening in this direction */
8819 element = Feld[x][y];
8821 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8822 game.engine_version >= VERSION_IDENT(2,2,0,0))
8823 return MF_NO_ACTION;
8827 case EL_SP_PORT_LEFT:
8828 case EL_SP_PORT_RIGHT:
8830 case EL_SP_PORT_DOWN:
8831 case EL_SP_PORT_HORIZONTAL:
8832 case EL_SP_PORT_VERTICAL:
8833 case EL_SP_PORT_ANY:
8834 case EL_SP_GRAVITY_PORT_LEFT:
8835 case EL_SP_GRAVITY_PORT_RIGHT:
8836 case EL_SP_GRAVITY_PORT_UP:
8837 case EL_SP_GRAVITY_PORT_DOWN:
8839 if (!canEnterSupaplexPort(x, y, dx, dy))
8840 return MF_NO_ACTION;
8843 element != EL_SP_PORT_LEFT &&
8844 element != EL_SP_GRAVITY_PORT_LEFT &&
8845 element != EL_SP_PORT_HORIZONTAL &&
8846 element != EL_SP_PORT_ANY) ||
8848 element != EL_SP_PORT_RIGHT &&
8849 element != EL_SP_GRAVITY_PORT_RIGHT &&
8850 element != EL_SP_PORT_HORIZONTAL &&
8851 element != EL_SP_PORT_ANY) ||
8853 element != EL_SP_PORT_UP &&
8854 element != EL_SP_GRAVITY_PORT_UP &&
8855 element != EL_SP_PORT_VERTICAL &&
8856 element != EL_SP_PORT_ANY) ||
8858 element != EL_SP_PORT_DOWN &&
8859 element != EL_SP_GRAVITY_PORT_DOWN &&
8860 element != EL_SP_PORT_VERTICAL &&
8861 element != EL_SP_PORT_ANY) ||
8862 !IN_LEV_FIELD(nextx, nexty) ||
8863 !IS_FREE(nextx, nexty))
8864 return MF_NO_ACTION;
8867 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8868 element == EL_SP_GRAVITY_PORT_RIGHT ||
8869 element == EL_SP_GRAVITY_PORT_UP ||
8870 element == EL_SP_GRAVITY_PORT_DOWN)
8871 game.gravity = !game.gravity;
8873 /* automatically move to the next field with double speed */
8874 player->programmed_action = move_direction;
8876 if (player->move_delay_reset_counter == 0)
8878 player->move_delay_reset_counter = 2; /* two double speed steps */
8880 DOUBLE_PLAYER_SPEED(player);
8883 player->move_delay_reset_counter = 2;
8885 DOUBLE_PLAYER_SPEED(player);
8888 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8892 case EL_TUBE_VERTICAL:
8893 case EL_TUBE_HORIZONTAL:
8894 case EL_TUBE_VERTICAL_LEFT:
8895 case EL_TUBE_VERTICAL_RIGHT:
8896 case EL_TUBE_HORIZONTAL_UP:
8897 case EL_TUBE_HORIZONTAL_DOWN:
8898 case EL_TUBE_LEFT_UP:
8899 case EL_TUBE_LEFT_DOWN:
8900 case EL_TUBE_RIGHT_UP:
8901 case EL_TUBE_RIGHT_DOWN:
8904 int tube_enter_directions[][2] =
8906 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8907 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8908 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8909 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8910 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8911 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8912 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8913 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8914 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8915 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8916 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8917 { -1, MV_NO_MOVING }
8920 while (tube_enter_directions[i][0] != element)
8923 if (tube_enter_directions[i][0] == -1) /* should not happen */
8927 if (!(tube_enter_directions[i][1] & move_direction))
8928 return MF_NO_ACTION; /* tube has no opening in this direction */
8930 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8936 if (IS_WALKABLE(element))
8938 int sound_action = ACTION_WALKING;
8940 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8942 if (!player->key[element - EL_GATE_1])
8943 return MF_NO_ACTION;
8945 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8947 if (!player->key[element - EL_GATE_1_GRAY])
8948 return MF_NO_ACTION;
8950 else if (element == EL_EXIT_OPEN ||
8951 element == EL_SP_EXIT_OPEN ||
8952 element == EL_SP_EXIT_OPENING)
8954 sound_action = ACTION_PASSING; /* player is passing exit */
8956 else if (element == EL_EMPTY)
8958 sound_action = ACTION_MOVING; /* nothing to walk on */
8961 /* play sound from background or player, whatever is available */
8962 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8963 PlayLevelSoundElementAction(x, y, element, sound_action);
8965 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8969 else if (IS_PASSABLE(element))
8971 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8972 return MF_NO_ACTION;
8975 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8976 return MF_NO_ACTION;
8979 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8981 if (!player->key[element - EL_EM_GATE_1])
8982 return MF_NO_ACTION;
8984 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8986 if (!player->key[element - EL_EM_GATE_1_GRAY])
8987 return MF_NO_ACTION;
8990 /* automatically move to the next field with double speed */
8991 player->programmed_action = move_direction;
8993 if (player->move_delay_reset_counter == 0)
8995 player->move_delay_reset_counter = 2; /* two double speed steps */
8997 DOUBLE_PLAYER_SPEED(player);
9000 player->move_delay_reset_counter = 2;
9002 DOUBLE_PLAYER_SPEED(player);
9005 PlayLevelSoundAction(x, y, ACTION_PASSING);
9009 else if (IS_DIGGABLE(element))
9013 if (mode != DF_SNAP)
9016 GfxElement[x][y] = GFX_ELEMENT(element);
9019 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9021 player->is_digging = TRUE;
9024 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9026 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9029 if (mode == DF_SNAP)
9030 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9035 else if (IS_COLLECTIBLE(element))
9039 if (mode != DF_SNAP)
9041 GfxElement[x][y] = element;
9042 player->is_collecting = TRUE;
9045 if (element == EL_SPEED_PILL)
9046 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9047 else if (element == EL_EXTRA_TIME && level.time > 0)
9050 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9052 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9054 player->shield_normal_time_left += 10;
9055 if (element == EL_SHIELD_DEADLY)
9056 player->shield_deadly_time_left += 10;
9058 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9060 if (player->inventory_size < MAX_INVENTORY_SIZE)
9061 player->inventory_element[player->inventory_size++] = element;
9063 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9064 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9066 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9068 player->dynabomb_count++;
9069 player->dynabombs_left++;
9071 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9073 player->dynabomb_size++;
9075 else if (element == EL_DYNABOMB_INCREASE_POWER)
9077 player->dynabomb_xl = TRUE;
9079 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9080 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9082 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9083 element - EL_KEY_1 : element - EL_EM_KEY_1);
9085 player->key[key_nr] = TRUE;
9087 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9088 el2edimg(EL_KEY_1 + key_nr));
9089 redraw_mask |= REDRAW_DOOR_1;
9091 else if (IS_ENVELOPE(element))
9094 player->show_envelope = element;
9096 ShowEnvelope(element - EL_ENVELOPE_1);
9099 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9103 for (i = 0; i < element_info[element].collect_count; i++)
9104 if (player->inventory_size < MAX_INVENTORY_SIZE)
9105 player->inventory_element[player->inventory_size++] = element;
9107 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9108 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9110 else if (element_info[element].collect_count > 0)
9112 local_player->gems_still_needed -=
9113 element_info[element].collect_count;
9114 if (local_player->gems_still_needed < 0)
9115 local_player->gems_still_needed = 0;
9117 DrawText(DX_EMERALDS, DY_EMERALDS,
9118 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9121 RaiseScoreElement(element);
9122 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9124 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9127 if (mode == DF_SNAP)
9128 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9133 else if (IS_PUSHABLE(element))
9135 if (mode == DF_SNAP && element != EL_BD_ROCK)
9136 return MF_NO_ACTION;
9138 if (CAN_FALL(element) && dy)
9139 return MF_NO_ACTION;
9141 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9142 !(element == EL_SPRING && use_spring_bug))
9143 return MF_NO_ACTION;
9146 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9147 ((move_direction & MV_VERTICAL &&
9148 ((element_info[element].move_pattern & MV_LEFT &&
9149 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9150 (element_info[element].move_pattern & MV_RIGHT &&
9151 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9152 (move_direction & MV_HORIZONTAL &&
9153 ((element_info[element].move_pattern & MV_UP &&
9154 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9155 (element_info[element].move_pattern & MV_DOWN &&
9156 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9157 return MF_NO_ACTION;
9161 /* do not push elements already moving away faster than player */
9162 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9163 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9164 return MF_NO_ACTION;
9166 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9167 return MF_NO_ACTION;
9171 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9173 if (player->push_delay_value == -1)
9174 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9176 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9178 if (!player->is_pushing)
9179 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9183 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9184 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9185 !player_is_pushing))
9186 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9189 if (!player->is_pushing &&
9190 game.engine_version >= VERSION_IDENT(2,2,0,7))
9191 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9195 printf("::: push delay: %ld [%d, %d] [%d]\n",
9196 player->push_delay_value, FrameCounter, game.engine_version,
9197 player->is_pushing);
9200 player->is_pushing = TRUE;
9202 if (!(IN_LEV_FIELD(nextx, nexty) &&
9203 (IS_FREE(nextx, nexty) ||
9204 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9205 IS_SB_ELEMENT(element)))))
9206 return MF_NO_ACTION;
9208 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9209 return MF_NO_ACTION;
9211 if (player->push_delay == 0) /* new pushing; restart delay */
9212 player->push_delay = FrameCounter;
9214 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9215 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9216 element != EL_SPRING && element != EL_BALLOON)
9218 /* make sure that there is no move delay before next try to push */
9219 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9220 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9222 return MF_NO_ACTION;
9226 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9229 if (IS_SB_ELEMENT(element))
9231 if (element == EL_SOKOBAN_FIELD_FULL)
9233 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9234 local_player->sokobanfields_still_needed++;
9237 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9239 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9240 local_player->sokobanfields_still_needed--;
9243 Feld[x][y] = EL_SOKOBAN_OBJECT;
9245 if (Back[x][y] == Back[nextx][nexty])
9246 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9247 else if (Back[x][y] != 0)
9248 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9251 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9254 if (local_player->sokobanfields_still_needed == 0 &&
9255 game.emulation == EMU_SOKOBAN)
9257 player->LevelSolved = player->GameOver = TRUE;
9258 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9262 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9264 InitMovingField(x, y, move_direction);
9265 GfxAction[x][y] = ACTION_PUSHING;
9267 if (mode == DF_SNAP)
9268 ContinueMoving(x, y);
9270 MovPos[x][y] = (dx != 0 ? dx : dy);
9272 Pushed[x][y] = TRUE;
9273 Pushed[nextx][nexty] = TRUE;
9275 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9276 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9278 player->push_delay_value = -1; /* get new value later */
9280 CheckTriggeredElementSideChange(x, y, element, dig_side,
9281 CE_OTHER_GETS_PUSHED);
9282 CheckElementSideChange(x, y, element, dig_side,
9283 CE_PUSHED_BY_PLAYER, -1);
9287 else if (IS_SWITCHABLE(element))
9289 if (PLAYER_SWITCHING(player, x, y))
9292 player->is_switching = TRUE;
9293 player->switch_x = x;
9294 player->switch_y = y;
9296 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9298 if (element == EL_ROBOT_WHEEL)
9300 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9304 DrawLevelField(x, y);
9306 else if (element == EL_SP_TERMINAL)
9310 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9312 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9314 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9315 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9318 else if (IS_BELT_SWITCH(element))
9320 ToggleBeltSwitch(x, y);
9322 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9323 element == EL_SWITCHGATE_SWITCH_DOWN)
9325 ToggleSwitchgateSwitch(x, y);
9327 else if (element == EL_LIGHT_SWITCH ||
9328 element == EL_LIGHT_SWITCH_ACTIVE)
9330 ToggleLightSwitch(x, y);
9333 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9334 SND_LIGHT_SWITCH_ACTIVATING :
9335 SND_LIGHT_SWITCH_DEACTIVATING);
9338 else if (element == EL_TIMEGATE_SWITCH)
9340 ActivateTimegateSwitch(x, y);
9342 else if (element == EL_BALLOON_SWITCH_LEFT ||
9343 element == EL_BALLOON_SWITCH_RIGHT ||
9344 element == EL_BALLOON_SWITCH_UP ||
9345 element == EL_BALLOON_SWITCH_DOWN ||
9346 element == EL_BALLOON_SWITCH_ANY)
9348 if (element == EL_BALLOON_SWITCH_ANY)
9349 game.balloon_dir = move_direction;
9351 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9352 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9353 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9354 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9357 else if (element == EL_LAMP)
9359 Feld[x][y] = EL_LAMP_ACTIVE;
9360 local_player->lights_still_needed--;
9362 DrawLevelField(x, y);
9364 else if (element == EL_TIME_ORB_FULL)
9366 Feld[x][y] = EL_TIME_ORB_EMPTY;
9368 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9370 DrawLevelField(x, y);
9373 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9381 if (!PLAYER_SWITCHING(player, x, y))
9383 player->is_switching = TRUE;
9384 player->switch_x = x;
9385 player->switch_y = y;
9387 CheckTriggeredElementSideChange(x, y, element, dig_side,
9388 CE_OTHER_IS_SWITCHING);
9389 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9392 CheckTriggeredElementSideChange(x, y, element, dig_side,
9393 CE_OTHER_GETS_PRESSED);
9394 CheckElementSideChange(x, y, element, dig_side,
9395 CE_PRESSED_BY_PLAYER, -1);
9398 return MF_NO_ACTION;
9401 player->push_delay = 0;
9403 if (Feld[x][y] != element) /* really digged/collected something */
9404 player->is_collecting = !player->is_digging;
9409 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9411 int jx = player->jx, jy = player->jy;
9412 int x = jx + dx, y = jy + dy;
9413 int snap_direction = (dx == -1 ? MV_LEFT :
9414 dx == +1 ? MV_RIGHT :
9416 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9418 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9421 if (!player->active || !IN_LEV_FIELD(x, y))
9429 if (player->MovPos == 0)
9430 player->is_pushing = FALSE;
9432 player->is_snapping = FALSE;
9434 if (player->MovPos == 0)
9436 player->is_moving = FALSE;
9437 player->is_digging = FALSE;
9438 player->is_collecting = FALSE;
9444 if (player->is_snapping)
9447 player->MovDir = snap_direction;
9450 if (player->MovPos == 0)
9453 player->is_moving = FALSE;
9454 player->is_digging = FALSE;
9455 player->is_collecting = FALSE;
9458 player->is_dropping = FALSE;
9460 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9463 player->is_snapping = TRUE;
9466 if (player->MovPos == 0)
9469 player->is_moving = FALSE;
9470 player->is_digging = FALSE;
9471 player->is_collecting = FALSE;
9474 DrawLevelField(x, y);
9480 boolean DropElement(struct PlayerInfo *player)
9482 int jx = player->jx, jy = player->jy;
9483 int old_element = Feld[jx][jy];
9486 /* check if player is active, not moving and ready to drop */
9487 if (!player->active || player->MovPos || player->drop_delay > 0)
9490 /* check if player has anything that can be dropped */
9491 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9494 /* check if anything can be dropped at the current position */
9495 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9498 /* collected custom elements can only be dropped on empty fields */
9499 if (player->inventory_size > 0 &&
9500 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9501 && old_element != EL_EMPTY)
9504 if (old_element != EL_EMPTY)
9505 Back[jx][jy] = old_element; /* store old element on this field */
9507 ResetGfxAnimation(jx, jy);
9508 ResetRandomAnimationValue(jx, jy);
9510 if (player->inventory_size > 0)
9512 player->inventory_size--;
9513 new_element = player->inventory_element[player->inventory_size];
9515 if (new_element == EL_DYNAMITE)
9516 new_element = EL_DYNAMITE_ACTIVE;
9517 else if (new_element == EL_SP_DISK_RED)
9518 new_element = EL_SP_DISK_RED_ACTIVE;
9520 Feld[jx][jy] = new_element;
9522 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9523 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9525 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9526 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9528 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9531 /* needed if previous element just changed to "empty" in the last frame */
9532 Changed[jx][jy] = 0; /* allow another change */
9535 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9536 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9538 TestIfElementTouchesCustomElement(jx, jy);
9540 else /* player is dropping a dyna bomb */
9542 player->dynabombs_left--;
9543 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9545 Feld[jx][jy] = new_element;
9547 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9548 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9550 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9557 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9559 InitField(jx, jy, FALSE);
9560 if (CAN_MOVE(Feld[jx][jy]))
9564 new_element = Feld[jx][jy];
9566 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9567 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9569 int move_stepsize = element_info[new_element].move_stepsize;
9570 int direction, dx, dy, nextx, nexty;
9572 if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
9573 MovDir[jx][jy] = player->MovDir;
9575 direction = MovDir[jx][jy];
9576 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9577 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9581 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9584 WasJustMoving[jx][jy] = 3;
9586 InitMovingField(jx, jy, direction);
9587 ContinueMoving(jx, jy);
9592 Changed[jx][jy] = 0; /* allow another change */
9595 TestIfElementHitsCustomElement(jx, jy, direction);
9597 CheckElementSideChange(jx, jy, new_element,
9598 direction, CE_HITTING_SOMETHING, -1);
9602 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9606 player->drop_delay = 8 + 8 + 8;
9611 player->is_dropping = TRUE;
9617 /* ------------------------------------------------------------------------- */
9618 /* game sound playing functions */
9619 /* ------------------------------------------------------------------------- */
9621 static int *loop_sound_frame = NULL;
9622 static int *loop_sound_volume = NULL;
9624 void InitPlayLevelSound()
9626 int num_sounds = getSoundListSize();
9628 checked_free(loop_sound_frame);
9629 checked_free(loop_sound_volume);
9631 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9632 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9635 static void PlayLevelSound(int x, int y, int nr)
9637 int sx = SCREENX(x), sy = SCREENY(y);
9638 int volume, stereo_position;
9639 int max_distance = 8;
9640 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9642 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9643 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9646 if (!IN_LEV_FIELD(x, y) ||
9647 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9648 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9651 volume = SOUND_MAX_VOLUME;
9653 if (!IN_SCR_FIELD(sx, sy))
9655 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9656 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9658 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9661 stereo_position = (SOUND_MAX_LEFT +
9662 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9663 (SCR_FIELDX + 2 * max_distance));
9665 if (IS_LOOP_SOUND(nr))
9667 /* This assures that quieter loop sounds do not overwrite louder ones,
9668 while restarting sound volume comparison with each new game frame. */
9670 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9673 loop_sound_volume[nr] = volume;
9674 loop_sound_frame[nr] = FrameCounter;
9677 PlaySoundExt(nr, volume, stereo_position, type);
9680 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9682 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9683 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9684 y < LEVELY(BY1) ? LEVELY(BY1) :
9685 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9689 static void PlayLevelSoundAction(int x, int y, int action)
9691 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9694 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9696 int sound_effect = element_info[element].sound[action];
9698 if (sound_effect != SND_UNDEFINED)
9699 PlayLevelSound(x, y, sound_effect);
9702 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9705 int sound_effect = element_info[element].sound[action];
9707 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9708 PlayLevelSound(x, y, sound_effect);
9711 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9713 int sound_effect = element_info[Feld[x][y]].sound[action];
9715 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9716 PlayLevelSound(x, y, sound_effect);
9719 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9721 int sound_effect = element_info[Feld[x][y]].sound[action];
9723 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9724 StopSound(sound_effect);
9727 static void PlayLevelMusic()
9729 if (levelset.music[level_nr] != MUS_UNDEFINED)
9730 PlayMusic(levelset.music[level_nr]); /* from config file */
9732 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9735 void RaiseScore(int value)
9737 local_player->score += value;
9738 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9741 void RaiseScoreElement(int element)
9747 case EL_EMERALD_YELLOW:
9748 case EL_EMERALD_RED:
9749 case EL_EMERALD_PURPLE:
9750 case EL_SP_INFOTRON:
9751 RaiseScore(level.score[SC_EMERALD]);
9754 RaiseScore(level.score[SC_DIAMOND]);
9757 RaiseScore(level.score[SC_CRYSTAL]);
9760 RaiseScore(level.score[SC_PEARL]);
9763 case EL_BD_BUTTERFLY:
9764 case EL_SP_ELECTRON:
9765 RaiseScore(level.score[SC_BUG]);
9769 case EL_SP_SNIKSNAK:
9770 RaiseScore(level.score[SC_SPACESHIP]);
9773 case EL_DARK_YAMYAM:
9774 RaiseScore(level.score[SC_YAMYAM]);
9777 RaiseScore(level.score[SC_ROBOT]);
9780 RaiseScore(level.score[SC_PACMAN]);
9783 RaiseScore(level.score[SC_NUT]);
9786 case EL_SP_DISK_RED:
9787 case EL_DYNABOMB_INCREASE_NUMBER:
9788 case EL_DYNABOMB_INCREASE_SIZE:
9789 case EL_DYNABOMB_INCREASE_POWER:
9790 RaiseScore(level.score[SC_DYNAMITE]);
9792 case EL_SHIELD_NORMAL:
9793 case EL_SHIELD_DEADLY:
9794 RaiseScore(level.score[SC_SHIELD]);
9797 RaiseScore(level.score[SC_TIME_BONUS]);
9803 RaiseScore(level.score[SC_KEY]);
9806 RaiseScore(element_info[element].collect_score);
9811 void RequestQuitGame(boolean ask_if_really_quit)
9813 if (AllPlayersGone ||
9814 !ask_if_really_quit ||
9815 level_editor_test_game ||
9816 Request("Do you really want to quit the game ?",
9817 REQ_ASK | REQ_STAY_CLOSED))
9819 #if defined(PLATFORM_UNIX)
9820 if (options.network)
9821 SendToServer_StopPlaying();
9825 game_status = GAME_MODE_MAIN;
9831 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9836 /* ---------- new game button stuff ---------------------------------------- */
9838 /* graphic position values for game buttons */
9839 #define GAME_BUTTON_XSIZE 30
9840 #define GAME_BUTTON_YSIZE 30
9841 #define GAME_BUTTON_XPOS 5
9842 #define GAME_BUTTON_YPOS 215
9843 #define SOUND_BUTTON_XPOS 5
9844 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9846 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9847 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9848 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9849 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9850 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9851 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9858 } gamebutton_info[NUM_GAME_BUTTONS] =
9861 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9866 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9871 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9876 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9877 SOUND_CTRL_ID_MUSIC,
9878 "background music on/off"
9881 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9882 SOUND_CTRL_ID_LOOPS,
9883 "sound loops on/off"
9886 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9887 SOUND_CTRL_ID_SIMPLE,
9888 "normal sounds on/off"
9892 void CreateGameButtons()
9896 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9898 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9899 struct GadgetInfo *gi;
9902 unsigned long event_mask;
9903 int gd_xoffset, gd_yoffset;
9904 int gd_x1, gd_x2, gd_y1, gd_y2;
9907 gd_xoffset = gamebutton_info[i].x;
9908 gd_yoffset = gamebutton_info[i].y;
9909 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9910 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9912 if (id == GAME_CTRL_ID_STOP ||
9913 id == GAME_CTRL_ID_PAUSE ||
9914 id == GAME_CTRL_ID_PLAY)
9916 button_type = GD_TYPE_NORMAL_BUTTON;
9918 event_mask = GD_EVENT_RELEASED;
9919 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9920 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9924 button_type = GD_TYPE_CHECK_BUTTON;
9926 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9927 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9928 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9929 event_mask = GD_EVENT_PRESSED;
9930 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9931 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9934 gi = CreateGadget(GDI_CUSTOM_ID, id,
9935 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9936 GDI_X, DX + gd_xoffset,
9937 GDI_Y, DY + gd_yoffset,
9938 GDI_WIDTH, GAME_BUTTON_XSIZE,
9939 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9940 GDI_TYPE, button_type,
9941 GDI_STATE, GD_BUTTON_UNPRESSED,
9942 GDI_CHECKED, checked,
9943 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9944 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9945 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9946 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9947 GDI_EVENT_MASK, event_mask,
9948 GDI_CALLBACK_ACTION, HandleGameButtons,
9952 Error(ERR_EXIT, "cannot create gadget");
9954 game_gadget[id] = gi;
9958 void FreeGameButtons()
9962 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9963 FreeGadget(game_gadget[i]);
9966 static void MapGameButtons()
9970 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9971 MapGadget(game_gadget[i]);
9974 void UnmapGameButtons()
9978 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9979 UnmapGadget(game_gadget[i]);
9982 static void HandleGameButtons(struct GadgetInfo *gi)
9984 int id = gi->custom_id;
9986 if (game_status != GAME_MODE_PLAYING)
9991 case GAME_CTRL_ID_STOP:
9992 RequestQuitGame(TRUE);
9995 case GAME_CTRL_ID_PAUSE:
9996 if (options.network)
9998 #if defined(PLATFORM_UNIX)
10000 SendToServer_ContinuePlaying();
10002 SendToServer_PausePlaying();
10006 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10009 case GAME_CTRL_ID_PLAY:
10012 #if defined(PLATFORM_UNIX)
10013 if (options.network)
10014 SendToServer_ContinuePlaying();
10018 tape.pausing = FALSE;
10019 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10024 case SOUND_CTRL_ID_MUSIC:
10025 if (setup.sound_music)
10027 setup.sound_music = FALSE;
10030 else if (audio.music_available)
10032 setup.sound = setup.sound_music = TRUE;
10034 SetAudioMode(setup.sound);
10040 case SOUND_CTRL_ID_LOOPS:
10041 if (setup.sound_loops)
10042 setup.sound_loops = FALSE;
10043 else if (audio.loops_available)
10045 setup.sound = setup.sound_loops = TRUE;
10046 SetAudioMode(setup.sound);
10050 case SOUND_CTRL_ID_SIMPLE:
10051 if (setup.sound_simple)
10052 setup.sound_simple = FALSE;
10053 else if (audio.sound_available)
10055 setup.sound = setup.sound_simple = TRUE;
10056 SetAudioMode(setup.sound);