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 if (!options.network || player->connected)
611 player->active = TRUE;
613 /* remove potentially duplicate players */
614 if (StorePlayer[jx][jy] == Feld[x][y])
615 StorePlayer[jx][jy] = 0;
617 StorePlayer[x][y] = Feld[x][y];
621 printf("Player %d activated.\n", player->element_nr);
622 printf("[Local player is %d and currently %s.]\n",
623 local_player->element_nr,
624 local_player->active ? "active" : "not active");
628 Feld[x][y] = EL_EMPTY;
629 player->jx = player->last_jx = x;
630 player->jy = player->last_jy = y;
634 static void InitField(int x, int y, boolean init_game)
636 int element = Feld[x][y];
645 InitPlayerField(x, y, element, init_game);
648 case EL_SOKOBAN_FIELD_PLAYER:
649 element = Feld[x][y] = EL_PLAYER_1;
650 InitField(x, y, init_game);
652 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
653 InitField(x, y, init_game);
656 case EL_SOKOBAN_FIELD_EMPTY:
657 local_player->sokobanfields_still_needed++;
661 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
662 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
663 else if (x > 0 && Feld[x-1][y] == EL_ACID)
664 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
665 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
666 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
667 else if (y > 0 && Feld[x][y-1] == EL_ACID)
668 Feld[x][y] = EL_ACID_POOL_BOTTOM;
669 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
670 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
678 case EL_SPACESHIP_RIGHT:
679 case EL_SPACESHIP_UP:
680 case EL_SPACESHIP_LEFT:
681 case EL_SPACESHIP_DOWN:
683 case EL_BD_BUTTERFLY_RIGHT:
684 case EL_BD_BUTTERFLY_UP:
685 case EL_BD_BUTTERFLY_LEFT:
686 case EL_BD_BUTTERFLY_DOWN:
687 case EL_BD_BUTTERFLY:
688 case EL_BD_FIREFLY_RIGHT:
689 case EL_BD_FIREFLY_UP:
690 case EL_BD_FIREFLY_LEFT:
691 case EL_BD_FIREFLY_DOWN:
693 case EL_PACMAN_RIGHT:
717 if (y == lev_fieldy - 1)
719 Feld[x][y] = EL_AMOEBA_GROWING;
720 Store[x][y] = EL_AMOEBA_WET;
724 case EL_DYNAMITE_ACTIVE:
725 case EL_SP_DISK_RED_ACTIVE:
726 case EL_DYNABOMB_PLAYER_1_ACTIVE:
727 case EL_DYNABOMB_PLAYER_2_ACTIVE:
728 case EL_DYNABOMB_PLAYER_3_ACTIVE:
729 case EL_DYNABOMB_PLAYER_4_ACTIVE:
734 local_player->lights_still_needed++;
738 local_player->friends_still_needed++;
743 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
748 Feld[x][y] = EL_EMPTY;
753 case EL_EM_KEY_1_FILE:
754 Feld[x][y] = EL_EM_KEY_1;
756 case EL_EM_KEY_2_FILE:
757 Feld[x][y] = EL_EM_KEY_2;
759 case EL_EM_KEY_3_FILE:
760 Feld[x][y] = EL_EM_KEY_3;
762 case EL_EM_KEY_4_FILE:
763 Feld[x][y] = EL_EM_KEY_4;
767 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
768 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
769 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
770 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
771 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
772 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
773 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
774 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
775 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
776 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
777 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
778 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
781 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
782 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
783 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
785 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
787 game.belt_dir[belt_nr] = belt_dir;
788 game.belt_dir_nr[belt_nr] = belt_dir_nr;
790 else /* more than one switch -- set it like the first switch */
792 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
797 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
799 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
802 case EL_LIGHT_SWITCH_ACTIVE:
804 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
808 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
810 else if (IS_GROUP_ELEMENT(element))
812 struct ElementGroupInfo *group = element_info[element].group;
813 int last_anim_random_frame = gfx.anim_random_frame;
816 if (group->choice_mode == ANIM_RANDOM)
817 gfx.anim_random_frame = RND(group->num_elements_resolved);
819 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
820 group->choice_mode, 0,
823 if (group->choice_mode == ANIM_RANDOM)
824 gfx.anim_random_frame = last_anim_random_frame;
828 Feld[x][y] = group->element_resolved[element_pos];
830 InitField(x, y, init_game);
836 void DrawGameDoorValues()
840 for (i = 0; i < MAX_PLAYERS; i++)
841 for (j = 0; j < 4; j++)
842 if (stored_player[i].key[j])
843 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
844 el2edimg(EL_KEY_1 + j));
846 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
847 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
848 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
849 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
850 DrawText(DX + XX_SCORE, DY + YY_SCORE,
851 int2str(local_player->score, 5), FONT_TEXT_2);
852 DrawText(DX + XX_TIME, DY + YY_TIME,
853 int2str(TimeLeft, 3), FONT_TEXT_2);
856 static void resolve_group_element(int group_element, int recursion_depth)
859 static struct ElementGroupInfo *group;
860 struct ElementGroupInfo *actual_group = element_info[group_element].group;
863 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
865 Error(ERR_WARN, "recursion too deep when resolving group element %d",
866 group_element - EL_GROUP_START + 1);
868 /* replace element which caused too deep recursion by question mark */
869 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
874 if (recursion_depth == 0) /* initialization */
876 group = element_info[group_element].group;
877 group_nr = group_element - EL_GROUP_START;
879 group->num_elements_resolved = 0;
880 group->choice_pos = 0;
883 for (i = 0; i < actual_group->num_elements; i++)
885 int element = actual_group->element[i];
887 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
890 if (IS_GROUP_ELEMENT(element))
891 resolve_group_element(element, recursion_depth + 1);
894 group->element_resolved[group->num_elements_resolved++] = element;
895 element_info[element].in_group[group_nr] = TRUE;
900 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
902 printf("::: group %d: %d resolved elements\n",
903 group_element - EL_GROUP_START, group->num_elements_resolved);
904 for (i = 0; i < group->num_elements_resolved; i++)
905 printf("::: - %d ['%s']\n", group->element_resolved[i],
906 element_info[group->element_resolved[i]].token_name);
913 =============================================================================
915 -----------------------------------------------------------------------------
916 initialize game engine due to level / tape version number
917 =============================================================================
920 static void InitGameEngine()
924 /* set game engine from tape file when re-playing, else from level file */
925 game.engine_version = (tape.playing ? tape.engine_version :
928 /* dynamically adjust element properties according to game engine version */
929 InitElementPropertiesEngine(game.engine_version);
932 printf("level %d: level version == %06d\n", level_nr, level.game_version);
933 printf(" tape version == %06d [%s] [file: %06d]\n",
934 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
936 printf(" => game.engine_version == %06d\n", game.engine_version);
939 /* ---------- recursively resolve group elements ------------------------- */
941 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
942 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
943 element_info[i].in_group[j] = FALSE;
945 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
946 resolve_group_element(EL_GROUP_START + i, 0);
948 /* ---------- initialize player's initial move delay --------------------- */
950 /* dynamically adjust player properties according to game engine version */
951 game.initial_move_delay =
952 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
953 INITIAL_MOVE_DELAY_OFF);
955 /* dynamically adjust player properties according to level information */
956 game.initial_move_delay_value =
957 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
959 /* ---------- initialize player's initial push delay --------------------- */
961 /* dynamically adjust player properties according to game engine version */
962 game.initial_push_delay_value =
963 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
965 /* ---------- initialize changing elements ------------------------------- */
967 /* initialize changing elements information */
968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
970 struct ElementInfo *ei = &element_info[i];
972 /* this pointer might have been changed in the level editor */
973 ei->change = &ei->change_page[0];
975 if (!IS_CUSTOM_ELEMENT(i))
977 ei->change->target_element = EL_EMPTY_SPACE;
978 ei->change->delay_fixed = 0;
979 ei->change->delay_random = 0;
980 ei->change->delay_frames = 1;
983 ei->change_events = CE_BITMASK_DEFAULT;
984 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
986 ei->event_page_nr[j] = 0;
987 ei->event_page[j] = &ei->change_page[0];
991 /* add changing elements from pre-defined list */
992 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
994 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
995 struct ElementInfo *ei = &element_info[ch_delay->element];
997 ei->change->target_element = ch_delay->target_element;
998 ei->change->delay_fixed = ch_delay->change_delay;
1000 ei->change->pre_change_function = ch_delay->pre_change_function;
1001 ei->change->change_function = ch_delay->change_function;
1002 ei->change->post_change_function = ch_delay->post_change_function;
1004 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1008 /* add change events from custom element configuration */
1009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1011 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1013 for (j = 0; j < ei->num_change_pages; j++)
1015 if (!ei->change_page[j].can_change)
1018 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1020 /* only add event page for the first page found with this event */
1021 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1022 !(ei->change_events & CH_EVENT_BIT(k)))
1024 ei->change_events |= CH_EVENT_BIT(k);
1025 ei->event_page_nr[k] = j;
1026 ei->event_page[k] = &ei->change_page[j];
1034 /* add change events from custom element configuration */
1035 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1037 int element = EL_CUSTOM_START + i;
1039 /* only add custom elements that change after fixed/random frame delay */
1040 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1041 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1045 /* ---------- initialize trigger events ---------------------------------- */
1047 /* initialize trigger events information */
1048 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1049 trigger_events[i] = EP_BITMASK_DEFAULT;
1052 /* add trigger events from element change event properties */
1053 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1055 struct ElementInfo *ei = &element_info[i];
1057 for (j = 0; j < ei->num_change_pages; j++)
1059 if (!ei->change_page[j].can_change)
1062 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1064 int trigger_element = ei->change_page[j].trigger_element;
1066 if (IS_GROUP_ELEMENT(trigger_element))
1068 struct ElementGroupInfo *group = element_info[trigger_element].group;
1070 for (k = 0; k < group->num_elements_resolved; k++)
1071 trigger_events[group->element_resolved[k]]
1072 |= ei->change_page[j].events;
1075 trigger_events[trigger_element] |= ei->change_page[j].events;
1080 /* add trigger events from element change event properties */
1081 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1082 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1083 trigger_events[element_info[i].change->trigger_element] |=
1084 element_info[i].change->events;
1087 /* ---------- initialize push delay -------------------------------------- */
1089 /* initialize push delay values to default */
1090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1092 if (!IS_CUSTOM_ELEMENT(i))
1094 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1095 element_info[i].push_delay_random = game.default_push_delay_random;
1099 /* set push delay value for certain elements from pre-defined list */
1100 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1102 int e = push_delay_list[i].element;
1104 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1105 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1108 /* ---------- initialize move stepsize ----------------------------------- */
1110 /* initialize move stepsize values to default */
1111 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1112 if (!IS_CUSTOM_ELEMENT(i))
1113 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1115 /* set move stepsize value for certain elements from pre-defined list */
1116 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1118 int e = move_stepsize_list[i].element;
1120 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1123 /* ---------- initialize move dig/leave ---------------------------------- */
1125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1127 element_info[i].can_leave_element = FALSE;
1128 element_info[i].can_leave_element_last = FALSE;
1131 /* ---------- initialize gem count --------------------------------------- */
1133 /* initialize gem count values for each element */
1134 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1135 if (!IS_CUSTOM_ELEMENT(i))
1136 element_info[i].collect_count = 0;
1138 /* add gem count values for all elements from pre-defined list */
1139 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1140 element_info[collect_count_list[i].element].collect_count =
1141 collect_count_list[i].count;
1146 =============================================================================
1148 -----------------------------------------------------------------------------
1149 initialize and start new game
1150 =============================================================================
1155 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1156 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1157 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1164 #if USE_NEW_AMOEBA_CODE
1165 printf("Using new amoeba code.\n");
1167 printf("Using old amoeba code.\n");
1172 /* don't play tapes over network */
1173 network_playing = (options.network && !tape.playing);
1175 for (i = 0; i < MAX_PLAYERS; i++)
1177 struct PlayerInfo *player = &stored_player[i];
1179 player->index_nr = i;
1180 player->element_nr = EL_PLAYER_1 + i;
1182 player->present = FALSE;
1183 player->active = FALSE;
1186 player->effective_action = 0;
1187 player->programmed_action = 0;
1190 player->gems_still_needed = level.gems_needed;
1191 player->sokobanfields_still_needed = 0;
1192 player->lights_still_needed = 0;
1193 player->friends_still_needed = 0;
1195 for (j = 0; j < 4; j++)
1196 player->key[j] = FALSE;
1198 player->dynabomb_count = 0;
1199 player->dynabomb_size = 1;
1200 player->dynabombs_left = 0;
1201 player->dynabomb_xl = FALSE;
1203 player->MovDir = MV_NO_MOVING;
1206 player->GfxDir = MV_NO_MOVING;
1207 player->GfxAction = ACTION_DEFAULT;
1209 player->StepFrame = 0;
1211 player->use_murphy_graphic = FALSE;
1213 player->actual_frame_counter = 0;
1215 player->step_counter = 0;
1217 player->last_move_dir = MV_NO_MOVING;
1219 player->is_waiting = FALSE;
1220 player->is_moving = FALSE;
1221 player->is_digging = FALSE;
1222 player->is_snapping = FALSE;
1223 player->is_collecting = FALSE;
1224 player->is_pushing = FALSE;
1225 player->is_switching = FALSE;
1226 player->is_dropping = FALSE;
1228 player->is_bored = FALSE;
1229 player->is_sleeping = FALSE;
1231 player->frame_counter_bored = -1;
1232 player->frame_counter_sleeping = -1;
1234 player->anim_delay_counter = 0;
1235 player->post_delay_counter = 0;
1237 player->action_waiting = ACTION_DEFAULT;
1238 player->last_action_waiting = ACTION_DEFAULT;
1239 player->special_action_bored = ACTION_DEFAULT;
1240 player->special_action_sleeping = ACTION_DEFAULT;
1242 player->num_special_action_bored = 0;
1243 player->num_special_action_sleeping = 0;
1245 /* determine number of special actions for bored and sleeping animation */
1246 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1248 boolean found = FALSE;
1250 for (k = 0; k < NUM_DIRECTIONS; k++)
1251 if (el_act_dir2img(player->element_nr, j, k) !=
1252 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1256 player->num_special_action_bored++;
1260 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1262 boolean found = FALSE;
1264 for (k = 0; k < NUM_DIRECTIONS; k++)
1265 if (el_act_dir2img(player->element_nr, j, k) !=
1266 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1270 player->num_special_action_sleeping++;
1275 player->switch_x = -1;
1276 player->switch_y = -1;
1278 player->show_envelope = 0;
1280 player->move_delay = game.initial_move_delay;
1281 player->move_delay_value = game.initial_move_delay_value;
1283 player->move_delay_reset_counter = 0;
1285 player->push_delay = 0;
1286 player->push_delay_value = game.initial_push_delay_value;
1288 player->drop_delay = 0;
1290 player->last_jx = player->last_jy = 0;
1291 player->jx = player->jy = 0;
1293 player->shield_normal_time_left = 0;
1294 player->shield_deadly_time_left = 0;
1296 player->inventory_size = 0;
1298 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1299 SnapField(player, 0, 0);
1301 player->LevelSolved = FALSE;
1302 player->GameOver = FALSE;
1305 network_player_action_received = FALSE;
1307 #if defined(PLATFORM_UNIX)
1308 /* initial null action */
1309 if (network_playing)
1310 SendToServer_MovePlayer(MV_NO_MOVING);
1318 TimeLeft = level.time;
1320 ScreenMovDir = MV_NO_MOVING;
1324 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1326 AllPlayersGone = FALSE;
1328 game.yamyam_content_nr = 0;
1329 game.magic_wall_active = FALSE;
1330 game.magic_wall_time_left = 0;
1331 game.light_time_left = 0;
1332 game.timegate_time_left = 0;
1333 game.switchgate_pos = 0;
1334 game.balloon_dir = MV_NO_MOVING;
1335 game.gravity = level.initial_gravity;
1336 game.explosions_delayed = TRUE;
1338 game.envelope_active = FALSE;
1340 for (i = 0; i < 4; i++)
1342 game.belt_dir[i] = MV_NO_MOVING;
1343 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1346 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1347 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1349 for (x = 0; x < lev_fieldx; x++)
1351 for (y = 0; y < lev_fieldy; y++)
1353 Feld[x][y] = level.field[x][y];
1354 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1355 ChangeDelay[x][y] = 0;
1356 ChangePage[x][y] = -1;
1357 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1359 WasJustMoving[x][y] = 0;
1360 WasJustFalling[x][y] = 0;
1362 Pushed[x][y] = FALSE;
1364 Changed[x][y] = CE_BITMASK_DEFAULT;
1365 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1367 ExplodePhase[x][y] = 0;
1368 ExplodeField[x][y] = EX_NO_EXPLOSION;
1370 RunnerVisit[x][y] = 0;
1371 PlayerVisit[x][y] = 0;
1374 GfxRandom[x][y] = INIT_GFX_RANDOM();
1375 GfxElement[x][y] = EL_UNDEFINED;
1376 GfxAction[x][y] = ACTION_DEFAULT;
1377 GfxDir[x][y] = MV_NO_MOVING;
1381 for (y = 0; y < lev_fieldy; y++)
1383 for (x = 0; x < lev_fieldx; x++)
1385 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1387 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1389 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1392 InitField(x, y, TRUE);
1398 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1399 emulate_sb ? EMU_SOKOBAN :
1400 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1402 /* correct non-moving belts to start moving left */
1403 for (i = 0; i < 4; i++)
1404 if (game.belt_dir[i] == MV_NO_MOVING)
1405 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1407 /* check if any connected player was not found in playfield */
1408 for (i = 0; i < MAX_PLAYERS; i++)
1410 struct PlayerInfo *player = &stored_player[i];
1412 if (player->connected && !player->present)
1414 for (j = 0; j < MAX_PLAYERS; j++)
1416 struct PlayerInfo *some_player = &stored_player[j];
1417 int jx = some_player->jx, jy = some_player->jy;
1419 /* assign first free player found that is present in the playfield */
1420 if (some_player->present && !some_player->connected)
1422 player->present = TRUE;
1423 player->active = TRUE;
1425 some_player->present = FALSE;
1426 some_player->active = FALSE;
1428 StorePlayer[jx][jy] = player->element_nr;
1429 player->jx = player->last_jx = jx;
1430 player->jy = player->last_jy = jy;
1440 /* when playing a tape, eliminate all players which do not participate */
1442 for (i = 0; i < MAX_PLAYERS; i++)
1444 if (stored_player[i].active && !tape.player_participates[i])
1446 struct PlayerInfo *player = &stored_player[i];
1447 int jx = player->jx, jy = player->jy;
1449 player->active = FALSE;
1450 StorePlayer[jx][jy] = 0;
1451 Feld[jx][jy] = EL_EMPTY;
1455 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1457 /* when in single player mode, eliminate all but the first active player */
1459 for (i = 0; i < MAX_PLAYERS; i++)
1461 if (stored_player[i].active)
1463 for (j = i + 1; j < MAX_PLAYERS; j++)
1465 if (stored_player[j].active)
1467 struct PlayerInfo *player = &stored_player[j];
1468 int jx = player->jx, jy = player->jy;
1470 player->active = FALSE;
1471 player->present = FALSE;
1473 StorePlayer[jx][jy] = 0;
1474 Feld[jx][jy] = EL_EMPTY;
1481 /* when recording the game, store which players take part in the game */
1484 for (i = 0; i < MAX_PLAYERS; i++)
1485 if (stored_player[i].active)
1486 tape.player_participates[i] = TRUE;
1491 for (i = 0; i < MAX_PLAYERS; i++)
1493 struct PlayerInfo *player = &stored_player[i];
1495 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1500 if (local_player == player)
1501 printf("Player %d is local player.\n", i+1);
1505 if (BorderElement == EL_EMPTY)
1508 SBX_Right = lev_fieldx - SCR_FIELDX;
1510 SBY_Lower = lev_fieldy - SCR_FIELDY;
1515 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1517 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1520 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1521 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1523 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1524 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1526 /* if local player not found, look for custom element that might create
1527 the player (make some assumptions about the right custom element) */
1528 if (!local_player->present)
1530 int start_x = 0, start_y = 0;
1531 int found_rating = 0;
1532 int found_element = EL_UNDEFINED;
1534 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1536 int element = Feld[x][y];
1541 if (!IS_CUSTOM_ELEMENT(element))
1544 if (CAN_CHANGE(element))
1546 for (i = 0; i < element_info[element].num_change_pages; i++)
1548 content = element_info[element].change_page[i].target_element;
1549 is_player = ELEM_IS_PLAYER(content);
1551 if (is_player && (found_rating < 3 || element < found_element))
1557 found_element = element;
1562 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1564 content = element_info[element].content[xx][yy];
1565 is_player = ELEM_IS_PLAYER(content);
1567 if (is_player && (found_rating < 2 || element < found_element))
1569 start_x = x + xx - 1;
1570 start_y = y + yy - 1;
1573 found_element = element;
1576 if (!CAN_CHANGE(element))
1579 for (i = 0; i < element_info[element].num_change_pages; i++)
1581 content = element_info[element].change_page[i].content[xx][yy];
1582 is_player = ELEM_IS_PLAYER(content);
1584 if (is_player && (found_rating < 1 || element < found_element))
1586 start_x = x + xx - 1;
1587 start_y = y + yy - 1;
1590 found_element = element;
1596 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1597 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1600 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1601 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1607 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1608 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1609 local_player->jx - MIDPOSX);
1611 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1612 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1613 local_player->jy - MIDPOSY);
1615 scroll_x = SBX_Left;
1616 scroll_y = SBY_Upper;
1617 if (local_player->jx >= SBX_Left + MIDPOSX)
1618 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1619 local_player->jx - MIDPOSX :
1621 if (local_player->jy >= SBY_Upper + MIDPOSY)
1622 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1623 local_player->jy - MIDPOSY :
1628 CloseDoor(DOOR_CLOSE_1);
1633 /* after drawing the level, correct some elements */
1634 if (game.timegate_time_left == 0)
1635 CloseAllOpenTimegates();
1637 if (setup.soft_scrolling)
1638 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1640 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1643 /* copy default game door content to main double buffer */
1644 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1645 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1648 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1651 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1652 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1653 BlitBitmap(drawto, drawto,
1654 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1655 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1656 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1657 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1660 DrawGameDoorValues();
1664 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1665 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1666 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1670 /* copy actual game door content to door double buffer for OpenDoor() */
1671 BlitBitmap(drawto, bitmap_db_door,
1672 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1674 OpenDoor(DOOR_OPEN_ALL);
1676 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1678 if (setup.sound_music)
1681 KeyboardAutoRepeatOffUnlessAutoplay();
1685 for (i = 0; i < 4; i++)
1686 printf("Player %d %sactive.\n",
1687 i + 1, (stored_player[i].active ? "" : "not "));
1691 printf("::: starting game [%d]\n", FrameCounter);
1695 void InitMovDir(int x, int y)
1697 int i, element = Feld[x][y];
1698 static int xy[4][2] =
1705 static int direction[3][4] =
1707 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1708 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1709 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1718 Feld[x][y] = EL_BUG;
1719 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1722 case EL_SPACESHIP_RIGHT:
1723 case EL_SPACESHIP_UP:
1724 case EL_SPACESHIP_LEFT:
1725 case EL_SPACESHIP_DOWN:
1726 Feld[x][y] = EL_SPACESHIP;
1727 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1730 case EL_BD_BUTTERFLY_RIGHT:
1731 case EL_BD_BUTTERFLY_UP:
1732 case EL_BD_BUTTERFLY_LEFT:
1733 case EL_BD_BUTTERFLY_DOWN:
1734 Feld[x][y] = EL_BD_BUTTERFLY;
1735 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1738 case EL_BD_FIREFLY_RIGHT:
1739 case EL_BD_FIREFLY_UP:
1740 case EL_BD_FIREFLY_LEFT:
1741 case EL_BD_FIREFLY_DOWN:
1742 Feld[x][y] = EL_BD_FIREFLY;
1743 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1746 case EL_PACMAN_RIGHT:
1748 case EL_PACMAN_LEFT:
1749 case EL_PACMAN_DOWN:
1750 Feld[x][y] = EL_PACMAN;
1751 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1754 case EL_SP_SNIKSNAK:
1755 MovDir[x][y] = MV_UP;
1758 case EL_SP_ELECTRON:
1759 MovDir[x][y] = MV_LEFT;
1766 Feld[x][y] = EL_MOLE;
1767 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1771 if (IS_CUSTOM_ELEMENT(element))
1773 struct ElementInfo *ei = &element_info[element];
1774 int move_direction_initial = ei->move_direction_initial;
1775 int move_pattern = ei->move_pattern;
1777 if (move_direction_initial == MV_PREVIOUS)
1779 if (MovDir[x][y] != MV_NO_MOVING)
1782 move_direction_initial = MV_AUTOMATIC;
1785 if (move_direction_initial == MV_RANDOM)
1786 MovDir[x][y] = 1 << RND(4);
1787 else if (move_direction_initial & MV_ANY_DIRECTION)
1788 MovDir[x][y] = move_direction_initial;
1789 else if (move_pattern == MV_ALL_DIRECTIONS ||
1790 move_pattern == MV_TURNING_LEFT ||
1791 move_pattern == MV_TURNING_RIGHT ||
1792 move_pattern == MV_TURNING_LEFT_RIGHT ||
1793 move_pattern == MV_TURNING_RIGHT_LEFT ||
1794 move_pattern == MV_TURNING_RANDOM)
1795 MovDir[x][y] = 1 << RND(4);
1796 else if (move_pattern == MV_HORIZONTAL)
1797 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1798 else if (move_pattern == MV_VERTICAL)
1799 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1800 else if (move_pattern & MV_ANY_DIRECTION)
1801 MovDir[x][y] = element_info[element].move_pattern;
1802 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1803 move_pattern == MV_ALONG_RIGHT_SIDE)
1805 for (i = 0; i < 4; i++)
1807 int x1 = x + xy[i][0];
1808 int y1 = y + xy[i][1];
1810 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1812 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1813 MovDir[x][y] = direction[0][i];
1815 MovDir[x][y] = direction[1][i];
1824 MovDir[x][y] = 1 << RND(4);
1826 if (element != EL_BUG &&
1827 element != EL_SPACESHIP &&
1828 element != EL_BD_BUTTERFLY &&
1829 element != EL_BD_FIREFLY)
1832 for (i = 0; i < 4; i++)
1834 int x1 = x + xy[i][0];
1835 int y1 = y + xy[i][1];
1837 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1839 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1841 MovDir[x][y] = direction[0][i];
1844 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1845 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1847 MovDir[x][y] = direction[1][i];
1856 GfxDir[x][y] = MovDir[x][y];
1859 void InitAmoebaNr(int x, int y)
1862 int group_nr = AmoebeNachbarNr(x, y);
1866 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1868 if (AmoebaCnt[i] == 0)
1876 AmoebaNr[x][y] = group_nr;
1877 AmoebaCnt[group_nr]++;
1878 AmoebaCnt2[group_nr]++;
1884 boolean raise_level = FALSE;
1886 if (local_player->MovPos)
1890 if (tape.auto_play) /* tape might already be stopped here */
1891 tape.auto_play_level_solved = TRUE;
1893 if (tape.playing && tape.auto_play)
1894 tape.auto_play_level_solved = TRUE;
1897 local_player->LevelSolved = FALSE;
1899 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1903 if (!tape.playing && setup.sound_loops)
1904 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1905 SND_CTRL_PLAY_LOOP);
1907 while (TimeLeft > 0)
1909 if (!tape.playing && !setup.sound_loops)
1910 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1911 if (TimeLeft > 0 && !(TimeLeft % 10))
1912 RaiseScore(level.score[SC_TIME_BONUS]);
1913 if (TimeLeft > 100 && !(TimeLeft % 10))
1917 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1924 if (!tape.playing && setup.sound_loops)
1925 StopSound(SND_GAME_LEVELTIME_BONUS);
1927 else if (level.time == 0) /* level without time limit */
1929 if (!tape.playing && setup.sound_loops)
1930 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1931 SND_CTRL_PLAY_LOOP);
1933 while (TimePlayed < 999)
1935 if (!tape.playing && !setup.sound_loops)
1936 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1937 if (TimePlayed < 999 && !(TimePlayed % 10))
1938 RaiseScore(level.score[SC_TIME_BONUS]);
1939 if (TimePlayed < 900 && !(TimePlayed % 10))
1943 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1950 if (!tape.playing && setup.sound_loops)
1951 StopSound(SND_GAME_LEVELTIME_BONUS);
1954 /* close exit door after last player */
1955 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1956 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1958 int element = Feld[ExitX][ExitY];
1960 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1961 EL_SP_EXIT_CLOSING);
1963 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1966 /* Hero disappears */
1967 DrawLevelField(ExitX, ExitY);
1973 CloseDoor(DOOR_CLOSE_1);
1978 SaveTape(tape.level_nr); /* Ask to save tape */
1981 if (level_nr == leveldir_current->handicap_level)
1983 leveldir_current->handicap_level++;
1984 SaveLevelSetup_SeriesInfo();
1987 if (level_editor_test_game)
1988 local_player->score = -1; /* no highscore when playing from editor */
1989 else if (level_nr < leveldir_current->last_level)
1990 raise_level = TRUE; /* advance to next level */
1992 if ((hi_pos = NewHiScore()) >= 0)
1994 game_status = GAME_MODE_SCORES;
1995 DrawHallOfFame(hi_pos);
2004 game_status = GAME_MODE_MAIN;
2021 LoadScore(level_nr);
2023 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2024 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2027 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2029 if (local_player->score > highscore[k].Score)
2031 /* player has made it to the hall of fame */
2033 if (k < MAX_SCORE_ENTRIES - 1)
2035 int m = MAX_SCORE_ENTRIES - 1;
2038 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2039 if (!strcmp(setup.player_name, highscore[l].Name))
2041 if (m == k) /* player's new highscore overwrites his old one */
2045 for (l = m; l > k; l--)
2047 strcpy(highscore[l].Name, highscore[l - 1].Name);
2048 highscore[l].Score = highscore[l - 1].Score;
2055 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2056 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2057 highscore[k].Score = local_player->score;
2063 else if (!strncmp(setup.player_name, highscore[k].Name,
2064 MAX_PLAYER_NAME_LEN))
2065 break; /* player already there with a higher score */
2071 SaveScore(level_nr);
2076 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2078 if (player->GfxAction != action || player->GfxDir != dir)
2081 printf("Player frame reset! (%d => %d, %d => %d)\n",
2082 player->GfxAction, action, player->GfxDir, dir);
2085 player->GfxAction = action;
2086 player->GfxDir = dir;
2088 player->StepFrame = 0;
2092 static void ResetRandomAnimationValue(int x, int y)
2094 GfxRandom[x][y] = INIT_GFX_RANDOM();
2097 static void ResetGfxAnimation(int x, int y)
2100 GfxAction[x][y] = ACTION_DEFAULT;
2101 GfxDir[x][y] = MovDir[x][y];
2104 void InitMovingField(int x, int y, int direction)
2106 int element = Feld[x][y];
2107 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2108 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2112 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2113 ResetGfxAnimation(x, y);
2115 MovDir[newx][newy] = MovDir[x][y] = direction;
2116 GfxDir[x][y] = direction;
2118 if (Feld[newx][newy] == EL_EMPTY)
2119 Feld[newx][newy] = EL_BLOCKED;
2121 if (direction == MV_DOWN && CAN_FALL(element))
2122 GfxAction[x][y] = ACTION_FALLING;
2124 GfxAction[x][y] = ACTION_MOVING;
2126 GfxFrame[newx][newy] = GfxFrame[x][y];
2127 GfxRandom[newx][newy] = GfxRandom[x][y];
2128 GfxAction[newx][newy] = GfxAction[x][y];
2129 GfxDir[newx][newy] = GfxDir[x][y];
2132 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2134 int direction = MovDir[x][y];
2135 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2136 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2142 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2144 int oldx = x, oldy = y;
2145 int direction = MovDir[x][y];
2147 if (direction == MV_LEFT)
2149 else if (direction == MV_RIGHT)
2151 else if (direction == MV_UP)
2153 else if (direction == MV_DOWN)
2156 *comes_from_x = oldx;
2157 *comes_from_y = oldy;
2160 int MovingOrBlocked2Element(int x, int y)
2162 int element = Feld[x][y];
2164 if (element == EL_BLOCKED)
2168 Blocked2Moving(x, y, &oldx, &oldy);
2169 return Feld[oldx][oldy];
2175 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2177 /* like MovingOrBlocked2Element(), but if element is moving
2178 and (x,y) is the field the moving element is just leaving,
2179 return EL_BLOCKED instead of the element value */
2180 int element = Feld[x][y];
2182 if (IS_MOVING(x, y))
2184 if (element == EL_BLOCKED)
2188 Blocked2Moving(x, y, &oldx, &oldy);
2189 return Feld[oldx][oldy];
2198 static void RemoveField(int x, int y)
2200 Feld[x][y] = EL_EMPTY;
2207 ChangeDelay[x][y] = 0;
2208 ChangePage[x][y] = -1;
2209 Pushed[x][y] = FALSE;
2211 GfxElement[x][y] = EL_UNDEFINED;
2212 GfxAction[x][y] = ACTION_DEFAULT;
2213 GfxDir[x][y] = MV_NO_MOVING;
2216 void RemoveMovingField(int x, int y)
2218 int oldx = x, oldy = y, newx = x, newy = y;
2219 int element = Feld[x][y];
2220 int next_element = EL_UNDEFINED;
2222 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2225 if (IS_MOVING(x, y))
2227 Moving2Blocked(x, y, &newx, &newy);
2228 if (Feld[newx][newy] != EL_BLOCKED)
2231 else if (element == EL_BLOCKED)
2233 Blocked2Moving(x, y, &oldx, &oldy);
2234 if (!IS_MOVING(oldx, oldy))
2238 if (element == EL_BLOCKED &&
2239 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2240 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2241 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2242 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2243 next_element = get_next_element(Feld[oldx][oldy]);
2245 RemoveField(oldx, oldy);
2246 RemoveField(newx, newy);
2248 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2250 if (next_element != EL_UNDEFINED)
2251 Feld[oldx][oldy] = next_element;
2253 DrawLevelField(oldx, oldy);
2254 DrawLevelField(newx, newy);
2257 void DrawDynamite(int x, int y)
2259 int sx = SCREENX(x), sy = SCREENY(y);
2260 int graphic = el2img(Feld[x][y]);
2263 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2266 if (IS_WALKABLE_INSIDE(Back[x][y]))
2270 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2271 else if (Store[x][y])
2272 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2274 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2277 if (Back[x][y] || Store[x][y])
2278 DrawGraphicThruMask(sx, sy, graphic, frame);
2280 DrawGraphic(sx, sy, graphic, frame);
2282 if (game.emulation == EMU_SUPAPLEX)
2283 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2284 else if (Store[x][y])
2285 DrawGraphicThruMask(sx, sy, graphic, frame);
2287 DrawGraphic(sx, sy, graphic, frame);
2291 void CheckDynamite(int x, int y)
2293 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2297 if (MovDelay[x][y] != 0)
2300 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2307 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2309 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2310 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2311 StopSound(SND_DYNAMITE_ACTIVE);
2313 StopSound(SND_DYNABOMB_ACTIVE);
2319 void RelocatePlayer(int x, int y, int element)
2321 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2323 if (player->GameOver) /* do not reanimate dead player */
2327 RemoveField(x, y); /* temporarily remove newly placed player */
2328 DrawLevelField(x, y);
2331 if (player->present)
2333 while (player->MovPos)
2335 ScrollPlayer(player, SCROLL_GO_ON);
2336 ScrollScreen(NULL, SCROLL_GO_ON);
2342 Delay(GAME_FRAME_DELAY);
2345 DrawPlayer(player); /* needed here only to cleanup last field */
2346 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2348 player->is_moving = FALSE;
2351 Feld[x][y] = element;
2352 InitPlayerField(x, y, element, TRUE);
2354 if (player == local_player)
2356 int scroll_xx = -999, scroll_yy = -999;
2358 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2361 int fx = FX, fy = FY;
2363 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2364 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2365 local_player->jx - MIDPOSX);
2367 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2368 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2369 local_player->jy - MIDPOSY);
2371 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2372 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2377 fx += dx * TILEX / 2;
2378 fy += dy * TILEY / 2;
2380 ScrollLevel(dx, dy);
2383 /* scroll in two steps of half tile size to make things smoother */
2384 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2386 Delay(GAME_FRAME_DELAY);
2388 /* scroll second step to align at full tile size */
2390 Delay(GAME_FRAME_DELAY);
2395 void Explode(int ex, int ey, int phase, int mode)
2399 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2400 int last_phase = num_phase * delay;
2401 int half_phase = (num_phase / 2) * delay;
2402 int first_phase_after_start = EX_PHASE_START + 1;
2404 if (game.explosions_delayed)
2406 ExplodeField[ex][ey] = mode;
2410 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2412 int center_element = Feld[ex][ey];
2415 /* --- This is only really needed (and now handled) in "Impact()". --- */
2416 /* do not explode moving elements that left the explode field in time */
2417 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2418 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2422 if (mode == EX_NORMAL || mode == EX_CENTER)
2423 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2425 /* remove things displayed in background while burning dynamite */
2426 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2429 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2431 /* put moving element to center field (and let it explode there) */
2432 center_element = MovingOrBlocked2Element(ex, ey);
2433 RemoveMovingField(ex, ey);
2434 Feld[ex][ey] = center_element;
2437 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2439 int xx = x - ex + 1;
2440 int yy = y - ey + 1;
2443 if (!IN_LEV_FIELD(x, y) ||
2444 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2445 (x != ex || y != ey)))
2448 element = Feld[x][y];
2450 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2452 element = MovingOrBlocked2Element(x, y);
2454 if (!IS_EXPLOSION_PROOF(element))
2455 RemoveMovingField(x, y);
2461 if (IS_EXPLOSION_PROOF(element))
2464 /* indestructible elements can only explode in center (but not flames) */
2465 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2466 element == EL_FLAMES)
2471 if ((IS_INDESTRUCTIBLE(element) &&
2472 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2473 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2474 element == EL_FLAMES)
2478 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2480 if (IS_ACTIVE_BOMB(element))
2482 /* re-activate things under the bomb like gate or penguin */
2483 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2490 /* save walkable background elements while explosion on same tile */
2492 if (IS_INDESTRUCTIBLE(element))
2493 Back[x][y] = element;
2495 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2496 Back[x][y] = element;
2499 /* ignite explodable elements reached by other explosion */
2500 if (element == EL_EXPLOSION)
2501 element = Store2[x][y];
2504 if (AmoebaNr[x][y] &&
2505 (element == EL_AMOEBA_FULL ||
2506 element == EL_BD_AMOEBA ||
2507 element == EL_AMOEBA_GROWING))
2509 AmoebaCnt[AmoebaNr[x][y]]--;
2510 AmoebaCnt2[AmoebaNr[x][y]]--;
2516 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2518 switch(StorePlayer[ex][ey])
2521 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2524 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2527 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2531 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2535 if (game.emulation == EMU_SUPAPLEX)
2536 Store[x][y] = EL_EMPTY;
2538 else if (center_element == EL_MOLE)
2539 Store[x][y] = EL_EMERALD_RED;
2540 else if (center_element == EL_PENGUIN)
2541 Store[x][y] = EL_EMERALD_PURPLE;
2542 else if (center_element == EL_BUG)
2543 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2544 else if (center_element == EL_BD_BUTTERFLY)
2545 Store[x][y] = EL_BD_DIAMOND;
2546 else if (center_element == EL_SP_ELECTRON)
2547 Store[x][y] = EL_SP_INFOTRON;
2548 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2549 Store[x][y] = level.amoeba_content;
2550 else if (center_element == EL_YAMYAM)
2551 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2552 else if (IS_CUSTOM_ELEMENT(center_element) &&
2553 element_info[center_element].content[xx][yy] != EL_EMPTY)
2554 Store[x][y] = element_info[center_element].content[xx][yy];
2555 else if (element == EL_WALL_EMERALD)
2556 Store[x][y] = EL_EMERALD;
2557 else if (element == EL_WALL_DIAMOND)
2558 Store[x][y] = EL_DIAMOND;
2559 else if (element == EL_WALL_BD_DIAMOND)
2560 Store[x][y] = EL_BD_DIAMOND;
2561 else if (element == EL_WALL_EMERALD_YELLOW)
2562 Store[x][y] = EL_EMERALD_YELLOW;
2563 else if (element == EL_WALL_EMERALD_RED)
2564 Store[x][y] = EL_EMERALD_RED;
2565 else if (element == EL_WALL_EMERALD_PURPLE)
2566 Store[x][y] = EL_EMERALD_PURPLE;
2567 else if (element == EL_WALL_PEARL)
2568 Store[x][y] = EL_PEARL;
2569 else if (element == EL_WALL_CRYSTAL)
2570 Store[x][y] = EL_CRYSTAL;
2571 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2572 Store[x][y] = element_info[element].content[1][1];
2574 Store[x][y] = EL_EMPTY;
2576 if (x != ex || y != ey ||
2577 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2578 Store2[x][y] = element;
2581 if (AmoebaNr[x][y] &&
2582 (element == EL_AMOEBA_FULL ||
2583 element == EL_BD_AMOEBA ||
2584 element == EL_AMOEBA_GROWING))
2586 AmoebaCnt[AmoebaNr[x][y]]--;
2587 AmoebaCnt2[AmoebaNr[x][y]]--;
2593 MovDir[x][y] = MovPos[x][y] = 0;
2594 GfxDir[x][y] = MovDir[x][y];
2599 Feld[x][y] = EL_EXPLOSION;
2601 GfxElement[x][y] = center_element;
2603 GfxElement[x][y] = EL_UNDEFINED;
2606 ExplodePhase[x][y] = 1;
2610 if (center_element == EL_YAMYAM)
2611 game.yamyam_content_nr =
2612 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2623 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2627 /* activate this even in non-DEBUG version until cause for crash in
2628 getGraphicAnimationFrame() (see below) is found and eliminated */
2632 if (GfxElement[x][y] == EL_UNDEFINED)
2635 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2636 printf("Explode(): This should never happen!\n");
2639 GfxElement[x][y] = EL_EMPTY;
2643 if (phase == first_phase_after_start)
2645 int element = Store2[x][y];
2647 if (element == EL_BLACK_ORB)
2649 Feld[x][y] = Store2[x][y];
2654 else if (phase == half_phase)
2656 int element = Store2[x][y];
2658 if (IS_PLAYER(x, y))
2659 KillHeroUnlessProtected(x, y);
2660 else if (CAN_EXPLODE_BY_FIRE(element))
2662 Feld[x][y] = Store2[x][y];
2666 else if (element == EL_AMOEBA_TO_DIAMOND)
2667 AmoebeUmwandeln(x, y);
2670 if (phase == last_phase)
2674 element = Feld[x][y] = Store[x][y];
2675 Store[x][y] = Store2[x][y] = 0;
2676 GfxElement[x][y] = EL_UNDEFINED;
2678 /* player can escape from explosions and might therefore be still alive */
2679 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2680 element <= EL_PLAYER_IS_EXPLODING_4)
2681 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2683 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2684 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2685 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2688 /* restore probably existing indestructible background element */
2689 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2690 element = Feld[x][y] = Back[x][y];
2693 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2694 GfxDir[x][y] = MV_NO_MOVING;
2695 ChangeDelay[x][y] = 0;
2696 ChangePage[x][y] = -1;
2698 InitField(x, y, FALSE);
2700 /* !!! not needed !!! */
2701 if (CAN_MOVE(element))
2704 DrawLevelField(x, y);
2706 TestIfElementTouchesCustomElement(x, y);
2708 if (GFX_CRUMBLED(element))
2709 DrawLevelFieldCrumbledSandNeighbours(x, y);
2711 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2712 StorePlayer[x][y] = 0;
2714 if (ELEM_IS_PLAYER(element))
2715 RelocatePlayer(x, y, element);
2717 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2720 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2722 int stored = Store[x][y];
2723 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2724 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2727 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2730 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2731 element_info[GfxElement[x][y]].token_name,
2736 DrawLevelFieldCrumbledSand(x, y);
2738 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2740 DrawLevelElement(x, y, Back[x][y]);
2741 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2743 else if (IS_WALKABLE_UNDER(Back[x][y]))
2745 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2746 DrawLevelElementThruMask(x, y, Back[x][y]);
2748 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2749 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2753 void DynaExplode(int ex, int ey)
2756 int dynabomb_element = Feld[ex][ey];
2757 int dynabomb_size = 1;
2758 boolean dynabomb_xl = FALSE;
2759 struct PlayerInfo *player;
2760 static int xy[4][2] =
2768 if (IS_ACTIVE_BOMB(dynabomb_element))
2770 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2771 dynabomb_size = player->dynabomb_size;
2772 dynabomb_xl = player->dynabomb_xl;
2773 player->dynabombs_left++;
2776 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2778 for (i = 0; i < 4; i++)
2780 for (j = 1; j <= dynabomb_size; j++)
2782 int x = ex + j * xy[i % 4][0];
2783 int y = ey + j * xy[i % 4][1];
2786 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2789 element = Feld[x][y];
2791 /* do not restart explosions of fields with active bombs */
2792 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2795 Explode(x, y, EX_PHASE_START, EX_BORDER);
2797 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2798 if (element != EL_EMPTY &&
2799 element != EL_SAND &&
2800 element != EL_EXPLOSION &&
2807 void Bang(int x, int y)
2810 int element = MovingOrBlocked2Element(x, y);
2812 int element = Feld[x][y];
2816 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2818 if (IS_PLAYER(x, y))
2821 struct PlayerInfo *player = PLAYERINFO(x, y);
2823 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2824 player->element_nr);
2829 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2831 if (game.emulation == EMU_SUPAPLEX)
2832 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2834 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2839 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2847 case EL_BD_BUTTERFLY:
2850 case EL_DARK_YAMYAM:
2854 RaiseScoreElement(element);
2855 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2857 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2858 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2859 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2860 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2861 case EL_DYNABOMB_INCREASE_NUMBER:
2862 case EL_DYNABOMB_INCREASE_SIZE:
2863 case EL_DYNABOMB_INCREASE_POWER:
2868 case EL_LAMP_ACTIVE:
2869 if (IS_PLAYER(x, y))
2870 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2872 Explode(x, y, EX_PHASE_START, EX_CENTER);
2875 if (CAN_EXPLODE_DYNA(element))
2877 else if (CAN_EXPLODE_1X1(element))
2878 Explode(x, y, EX_PHASE_START, EX_CENTER);
2880 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2884 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2887 void SplashAcid(int x, int y)
2889 int element = Feld[x][y];
2891 if (element != EL_ACID_SPLASH_LEFT &&
2892 element != EL_ACID_SPLASH_RIGHT)
2894 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2896 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2897 (!IN_LEV_FIELD(x-1, y-1) ||
2898 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2899 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2901 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2902 (!IN_LEV_FIELD(x+1, y-1) ||
2903 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2904 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2908 static void InitBeltMovement()
2910 static int belt_base_element[4] =
2912 EL_CONVEYOR_BELT_1_LEFT,
2913 EL_CONVEYOR_BELT_2_LEFT,
2914 EL_CONVEYOR_BELT_3_LEFT,
2915 EL_CONVEYOR_BELT_4_LEFT
2917 static int belt_base_active_element[4] =
2919 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2920 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2921 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2922 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2927 /* set frame order for belt animation graphic according to belt direction */
2928 for (i = 0; i < 4; i++)
2932 for (j = 0; j < 3; j++)
2934 int element = belt_base_active_element[belt_nr] + j;
2935 int graphic = el2img(element);
2937 if (game.belt_dir[i] == MV_LEFT)
2938 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2940 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2944 for (y = 0; y < lev_fieldy; y++)
2946 for (x = 0; x < lev_fieldx; x++)
2948 int element = Feld[x][y];
2950 for (i = 0; i < 4; i++)
2952 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2954 int e_belt_nr = getBeltNrFromBeltElement(element);
2957 if (e_belt_nr == belt_nr)
2959 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2961 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2969 static void ToggleBeltSwitch(int x, int y)
2971 static int belt_base_element[4] =
2973 EL_CONVEYOR_BELT_1_LEFT,
2974 EL_CONVEYOR_BELT_2_LEFT,
2975 EL_CONVEYOR_BELT_3_LEFT,
2976 EL_CONVEYOR_BELT_4_LEFT
2978 static int belt_base_active_element[4] =
2980 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2981 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2982 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2983 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2985 static int belt_base_switch_element[4] =
2987 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2988 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2989 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2990 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2992 static int belt_move_dir[4] =
3000 int element = Feld[x][y];
3001 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3002 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3003 int belt_dir = belt_move_dir[belt_dir_nr];
3006 if (!IS_BELT_SWITCH(element))
3009 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3010 game.belt_dir[belt_nr] = belt_dir;
3012 if (belt_dir_nr == 3)
3015 /* set frame order for belt animation graphic according to belt direction */
3016 for (i = 0; i < 3; i++)
3018 int element = belt_base_active_element[belt_nr] + i;
3019 int graphic = el2img(element);
3021 if (belt_dir == MV_LEFT)
3022 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3024 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3027 for (yy = 0; yy < lev_fieldy; yy++)
3029 for (xx = 0; xx < lev_fieldx; xx++)
3031 int element = Feld[xx][yy];
3033 if (IS_BELT_SWITCH(element))
3035 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3037 if (e_belt_nr == belt_nr)
3039 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3040 DrawLevelField(xx, yy);
3043 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3045 int e_belt_nr = getBeltNrFromBeltElement(element);
3047 if (e_belt_nr == belt_nr)
3049 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3051 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3052 DrawLevelField(xx, yy);
3055 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3057 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3059 if (e_belt_nr == belt_nr)
3061 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3063 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3064 DrawLevelField(xx, yy);
3071 static void ToggleSwitchgateSwitch(int x, int y)
3075 game.switchgate_pos = !game.switchgate_pos;
3077 for (yy = 0; yy < lev_fieldy; yy++)
3079 for (xx = 0; xx < lev_fieldx; xx++)
3081 int element = Feld[xx][yy];
3083 if (element == EL_SWITCHGATE_SWITCH_UP ||
3084 element == EL_SWITCHGATE_SWITCH_DOWN)
3086 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3087 DrawLevelField(xx, yy);
3089 else if (element == EL_SWITCHGATE_OPEN ||
3090 element == EL_SWITCHGATE_OPENING)
3092 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3094 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3096 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3099 else if (element == EL_SWITCHGATE_CLOSED ||
3100 element == EL_SWITCHGATE_CLOSING)
3102 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3104 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3106 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3113 static int getInvisibleActiveFromInvisibleElement(int element)
3115 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3116 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3117 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3121 static int getInvisibleFromInvisibleActiveElement(int element)
3123 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3124 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3125 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3129 static void RedrawAllLightSwitchesAndInvisibleElements()
3133 for (y = 0; y < lev_fieldy; y++)
3135 for (x = 0; x < lev_fieldx; x++)
3137 int element = Feld[x][y];
3139 if (element == EL_LIGHT_SWITCH &&
3140 game.light_time_left > 0)
3142 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3143 DrawLevelField(x, y);
3145 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3146 game.light_time_left == 0)
3148 Feld[x][y] = EL_LIGHT_SWITCH;
3149 DrawLevelField(x, y);
3151 else if (element == EL_INVISIBLE_STEELWALL ||
3152 element == EL_INVISIBLE_WALL ||
3153 element == EL_INVISIBLE_SAND)
3155 if (game.light_time_left > 0)
3156 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3158 DrawLevelField(x, y);
3160 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3161 element == EL_INVISIBLE_WALL_ACTIVE ||
3162 element == EL_INVISIBLE_SAND_ACTIVE)
3164 if (game.light_time_left == 0)
3165 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3167 DrawLevelField(x, y);
3173 static void ToggleLightSwitch(int x, int y)
3175 int element = Feld[x][y];
3177 game.light_time_left =
3178 (element == EL_LIGHT_SWITCH ?
3179 level.time_light * FRAMES_PER_SECOND : 0);
3181 RedrawAllLightSwitchesAndInvisibleElements();
3184 static void ActivateTimegateSwitch(int x, int y)
3188 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3190 for (yy = 0; yy < lev_fieldy; yy++)
3192 for (xx = 0; xx < lev_fieldx; xx++)
3194 int element = Feld[xx][yy];
3196 if (element == EL_TIMEGATE_CLOSED ||
3197 element == EL_TIMEGATE_CLOSING)
3199 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3200 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3204 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3206 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3207 DrawLevelField(xx, yy);
3214 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3217 inline static int getElementMoveStepsize(int x, int y)
3219 int element = Feld[x][y];
3220 int direction = MovDir[x][y];
3221 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3222 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3223 int horiz_move = (dx != 0);
3224 int sign = (horiz_move ? dx : dy);
3225 int step = sign * element_info[element].move_stepsize;
3227 /* special values for move stepsize for spring and things on conveyor belt */
3230 if (CAN_FALL(element) &&
3231 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3232 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3233 else if (element == EL_SPRING)
3234 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3240 void Impact(int x, int y)
3242 boolean lastline = (y == lev_fieldy-1);
3243 boolean object_hit = FALSE;
3244 boolean impact = (lastline || object_hit);
3245 int element = Feld[x][y];
3246 int smashed = EL_UNDEFINED;
3248 if (!lastline) /* check if element below was hit */
3250 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3253 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3254 MovDir[x][y + 1] != MV_DOWN ||
3255 MovPos[x][y + 1] <= TILEY / 2));
3258 object_hit = !IS_FREE(x, y + 1);
3261 /* do not smash moving elements that left the smashed field in time */
3262 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3263 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3267 smashed = MovingOrBlocked2Element(x, y + 1);
3269 impact = (lastline || object_hit);
3272 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3278 /* only reset graphic animation if graphic really changes after impact */
3280 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3282 ResetGfxAnimation(x, y);
3283 DrawLevelField(x, y);
3286 if (impact && CAN_EXPLODE_IMPACT(element))
3291 else if (impact && element == EL_PEARL)
3293 Feld[x][y] = EL_PEARL_BREAKING;
3294 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3297 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3299 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3304 if (impact && element == EL_AMOEBA_DROP)
3306 if (object_hit && IS_PLAYER(x, y + 1))
3307 KillHeroUnlessProtected(x, y + 1);
3308 else if (object_hit && smashed == EL_PENGUIN)
3312 Feld[x][y] = EL_AMOEBA_GROWING;
3313 Store[x][y] = EL_AMOEBA_WET;
3315 ResetRandomAnimationValue(x, y);
3320 if (object_hit) /* check which object was hit */
3322 if (CAN_PASS_MAGIC_WALL(element) &&
3323 (smashed == EL_MAGIC_WALL ||
3324 smashed == EL_BD_MAGIC_WALL))
3327 int activated_magic_wall =
3328 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3329 EL_BD_MAGIC_WALL_ACTIVE);
3331 /* activate magic wall / mill */
3332 for (yy = 0; yy < lev_fieldy; yy++)
3333 for (xx = 0; xx < lev_fieldx; xx++)
3334 if (Feld[xx][yy] == smashed)
3335 Feld[xx][yy] = activated_magic_wall;
3337 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3338 game.magic_wall_active = TRUE;
3340 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3341 SND_MAGIC_WALL_ACTIVATING :
3342 SND_BD_MAGIC_WALL_ACTIVATING));
3345 if (IS_PLAYER(x, y + 1))
3347 if (CAN_SMASH_PLAYER(element))
3349 KillHeroUnlessProtected(x, y + 1);
3353 else if (smashed == EL_PENGUIN)
3355 if (CAN_SMASH_PLAYER(element))
3361 else if (element == EL_BD_DIAMOND)
3363 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3369 else if (((element == EL_SP_INFOTRON ||
3370 element == EL_SP_ZONK) &&
3371 (smashed == EL_SP_SNIKSNAK ||
3372 smashed == EL_SP_ELECTRON ||
3373 smashed == EL_SP_DISK_ORANGE)) ||
3374 (element == EL_SP_INFOTRON &&
3375 smashed == EL_SP_DISK_YELLOW))
3381 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3387 else if (CAN_SMASH_EVERYTHING(element))
3389 if (IS_CLASSIC_ENEMY(smashed) ||
3390 CAN_EXPLODE_SMASHED(smashed))
3395 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3397 if (smashed == EL_LAMP ||
3398 smashed == EL_LAMP_ACTIVE)
3403 else if (smashed == EL_NUT)
3405 Feld[x][y + 1] = EL_NUT_BREAKING;
3406 PlayLevelSound(x, y, SND_NUT_BREAKING);
3407 RaiseScoreElement(EL_NUT);
3410 else if (smashed == EL_PEARL)
3412 Feld[x][y + 1] = EL_PEARL_BREAKING;
3413 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3416 else if (smashed == EL_DIAMOND)
3418 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3419 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3422 else if (IS_BELT_SWITCH(smashed))
3424 ToggleBeltSwitch(x, y + 1);
3426 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3427 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3429 ToggleSwitchgateSwitch(x, y + 1);
3431 else if (smashed == EL_LIGHT_SWITCH ||
3432 smashed == EL_LIGHT_SWITCH_ACTIVE)
3434 ToggleLightSwitch(x, y + 1);
3438 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3440 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3441 CE_OTHER_IS_SWITCHING);
3442 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3448 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3453 /* play sound of magic wall / mill */
3455 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3456 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3458 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3459 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3460 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3461 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3466 /* play sound of object that hits the ground */
3467 if (lastline || object_hit)
3468 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3471 inline static void TurnRoundExt(int x, int y)
3483 { 0, 0 }, { 0, 0 }, { 0, 0 },
3488 int left, right, back;
3492 { MV_DOWN, MV_UP, MV_RIGHT },
3493 { MV_UP, MV_DOWN, MV_LEFT },
3495 { MV_LEFT, MV_RIGHT, MV_DOWN },
3499 { MV_RIGHT, MV_LEFT, MV_UP }
3502 int element = Feld[x][y];
3503 int move_pattern = element_info[element].move_pattern;
3505 int old_move_dir = MovDir[x][y];
3506 int left_dir = turn[old_move_dir].left;
3507 int right_dir = turn[old_move_dir].right;
3508 int back_dir = turn[old_move_dir].back;
3510 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3511 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3512 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3513 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3515 int left_x = x + left_dx, left_y = y + left_dy;
3516 int right_x = x + right_dx, right_y = y + right_dy;
3517 int move_x = x + move_dx, move_y = y + move_dy;
3521 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3523 TestIfBadThingTouchesOtherBadThing(x, y);
3525 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3526 MovDir[x][y] = right_dir;
3527 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3528 MovDir[x][y] = left_dir;
3530 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3532 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3535 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3536 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3538 TestIfBadThingTouchesOtherBadThing(x, y);
3540 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3541 MovDir[x][y] = left_dir;
3542 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3543 MovDir[x][y] = right_dir;
3545 if ((element == EL_SPACESHIP ||
3546 element == EL_SP_SNIKSNAK ||
3547 element == EL_SP_ELECTRON)
3548 && MovDir[x][y] != old_move_dir)
3550 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3553 else if (element == EL_YAMYAM)
3555 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3556 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3558 if (can_turn_left && can_turn_right)
3559 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3560 else if (can_turn_left)
3561 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3562 else if (can_turn_right)
3563 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3565 MovDir[x][y] = back_dir;
3567 MovDelay[x][y] = 16 + 16 * RND(3);
3569 else if (element == EL_DARK_YAMYAM)
3571 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3572 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3574 if (can_turn_left && can_turn_right)
3575 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3576 else if (can_turn_left)
3577 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3578 else if (can_turn_right)
3579 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3581 MovDir[x][y] = back_dir;
3583 MovDelay[x][y] = 16 + 16 * RND(3);
3585 else if (element == EL_PACMAN)
3587 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3588 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3590 if (can_turn_left && can_turn_right)
3591 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3592 else if (can_turn_left)
3593 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3594 else if (can_turn_right)
3595 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3597 MovDir[x][y] = back_dir;
3599 MovDelay[x][y] = 6 + RND(40);
3601 else if (element == EL_PIG)
3603 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3604 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3605 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3606 boolean should_turn_left, should_turn_right, should_move_on;
3608 int rnd = RND(rnd_value);
3610 should_turn_left = (can_turn_left &&
3612 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3613 y + back_dy + left_dy)));
3614 should_turn_right = (can_turn_right &&
3616 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3617 y + back_dy + right_dy)));
3618 should_move_on = (can_move_on &&
3621 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3622 y + move_dy + left_dy) ||
3623 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3624 y + move_dy + right_dy)));
3626 if (should_turn_left || should_turn_right || should_move_on)
3628 if (should_turn_left && should_turn_right && should_move_on)
3629 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3630 rnd < 2 * rnd_value / 3 ? right_dir :
3632 else if (should_turn_left && should_turn_right)
3633 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3634 else if (should_turn_left && should_move_on)
3635 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3636 else if (should_turn_right && should_move_on)
3637 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3638 else if (should_turn_left)
3639 MovDir[x][y] = left_dir;
3640 else if (should_turn_right)
3641 MovDir[x][y] = right_dir;
3642 else if (should_move_on)
3643 MovDir[x][y] = old_move_dir;
3645 else if (can_move_on && rnd > rnd_value / 8)
3646 MovDir[x][y] = old_move_dir;
3647 else if (can_turn_left && can_turn_right)
3648 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3649 else if (can_turn_left && rnd > rnd_value / 8)
3650 MovDir[x][y] = left_dir;
3651 else if (can_turn_right && rnd > rnd_value/8)
3652 MovDir[x][y] = right_dir;
3654 MovDir[x][y] = back_dir;
3656 xx = x + move_xy[MovDir[x][y]].x;
3657 yy = y + move_xy[MovDir[x][y]].y;
3659 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3660 MovDir[x][y] = old_move_dir;
3664 else if (element == EL_DRAGON)
3666 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3667 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3668 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3670 int rnd = RND(rnd_value);
3673 if (FrameCounter < 1 && x == 0 && y == 29)
3674 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3677 if (can_move_on && rnd > rnd_value / 8)
3678 MovDir[x][y] = old_move_dir;
3679 else if (can_turn_left && can_turn_right)
3680 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3681 else if (can_turn_left && rnd > rnd_value / 8)
3682 MovDir[x][y] = left_dir;
3683 else if (can_turn_right && rnd > rnd_value / 8)
3684 MovDir[x][y] = right_dir;
3686 MovDir[x][y] = back_dir;
3688 xx = x + move_xy[MovDir[x][y]].x;
3689 yy = y + move_xy[MovDir[x][y]].y;
3692 if (FrameCounter < 1 && x == 0 && y == 29)
3693 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3694 xx, yy, Feld[xx][yy],
3699 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3700 MovDir[x][y] = old_move_dir;
3702 if (!IS_FREE(xx, yy))
3703 MovDir[x][y] = old_move_dir;
3707 if (FrameCounter < 1 && x == 0 && y == 29)
3708 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3713 else if (element == EL_MOLE)
3715 boolean can_move_on =
3716 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3717 IS_AMOEBOID(Feld[move_x][move_y]) ||
3718 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3721 boolean can_turn_left =
3722 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3723 IS_AMOEBOID(Feld[left_x][left_y])));
3725 boolean can_turn_right =
3726 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3727 IS_AMOEBOID(Feld[right_x][right_y])));
3729 if (can_turn_left && can_turn_right)
3730 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3731 else if (can_turn_left)
3732 MovDir[x][y] = left_dir;
3734 MovDir[x][y] = right_dir;
3737 if (MovDir[x][y] != old_move_dir)
3740 else if (element == EL_BALLOON)
3742 MovDir[x][y] = game.balloon_dir;
3745 else if (element == EL_SPRING)
3747 if (MovDir[x][y] & MV_HORIZONTAL &&
3748 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3749 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3750 MovDir[x][y] = MV_NO_MOVING;
3754 else if (element == EL_ROBOT ||
3755 element == EL_SATELLITE ||
3756 element == EL_PENGUIN)
3758 int attr_x = -1, attr_y = -1;
3769 for (i = 0; i < MAX_PLAYERS; i++)
3771 struct PlayerInfo *player = &stored_player[i];
3772 int jx = player->jx, jy = player->jy;
3774 if (!player->active)
3778 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3786 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3792 if (element == EL_PENGUIN)
3795 static int xy[4][2] =
3803 for (i = 0; i < 4; i++)
3805 int ex = x + xy[i % 4][0];
3806 int ey = y + xy[i % 4][1];
3808 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3817 MovDir[x][y] = MV_NO_MOVING;
3819 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3820 else if (attr_x > x)
3821 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3823 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3824 else if (attr_y > y)
3825 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3827 if (element == EL_ROBOT)
3831 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3832 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3833 Moving2Blocked(x, y, &newx, &newy);
3835 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3836 MovDelay[x][y] = 8 + 8 * !RND(3);
3838 MovDelay[x][y] = 16;
3840 else if (element == EL_PENGUIN)
3846 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3848 boolean first_horiz = RND(2);
3849 int new_move_dir = MovDir[x][y];
3852 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3853 Moving2Blocked(x, y, &newx, &newy);
3855 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3859 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3860 Moving2Blocked(x, y, &newx, &newy);
3862 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3865 MovDir[x][y] = old_move_dir;
3869 else /* (element == EL_SATELLITE) */
3875 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3877 boolean first_horiz = RND(2);
3878 int new_move_dir = MovDir[x][y];
3881 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3882 Moving2Blocked(x, y, &newx, &newy);
3884 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3888 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3889 Moving2Blocked(x, y, &newx, &newy);
3891 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3894 MovDir[x][y] = old_move_dir;
3899 else if (move_pattern == MV_TURNING_LEFT ||
3900 move_pattern == MV_TURNING_RIGHT ||
3901 move_pattern == MV_TURNING_LEFT_RIGHT ||
3902 move_pattern == MV_TURNING_RIGHT_LEFT ||
3903 move_pattern == MV_TURNING_RANDOM ||
3904 move_pattern == MV_ALL_DIRECTIONS)
3906 boolean can_turn_left =
3907 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3908 boolean can_turn_right =
3909 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3911 if (move_pattern == MV_TURNING_LEFT)
3912 MovDir[x][y] = left_dir;
3913 else if (move_pattern == MV_TURNING_RIGHT)
3914 MovDir[x][y] = right_dir;
3915 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3916 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3917 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3918 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3919 else if (move_pattern == MV_TURNING_RANDOM)
3920 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3921 can_turn_right && !can_turn_left ? right_dir :
3922 RND(2) ? left_dir : right_dir);
3923 else if (can_turn_left && can_turn_right)
3924 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3925 else if (can_turn_left)
3926 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3927 else if (can_turn_right)
3928 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3930 MovDir[x][y] = back_dir;
3932 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3934 else if (move_pattern == MV_HORIZONTAL ||
3935 move_pattern == MV_VERTICAL)
3937 if (move_pattern & old_move_dir)
3938 MovDir[x][y] = back_dir;
3939 else if (move_pattern == MV_HORIZONTAL)
3940 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3941 else if (move_pattern == MV_VERTICAL)
3942 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3944 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3946 else if (move_pattern & MV_ANY_DIRECTION)
3948 MovDir[x][y] = move_pattern;
3949 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3951 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3953 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3954 MovDir[x][y] = left_dir;
3955 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3956 MovDir[x][y] = right_dir;
3958 if (MovDir[x][y] != old_move_dir)
3959 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3961 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3963 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3964 MovDir[x][y] = right_dir;
3965 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3966 MovDir[x][y] = left_dir;
3968 if (MovDir[x][y] != old_move_dir)
3969 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3971 else if (move_pattern == MV_TOWARDS_PLAYER ||
3972 move_pattern == MV_AWAY_FROM_PLAYER)
3974 int attr_x = -1, attr_y = -1;
3976 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3987 for (i = 0; i < MAX_PLAYERS; i++)
3989 struct PlayerInfo *player = &stored_player[i];
3990 int jx = player->jx, jy = player->jy;
3992 if (!player->active)
3996 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4004 MovDir[x][y] = MV_NO_MOVING;
4006 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4007 else if (attr_x > x)
4008 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4010 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4011 else if (attr_y > y)
4012 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4014 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4016 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4018 boolean first_horiz = RND(2);
4019 int new_move_dir = MovDir[x][y];
4022 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4023 Moving2Blocked(x, y, &newx, &newy);
4025 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4029 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4030 Moving2Blocked(x, y, &newx, &newy);
4032 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4035 MovDir[x][y] = old_move_dir;
4038 else if (move_pattern == MV_WHEN_PUSHED ||
4039 move_pattern == MV_WHEN_DROPPED)
4041 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4042 MovDir[x][y] = MV_NO_MOVING;
4046 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4048 static int test_xy[7][2] =
4058 static int test_dir[7] =
4068 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4069 int move_preference = -1000000; /* start with very low preference */
4070 int new_move_dir = MV_NO_MOVING;
4071 int start_test = RND(4);
4074 for (i = 0; i < 4; i++)
4076 int move_dir = test_dir[start_test + i];
4077 int move_dir_preference;
4079 xx = x + test_xy[start_test + i][0];
4080 yy = y + test_xy[start_test + i][1];
4082 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4083 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4085 new_move_dir = move_dir;
4090 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4093 move_dir_preference = -1 * RunnerVisit[xx][yy];
4094 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4095 move_dir_preference = PlayerVisit[xx][yy];
4097 if (move_dir_preference > move_preference)
4099 /* prefer field that has not been visited for the longest time */
4100 move_preference = move_dir_preference;
4101 new_move_dir = move_dir;
4103 else if (move_dir_preference == move_preference &&
4104 move_dir == old_move_dir)
4106 /* prefer last direction when all directions are preferred equally */
4107 move_preference = move_dir_preference;
4108 new_move_dir = move_dir;
4112 MovDir[x][y] = new_move_dir;
4113 if (old_move_dir != new_move_dir)
4118 static void TurnRound(int x, int y)
4120 int direction = MovDir[x][y];
4123 GfxDir[x][y] = MovDir[x][y];
4129 GfxDir[x][y] = MovDir[x][y];
4132 if (direction != MovDir[x][y])
4137 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4140 GfxAction[x][y] = ACTION_WAITING;
4144 static boolean JustBeingPushed(int x, int y)
4148 for (i = 0; i < MAX_PLAYERS; i++)
4150 struct PlayerInfo *player = &stored_player[i];
4152 if (player->active && player->is_pushing && player->MovPos)
4154 int next_jx = player->jx + (player->jx - player->last_jx);
4155 int next_jy = player->jy + (player->jy - player->last_jy);
4157 if (x == next_jx && y == next_jy)
4165 void StartMoving(int x, int y)
4167 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4168 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4169 int element = Feld[x][y];
4175 if (MovDelay[x][y] == 0)
4176 GfxAction[x][y] = ACTION_DEFAULT;
4178 /* !!! this should be handled more generic (not only for mole) !!! */
4179 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4180 GfxAction[x][y] = ACTION_DEFAULT;
4183 if (CAN_FALL(element) && y < lev_fieldy - 1)
4185 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4186 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4187 if (JustBeingPushed(x, y))
4190 if (element == EL_QUICKSAND_FULL)
4192 if (IS_FREE(x, y + 1))
4194 InitMovingField(x, y, MV_DOWN);
4195 started_moving = TRUE;
4197 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4198 Store[x][y] = EL_ROCK;
4200 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4202 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4205 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4207 if (!MovDelay[x][y])
4208 MovDelay[x][y] = TILEY + 1;
4217 Feld[x][y] = EL_QUICKSAND_EMPTY;
4218 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4219 Store[x][y + 1] = Store[x][y];
4222 PlayLevelSoundAction(x, y, ACTION_FILLING);
4224 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4228 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4229 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4231 InitMovingField(x, y, MV_DOWN);
4232 started_moving = TRUE;
4234 Feld[x][y] = EL_QUICKSAND_FILLING;
4235 Store[x][y] = element;
4237 PlayLevelSoundAction(x, y, ACTION_FILLING);
4239 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4242 else if (element == EL_MAGIC_WALL_FULL)
4244 if (IS_FREE(x, y + 1))
4246 InitMovingField(x, y, MV_DOWN);
4247 started_moving = TRUE;
4249 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4250 Store[x][y] = EL_CHANGED(Store[x][y]);
4252 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4254 if (!MovDelay[x][y])
4255 MovDelay[x][y] = TILEY/4 + 1;
4264 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4265 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4266 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4270 else if (element == EL_BD_MAGIC_WALL_FULL)
4272 if (IS_FREE(x, y + 1))
4274 InitMovingField(x, y, MV_DOWN);
4275 started_moving = TRUE;
4277 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4278 Store[x][y] = EL_CHANGED2(Store[x][y]);
4280 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4282 if (!MovDelay[x][y])
4283 MovDelay[x][y] = TILEY/4 + 1;
4292 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4293 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4294 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4298 else if (CAN_PASS_MAGIC_WALL(element) &&
4299 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4300 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4302 InitMovingField(x, y, MV_DOWN);
4303 started_moving = TRUE;
4306 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4307 EL_BD_MAGIC_WALL_FILLING);
4308 Store[x][y] = element;
4311 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4313 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4318 InitMovingField(x, y, MV_DOWN);
4319 started_moving = TRUE;
4321 Store[x][y] = EL_ACID;
4323 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4324 GfxAction[x][y + 1] = ACTION_ACTIVE;
4328 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4329 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4330 (Feld[x][y + 1] == EL_BLOCKED)) ||
4331 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4332 CAN_SMASH(element) && WasJustFalling[x][y] &&
4333 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4337 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4338 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4339 WasJustMoving[x][y] && !Pushed[x][y + 1])
4341 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4342 WasJustMoving[x][y])
4347 /* this is needed for a special case not covered by calling "Impact()"
4348 from "ContinueMoving()": if an element moves to a tile directly below
4349 another element which was just falling on that tile (which was empty
4350 in the previous frame), the falling element above would just stop
4351 instead of smashing the element below (in previous version, the above
4352 element was just checked for "moving" instead of "falling", resulting
4353 in incorrect smashes caused by horizontal movement of the above
4354 element; also, the case of the player being the element to smash was
4355 simply not covered here... :-/ ) */
4358 WasJustMoving[x][y] = 0;
4359 WasJustFalling[x][y] = 0;
4364 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4366 if (MovDir[x][y] == MV_NO_MOVING)
4368 InitMovingField(x, y, MV_DOWN);
4369 started_moving = TRUE;
4372 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4374 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4375 MovDir[x][y] = MV_DOWN;
4377 InitMovingField(x, y, MV_DOWN);
4378 started_moving = TRUE;
4380 else if (element == EL_AMOEBA_DROP)
4382 Feld[x][y] = EL_AMOEBA_GROWING;
4383 Store[x][y] = EL_AMOEBA_WET;
4385 /* Store[x][y + 1] must be zero, because:
4386 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4389 #if OLD_GAME_BEHAVIOUR
4390 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4392 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4393 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4394 element != EL_DX_SUPABOMB)
4397 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4398 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4399 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4400 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4403 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4404 (IS_FREE(x - 1, y + 1) ||
4405 Feld[x - 1][y + 1] == EL_ACID));
4406 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4407 (IS_FREE(x + 1, y + 1) ||
4408 Feld[x + 1][y + 1] == EL_ACID));
4409 boolean can_fall_any = (can_fall_left || can_fall_right);
4410 boolean can_fall_both = (can_fall_left && can_fall_right);
4412 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4414 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4416 if (slippery_type == SLIPPERY_ONLY_LEFT)
4417 can_fall_right = FALSE;
4418 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4419 can_fall_left = FALSE;
4420 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4421 can_fall_right = FALSE;
4422 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4423 can_fall_left = FALSE;
4425 can_fall_any = (can_fall_left || can_fall_right);
4426 can_fall_both = (can_fall_left && can_fall_right);
4431 if (can_fall_both &&
4432 (game.emulation != EMU_BOULDERDASH &&
4433 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4434 can_fall_left = !(can_fall_right = RND(2));
4436 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4437 started_moving = TRUE;
4440 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4442 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4443 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4444 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4445 int belt_dir = game.belt_dir[belt_nr];
4447 if ((belt_dir == MV_LEFT && left_is_free) ||
4448 (belt_dir == MV_RIGHT && right_is_free))
4450 InitMovingField(x, y, belt_dir);
4451 started_moving = TRUE;
4453 GfxAction[x][y] = ACTION_DEFAULT;
4458 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4459 if (CAN_MOVE(element) && !started_moving)
4461 int move_pattern = element_info[element].move_pattern;
4464 Moving2Blocked(x, y, &newx, &newy);
4467 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4470 if ((element == EL_SATELLITE ||
4471 element == EL_BALLOON ||
4472 element == EL_SPRING)
4473 && JustBeingPushed(x, y))
4478 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4479 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4480 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4483 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4484 element, element_info[element].token_name,
4485 WasJustMoving[x][y],
4486 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4487 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4488 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4489 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4493 WasJustMoving[x][y] = 0;
4496 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4499 if (Feld[x][y] != element) /* element has changed */
4501 element = Feld[x][y];
4502 move_pattern = element_info[element].move_pattern;
4504 if (!CAN_MOVE(element))
4508 if (Feld[x][y] != element) /* element has changed */
4516 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4517 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4519 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4521 Moving2Blocked(x, y, &newx, &newy);
4522 if (Feld[newx][newy] == EL_BLOCKED)
4523 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4529 if (FrameCounter < 1 && x == 0 && y == 29)
4530 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4533 if (!MovDelay[x][y]) /* start new movement phase */
4535 /* all objects that can change their move direction after each step
4536 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4538 if (element != EL_YAMYAM &&
4539 element != EL_DARK_YAMYAM &&
4540 element != EL_PACMAN &&
4541 !(move_pattern & MV_ANY_DIRECTION) &&
4542 move_pattern != MV_TURNING_LEFT &&
4543 move_pattern != MV_TURNING_RIGHT &&
4544 move_pattern != MV_TURNING_LEFT_RIGHT &&
4545 move_pattern != MV_TURNING_RIGHT_LEFT &&
4546 move_pattern != MV_TURNING_RANDOM)
4551 if (FrameCounter < 1 && x == 0 && y == 29)
4552 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4555 if (MovDelay[x][y] && (element == EL_BUG ||
4556 element == EL_SPACESHIP ||
4557 element == EL_SP_SNIKSNAK ||
4558 element == EL_SP_ELECTRON ||
4559 element == EL_MOLE))
4560 DrawLevelField(x, y);
4564 if (MovDelay[x][y]) /* wait some time before next movement */
4569 if (element == EL_YAMYAM)
4572 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4573 DrawLevelElementAnimation(x, y, element);
4577 if (MovDelay[x][y]) /* element still has to wait some time */
4580 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4581 ResetGfxAnimation(x, y);
4585 if (GfxAction[x][y] != ACTION_WAITING)
4586 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4588 GfxAction[x][y] = ACTION_WAITING;
4592 if (element == EL_ROBOT ||
4594 element == EL_PACMAN ||
4596 element == EL_YAMYAM ||
4597 element == EL_DARK_YAMYAM)
4600 DrawLevelElementAnimation(x, y, element);
4602 DrawLevelElementAnimationIfNeeded(x, y, element);
4604 PlayLevelSoundAction(x, y, ACTION_WAITING);
4606 else if (element == EL_SP_ELECTRON)
4607 DrawLevelElementAnimationIfNeeded(x, y, element);
4608 else if (element == EL_DRAGON)
4611 int dir = MovDir[x][y];
4612 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4613 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4614 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4615 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4616 dir == MV_UP ? IMG_FLAMES_1_UP :
4617 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4618 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4621 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4624 GfxAction[x][y] = ACTION_ATTACKING;
4626 if (IS_PLAYER(x, y))
4627 DrawPlayerField(x, y);
4629 DrawLevelField(x, y);
4631 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4633 for (i = 1; i <= 3; i++)
4635 int xx = x + i * dx;
4636 int yy = y + i * dy;
4637 int sx = SCREENX(xx);
4638 int sy = SCREENY(yy);
4639 int flame_graphic = graphic + (i - 1);
4641 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4646 int flamed = MovingOrBlocked2Element(xx, yy);
4648 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4651 RemoveMovingField(xx, yy);
4653 Feld[xx][yy] = EL_FLAMES;
4654 if (IN_SCR_FIELD(sx, sy))
4656 DrawLevelFieldCrumbledSand(xx, yy);
4657 DrawGraphic(sx, sy, flame_graphic, frame);
4662 if (Feld[xx][yy] == EL_FLAMES)
4663 Feld[xx][yy] = EL_EMPTY;
4664 DrawLevelField(xx, yy);
4669 if (MovDelay[x][y]) /* element still has to wait some time */
4671 PlayLevelSoundAction(x, y, ACTION_WAITING);
4677 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4678 for all other elements GfxAction will be set by InitMovingField() */
4679 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4680 GfxAction[x][y] = ACTION_MOVING;
4684 /* now make next step */
4686 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4688 if (DONT_COLLIDE_WITH(element) &&
4689 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4690 !PLAYER_PROTECTED(newx, newy))
4693 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4697 /* player killed by element which is deadly when colliding with */
4699 KillHero(PLAYERINFO(newx, newy));
4704 else if ((element == EL_PENGUIN ||
4705 element == EL_ROBOT ||
4706 element == EL_SATELLITE ||
4707 element == EL_BALLOON ||
4708 IS_CUSTOM_ELEMENT(element)) &&
4709 IN_LEV_FIELD(newx, newy) &&
4710 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4713 Store[x][y] = EL_ACID;
4715 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4717 if (Feld[newx][newy] == EL_EXIT_OPEN)
4721 DrawLevelField(x, y);
4723 Feld[x][y] = EL_EMPTY;
4724 DrawLevelField(x, y);
4727 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4728 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4729 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4731 local_player->friends_still_needed--;
4732 if (!local_player->friends_still_needed &&
4733 !local_player->GameOver && AllPlayersGone)
4734 local_player->LevelSolved = local_player->GameOver = TRUE;
4738 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4740 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4741 DrawLevelField(newx, newy);
4743 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4745 else if (!IS_FREE(newx, newy))
4747 GfxAction[x][y] = ACTION_WAITING;
4749 if (IS_PLAYER(x, y))
4750 DrawPlayerField(x, y);
4752 DrawLevelField(x, y);
4757 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4759 if (IS_FOOD_PIG(Feld[newx][newy]))
4761 if (IS_MOVING(newx, newy))
4762 RemoveMovingField(newx, newy);
4765 Feld[newx][newy] = EL_EMPTY;
4766 DrawLevelField(newx, newy);
4769 PlayLevelSound(x, y, SND_PIG_DIGGING);
4771 else if (!IS_FREE(newx, newy))
4773 if (IS_PLAYER(x, y))
4774 DrawPlayerField(x, y);
4776 DrawLevelField(x, y);
4785 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4788 else if (IS_CUSTOM_ELEMENT(element) &&
4789 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4793 !IS_FREE(newx, newy)
4798 int new_element = Feld[newx][newy];
4801 printf("::: '%s' digs '%s' [%d]\n",
4802 element_info[element].token_name,
4803 element_info[Feld[newx][newy]].token_name,
4804 StorePlayer[newx][newy]);
4807 if (!IS_FREE(newx, newy))
4809 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4810 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4813 /* no element can dig solid indestructible elements */
4814 if (IS_INDESTRUCTIBLE(new_element) &&
4815 !IS_DIGGABLE(new_element) &&
4816 !IS_COLLECTIBLE(new_element))
4819 if (AmoebaNr[newx][newy] &&
4820 (new_element == EL_AMOEBA_FULL ||
4821 new_element == EL_BD_AMOEBA ||
4822 new_element == EL_AMOEBA_GROWING))
4824 AmoebaCnt[AmoebaNr[newx][newy]]--;
4825 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4828 if (IS_MOVING(newx, newy))
4829 RemoveMovingField(newx, newy);
4832 RemoveField(newx, newy);
4833 DrawLevelField(newx, newy);
4836 PlayLevelSoundAction(x, y, action);
4839 if (new_element == element_info[element].move_enter_element)
4840 element_info[element].can_leave_element = TRUE;
4842 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4844 RunnerVisit[x][y] = FrameCounter;
4845 PlayerVisit[x][y] /= 8; /* expire player visit path */
4851 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4853 if (!IS_FREE(newx, newy))
4855 if (IS_PLAYER(x, y))
4856 DrawPlayerField(x, y);
4858 DrawLevelField(x, y);
4864 boolean wanna_flame = !RND(10);
4865 int dx = newx - x, dy = newy - y;
4866 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4867 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4868 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4869 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4870 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4871 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4874 IS_CLASSIC_ENEMY(element1) ||
4875 IS_CLASSIC_ENEMY(element2)) &&
4876 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4877 element1 != EL_FLAMES && element2 != EL_FLAMES)
4880 ResetGfxAnimation(x, y);
4881 GfxAction[x][y] = ACTION_ATTACKING;
4884 if (IS_PLAYER(x, y))
4885 DrawPlayerField(x, y);
4887 DrawLevelField(x, y);
4889 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4891 MovDelay[x][y] = 50;
4893 Feld[newx][newy] = EL_FLAMES;
4894 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4895 Feld[newx1][newy1] = EL_FLAMES;
4896 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4897 Feld[newx2][newy2] = EL_FLAMES;
4903 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4904 Feld[newx][newy] == EL_DIAMOND)
4906 if (IS_MOVING(newx, newy))
4907 RemoveMovingField(newx, newy);
4910 Feld[newx][newy] = EL_EMPTY;
4911 DrawLevelField(newx, newy);
4914 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4916 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4917 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4919 if (AmoebaNr[newx][newy])
4921 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4922 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4923 Feld[newx][newy] == EL_BD_AMOEBA)
4924 AmoebaCnt[AmoebaNr[newx][newy]]--;
4927 if (IS_MOVING(newx, newy))
4928 RemoveMovingField(newx, newy);
4931 Feld[newx][newy] = EL_EMPTY;
4932 DrawLevelField(newx, newy);
4935 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4937 else if ((element == EL_PACMAN || element == EL_MOLE)
4938 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4940 if (AmoebaNr[newx][newy])
4942 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4943 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4944 Feld[newx][newy] == EL_BD_AMOEBA)
4945 AmoebaCnt[AmoebaNr[newx][newy]]--;
4948 if (element == EL_MOLE)
4950 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4951 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4953 ResetGfxAnimation(x, y);
4954 GfxAction[x][y] = ACTION_DIGGING;
4955 DrawLevelField(x, y);
4957 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4959 return; /* wait for shrinking amoeba */
4961 else /* element == EL_PACMAN */
4963 Feld[newx][newy] = EL_EMPTY;
4964 DrawLevelField(newx, newy);
4965 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4968 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4969 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4970 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4972 /* wait for shrinking amoeba to completely disappear */
4975 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4977 /* object was running against a wall */
4982 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4983 DrawLevelElementAnimation(x, y, element);
4985 if (element == EL_BUG ||
4986 element == EL_SPACESHIP ||
4987 element == EL_SP_SNIKSNAK)
4988 DrawLevelField(x, y);
4989 else if (element == EL_MOLE)
4990 DrawLevelField(x, y);
4991 else if (element == EL_BD_BUTTERFLY ||
4992 element == EL_BD_FIREFLY)
4993 DrawLevelElementAnimationIfNeeded(x, y, element);
4994 else if (element == EL_SATELLITE)
4995 DrawLevelElementAnimationIfNeeded(x, y, element);
4996 else if (element == EL_SP_ELECTRON)
4997 DrawLevelElementAnimationIfNeeded(x, y, element);
5000 if (DONT_TOUCH(element))
5001 TestIfBadThingTouchesHero(x, y);
5004 PlayLevelSoundAction(x, y, ACTION_WAITING);
5010 InitMovingField(x, y, MovDir[x][y]);
5012 PlayLevelSoundAction(x, y, ACTION_MOVING);
5016 ContinueMoving(x, y);
5019 void ContinueMoving(int x, int y)
5021 int element = Feld[x][y];
5022 struct ElementInfo *ei = &element_info[element];
5023 int direction = MovDir[x][y];
5024 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5025 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5026 int newx = x + dx, newy = y + dy;
5028 int nextx = newx + dx, nexty = newy + dy;
5030 boolean pushed = Pushed[x][y];
5032 MovPos[x][y] += getElementMoveStepsize(x, y);
5034 if (pushed) /* special case: moving object pushed by player */
5035 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5037 if (ABS(MovPos[x][y]) < TILEX)
5039 DrawLevelField(x, y);
5041 return; /* element is still moving */
5044 /* element reached destination field */
5046 Feld[x][y] = EL_EMPTY;
5047 Feld[newx][newy] = element;
5048 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5050 if (element == EL_MOLE)
5052 Feld[x][y] = EL_SAND;
5054 DrawLevelFieldCrumbledSandNeighbours(x, y);
5056 else if (element == EL_QUICKSAND_FILLING)
5058 element = Feld[newx][newy] = get_next_element(element);
5059 Store[newx][newy] = Store[x][y];
5061 else if (element == EL_QUICKSAND_EMPTYING)
5063 Feld[x][y] = get_next_element(element);
5064 element = Feld[newx][newy] = Store[x][y];
5066 else if (element == EL_MAGIC_WALL_FILLING)
5068 element = Feld[newx][newy] = get_next_element(element);
5069 if (!game.magic_wall_active)
5070 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5071 Store[newx][newy] = Store[x][y];
5073 else if (element == EL_MAGIC_WALL_EMPTYING)
5075 Feld[x][y] = get_next_element(element);
5076 if (!game.magic_wall_active)
5077 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5078 element = Feld[newx][newy] = Store[x][y];
5080 else if (element == EL_BD_MAGIC_WALL_FILLING)
5082 element = Feld[newx][newy] = get_next_element(element);
5083 if (!game.magic_wall_active)
5084 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5085 Store[newx][newy] = Store[x][y];
5087 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5089 Feld[x][y] = get_next_element(element);
5090 if (!game.magic_wall_active)
5091 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5092 element = Feld[newx][newy] = Store[x][y];
5094 else if (element == EL_AMOEBA_DROPPING)
5096 Feld[x][y] = get_next_element(element);
5097 element = Feld[newx][newy] = Store[x][y];
5099 else if (element == EL_SOKOBAN_OBJECT)
5102 Feld[x][y] = Back[x][y];
5104 if (Back[newx][newy])
5105 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5107 Back[x][y] = Back[newx][newy] = 0;
5109 else if (Store[x][y] == EL_ACID)
5111 element = Feld[newx][newy] = EL_ACID;
5115 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5116 MovDelay[newx][newy] = 0;
5118 /* copy element change control values to new field */
5119 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5120 ChangePage[newx][newy] = ChangePage[x][y];
5121 Changed[newx][newy] = Changed[x][y];
5122 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5124 ChangeDelay[x][y] = 0;
5125 ChangePage[x][y] = -1;
5126 Changed[x][y] = CE_BITMASK_DEFAULT;
5127 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5129 /* copy animation control values to new field */
5130 GfxFrame[newx][newy] = GfxFrame[x][y];
5131 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5132 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5133 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5135 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5137 ResetGfxAnimation(x, y); /* reset animation values for old field */
5140 /* some elements can leave other elements behind after moving */
5141 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5142 ei->move_leave_element != EL_EMPTY &&
5143 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5144 ei->can_leave_element_last))
5146 Feld[x][y] = ei->move_leave_element;
5147 InitField(x, y, FALSE);
5149 if (GFX_CRUMBLED(Feld[x][y]))
5150 DrawLevelFieldCrumbledSandNeighbours(x, y);
5153 ei->can_leave_element_last = ei->can_leave_element;
5154 ei->can_leave_element = FALSE;
5158 /* 2.1.1 (does not work correctly for spring) */
5159 if (!CAN_MOVE(element))
5160 MovDir[newx][newy] = 0;
5164 /* (does not work for falling objects that slide horizontally) */
5165 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5166 MovDir[newx][newy] = 0;
5169 if (!CAN_MOVE(element) ||
5170 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5171 MovDir[newx][newy] = 0;
5174 if (!CAN_MOVE(element) ||
5175 (CAN_FALL(element) && direction == MV_DOWN))
5176 GfxDir[x][y] = MovDir[newx][newy] = 0;
5181 DrawLevelField(x, y);
5182 DrawLevelField(newx, newy);
5184 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5186 /* prevent pushed element from moving on in pushed direction */
5187 if (pushed && CAN_MOVE(element) &&
5188 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5189 !(element_info[element].move_pattern & direction))
5190 TurnRound(newx, newy);
5192 if (!pushed) /* special case: moving object pushed by player */
5194 WasJustMoving[newx][newy] = 3;
5196 if (CAN_FALL(element) && direction == MV_DOWN)
5197 WasJustFalling[newx][newy] = 3;
5200 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5202 TestIfBadThingTouchesHero(newx, newy);
5203 TestIfBadThingTouchesFriend(newx, newy);
5205 if (!IS_CUSTOM_ELEMENT(element))
5206 TestIfBadThingTouchesOtherBadThing(newx, newy);
5208 else if (element == EL_PENGUIN)
5209 TestIfFriendTouchesBadThing(newx, newy);
5211 if (CAN_FALL(element) && direction == MV_DOWN &&
5212 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5216 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5220 if (ChangePage[newx][newy] != -1) /* delayed change */
5221 ChangeElement(newx, newy, ChangePage[newx][newy]);
5226 TestIfElementHitsCustomElement(newx, newy, direction);
5230 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5232 int hitting_element = Feld[newx][newy];
5234 /* !!! fix side (direction) orientation here and elsewhere !!! */
5235 CheckElementSideChange(newx, newy, hitting_element,
5236 direction, CE_HITTING_SOMETHING, -1);
5239 if (IN_LEV_FIELD(nextx, nexty))
5241 static int opposite_directions[] =
5248 int move_dir_bit = MV_DIR_BIT(direction);
5249 int opposite_direction = opposite_directions[move_dir_bit];
5250 int hitting_side = direction;
5251 int touched_side = opposite_direction;
5252 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5253 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5254 MovDir[nextx][nexty] != direction ||
5255 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5261 CheckElementSideChange(nextx, nexty, touched_element,
5262 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5264 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5265 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5267 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5269 struct ElementChangeInfo *change =
5270 &element_info[hitting_element].change_page[i];
5272 if (change->can_change &&
5273 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5274 change->sides & touched_side &&
5275 change->trigger_element == touched_element)
5277 CheckElementSideChange(newx, newy, hitting_element,
5278 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5284 if (IS_CUSTOM_ELEMENT(touched_element) &&
5285 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5287 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5289 struct ElementChangeInfo *change =
5290 &element_info[touched_element].change_page[i];
5292 if (change->can_change &&
5293 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5294 change->sides & hitting_side &&
5295 change->trigger_element == hitting_element)
5297 CheckElementSideChange(nextx, nexty, touched_element,
5298 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5309 TestIfPlayerTouchesCustomElement(newx, newy);
5310 TestIfElementTouchesCustomElement(newx, newy);
5313 int AmoebeNachbarNr(int ax, int ay)
5316 int element = Feld[ax][ay];
5318 static int xy[4][2] =
5326 for (i = 0; i < 4; i++)
5328 int x = ax + xy[i][0];
5329 int y = ay + xy[i][1];
5331 if (!IN_LEV_FIELD(x, y))
5334 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5335 group_nr = AmoebaNr[x][y];
5341 void AmoebenVereinigen(int ax, int ay)
5343 int i, x, y, xx, yy;
5344 int new_group_nr = AmoebaNr[ax][ay];
5345 static int xy[4][2] =
5353 if (new_group_nr == 0)
5356 for (i = 0; i < 4; i++)
5361 if (!IN_LEV_FIELD(x, y))
5364 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5365 Feld[x][y] == EL_BD_AMOEBA ||
5366 Feld[x][y] == EL_AMOEBA_DEAD) &&
5367 AmoebaNr[x][y] != new_group_nr)
5369 int old_group_nr = AmoebaNr[x][y];
5371 if (old_group_nr == 0)
5374 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5375 AmoebaCnt[old_group_nr] = 0;
5376 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5377 AmoebaCnt2[old_group_nr] = 0;
5379 for (yy = 0; yy < lev_fieldy; yy++)
5381 for (xx = 0; xx < lev_fieldx; xx++)
5383 if (AmoebaNr[xx][yy] == old_group_nr)
5384 AmoebaNr[xx][yy] = new_group_nr;
5391 void AmoebeUmwandeln(int ax, int ay)
5395 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5397 int group_nr = AmoebaNr[ax][ay];
5402 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5403 printf("AmoebeUmwandeln(): This should never happen!\n");
5408 for (y = 0; y < lev_fieldy; y++)
5410 for (x = 0; x < lev_fieldx; x++)
5412 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5415 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5419 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5420 SND_AMOEBA_TURNING_TO_GEM :
5421 SND_AMOEBA_TURNING_TO_ROCK));
5426 static int xy[4][2] =
5434 for (i = 0; i < 4; i++)
5439 if (!IN_LEV_FIELD(x, y))
5442 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5444 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5445 SND_AMOEBA_TURNING_TO_GEM :
5446 SND_AMOEBA_TURNING_TO_ROCK));
5453 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5456 int group_nr = AmoebaNr[ax][ay];
5457 boolean done = FALSE;
5462 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5463 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5468 for (y = 0; y < lev_fieldy; y++)
5470 for (x = 0; x < lev_fieldx; x++)
5472 if (AmoebaNr[x][y] == group_nr &&
5473 (Feld[x][y] == EL_AMOEBA_DEAD ||
5474 Feld[x][y] == EL_BD_AMOEBA ||
5475 Feld[x][y] == EL_AMOEBA_GROWING))
5478 Feld[x][y] = new_element;
5479 InitField(x, y, FALSE);
5480 DrawLevelField(x, y);
5487 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5488 SND_BD_AMOEBA_TURNING_TO_ROCK :
5489 SND_BD_AMOEBA_TURNING_TO_GEM));
5492 void AmoebeWaechst(int x, int y)
5494 static unsigned long sound_delay = 0;
5495 static unsigned long sound_delay_value = 0;
5497 if (!MovDelay[x][y]) /* start new growing cycle */
5501 if (DelayReached(&sound_delay, sound_delay_value))
5504 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5506 if (Store[x][y] == EL_BD_AMOEBA)
5507 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5509 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5511 sound_delay_value = 30;
5515 if (MovDelay[x][y]) /* wait some time before growing bigger */
5518 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5520 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5521 6 - MovDelay[x][y]);
5523 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5526 if (!MovDelay[x][y])
5528 Feld[x][y] = Store[x][y];
5530 DrawLevelField(x, y);
5535 void AmoebaDisappearing(int x, int y)
5537 static unsigned long sound_delay = 0;
5538 static unsigned long sound_delay_value = 0;
5540 if (!MovDelay[x][y]) /* start new shrinking cycle */
5544 if (DelayReached(&sound_delay, sound_delay_value))
5545 sound_delay_value = 30;
5548 if (MovDelay[x][y]) /* wait some time before shrinking */
5551 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5553 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5554 6 - MovDelay[x][y]);
5556 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5559 if (!MovDelay[x][y])
5561 Feld[x][y] = EL_EMPTY;
5562 DrawLevelField(x, y);
5564 /* don't let mole enter this field in this cycle;
5565 (give priority to objects falling to this field from above) */
5571 void AmoebeAbleger(int ax, int ay)
5574 int element = Feld[ax][ay];
5575 int graphic = el2img(element);
5576 int newax = ax, neway = ay;
5577 static int xy[4][2] =
5585 if (!level.amoeba_speed)
5587 Feld[ax][ay] = EL_AMOEBA_DEAD;
5588 DrawLevelField(ax, ay);
5592 if (IS_ANIMATED(graphic))
5593 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5595 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5596 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5598 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5601 if (MovDelay[ax][ay])
5605 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5608 int x = ax + xy[start][0];
5609 int y = ay + xy[start][1];
5611 if (!IN_LEV_FIELD(x, y))
5614 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5615 if (IS_FREE(x, y) ||
5616 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5622 if (newax == ax && neway == ay)
5625 else /* normal or "filled" (BD style) amoeba */
5628 boolean waiting_for_player = FALSE;
5630 for (i = 0; i < 4; i++)
5632 int j = (start + i) % 4;
5633 int x = ax + xy[j][0];
5634 int y = ay + xy[j][1];
5636 if (!IN_LEV_FIELD(x, y))
5639 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5640 if (IS_FREE(x, y) ||
5641 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5647 else if (IS_PLAYER(x, y))
5648 waiting_for_player = TRUE;
5651 if (newax == ax && neway == ay) /* amoeba cannot grow */
5653 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5655 Feld[ax][ay] = EL_AMOEBA_DEAD;
5656 DrawLevelField(ax, ay);
5657 AmoebaCnt[AmoebaNr[ax][ay]]--;
5659 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5661 if (element == EL_AMOEBA_FULL)
5662 AmoebeUmwandeln(ax, ay);
5663 else if (element == EL_BD_AMOEBA)
5664 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5669 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5671 /* amoeba gets larger by growing in some direction */
5673 int new_group_nr = AmoebaNr[ax][ay];
5676 if (new_group_nr == 0)
5678 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5679 printf("AmoebeAbleger(): This should never happen!\n");
5684 AmoebaNr[newax][neway] = new_group_nr;
5685 AmoebaCnt[new_group_nr]++;
5686 AmoebaCnt2[new_group_nr]++;
5688 /* if amoeba touches other amoeba(s) after growing, unify them */
5689 AmoebenVereinigen(newax, neway);
5691 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5693 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5699 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5700 (neway == lev_fieldy - 1 && newax != ax))
5702 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5703 Store[newax][neway] = element;
5705 else if (neway == ay)
5707 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5709 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5711 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5716 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5717 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5718 Store[ax][ay] = EL_AMOEBA_DROP;
5719 ContinueMoving(ax, ay);
5723 DrawLevelField(newax, neway);
5726 void Life(int ax, int ay)
5729 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5731 int element = Feld[ax][ay];
5732 int graphic = el2img(element);
5733 boolean changed = FALSE;
5735 if (IS_ANIMATED(graphic))
5736 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5741 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5742 MovDelay[ax][ay] = life_time;
5744 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5747 if (MovDelay[ax][ay])
5751 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5753 int xx = ax+x1, yy = ay+y1;
5756 if (!IN_LEV_FIELD(xx, yy))
5759 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5761 int x = xx+x2, y = yy+y2;
5763 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5766 if (((Feld[x][y] == element ||
5767 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5769 (IS_FREE(x, y) && Stop[x][y]))
5773 if (xx == ax && yy == ay) /* field in the middle */
5775 if (nachbarn < life[0] || nachbarn > life[1])
5777 Feld[xx][yy] = EL_EMPTY;
5779 DrawLevelField(xx, yy);
5780 Stop[xx][yy] = TRUE;
5784 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5785 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5786 { /* free border field */
5787 if (nachbarn >= life[2] && nachbarn <= life[3])
5789 Feld[xx][yy] = element;
5790 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5792 DrawLevelField(xx, yy);
5793 Stop[xx][yy] = TRUE;
5800 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5801 SND_GAME_OF_LIFE_GROWING);
5804 static void InitRobotWheel(int x, int y)
5806 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5809 static void RunRobotWheel(int x, int y)
5811 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5814 static void StopRobotWheel(int x, int y)
5816 if (ZX == x && ZY == y)
5820 static void InitTimegateWheel(int x, int y)
5822 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5825 static void RunTimegateWheel(int x, int y)
5827 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5830 void CheckExit(int x, int y)
5832 if (local_player->gems_still_needed > 0 ||
5833 local_player->sokobanfields_still_needed > 0 ||
5834 local_player->lights_still_needed > 0)
5836 int element = Feld[x][y];
5837 int graphic = el2img(element);
5839 if (IS_ANIMATED(graphic))
5840 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5845 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5848 Feld[x][y] = EL_EXIT_OPENING;
5850 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5853 void CheckExitSP(int x, int y)
5855 if (local_player->gems_still_needed > 0)
5857 int element = Feld[x][y];
5858 int graphic = el2img(element);
5860 if (IS_ANIMATED(graphic))
5861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5866 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5869 Feld[x][y] = EL_SP_EXIT_OPENING;
5871 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5874 static void CloseAllOpenTimegates()
5878 for (y = 0; y < lev_fieldy; y++)
5880 for (x = 0; x < lev_fieldx; x++)
5882 int element = Feld[x][y];
5884 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5886 Feld[x][y] = EL_TIMEGATE_CLOSING;
5888 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5890 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5897 void EdelsteinFunkeln(int x, int y)
5899 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5902 if (Feld[x][y] == EL_BD_DIAMOND)
5905 if (MovDelay[x][y] == 0) /* next animation frame */
5906 MovDelay[x][y] = 11 * !SimpleRND(500);
5908 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5912 if (setup.direct_draw && MovDelay[x][y])
5913 SetDrawtoField(DRAW_BUFFERED);
5915 DrawLevelElementAnimation(x, y, Feld[x][y]);
5917 if (MovDelay[x][y] != 0)
5919 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5920 10 - MovDelay[x][y]);
5922 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5924 if (setup.direct_draw)
5928 dest_x = FX + SCREENX(x) * TILEX;
5929 dest_y = FY + SCREENY(y) * TILEY;
5931 BlitBitmap(drawto_field, window,
5932 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5933 SetDrawtoField(DRAW_DIRECT);
5939 void MauerWaechst(int x, int y)
5943 if (!MovDelay[x][y]) /* next animation frame */
5944 MovDelay[x][y] = 3 * delay;
5946 if (MovDelay[x][y]) /* wait some time before next frame */
5950 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5952 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5953 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5955 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5958 if (!MovDelay[x][y])
5960 if (MovDir[x][y] == MV_LEFT)
5962 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5963 DrawLevelField(x - 1, y);
5965 else if (MovDir[x][y] == MV_RIGHT)
5967 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5968 DrawLevelField(x + 1, y);
5970 else if (MovDir[x][y] == MV_UP)
5972 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5973 DrawLevelField(x, y - 1);
5977 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5978 DrawLevelField(x, y + 1);
5981 Feld[x][y] = Store[x][y];
5983 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5984 DrawLevelField(x, y);
5989 void MauerAbleger(int ax, int ay)
5991 int element = Feld[ax][ay];
5992 int graphic = el2img(element);
5993 boolean oben_frei = FALSE, unten_frei = FALSE;
5994 boolean links_frei = FALSE, rechts_frei = FALSE;
5995 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5996 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5997 boolean new_wall = FALSE;
5999 if (IS_ANIMATED(graphic))
6000 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6002 if (!MovDelay[ax][ay]) /* start building new wall */
6003 MovDelay[ax][ay] = 6;
6005 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6008 if (MovDelay[ax][ay])
6012 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6014 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6016 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6018 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6021 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6022 element == EL_EXPANDABLE_WALL_ANY)
6026 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6027 Store[ax][ay-1] = element;
6028 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6029 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6030 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6031 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6036 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6037 Store[ax][ay+1] = element;
6038 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6039 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6040 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6041 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6046 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6047 element == EL_EXPANDABLE_WALL_ANY ||
6048 element == EL_EXPANDABLE_WALL)
6052 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6053 Store[ax-1][ay] = element;
6054 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6055 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6056 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6057 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6063 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6064 Store[ax+1][ay] = element;
6065 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6066 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6067 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6068 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6073 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6074 DrawLevelField(ax, ay);
6076 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6078 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6079 unten_massiv = TRUE;
6080 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6081 links_massiv = TRUE;
6082 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6083 rechts_massiv = TRUE;
6085 if (((oben_massiv && unten_massiv) ||
6086 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6087 element == EL_EXPANDABLE_WALL) &&
6088 ((links_massiv && rechts_massiv) ||
6089 element == EL_EXPANDABLE_WALL_VERTICAL))
6090 Feld[ax][ay] = EL_WALL;
6094 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6096 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6100 void CheckForDragon(int x, int y)
6103 boolean dragon_found = FALSE;
6104 static int xy[4][2] =
6112 for (i = 0; i < 4; i++)
6114 for (j = 0; j < 4; j++)
6116 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6118 if (IN_LEV_FIELD(xx, yy) &&
6119 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6121 if (Feld[xx][yy] == EL_DRAGON)
6122 dragon_found = TRUE;
6131 for (i = 0; i < 4; i++)
6133 for (j = 0; j < 3; j++)
6135 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6137 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6139 Feld[xx][yy] = EL_EMPTY;
6140 DrawLevelField(xx, yy);
6149 static void InitBuggyBase(int x, int y)
6151 int element = Feld[x][y];
6152 int activating_delay = FRAMES_PER_SECOND / 4;
6155 (element == EL_SP_BUGGY_BASE ?
6156 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6157 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6159 element == EL_SP_BUGGY_BASE_ACTIVE ?
6160 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6163 static void WarnBuggyBase(int x, int y)
6166 static int xy[4][2] =
6174 for (i = 0; i < 4; i++)
6176 int xx = x + xy[i][0], yy = y + xy[i][1];
6178 if (IS_PLAYER(xx, yy))
6180 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6187 static void InitTrap(int x, int y)
6189 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6192 static void ActivateTrap(int x, int y)
6194 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6197 static void ChangeActiveTrap(int x, int y)
6199 int graphic = IMG_TRAP_ACTIVE;
6201 /* if new animation frame was drawn, correct crumbled sand border */
6202 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6203 DrawLevelFieldCrumbledSand(x, y);
6206 static void ChangeElementNowExt(int x, int y, int target_element)
6208 int previous_move_direction = MovDir[x][y];
6210 /* check if element under player changes from accessible to unaccessible
6211 (needed for special case of dropping element which then changes) */
6212 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6213 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6220 Feld[x][y] = target_element;
6222 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6224 ResetGfxAnimation(x, y);
6225 ResetRandomAnimationValue(x, y);
6227 if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
6228 MovDir[x][y] = previous_move_direction;
6230 InitField(x, y, FALSE);
6231 if (CAN_MOVE(Feld[x][y]))
6234 DrawLevelField(x, y);
6236 if (GFX_CRUMBLED(Feld[x][y]))
6237 DrawLevelFieldCrumbledSandNeighbours(x, y);
6239 TestIfBadThingTouchesHero(x, y);
6240 TestIfPlayerTouchesCustomElement(x, y);
6241 TestIfElementTouchesCustomElement(x, y);
6243 if (ELEM_IS_PLAYER(target_element))
6244 RelocatePlayer(x, y, target_element);
6247 static boolean ChangeElementNow(int x, int y, int element, int page)
6249 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6251 /* always use default change event to prevent running into a loop */
6252 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6253 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6255 /* do not change already changed elements with same change event */
6257 if (Changed[x][y] & ChangeEvent[x][y])
6264 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6266 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6268 if (change->explode)
6275 if (change->use_content)
6277 boolean complete_change = TRUE;
6278 boolean can_change[3][3];
6281 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6283 boolean half_destructible;
6284 int ex = x + xx - 1;
6285 int ey = y + yy - 1;
6288 can_change[xx][yy] = TRUE;
6290 if (ex == x && ey == y) /* do not check changing element itself */
6293 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6295 can_change[xx][yy] = FALSE; /* do not change empty borders */
6300 if (!IN_LEV_FIELD(ex, ey))
6302 can_change[xx][yy] = FALSE;
6303 complete_change = FALSE;
6310 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6311 e = MovingOrBlocked2Element(ex, ey);
6313 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6315 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6316 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6317 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6319 can_change[xx][yy] = FALSE;
6320 complete_change = FALSE;
6324 if (!change->only_complete || complete_change)
6326 boolean something_has_changed = FALSE;
6328 if (change->only_complete && change->use_random_change &&
6329 RND(100) < change->random)
6332 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6334 int ex = x + xx - 1;
6335 int ey = y + yy - 1;
6337 if (can_change[xx][yy] && (!change->use_random_change ||
6338 RND(100) < change->random))
6340 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6341 RemoveMovingField(ex, ey);
6343 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6345 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6347 something_has_changed = TRUE;
6349 /* for symmetry reasons, freeze newly created border elements */
6350 if (ex != x || ey != y)
6351 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6355 if (something_has_changed)
6356 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6361 ChangeElementNowExt(x, y, change->target_element);
6363 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6369 static void ChangeElement(int x, int y, int page)
6371 int element = MovingOrBlocked2Element(x, y);
6372 struct ElementInfo *ei = &element_info[element];
6373 struct ElementChangeInfo *change = &ei->change_page[page];
6377 if (!CAN_CHANGE(element))
6380 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6381 x, y, element, element_info[element].token_name);
6382 printf("ChangeElement(): This should never happen!\n");
6388 if (ChangeDelay[x][y] == 0) /* initialize element change */
6390 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6391 RND(change->delay_random * change->delay_frames)) + 1;
6393 ResetGfxAnimation(x, y);
6394 ResetRandomAnimationValue(x, y);
6396 if (change->pre_change_function)
6397 change->pre_change_function(x, y);
6400 ChangeDelay[x][y]--;
6402 if (ChangeDelay[x][y] != 0) /* continue element change */
6404 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6406 if (IS_ANIMATED(graphic))
6407 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6409 if (change->change_function)
6410 change->change_function(x, y);
6412 else /* finish element change */
6414 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6416 page = ChangePage[x][y];
6417 ChangePage[x][y] = -1;
6421 if (IS_MOVING(x, y) && !change->explode)
6423 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6426 ChangeDelay[x][y] = 1; /* try change after next move step */
6427 ChangePage[x][y] = page; /* remember page to use for change */
6432 if (ChangeElementNow(x, y, element, page))
6434 if (change->post_change_function)
6435 change->post_change_function(x, y);
6440 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6441 int trigger_element,
6447 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6450 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6452 int element = EL_CUSTOM_START + i;
6454 boolean change_element = FALSE;
6457 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6460 for (j = 0; j < element_info[element].num_change_pages; j++)
6462 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6464 if (change->can_change &&
6466 change->events & CH_EVENT_BIT(trigger_event) &&
6468 change->sides & trigger_side &&
6470 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6472 change->trigger_element == trigger_element
6477 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6478 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6479 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6482 change_element = TRUE;
6489 if (!change_element)
6492 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6495 if (x == lx && y == ly) /* do not change trigger element itself */
6499 if (Feld[x][y] == element)
6501 ChangeDelay[x][y] = 1;
6502 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6503 ChangeElement(x, y, page);
6511 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6514 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6518 static boolean CheckElementSideChange(int x, int y, int element, int side,
6519 int trigger_event, int page)
6521 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6524 if (Feld[x][y] == EL_BLOCKED)
6526 Blocked2Moving(x, y, &x, &y);
6527 element = Feld[x][y];
6531 page = element_info[element].event_page_nr[trigger_event];
6533 if (!(element_info[element].change_page[page].sides & side))
6536 ChangeDelay[x][y] = 1;
6537 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6538 ChangeElement(x, y, page);
6543 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6545 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6548 static void PlayPlayerSound(struct PlayerInfo *player)
6550 int jx = player->jx, jy = player->jy;
6551 int element = player->element_nr;
6552 int last_action = player->last_action_waiting;
6553 int action = player->action_waiting;
6555 if (player->is_waiting)
6557 if (action != last_action)
6558 PlayLevelSoundElementAction(jx, jy, element, action);
6560 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6564 if (action != last_action)
6565 StopSound(element_info[element].sound[last_action]);
6567 if (last_action == ACTION_SLEEPING)
6568 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6572 static void PlayAllPlayersSound()
6576 for (i = 0; i < MAX_PLAYERS; i++)
6577 if (stored_player[i].active)
6578 PlayPlayerSound(&stored_player[i]);
6581 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6583 boolean last_waiting = player->is_waiting;
6584 int move_dir = player->MovDir;
6586 player->last_action_waiting = player->action_waiting;
6590 if (!last_waiting) /* not waiting -> waiting */
6592 player->is_waiting = TRUE;
6594 player->frame_counter_bored =
6596 game.player_boring_delay_fixed +
6597 SimpleRND(game.player_boring_delay_random);
6598 player->frame_counter_sleeping =
6600 game.player_sleeping_delay_fixed +
6601 SimpleRND(game.player_sleeping_delay_random);
6603 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6606 if (game.player_sleeping_delay_fixed +
6607 game.player_sleeping_delay_random > 0 &&
6608 player->anim_delay_counter == 0 &&
6609 player->post_delay_counter == 0 &&
6610 FrameCounter >= player->frame_counter_sleeping)
6611 player->is_sleeping = TRUE;
6612 else if (game.player_boring_delay_fixed +
6613 game.player_boring_delay_random > 0 &&
6614 FrameCounter >= player->frame_counter_bored)
6615 player->is_bored = TRUE;
6617 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6618 player->is_bored ? ACTION_BORING :
6621 if (player->is_sleeping)
6623 if (player->num_special_action_sleeping > 0)
6625 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6627 int last_special_action = player->special_action_sleeping;
6628 int num_special_action = player->num_special_action_sleeping;
6629 int special_action =
6630 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6631 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6632 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6633 last_special_action + 1 : ACTION_SLEEPING);
6634 int special_graphic =
6635 el_act_dir2img(player->element_nr, special_action, move_dir);
6637 player->anim_delay_counter =
6638 graphic_info[special_graphic].anim_delay_fixed +
6639 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6640 player->post_delay_counter =
6641 graphic_info[special_graphic].post_delay_fixed +
6642 SimpleRND(graphic_info[special_graphic].post_delay_random);
6644 player->special_action_sleeping = special_action;
6647 if (player->anim_delay_counter > 0)
6649 player->action_waiting = player->special_action_sleeping;
6650 player->anim_delay_counter--;
6652 else if (player->post_delay_counter > 0)
6654 player->post_delay_counter--;
6658 else if (player->is_bored)
6660 if (player->num_special_action_bored > 0)
6662 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6664 int special_action =
6665 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6666 int special_graphic =
6667 el_act_dir2img(player->element_nr, special_action, move_dir);
6669 player->anim_delay_counter =
6670 graphic_info[special_graphic].anim_delay_fixed +
6671 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6672 player->post_delay_counter =
6673 graphic_info[special_graphic].post_delay_fixed +
6674 SimpleRND(graphic_info[special_graphic].post_delay_random);
6676 player->special_action_bored = special_action;
6679 if (player->anim_delay_counter > 0)
6681 player->action_waiting = player->special_action_bored;
6682 player->anim_delay_counter--;
6684 else if (player->post_delay_counter > 0)
6686 player->post_delay_counter--;
6691 else if (last_waiting) /* waiting -> not waiting */
6693 player->is_waiting = FALSE;
6694 player->is_bored = FALSE;
6695 player->is_sleeping = FALSE;
6697 player->frame_counter_bored = -1;
6698 player->frame_counter_sleeping = -1;
6700 player->anim_delay_counter = 0;
6701 player->post_delay_counter = 0;
6703 player->action_waiting = ACTION_DEFAULT;
6705 player->special_action_bored = ACTION_DEFAULT;
6706 player->special_action_sleeping = ACTION_DEFAULT;
6711 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6714 static byte stored_player_action[MAX_PLAYERS];
6715 static int num_stored_actions = 0;
6717 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6718 int left = player_action & JOY_LEFT;
6719 int right = player_action & JOY_RIGHT;
6720 int up = player_action & JOY_UP;
6721 int down = player_action & JOY_DOWN;
6722 int button1 = player_action & JOY_BUTTON_1;
6723 int button2 = player_action & JOY_BUTTON_2;
6724 int dx = (left ? -1 : right ? 1 : 0);
6725 int dy = (up ? -1 : down ? 1 : 0);
6728 stored_player_action[player->index_nr] = 0;
6729 num_stored_actions++;
6733 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6736 if (!player->active || tape.pausing)
6740 printf("::: [%d %d %d %d] [%d %d]\n",
6741 left, right, up, down, button1, button2);
6747 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6751 snapped = SnapField(player, dx, dy);
6755 dropped = DropElement(player);
6757 moved = MovePlayer(player, dx, dy);
6760 if (tape.single_step && tape.recording && !tape.pausing)
6762 if (button1 || (dropped && !moved))
6764 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6765 SnapField(player, 0, 0); /* stop snapping */
6769 SetPlayerWaiting(player, FALSE);
6772 return player_action;
6774 stored_player_action[player->index_nr] = player_action;
6780 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6783 /* no actions for this player (no input at player's configured device) */
6785 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6786 SnapField(player, 0, 0);
6787 CheckGravityMovement(player);
6789 if (player->MovPos == 0)
6790 SetPlayerWaiting(player, TRUE);
6792 if (player->MovPos == 0) /* needed for tape.playing */
6793 player->is_moving = FALSE;
6795 player->is_dropping = FALSE;
6801 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6803 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6805 TapeRecordAction(stored_player_action);
6806 num_stored_actions = 0;
6813 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6815 static byte stored_player_action[MAX_PLAYERS];
6816 static int num_stored_actions = 0;
6817 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6818 int left = player_action & JOY_LEFT;
6819 int right = player_action & JOY_RIGHT;
6820 int up = player_action & JOY_UP;
6821 int down = player_action & JOY_DOWN;
6822 int button1 = player_action & JOY_BUTTON_1;
6823 int button2 = player_action & JOY_BUTTON_2;
6824 int dx = (left ? -1 : right ? 1 : 0);
6825 int dy = (up ? -1 : down ? 1 : 0);
6827 stored_player_action[player->index_nr] = 0;
6828 num_stored_actions++;
6830 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6832 if (!player->active || tape.pausing)
6837 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6840 snapped = SnapField(player, dx, dy);
6844 dropped = DropElement(player);
6846 moved = MovePlayer(player, dx, dy);
6849 if (tape.single_step && tape.recording && !tape.pausing)
6851 if (button1 || (dropped && !moved))
6853 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6854 SnapField(player, 0, 0); /* stop snapping */
6858 stored_player_action[player->index_nr] = player_action;
6862 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6864 /* no actions for this player (no input at player's configured device) */
6866 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6867 SnapField(player, 0, 0);
6868 CheckGravityMovement(player);
6870 if (player->MovPos == 0)
6871 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6873 if (player->MovPos == 0) /* needed for tape.playing */
6874 player->is_moving = FALSE;
6877 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6879 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6881 TapeRecordAction(stored_player_action);
6882 num_stored_actions = 0;
6889 static unsigned long action_delay = 0;
6890 unsigned long action_delay_value;
6891 int magic_wall_x = 0, magic_wall_y = 0;
6892 int i, x, y, element, graphic;
6893 byte *recorded_player_action;
6894 byte summarized_player_action = 0;
6896 byte tape_action[MAX_PLAYERS];
6899 if (game_status != GAME_MODE_PLAYING)
6902 action_delay_value =
6903 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6905 if (tape.playing && tape.index_search && !tape.pausing)
6906 action_delay_value = 0;
6908 /* ---------- main game synchronization point ---------- */
6910 WaitUntilDelayReached(&action_delay, action_delay_value);
6912 if (network_playing && !network_player_action_received)
6916 printf("DEBUG: try to get network player actions in time\n");
6920 #if defined(PLATFORM_UNIX)
6921 /* last chance to get network player actions without main loop delay */
6925 if (game_status != GAME_MODE_PLAYING)
6928 if (!network_player_action_received)
6932 printf("DEBUG: failed to get network player actions in time\n");
6943 printf("::: getting new tape action [%d]\n", FrameCounter);
6946 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6948 for (i = 0; i < MAX_PLAYERS; i++)
6950 summarized_player_action |= stored_player[i].action;
6952 if (!network_playing)
6953 stored_player[i].effective_action = stored_player[i].action;
6956 #if defined(PLATFORM_UNIX)
6957 if (network_playing)
6958 SendToServer_MovePlayer(summarized_player_action);
6961 if (!options.network && !setup.team_mode)
6962 local_player->effective_action = summarized_player_action;
6964 for (i = 0; i < MAX_PLAYERS; i++)
6966 int actual_player_action = stored_player[i].effective_action;
6968 if (stored_player[i].programmed_action)
6969 actual_player_action = stored_player[i].programmed_action;
6971 if (recorded_player_action)
6972 actual_player_action = recorded_player_action[i];
6974 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6976 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6977 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6979 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6984 TapeRecordAction(tape_action);
6987 network_player_action_received = FALSE;
6989 ScrollScreen(NULL, SCROLL_GO_ON);
6995 for (i = 0; i < MAX_PLAYERS; i++)
6996 stored_player[i].Frame++;
7000 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7002 for (i = 0; i < MAX_PLAYERS; i++)
7004 struct PlayerInfo *player = &stored_player[i];
7008 if (player->active && player->is_pushing && player->is_moving &&
7011 ContinueMoving(x, y);
7013 /* continue moving after pushing (this is actually a bug) */
7014 if (!IS_MOVING(x, y))
7023 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7025 Changed[x][y] = CE_BITMASK_DEFAULT;
7026 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7029 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7031 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7032 printf("GameActions(): This should never happen!\n");
7034 ChangePage[x][y] = -1;
7039 if (WasJustMoving[x][y] > 0)
7040 WasJustMoving[x][y]--;
7041 if (WasJustFalling[x][y] > 0)
7042 WasJustFalling[x][y]--;
7047 /* reset finished pushing action (not done in ContinueMoving() to allow
7048 continous pushing animation for elements with zero push delay) */
7049 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7051 ResetGfxAnimation(x, y);
7052 DrawLevelField(x, y);
7057 if (IS_BLOCKED(x, y))
7061 Blocked2Moving(x, y, &oldx, &oldy);
7062 if (!IS_MOVING(oldx, oldy))
7064 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7065 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7066 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7067 printf("GameActions(): This should never happen!\n");
7073 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7075 element = Feld[x][y];
7077 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7079 graphic = el2img(element);
7085 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7087 element = graphic = 0;
7091 if (graphic_info[graphic].anim_global_sync)
7092 GfxFrame[x][y] = FrameCounter;
7094 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7095 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7096 ResetRandomAnimationValue(x, y);
7098 SetRandomAnimationValue(x, y);
7101 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7104 if (IS_INACTIVE(element))
7106 if (IS_ANIMATED(graphic))
7107 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7113 /* this may take place after moving, so 'element' may have changed */
7115 if (IS_CHANGING(x, y))
7117 if (IS_CHANGING(x, y) &&
7118 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7122 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7123 element_info[element].event_page_nr[CE_DELAY]);
7125 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7128 element = Feld[x][y];
7129 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7133 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7138 element = Feld[x][y];
7139 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7141 if (element == EL_MOLE)
7142 printf("::: %d, %d, %d [%d]\n",
7143 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7147 if (element == EL_YAMYAM)
7148 printf("::: %d, %d, %d\n",
7149 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7153 if (IS_ANIMATED(graphic) &&
7157 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7160 if (element == EL_BUG)
7161 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7165 if (element == EL_MOLE)
7166 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7170 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7171 EdelsteinFunkeln(x, y);
7173 else if ((element == EL_ACID ||
7174 element == EL_EXIT_OPEN ||
7175 element == EL_SP_EXIT_OPEN ||
7176 element == EL_SP_TERMINAL ||
7177 element == EL_SP_TERMINAL_ACTIVE ||
7178 element == EL_EXTRA_TIME ||
7179 element == EL_SHIELD_NORMAL ||
7180 element == EL_SHIELD_DEADLY) &&
7181 IS_ANIMATED(graphic))
7182 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7183 else if (IS_MOVING(x, y))
7184 ContinueMoving(x, y);
7185 else if (IS_ACTIVE_BOMB(element))
7186 CheckDynamite(x, y);
7188 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7189 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7191 else if (element == EL_AMOEBA_GROWING)
7192 AmoebeWaechst(x, y);
7193 else if (element == EL_AMOEBA_SHRINKING)
7194 AmoebaDisappearing(x, y);
7196 #if !USE_NEW_AMOEBA_CODE
7197 else if (IS_AMOEBALIVE(element))
7198 AmoebeAbleger(x, y);
7201 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7203 else if (element == EL_EXIT_CLOSED)
7205 else if (element == EL_SP_EXIT_CLOSED)
7207 else if (element == EL_EXPANDABLE_WALL_GROWING)
7209 else if (element == EL_EXPANDABLE_WALL ||
7210 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7211 element == EL_EXPANDABLE_WALL_VERTICAL ||
7212 element == EL_EXPANDABLE_WALL_ANY)
7214 else if (element == EL_FLAMES)
7215 CheckForDragon(x, y);
7217 else if (IS_AUTO_CHANGING(element))
7218 ChangeElement(x, y);
7220 else if (element == EL_EXPLOSION)
7221 ; /* drawing of correct explosion animation is handled separately */
7222 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7223 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7226 /* this may take place after moving, so 'element' may have changed */
7227 if (IS_AUTO_CHANGING(Feld[x][y]))
7228 ChangeElement(x, y);
7231 if (IS_BELT_ACTIVE(element))
7232 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7234 if (game.magic_wall_active)
7236 int jx = local_player->jx, jy = local_player->jy;
7238 /* play the element sound at the position nearest to the player */
7239 if ((element == EL_MAGIC_WALL_FULL ||
7240 element == EL_MAGIC_WALL_ACTIVE ||
7241 element == EL_MAGIC_WALL_EMPTYING ||
7242 element == EL_BD_MAGIC_WALL_FULL ||
7243 element == EL_BD_MAGIC_WALL_ACTIVE ||
7244 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7245 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7253 #if USE_NEW_AMOEBA_CODE
7254 /* new experimental amoeba growth stuff */
7256 if (!(FrameCounter % 8))
7259 static unsigned long random = 1684108901;
7261 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7264 x = (random >> 10) % lev_fieldx;
7265 y = (random >> 20) % lev_fieldy;
7267 x = RND(lev_fieldx);
7268 y = RND(lev_fieldy);
7270 element = Feld[x][y];
7272 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7273 if (!IS_PLAYER(x,y) &&
7274 (element == EL_EMPTY ||
7275 element == EL_SAND ||
7276 element == EL_QUICKSAND_EMPTY ||
7277 element == EL_ACID_SPLASH_LEFT ||
7278 element == EL_ACID_SPLASH_RIGHT))
7280 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7281 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7282 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7283 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7284 Feld[x][y] = EL_AMOEBA_DROP;
7287 random = random * 129 + 1;
7293 if (game.explosions_delayed)
7296 game.explosions_delayed = FALSE;
7298 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7300 element = Feld[x][y];
7302 if (ExplodeField[x][y])
7303 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7304 else if (element == EL_EXPLOSION)
7305 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7307 ExplodeField[x][y] = EX_NO_EXPLOSION;
7310 game.explosions_delayed = TRUE;
7313 if (game.magic_wall_active)
7315 if (!(game.magic_wall_time_left % 4))
7317 int element = Feld[magic_wall_x][magic_wall_y];
7319 if (element == EL_BD_MAGIC_WALL_FULL ||
7320 element == EL_BD_MAGIC_WALL_ACTIVE ||
7321 element == EL_BD_MAGIC_WALL_EMPTYING)
7322 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7324 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7327 if (game.magic_wall_time_left > 0)
7329 game.magic_wall_time_left--;
7330 if (!game.magic_wall_time_left)
7332 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7334 element = Feld[x][y];
7336 if (element == EL_MAGIC_WALL_ACTIVE ||
7337 element == EL_MAGIC_WALL_FULL)
7339 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7340 DrawLevelField(x, y);
7342 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7343 element == EL_BD_MAGIC_WALL_FULL)
7345 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7346 DrawLevelField(x, y);
7350 game.magic_wall_active = FALSE;
7355 if (game.light_time_left > 0)
7357 game.light_time_left--;
7359 if (game.light_time_left == 0)
7360 RedrawAllLightSwitchesAndInvisibleElements();
7363 if (game.timegate_time_left > 0)
7365 game.timegate_time_left--;
7367 if (game.timegate_time_left == 0)
7368 CloseAllOpenTimegates();
7371 for (i = 0; i < MAX_PLAYERS; i++)
7373 struct PlayerInfo *player = &stored_player[i];
7375 if (SHIELD_ON(player))
7377 if (player->shield_deadly_time_left)
7378 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7379 else if (player->shield_normal_time_left)
7380 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7384 if (TimeFrames >= FRAMES_PER_SECOND)
7389 for (i = 0; i < MAX_PLAYERS; i++)
7391 struct PlayerInfo *player = &stored_player[i];
7393 if (SHIELD_ON(player))
7395 player->shield_normal_time_left--;
7397 if (player->shield_deadly_time_left > 0)
7398 player->shield_deadly_time_left--;
7402 if (tape.recording || tape.playing)
7403 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7409 if (TimeLeft <= 10 && setup.time_limit)
7410 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7412 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7414 if (!TimeLeft && setup.time_limit)
7415 for (i = 0; i < MAX_PLAYERS; i++)
7416 KillHero(&stored_player[i]);
7418 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7419 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7423 PlayAllPlayersSound();
7425 if (options.debug) /* calculate frames per second */
7427 static unsigned long fps_counter = 0;
7428 static int fps_frames = 0;
7429 unsigned long fps_delay_ms = Counter() - fps_counter;
7433 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7435 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7438 fps_counter = Counter();
7441 redraw_mask |= REDRAW_FPS;
7445 if (stored_player[0].jx != stored_player[0].last_jx ||
7446 stored_player[0].jy != stored_player[0].last_jy)
7447 printf("::: %d, %d, %d, %d, %d\n",
7448 stored_player[0].MovDir,
7449 stored_player[0].MovPos,
7450 stored_player[0].GfxPos,
7451 stored_player[0].Frame,
7452 stored_player[0].StepFrame);
7459 for (i = 0; i < MAX_PLAYERS; i++)
7462 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7464 stored_player[i].Frame += move_frames;
7466 if (stored_player[i].MovPos != 0)
7467 stored_player[i].StepFrame += move_frames;
7469 if (stored_player[i].drop_delay > 0)
7470 stored_player[i].drop_delay--;
7475 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7477 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7479 local_player->show_envelope = 0;
7484 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7486 int min_x = x, min_y = y, max_x = x, max_y = y;
7489 for (i = 0; i < MAX_PLAYERS; i++)
7491 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7493 if (!stored_player[i].active || &stored_player[i] == player)
7496 min_x = MIN(min_x, jx);
7497 min_y = MIN(min_y, jy);
7498 max_x = MAX(max_x, jx);
7499 max_y = MAX(max_y, jy);
7502 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7505 static boolean AllPlayersInVisibleScreen()
7509 for (i = 0; i < MAX_PLAYERS; i++)
7511 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7513 if (!stored_player[i].active)
7516 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7523 void ScrollLevel(int dx, int dy)
7525 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7528 BlitBitmap(drawto_field, drawto_field,
7529 FX + TILEX * (dx == -1) - softscroll_offset,
7530 FY + TILEY * (dy == -1) - softscroll_offset,
7531 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7532 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7533 FX + TILEX * (dx == 1) - softscroll_offset,
7534 FY + TILEY * (dy == 1) - softscroll_offset);
7538 x = (dx == 1 ? BX1 : BX2);
7539 for (y = BY1; y <= BY2; y++)
7540 DrawScreenField(x, y);
7545 y = (dy == 1 ? BY1 : BY2);
7546 for (x = BX1; x <= BX2; x++)
7547 DrawScreenField(x, y);
7550 redraw_mask |= REDRAW_FIELD;
7553 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7555 int nextx = x + dx, nexty = y + dy;
7556 int element = Feld[x][y];
7559 element != EL_SP_PORT_LEFT &&
7560 element != EL_SP_GRAVITY_PORT_LEFT &&
7561 element != EL_SP_PORT_HORIZONTAL &&
7562 element != EL_SP_PORT_ANY) ||
7564 element != EL_SP_PORT_RIGHT &&
7565 element != EL_SP_GRAVITY_PORT_RIGHT &&
7566 element != EL_SP_PORT_HORIZONTAL &&
7567 element != EL_SP_PORT_ANY) ||
7569 element != EL_SP_PORT_UP &&
7570 element != EL_SP_GRAVITY_PORT_UP &&
7571 element != EL_SP_PORT_VERTICAL &&
7572 element != EL_SP_PORT_ANY) ||
7574 element != EL_SP_PORT_DOWN &&
7575 element != EL_SP_GRAVITY_PORT_DOWN &&
7576 element != EL_SP_PORT_VERTICAL &&
7577 element != EL_SP_PORT_ANY) ||
7578 !IN_LEV_FIELD(nextx, nexty) ||
7579 !IS_FREE(nextx, nexty))
7585 static void CheckGravityMovement(struct PlayerInfo *player)
7587 if (game.gravity && !player->programmed_action)
7589 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7590 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7592 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7593 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7594 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7595 int jx = player->jx, jy = player->jy;
7596 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7597 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7598 int new_jx = jx + dx, new_jy = jy + dy;
7599 boolean field_under_player_is_free =
7600 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7601 boolean player_is_moving_to_valid_field =
7602 (IN_LEV_FIELD(new_jx, new_jy) &&
7603 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7604 Feld[new_jx][new_jy] == EL_SAND ||
7605 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7606 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7607 /* !!! extend EL_SAND to anything diggable !!! */
7609 if (field_under_player_is_free &&
7610 !player_is_moving_to_valid_field &&
7611 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7612 player->programmed_action = MV_DOWN;
7618 -----------------------------------------------------------------------------
7619 dx, dy: direction (non-diagonal) to try to move the player to
7620 real_dx, real_dy: direction as read from input device (can be diagonal)
7623 boolean MovePlayerOneStep(struct PlayerInfo *player,
7624 int dx, int dy, int real_dx, int real_dy)
7627 static int change_sides[4][2] =
7629 /* enter side leave side */
7630 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7631 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7632 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7633 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7635 int move_direction = (dx == -1 ? MV_LEFT :
7636 dx == +1 ? MV_RIGHT :
7638 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7639 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7640 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7642 int jx = player->jx, jy = player->jy;
7643 int new_jx = jx + dx, new_jy = jy + dy;
7647 if (!player->active || (!dx && !dy))
7648 return MF_NO_ACTION;
7650 player->MovDir = (dx < 0 ? MV_LEFT :
7653 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7655 if (!IN_LEV_FIELD(new_jx, new_jy))
7656 return MF_NO_ACTION;
7658 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7659 return MF_NO_ACTION;
7662 element = MovingOrBlocked2Element(new_jx, new_jy);
7664 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7667 if (DONT_RUN_INTO(element))
7669 if (element == EL_ACID && dx == 0 && dy == 1)
7672 Feld[jx][jy] = EL_PLAYER_1;
7673 InitMovingField(jx, jy, MV_DOWN);
7674 Store[jx][jy] = EL_ACID;
7675 ContinueMoving(jx, jy);
7679 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7684 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7685 if (can_move != MF_MOVING)
7688 /* check if DigField() has caused relocation of the player */
7689 if (player->jx != jx || player->jy != jy)
7690 return MF_NO_ACTION;
7692 StorePlayer[jx][jy] = 0;
7693 player->last_jx = jx;
7694 player->last_jy = jy;
7695 player->jx = new_jx;
7696 player->jy = new_jy;
7697 StorePlayer[new_jx][new_jy] = player->element_nr;
7700 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7702 player->step_counter++;
7704 player->drop_delay = 0;
7706 PlayerVisit[jx][jy] = FrameCounter;
7708 ScrollPlayer(player, SCROLL_INIT);
7711 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7713 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7714 CE_OTHER_GETS_LEFT);
7715 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7716 CE_LEFT_BY_PLAYER, -1);
7719 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7721 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7722 enter_side, CE_OTHER_GETS_ENTERED);
7723 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7724 CE_ENTERED_BY_PLAYER, -1);
7731 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7733 int jx = player->jx, jy = player->jy;
7734 int old_jx = jx, old_jy = jy;
7735 int moved = MF_NO_ACTION;
7738 if (!player->active)
7743 if (player->MovPos == 0)
7745 player->is_moving = FALSE;
7746 player->is_digging = FALSE;
7747 player->is_collecting = FALSE;
7748 player->is_snapping = FALSE;
7749 player->is_pushing = FALSE;
7755 if (!player->active || (!dx && !dy))
7760 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7764 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7765 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7769 /* remove the last programmed player action */
7770 player->programmed_action = 0;
7774 /* should only happen if pre-1.2 tape recordings are played */
7775 /* this is only for backward compatibility */
7777 int original_move_delay_value = player->move_delay_value;
7780 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7784 /* scroll remaining steps with finest movement resolution */
7785 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7787 while (player->MovPos)
7789 ScrollPlayer(player, SCROLL_GO_ON);
7790 ScrollScreen(NULL, SCROLL_GO_ON);
7796 player->move_delay_value = original_move_delay_value;
7799 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7801 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7802 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7806 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7807 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7813 if (moved & MF_MOVING && !ScreenMovPos &&
7814 (player == local_player || !options.network))
7816 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7817 int offset = (setup.scroll_delay ? 3 : 0);
7819 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7821 /* actual player has left the screen -- scroll in that direction */
7822 if (jx != old_jx) /* player has moved horizontally */
7823 scroll_x += (jx - old_jx);
7824 else /* player has moved vertically */
7825 scroll_y += (jy - old_jy);
7829 if (jx != old_jx) /* player has moved horizontally */
7831 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7832 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7833 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7835 /* don't scroll over playfield boundaries */
7836 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7837 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7839 /* don't scroll more than one field at a time */
7840 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7842 /* don't scroll against the player's moving direction */
7843 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7844 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7845 scroll_x = old_scroll_x;
7847 else /* player has moved vertically */
7849 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7850 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7851 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7853 /* don't scroll over playfield boundaries */
7854 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7855 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7857 /* don't scroll more than one field at a time */
7858 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7860 /* don't scroll against the player's moving direction */
7861 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7862 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7863 scroll_y = old_scroll_y;
7867 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7869 if (!options.network && !AllPlayersInVisibleScreen())
7871 scroll_x = old_scroll_x;
7872 scroll_y = old_scroll_y;
7876 ScrollScreen(player, SCROLL_INIT);
7877 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7884 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7886 if (!(moved & MF_MOVING) && !player->is_pushing)
7891 player->StepFrame = 0;
7893 if (moved & MF_MOVING)
7895 if (old_jx != jx && old_jy == jy)
7896 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7897 else if (old_jx == jx && old_jy != jy)
7898 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7900 DrawLevelField(jx, jy); /* for "crumbled sand" */
7902 player->last_move_dir = player->MovDir;
7903 player->is_moving = TRUE;
7905 player->is_snapping = FALSE;
7909 player->is_switching = FALSE;
7912 player->is_dropping = FALSE;
7917 static int change_sides[4][2] =
7919 /* enter side leave side */
7920 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7921 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7922 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7923 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7925 int move_direction = player->MovDir;
7926 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7927 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7930 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7932 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7933 leave_side, CE_OTHER_GETS_LEFT);
7934 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7935 leave_side, CE_LEFT_BY_PLAYER, -1);
7938 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7940 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7941 enter_side, CE_OTHER_GETS_ENTERED);
7942 CheckElementSideChange(jx, jy, Feld[jx][jy],
7943 enter_side, CE_ENTERED_BY_PLAYER, -1);
7954 CheckGravityMovement(player);
7957 player->last_move_dir = MV_NO_MOVING;
7959 player->is_moving = FALSE;
7962 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7964 TestIfHeroTouchesBadThing(jx, jy);
7965 TestIfPlayerTouchesCustomElement(jx, jy);
7968 if (!player->active)
7974 void ScrollPlayer(struct PlayerInfo *player, int mode)
7976 int jx = player->jx, jy = player->jy;
7977 int last_jx = player->last_jx, last_jy = player->last_jy;
7978 int move_stepsize = TILEX / player->move_delay_value;
7980 if (!player->active || !player->MovPos)
7983 if (mode == SCROLL_INIT)
7985 player->actual_frame_counter = FrameCounter;
7986 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7988 if (Feld[last_jx][last_jy] == EL_EMPTY)
7989 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7996 else if (!FrameReached(&player->actual_frame_counter, 1))
7999 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8000 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8002 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8003 Feld[last_jx][last_jy] = EL_EMPTY;
8005 /* before DrawPlayer() to draw correct player graphic for this case */
8006 if (player->MovPos == 0)
8007 CheckGravityMovement(player);
8010 DrawPlayer(player); /* needed here only to cleanup last field */
8013 if (player->MovPos == 0) /* player reached destination field */
8016 if (player->move_delay_reset_counter > 0)
8018 player->move_delay_reset_counter--;
8020 if (player->move_delay_reset_counter == 0)
8022 /* continue with normal speed after quickly moving through gate */
8023 HALVE_PLAYER_SPEED(player);
8025 /* be able to make the next move without delay */
8026 player->move_delay = 0;
8030 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8032 /* continue with normal speed after quickly moving through gate */
8033 HALVE_PLAYER_SPEED(player);
8035 /* be able to make the next move without delay */
8036 player->move_delay = 0;
8040 player->last_jx = jx;
8041 player->last_jy = jy;
8043 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8044 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8045 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8047 DrawPlayer(player); /* needed here only to cleanup last field */
8050 if (local_player->friends_still_needed == 0 ||
8051 IS_SP_ELEMENT(Feld[jx][jy]))
8052 player->LevelSolved = player->GameOver = TRUE;
8055 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8057 TestIfHeroTouchesBadThing(jx, jy);
8058 TestIfPlayerTouchesCustomElement(jx, jy);
8060 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8063 if (!player->active)
8067 if (tape.single_step && tape.recording && !tape.pausing &&
8068 !player->programmed_action)
8069 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8073 void ScrollScreen(struct PlayerInfo *player, int mode)
8075 static unsigned long screen_frame_counter = 0;
8077 if (mode == SCROLL_INIT)
8079 /* set scrolling step size according to actual player's moving speed */
8080 ScrollStepSize = TILEX / player->move_delay_value;
8082 screen_frame_counter = FrameCounter;
8083 ScreenMovDir = player->MovDir;
8084 ScreenMovPos = player->MovPos;
8085 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8088 else if (!FrameReached(&screen_frame_counter, 1))
8093 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8094 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8095 redraw_mask |= REDRAW_FIELD;
8098 ScreenMovDir = MV_NO_MOVING;
8101 void TestIfPlayerTouchesCustomElement(int x, int y)
8103 static int xy[4][2] =
8110 static int change_sides[4][2] =
8112 /* center side border side */
8113 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8114 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8115 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8116 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8118 static int touch_dir[4] =
8125 int center_element = Feld[x][y]; /* should always be non-moving! */
8128 for (i = 0; i < 4; i++)
8130 int xx = x + xy[i][0];
8131 int yy = y + xy[i][1];
8132 int center_side = change_sides[i][0];
8133 int border_side = change_sides[i][1];
8136 if (!IN_LEV_FIELD(xx, yy))
8139 if (IS_PLAYER(x, y))
8141 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8142 border_element = Feld[xx][yy]; /* may be moving! */
8143 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8144 border_element = Feld[xx][yy];
8145 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8146 border_element = MovingOrBlocked2Element(xx, yy);
8148 continue; /* center and border element do not touch */
8150 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8151 CE_OTHER_GETS_TOUCHED);
8152 CheckElementSideChange(xx, yy, border_element, border_side,
8153 CE_TOUCHED_BY_PLAYER, -1);
8155 else if (IS_PLAYER(xx, yy))
8157 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8159 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8161 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8162 continue; /* center and border element do not touch */
8165 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8166 CE_OTHER_GETS_TOUCHED);
8167 CheckElementSideChange(x, y, center_element, center_side,
8168 CE_TOUCHED_BY_PLAYER, -1);
8175 void TestIfElementTouchesCustomElement(int x, int y)
8177 static int xy[4][2] =
8184 static int change_sides[4][2] =
8186 /* center side border side */
8187 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8188 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8189 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8190 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8192 static int touch_dir[4] =
8199 boolean change_center_element = FALSE;
8200 int center_element_change_page = 0;
8201 int center_element = Feld[x][y]; /* should always be non-moving! */
8204 for (i = 0; i < 4; i++)
8206 int xx = x + xy[i][0];
8207 int yy = y + xy[i][1];
8208 int center_side = change_sides[i][0];
8209 int border_side = change_sides[i][1];
8212 if (!IN_LEV_FIELD(xx, yy))
8215 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8216 border_element = Feld[xx][yy]; /* may be moving! */
8217 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8218 border_element = Feld[xx][yy];
8219 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8220 border_element = MovingOrBlocked2Element(xx, yy);
8222 continue; /* center and border element do not touch */
8224 /* check for change of center element (but change it only once) */
8225 if (IS_CUSTOM_ELEMENT(center_element) &&
8226 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8227 !change_center_element)
8229 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8231 struct ElementChangeInfo *change =
8232 &element_info[center_element].change_page[j];
8234 if (change->can_change &&
8235 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8236 change->sides & border_side &&
8238 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8240 change->trigger_element == border_element
8244 change_center_element = TRUE;
8245 center_element_change_page = j;
8252 /* check for change of border element */
8253 if (IS_CUSTOM_ELEMENT(border_element) &&
8254 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8256 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8258 struct ElementChangeInfo *change =
8259 &element_info[border_element].change_page[j];
8261 if (change->can_change &&
8262 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8263 change->sides & center_side &&
8265 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8267 change->trigger_element == center_element
8271 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8272 CE_OTHER_IS_TOUCHING, j);
8279 if (change_center_element)
8280 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8281 CE_OTHER_IS_TOUCHING, center_element_change_page);
8284 void TestIfElementHitsCustomElement(int x, int y, int direction)
8286 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8287 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8288 int hitx = x + dx, hity = y + dy;
8289 int hitting_element = Feld[x][y];
8291 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8292 !IS_FREE(hitx, hity) &&
8293 (!IS_MOVING(hitx, hity) ||
8294 MovDir[hitx][hity] != direction ||
8295 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8298 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8302 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8306 CheckElementSideChange(x, y, hitting_element,
8307 direction, CE_HITTING_SOMETHING, -1);
8309 if (IN_LEV_FIELD(hitx, hity))
8311 static int opposite_directions[] =
8318 int move_dir_bit = MV_DIR_BIT(direction);
8319 int opposite_direction = opposite_directions[move_dir_bit];
8320 int hitting_side = direction;
8321 int touched_side = opposite_direction;
8322 int touched_element = MovingOrBlocked2Element(hitx, hity);
8324 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8325 MovDir[hitx][hity] != direction ||
8326 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8335 CheckElementSideChange(hitx, hity, touched_element,
8336 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8338 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8339 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8341 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8343 struct ElementChangeInfo *change =
8344 &element_info[hitting_element].change_page[i];
8346 if (change->can_change &&
8347 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8348 change->sides & touched_side &&
8351 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8353 change->trigger_element == touched_element
8357 CheckElementSideChange(x, y, hitting_element,
8358 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8364 if (IS_CUSTOM_ELEMENT(touched_element) &&
8365 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8367 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8369 struct ElementChangeInfo *change =
8370 &element_info[touched_element].change_page[i];
8372 if (change->can_change &&
8373 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8374 change->sides & hitting_side &&
8376 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8378 change->trigger_element == hitting_element
8382 CheckElementSideChange(hitx, hity, touched_element,
8383 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8392 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8394 int i, kill_x = -1, kill_y = -1;
8395 static int test_xy[4][2] =
8402 static int test_dir[4] =
8410 for (i = 0; i < 4; i++)
8412 int test_x, test_y, test_move_dir, test_element;
8414 test_x = good_x + test_xy[i][0];
8415 test_y = good_y + test_xy[i][1];
8416 if (!IN_LEV_FIELD(test_x, test_y))
8420 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8423 test_element = Feld[test_x][test_y];
8425 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8428 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8429 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8431 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8432 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8440 if (kill_x != -1 || kill_y != -1)
8442 if (IS_PLAYER(good_x, good_y))
8444 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8446 if (player->shield_deadly_time_left > 0)
8447 Bang(kill_x, kill_y);
8448 else if (!PLAYER_PROTECTED(good_x, good_y))
8452 Bang(good_x, good_y);
8456 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8458 int i, kill_x = -1, kill_y = -1;
8459 int bad_element = Feld[bad_x][bad_y];
8460 static int test_xy[4][2] =
8467 static int touch_dir[4] =
8474 static int test_dir[4] =
8482 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8485 for (i = 0; i < 4; i++)
8487 int test_x, test_y, test_move_dir, test_element;
8489 test_x = bad_x + test_xy[i][0];
8490 test_y = bad_y + test_xy[i][1];
8491 if (!IN_LEV_FIELD(test_x, test_y))
8495 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8497 test_element = Feld[test_x][test_y];
8499 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8500 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8502 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8503 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8505 /* good thing is player or penguin that does not move away */
8506 if (IS_PLAYER(test_x, test_y))
8508 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8510 if (bad_element == EL_ROBOT && player->is_moving)
8511 continue; /* robot does not kill player if he is moving */
8513 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8515 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8516 continue; /* center and border element do not touch */
8523 else if (test_element == EL_PENGUIN)
8532 if (kill_x != -1 || kill_y != -1)
8534 if (IS_PLAYER(kill_x, kill_y))
8536 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8538 if (player->shield_deadly_time_left > 0)
8540 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8544 Bang(kill_x, kill_y);
8548 void TestIfHeroTouchesBadThing(int x, int y)
8550 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8553 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8555 TestIfGoodThingHitsBadThing(x, y, move_dir);
8558 void TestIfBadThingTouchesHero(int x, int y)
8560 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8563 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8565 TestIfBadThingHitsGoodThing(x, y, move_dir);
8568 void TestIfFriendTouchesBadThing(int x, int y)
8570 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8573 void TestIfBadThingTouchesFriend(int x, int y)
8575 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8578 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8580 int i, kill_x = bad_x, kill_y = bad_y;
8581 static int xy[4][2] =
8589 for (i = 0; i < 4; i++)
8593 x = bad_x + xy[i][0];
8594 y = bad_y + xy[i][1];
8595 if (!IN_LEV_FIELD(x, y))
8598 element = Feld[x][y];
8599 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8600 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8608 if (kill_x != bad_x || kill_y != bad_y)
8612 void KillHero(struct PlayerInfo *player)
8614 int jx = player->jx, jy = player->jy;
8616 if (!player->active)
8619 /* remove accessible field at the player's position */
8620 Feld[jx][jy] = EL_EMPTY;
8622 /* deactivate shield (else Bang()/Explode() would not work right) */
8623 player->shield_normal_time_left = 0;
8624 player->shield_deadly_time_left = 0;
8630 static void KillHeroUnlessProtected(int x, int y)
8632 if (!PLAYER_PROTECTED(x, y))
8633 KillHero(PLAYERINFO(x, y));
8636 void BuryHero(struct PlayerInfo *player)
8638 int jx = player->jx, jy = player->jy;
8640 if (!player->active)
8644 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8646 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8648 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8650 player->GameOver = TRUE;
8654 void RemoveHero(struct PlayerInfo *player)
8656 int jx = player->jx, jy = player->jy;
8657 int i, found = FALSE;
8659 player->present = FALSE;
8660 player->active = FALSE;
8662 if (!ExplodeField[jx][jy])
8663 StorePlayer[jx][jy] = 0;
8665 for (i = 0; i < MAX_PLAYERS; i++)
8666 if (stored_player[i].active)
8670 AllPlayersGone = TRUE;
8677 =============================================================================
8678 checkDiagonalPushing()
8679 -----------------------------------------------------------------------------
8680 check if diagonal input device direction results in pushing of object
8681 (by checking if the alternative direction is walkable, diggable, ...)
8682 =============================================================================
8685 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8686 int x, int y, int real_dx, int real_dy)
8688 int jx, jy, dx, dy, xx, yy;
8690 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8693 /* diagonal direction: check alternative direction */
8698 xx = jx + (dx == 0 ? real_dx : 0);
8699 yy = jy + (dy == 0 ? real_dy : 0);
8701 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8705 =============================================================================
8707 -----------------------------------------------------------------------------
8708 x, y: field next to player (non-diagonal) to try to dig to
8709 real_dx, real_dy: direction as read from input device (can be diagonal)
8710 =============================================================================
8713 int DigField(struct PlayerInfo *player,
8714 int x, int y, int real_dx, int real_dy, int mode)
8716 static int change_sides[4] =
8718 CH_SIDE_RIGHT, /* moving left */
8719 CH_SIDE_LEFT, /* moving right */
8720 CH_SIDE_BOTTOM, /* moving up */
8721 CH_SIDE_TOP, /* moving down */
8723 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8724 int jx = player->jx, jy = player->jy;
8725 int dx = x - jx, dy = y - jy;
8726 int nextx = x + dx, nexty = y + dy;
8727 int move_direction = (dx == -1 ? MV_LEFT :
8728 dx == +1 ? MV_RIGHT :
8730 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8731 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8734 if (player->MovPos == 0)
8736 player->is_digging = FALSE;
8737 player->is_collecting = FALSE;
8740 if (player->MovPos == 0) /* last pushing move finished */
8741 player->is_pushing = FALSE;
8743 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8745 player->is_switching = FALSE;
8746 player->push_delay = 0;
8748 return MF_NO_ACTION;
8751 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8752 return MF_NO_ACTION;
8755 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8757 if (IS_TUBE(Feld[jx][jy]) ||
8758 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8762 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8763 int tube_leave_directions[][2] =
8765 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8766 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8767 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8768 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8769 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8770 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8771 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8772 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8773 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8774 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8775 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8776 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8779 while (tube_leave_directions[i][0] != tube_element)
8782 if (tube_leave_directions[i][0] == -1) /* should not happen */
8786 if (!(tube_leave_directions[i][1] & move_direction))
8787 return MF_NO_ACTION; /* tube has no opening in this direction */
8790 element = Feld[x][y];
8792 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8793 game.engine_version >= VERSION_IDENT(2,2,0,0))
8794 return MF_NO_ACTION;
8798 case EL_SP_PORT_LEFT:
8799 case EL_SP_PORT_RIGHT:
8801 case EL_SP_PORT_DOWN:
8802 case EL_SP_PORT_HORIZONTAL:
8803 case EL_SP_PORT_VERTICAL:
8804 case EL_SP_PORT_ANY:
8805 case EL_SP_GRAVITY_PORT_LEFT:
8806 case EL_SP_GRAVITY_PORT_RIGHT:
8807 case EL_SP_GRAVITY_PORT_UP:
8808 case EL_SP_GRAVITY_PORT_DOWN:
8810 if (!canEnterSupaplexPort(x, y, dx, dy))
8811 return MF_NO_ACTION;
8814 element != EL_SP_PORT_LEFT &&
8815 element != EL_SP_GRAVITY_PORT_LEFT &&
8816 element != EL_SP_PORT_HORIZONTAL &&
8817 element != EL_SP_PORT_ANY) ||
8819 element != EL_SP_PORT_RIGHT &&
8820 element != EL_SP_GRAVITY_PORT_RIGHT &&
8821 element != EL_SP_PORT_HORIZONTAL &&
8822 element != EL_SP_PORT_ANY) ||
8824 element != EL_SP_PORT_UP &&
8825 element != EL_SP_GRAVITY_PORT_UP &&
8826 element != EL_SP_PORT_VERTICAL &&
8827 element != EL_SP_PORT_ANY) ||
8829 element != EL_SP_PORT_DOWN &&
8830 element != EL_SP_GRAVITY_PORT_DOWN &&
8831 element != EL_SP_PORT_VERTICAL &&
8832 element != EL_SP_PORT_ANY) ||
8833 !IN_LEV_FIELD(nextx, nexty) ||
8834 !IS_FREE(nextx, nexty))
8835 return MF_NO_ACTION;
8838 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8839 element == EL_SP_GRAVITY_PORT_RIGHT ||
8840 element == EL_SP_GRAVITY_PORT_UP ||
8841 element == EL_SP_GRAVITY_PORT_DOWN)
8842 game.gravity = !game.gravity;
8844 /* automatically move to the next field with double speed */
8845 player->programmed_action = move_direction;
8847 if (player->move_delay_reset_counter == 0)
8849 player->move_delay_reset_counter = 2; /* two double speed steps */
8851 DOUBLE_PLAYER_SPEED(player);
8854 player->move_delay_reset_counter = 2;
8856 DOUBLE_PLAYER_SPEED(player);
8859 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8863 case EL_TUBE_VERTICAL:
8864 case EL_TUBE_HORIZONTAL:
8865 case EL_TUBE_VERTICAL_LEFT:
8866 case EL_TUBE_VERTICAL_RIGHT:
8867 case EL_TUBE_HORIZONTAL_UP:
8868 case EL_TUBE_HORIZONTAL_DOWN:
8869 case EL_TUBE_LEFT_UP:
8870 case EL_TUBE_LEFT_DOWN:
8871 case EL_TUBE_RIGHT_UP:
8872 case EL_TUBE_RIGHT_DOWN:
8875 int tube_enter_directions[][2] =
8877 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8878 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8879 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8880 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8881 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8882 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8883 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8884 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8885 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8886 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8887 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8888 { -1, MV_NO_MOVING }
8891 while (tube_enter_directions[i][0] != element)
8894 if (tube_enter_directions[i][0] == -1) /* should not happen */
8898 if (!(tube_enter_directions[i][1] & move_direction))
8899 return MF_NO_ACTION; /* tube has no opening in this direction */
8901 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8907 if (IS_WALKABLE(element))
8909 int sound_action = ACTION_WALKING;
8911 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8913 if (!player->key[element - EL_GATE_1])
8914 return MF_NO_ACTION;
8916 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8918 if (!player->key[element - EL_GATE_1_GRAY])
8919 return MF_NO_ACTION;
8921 else if (element == EL_EXIT_OPEN ||
8922 element == EL_SP_EXIT_OPEN ||
8923 element == EL_SP_EXIT_OPENING)
8925 sound_action = ACTION_PASSING; /* player is passing exit */
8927 else if (element == EL_EMPTY)
8929 sound_action = ACTION_MOVING; /* nothing to walk on */
8932 /* play sound from background or player, whatever is available */
8933 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8934 PlayLevelSoundElementAction(x, y, element, sound_action);
8936 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8940 else if (IS_PASSABLE(element))
8942 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8943 return MF_NO_ACTION;
8946 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8947 return MF_NO_ACTION;
8950 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8952 if (!player->key[element - EL_EM_GATE_1])
8953 return MF_NO_ACTION;
8955 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8957 if (!player->key[element - EL_EM_GATE_1_GRAY])
8958 return MF_NO_ACTION;
8961 /* automatically move to the next field with double speed */
8962 player->programmed_action = move_direction;
8964 if (player->move_delay_reset_counter == 0)
8966 player->move_delay_reset_counter = 2; /* two double speed steps */
8968 DOUBLE_PLAYER_SPEED(player);
8971 player->move_delay_reset_counter = 2;
8973 DOUBLE_PLAYER_SPEED(player);
8976 PlayLevelSoundAction(x, y, ACTION_PASSING);
8980 else if (IS_DIGGABLE(element))
8984 if (mode != DF_SNAP)
8987 GfxElement[x][y] = GFX_ELEMENT(element);
8990 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8992 player->is_digging = TRUE;
8995 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8997 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9000 if (mode == DF_SNAP)
9001 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9006 else if (IS_COLLECTIBLE(element))
9010 if (mode != DF_SNAP)
9012 GfxElement[x][y] = element;
9013 player->is_collecting = TRUE;
9016 if (element == EL_SPEED_PILL)
9017 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9018 else if (element == EL_EXTRA_TIME && level.time > 0)
9021 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9023 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9025 player->shield_normal_time_left += 10;
9026 if (element == EL_SHIELD_DEADLY)
9027 player->shield_deadly_time_left += 10;
9029 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9031 if (player->inventory_size < MAX_INVENTORY_SIZE)
9032 player->inventory_element[player->inventory_size++] = element;
9034 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9035 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9037 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9039 player->dynabomb_count++;
9040 player->dynabombs_left++;
9042 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9044 player->dynabomb_size++;
9046 else if (element == EL_DYNABOMB_INCREASE_POWER)
9048 player->dynabomb_xl = TRUE;
9050 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9051 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9053 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9054 element - EL_KEY_1 : element - EL_EM_KEY_1);
9056 player->key[key_nr] = TRUE;
9058 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9059 el2edimg(EL_KEY_1 + key_nr));
9060 redraw_mask |= REDRAW_DOOR_1;
9062 else if (IS_ENVELOPE(element))
9065 player->show_envelope = element;
9067 ShowEnvelope(element - EL_ENVELOPE_1);
9070 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9074 for (i = 0; i < element_info[element].collect_count; i++)
9075 if (player->inventory_size < MAX_INVENTORY_SIZE)
9076 player->inventory_element[player->inventory_size++] = element;
9078 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9079 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9081 else if (element_info[element].collect_count > 0)
9083 local_player->gems_still_needed -=
9084 element_info[element].collect_count;
9085 if (local_player->gems_still_needed < 0)
9086 local_player->gems_still_needed = 0;
9088 DrawText(DX_EMERALDS, DY_EMERALDS,
9089 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9092 RaiseScoreElement(element);
9093 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9095 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9098 if (mode == DF_SNAP)
9099 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9104 else if (IS_PUSHABLE(element))
9106 if (mode == DF_SNAP && element != EL_BD_ROCK)
9107 return MF_NO_ACTION;
9109 if (CAN_FALL(element) && dy)
9110 return MF_NO_ACTION;
9112 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9113 !(element == EL_SPRING && use_spring_bug))
9114 return MF_NO_ACTION;
9117 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9118 ((move_direction & MV_VERTICAL &&
9119 ((element_info[element].move_pattern & MV_LEFT &&
9120 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9121 (element_info[element].move_pattern & MV_RIGHT &&
9122 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9123 (move_direction & MV_HORIZONTAL &&
9124 ((element_info[element].move_pattern & MV_UP &&
9125 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9126 (element_info[element].move_pattern & MV_DOWN &&
9127 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9128 return MF_NO_ACTION;
9132 /* do not push elements already moving away faster than player */
9133 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9134 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9135 return MF_NO_ACTION;
9137 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9138 return MF_NO_ACTION;
9142 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9144 if (player->push_delay_value == -1)
9145 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9147 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9149 if (!player->is_pushing)
9150 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9154 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9155 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9156 !player_is_pushing))
9157 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9160 if (!player->is_pushing &&
9161 game.engine_version >= VERSION_IDENT(2,2,0,7))
9162 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9166 printf("::: push delay: %ld [%d, %d] [%d]\n",
9167 player->push_delay_value, FrameCounter, game.engine_version,
9168 player->is_pushing);
9171 player->is_pushing = TRUE;
9173 if (!(IN_LEV_FIELD(nextx, nexty) &&
9174 (IS_FREE(nextx, nexty) ||
9175 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9176 IS_SB_ELEMENT(element)))))
9177 return MF_NO_ACTION;
9179 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9180 return MF_NO_ACTION;
9182 if (player->push_delay == 0) /* new pushing; restart delay */
9183 player->push_delay = FrameCounter;
9185 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9186 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9187 element != EL_SPRING && element != EL_BALLOON)
9189 /* make sure that there is no move delay before next try to push */
9190 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9191 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9193 return MF_NO_ACTION;
9197 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9200 if (IS_SB_ELEMENT(element))
9202 if (element == EL_SOKOBAN_FIELD_FULL)
9204 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9205 local_player->sokobanfields_still_needed++;
9208 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9210 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9211 local_player->sokobanfields_still_needed--;
9214 Feld[x][y] = EL_SOKOBAN_OBJECT;
9216 if (Back[x][y] == Back[nextx][nexty])
9217 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9218 else if (Back[x][y] != 0)
9219 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9222 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9225 if (local_player->sokobanfields_still_needed == 0 &&
9226 game.emulation == EMU_SOKOBAN)
9228 player->LevelSolved = player->GameOver = TRUE;
9229 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9233 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9235 InitMovingField(x, y, move_direction);
9236 GfxAction[x][y] = ACTION_PUSHING;
9238 if (mode == DF_SNAP)
9239 ContinueMoving(x, y);
9241 MovPos[x][y] = (dx != 0 ? dx : dy);
9243 Pushed[x][y] = TRUE;
9244 Pushed[nextx][nexty] = TRUE;
9246 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9247 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9249 player->push_delay_value = -1; /* get new value later */
9251 CheckTriggeredElementSideChange(x, y, element, dig_side,
9252 CE_OTHER_GETS_PUSHED);
9253 CheckElementSideChange(x, y, element, dig_side,
9254 CE_PUSHED_BY_PLAYER, -1);
9258 else if (IS_SWITCHABLE(element))
9260 if (PLAYER_SWITCHING(player, x, y))
9263 player->is_switching = TRUE;
9264 player->switch_x = x;
9265 player->switch_y = y;
9267 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9269 if (element == EL_ROBOT_WHEEL)
9271 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9275 DrawLevelField(x, y);
9277 else if (element == EL_SP_TERMINAL)
9281 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9283 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9285 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9286 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9289 else if (IS_BELT_SWITCH(element))
9291 ToggleBeltSwitch(x, y);
9293 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9294 element == EL_SWITCHGATE_SWITCH_DOWN)
9296 ToggleSwitchgateSwitch(x, y);
9298 else if (element == EL_LIGHT_SWITCH ||
9299 element == EL_LIGHT_SWITCH_ACTIVE)
9301 ToggleLightSwitch(x, y);
9304 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9305 SND_LIGHT_SWITCH_ACTIVATING :
9306 SND_LIGHT_SWITCH_DEACTIVATING);
9309 else if (element == EL_TIMEGATE_SWITCH)
9311 ActivateTimegateSwitch(x, y);
9313 else if (element == EL_BALLOON_SWITCH_LEFT ||
9314 element == EL_BALLOON_SWITCH_RIGHT ||
9315 element == EL_BALLOON_SWITCH_UP ||
9316 element == EL_BALLOON_SWITCH_DOWN ||
9317 element == EL_BALLOON_SWITCH_ANY)
9319 if (element == EL_BALLOON_SWITCH_ANY)
9320 game.balloon_dir = move_direction;
9322 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9323 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9324 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9325 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9328 else if (element == EL_LAMP)
9330 Feld[x][y] = EL_LAMP_ACTIVE;
9331 local_player->lights_still_needed--;
9333 DrawLevelField(x, y);
9335 else if (element == EL_TIME_ORB_FULL)
9337 Feld[x][y] = EL_TIME_ORB_EMPTY;
9339 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9341 DrawLevelField(x, y);
9344 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9352 if (!PLAYER_SWITCHING(player, x, y))
9354 player->is_switching = TRUE;
9355 player->switch_x = x;
9356 player->switch_y = y;
9358 CheckTriggeredElementSideChange(x, y, element, dig_side,
9359 CE_OTHER_IS_SWITCHING);
9360 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9363 CheckTriggeredElementSideChange(x, y, element, dig_side,
9364 CE_OTHER_GETS_PRESSED);
9365 CheckElementSideChange(x, y, element, dig_side,
9366 CE_PRESSED_BY_PLAYER, -1);
9369 return MF_NO_ACTION;
9372 player->push_delay = 0;
9374 if (Feld[x][y] != element) /* really digged/collected something */
9375 player->is_collecting = !player->is_digging;
9380 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9382 int jx = player->jx, jy = player->jy;
9383 int x = jx + dx, y = jy + dy;
9384 int snap_direction = (dx == -1 ? MV_LEFT :
9385 dx == +1 ? MV_RIGHT :
9387 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9389 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9392 if (!player->active || !IN_LEV_FIELD(x, y))
9400 if (player->MovPos == 0)
9401 player->is_pushing = FALSE;
9403 player->is_snapping = FALSE;
9405 if (player->MovPos == 0)
9407 player->is_moving = FALSE;
9408 player->is_digging = FALSE;
9409 player->is_collecting = FALSE;
9415 if (player->is_snapping)
9418 player->MovDir = snap_direction;
9421 if (player->MovPos == 0)
9424 player->is_moving = FALSE;
9425 player->is_digging = FALSE;
9426 player->is_collecting = FALSE;
9429 player->is_dropping = FALSE;
9431 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9434 player->is_snapping = TRUE;
9437 if (player->MovPos == 0)
9440 player->is_moving = FALSE;
9441 player->is_digging = FALSE;
9442 player->is_collecting = FALSE;
9445 DrawLevelField(x, y);
9451 boolean DropElement(struct PlayerInfo *player)
9453 int jx = player->jx, jy = player->jy;
9454 int old_element = Feld[jx][jy];
9457 /* check if player is active, not moving and ready to drop */
9458 if (!player->active || player->MovPos || player->drop_delay > 0)
9461 /* check if player has anything that can be dropped */
9462 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9465 /* check if anything can be dropped at the current position */
9466 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9469 /* collected custom elements can only be dropped on empty fields */
9470 if (player->inventory_size > 0 &&
9471 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9472 && old_element != EL_EMPTY)
9475 if (old_element != EL_EMPTY)
9476 Back[jx][jy] = old_element; /* store old element on this field */
9478 ResetGfxAnimation(jx, jy);
9479 ResetRandomAnimationValue(jx, jy);
9481 if (player->inventory_size > 0)
9483 player->inventory_size--;
9484 new_element = player->inventory_element[player->inventory_size];
9486 if (new_element == EL_DYNAMITE)
9487 new_element = EL_DYNAMITE_ACTIVE;
9488 else if (new_element == EL_SP_DISK_RED)
9489 new_element = EL_SP_DISK_RED_ACTIVE;
9491 Feld[jx][jy] = new_element;
9493 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9494 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9496 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9497 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9499 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9501 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9502 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9504 TestIfElementTouchesCustomElement(jx, jy);
9506 else /* player is dropping a dyna bomb */
9508 player->dynabombs_left--;
9509 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9511 Feld[jx][jy] = new_element;
9513 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9514 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9516 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9523 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9525 InitField(jx, jy, FALSE);
9526 if (CAN_MOVE(Feld[jx][jy]))
9530 new_element = Feld[jx][jy];
9532 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9533 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9535 int move_stepsize = element_info[new_element].move_stepsize;
9536 int direction, dx, dy, nextx, nexty;
9538 if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
9539 MovDir[jx][jy] = player->MovDir;
9541 direction = MovDir[jx][jy];
9542 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9543 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9547 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9550 WasJustMoving[jx][jy] = 3;
9552 InitMovingField(jx, jy, direction);
9553 ContinueMoving(jx, jy);
9558 Changed[jx][jy] = 0; /* allow another change */
9561 TestIfElementHitsCustomElement(jx, jy, direction);
9563 CheckElementSideChange(jx, jy, new_element,
9564 direction, CE_HITTING_SOMETHING, -1);
9568 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9572 player->drop_delay = 8 + 8 + 8;
9577 player->is_dropping = TRUE;
9583 /* ------------------------------------------------------------------------- */
9584 /* game sound playing functions */
9585 /* ------------------------------------------------------------------------- */
9587 static int *loop_sound_frame = NULL;
9588 static int *loop_sound_volume = NULL;
9590 void InitPlayLevelSound()
9592 int num_sounds = getSoundListSize();
9594 checked_free(loop_sound_frame);
9595 checked_free(loop_sound_volume);
9597 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9598 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9601 static void PlayLevelSound(int x, int y, int nr)
9603 int sx = SCREENX(x), sy = SCREENY(y);
9604 int volume, stereo_position;
9605 int max_distance = 8;
9606 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9608 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9609 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9612 if (!IN_LEV_FIELD(x, y) ||
9613 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9614 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9617 volume = SOUND_MAX_VOLUME;
9619 if (!IN_SCR_FIELD(sx, sy))
9621 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9622 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9624 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9627 stereo_position = (SOUND_MAX_LEFT +
9628 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9629 (SCR_FIELDX + 2 * max_distance));
9631 if (IS_LOOP_SOUND(nr))
9633 /* This assures that quieter loop sounds do not overwrite louder ones,
9634 while restarting sound volume comparison with each new game frame. */
9636 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9639 loop_sound_volume[nr] = volume;
9640 loop_sound_frame[nr] = FrameCounter;
9643 PlaySoundExt(nr, volume, stereo_position, type);
9646 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9648 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9649 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9650 y < LEVELY(BY1) ? LEVELY(BY1) :
9651 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9655 static void PlayLevelSoundAction(int x, int y, int action)
9657 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9660 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9662 int sound_effect = element_info[element].sound[action];
9664 if (sound_effect != SND_UNDEFINED)
9665 PlayLevelSound(x, y, sound_effect);
9668 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9671 int sound_effect = element_info[element].sound[action];
9673 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9674 PlayLevelSound(x, y, sound_effect);
9677 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9679 int sound_effect = element_info[Feld[x][y]].sound[action];
9681 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9682 PlayLevelSound(x, y, sound_effect);
9685 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9687 int sound_effect = element_info[Feld[x][y]].sound[action];
9689 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9690 StopSound(sound_effect);
9693 static void PlayLevelMusic()
9695 if (levelset.music[level_nr] != MUS_UNDEFINED)
9696 PlayMusic(levelset.music[level_nr]); /* from config file */
9698 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9701 void RaiseScore(int value)
9703 local_player->score += value;
9704 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9707 void RaiseScoreElement(int element)
9713 case EL_EMERALD_YELLOW:
9714 case EL_EMERALD_RED:
9715 case EL_EMERALD_PURPLE:
9716 case EL_SP_INFOTRON:
9717 RaiseScore(level.score[SC_EMERALD]);
9720 RaiseScore(level.score[SC_DIAMOND]);
9723 RaiseScore(level.score[SC_CRYSTAL]);
9726 RaiseScore(level.score[SC_PEARL]);
9729 case EL_BD_BUTTERFLY:
9730 case EL_SP_ELECTRON:
9731 RaiseScore(level.score[SC_BUG]);
9735 case EL_SP_SNIKSNAK:
9736 RaiseScore(level.score[SC_SPACESHIP]);
9739 case EL_DARK_YAMYAM:
9740 RaiseScore(level.score[SC_YAMYAM]);
9743 RaiseScore(level.score[SC_ROBOT]);
9746 RaiseScore(level.score[SC_PACMAN]);
9749 RaiseScore(level.score[SC_NUT]);
9752 case EL_SP_DISK_RED:
9753 case EL_DYNABOMB_INCREASE_NUMBER:
9754 case EL_DYNABOMB_INCREASE_SIZE:
9755 case EL_DYNABOMB_INCREASE_POWER:
9756 RaiseScore(level.score[SC_DYNAMITE]);
9758 case EL_SHIELD_NORMAL:
9759 case EL_SHIELD_DEADLY:
9760 RaiseScore(level.score[SC_SHIELD]);
9763 RaiseScore(level.score[SC_TIME_BONUS]);
9769 RaiseScore(level.score[SC_KEY]);
9772 RaiseScore(element_info[element].collect_score);
9777 void RequestQuitGame(boolean ask_if_really_quit)
9779 if (AllPlayersGone ||
9780 !ask_if_really_quit ||
9781 level_editor_test_game ||
9782 Request("Do you really want to quit the game ?",
9783 REQ_ASK | REQ_STAY_CLOSED))
9785 #if defined(PLATFORM_UNIX)
9786 if (options.network)
9787 SendToServer_StopPlaying();
9791 game_status = GAME_MODE_MAIN;
9797 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9802 /* ---------- new game button stuff ---------------------------------------- */
9804 /* graphic position values for game buttons */
9805 #define GAME_BUTTON_XSIZE 30
9806 #define GAME_BUTTON_YSIZE 30
9807 #define GAME_BUTTON_XPOS 5
9808 #define GAME_BUTTON_YPOS 215
9809 #define SOUND_BUTTON_XPOS 5
9810 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9812 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9813 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9814 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9815 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9816 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9817 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9824 } gamebutton_info[NUM_GAME_BUTTONS] =
9827 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9832 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9837 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9842 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9843 SOUND_CTRL_ID_MUSIC,
9844 "background music on/off"
9847 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9848 SOUND_CTRL_ID_LOOPS,
9849 "sound loops on/off"
9852 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9853 SOUND_CTRL_ID_SIMPLE,
9854 "normal sounds on/off"
9858 void CreateGameButtons()
9862 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9864 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9865 struct GadgetInfo *gi;
9868 unsigned long event_mask;
9869 int gd_xoffset, gd_yoffset;
9870 int gd_x1, gd_x2, gd_y1, gd_y2;
9873 gd_xoffset = gamebutton_info[i].x;
9874 gd_yoffset = gamebutton_info[i].y;
9875 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9876 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9878 if (id == GAME_CTRL_ID_STOP ||
9879 id == GAME_CTRL_ID_PAUSE ||
9880 id == GAME_CTRL_ID_PLAY)
9882 button_type = GD_TYPE_NORMAL_BUTTON;
9884 event_mask = GD_EVENT_RELEASED;
9885 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9886 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9890 button_type = GD_TYPE_CHECK_BUTTON;
9892 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9893 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9894 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9895 event_mask = GD_EVENT_PRESSED;
9896 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9897 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9900 gi = CreateGadget(GDI_CUSTOM_ID, id,
9901 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9902 GDI_X, DX + gd_xoffset,
9903 GDI_Y, DY + gd_yoffset,
9904 GDI_WIDTH, GAME_BUTTON_XSIZE,
9905 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9906 GDI_TYPE, button_type,
9907 GDI_STATE, GD_BUTTON_UNPRESSED,
9908 GDI_CHECKED, checked,
9909 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9910 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9911 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9912 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9913 GDI_EVENT_MASK, event_mask,
9914 GDI_CALLBACK_ACTION, HandleGameButtons,
9918 Error(ERR_EXIT, "cannot create gadget");
9920 game_gadget[id] = gi;
9924 void FreeGameButtons()
9928 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9929 FreeGadget(game_gadget[i]);
9932 static void MapGameButtons()
9936 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9937 MapGadget(game_gadget[i]);
9940 void UnmapGameButtons()
9944 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9945 UnmapGadget(game_gadget[i]);
9948 static void HandleGameButtons(struct GadgetInfo *gi)
9950 int id = gi->custom_id;
9952 if (game_status != GAME_MODE_PLAYING)
9957 case GAME_CTRL_ID_STOP:
9958 RequestQuitGame(TRUE);
9961 case GAME_CTRL_ID_PAUSE:
9962 if (options.network)
9964 #if defined(PLATFORM_UNIX)
9966 SendToServer_ContinuePlaying();
9968 SendToServer_PausePlaying();
9972 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9975 case GAME_CTRL_ID_PLAY:
9978 #if defined(PLATFORM_UNIX)
9979 if (options.network)
9980 SendToServer_ContinuePlaying();
9984 tape.pausing = FALSE;
9985 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9990 case SOUND_CTRL_ID_MUSIC:
9991 if (setup.sound_music)
9993 setup.sound_music = FALSE;
9996 else if (audio.music_available)
9998 setup.sound = setup.sound_music = TRUE;
10000 SetAudioMode(setup.sound);
10006 case SOUND_CTRL_ID_LOOPS:
10007 if (setup.sound_loops)
10008 setup.sound_loops = FALSE;
10009 else if (audio.loops_available)
10011 setup.sound = setup.sound_loops = TRUE;
10012 SetAudioMode(setup.sound);
10016 case SOUND_CTRL_ID_SIMPLE:
10017 if (setup.sound_simple)
10018 setup.sound_simple = FALSE;
10019 else if (audio.sound_available)
10021 setup.sound = setup.sound_simple = TRUE;
10022 SetAudioMode(setup.sound);