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 random_pos = RND(group->num_elements_resolved);
815 Feld[x][y] = group->element_resolved[random_pos];
817 InitField(x, y, init_game);
823 void DrawGameDoorValues()
827 for (i = 0; i < MAX_PLAYERS; i++)
828 for (j = 0; j < 4; j++)
829 if (stored_player[i].key[j])
830 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
831 el2edimg(EL_KEY_1 + j));
833 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
834 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
835 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
836 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
837 DrawText(DX + XX_SCORE, DY + YY_SCORE,
838 int2str(local_player->score, 5), FONT_TEXT_2);
839 DrawText(DX + XX_TIME, DY + YY_TIME,
840 int2str(TimeLeft, 3), FONT_TEXT_2);
843 static void resolve_group_element(int group_element, int recursion_depth)
846 static struct ElementGroupInfo *group;
847 struct ElementGroupInfo *actual_group = element_info[group_element].group;
850 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
852 Error(ERR_WARN, "recursion too deep when resolving group element %d",
853 group_element - EL_GROUP_START + 1);
855 /* replace element which caused too deep recursion by question mark */
856 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
861 if (recursion_depth == 0) /* initialization */
863 group = element_info[group_element].group;
864 group->num_elements_resolved = 0;
865 group_nr = group_element - EL_GROUP_START;
868 for (i = 0; i < actual_group->num_elements; i++)
870 int element = actual_group->element[i];
872 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
875 if (IS_GROUP_ELEMENT(element))
876 resolve_group_element(element, recursion_depth + 1);
879 group->element_resolved[group->num_elements_resolved++] = element;
880 element_info[element].in_group[group_nr] = TRUE;
885 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
887 printf("::: group %d: %d resolved elements\n",
888 group_element - EL_GROUP_START, group->num_elements_resolved);
889 for (i = 0; i < group->num_elements_resolved; i++)
890 printf("::: - %d ['%s']\n", group->element_resolved[i],
891 element_info[group->element_resolved[i]].token_name);
898 =============================================================================
900 -----------------------------------------------------------------------------
901 initialize game engine due to level / tape version number
902 =============================================================================
905 static void InitGameEngine()
909 /* set game engine from tape file when re-playing, else from level file */
910 game.engine_version = (tape.playing ? tape.engine_version :
913 /* dynamically adjust element properties according to game engine version */
914 InitElementPropertiesEngine(game.engine_version);
917 printf("level %d: level version == %06d\n", level_nr, level.game_version);
918 printf(" tape version == %06d [%s] [file: %06d]\n",
919 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
921 printf(" => game.engine_version == %06d\n", game.engine_version);
924 /* ---------- recursively resolve group elements ------------------------- */
926 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
927 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
928 element_info[i].in_group[j] = FALSE;
930 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
931 resolve_group_element(EL_GROUP_START + i, 0);
933 /* ---------- initialize player's initial move delay --------------------- */
935 /* dynamically adjust player properties according to game engine version */
936 game.initial_move_delay =
937 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
938 INITIAL_MOVE_DELAY_OFF);
940 /* dynamically adjust player properties according to level information */
941 game.initial_move_delay_value =
942 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
944 /* ---------- initialize player's initial push delay --------------------- */
946 /* dynamically adjust player properties according to game engine version */
947 game.initial_push_delay_value =
948 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
950 /* ---------- initialize changing elements ------------------------------- */
952 /* initialize changing elements information */
953 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
955 struct ElementInfo *ei = &element_info[i];
957 /* this pointer might have been changed in the level editor */
958 ei->change = &ei->change_page[0];
960 if (!IS_CUSTOM_ELEMENT(i))
962 ei->change->target_element = EL_EMPTY_SPACE;
963 ei->change->delay_fixed = 0;
964 ei->change->delay_random = 0;
965 ei->change->delay_frames = 1;
968 ei->change_events = CE_BITMASK_DEFAULT;
969 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
971 ei->event_page_nr[j] = 0;
972 ei->event_page[j] = &ei->change_page[0];
976 /* add changing elements from pre-defined list */
977 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
979 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
980 struct ElementInfo *ei = &element_info[ch_delay->element];
982 ei->change->target_element = ch_delay->target_element;
983 ei->change->delay_fixed = ch_delay->change_delay;
985 ei->change->pre_change_function = ch_delay->pre_change_function;
986 ei->change->change_function = ch_delay->change_function;
987 ei->change->post_change_function = ch_delay->post_change_function;
989 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
993 /* add change events from custom element configuration */
994 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
996 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
998 for (j = 0; j < ei->num_change_pages; j++)
1000 if (!ei->change_page[j].can_change)
1003 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1005 /* only add event page for the first page found with this event */
1006 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1007 !(ei->change_events & CH_EVENT_BIT(k)))
1009 ei->change_events |= CH_EVENT_BIT(k);
1010 ei->event_page_nr[k] = j;
1011 ei->event_page[k] = &ei->change_page[j];
1019 /* add change events from custom element configuration */
1020 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1022 int element = EL_CUSTOM_START + i;
1024 /* only add custom elements that change after fixed/random frame delay */
1025 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1026 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1030 /* ---------- initialize trigger events ---------------------------------- */
1032 /* initialize trigger events information */
1033 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1034 trigger_events[i] = EP_BITMASK_DEFAULT;
1037 /* add trigger events from element change event properties */
1038 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1040 struct ElementInfo *ei = &element_info[i];
1042 for (j = 0; j < ei->num_change_pages; j++)
1044 if (!ei->change_page[j].can_change)
1047 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1049 int trigger_element = ei->change_page[j].trigger_element;
1051 if (IS_GROUP_ELEMENT(trigger_element))
1053 struct ElementGroupInfo *group = element_info[trigger_element].group;
1055 for (k = 0; k < group->num_elements_resolved; k++)
1056 trigger_events[group->element_resolved[k]]
1057 |= ei->change_page[j].events;
1060 trigger_events[trigger_element] |= ei->change_page[j].events;
1065 /* add trigger events from element change event properties */
1066 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1067 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1068 trigger_events[element_info[i].change->trigger_element] |=
1069 element_info[i].change->events;
1072 /* ---------- initialize push delay -------------------------------------- */
1074 /* initialize push delay values to default */
1075 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1077 if (!IS_CUSTOM_ELEMENT(i))
1079 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1080 element_info[i].push_delay_random = game.default_push_delay_random;
1084 /* set push delay value for certain elements from pre-defined list */
1085 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1087 int e = push_delay_list[i].element;
1089 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1090 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1093 /* ---------- initialize move stepsize ----------------------------------- */
1095 /* initialize move stepsize values to default */
1096 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1097 if (!IS_CUSTOM_ELEMENT(i))
1098 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1100 /* set move stepsize value for certain elements from pre-defined list */
1101 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1103 int e = move_stepsize_list[i].element;
1105 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1108 /* ---------- initialize move dig/leave ---------------------------------- */
1110 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1112 element_info[i].can_leave_element = FALSE;
1113 element_info[i].can_leave_element_last = FALSE;
1116 /* ---------- initialize gem count --------------------------------------- */
1118 /* initialize gem count values for each element */
1119 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1120 if (!IS_CUSTOM_ELEMENT(i))
1121 element_info[i].collect_count = 0;
1123 /* add gem count values for all elements from pre-defined list */
1124 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1125 element_info[collect_count_list[i].element].collect_count =
1126 collect_count_list[i].count;
1131 =============================================================================
1133 -----------------------------------------------------------------------------
1134 initialize and start new game
1135 =============================================================================
1140 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1141 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1142 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1149 #if USE_NEW_AMOEBA_CODE
1150 printf("Using new amoeba code.\n");
1152 printf("Using old amoeba code.\n");
1157 /* don't play tapes over network */
1158 network_playing = (options.network && !tape.playing);
1160 for (i = 0; i < MAX_PLAYERS; i++)
1162 struct PlayerInfo *player = &stored_player[i];
1164 player->index_nr = i;
1165 player->element_nr = EL_PLAYER_1 + i;
1167 player->present = FALSE;
1168 player->active = FALSE;
1171 player->effective_action = 0;
1172 player->programmed_action = 0;
1175 player->gems_still_needed = level.gems_needed;
1176 player->sokobanfields_still_needed = 0;
1177 player->lights_still_needed = 0;
1178 player->friends_still_needed = 0;
1180 for (j = 0; j < 4; j++)
1181 player->key[j] = FALSE;
1183 player->dynabomb_count = 0;
1184 player->dynabomb_size = 1;
1185 player->dynabombs_left = 0;
1186 player->dynabomb_xl = FALSE;
1188 player->MovDir = MV_NO_MOVING;
1191 player->GfxDir = MV_NO_MOVING;
1192 player->GfxAction = ACTION_DEFAULT;
1194 player->StepFrame = 0;
1196 player->use_murphy_graphic = FALSE;
1198 player->actual_frame_counter = 0;
1200 player->step_counter = 0;
1202 player->last_move_dir = MV_NO_MOVING;
1204 player->is_waiting = FALSE;
1205 player->is_moving = FALSE;
1206 player->is_digging = FALSE;
1207 player->is_snapping = FALSE;
1208 player->is_collecting = FALSE;
1209 player->is_pushing = FALSE;
1210 player->is_switching = FALSE;
1211 player->is_dropping = FALSE;
1213 player->is_bored = FALSE;
1214 player->is_sleeping = FALSE;
1216 player->frame_counter_bored = -1;
1217 player->frame_counter_sleeping = -1;
1219 player->anim_delay_counter = 0;
1220 player->post_delay_counter = 0;
1222 player->action_waiting = ACTION_DEFAULT;
1223 player->last_action_waiting = ACTION_DEFAULT;
1224 player->special_action_bored = ACTION_DEFAULT;
1225 player->special_action_sleeping = ACTION_DEFAULT;
1227 player->num_special_action_bored = 0;
1228 player->num_special_action_sleeping = 0;
1230 /* determine number of special actions for bored and sleeping animation */
1231 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1233 boolean found = FALSE;
1235 for (k = 0; k < NUM_DIRECTIONS; k++)
1236 if (el_act_dir2img(player->element_nr, j, k) !=
1237 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1241 player->num_special_action_bored++;
1245 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1247 boolean found = FALSE;
1249 for (k = 0; k < NUM_DIRECTIONS; k++)
1250 if (el_act_dir2img(player->element_nr, j, k) !=
1251 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1255 player->num_special_action_sleeping++;
1260 player->switch_x = -1;
1261 player->switch_y = -1;
1263 player->show_envelope = 0;
1265 player->move_delay = game.initial_move_delay;
1266 player->move_delay_value = game.initial_move_delay_value;
1268 player->move_delay_reset_counter = 0;
1270 player->push_delay = 0;
1271 player->push_delay_value = game.initial_push_delay_value;
1273 player->drop_delay = 0;
1275 player->last_jx = player->last_jy = 0;
1276 player->jx = player->jy = 0;
1278 player->shield_normal_time_left = 0;
1279 player->shield_deadly_time_left = 0;
1281 player->inventory_size = 0;
1283 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1284 SnapField(player, 0, 0);
1286 player->LevelSolved = FALSE;
1287 player->GameOver = FALSE;
1290 network_player_action_received = FALSE;
1292 #if defined(PLATFORM_UNIX)
1293 /* initial null action */
1294 if (network_playing)
1295 SendToServer_MovePlayer(MV_NO_MOVING);
1303 TimeLeft = level.time;
1305 ScreenMovDir = MV_NO_MOVING;
1309 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1311 AllPlayersGone = FALSE;
1313 game.yamyam_content_nr = 0;
1314 game.magic_wall_active = FALSE;
1315 game.magic_wall_time_left = 0;
1316 game.light_time_left = 0;
1317 game.timegate_time_left = 0;
1318 game.switchgate_pos = 0;
1319 game.balloon_dir = MV_NO_MOVING;
1320 game.gravity = level.initial_gravity;
1321 game.explosions_delayed = TRUE;
1323 game.envelope_active = FALSE;
1325 for (i = 0; i < 4; i++)
1327 game.belt_dir[i] = MV_NO_MOVING;
1328 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1331 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1332 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1334 for (x = 0; x < lev_fieldx; x++)
1336 for (y = 0; y < lev_fieldy; y++)
1338 Feld[x][y] = level.field[x][y];
1339 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1340 ChangeDelay[x][y] = 0;
1341 ChangePage[x][y] = -1;
1342 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1344 WasJustMoving[x][y] = 0;
1345 WasJustFalling[x][y] = 0;
1347 Pushed[x][y] = FALSE;
1349 Changed[x][y] = CE_BITMASK_DEFAULT;
1350 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1352 ExplodePhase[x][y] = 0;
1353 ExplodeField[x][y] = EX_NO_EXPLOSION;
1355 RunnerVisit[x][y] = 0;
1356 PlayerVisit[x][y] = 0;
1359 GfxRandom[x][y] = INIT_GFX_RANDOM();
1360 GfxElement[x][y] = EL_UNDEFINED;
1361 GfxAction[x][y] = ACTION_DEFAULT;
1362 GfxDir[x][y] = MV_NO_MOVING;
1366 for (y = 0; y < lev_fieldy; y++)
1368 for (x = 0; x < lev_fieldx; x++)
1370 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1372 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1374 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1377 InitField(x, y, TRUE);
1383 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1384 emulate_sb ? EMU_SOKOBAN :
1385 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1387 /* correct non-moving belts to start moving left */
1388 for (i = 0; i < 4; i++)
1389 if (game.belt_dir[i] == MV_NO_MOVING)
1390 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1392 /* check if any connected player was not found in playfield */
1393 for (i = 0; i < MAX_PLAYERS; i++)
1395 struct PlayerInfo *player = &stored_player[i];
1397 if (player->connected && !player->present)
1399 for (j = 0; j < MAX_PLAYERS; j++)
1401 struct PlayerInfo *some_player = &stored_player[j];
1402 int jx = some_player->jx, jy = some_player->jy;
1404 /* assign first free player found that is present in the playfield */
1405 if (some_player->present && !some_player->connected)
1407 player->present = TRUE;
1408 player->active = TRUE;
1409 some_player->present = FALSE;
1411 StorePlayer[jx][jy] = player->element_nr;
1412 player->jx = player->last_jx = jx;
1413 player->jy = player->last_jy = jy;
1423 /* when playing a tape, eliminate all players who do not participate */
1425 for (i = 0; i < MAX_PLAYERS; i++)
1427 if (stored_player[i].active && !tape.player_participates[i])
1429 struct PlayerInfo *player = &stored_player[i];
1430 int jx = player->jx, jy = player->jy;
1432 player->active = FALSE;
1433 StorePlayer[jx][jy] = 0;
1434 Feld[jx][jy] = EL_EMPTY;
1438 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1440 /* when in single player mode, eliminate all but the first active player */
1442 for (i = 0; i < MAX_PLAYERS; i++)
1444 if (stored_player[i].active)
1446 for (j = i + 1; j < MAX_PLAYERS; j++)
1448 if (stored_player[j].active)
1450 struct PlayerInfo *player = &stored_player[j];
1451 int jx = player->jx, jy = player->jy;
1453 player->active = FALSE;
1454 StorePlayer[jx][jy] = 0;
1455 Feld[jx][jy] = EL_EMPTY;
1462 /* when recording the game, store which players take part in the game */
1465 for (i = 0; i < MAX_PLAYERS; i++)
1466 if (stored_player[i].active)
1467 tape.player_participates[i] = TRUE;
1472 for (i = 0; i < MAX_PLAYERS; i++)
1474 struct PlayerInfo *player = &stored_player[i];
1476 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1481 if (local_player == player)
1482 printf("Player %d is local player.\n", i+1);
1486 if (BorderElement == EL_EMPTY)
1489 SBX_Right = lev_fieldx - SCR_FIELDX;
1491 SBY_Lower = lev_fieldy - SCR_FIELDY;
1496 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1498 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1501 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1502 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1504 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1505 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1507 /* if local player not found, look for custom element that might create
1508 the player (make some assumptions about the right custom element) */
1509 if (!local_player->present)
1511 int start_x = 0, start_y = 0;
1512 int found_rating = 0;
1513 int found_element = EL_UNDEFINED;
1515 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1517 int element = Feld[x][y];
1522 if (!IS_CUSTOM_ELEMENT(element))
1525 if (CAN_CHANGE(element))
1527 for (i = 0; i < element_info[element].num_change_pages; i++)
1529 content = element_info[element].change_page[i].target_element;
1530 is_player = ELEM_IS_PLAYER(content);
1532 if (is_player && (found_rating < 3 || element < found_element))
1538 found_element = element;
1543 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1545 content = element_info[element].content[xx][yy];
1546 is_player = ELEM_IS_PLAYER(content);
1548 if (is_player && (found_rating < 2 || element < found_element))
1550 start_x = x + xx - 1;
1551 start_y = y + yy - 1;
1554 found_element = element;
1557 if (!CAN_CHANGE(element))
1560 for (i = 0; i < element_info[element].num_change_pages; i++)
1562 content = element_info[element].change_page[i].content[xx][yy];
1563 is_player = ELEM_IS_PLAYER(content);
1565 if (is_player && (found_rating < 1 || element < found_element))
1567 start_x = x + xx - 1;
1568 start_y = y + yy - 1;
1571 found_element = element;
1577 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1578 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1581 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1582 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1588 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1589 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1590 local_player->jx - MIDPOSX);
1592 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1593 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1594 local_player->jy - MIDPOSY);
1596 scroll_x = SBX_Left;
1597 scroll_y = SBY_Upper;
1598 if (local_player->jx >= SBX_Left + MIDPOSX)
1599 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1600 local_player->jx - MIDPOSX :
1602 if (local_player->jy >= SBY_Upper + MIDPOSY)
1603 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1604 local_player->jy - MIDPOSY :
1609 CloseDoor(DOOR_CLOSE_1);
1614 /* after drawing the level, correct some elements */
1615 if (game.timegate_time_left == 0)
1616 CloseAllOpenTimegates();
1618 if (setup.soft_scrolling)
1619 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1621 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1624 /* copy default game door content to main double buffer */
1625 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1626 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1629 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1632 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1633 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1634 BlitBitmap(drawto, drawto,
1635 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1636 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1637 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1638 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1641 DrawGameDoorValues();
1645 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1646 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1647 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1651 /* copy actual game door content to door double buffer for OpenDoor() */
1652 BlitBitmap(drawto, bitmap_db_door,
1653 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1655 OpenDoor(DOOR_OPEN_ALL);
1657 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1659 if (setup.sound_music)
1662 KeyboardAutoRepeatOffUnlessAutoplay();
1666 for (i = 0; i < 4; i++)
1667 printf("Player %d %sactive.\n",
1668 i + 1, (stored_player[i].active ? "" : "not "));
1672 printf("::: starting game [%d]\n", FrameCounter);
1676 void InitMovDir(int x, int y)
1678 int i, element = Feld[x][y];
1679 static int xy[4][2] =
1686 static int direction[3][4] =
1688 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1689 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1690 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1699 Feld[x][y] = EL_BUG;
1700 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1703 case EL_SPACESHIP_RIGHT:
1704 case EL_SPACESHIP_UP:
1705 case EL_SPACESHIP_LEFT:
1706 case EL_SPACESHIP_DOWN:
1707 Feld[x][y] = EL_SPACESHIP;
1708 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1711 case EL_BD_BUTTERFLY_RIGHT:
1712 case EL_BD_BUTTERFLY_UP:
1713 case EL_BD_BUTTERFLY_LEFT:
1714 case EL_BD_BUTTERFLY_DOWN:
1715 Feld[x][y] = EL_BD_BUTTERFLY;
1716 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1719 case EL_BD_FIREFLY_RIGHT:
1720 case EL_BD_FIREFLY_UP:
1721 case EL_BD_FIREFLY_LEFT:
1722 case EL_BD_FIREFLY_DOWN:
1723 Feld[x][y] = EL_BD_FIREFLY;
1724 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1727 case EL_PACMAN_RIGHT:
1729 case EL_PACMAN_LEFT:
1730 case EL_PACMAN_DOWN:
1731 Feld[x][y] = EL_PACMAN;
1732 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1735 case EL_SP_SNIKSNAK:
1736 MovDir[x][y] = MV_UP;
1739 case EL_SP_ELECTRON:
1740 MovDir[x][y] = MV_LEFT;
1747 Feld[x][y] = EL_MOLE;
1748 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1752 if (IS_CUSTOM_ELEMENT(element))
1754 struct ElementInfo *ei = &element_info[element];
1755 int move_direction_initial = ei->move_direction_initial;
1756 int move_pattern = ei->move_pattern;
1758 if (move_direction_initial == MV_PREVIOUS)
1760 if (MovDir[x][y] != MV_NO_MOVING)
1763 move_direction_initial = MV_AUTOMATIC;
1766 if (move_direction_initial == MV_RANDOM)
1767 MovDir[x][y] = 1 << RND(4);
1768 else if (move_direction_initial & MV_ANY_DIRECTION)
1769 MovDir[x][y] = move_direction_initial;
1770 else if (move_pattern == MV_ALL_DIRECTIONS ||
1771 move_pattern == MV_TURNING_LEFT ||
1772 move_pattern == MV_TURNING_RIGHT ||
1773 move_pattern == MV_TURNING_LEFT_RIGHT ||
1774 move_pattern == MV_TURNING_RIGHT_LEFT ||
1775 move_pattern == MV_TURNING_RANDOM)
1776 MovDir[x][y] = 1 << RND(4);
1777 else if (move_pattern == MV_HORIZONTAL)
1778 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1779 else if (move_pattern == MV_VERTICAL)
1780 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1781 else if (move_pattern & MV_ANY_DIRECTION)
1782 MovDir[x][y] = element_info[element].move_pattern;
1783 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1784 move_pattern == MV_ALONG_RIGHT_SIDE)
1786 for (i = 0; i < 4; i++)
1788 int x1 = x + xy[i][0];
1789 int y1 = y + xy[i][1];
1791 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1793 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1794 MovDir[x][y] = direction[0][i];
1796 MovDir[x][y] = direction[1][i];
1805 MovDir[x][y] = 1 << RND(4);
1807 if (element != EL_BUG &&
1808 element != EL_SPACESHIP &&
1809 element != EL_BD_BUTTERFLY &&
1810 element != EL_BD_FIREFLY)
1813 for (i = 0; i < 4; i++)
1815 int x1 = x + xy[i][0];
1816 int y1 = y + xy[i][1];
1818 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1820 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1822 MovDir[x][y] = direction[0][i];
1825 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1826 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1828 MovDir[x][y] = direction[1][i];
1837 GfxDir[x][y] = MovDir[x][y];
1840 void InitAmoebaNr(int x, int y)
1843 int group_nr = AmoebeNachbarNr(x, y);
1847 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1849 if (AmoebaCnt[i] == 0)
1857 AmoebaNr[x][y] = group_nr;
1858 AmoebaCnt[group_nr]++;
1859 AmoebaCnt2[group_nr]++;
1865 boolean raise_level = FALSE;
1867 if (local_player->MovPos)
1871 if (tape.auto_play) /* tape might already be stopped here */
1872 tape.auto_play_level_solved = TRUE;
1874 if (tape.playing && tape.auto_play)
1875 tape.auto_play_level_solved = TRUE;
1878 local_player->LevelSolved = FALSE;
1880 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1884 if (!tape.playing && setup.sound_loops)
1885 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1886 SND_CTRL_PLAY_LOOP);
1888 while (TimeLeft > 0)
1890 if (!tape.playing && !setup.sound_loops)
1891 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1892 if (TimeLeft > 0 && !(TimeLeft % 10))
1893 RaiseScore(level.score[SC_TIME_BONUS]);
1894 if (TimeLeft > 100 && !(TimeLeft % 10))
1898 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1905 if (!tape.playing && setup.sound_loops)
1906 StopSound(SND_GAME_LEVELTIME_BONUS);
1908 else if (level.time == 0) /* level without time limit */
1910 if (!tape.playing && setup.sound_loops)
1911 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1912 SND_CTRL_PLAY_LOOP);
1914 while (TimePlayed < 999)
1916 if (!tape.playing && !setup.sound_loops)
1917 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1918 if (TimePlayed < 999 && !(TimePlayed % 10))
1919 RaiseScore(level.score[SC_TIME_BONUS]);
1920 if (TimePlayed < 900 && !(TimePlayed % 10))
1924 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1931 if (!tape.playing && setup.sound_loops)
1932 StopSound(SND_GAME_LEVELTIME_BONUS);
1935 /* close exit door after last player */
1936 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1937 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1939 int element = Feld[ExitX][ExitY];
1941 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1942 EL_SP_EXIT_CLOSING);
1944 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1947 /* Hero disappears */
1948 DrawLevelField(ExitX, ExitY);
1954 CloseDoor(DOOR_CLOSE_1);
1959 SaveTape(tape.level_nr); /* Ask to save tape */
1962 if (level_nr == leveldir_current->handicap_level)
1964 leveldir_current->handicap_level++;
1965 SaveLevelSetup_SeriesInfo();
1968 if (level_editor_test_game)
1969 local_player->score = -1; /* no highscore when playing from editor */
1970 else if (level_nr < leveldir_current->last_level)
1971 raise_level = TRUE; /* advance to next level */
1973 if ((hi_pos = NewHiScore()) >= 0)
1975 game_status = GAME_MODE_SCORES;
1976 DrawHallOfFame(hi_pos);
1985 game_status = GAME_MODE_MAIN;
2002 LoadScore(level_nr);
2004 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2005 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2008 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2010 if (local_player->score > highscore[k].Score)
2012 /* player has made it to the hall of fame */
2014 if (k < MAX_SCORE_ENTRIES - 1)
2016 int m = MAX_SCORE_ENTRIES - 1;
2019 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2020 if (!strcmp(setup.player_name, highscore[l].Name))
2022 if (m == k) /* player's new highscore overwrites his old one */
2026 for (l = m; l > k; l--)
2028 strcpy(highscore[l].Name, highscore[l - 1].Name);
2029 highscore[l].Score = highscore[l - 1].Score;
2036 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2037 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2038 highscore[k].Score = local_player->score;
2044 else if (!strncmp(setup.player_name, highscore[k].Name,
2045 MAX_PLAYER_NAME_LEN))
2046 break; /* player already there with a higher score */
2052 SaveScore(level_nr);
2057 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2059 if (player->GfxAction != action || player->GfxDir != dir)
2062 printf("Player frame reset! (%d => %d, %d => %d)\n",
2063 player->GfxAction, action, player->GfxDir, dir);
2066 player->GfxAction = action;
2067 player->GfxDir = dir;
2069 player->StepFrame = 0;
2073 static void ResetRandomAnimationValue(int x, int y)
2075 GfxRandom[x][y] = INIT_GFX_RANDOM();
2078 static void ResetGfxAnimation(int x, int y)
2081 GfxAction[x][y] = ACTION_DEFAULT;
2082 GfxDir[x][y] = MovDir[x][y];
2085 void InitMovingField(int x, int y, int direction)
2087 int element = Feld[x][y];
2088 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2089 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2093 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2094 ResetGfxAnimation(x, y);
2096 MovDir[newx][newy] = MovDir[x][y] = direction;
2097 GfxDir[x][y] = direction;
2099 if (Feld[newx][newy] == EL_EMPTY)
2100 Feld[newx][newy] = EL_BLOCKED;
2102 if (direction == MV_DOWN && CAN_FALL(element))
2103 GfxAction[x][y] = ACTION_FALLING;
2105 GfxAction[x][y] = ACTION_MOVING;
2107 GfxFrame[newx][newy] = GfxFrame[x][y];
2108 GfxRandom[newx][newy] = GfxRandom[x][y];
2109 GfxAction[newx][newy] = GfxAction[x][y];
2110 GfxDir[newx][newy] = GfxDir[x][y];
2113 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2115 int direction = MovDir[x][y];
2116 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2117 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2123 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2125 int oldx = x, oldy = y;
2126 int direction = MovDir[x][y];
2128 if (direction == MV_LEFT)
2130 else if (direction == MV_RIGHT)
2132 else if (direction == MV_UP)
2134 else if (direction == MV_DOWN)
2137 *comes_from_x = oldx;
2138 *comes_from_y = oldy;
2141 int MovingOrBlocked2Element(int x, int y)
2143 int element = Feld[x][y];
2145 if (element == EL_BLOCKED)
2149 Blocked2Moving(x, y, &oldx, &oldy);
2150 return Feld[oldx][oldy];
2156 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2158 /* like MovingOrBlocked2Element(), but if element is moving
2159 and (x,y) is the field the moving element is just leaving,
2160 return EL_BLOCKED instead of the element value */
2161 int element = Feld[x][y];
2163 if (IS_MOVING(x, y))
2165 if (element == EL_BLOCKED)
2169 Blocked2Moving(x, y, &oldx, &oldy);
2170 return Feld[oldx][oldy];
2179 static void RemoveField(int x, int y)
2181 Feld[x][y] = EL_EMPTY;
2188 ChangeDelay[x][y] = 0;
2189 ChangePage[x][y] = -1;
2190 Pushed[x][y] = FALSE;
2192 GfxElement[x][y] = EL_UNDEFINED;
2193 GfxAction[x][y] = ACTION_DEFAULT;
2194 GfxDir[x][y] = MV_NO_MOVING;
2197 void RemoveMovingField(int x, int y)
2199 int oldx = x, oldy = y, newx = x, newy = y;
2200 int element = Feld[x][y];
2201 int next_element = EL_UNDEFINED;
2203 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2206 if (IS_MOVING(x, y))
2208 Moving2Blocked(x, y, &newx, &newy);
2209 if (Feld[newx][newy] != EL_BLOCKED)
2212 else if (element == EL_BLOCKED)
2214 Blocked2Moving(x, y, &oldx, &oldy);
2215 if (!IS_MOVING(oldx, oldy))
2219 if (element == EL_BLOCKED &&
2220 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2221 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2222 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2223 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2224 next_element = get_next_element(Feld[oldx][oldy]);
2226 RemoveField(oldx, oldy);
2227 RemoveField(newx, newy);
2229 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2231 if (next_element != EL_UNDEFINED)
2232 Feld[oldx][oldy] = next_element;
2234 DrawLevelField(oldx, oldy);
2235 DrawLevelField(newx, newy);
2238 void DrawDynamite(int x, int y)
2240 int sx = SCREENX(x), sy = SCREENY(y);
2241 int graphic = el2img(Feld[x][y]);
2244 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2247 if (IS_WALKABLE_INSIDE(Back[x][y]))
2251 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2252 else if (Store[x][y])
2253 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2255 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2258 if (Back[x][y] || Store[x][y])
2259 DrawGraphicThruMask(sx, sy, graphic, frame);
2261 DrawGraphic(sx, sy, graphic, frame);
2263 if (game.emulation == EMU_SUPAPLEX)
2264 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2265 else if (Store[x][y])
2266 DrawGraphicThruMask(sx, sy, graphic, frame);
2268 DrawGraphic(sx, sy, graphic, frame);
2272 void CheckDynamite(int x, int y)
2274 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2278 if (MovDelay[x][y] != 0)
2281 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2288 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2290 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2291 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2292 StopSound(SND_DYNAMITE_ACTIVE);
2294 StopSound(SND_DYNABOMB_ACTIVE);
2300 void RelocatePlayer(int x, int y, int element)
2302 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2304 if (player->GameOver) /* do not reanimate dead player */
2308 RemoveField(x, y); /* temporarily remove newly placed player */
2309 DrawLevelField(x, y);
2312 if (player->present)
2314 while (player->MovPos)
2316 ScrollPlayer(player, SCROLL_GO_ON);
2317 ScrollScreen(NULL, SCROLL_GO_ON);
2323 Delay(GAME_FRAME_DELAY);
2326 DrawPlayer(player); /* needed here only to cleanup last field */
2327 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2329 player->is_moving = FALSE;
2332 Feld[x][y] = element;
2333 InitPlayerField(x, y, element, TRUE);
2335 if (player == local_player)
2337 int scroll_xx = -999, scroll_yy = -999;
2339 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2342 int fx = FX, fy = FY;
2344 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2345 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2346 local_player->jx - MIDPOSX);
2348 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2349 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2350 local_player->jy - MIDPOSY);
2352 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2353 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2358 fx += dx * TILEX / 2;
2359 fy += dy * TILEY / 2;
2361 ScrollLevel(dx, dy);
2364 /* scroll in two steps of half tile size to make things smoother */
2365 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2367 Delay(GAME_FRAME_DELAY);
2369 /* scroll second step to align at full tile size */
2371 Delay(GAME_FRAME_DELAY);
2376 void Explode(int ex, int ey, int phase, int mode)
2380 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2381 int last_phase = num_phase * delay;
2382 int half_phase = (num_phase / 2) * delay;
2383 int first_phase_after_start = EX_PHASE_START + 1;
2385 if (game.explosions_delayed)
2387 ExplodeField[ex][ey] = mode;
2391 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2393 int center_element = Feld[ex][ey];
2396 /* --- This is only really needed (and now handled) in "Impact()". --- */
2397 /* do not explode moving elements that left the explode field in time */
2398 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2399 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2403 if (mode == EX_NORMAL || mode == EX_CENTER)
2404 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2406 /* remove things displayed in background while burning dynamite */
2407 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2410 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2412 /* put moving element to center field (and let it explode there) */
2413 center_element = MovingOrBlocked2Element(ex, ey);
2414 RemoveMovingField(ex, ey);
2415 Feld[ex][ey] = center_element;
2418 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2420 int xx = x - ex + 1;
2421 int yy = y - ey + 1;
2424 if (!IN_LEV_FIELD(x, y) ||
2425 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2426 (x != ex || y != ey)))
2429 element = Feld[x][y];
2431 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2433 element = MovingOrBlocked2Element(x, y);
2435 if (!IS_EXPLOSION_PROOF(element))
2436 RemoveMovingField(x, y);
2442 if (IS_EXPLOSION_PROOF(element))
2445 /* indestructible elements can only explode in center (but not flames) */
2446 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2447 element == EL_FLAMES)
2452 if ((IS_INDESTRUCTIBLE(element) &&
2453 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2454 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2455 element == EL_FLAMES)
2459 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2461 if (IS_ACTIVE_BOMB(element))
2463 /* re-activate things under the bomb like gate or penguin */
2464 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2471 /* save walkable background elements while explosion on same tile */
2473 if (IS_INDESTRUCTIBLE(element))
2474 Back[x][y] = element;
2476 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2477 Back[x][y] = element;
2480 /* ignite explodable elements reached by other explosion */
2481 if (element == EL_EXPLOSION)
2482 element = Store2[x][y];
2485 if (AmoebaNr[x][y] &&
2486 (element == EL_AMOEBA_FULL ||
2487 element == EL_BD_AMOEBA ||
2488 element == EL_AMOEBA_GROWING))
2490 AmoebaCnt[AmoebaNr[x][y]]--;
2491 AmoebaCnt2[AmoebaNr[x][y]]--;
2497 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2499 switch(StorePlayer[ex][ey])
2502 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2505 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2508 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2512 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2516 if (game.emulation == EMU_SUPAPLEX)
2517 Store[x][y] = EL_EMPTY;
2519 else if (center_element == EL_MOLE)
2520 Store[x][y] = EL_EMERALD_RED;
2521 else if (center_element == EL_PENGUIN)
2522 Store[x][y] = EL_EMERALD_PURPLE;
2523 else if (center_element == EL_BUG)
2524 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2525 else if (center_element == EL_BD_BUTTERFLY)
2526 Store[x][y] = EL_BD_DIAMOND;
2527 else if (center_element == EL_SP_ELECTRON)
2528 Store[x][y] = EL_SP_INFOTRON;
2529 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2530 Store[x][y] = level.amoeba_content;
2531 else if (center_element == EL_YAMYAM)
2532 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2533 else if (IS_CUSTOM_ELEMENT(center_element) &&
2534 element_info[center_element].content[xx][yy] != EL_EMPTY)
2535 Store[x][y] = element_info[center_element].content[xx][yy];
2536 else if (element == EL_WALL_EMERALD)
2537 Store[x][y] = EL_EMERALD;
2538 else if (element == EL_WALL_DIAMOND)
2539 Store[x][y] = EL_DIAMOND;
2540 else if (element == EL_WALL_BD_DIAMOND)
2541 Store[x][y] = EL_BD_DIAMOND;
2542 else if (element == EL_WALL_EMERALD_YELLOW)
2543 Store[x][y] = EL_EMERALD_YELLOW;
2544 else if (element == EL_WALL_EMERALD_RED)
2545 Store[x][y] = EL_EMERALD_RED;
2546 else if (element == EL_WALL_EMERALD_PURPLE)
2547 Store[x][y] = EL_EMERALD_PURPLE;
2548 else if (element == EL_WALL_PEARL)
2549 Store[x][y] = EL_PEARL;
2550 else if (element == EL_WALL_CRYSTAL)
2551 Store[x][y] = EL_CRYSTAL;
2552 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2553 Store[x][y] = element_info[element].content[1][1];
2555 Store[x][y] = EL_EMPTY;
2557 if (x != ex || y != ey ||
2558 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2559 Store2[x][y] = element;
2562 if (AmoebaNr[x][y] &&
2563 (element == EL_AMOEBA_FULL ||
2564 element == EL_BD_AMOEBA ||
2565 element == EL_AMOEBA_GROWING))
2567 AmoebaCnt[AmoebaNr[x][y]]--;
2568 AmoebaCnt2[AmoebaNr[x][y]]--;
2574 MovDir[x][y] = MovPos[x][y] = 0;
2575 GfxDir[x][y] = MovDir[x][y];
2580 Feld[x][y] = EL_EXPLOSION;
2582 GfxElement[x][y] = center_element;
2584 GfxElement[x][y] = EL_UNDEFINED;
2587 ExplodePhase[x][y] = 1;
2591 if (center_element == EL_YAMYAM)
2592 game.yamyam_content_nr =
2593 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2604 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2608 /* activate this even in non-DEBUG version until cause for crash in
2609 getGraphicAnimationFrame() (see below) is found and eliminated */
2613 if (GfxElement[x][y] == EL_UNDEFINED)
2616 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2617 printf("Explode(): This should never happen!\n");
2620 GfxElement[x][y] = EL_EMPTY;
2624 if (phase == first_phase_after_start)
2626 int element = Store2[x][y];
2628 if (element == EL_BLACK_ORB)
2630 Feld[x][y] = Store2[x][y];
2635 else if (phase == half_phase)
2637 int element = Store2[x][y];
2639 if (IS_PLAYER(x, y))
2640 KillHeroUnlessProtected(x, y);
2641 else if (CAN_EXPLODE_BY_FIRE(element))
2643 Feld[x][y] = Store2[x][y];
2647 else if (element == EL_AMOEBA_TO_DIAMOND)
2648 AmoebeUmwandeln(x, y);
2651 if (phase == last_phase)
2655 element = Feld[x][y] = Store[x][y];
2656 Store[x][y] = Store2[x][y] = 0;
2657 GfxElement[x][y] = EL_UNDEFINED;
2659 /* player can escape from explosions and might therefore be still alive */
2660 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2661 element <= EL_PLAYER_IS_EXPLODING_4)
2662 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2664 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2665 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2666 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2669 /* restore probably existing indestructible background element */
2670 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2671 element = Feld[x][y] = Back[x][y];
2674 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2675 GfxDir[x][y] = MV_NO_MOVING;
2676 ChangeDelay[x][y] = 0;
2677 ChangePage[x][y] = -1;
2679 InitField(x, y, FALSE);
2681 /* !!! not needed !!! */
2682 if (CAN_MOVE(element))
2685 DrawLevelField(x, y);
2687 TestIfElementTouchesCustomElement(x, y);
2689 if (GFX_CRUMBLED(element))
2690 DrawLevelFieldCrumbledSandNeighbours(x, y);
2692 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2693 StorePlayer[x][y] = 0;
2695 if (ELEM_IS_PLAYER(element))
2696 RelocatePlayer(x, y, element);
2698 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2701 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2703 int stored = Store[x][y];
2704 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2705 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2708 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2711 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2712 element_info[GfxElement[x][y]].token_name,
2717 DrawLevelFieldCrumbledSand(x, y);
2719 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2721 DrawLevelElement(x, y, Back[x][y]);
2722 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2724 else if (IS_WALKABLE_UNDER(Back[x][y]))
2726 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2727 DrawLevelElementThruMask(x, y, Back[x][y]);
2729 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2730 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2734 void DynaExplode(int ex, int ey)
2737 int dynabomb_element = Feld[ex][ey];
2738 int dynabomb_size = 1;
2739 boolean dynabomb_xl = FALSE;
2740 struct PlayerInfo *player;
2741 static int xy[4][2] =
2749 if (IS_ACTIVE_BOMB(dynabomb_element))
2751 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2752 dynabomb_size = player->dynabomb_size;
2753 dynabomb_xl = player->dynabomb_xl;
2754 player->dynabombs_left++;
2757 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2759 for (i = 0; i < 4; i++)
2761 for (j = 1; j <= dynabomb_size; j++)
2763 int x = ex + j * xy[i % 4][0];
2764 int y = ey + j * xy[i % 4][1];
2767 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2770 element = Feld[x][y];
2772 /* do not restart explosions of fields with active bombs */
2773 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2776 Explode(x, y, EX_PHASE_START, EX_BORDER);
2778 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2779 if (element != EL_EMPTY &&
2780 element != EL_SAND &&
2781 element != EL_EXPLOSION &&
2788 void Bang(int x, int y)
2791 int element = MovingOrBlocked2Element(x, y);
2793 int element = Feld[x][y];
2797 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2799 if (IS_PLAYER(x, y))
2802 struct PlayerInfo *player = PLAYERINFO(x, y);
2804 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2805 player->element_nr);
2810 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2812 if (game.emulation == EMU_SUPAPLEX)
2813 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2815 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2820 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2828 case EL_BD_BUTTERFLY:
2831 case EL_DARK_YAMYAM:
2835 RaiseScoreElement(element);
2836 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2838 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2839 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2840 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2841 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2842 case EL_DYNABOMB_INCREASE_NUMBER:
2843 case EL_DYNABOMB_INCREASE_SIZE:
2844 case EL_DYNABOMB_INCREASE_POWER:
2849 case EL_LAMP_ACTIVE:
2850 if (IS_PLAYER(x, y))
2851 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2853 Explode(x, y, EX_PHASE_START, EX_CENTER);
2856 if (CAN_EXPLODE_DYNA(element))
2858 else if (CAN_EXPLODE_1X1(element))
2859 Explode(x, y, EX_PHASE_START, EX_CENTER);
2861 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2865 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2868 void SplashAcid(int x, int y)
2870 int element = Feld[x][y];
2872 if (element != EL_ACID_SPLASH_LEFT &&
2873 element != EL_ACID_SPLASH_RIGHT)
2875 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2877 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2878 (!IN_LEV_FIELD(x-1, y-1) ||
2879 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2880 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2882 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2883 (!IN_LEV_FIELD(x+1, y-1) ||
2884 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2885 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2889 static void InitBeltMovement()
2891 static int belt_base_element[4] =
2893 EL_CONVEYOR_BELT_1_LEFT,
2894 EL_CONVEYOR_BELT_2_LEFT,
2895 EL_CONVEYOR_BELT_3_LEFT,
2896 EL_CONVEYOR_BELT_4_LEFT
2898 static int belt_base_active_element[4] =
2900 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2901 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2902 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2903 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2908 /* set frame order for belt animation graphic according to belt direction */
2909 for (i = 0; i < 4; i++)
2913 for (j = 0; j < 3; j++)
2915 int element = belt_base_active_element[belt_nr] + j;
2916 int graphic = el2img(element);
2918 if (game.belt_dir[i] == MV_LEFT)
2919 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2921 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2925 for (y = 0; y < lev_fieldy; y++)
2927 for (x = 0; x < lev_fieldx; x++)
2929 int element = Feld[x][y];
2931 for (i = 0; i < 4; i++)
2933 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2935 int e_belt_nr = getBeltNrFromBeltElement(element);
2938 if (e_belt_nr == belt_nr)
2940 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2942 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2950 static void ToggleBeltSwitch(int x, int y)
2952 static int belt_base_element[4] =
2954 EL_CONVEYOR_BELT_1_LEFT,
2955 EL_CONVEYOR_BELT_2_LEFT,
2956 EL_CONVEYOR_BELT_3_LEFT,
2957 EL_CONVEYOR_BELT_4_LEFT
2959 static int belt_base_active_element[4] =
2961 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2962 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2963 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2964 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2966 static int belt_base_switch_element[4] =
2968 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2969 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2970 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2971 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2973 static int belt_move_dir[4] =
2981 int element = Feld[x][y];
2982 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2983 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2984 int belt_dir = belt_move_dir[belt_dir_nr];
2987 if (!IS_BELT_SWITCH(element))
2990 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2991 game.belt_dir[belt_nr] = belt_dir;
2993 if (belt_dir_nr == 3)
2996 /* set frame order for belt animation graphic according to belt direction */
2997 for (i = 0; i < 3; i++)
2999 int element = belt_base_active_element[belt_nr] + i;
3000 int graphic = el2img(element);
3002 if (belt_dir == MV_LEFT)
3003 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3005 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3008 for (yy = 0; yy < lev_fieldy; yy++)
3010 for (xx = 0; xx < lev_fieldx; xx++)
3012 int element = Feld[xx][yy];
3014 if (IS_BELT_SWITCH(element))
3016 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3018 if (e_belt_nr == belt_nr)
3020 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3021 DrawLevelField(xx, yy);
3024 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3026 int e_belt_nr = getBeltNrFromBeltElement(element);
3028 if (e_belt_nr == belt_nr)
3030 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3032 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3033 DrawLevelField(xx, yy);
3036 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3038 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3040 if (e_belt_nr == belt_nr)
3042 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3044 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3045 DrawLevelField(xx, yy);
3052 static void ToggleSwitchgateSwitch(int x, int y)
3056 game.switchgate_pos = !game.switchgate_pos;
3058 for (yy = 0; yy < lev_fieldy; yy++)
3060 for (xx = 0; xx < lev_fieldx; xx++)
3062 int element = Feld[xx][yy];
3064 if (element == EL_SWITCHGATE_SWITCH_UP ||
3065 element == EL_SWITCHGATE_SWITCH_DOWN)
3067 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3068 DrawLevelField(xx, yy);
3070 else if (element == EL_SWITCHGATE_OPEN ||
3071 element == EL_SWITCHGATE_OPENING)
3073 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3075 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3077 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3080 else if (element == EL_SWITCHGATE_CLOSED ||
3081 element == EL_SWITCHGATE_CLOSING)
3083 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3085 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3087 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3094 static int getInvisibleActiveFromInvisibleElement(int element)
3096 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3097 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3098 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3102 static int getInvisibleFromInvisibleActiveElement(int element)
3104 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3105 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3106 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3110 static void RedrawAllLightSwitchesAndInvisibleElements()
3114 for (y = 0; y < lev_fieldy; y++)
3116 for (x = 0; x < lev_fieldx; x++)
3118 int element = Feld[x][y];
3120 if (element == EL_LIGHT_SWITCH &&
3121 game.light_time_left > 0)
3123 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3124 DrawLevelField(x, y);
3126 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3127 game.light_time_left == 0)
3129 Feld[x][y] = EL_LIGHT_SWITCH;
3130 DrawLevelField(x, y);
3132 else if (element == EL_INVISIBLE_STEELWALL ||
3133 element == EL_INVISIBLE_WALL ||
3134 element == EL_INVISIBLE_SAND)
3136 if (game.light_time_left > 0)
3137 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3139 DrawLevelField(x, y);
3141 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3142 element == EL_INVISIBLE_WALL_ACTIVE ||
3143 element == EL_INVISIBLE_SAND_ACTIVE)
3145 if (game.light_time_left == 0)
3146 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3148 DrawLevelField(x, y);
3154 static void ToggleLightSwitch(int x, int y)
3156 int element = Feld[x][y];
3158 game.light_time_left =
3159 (element == EL_LIGHT_SWITCH ?
3160 level.time_light * FRAMES_PER_SECOND : 0);
3162 RedrawAllLightSwitchesAndInvisibleElements();
3165 static void ActivateTimegateSwitch(int x, int y)
3169 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3171 for (yy = 0; yy < lev_fieldy; yy++)
3173 for (xx = 0; xx < lev_fieldx; xx++)
3175 int element = Feld[xx][yy];
3177 if (element == EL_TIMEGATE_CLOSED ||
3178 element == EL_TIMEGATE_CLOSING)
3180 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3181 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3185 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3187 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3188 DrawLevelField(xx, yy);
3195 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3198 inline static int getElementMoveStepsize(int x, int y)
3200 int element = Feld[x][y];
3201 int direction = MovDir[x][y];
3202 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3203 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3204 int horiz_move = (dx != 0);
3205 int sign = (horiz_move ? dx : dy);
3206 int step = sign * element_info[element].move_stepsize;
3208 /* special values for move stepsize for spring and things on conveyor belt */
3211 if (CAN_FALL(element) &&
3212 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3213 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3214 else if (element == EL_SPRING)
3215 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3221 void Impact(int x, int y)
3223 boolean lastline = (y == lev_fieldy-1);
3224 boolean object_hit = FALSE;
3225 boolean impact = (lastline || object_hit);
3226 int element = Feld[x][y];
3227 int smashed = EL_UNDEFINED;
3229 if (!lastline) /* check if element below was hit */
3231 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3234 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3235 MovDir[x][y + 1] != MV_DOWN ||
3236 MovPos[x][y + 1] <= TILEY / 2));
3239 object_hit = !IS_FREE(x, y + 1);
3242 /* do not smash moving elements that left the smashed field in time */
3243 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3244 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3248 smashed = MovingOrBlocked2Element(x, y + 1);
3250 impact = (lastline || object_hit);
3253 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3259 /* only reset graphic animation if graphic really changes after impact */
3261 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3263 ResetGfxAnimation(x, y);
3264 DrawLevelField(x, y);
3267 if (impact && CAN_EXPLODE_IMPACT(element))
3272 else if (impact && element == EL_PEARL)
3274 Feld[x][y] = EL_PEARL_BREAKING;
3275 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3278 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3280 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3285 if (impact && element == EL_AMOEBA_DROP)
3287 if (object_hit && IS_PLAYER(x, y + 1))
3288 KillHeroUnlessProtected(x, y + 1);
3289 else if (object_hit && smashed == EL_PENGUIN)
3293 Feld[x][y] = EL_AMOEBA_GROWING;
3294 Store[x][y] = EL_AMOEBA_WET;
3296 ResetRandomAnimationValue(x, y);
3301 if (object_hit) /* check which object was hit */
3303 if (CAN_PASS_MAGIC_WALL(element) &&
3304 (smashed == EL_MAGIC_WALL ||
3305 smashed == EL_BD_MAGIC_WALL))
3308 int activated_magic_wall =
3309 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3310 EL_BD_MAGIC_WALL_ACTIVE);
3312 /* activate magic wall / mill */
3313 for (yy = 0; yy < lev_fieldy; yy++)
3314 for (xx = 0; xx < lev_fieldx; xx++)
3315 if (Feld[xx][yy] == smashed)
3316 Feld[xx][yy] = activated_magic_wall;
3318 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3319 game.magic_wall_active = TRUE;
3321 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3322 SND_MAGIC_WALL_ACTIVATING :
3323 SND_BD_MAGIC_WALL_ACTIVATING));
3326 if (IS_PLAYER(x, y + 1))
3328 if (CAN_SMASH_PLAYER(element))
3330 KillHeroUnlessProtected(x, y + 1);
3334 else if (smashed == EL_PENGUIN)
3336 if (CAN_SMASH_PLAYER(element))
3342 else if (element == EL_BD_DIAMOND)
3344 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3350 else if ((element == EL_SP_INFOTRON ||
3351 element == EL_SP_ZONK) &&
3352 (smashed == EL_SP_SNIKSNAK ||
3353 smashed == EL_SP_ELECTRON ||
3354 smashed == EL_SP_DISK_ORANGE))
3360 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3366 else if (CAN_SMASH_EVERYTHING(element))
3368 if (IS_CLASSIC_ENEMY(smashed) ||
3369 CAN_EXPLODE_SMASHED(smashed))
3374 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3376 if (smashed == EL_LAMP ||
3377 smashed == EL_LAMP_ACTIVE)
3382 else if (smashed == EL_NUT)
3384 Feld[x][y + 1] = EL_NUT_BREAKING;
3385 PlayLevelSound(x, y, SND_NUT_BREAKING);
3386 RaiseScoreElement(EL_NUT);
3389 else if (smashed == EL_PEARL)
3391 Feld[x][y + 1] = EL_PEARL_BREAKING;
3392 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3395 else if (smashed == EL_DIAMOND)
3397 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3398 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3401 else if (IS_BELT_SWITCH(smashed))
3403 ToggleBeltSwitch(x, y + 1);
3405 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3406 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3408 ToggleSwitchgateSwitch(x, y + 1);
3410 else if (smashed == EL_LIGHT_SWITCH ||
3411 smashed == EL_LIGHT_SWITCH_ACTIVE)
3413 ToggleLightSwitch(x, y + 1);
3417 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3419 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3420 CE_OTHER_IS_SWITCHING);
3421 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3427 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3432 /* play sound of magic wall / mill */
3434 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3435 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3437 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3438 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3439 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3440 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3445 /* play sound of object that hits the ground */
3446 if (lastline || object_hit)
3447 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3450 inline static void TurnRoundExt(int x, int y)
3462 { 0, 0 }, { 0, 0 }, { 0, 0 },
3467 int left, right, back;
3471 { MV_DOWN, MV_UP, MV_RIGHT },
3472 { MV_UP, MV_DOWN, MV_LEFT },
3474 { MV_LEFT, MV_RIGHT, MV_DOWN },
3478 { MV_RIGHT, MV_LEFT, MV_UP }
3481 int element = Feld[x][y];
3482 int move_pattern = element_info[element].move_pattern;
3484 int old_move_dir = MovDir[x][y];
3485 int left_dir = turn[old_move_dir].left;
3486 int right_dir = turn[old_move_dir].right;
3487 int back_dir = turn[old_move_dir].back;
3489 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3490 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3491 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3492 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3494 int left_x = x + left_dx, left_y = y + left_dy;
3495 int right_x = x + right_dx, right_y = y + right_dy;
3496 int move_x = x + move_dx, move_y = y + move_dy;
3500 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3502 TestIfBadThingTouchesOtherBadThing(x, y);
3504 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3505 MovDir[x][y] = right_dir;
3506 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3507 MovDir[x][y] = left_dir;
3509 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3511 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3514 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3515 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3517 TestIfBadThingTouchesOtherBadThing(x, y);
3519 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3520 MovDir[x][y] = left_dir;
3521 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3522 MovDir[x][y] = right_dir;
3524 if ((element == EL_SPACESHIP ||
3525 element == EL_SP_SNIKSNAK ||
3526 element == EL_SP_ELECTRON)
3527 && MovDir[x][y] != old_move_dir)
3529 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3532 else if (element == EL_YAMYAM)
3534 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3535 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3537 if (can_turn_left && can_turn_right)
3538 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3539 else if (can_turn_left)
3540 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3541 else if (can_turn_right)
3542 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3544 MovDir[x][y] = back_dir;
3546 MovDelay[x][y] = 16 + 16 * RND(3);
3548 else if (element == EL_DARK_YAMYAM)
3550 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3551 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3553 if (can_turn_left && can_turn_right)
3554 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3555 else if (can_turn_left)
3556 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3557 else if (can_turn_right)
3558 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3560 MovDir[x][y] = back_dir;
3562 MovDelay[x][y] = 16 + 16 * RND(3);
3564 else if (element == EL_PACMAN)
3566 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3567 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3569 if (can_turn_left && can_turn_right)
3570 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3571 else if (can_turn_left)
3572 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3573 else if (can_turn_right)
3574 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3576 MovDir[x][y] = back_dir;
3578 MovDelay[x][y] = 6 + RND(40);
3580 else if (element == EL_PIG)
3582 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3583 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3584 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3585 boolean should_turn_left, should_turn_right, should_move_on;
3587 int rnd = RND(rnd_value);
3589 should_turn_left = (can_turn_left &&
3591 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3592 y + back_dy + left_dy)));
3593 should_turn_right = (can_turn_right &&
3595 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3596 y + back_dy + right_dy)));
3597 should_move_on = (can_move_on &&
3600 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3601 y + move_dy + left_dy) ||
3602 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3603 y + move_dy + right_dy)));
3605 if (should_turn_left || should_turn_right || should_move_on)
3607 if (should_turn_left && should_turn_right && should_move_on)
3608 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3609 rnd < 2 * rnd_value / 3 ? right_dir :
3611 else if (should_turn_left && should_turn_right)
3612 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3613 else if (should_turn_left && should_move_on)
3614 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3615 else if (should_turn_right && should_move_on)
3616 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3617 else if (should_turn_left)
3618 MovDir[x][y] = left_dir;
3619 else if (should_turn_right)
3620 MovDir[x][y] = right_dir;
3621 else if (should_move_on)
3622 MovDir[x][y] = old_move_dir;
3624 else if (can_move_on && rnd > rnd_value / 8)
3625 MovDir[x][y] = old_move_dir;
3626 else if (can_turn_left && can_turn_right)
3627 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3628 else if (can_turn_left && rnd > rnd_value / 8)
3629 MovDir[x][y] = left_dir;
3630 else if (can_turn_right && rnd > rnd_value/8)
3631 MovDir[x][y] = right_dir;
3633 MovDir[x][y] = back_dir;
3635 xx = x + move_xy[MovDir[x][y]].x;
3636 yy = y + move_xy[MovDir[x][y]].y;
3638 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3639 MovDir[x][y] = old_move_dir;
3643 else if (element == EL_DRAGON)
3645 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3646 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3647 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3649 int rnd = RND(rnd_value);
3652 if (FrameCounter < 1 && x == 0 && y == 29)
3653 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3656 if (can_move_on && rnd > rnd_value / 8)
3657 MovDir[x][y] = old_move_dir;
3658 else if (can_turn_left && can_turn_right)
3659 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3660 else if (can_turn_left && rnd > rnd_value / 8)
3661 MovDir[x][y] = left_dir;
3662 else if (can_turn_right && rnd > rnd_value / 8)
3663 MovDir[x][y] = right_dir;
3665 MovDir[x][y] = back_dir;
3667 xx = x + move_xy[MovDir[x][y]].x;
3668 yy = y + move_xy[MovDir[x][y]].y;
3671 if (FrameCounter < 1 && x == 0 && y == 29)
3672 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3673 xx, yy, Feld[xx][yy],
3678 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3679 MovDir[x][y] = old_move_dir;
3681 if (!IS_FREE(xx, yy))
3682 MovDir[x][y] = old_move_dir;
3686 if (FrameCounter < 1 && x == 0 && y == 29)
3687 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3692 else if (element == EL_MOLE)
3694 boolean can_move_on =
3695 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3696 IS_AMOEBOID(Feld[move_x][move_y]) ||
3697 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3700 boolean can_turn_left =
3701 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3702 IS_AMOEBOID(Feld[left_x][left_y])));
3704 boolean can_turn_right =
3705 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3706 IS_AMOEBOID(Feld[right_x][right_y])));
3708 if (can_turn_left && can_turn_right)
3709 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3710 else if (can_turn_left)
3711 MovDir[x][y] = left_dir;
3713 MovDir[x][y] = right_dir;
3716 if (MovDir[x][y] != old_move_dir)
3719 else if (element == EL_BALLOON)
3721 MovDir[x][y] = game.balloon_dir;
3724 else if (element == EL_SPRING)
3726 if (MovDir[x][y] & MV_HORIZONTAL &&
3727 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3728 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3729 MovDir[x][y] = MV_NO_MOVING;
3733 else if (element == EL_ROBOT ||
3734 element == EL_SATELLITE ||
3735 element == EL_PENGUIN)
3737 int attr_x = -1, attr_y = -1;
3748 for (i = 0; i < MAX_PLAYERS; i++)
3750 struct PlayerInfo *player = &stored_player[i];
3751 int jx = player->jx, jy = player->jy;
3753 if (!player->active)
3757 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3765 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3771 if (element == EL_PENGUIN)
3774 static int xy[4][2] =
3782 for (i = 0; i < 4; i++)
3784 int ex = x + xy[i % 4][0];
3785 int ey = y + xy[i % 4][1];
3787 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3796 MovDir[x][y] = MV_NO_MOVING;
3798 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3799 else if (attr_x > x)
3800 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3802 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3803 else if (attr_y > y)
3804 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3806 if (element == EL_ROBOT)
3810 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3811 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3812 Moving2Blocked(x, y, &newx, &newy);
3814 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3815 MovDelay[x][y] = 8 + 8 * !RND(3);
3817 MovDelay[x][y] = 16;
3819 else if (element == EL_PENGUIN)
3825 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3827 boolean first_horiz = RND(2);
3828 int new_move_dir = MovDir[x][y];
3831 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3832 Moving2Blocked(x, y, &newx, &newy);
3834 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3838 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3839 Moving2Blocked(x, y, &newx, &newy);
3841 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3844 MovDir[x][y] = old_move_dir;
3848 else /* (element == EL_SATELLITE) */
3854 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3856 boolean first_horiz = RND(2);
3857 int new_move_dir = MovDir[x][y];
3860 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3861 Moving2Blocked(x, y, &newx, &newy);
3863 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3867 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3868 Moving2Blocked(x, y, &newx, &newy);
3870 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3873 MovDir[x][y] = old_move_dir;
3878 else if (move_pattern == MV_TURNING_LEFT ||
3879 move_pattern == MV_TURNING_RIGHT ||
3880 move_pattern == MV_TURNING_LEFT_RIGHT ||
3881 move_pattern == MV_TURNING_RIGHT_LEFT ||
3882 move_pattern == MV_TURNING_RANDOM ||
3883 move_pattern == MV_ALL_DIRECTIONS)
3885 boolean can_turn_left =
3886 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3887 boolean can_turn_right =
3888 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3890 if (move_pattern == MV_TURNING_LEFT)
3891 MovDir[x][y] = left_dir;
3892 else if (move_pattern == MV_TURNING_RIGHT)
3893 MovDir[x][y] = right_dir;
3894 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3895 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3896 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3897 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3898 else if (move_pattern == MV_TURNING_RANDOM)
3899 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3900 can_turn_right && !can_turn_left ? right_dir :
3901 RND(2) ? left_dir : right_dir);
3902 else if (can_turn_left && can_turn_right)
3903 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3904 else if (can_turn_left)
3905 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3906 else if (can_turn_right)
3907 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3909 MovDir[x][y] = back_dir;
3911 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3913 else if (move_pattern == MV_HORIZONTAL ||
3914 move_pattern == MV_VERTICAL)
3916 if (move_pattern & old_move_dir)
3917 MovDir[x][y] = back_dir;
3918 else if (move_pattern == MV_HORIZONTAL)
3919 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3920 else if (move_pattern == MV_VERTICAL)
3921 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3923 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3925 else if (move_pattern & MV_ANY_DIRECTION)
3927 MovDir[x][y] = move_pattern;
3928 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3930 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3932 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3933 MovDir[x][y] = left_dir;
3934 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3935 MovDir[x][y] = right_dir;
3937 if (MovDir[x][y] != old_move_dir)
3938 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3940 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3942 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3943 MovDir[x][y] = right_dir;
3944 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3945 MovDir[x][y] = left_dir;
3947 if (MovDir[x][y] != old_move_dir)
3948 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3950 else if (move_pattern == MV_TOWARDS_PLAYER ||
3951 move_pattern == MV_AWAY_FROM_PLAYER)
3953 int attr_x = -1, attr_y = -1;
3955 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3966 for (i = 0; i < MAX_PLAYERS; i++)
3968 struct PlayerInfo *player = &stored_player[i];
3969 int jx = player->jx, jy = player->jy;
3971 if (!player->active)
3975 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3983 MovDir[x][y] = MV_NO_MOVING;
3985 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3986 else if (attr_x > x)
3987 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3989 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3990 else if (attr_y > y)
3991 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3993 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3995 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3997 boolean first_horiz = RND(2);
3998 int new_move_dir = MovDir[x][y];
4001 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4002 Moving2Blocked(x, y, &newx, &newy);
4004 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4008 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4009 Moving2Blocked(x, y, &newx, &newy);
4011 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4014 MovDir[x][y] = old_move_dir;
4017 else if (move_pattern == MV_WHEN_PUSHED ||
4018 move_pattern == MV_WHEN_DROPPED)
4020 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4021 MovDir[x][y] = MV_NO_MOVING;
4025 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4027 static int test_xy[7][2] =
4037 static int test_dir[7] =
4047 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4048 int move_preference = -1000000; /* start with very low preference */
4049 int new_move_dir = MV_NO_MOVING;
4050 int start_test = RND(4);
4053 for (i = 0; i < 4; i++)
4055 int move_dir = test_dir[start_test + i];
4056 int move_dir_preference;
4058 xx = x + test_xy[start_test + i][0];
4059 yy = y + test_xy[start_test + i][1];
4061 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4062 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4064 new_move_dir = move_dir;
4069 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4072 move_dir_preference = -1 * RunnerVisit[xx][yy];
4073 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4074 move_dir_preference = PlayerVisit[xx][yy];
4076 if (move_dir_preference > move_preference)
4078 /* prefer field that has not been visited for the longest time */
4079 move_preference = move_dir_preference;
4080 new_move_dir = move_dir;
4082 else if (move_dir_preference == move_preference &&
4083 move_dir == old_move_dir)
4085 /* prefer last direction when all directions are preferred equally */
4086 move_preference = move_dir_preference;
4087 new_move_dir = move_dir;
4091 MovDir[x][y] = new_move_dir;
4092 if (old_move_dir != new_move_dir)
4097 static void TurnRound(int x, int y)
4099 int direction = MovDir[x][y];
4102 GfxDir[x][y] = MovDir[x][y];
4108 GfxDir[x][y] = MovDir[x][y];
4111 if (direction != MovDir[x][y])
4116 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4119 GfxAction[x][y] = ACTION_WAITING;
4123 static boolean JustBeingPushed(int x, int y)
4127 for (i = 0; i < MAX_PLAYERS; i++)
4129 struct PlayerInfo *player = &stored_player[i];
4131 if (player->active && player->is_pushing && player->MovPos)
4133 int next_jx = player->jx + (player->jx - player->last_jx);
4134 int next_jy = player->jy + (player->jy - player->last_jy);
4136 if (x == next_jx && y == next_jy)
4144 void StartMoving(int x, int y)
4146 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4147 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4148 int element = Feld[x][y];
4154 if (MovDelay[x][y] == 0)
4155 GfxAction[x][y] = ACTION_DEFAULT;
4157 /* !!! this should be handled more generic (not only for mole) !!! */
4158 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4159 GfxAction[x][y] = ACTION_DEFAULT;
4162 if (CAN_FALL(element) && y < lev_fieldy - 1)
4164 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4165 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4166 if (JustBeingPushed(x, y))
4169 if (element == EL_QUICKSAND_FULL)
4171 if (IS_FREE(x, y + 1))
4173 InitMovingField(x, y, MV_DOWN);
4174 started_moving = TRUE;
4176 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4177 Store[x][y] = EL_ROCK;
4179 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4181 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4184 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4186 if (!MovDelay[x][y])
4187 MovDelay[x][y] = TILEY + 1;
4196 Feld[x][y] = EL_QUICKSAND_EMPTY;
4197 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4198 Store[x][y + 1] = Store[x][y];
4201 PlayLevelSoundAction(x, y, ACTION_FILLING);
4203 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4207 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4208 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4210 InitMovingField(x, y, MV_DOWN);
4211 started_moving = TRUE;
4213 Feld[x][y] = EL_QUICKSAND_FILLING;
4214 Store[x][y] = element;
4216 PlayLevelSoundAction(x, y, ACTION_FILLING);
4218 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4221 else if (element == EL_MAGIC_WALL_FULL)
4223 if (IS_FREE(x, y + 1))
4225 InitMovingField(x, y, MV_DOWN);
4226 started_moving = TRUE;
4228 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4229 Store[x][y] = EL_CHANGED(Store[x][y]);
4231 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4233 if (!MovDelay[x][y])
4234 MovDelay[x][y] = TILEY/4 + 1;
4243 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4244 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4245 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4249 else if (element == EL_BD_MAGIC_WALL_FULL)
4251 if (IS_FREE(x, y + 1))
4253 InitMovingField(x, y, MV_DOWN);
4254 started_moving = TRUE;
4256 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4257 Store[x][y] = EL_CHANGED2(Store[x][y]);
4259 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4261 if (!MovDelay[x][y])
4262 MovDelay[x][y] = TILEY/4 + 1;
4271 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4272 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4273 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4277 else if (CAN_PASS_MAGIC_WALL(element) &&
4278 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4279 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4281 InitMovingField(x, y, MV_DOWN);
4282 started_moving = TRUE;
4285 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4286 EL_BD_MAGIC_WALL_FILLING);
4287 Store[x][y] = element;
4290 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4292 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4297 InitMovingField(x, y, MV_DOWN);
4298 started_moving = TRUE;
4300 Store[x][y] = EL_ACID;
4302 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4303 GfxAction[x][y + 1] = ACTION_ACTIVE;
4307 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4308 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4309 (Feld[x][y + 1] == EL_BLOCKED)) ||
4310 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4311 CAN_SMASH(element) && WasJustFalling[x][y] &&
4312 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4316 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4317 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4318 WasJustMoving[x][y] && !Pushed[x][y + 1])
4320 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4321 WasJustMoving[x][y])
4326 /* this is needed for a special case not covered by calling "Impact()"
4327 from "ContinueMoving()": if an element moves to a tile directly below
4328 another element which was just falling on that tile (which was empty
4329 in the previous frame), the falling element above would just stop
4330 instead of smashing the element below (in previous version, the above
4331 element was just checked for "moving" instead of "falling", resulting
4332 in incorrect smashes caused by horizontal movement of the above
4333 element; also, the case of the player being the element to smash was
4334 simply not covered here... :-/ ) */
4337 WasJustMoving[x][y] = 0;
4338 WasJustFalling[x][y] = 0;
4343 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4345 if (MovDir[x][y] == MV_NO_MOVING)
4347 InitMovingField(x, y, MV_DOWN);
4348 started_moving = TRUE;
4351 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4353 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4354 MovDir[x][y] = MV_DOWN;
4356 InitMovingField(x, y, MV_DOWN);
4357 started_moving = TRUE;
4359 else if (element == EL_AMOEBA_DROP)
4361 Feld[x][y] = EL_AMOEBA_GROWING;
4362 Store[x][y] = EL_AMOEBA_WET;
4364 /* Store[x][y + 1] must be zero, because:
4365 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4368 #if OLD_GAME_BEHAVIOUR
4369 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4371 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4372 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4373 element != EL_DX_SUPABOMB)
4376 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4377 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4378 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4379 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4382 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4383 (IS_FREE(x - 1, y + 1) ||
4384 Feld[x - 1][y + 1] == EL_ACID));
4385 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4386 (IS_FREE(x + 1, y + 1) ||
4387 Feld[x + 1][y + 1] == EL_ACID));
4388 boolean can_fall_any = (can_fall_left || can_fall_right);
4389 boolean can_fall_both = (can_fall_left && can_fall_right);
4391 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4393 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4395 if (slippery_type == SLIPPERY_ONLY_LEFT)
4396 can_fall_right = FALSE;
4397 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4398 can_fall_left = FALSE;
4399 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4400 can_fall_right = FALSE;
4401 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4402 can_fall_left = FALSE;
4404 can_fall_any = (can_fall_left || can_fall_right);
4405 can_fall_both = (can_fall_left && can_fall_right);
4410 if (can_fall_both &&
4411 (game.emulation != EMU_BOULDERDASH &&
4412 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4413 can_fall_left = !(can_fall_right = RND(2));
4415 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4416 started_moving = TRUE;
4419 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4421 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4422 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4423 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4424 int belt_dir = game.belt_dir[belt_nr];
4426 if ((belt_dir == MV_LEFT && left_is_free) ||
4427 (belt_dir == MV_RIGHT && right_is_free))
4429 InitMovingField(x, y, belt_dir);
4430 started_moving = TRUE;
4432 GfxAction[x][y] = ACTION_DEFAULT;
4437 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4438 if (CAN_MOVE(element) && !started_moving)
4440 int move_pattern = element_info[element].move_pattern;
4443 Moving2Blocked(x, y, &newx, &newy);
4446 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4449 if ((element == EL_SATELLITE ||
4450 element == EL_BALLOON ||
4451 element == EL_SPRING)
4452 && JustBeingPushed(x, y))
4457 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4458 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4459 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4462 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4463 element, element_info[element].token_name,
4464 WasJustMoving[x][y],
4465 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4466 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4467 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4468 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4472 WasJustMoving[x][y] = 0;
4475 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4478 if (Feld[x][y] != element) /* element has changed */
4480 element = Feld[x][y];
4481 move_pattern = element_info[element].move_pattern;
4483 if (!CAN_MOVE(element))
4487 if (Feld[x][y] != element) /* element has changed */
4495 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4496 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4498 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4500 Moving2Blocked(x, y, &newx, &newy);
4501 if (Feld[newx][newy] == EL_BLOCKED)
4502 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4508 if (FrameCounter < 1 && x == 0 && y == 29)
4509 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4512 if (!MovDelay[x][y]) /* start new movement phase */
4514 /* all objects that can change their move direction after each step
4515 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4517 if (element != EL_YAMYAM &&
4518 element != EL_DARK_YAMYAM &&
4519 element != EL_PACMAN &&
4520 !(move_pattern & MV_ANY_DIRECTION) &&
4521 move_pattern != MV_TURNING_LEFT &&
4522 move_pattern != MV_TURNING_RIGHT &&
4523 move_pattern != MV_TURNING_LEFT_RIGHT &&
4524 move_pattern != MV_TURNING_RIGHT_LEFT &&
4525 move_pattern != MV_TURNING_RANDOM)
4530 if (FrameCounter < 1 && x == 0 && y == 29)
4531 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4534 if (MovDelay[x][y] && (element == EL_BUG ||
4535 element == EL_SPACESHIP ||
4536 element == EL_SP_SNIKSNAK ||
4537 element == EL_SP_ELECTRON ||
4538 element == EL_MOLE))
4539 DrawLevelField(x, y);
4543 if (MovDelay[x][y]) /* wait some time before next movement */
4548 if (element == EL_YAMYAM)
4551 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4552 DrawLevelElementAnimation(x, y, element);
4556 if (MovDelay[x][y]) /* element still has to wait some time */
4559 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4560 ResetGfxAnimation(x, y);
4564 if (GfxAction[x][y] != ACTION_WAITING)
4565 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4567 GfxAction[x][y] = ACTION_WAITING;
4571 if (element == EL_ROBOT ||
4573 element == EL_PACMAN ||
4575 element == EL_YAMYAM ||
4576 element == EL_DARK_YAMYAM)
4579 DrawLevelElementAnimation(x, y, element);
4581 DrawLevelElementAnimationIfNeeded(x, y, element);
4583 PlayLevelSoundAction(x, y, ACTION_WAITING);
4585 else if (element == EL_SP_ELECTRON)
4586 DrawLevelElementAnimationIfNeeded(x, y, element);
4587 else if (element == EL_DRAGON)
4590 int dir = MovDir[x][y];
4591 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4592 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4593 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4594 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4595 dir == MV_UP ? IMG_FLAMES_1_UP :
4596 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4597 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4600 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4603 GfxAction[x][y] = ACTION_ATTACKING;
4605 if (IS_PLAYER(x, y))
4606 DrawPlayerField(x, y);
4608 DrawLevelField(x, y);
4610 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4612 for (i = 1; i <= 3; i++)
4614 int xx = x + i * dx;
4615 int yy = y + i * dy;
4616 int sx = SCREENX(xx);
4617 int sy = SCREENY(yy);
4618 int flame_graphic = graphic + (i - 1);
4620 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4625 int flamed = MovingOrBlocked2Element(xx, yy);
4627 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4630 RemoveMovingField(xx, yy);
4632 Feld[xx][yy] = EL_FLAMES;
4633 if (IN_SCR_FIELD(sx, sy))
4635 DrawLevelFieldCrumbledSand(xx, yy);
4636 DrawGraphic(sx, sy, flame_graphic, frame);
4641 if (Feld[xx][yy] == EL_FLAMES)
4642 Feld[xx][yy] = EL_EMPTY;
4643 DrawLevelField(xx, yy);
4648 if (MovDelay[x][y]) /* element still has to wait some time */
4650 PlayLevelSoundAction(x, y, ACTION_WAITING);
4656 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4657 for all other elements GfxAction will be set by InitMovingField() */
4658 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4659 GfxAction[x][y] = ACTION_MOVING;
4663 /* now make next step */
4665 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4667 if (DONT_COLLIDE_WITH(element) &&
4668 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4669 !PLAYER_PROTECTED(newx, newy))
4672 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4676 /* player killed by element which is deadly when colliding with */
4678 KillHero(PLAYERINFO(newx, newy));
4683 else if ((element == EL_PENGUIN ||
4684 element == EL_ROBOT ||
4685 element == EL_SATELLITE ||
4686 element == EL_BALLOON ||
4687 IS_CUSTOM_ELEMENT(element)) &&
4688 IN_LEV_FIELD(newx, newy) &&
4689 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4692 Store[x][y] = EL_ACID;
4694 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4696 if (Feld[newx][newy] == EL_EXIT_OPEN)
4700 DrawLevelField(x, y);
4702 Feld[x][y] = EL_EMPTY;
4703 DrawLevelField(x, y);
4706 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4707 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4708 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4710 local_player->friends_still_needed--;
4711 if (!local_player->friends_still_needed &&
4712 !local_player->GameOver && AllPlayersGone)
4713 local_player->LevelSolved = local_player->GameOver = TRUE;
4717 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4719 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4720 DrawLevelField(newx, newy);
4722 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4724 else if (!IS_FREE(newx, newy))
4726 GfxAction[x][y] = ACTION_WAITING;
4728 if (IS_PLAYER(x, y))
4729 DrawPlayerField(x, y);
4731 DrawLevelField(x, y);
4736 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4738 if (IS_FOOD_PIG(Feld[newx][newy]))
4740 if (IS_MOVING(newx, newy))
4741 RemoveMovingField(newx, newy);
4744 Feld[newx][newy] = EL_EMPTY;
4745 DrawLevelField(newx, newy);
4748 PlayLevelSound(x, y, SND_PIG_DIGGING);
4750 else if (!IS_FREE(newx, newy))
4752 if (IS_PLAYER(x, y))
4753 DrawPlayerField(x, y);
4755 DrawLevelField(x, y);
4764 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4767 else if (IS_CUSTOM_ELEMENT(element) &&
4768 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4772 !IS_FREE(newx, newy)
4777 int new_element = Feld[newx][newy];
4780 printf("::: '%s' digs '%s' [%d]\n",
4781 element_info[element].token_name,
4782 element_info[Feld[newx][newy]].token_name,
4783 StorePlayer[newx][newy]);
4786 if (!IS_FREE(newx, newy))
4788 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4789 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4792 /* no element can dig solid indestructible elements */
4793 if (IS_INDESTRUCTIBLE(new_element) &&
4794 !IS_DIGGABLE(new_element) &&
4795 !IS_COLLECTIBLE(new_element))
4798 if (AmoebaNr[newx][newy] &&
4799 (new_element == EL_AMOEBA_FULL ||
4800 new_element == EL_BD_AMOEBA ||
4801 new_element == EL_AMOEBA_GROWING))
4803 AmoebaCnt[AmoebaNr[newx][newy]]--;
4804 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4807 if (IS_MOVING(newx, newy))
4808 RemoveMovingField(newx, newy);
4811 RemoveField(newx, newy);
4812 DrawLevelField(newx, newy);
4815 PlayLevelSoundAction(x, y, action);
4818 if (new_element == element_info[element].move_enter_element)
4819 element_info[element].can_leave_element = TRUE;
4821 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4823 RunnerVisit[x][y] = FrameCounter;
4824 PlayerVisit[x][y] /= 8; /* expire player visit path */
4830 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4832 if (!IS_FREE(newx, newy))
4834 if (IS_PLAYER(x, y))
4835 DrawPlayerField(x, y);
4837 DrawLevelField(x, y);
4843 boolean wanna_flame = !RND(10);
4844 int dx = newx - x, dy = newy - y;
4845 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4846 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4847 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4848 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4849 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4850 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4853 IS_CLASSIC_ENEMY(element1) ||
4854 IS_CLASSIC_ENEMY(element2)) &&
4855 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4856 element1 != EL_FLAMES && element2 != EL_FLAMES)
4859 ResetGfxAnimation(x, y);
4860 GfxAction[x][y] = ACTION_ATTACKING;
4863 if (IS_PLAYER(x, y))
4864 DrawPlayerField(x, y);
4866 DrawLevelField(x, y);
4868 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4870 MovDelay[x][y] = 50;
4872 Feld[newx][newy] = EL_FLAMES;
4873 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4874 Feld[newx1][newy1] = EL_FLAMES;
4875 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4876 Feld[newx2][newy2] = EL_FLAMES;
4882 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4883 Feld[newx][newy] == EL_DIAMOND)
4885 if (IS_MOVING(newx, newy))
4886 RemoveMovingField(newx, newy);
4889 Feld[newx][newy] = EL_EMPTY;
4890 DrawLevelField(newx, newy);
4893 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4895 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4896 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4898 if (AmoebaNr[newx][newy])
4900 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4901 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4902 Feld[newx][newy] == EL_BD_AMOEBA)
4903 AmoebaCnt[AmoebaNr[newx][newy]]--;
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_DARK_YAMYAM_DIGGING);
4916 else if ((element == EL_PACMAN || element == EL_MOLE)
4917 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(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 (element == EL_MOLE)
4929 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4930 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4932 ResetGfxAnimation(x, y);
4933 GfxAction[x][y] = ACTION_DIGGING;
4934 DrawLevelField(x, y);
4936 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4938 return; /* wait for shrinking amoeba */
4940 else /* element == EL_PACMAN */
4942 Feld[newx][newy] = EL_EMPTY;
4943 DrawLevelField(newx, newy);
4944 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4947 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4948 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4949 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4951 /* wait for shrinking amoeba to completely disappear */
4954 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4956 /* object was running against a wall */
4961 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4962 DrawLevelElementAnimation(x, y, element);
4964 if (element == EL_BUG ||
4965 element == EL_SPACESHIP ||
4966 element == EL_SP_SNIKSNAK)
4967 DrawLevelField(x, y);
4968 else if (element == EL_MOLE)
4969 DrawLevelField(x, y);
4970 else if (element == EL_BD_BUTTERFLY ||
4971 element == EL_BD_FIREFLY)
4972 DrawLevelElementAnimationIfNeeded(x, y, element);
4973 else if (element == EL_SATELLITE)
4974 DrawLevelElementAnimationIfNeeded(x, y, element);
4975 else if (element == EL_SP_ELECTRON)
4976 DrawLevelElementAnimationIfNeeded(x, y, element);
4979 if (DONT_TOUCH(element))
4980 TestIfBadThingTouchesHero(x, y);
4983 PlayLevelSoundAction(x, y, ACTION_WAITING);
4989 InitMovingField(x, y, MovDir[x][y]);
4991 PlayLevelSoundAction(x, y, ACTION_MOVING);
4995 ContinueMoving(x, y);
4998 void ContinueMoving(int x, int y)
5000 int element = Feld[x][y];
5001 struct ElementInfo *ei = &element_info[element];
5002 int direction = MovDir[x][y];
5003 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5004 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5005 int newx = x + dx, newy = y + dy;
5007 int nextx = newx + dx, nexty = newy + dy;
5009 boolean pushed = Pushed[x][y];
5011 MovPos[x][y] += getElementMoveStepsize(x, y);
5013 if (pushed) /* special case: moving object pushed by player */
5014 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5016 if (ABS(MovPos[x][y]) < TILEX)
5018 DrawLevelField(x, y);
5020 return; /* element is still moving */
5023 /* element reached destination field */
5025 Feld[x][y] = EL_EMPTY;
5026 Feld[newx][newy] = element;
5027 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5029 if (element == EL_MOLE)
5031 Feld[x][y] = EL_SAND;
5033 DrawLevelFieldCrumbledSandNeighbours(x, y);
5035 else if (element == EL_QUICKSAND_FILLING)
5037 element = Feld[newx][newy] = get_next_element(element);
5038 Store[newx][newy] = Store[x][y];
5040 else if (element == EL_QUICKSAND_EMPTYING)
5042 Feld[x][y] = get_next_element(element);
5043 element = Feld[newx][newy] = Store[x][y];
5045 else if (element == EL_MAGIC_WALL_FILLING)
5047 element = Feld[newx][newy] = get_next_element(element);
5048 if (!game.magic_wall_active)
5049 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5050 Store[newx][newy] = Store[x][y];
5052 else if (element == EL_MAGIC_WALL_EMPTYING)
5054 Feld[x][y] = get_next_element(element);
5055 if (!game.magic_wall_active)
5056 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5057 element = Feld[newx][newy] = Store[x][y];
5059 else if (element == EL_BD_MAGIC_WALL_FILLING)
5061 element = Feld[newx][newy] = get_next_element(element);
5062 if (!game.magic_wall_active)
5063 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5064 Store[newx][newy] = Store[x][y];
5066 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5068 Feld[x][y] = get_next_element(element);
5069 if (!game.magic_wall_active)
5070 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5071 element = Feld[newx][newy] = Store[x][y];
5073 else if (element == EL_AMOEBA_DROPPING)
5075 Feld[x][y] = get_next_element(element);
5076 element = Feld[newx][newy] = Store[x][y];
5078 else if (element == EL_SOKOBAN_OBJECT)
5081 Feld[x][y] = Back[x][y];
5083 if (Back[newx][newy])
5084 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5086 Back[x][y] = Back[newx][newy] = 0;
5088 else if (Store[x][y] == EL_ACID)
5090 element = Feld[newx][newy] = EL_ACID;
5094 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5095 MovDelay[newx][newy] = 0;
5097 /* copy element change control values to new field */
5098 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5099 ChangePage[newx][newy] = ChangePage[x][y];
5100 Changed[newx][newy] = Changed[x][y];
5101 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5103 ChangeDelay[x][y] = 0;
5104 ChangePage[x][y] = -1;
5105 Changed[x][y] = CE_BITMASK_DEFAULT;
5106 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5108 /* copy animation control values to new field */
5109 GfxFrame[newx][newy] = GfxFrame[x][y];
5110 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5111 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5112 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5114 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5116 ResetGfxAnimation(x, y); /* reset animation values for old field */
5119 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5120 ei->move_leave_element != EL_EMPTY &&
5121 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5122 ei->can_leave_element_last))
5124 Feld[x][y] = ei->move_leave_element;
5125 InitField(x, y, FALSE);
5127 if (GFX_CRUMBLED(Feld[x][y]))
5128 DrawLevelFieldCrumbledSandNeighbours(x, y);
5131 ei->can_leave_element_last = ei->can_leave_element;
5132 ei->can_leave_element = FALSE;
5136 /* 2.1.1 (does not work correctly for spring) */
5137 if (!CAN_MOVE(element))
5138 MovDir[newx][newy] = 0;
5142 /* (does not work for falling objects that slide horizontally) */
5143 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5144 MovDir[newx][newy] = 0;
5147 if (!CAN_MOVE(element) ||
5148 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5149 MovDir[newx][newy] = 0;
5152 if (!CAN_MOVE(element) ||
5153 (CAN_FALL(element) && direction == MV_DOWN))
5154 GfxDir[x][y] = MovDir[newx][newy] = 0;
5159 DrawLevelField(x, y);
5160 DrawLevelField(newx, newy);
5162 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5164 /* prevent pushed element from moving on in pushed direction */
5165 if (pushed && CAN_MOVE(element) &&
5166 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5167 !(element_info[element].move_pattern & direction))
5168 TurnRound(newx, newy);
5170 if (!pushed) /* special case: moving object pushed by player */
5172 WasJustMoving[newx][newy] = 3;
5174 if (CAN_FALL(element) && direction == MV_DOWN)
5175 WasJustFalling[newx][newy] = 3;
5178 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5180 TestIfBadThingTouchesHero(newx, newy);
5181 TestIfBadThingTouchesFriend(newx, newy);
5183 if (!IS_CUSTOM_ELEMENT(element))
5184 TestIfBadThingTouchesOtherBadThing(newx, newy);
5186 else if (element == EL_PENGUIN)
5187 TestIfFriendTouchesBadThing(newx, newy);
5189 if (CAN_FALL(element) && direction == MV_DOWN &&
5190 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5194 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5198 if (ChangePage[newx][newy] != -1) /* delayed change */
5199 ChangeElement(newx, newy, ChangePage[newx][newy]);
5204 TestIfElementHitsCustomElement(newx, newy, direction);
5208 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5210 int hitting_element = Feld[newx][newy];
5212 /* !!! fix side (direction) orientation here and elsewhere !!! */
5213 CheckElementSideChange(newx, newy, hitting_element,
5214 direction, CE_HITTING_SOMETHING, -1);
5217 if (IN_LEV_FIELD(nextx, nexty))
5219 static int opposite_directions[] =
5226 int move_dir_bit = MV_DIR_BIT(direction);
5227 int opposite_direction = opposite_directions[move_dir_bit];
5228 int hitting_side = direction;
5229 int touched_side = opposite_direction;
5230 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5231 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5232 MovDir[nextx][nexty] != direction ||
5233 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5239 CheckElementSideChange(nextx, nexty, touched_element,
5240 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5242 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5243 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5245 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5247 struct ElementChangeInfo *change =
5248 &element_info[hitting_element].change_page[i];
5250 if (change->can_change &&
5251 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5252 change->sides & touched_side &&
5253 change->trigger_element == touched_element)
5255 CheckElementSideChange(newx, newy, hitting_element,
5256 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5262 if (IS_CUSTOM_ELEMENT(touched_element) &&
5263 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5265 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5267 struct ElementChangeInfo *change =
5268 &element_info[touched_element].change_page[i];
5270 if (change->can_change &&
5271 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5272 change->sides & hitting_side &&
5273 change->trigger_element == hitting_element)
5275 CheckElementSideChange(nextx, nexty, touched_element,
5276 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5287 TestIfPlayerTouchesCustomElement(newx, newy);
5288 TestIfElementTouchesCustomElement(newx, newy);
5291 int AmoebeNachbarNr(int ax, int ay)
5294 int element = Feld[ax][ay];
5296 static int xy[4][2] =
5304 for (i = 0; i < 4; i++)
5306 int x = ax + xy[i][0];
5307 int y = ay + xy[i][1];
5309 if (!IN_LEV_FIELD(x, y))
5312 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5313 group_nr = AmoebaNr[x][y];
5319 void AmoebenVereinigen(int ax, int ay)
5321 int i, x, y, xx, yy;
5322 int new_group_nr = AmoebaNr[ax][ay];
5323 static int xy[4][2] =
5331 if (new_group_nr == 0)
5334 for (i = 0; i < 4; i++)
5339 if (!IN_LEV_FIELD(x, y))
5342 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5343 Feld[x][y] == EL_BD_AMOEBA ||
5344 Feld[x][y] == EL_AMOEBA_DEAD) &&
5345 AmoebaNr[x][y] != new_group_nr)
5347 int old_group_nr = AmoebaNr[x][y];
5349 if (old_group_nr == 0)
5352 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5353 AmoebaCnt[old_group_nr] = 0;
5354 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5355 AmoebaCnt2[old_group_nr] = 0;
5357 for (yy = 0; yy < lev_fieldy; yy++)
5359 for (xx = 0; xx < lev_fieldx; xx++)
5361 if (AmoebaNr[xx][yy] == old_group_nr)
5362 AmoebaNr[xx][yy] = new_group_nr;
5369 void AmoebeUmwandeln(int ax, int ay)
5373 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5375 int group_nr = AmoebaNr[ax][ay];
5380 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5381 printf("AmoebeUmwandeln(): This should never happen!\n");
5386 for (y = 0; y < lev_fieldy; y++)
5388 for (x = 0; x < lev_fieldx; x++)
5390 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5393 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5397 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5398 SND_AMOEBA_TURNING_TO_GEM :
5399 SND_AMOEBA_TURNING_TO_ROCK));
5404 static int xy[4][2] =
5412 for (i = 0; i < 4; i++)
5417 if (!IN_LEV_FIELD(x, y))
5420 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5422 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5423 SND_AMOEBA_TURNING_TO_GEM :
5424 SND_AMOEBA_TURNING_TO_ROCK));
5431 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5434 int group_nr = AmoebaNr[ax][ay];
5435 boolean done = FALSE;
5440 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5441 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5446 for (y = 0; y < lev_fieldy; y++)
5448 for (x = 0; x < lev_fieldx; x++)
5450 if (AmoebaNr[x][y] == group_nr &&
5451 (Feld[x][y] == EL_AMOEBA_DEAD ||
5452 Feld[x][y] == EL_BD_AMOEBA ||
5453 Feld[x][y] == EL_AMOEBA_GROWING))
5456 Feld[x][y] = new_element;
5457 InitField(x, y, FALSE);
5458 DrawLevelField(x, y);
5465 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5466 SND_BD_AMOEBA_TURNING_TO_ROCK :
5467 SND_BD_AMOEBA_TURNING_TO_GEM));
5470 void AmoebeWaechst(int x, int y)
5472 static unsigned long sound_delay = 0;
5473 static unsigned long sound_delay_value = 0;
5475 if (!MovDelay[x][y]) /* start new growing cycle */
5479 if (DelayReached(&sound_delay, sound_delay_value))
5482 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5484 if (Store[x][y] == EL_BD_AMOEBA)
5485 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5487 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5489 sound_delay_value = 30;
5493 if (MovDelay[x][y]) /* wait some time before growing bigger */
5496 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5498 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5499 6 - MovDelay[x][y]);
5501 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5504 if (!MovDelay[x][y])
5506 Feld[x][y] = Store[x][y];
5508 DrawLevelField(x, y);
5513 void AmoebaDisappearing(int x, int y)
5515 static unsigned long sound_delay = 0;
5516 static unsigned long sound_delay_value = 0;
5518 if (!MovDelay[x][y]) /* start new shrinking cycle */
5522 if (DelayReached(&sound_delay, sound_delay_value))
5523 sound_delay_value = 30;
5526 if (MovDelay[x][y]) /* wait some time before shrinking */
5529 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5531 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5532 6 - MovDelay[x][y]);
5534 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5537 if (!MovDelay[x][y])
5539 Feld[x][y] = EL_EMPTY;
5540 DrawLevelField(x, y);
5542 /* don't let mole enter this field in this cycle;
5543 (give priority to objects falling to this field from above) */
5549 void AmoebeAbleger(int ax, int ay)
5552 int element = Feld[ax][ay];
5553 int graphic = el2img(element);
5554 int newax = ax, neway = ay;
5555 static int xy[4][2] =
5563 if (!level.amoeba_speed)
5565 Feld[ax][ay] = EL_AMOEBA_DEAD;
5566 DrawLevelField(ax, ay);
5570 if (IS_ANIMATED(graphic))
5571 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5573 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5574 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5576 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5579 if (MovDelay[ax][ay])
5583 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5586 int x = ax + xy[start][0];
5587 int y = ay + xy[start][1];
5589 if (!IN_LEV_FIELD(x, y))
5592 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5593 if (IS_FREE(x, y) ||
5594 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5600 if (newax == ax && neway == ay)
5603 else /* normal or "filled" (BD style) amoeba */
5606 boolean waiting_for_player = FALSE;
5608 for (i = 0; i < 4; i++)
5610 int j = (start + i) % 4;
5611 int x = ax + xy[j][0];
5612 int y = ay + xy[j][1];
5614 if (!IN_LEV_FIELD(x, y))
5617 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5618 if (IS_FREE(x, y) ||
5619 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5625 else if (IS_PLAYER(x, y))
5626 waiting_for_player = TRUE;
5629 if (newax == ax && neway == ay) /* amoeba cannot grow */
5631 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5633 Feld[ax][ay] = EL_AMOEBA_DEAD;
5634 DrawLevelField(ax, ay);
5635 AmoebaCnt[AmoebaNr[ax][ay]]--;
5637 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5639 if (element == EL_AMOEBA_FULL)
5640 AmoebeUmwandeln(ax, ay);
5641 else if (element == EL_BD_AMOEBA)
5642 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5647 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5649 /* amoeba gets larger by growing in some direction */
5651 int new_group_nr = AmoebaNr[ax][ay];
5654 if (new_group_nr == 0)
5656 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5657 printf("AmoebeAbleger(): This should never happen!\n");
5662 AmoebaNr[newax][neway] = new_group_nr;
5663 AmoebaCnt[new_group_nr]++;
5664 AmoebaCnt2[new_group_nr]++;
5666 /* if amoeba touches other amoeba(s) after growing, unify them */
5667 AmoebenVereinigen(newax, neway);
5669 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5671 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5677 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5678 (neway == lev_fieldy - 1 && newax != ax))
5680 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5681 Store[newax][neway] = element;
5683 else if (neway == ay)
5685 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5687 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5689 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5694 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5695 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5696 Store[ax][ay] = EL_AMOEBA_DROP;
5697 ContinueMoving(ax, ay);
5701 DrawLevelField(newax, neway);
5704 void Life(int ax, int ay)
5707 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5709 int element = Feld[ax][ay];
5710 int graphic = el2img(element);
5711 boolean changed = FALSE;
5713 if (IS_ANIMATED(graphic))
5714 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5719 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5720 MovDelay[ax][ay] = life_time;
5722 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5725 if (MovDelay[ax][ay])
5729 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5731 int xx = ax+x1, yy = ay+y1;
5734 if (!IN_LEV_FIELD(xx, yy))
5737 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5739 int x = xx+x2, y = yy+y2;
5741 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5744 if (((Feld[x][y] == element ||
5745 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5747 (IS_FREE(x, y) && Stop[x][y]))
5751 if (xx == ax && yy == ay) /* field in the middle */
5753 if (nachbarn < life[0] || nachbarn > life[1])
5755 Feld[xx][yy] = EL_EMPTY;
5757 DrawLevelField(xx, yy);
5758 Stop[xx][yy] = TRUE;
5762 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5763 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5764 { /* free border field */
5765 if (nachbarn >= life[2] && nachbarn <= life[3])
5767 Feld[xx][yy] = element;
5768 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5770 DrawLevelField(xx, yy);
5771 Stop[xx][yy] = TRUE;
5778 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5779 SND_GAME_OF_LIFE_GROWING);
5782 static void InitRobotWheel(int x, int y)
5784 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5787 static void RunRobotWheel(int x, int y)
5789 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5792 static void StopRobotWheel(int x, int y)
5794 if (ZX == x && ZY == y)
5798 static void InitTimegateWheel(int x, int y)
5800 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5803 static void RunTimegateWheel(int x, int y)
5805 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5808 void CheckExit(int x, int y)
5810 if (local_player->gems_still_needed > 0 ||
5811 local_player->sokobanfields_still_needed > 0 ||
5812 local_player->lights_still_needed > 0)
5814 int element = Feld[x][y];
5815 int graphic = el2img(element);
5817 if (IS_ANIMATED(graphic))
5818 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5823 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5826 Feld[x][y] = EL_EXIT_OPENING;
5828 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5831 void CheckExitSP(int x, int y)
5833 if (local_player->gems_still_needed > 0)
5835 int element = Feld[x][y];
5836 int graphic = el2img(element);
5838 if (IS_ANIMATED(graphic))
5839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5844 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5847 Feld[x][y] = EL_SP_EXIT_OPENING;
5849 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5852 static void CloseAllOpenTimegates()
5856 for (y = 0; y < lev_fieldy; y++)
5858 for (x = 0; x < lev_fieldx; x++)
5860 int element = Feld[x][y];
5862 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5864 Feld[x][y] = EL_TIMEGATE_CLOSING;
5866 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5868 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5875 void EdelsteinFunkeln(int x, int y)
5877 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5880 if (Feld[x][y] == EL_BD_DIAMOND)
5883 if (MovDelay[x][y] == 0) /* next animation frame */
5884 MovDelay[x][y] = 11 * !SimpleRND(500);
5886 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5890 if (setup.direct_draw && MovDelay[x][y])
5891 SetDrawtoField(DRAW_BUFFERED);
5893 DrawLevelElementAnimation(x, y, Feld[x][y]);
5895 if (MovDelay[x][y] != 0)
5897 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5898 10 - MovDelay[x][y]);
5900 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5902 if (setup.direct_draw)
5906 dest_x = FX + SCREENX(x) * TILEX;
5907 dest_y = FY + SCREENY(y) * TILEY;
5909 BlitBitmap(drawto_field, window,
5910 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5911 SetDrawtoField(DRAW_DIRECT);
5917 void MauerWaechst(int x, int y)
5921 if (!MovDelay[x][y]) /* next animation frame */
5922 MovDelay[x][y] = 3 * delay;
5924 if (MovDelay[x][y]) /* wait some time before next frame */
5928 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5930 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5931 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5933 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5936 if (!MovDelay[x][y])
5938 if (MovDir[x][y] == MV_LEFT)
5940 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5941 DrawLevelField(x - 1, y);
5943 else if (MovDir[x][y] == MV_RIGHT)
5945 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5946 DrawLevelField(x + 1, y);
5948 else if (MovDir[x][y] == MV_UP)
5950 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5951 DrawLevelField(x, y - 1);
5955 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5956 DrawLevelField(x, y + 1);
5959 Feld[x][y] = Store[x][y];
5961 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5962 DrawLevelField(x, y);
5967 void MauerAbleger(int ax, int ay)
5969 int element = Feld[ax][ay];
5970 int graphic = el2img(element);
5971 boolean oben_frei = FALSE, unten_frei = FALSE;
5972 boolean links_frei = FALSE, rechts_frei = FALSE;
5973 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5974 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5975 boolean new_wall = FALSE;
5977 if (IS_ANIMATED(graphic))
5978 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5980 if (!MovDelay[ax][ay]) /* start building new wall */
5981 MovDelay[ax][ay] = 6;
5983 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5986 if (MovDelay[ax][ay])
5990 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5992 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5994 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5996 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5999 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6000 element == EL_EXPANDABLE_WALL_ANY)
6004 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6005 Store[ax][ay-1] = element;
6006 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6007 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6008 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6009 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6014 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6015 Store[ax][ay+1] = element;
6016 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6017 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6018 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6019 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6024 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6025 element == EL_EXPANDABLE_WALL_ANY ||
6026 element == EL_EXPANDABLE_WALL)
6030 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6031 Store[ax-1][ay] = element;
6032 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6033 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6034 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6035 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6041 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6042 Store[ax+1][ay] = element;
6043 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6044 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6045 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6046 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6051 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6052 DrawLevelField(ax, ay);
6054 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6056 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6057 unten_massiv = TRUE;
6058 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6059 links_massiv = TRUE;
6060 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6061 rechts_massiv = TRUE;
6063 if (((oben_massiv && unten_massiv) ||
6064 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6065 element == EL_EXPANDABLE_WALL) &&
6066 ((links_massiv && rechts_massiv) ||
6067 element == EL_EXPANDABLE_WALL_VERTICAL))
6068 Feld[ax][ay] = EL_WALL;
6072 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6074 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6078 void CheckForDragon(int x, int y)
6081 boolean dragon_found = FALSE;
6082 static int xy[4][2] =
6090 for (i = 0; i < 4; i++)
6092 for (j = 0; j < 4; j++)
6094 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6096 if (IN_LEV_FIELD(xx, yy) &&
6097 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6099 if (Feld[xx][yy] == EL_DRAGON)
6100 dragon_found = TRUE;
6109 for (i = 0; i < 4; i++)
6111 for (j = 0; j < 3; j++)
6113 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6115 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6117 Feld[xx][yy] = EL_EMPTY;
6118 DrawLevelField(xx, yy);
6127 static void InitBuggyBase(int x, int y)
6129 int element = Feld[x][y];
6130 int activating_delay = FRAMES_PER_SECOND / 4;
6133 (element == EL_SP_BUGGY_BASE ?
6134 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6135 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6137 element == EL_SP_BUGGY_BASE_ACTIVE ?
6138 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6141 static void WarnBuggyBase(int x, int y)
6144 static int xy[4][2] =
6152 for (i = 0; i < 4; i++)
6154 int xx = x + xy[i][0], yy = y + xy[i][1];
6156 if (IS_PLAYER(xx, yy))
6158 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6165 static void InitTrap(int x, int y)
6167 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6170 static void ActivateTrap(int x, int y)
6172 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6175 static void ChangeActiveTrap(int x, int y)
6177 int graphic = IMG_TRAP_ACTIVE;
6179 /* if new animation frame was drawn, correct crumbled sand border */
6180 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6181 DrawLevelFieldCrumbledSand(x, y);
6184 static void ChangeElementNowExt(int x, int y, int target_element)
6186 int previous_move_direction = MovDir[x][y];
6188 /* check if element under player changes from accessible to unaccessible
6189 (needed for special case of dropping element which then changes) */
6190 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6191 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6198 Feld[x][y] = target_element;
6200 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6202 ResetGfxAnimation(x, y);
6203 ResetRandomAnimationValue(x, y);
6205 if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
6206 MovDir[x][y] = previous_move_direction;
6208 InitField(x, y, FALSE);
6209 if (CAN_MOVE(Feld[x][y]))
6212 DrawLevelField(x, y);
6214 if (GFX_CRUMBLED(Feld[x][y]))
6215 DrawLevelFieldCrumbledSandNeighbours(x, y);
6217 TestIfBadThingTouchesHero(x, y);
6218 TestIfPlayerTouchesCustomElement(x, y);
6219 TestIfElementTouchesCustomElement(x, y);
6221 if (ELEM_IS_PLAYER(target_element))
6222 RelocatePlayer(x, y, target_element);
6225 static boolean ChangeElementNow(int x, int y, int element, int page)
6227 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6229 /* always use default change event to prevent running into a loop */
6230 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6231 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6233 /* do not change already changed elements with same change event */
6235 if (Changed[x][y] & ChangeEvent[x][y])
6242 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6244 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6246 if (change->explode)
6253 if (change->use_content)
6255 boolean complete_change = TRUE;
6256 boolean can_change[3][3];
6259 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6261 boolean half_destructible;
6262 int ex = x + xx - 1;
6263 int ey = y + yy - 1;
6266 can_change[xx][yy] = TRUE;
6268 if (ex == x && ey == y) /* do not check changing element itself */
6271 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6273 can_change[xx][yy] = FALSE; /* do not change empty borders */
6278 if (!IN_LEV_FIELD(ex, ey))
6280 can_change[xx][yy] = FALSE;
6281 complete_change = FALSE;
6288 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6289 e = MovingOrBlocked2Element(ex, ey);
6291 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6293 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6294 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6295 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6297 can_change[xx][yy] = FALSE;
6298 complete_change = FALSE;
6302 if (!change->only_complete || complete_change)
6304 boolean something_has_changed = FALSE;
6306 if (change->only_complete && change->use_random_change &&
6307 RND(100) < change->random)
6310 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6312 int ex = x + xx - 1;
6313 int ey = y + yy - 1;
6315 if (can_change[xx][yy] && (!change->use_random_change ||
6316 RND(100) < change->random))
6318 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6319 RemoveMovingField(ex, ey);
6321 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6323 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6325 something_has_changed = TRUE;
6327 /* for symmetry reasons, freeze newly created border elements */
6328 if (ex != x || ey != y)
6329 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6333 if (something_has_changed)
6334 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6339 ChangeElementNowExt(x, y, change->target_element);
6341 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6347 static void ChangeElement(int x, int y, int page)
6349 int element = MovingOrBlocked2Element(x, y);
6350 struct ElementInfo *ei = &element_info[element];
6351 struct ElementChangeInfo *change = &ei->change_page[page];
6355 if (!CAN_CHANGE(element))
6358 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6359 x, y, element, element_info[element].token_name);
6360 printf("ChangeElement(): This should never happen!\n");
6366 if (ChangeDelay[x][y] == 0) /* initialize element change */
6368 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6369 RND(change->delay_random * change->delay_frames)) + 1;
6371 ResetGfxAnimation(x, y);
6372 ResetRandomAnimationValue(x, y);
6374 if (change->pre_change_function)
6375 change->pre_change_function(x, y);
6378 ChangeDelay[x][y]--;
6380 if (ChangeDelay[x][y] != 0) /* continue element change */
6382 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6384 if (IS_ANIMATED(graphic))
6385 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6387 if (change->change_function)
6388 change->change_function(x, y);
6390 else /* finish element change */
6392 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6394 page = ChangePage[x][y];
6395 ChangePage[x][y] = -1;
6399 if (IS_MOVING(x, y) && !change->explode)
6401 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6404 ChangeDelay[x][y] = 1; /* try change after next move step */
6405 ChangePage[x][y] = page; /* remember page to use for change */
6410 if (ChangeElementNow(x, y, element, page))
6412 if (change->post_change_function)
6413 change->post_change_function(x, y);
6418 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6419 int trigger_element,
6425 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6428 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6430 int element = EL_CUSTOM_START + i;
6432 boolean change_element = FALSE;
6435 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6438 for (j = 0; j < element_info[element].num_change_pages; j++)
6440 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6442 if (change->can_change &&
6444 change->events & CH_EVENT_BIT(trigger_event) &&
6446 change->sides & trigger_side &&
6448 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6450 change->trigger_element == trigger_element
6455 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6456 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6457 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6460 change_element = TRUE;
6467 if (!change_element)
6470 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6473 if (x == lx && y == ly) /* do not change trigger element itself */
6477 if (Feld[x][y] == element)
6479 ChangeDelay[x][y] = 1;
6480 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6481 ChangeElement(x, y, page);
6489 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6492 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6496 static boolean CheckElementSideChange(int x, int y, int element, int side,
6497 int trigger_event, int page)
6499 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6502 if (Feld[x][y] == EL_BLOCKED)
6504 Blocked2Moving(x, y, &x, &y);
6505 element = Feld[x][y];
6509 page = element_info[element].event_page_nr[trigger_event];
6511 if (!(element_info[element].change_page[page].sides & side))
6514 ChangeDelay[x][y] = 1;
6515 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6516 ChangeElement(x, y, page);
6521 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6523 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6526 static void PlayPlayerSound(struct PlayerInfo *player)
6528 int jx = player->jx, jy = player->jy;
6529 int element = player->element_nr;
6530 int last_action = player->last_action_waiting;
6531 int action = player->action_waiting;
6533 if (player->is_waiting)
6535 if (action != last_action)
6536 PlayLevelSoundElementAction(jx, jy, element, action);
6538 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6542 if (action != last_action)
6543 StopSound(element_info[element].sound[last_action]);
6545 if (last_action == ACTION_SLEEPING)
6546 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6550 static void PlayAllPlayersSound()
6554 for (i = 0; i < MAX_PLAYERS; i++)
6555 if (stored_player[i].active)
6556 PlayPlayerSound(&stored_player[i]);
6559 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6561 boolean last_waiting = player->is_waiting;
6562 int move_dir = player->MovDir;
6564 player->last_action_waiting = player->action_waiting;
6568 if (!last_waiting) /* not waiting -> waiting */
6570 player->is_waiting = TRUE;
6572 player->frame_counter_bored =
6574 game.player_boring_delay_fixed +
6575 SimpleRND(game.player_boring_delay_random);
6576 player->frame_counter_sleeping =
6578 game.player_sleeping_delay_fixed +
6579 SimpleRND(game.player_sleeping_delay_random);
6581 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6584 if (game.player_sleeping_delay_fixed +
6585 game.player_sleeping_delay_random > 0 &&
6586 player->anim_delay_counter == 0 &&
6587 player->post_delay_counter == 0 &&
6588 FrameCounter >= player->frame_counter_sleeping)
6589 player->is_sleeping = TRUE;
6590 else if (game.player_boring_delay_fixed +
6591 game.player_boring_delay_random > 0 &&
6592 FrameCounter >= player->frame_counter_bored)
6593 player->is_bored = TRUE;
6595 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6596 player->is_bored ? ACTION_BORING :
6599 if (player->is_sleeping)
6601 if (player->num_special_action_sleeping > 0)
6603 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6605 int last_special_action = player->special_action_sleeping;
6606 int num_special_action = player->num_special_action_sleeping;
6607 int special_action =
6608 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6609 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6610 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6611 last_special_action + 1 : ACTION_SLEEPING);
6612 int special_graphic =
6613 el_act_dir2img(player->element_nr, special_action, move_dir);
6615 player->anim_delay_counter =
6616 graphic_info[special_graphic].anim_delay_fixed +
6617 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6618 player->post_delay_counter =
6619 graphic_info[special_graphic].post_delay_fixed +
6620 SimpleRND(graphic_info[special_graphic].post_delay_random);
6622 player->special_action_sleeping = special_action;
6625 if (player->anim_delay_counter > 0)
6627 player->action_waiting = player->special_action_sleeping;
6628 player->anim_delay_counter--;
6630 else if (player->post_delay_counter > 0)
6632 player->post_delay_counter--;
6636 else if (player->is_bored)
6638 if (player->num_special_action_bored > 0)
6640 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6642 int special_action =
6643 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6644 int special_graphic =
6645 el_act_dir2img(player->element_nr, special_action, move_dir);
6647 player->anim_delay_counter =
6648 graphic_info[special_graphic].anim_delay_fixed +
6649 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6650 player->post_delay_counter =
6651 graphic_info[special_graphic].post_delay_fixed +
6652 SimpleRND(graphic_info[special_graphic].post_delay_random);
6654 player->special_action_bored = special_action;
6657 if (player->anim_delay_counter > 0)
6659 player->action_waiting = player->special_action_bored;
6660 player->anim_delay_counter--;
6662 else if (player->post_delay_counter > 0)
6664 player->post_delay_counter--;
6669 else if (last_waiting) /* waiting -> not waiting */
6671 player->is_waiting = FALSE;
6672 player->is_bored = FALSE;
6673 player->is_sleeping = FALSE;
6675 player->frame_counter_bored = -1;
6676 player->frame_counter_sleeping = -1;
6678 player->anim_delay_counter = 0;
6679 player->post_delay_counter = 0;
6681 player->action_waiting = ACTION_DEFAULT;
6683 player->special_action_bored = ACTION_DEFAULT;
6684 player->special_action_sleeping = ACTION_DEFAULT;
6689 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6692 static byte stored_player_action[MAX_PLAYERS];
6693 static int num_stored_actions = 0;
6695 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6696 int left = player_action & JOY_LEFT;
6697 int right = player_action & JOY_RIGHT;
6698 int up = player_action & JOY_UP;
6699 int down = player_action & JOY_DOWN;
6700 int button1 = player_action & JOY_BUTTON_1;
6701 int button2 = player_action & JOY_BUTTON_2;
6702 int dx = (left ? -1 : right ? 1 : 0);
6703 int dy = (up ? -1 : down ? 1 : 0);
6706 stored_player_action[player->index_nr] = 0;
6707 num_stored_actions++;
6711 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6714 if (!player->active || tape.pausing)
6718 printf("::: [%d %d %d %d] [%d %d]\n",
6719 left, right, up, down, button1, button2);
6725 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6729 snapped = SnapField(player, dx, dy);
6733 dropped = DropElement(player);
6735 moved = MovePlayer(player, dx, dy);
6738 if (tape.single_step && tape.recording && !tape.pausing)
6740 if (button1 || (dropped && !moved))
6742 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6743 SnapField(player, 0, 0); /* stop snapping */
6747 SetPlayerWaiting(player, FALSE);
6750 return player_action;
6752 stored_player_action[player->index_nr] = player_action;
6758 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6761 /* no actions for this player (no input at player's configured device) */
6763 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6764 SnapField(player, 0, 0);
6765 CheckGravityMovement(player);
6767 if (player->MovPos == 0)
6768 SetPlayerWaiting(player, TRUE);
6770 if (player->MovPos == 0) /* needed for tape.playing */
6771 player->is_moving = FALSE;
6773 player->is_dropping = FALSE;
6779 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6781 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6783 TapeRecordAction(stored_player_action);
6784 num_stored_actions = 0;
6791 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6793 static byte stored_player_action[MAX_PLAYERS];
6794 static int num_stored_actions = 0;
6795 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6796 int left = player_action & JOY_LEFT;
6797 int right = player_action & JOY_RIGHT;
6798 int up = player_action & JOY_UP;
6799 int down = player_action & JOY_DOWN;
6800 int button1 = player_action & JOY_BUTTON_1;
6801 int button2 = player_action & JOY_BUTTON_2;
6802 int dx = (left ? -1 : right ? 1 : 0);
6803 int dy = (up ? -1 : down ? 1 : 0);
6805 stored_player_action[player->index_nr] = 0;
6806 num_stored_actions++;
6808 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6810 if (!player->active || tape.pausing)
6815 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6818 snapped = SnapField(player, dx, dy);
6822 dropped = DropElement(player);
6824 moved = MovePlayer(player, dx, dy);
6827 if (tape.single_step && tape.recording && !tape.pausing)
6829 if (button1 || (dropped && !moved))
6831 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6832 SnapField(player, 0, 0); /* stop snapping */
6836 stored_player_action[player->index_nr] = player_action;
6840 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6842 /* no actions for this player (no input at player's configured device) */
6844 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6845 SnapField(player, 0, 0);
6846 CheckGravityMovement(player);
6848 if (player->MovPos == 0)
6849 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6851 if (player->MovPos == 0) /* needed for tape.playing */
6852 player->is_moving = FALSE;
6855 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6857 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6859 TapeRecordAction(stored_player_action);
6860 num_stored_actions = 0;
6867 static unsigned long action_delay = 0;
6868 unsigned long action_delay_value;
6869 int magic_wall_x = 0, magic_wall_y = 0;
6870 int i, x, y, element, graphic;
6871 byte *recorded_player_action;
6872 byte summarized_player_action = 0;
6874 byte tape_action[MAX_PLAYERS];
6877 if (game_status != GAME_MODE_PLAYING)
6880 action_delay_value =
6881 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6883 if (tape.playing && tape.index_search && !tape.pausing)
6884 action_delay_value = 0;
6886 /* ---------- main game synchronization point ---------- */
6888 WaitUntilDelayReached(&action_delay, action_delay_value);
6890 if (network_playing && !network_player_action_received)
6894 printf("DEBUG: try to get network player actions in time\n");
6898 #if defined(PLATFORM_UNIX)
6899 /* last chance to get network player actions without main loop delay */
6903 if (game_status != GAME_MODE_PLAYING)
6906 if (!network_player_action_received)
6910 printf("DEBUG: failed to get network player actions in time\n");
6921 printf("::: getting new tape action [%d]\n", FrameCounter);
6924 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6926 for (i = 0; i < MAX_PLAYERS; i++)
6928 summarized_player_action |= stored_player[i].action;
6930 if (!network_playing)
6931 stored_player[i].effective_action = stored_player[i].action;
6934 #if defined(PLATFORM_UNIX)
6935 if (network_playing)
6936 SendToServer_MovePlayer(summarized_player_action);
6939 if (!options.network && !setup.team_mode)
6940 local_player->effective_action = summarized_player_action;
6942 for (i = 0; i < MAX_PLAYERS; i++)
6944 int actual_player_action = stored_player[i].effective_action;
6946 if (stored_player[i].programmed_action)
6947 actual_player_action = stored_player[i].programmed_action;
6949 if (recorded_player_action)
6950 actual_player_action = recorded_player_action[i];
6952 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6954 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6955 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6957 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6962 TapeRecordAction(tape_action);
6965 network_player_action_received = FALSE;
6967 ScrollScreen(NULL, SCROLL_GO_ON);
6973 for (i = 0; i < MAX_PLAYERS; i++)
6974 stored_player[i].Frame++;
6978 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6980 for (i = 0; i < MAX_PLAYERS; i++)
6982 struct PlayerInfo *player = &stored_player[i];
6986 if (player->active && player->is_pushing && player->is_moving &&
6989 ContinueMoving(x, y);
6991 /* continue moving after pushing (this is actually a bug) */
6992 if (!IS_MOVING(x, y))
7001 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7003 Changed[x][y] = CE_BITMASK_DEFAULT;
7004 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7007 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7009 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7010 printf("GameActions(): This should never happen!\n");
7012 ChangePage[x][y] = -1;
7017 if (WasJustMoving[x][y] > 0)
7018 WasJustMoving[x][y]--;
7019 if (WasJustFalling[x][y] > 0)
7020 WasJustFalling[x][y]--;
7025 /* reset finished pushing action (not done in ContinueMoving() to allow
7026 continous pushing animation for elements with zero push delay) */
7027 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7029 ResetGfxAnimation(x, y);
7030 DrawLevelField(x, y);
7035 if (IS_BLOCKED(x, y))
7039 Blocked2Moving(x, y, &oldx, &oldy);
7040 if (!IS_MOVING(oldx, oldy))
7042 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7043 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7044 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7045 printf("GameActions(): This should never happen!\n");
7051 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7053 element = Feld[x][y];
7055 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7057 graphic = el2img(element);
7063 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7065 element = graphic = 0;
7069 if (graphic_info[graphic].anim_global_sync)
7070 GfxFrame[x][y] = FrameCounter;
7072 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7073 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7074 ResetRandomAnimationValue(x, y);
7076 SetRandomAnimationValue(x, y);
7079 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7082 if (IS_INACTIVE(element))
7084 if (IS_ANIMATED(graphic))
7085 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7091 /* this may take place after moving, so 'element' may have changed */
7093 if (IS_CHANGING(x, y))
7095 if (IS_CHANGING(x, y) &&
7096 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7100 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7101 element_info[element].event_page_nr[CE_DELAY]);
7103 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7106 element = Feld[x][y];
7107 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7111 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7116 element = Feld[x][y];
7117 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7119 if (element == EL_MOLE)
7120 printf("::: %d, %d, %d [%d]\n",
7121 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7125 if (element == EL_YAMYAM)
7126 printf("::: %d, %d, %d\n",
7127 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7131 if (IS_ANIMATED(graphic) &&
7135 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7138 if (element == EL_BUG)
7139 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7143 if (element == EL_MOLE)
7144 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7148 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7149 EdelsteinFunkeln(x, y);
7151 else if ((element == EL_ACID ||
7152 element == EL_EXIT_OPEN ||
7153 element == EL_SP_EXIT_OPEN ||
7154 element == EL_SP_TERMINAL ||
7155 element == EL_SP_TERMINAL_ACTIVE ||
7156 element == EL_EXTRA_TIME ||
7157 element == EL_SHIELD_NORMAL ||
7158 element == EL_SHIELD_DEADLY) &&
7159 IS_ANIMATED(graphic))
7160 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7161 else if (IS_MOVING(x, y))
7162 ContinueMoving(x, y);
7163 else if (IS_ACTIVE_BOMB(element))
7164 CheckDynamite(x, y);
7166 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7167 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7169 else if (element == EL_AMOEBA_GROWING)
7170 AmoebeWaechst(x, y);
7171 else if (element == EL_AMOEBA_SHRINKING)
7172 AmoebaDisappearing(x, y);
7174 #if !USE_NEW_AMOEBA_CODE
7175 else if (IS_AMOEBALIVE(element))
7176 AmoebeAbleger(x, y);
7179 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7181 else if (element == EL_EXIT_CLOSED)
7183 else if (element == EL_SP_EXIT_CLOSED)
7185 else if (element == EL_EXPANDABLE_WALL_GROWING)
7187 else if (element == EL_EXPANDABLE_WALL ||
7188 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7189 element == EL_EXPANDABLE_WALL_VERTICAL ||
7190 element == EL_EXPANDABLE_WALL_ANY)
7192 else if (element == EL_FLAMES)
7193 CheckForDragon(x, y);
7195 else if (IS_AUTO_CHANGING(element))
7196 ChangeElement(x, y);
7198 else if (element == EL_EXPLOSION)
7199 ; /* drawing of correct explosion animation is handled separately */
7200 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7201 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7204 /* this may take place after moving, so 'element' may have changed */
7205 if (IS_AUTO_CHANGING(Feld[x][y]))
7206 ChangeElement(x, y);
7209 if (IS_BELT_ACTIVE(element))
7210 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7212 if (game.magic_wall_active)
7214 int jx = local_player->jx, jy = local_player->jy;
7216 /* play the element sound at the position nearest to the player */
7217 if ((element == EL_MAGIC_WALL_FULL ||
7218 element == EL_MAGIC_WALL_ACTIVE ||
7219 element == EL_MAGIC_WALL_EMPTYING ||
7220 element == EL_BD_MAGIC_WALL_FULL ||
7221 element == EL_BD_MAGIC_WALL_ACTIVE ||
7222 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7223 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7231 #if USE_NEW_AMOEBA_CODE
7232 /* new experimental amoeba growth stuff */
7234 if (!(FrameCounter % 8))
7237 static unsigned long random = 1684108901;
7239 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7242 x = (random >> 10) % lev_fieldx;
7243 y = (random >> 20) % lev_fieldy;
7245 x = RND(lev_fieldx);
7246 y = RND(lev_fieldy);
7248 element = Feld[x][y];
7250 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7251 if (!IS_PLAYER(x,y) &&
7252 (element == EL_EMPTY ||
7253 element == EL_SAND ||
7254 element == EL_QUICKSAND_EMPTY ||
7255 element == EL_ACID_SPLASH_LEFT ||
7256 element == EL_ACID_SPLASH_RIGHT))
7258 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7259 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7260 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7261 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7262 Feld[x][y] = EL_AMOEBA_DROP;
7265 random = random * 129 + 1;
7271 if (game.explosions_delayed)
7274 game.explosions_delayed = FALSE;
7276 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7278 element = Feld[x][y];
7280 if (ExplodeField[x][y])
7281 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7282 else if (element == EL_EXPLOSION)
7283 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7285 ExplodeField[x][y] = EX_NO_EXPLOSION;
7288 game.explosions_delayed = TRUE;
7291 if (game.magic_wall_active)
7293 if (!(game.magic_wall_time_left % 4))
7295 int element = Feld[magic_wall_x][magic_wall_y];
7297 if (element == EL_BD_MAGIC_WALL_FULL ||
7298 element == EL_BD_MAGIC_WALL_ACTIVE ||
7299 element == EL_BD_MAGIC_WALL_EMPTYING)
7300 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7302 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7305 if (game.magic_wall_time_left > 0)
7307 game.magic_wall_time_left--;
7308 if (!game.magic_wall_time_left)
7310 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7312 element = Feld[x][y];
7314 if (element == EL_MAGIC_WALL_ACTIVE ||
7315 element == EL_MAGIC_WALL_FULL)
7317 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7318 DrawLevelField(x, y);
7320 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7321 element == EL_BD_MAGIC_WALL_FULL)
7323 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7324 DrawLevelField(x, y);
7328 game.magic_wall_active = FALSE;
7333 if (game.light_time_left > 0)
7335 game.light_time_left--;
7337 if (game.light_time_left == 0)
7338 RedrawAllLightSwitchesAndInvisibleElements();
7341 if (game.timegate_time_left > 0)
7343 game.timegate_time_left--;
7345 if (game.timegate_time_left == 0)
7346 CloseAllOpenTimegates();
7349 for (i = 0; i < MAX_PLAYERS; i++)
7351 struct PlayerInfo *player = &stored_player[i];
7353 if (SHIELD_ON(player))
7355 if (player->shield_deadly_time_left)
7356 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7357 else if (player->shield_normal_time_left)
7358 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7362 if (TimeFrames >= FRAMES_PER_SECOND)
7367 for (i = 0; i < MAX_PLAYERS; i++)
7369 struct PlayerInfo *player = &stored_player[i];
7371 if (SHIELD_ON(player))
7373 player->shield_normal_time_left--;
7375 if (player->shield_deadly_time_left > 0)
7376 player->shield_deadly_time_left--;
7380 if (tape.recording || tape.playing)
7381 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7387 if (TimeLeft <= 10 && setup.time_limit)
7388 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7390 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7392 if (!TimeLeft && setup.time_limit)
7393 for (i = 0; i < MAX_PLAYERS; i++)
7394 KillHero(&stored_player[i]);
7396 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7397 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7401 PlayAllPlayersSound();
7403 if (options.debug) /* calculate frames per second */
7405 static unsigned long fps_counter = 0;
7406 static int fps_frames = 0;
7407 unsigned long fps_delay_ms = Counter() - fps_counter;
7411 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7413 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7416 fps_counter = Counter();
7419 redraw_mask |= REDRAW_FPS;
7423 if (stored_player[0].jx != stored_player[0].last_jx ||
7424 stored_player[0].jy != stored_player[0].last_jy)
7425 printf("::: %d, %d, %d, %d, %d\n",
7426 stored_player[0].MovDir,
7427 stored_player[0].MovPos,
7428 stored_player[0].GfxPos,
7429 stored_player[0].Frame,
7430 stored_player[0].StepFrame);
7437 for (i = 0; i < MAX_PLAYERS; i++)
7440 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7442 stored_player[i].Frame += move_frames;
7444 if (stored_player[i].MovPos != 0)
7445 stored_player[i].StepFrame += move_frames;
7447 if (stored_player[i].drop_delay > 0)
7448 stored_player[i].drop_delay--;
7453 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7455 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7457 local_player->show_envelope = 0;
7462 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7464 int min_x = x, min_y = y, max_x = x, max_y = y;
7467 for (i = 0; i < MAX_PLAYERS; i++)
7469 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7471 if (!stored_player[i].active || &stored_player[i] == player)
7474 min_x = MIN(min_x, jx);
7475 min_y = MIN(min_y, jy);
7476 max_x = MAX(max_x, jx);
7477 max_y = MAX(max_y, jy);
7480 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7483 static boolean AllPlayersInVisibleScreen()
7487 for (i = 0; i < MAX_PLAYERS; i++)
7489 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7491 if (!stored_player[i].active)
7494 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7501 void ScrollLevel(int dx, int dy)
7503 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7506 BlitBitmap(drawto_field, drawto_field,
7507 FX + TILEX * (dx == -1) - softscroll_offset,
7508 FY + TILEY * (dy == -1) - softscroll_offset,
7509 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7510 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7511 FX + TILEX * (dx == 1) - softscroll_offset,
7512 FY + TILEY * (dy == 1) - softscroll_offset);
7516 x = (dx == 1 ? BX1 : BX2);
7517 for (y = BY1; y <= BY2; y++)
7518 DrawScreenField(x, y);
7523 y = (dy == 1 ? BY1 : BY2);
7524 for (x = BX1; x <= BX2; x++)
7525 DrawScreenField(x, y);
7528 redraw_mask |= REDRAW_FIELD;
7531 static void CheckGravityMovement(struct PlayerInfo *player)
7533 if (game.gravity && !player->programmed_action)
7535 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7536 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7538 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7539 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7540 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7541 int jx = player->jx, jy = player->jy;
7542 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7543 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7544 int new_jx = jx + dx, new_jy = jy + dy;
7545 boolean field_under_player_is_free =
7546 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7547 boolean player_is_moving_to_valid_field =
7548 (IN_LEV_FIELD(new_jx, new_jy) &&
7549 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7550 Feld[new_jx][new_jy] == EL_SAND));
7551 /* !!! extend EL_SAND to anything diggable !!! */
7553 if (field_under_player_is_free &&
7554 !player_is_moving_to_valid_field &&
7555 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7556 player->programmed_action = MV_DOWN;
7562 -----------------------------------------------------------------------------
7563 dx, dy: direction (non-diagonal) to try to move the player to
7564 real_dx, real_dy: direction as read from input device (can be diagonal)
7567 boolean MovePlayerOneStep(struct PlayerInfo *player,
7568 int dx, int dy, int real_dx, int real_dy)
7571 static int change_sides[4][2] =
7573 /* enter side leave side */
7574 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7575 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7576 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7577 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7579 int move_direction = (dx == -1 ? MV_LEFT :
7580 dx == +1 ? MV_RIGHT :
7582 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7583 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7584 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7586 int jx = player->jx, jy = player->jy;
7587 int new_jx = jx + dx, new_jy = jy + dy;
7591 if (!player->active || (!dx && !dy))
7592 return MF_NO_ACTION;
7594 player->MovDir = (dx < 0 ? MV_LEFT :
7597 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7599 if (!IN_LEV_FIELD(new_jx, new_jy))
7600 return MF_NO_ACTION;
7602 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7603 return MF_NO_ACTION;
7606 element = MovingOrBlocked2Element(new_jx, new_jy);
7608 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7611 if (DONT_RUN_INTO(element))
7613 if (element == EL_ACID && dx == 0 && dy == 1)
7616 Feld[jx][jy] = EL_PLAYER_1;
7617 InitMovingField(jx, jy, MV_DOWN);
7618 Store[jx][jy] = EL_ACID;
7619 ContinueMoving(jx, jy);
7623 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7628 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7629 if (can_move != MF_MOVING)
7632 /* check if DigField() has caused relocation of the player */
7633 if (player->jx != jx || player->jy != jy)
7634 return MF_NO_ACTION;
7636 StorePlayer[jx][jy] = 0;
7637 player->last_jx = jx;
7638 player->last_jy = jy;
7639 player->jx = new_jx;
7640 player->jy = new_jy;
7641 StorePlayer[new_jx][new_jy] = player->element_nr;
7644 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7646 player->step_counter++;
7648 player->drop_delay = 0;
7650 PlayerVisit[jx][jy] = FrameCounter;
7652 ScrollPlayer(player, SCROLL_INIT);
7655 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7657 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7658 CE_OTHER_GETS_LEFT);
7659 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7660 CE_LEFT_BY_PLAYER, -1);
7663 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7665 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7666 enter_side, CE_OTHER_GETS_ENTERED);
7667 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7668 CE_ENTERED_BY_PLAYER, -1);
7675 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7677 int jx = player->jx, jy = player->jy;
7678 int old_jx = jx, old_jy = jy;
7679 int moved = MF_NO_ACTION;
7682 if (!player->active)
7687 if (player->MovPos == 0)
7689 player->is_moving = FALSE;
7690 player->is_digging = FALSE;
7691 player->is_collecting = FALSE;
7692 player->is_snapping = FALSE;
7693 player->is_pushing = FALSE;
7699 if (!player->active || (!dx && !dy))
7704 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7708 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7709 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7713 /* remove the last programmed player action */
7714 player->programmed_action = 0;
7718 /* should only happen if pre-1.2 tape recordings are played */
7719 /* this is only for backward compatibility */
7721 int original_move_delay_value = player->move_delay_value;
7724 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7728 /* scroll remaining steps with finest movement resolution */
7729 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7731 while (player->MovPos)
7733 ScrollPlayer(player, SCROLL_GO_ON);
7734 ScrollScreen(NULL, SCROLL_GO_ON);
7740 player->move_delay_value = original_move_delay_value;
7743 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7745 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7746 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7750 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7751 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7757 if (moved & MF_MOVING && !ScreenMovPos &&
7758 (player == local_player || !options.network))
7760 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7761 int offset = (setup.scroll_delay ? 3 : 0);
7763 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7765 /* actual player has left the screen -- scroll in that direction */
7766 if (jx != old_jx) /* player has moved horizontally */
7767 scroll_x += (jx - old_jx);
7768 else /* player has moved vertically */
7769 scroll_y += (jy - old_jy);
7773 if (jx != old_jx) /* player has moved horizontally */
7775 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7776 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7777 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7779 /* don't scroll over playfield boundaries */
7780 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7781 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7783 /* don't scroll more than one field at a time */
7784 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7786 /* don't scroll against the player's moving direction */
7787 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7788 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7789 scroll_x = old_scroll_x;
7791 else /* player has moved vertically */
7793 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7794 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7795 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7797 /* don't scroll over playfield boundaries */
7798 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7799 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7801 /* don't scroll more than one field at a time */
7802 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7804 /* don't scroll against the player's moving direction */
7805 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7806 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7807 scroll_y = old_scroll_y;
7811 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7813 if (!options.network && !AllPlayersInVisibleScreen())
7815 scroll_x = old_scroll_x;
7816 scroll_y = old_scroll_y;
7820 ScrollScreen(player, SCROLL_INIT);
7821 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7828 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7830 if (!(moved & MF_MOVING) && !player->is_pushing)
7835 player->StepFrame = 0;
7837 if (moved & MF_MOVING)
7839 if (old_jx != jx && old_jy == jy)
7840 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7841 else if (old_jx == jx && old_jy != jy)
7842 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7844 DrawLevelField(jx, jy); /* for "crumbled sand" */
7846 player->last_move_dir = player->MovDir;
7847 player->is_moving = TRUE;
7849 player->is_snapping = FALSE;
7853 player->is_switching = FALSE;
7856 player->is_dropping = FALSE;
7861 static int change_sides[4][2] =
7863 /* enter side leave side */
7864 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7865 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7866 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7867 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7869 int move_direction = player->MovDir;
7870 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7871 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7874 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7876 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7877 leave_side, CE_OTHER_GETS_LEFT);
7878 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7879 leave_side, CE_LEFT_BY_PLAYER, -1);
7882 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7884 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7885 enter_side, CE_OTHER_GETS_ENTERED);
7886 CheckElementSideChange(jx, jy, Feld[jx][jy],
7887 enter_side, CE_ENTERED_BY_PLAYER, -1);
7898 CheckGravityMovement(player);
7901 player->last_move_dir = MV_NO_MOVING;
7903 player->is_moving = FALSE;
7906 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7908 TestIfHeroTouchesBadThing(jx, jy);
7909 TestIfPlayerTouchesCustomElement(jx, jy);
7912 if (!player->active)
7918 void ScrollPlayer(struct PlayerInfo *player, int mode)
7920 int jx = player->jx, jy = player->jy;
7921 int last_jx = player->last_jx, last_jy = player->last_jy;
7922 int move_stepsize = TILEX / player->move_delay_value;
7924 if (!player->active || !player->MovPos)
7927 if (mode == SCROLL_INIT)
7929 player->actual_frame_counter = FrameCounter;
7930 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7932 if (Feld[last_jx][last_jy] == EL_EMPTY)
7933 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7940 else if (!FrameReached(&player->actual_frame_counter, 1))
7943 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7944 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7946 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7947 Feld[last_jx][last_jy] = EL_EMPTY;
7949 /* before DrawPlayer() to draw correct player graphic for this case */
7950 if (player->MovPos == 0)
7951 CheckGravityMovement(player);
7954 DrawPlayer(player); /* needed here only to cleanup last field */
7957 if (player->MovPos == 0) /* player reached destination field */
7960 if (player->move_delay_reset_counter > 0)
7962 player->move_delay_reset_counter--;
7964 if (player->move_delay_reset_counter == 0)
7966 /* continue with normal speed after quickly moving through gate */
7967 HALVE_PLAYER_SPEED(player);
7969 /* be able to make the next move without delay */
7970 player->move_delay = 0;
7974 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7976 /* continue with normal speed after quickly moving through gate */
7977 HALVE_PLAYER_SPEED(player);
7979 /* be able to make the next move without delay */
7980 player->move_delay = 0;
7984 player->last_jx = jx;
7985 player->last_jy = jy;
7987 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7988 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7989 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7991 DrawPlayer(player); /* needed here only to cleanup last field */
7994 if (local_player->friends_still_needed == 0 ||
7995 IS_SP_ELEMENT(Feld[jx][jy]))
7996 player->LevelSolved = player->GameOver = TRUE;
7999 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8001 TestIfHeroTouchesBadThing(jx, jy);
8002 TestIfPlayerTouchesCustomElement(jx, jy);
8004 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8007 if (!player->active)
8011 if (tape.single_step && tape.recording && !tape.pausing &&
8012 !player->programmed_action)
8013 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8017 void ScrollScreen(struct PlayerInfo *player, int mode)
8019 static unsigned long screen_frame_counter = 0;
8021 if (mode == SCROLL_INIT)
8023 /* set scrolling step size according to actual player's moving speed */
8024 ScrollStepSize = TILEX / player->move_delay_value;
8026 screen_frame_counter = FrameCounter;
8027 ScreenMovDir = player->MovDir;
8028 ScreenMovPos = player->MovPos;
8029 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8032 else if (!FrameReached(&screen_frame_counter, 1))
8037 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8038 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8039 redraw_mask |= REDRAW_FIELD;
8042 ScreenMovDir = MV_NO_MOVING;
8045 void TestIfPlayerTouchesCustomElement(int x, int y)
8047 static int xy[4][2] =
8054 static int change_sides[4][2] =
8056 /* center side border side */
8057 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8058 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8059 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8060 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8062 static int touch_dir[4] =
8069 int center_element = Feld[x][y]; /* should always be non-moving! */
8072 for (i = 0; i < 4; i++)
8074 int xx = x + xy[i][0];
8075 int yy = y + xy[i][1];
8076 int center_side = change_sides[i][0];
8077 int border_side = change_sides[i][1];
8080 if (!IN_LEV_FIELD(xx, yy))
8083 if (IS_PLAYER(x, y))
8085 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8086 border_element = Feld[xx][yy]; /* may be moving! */
8087 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8088 border_element = Feld[xx][yy];
8089 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8090 border_element = MovingOrBlocked2Element(xx, yy);
8092 continue; /* center and border element do not touch */
8094 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8095 CE_OTHER_GETS_TOUCHED);
8096 CheckElementSideChange(xx, yy, border_element, border_side,
8097 CE_TOUCHED_BY_PLAYER, -1);
8099 else if (IS_PLAYER(xx, yy))
8101 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8103 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8105 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8106 continue; /* center and border element do not touch */
8109 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8110 CE_OTHER_GETS_TOUCHED);
8111 CheckElementSideChange(x, y, center_element, center_side,
8112 CE_TOUCHED_BY_PLAYER, -1);
8119 void TestIfElementTouchesCustomElement(int x, int y)
8121 static int xy[4][2] =
8128 static int change_sides[4][2] =
8130 /* center side border side */
8131 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8132 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8133 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8134 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8136 static int touch_dir[4] =
8143 boolean change_center_element = FALSE;
8144 int center_element_change_page = 0;
8145 int center_element = Feld[x][y]; /* should always be non-moving! */
8148 for (i = 0; i < 4; i++)
8150 int xx = x + xy[i][0];
8151 int yy = y + xy[i][1];
8152 int center_side = change_sides[i][0];
8153 int border_side = change_sides[i][1];
8156 if (!IN_LEV_FIELD(xx, yy))
8159 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8160 border_element = Feld[xx][yy]; /* may be moving! */
8161 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8162 border_element = Feld[xx][yy];
8163 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8164 border_element = MovingOrBlocked2Element(xx, yy);
8166 continue; /* center and border element do not touch */
8168 /* check for change of center element (but change it only once) */
8169 if (IS_CUSTOM_ELEMENT(center_element) &&
8170 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8171 !change_center_element)
8173 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8175 struct ElementChangeInfo *change =
8176 &element_info[center_element].change_page[j];
8178 if (change->can_change &&
8179 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8180 change->sides & border_side &&
8182 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8184 change->trigger_element == border_element
8188 change_center_element = TRUE;
8189 center_element_change_page = j;
8196 /* check for change of border element */
8197 if (IS_CUSTOM_ELEMENT(border_element) &&
8198 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8200 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8202 struct ElementChangeInfo *change =
8203 &element_info[border_element].change_page[j];
8205 if (change->can_change &&
8206 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8207 change->sides & center_side &&
8209 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8211 change->trigger_element == center_element
8215 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8216 CE_OTHER_IS_TOUCHING, j);
8223 if (change_center_element)
8224 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8225 CE_OTHER_IS_TOUCHING, center_element_change_page);
8228 void TestIfElementHitsCustomElement(int x, int y, int direction)
8230 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8231 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8232 int hitx = x + dx, hity = y + dy;
8233 int hitting_element = Feld[x][y];
8235 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8236 !IS_FREE(hitx, hity) &&
8237 (!IS_MOVING(hitx, hity) ||
8238 MovDir[hitx][hity] != direction ||
8239 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8242 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8246 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8250 CheckElementSideChange(x, y, hitting_element,
8251 direction, CE_HITTING_SOMETHING, -1);
8253 if (IN_LEV_FIELD(hitx, hity))
8255 static int opposite_directions[] =
8262 int move_dir_bit = MV_DIR_BIT(direction);
8263 int opposite_direction = opposite_directions[move_dir_bit];
8264 int hitting_side = direction;
8265 int touched_side = opposite_direction;
8266 int touched_element = MovingOrBlocked2Element(hitx, hity);
8268 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8269 MovDir[hitx][hity] != direction ||
8270 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8279 CheckElementSideChange(hitx, hity, touched_element,
8280 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8282 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8283 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8285 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8287 struct ElementChangeInfo *change =
8288 &element_info[hitting_element].change_page[i];
8290 if (change->can_change &&
8291 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8292 change->sides & touched_side &&
8295 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8297 change->trigger_element == touched_element
8301 CheckElementSideChange(x, y, hitting_element,
8302 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8308 if (IS_CUSTOM_ELEMENT(touched_element) &&
8309 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8311 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8313 struct ElementChangeInfo *change =
8314 &element_info[touched_element].change_page[i];
8316 if (change->can_change &&
8317 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8318 change->sides & hitting_side &&
8320 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8322 change->trigger_element == hitting_element
8326 CheckElementSideChange(hitx, hity, touched_element,
8327 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8336 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8338 int i, kill_x = -1, kill_y = -1;
8339 static int test_xy[4][2] =
8346 static int test_dir[4] =
8354 for (i = 0; i < 4; i++)
8356 int test_x, test_y, test_move_dir, test_element;
8358 test_x = good_x + test_xy[i][0];
8359 test_y = good_y + test_xy[i][1];
8360 if (!IN_LEV_FIELD(test_x, test_y))
8364 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8367 test_element = Feld[test_x][test_y];
8369 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8372 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8373 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8375 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8376 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8384 if (kill_x != -1 || kill_y != -1)
8386 if (IS_PLAYER(good_x, good_y))
8388 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8390 if (player->shield_deadly_time_left > 0)
8391 Bang(kill_x, kill_y);
8392 else if (!PLAYER_PROTECTED(good_x, good_y))
8396 Bang(good_x, good_y);
8400 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8402 int i, kill_x = -1, kill_y = -1;
8403 int bad_element = Feld[bad_x][bad_y];
8404 static int test_xy[4][2] =
8411 static int touch_dir[4] =
8418 static int test_dir[4] =
8426 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8429 for (i = 0; i < 4; i++)
8431 int test_x, test_y, test_move_dir, test_element;
8433 test_x = bad_x + test_xy[i][0];
8434 test_y = bad_y + test_xy[i][1];
8435 if (!IN_LEV_FIELD(test_x, test_y))
8439 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8441 test_element = Feld[test_x][test_y];
8443 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8444 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8446 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8447 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8449 /* good thing is player or penguin that does not move away */
8450 if (IS_PLAYER(test_x, test_y))
8452 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8454 if (bad_element == EL_ROBOT && player->is_moving)
8455 continue; /* robot does not kill player if he is moving */
8457 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8459 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8460 continue; /* center and border element do not touch */
8467 else if (test_element == EL_PENGUIN)
8476 if (kill_x != -1 || kill_y != -1)
8478 if (IS_PLAYER(kill_x, kill_y))
8480 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8482 if (player->shield_deadly_time_left > 0)
8484 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8488 Bang(kill_x, kill_y);
8492 void TestIfHeroTouchesBadThing(int x, int y)
8494 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8497 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8499 TestIfGoodThingHitsBadThing(x, y, move_dir);
8502 void TestIfBadThingTouchesHero(int x, int y)
8504 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8507 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8509 TestIfBadThingHitsGoodThing(x, y, move_dir);
8512 void TestIfFriendTouchesBadThing(int x, int y)
8514 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8517 void TestIfBadThingTouchesFriend(int x, int y)
8519 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8522 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8524 int i, kill_x = bad_x, kill_y = bad_y;
8525 static int xy[4][2] =
8533 for (i = 0; i < 4; i++)
8537 x = bad_x + xy[i][0];
8538 y = bad_y + xy[i][1];
8539 if (!IN_LEV_FIELD(x, y))
8542 element = Feld[x][y];
8543 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8544 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8552 if (kill_x != bad_x || kill_y != bad_y)
8556 void KillHero(struct PlayerInfo *player)
8558 int jx = player->jx, jy = player->jy;
8560 if (!player->active)
8563 /* remove accessible field at the player's position */
8564 Feld[jx][jy] = EL_EMPTY;
8566 /* deactivate shield (else Bang()/Explode() would not work right) */
8567 player->shield_normal_time_left = 0;
8568 player->shield_deadly_time_left = 0;
8574 static void KillHeroUnlessProtected(int x, int y)
8576 if (!PLAYER_PROTECTED(x, y))
8577 KillHero(PLAYERINFO(x, y));
8580 void BuryHero(struct PlayerInfo *player)
8582 int jx = player->jx, jy = player->jy;
8584 if (!player->active)
8588 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8590 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8592 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8594 player->GameOver = TRUE;
8598 void RemoveHero(struct PlayerInfo *player)
8600 int jx = player->jx, jy = player->jy;
8601 int i, found = FALSE;
8603 player->present = FALSE;
8604 player->active = FALSE;
8606 if (!ExplodeField[jx][jy])
8607 StorePlayer[jx][jy] = 0;
8609 for (i = 0; i < MAX_PLAYERS; i++)
8610 if (stored_player[i].active)
8614 AllPlayersGone = TRUE;
8621 =============================================================================
8622 checkDiagonalPushing()
8623 -----------------------------------------------------------------------------
8624 check if diagonal input device direction results in pushing of object
8625 (by checking if the alternative direction is walkable, diggable, ...)
8626 =============================================================================
8629 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8630 int x, int y, int real_dx, int real_dy)
8632 int jx, jy, dx, dy, xx, yy;
8634 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8637 /* diagonal direction: check alternative direction */
8642 xx = jx + (dx == 0 ? real_dx : 0);
8643 yy = jy + (dy == 0 ? real_dy : 0);
8645 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8649 =============================================================================
8651 -----------------------------------------------------------------------------
8652 x, y: field next to player (non-diagonal) to try to dig to
8653 real_dx, real_dy: direction as read from input device (can be diagonal)
8654 =============================================================================
8657 int DigField(struct PlayerInfo *player,
8658 int x, int y, int real_dx, int real_dy, int mode)
8660 static int change_sides[4] =
8662 CH_SIDE_RIGHT, /* moving left */
8663 CH_SIDE_LEFT, /* moving right */
8664 CH_SIDE_BOTTOM, /* moving up */
8665 CH_SIDE_TOP, /* moving down */
8667 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8668 int jx = player->jx, jy = player->jy;
8669 int dx = x - jx, dy = y - jy;
8670 int nextx = x + dx, nexty = y + dy;
8671 int move_direction = (dx == -1 ? MV_LEFT :
8672 dx == +1 ? MV_RIGHT :
8674 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8675 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8678 if (player->MovPos == 0)
8680 player->is_digging = FALSE;
8681 player->is_collecting = FALSE;
8684 if (player->MovPos == 0) /* last pushing move finished */
8685 player->is_pushing = FALSE;
8687 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8689 player->is_switching = FALSE;
8690 player->push_delay = 0;
8692 return MF_NO_ACTION;
8695 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8696 return MF_NO_ACTION;
8699 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8701 if (IS_TUBE(Feld[jx][jy]) ||
8702 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8706 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8707 int tube_leave_directions[][2] =
8709 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8710 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8711 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8712 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8713 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8714 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8715 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8716 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8717 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8718 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8719 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8720 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8723 while (tube_leave_directions[i][0] != tube_element)
8726 if (tube_leave_directions[i][0] == -1) /* should not happen */
8730 if (!(tube_leave_directions[i][1] & move_direction))
8731 return MF_NO_ACTION; /* tube has no opening in this direction */
8734 element = Feld[x][y];
8736 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8737 game.engine_version >= VERSION_IDENT(2,2,0,0))
8738 return MF_NO_ACTION;
8742 case EL_SP_PORT_LEFT:
8743 case EL_SP_PORT_RIGHT:
8745 case EL_SP_PORT_DOWN:
8746 case EL_SP_PORT_HORIZONTAL:
8747 case EL_SP_PORT_VERTICAL:
8748 case EL_SP_PORT_ANY:
8749 case EL_SP_GRAVITY_PORT_LEFT:
8750 case EL_SP_GRAVITY_PORT_RIGHT:
8751 case EL_SP_GRAVITY_PORT_UP:
8752 case EL_SP_GRAVITY_PORT_DOWN:
8754 element != EL_SP_PORT_LEFT &&
8755 element != EL_SP_GRAVITY_PORT_LEFT &&
8756 element != EL_SP_PORT_HORIZONTAL &&
8757 element != EL_SP_PORT_ANY) ||
8759 element != EL_SP_PORT_RIGHT &&
8760 element != EL_SP_GRAVITY_PORT_RIGHT &&
8761 element != EL_SP_PORT_HORIZONTAL &&
8762 element != EL_SP_PORT_ANY) ||
8764 element != EL_SP_PORT_UP &&
8765 element != EL_SP_GRAVITY_PORT_UP &&
8766 element != EL_SP_PORT_VERTICAL &&
8767 element != EL_SP_PORT_ANY) ||
8769 element != EL_SP_PORT_DOWN &&
8770 element != EL_SP_GRAVITY_PORT_DOWN &&
8771 element != EL_SP_PORT_VERTICAL &&
8772 element != EL_SP_PORT_ANY) ||
8773 !IN_LEV_FIELD(nextx, nexty) ||
8774 !IS_FREE(nextx, nexty))
8775 return MF_NO_ACTION;
8777 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8778 element == EL_SP_GRAVITY_PORT_RIGHT ||
8779 element == EL_SP_GRAVITY_PORT_UP ||
8780 element == EL_SP_GRAVITY_PORT_DOWN)
8781 game.gravity = !game.gravity;
8783 /* automatically move to the next field with double speed */
8784 player->programmed_action = move_direction;
8786 if (player->move_delay_reset_counter == 0)
8788 player->move_delay_reset_counter = 2; /* two double speed steps */
8790 DOUBLE_PLAYER_SPEED(player);
8793 player->move_delay_reset_counter = 2;
8795 DOUBLE_PLAYER_SPEED(player);
8798 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8802 case EL_TUBE_VERTICAL:
8803 case EL_TUBE_HORIZONTAL:
8804 case EL_TUBE_VERTICAL_LEFT:
8805 case EL_TUBE_VERTICAL_RIGHT:
8806 case EL_TUBE_HORIZONTAL_UP:
8807 case EL_TUBE_HORIZONTAL_DOWN:
8808 case EL_TUBE_LEFT_UP:
8809 case EL_TUBE_LEFT_DOWN:
8810 case EL_TUBE_RIGHT_UP:
8811 case EL_TUBE_RIGHT_DOWN:
8814 int tube_enter_directions[][2] =
8816 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8817 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8818 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8819 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8820 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8821 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8822 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8823 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8824 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8825 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8826 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8827 { -1, MV_NO_MOVING }
8830 while (tube_enter_directions[i][0] != element)
8833 if (tube_enter_directions[i][0] == -1) /* should not happen */
8837 if (!(tube_enter_directions[i][1] & move_direction))
8838 return MF_NO_ACTION; /* tube has no opening in this direction */
8840 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8846 if (IS_WALKABLE(element))
8848 int sound_action = ACTION_WALKING;
8850 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8852 if (!player->key[element - EL_GATE_1])
8853 return MF_NO_ACTION;
8855 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8857 if (!player->key[element - EL_GATE_1_GRAY])
8858 return MF_NO_ACTION;
8860 else if (element == EL_EXIT_OPEN ||
8861 element == EL_SP_EXIT_OPEN ||
8862 element == EL_SP_EXIT_OPENING)
8864 sound_action = ACTION_PASSING; /* player is passing exit */
8866 else if (element == EL_EMPTY)
8868 sound_action = ACTION_MOVING; /* nothing to walk on */
8871 /* play sound from background or player, whatever is available */
8872 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8873 PlayLevelSoundElementAction(x, y, element, sound_action);
8875 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8879 else if (IS_PASSABLE(element))
8881 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8882 return MF_NO_ACTION;
8885 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8886 return MF_NO_ACTION;
8889 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8891 if (!player->key[element - EL_EM_GATE_1])
8892 return MF_NO_ACTION;
8894 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8896 if (!player->key[element - EL_EM_GATE_1_GRAY])
8897 return MF_NO_ACTION;
8900 /* automatically move to the next field with double speed */
8901 player->programmed_action = move_direction;
8903 if (player->move_delay_reset_counter == 0)
8905 player->move_delay_reset_counter = 2; /* two double speed steps */
8907 DOUBLE_PLAYER_SPEED(player);
8910 player->move_delay_reset_counter = 2;
8912 DOUBLE_PLAYER_SPEED(player);
8915 PlayLevelSoundAction(x, y, ACTION_PASSING);
8919 else if (IS_DIGGABLE(element))
8923 if (mode != DF_SNAP)
8926 GfxElement[x][y] = GFX_ELEMENT(element);
8929 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8931 player->is_digging = TRUE;
8934 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8936 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8939 if (mode == DF_SNAP)
8940 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8945 else if (IS_COLLECTIBLE(element))
8949 if (mode != DF_SNAP)
8951 GfxElement[x][y] = element;
8952 player->is_collecting = TRUE;
8955 if (element == EL_SPEED_PILL)
8956 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8957 else if (element == EL_EXTRA_TIME && level.time > 0)
8960 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8962 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8964 player->shield_normal_time_left += 10;
8965 if (element == EL_SHIELD_DEADLY)
8966 player->shield_deadly_time_left += 10;
8968 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8970 if (player->inventory_size < MAX_INVENTORY_SIZE)
8971 player->inventory_element[player->inventory_size++] = element;
8973 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8974 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8976 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8978 player->dynabomb_count++;
8979 player->dynabombs_left++;
8981 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8983 player->dynabomb_size++;
8985 else if (element == EL_DYNABOMB_INCREASE_POWER)
8987 player->dynabomb_xl = TRUE;
8989 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8990 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8992 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8993 element - EL_KEY_1 : element - EL_EM_KEY_1);
8995 player->key[key_nr] = TRUE;
8997 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8998 el2edimg(EL_KEY_1 + key_nr));
8999 redraw_mask |= REDRAW_DOOR_1;
9001 else if (IS_ENVELOPE(element))
9004 player->show_envelope = element;
9006 ShowEnvelope(element - EL_ENVELOPE_1);
9009 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9013 for (i = 0; i < element_info[element].collect_count; i++)
9014 if (player->inventory_size < MAX_INVENTORY_SIZE)
9015 player->inventory_element[player->inventory_size++] = element;
9017 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9018 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9020 else if (element_info[element].collect_count > 0)
9022 local_player->gems_still_needed -=
9023 element_info[element].collect_count;
9024 if (local_player->gems_still_needed < 0)
9025 local_player->gems_still_needed = 0;
9027 DrawText(DX_EMERALDS, DY_EMERALDS,
9028 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9031 RaiseScoreElement(element);
9032 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9034 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9037 if (mode == DF_SNAP)
9038 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9043 else if (IS_PUSHABLE(element))
9045 if (mode == DF_SNAP && element != EL_BD_ROCK)
9046 return MF_NO_ACTION;
9048 if (CAN_FALL(element) && dy)
9049 return MF_NO_ACTION;
9051 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9052 !(element == EL_SPRING && use_spring_bug))
9053 return MF_NO_ACTION;
9056 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9057 ((move_direction & MV_VERTICAL &&
9058 ((element_info[element].move_pattern & MV_LEFT &&
9059 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9060 (element_info[element].move_pattern & MV_RIGHT &&
9061 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9062 (move_direction & MV_HORIZONTAL &&
9063 ((element_info[element].move_pattern & MV_UP &&
9064 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9065 (element_info[element].move_pattern & MV_DOWN &&
9066 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9067 return MF_NO_ACTION;
9071 /* do not push elements already moving away faster than player */
9072 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9073 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9074 return MF_NO_ACTION;
9076 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9077 return MF_NO_ACTION;
9081 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9083 if (player->push_delay_value == -1)
9084 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9086 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9088 if (!player->is_pushing)
9089 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9093 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9094 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9095 !player_is_pushing))
9096 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9099 if (!player->is_pushing &&
9100 game.engine_version >= VERSION_IDENT(2,2,0,7))
9101 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9105 printf("::: push delay: %ld [%d, %d] [%d]\n",
9106 player->push_delay_value, FrameCounter, game.engine_version,
9107 player->is_pushing);
9110 player->is_pushing = TRUE;
9112 if (!(IN_LEV_FIELD(nextx, nexty) &&
9113 (IS_FREE(nextx, nexty) ||
9114 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9115 IS_SB_ELEMENT(element)))))
9116 return MF_NO_ACTION;
9118 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9119 return MF_NO_ACTION;
9121 if (player->push_delay == 0) /* new pushing; restart delay */
9122 player->push_delay = FrameCounter;
9124 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9125 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9126 element != EL_SPRING && element != EL_BALLOON)
9128 /* make sure that there is no move delay before next try to push */
9129 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9130 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9132 return MF_NO_ACTION;
9136 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9139 if (IS_SB_ELEMENT(element))
9141 if (element == EL_SOKOBAN_FIELD_FULL)
9143 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9144 local_player->sokobanfields_still_needed++;
9147 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9149 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9150 local_player->sokobanfields_still_needed--;
9153 Feld[x][y] = EL_SOKOBAN_OBJECT;
9155 if (Back[x][y] == Back[nextx][nexty])
9156 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9157 else if (Back[x][y] != 0)
9158 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9161 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9164 if (local_player->sokobanfields_still_needed == 0 &&
9165 game.emulation == EMU_SOKOBAN)
9167 player->LevelSolved = player->GameOver = TRUE;
9168 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9172 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9174 InitMovingField(x, y, move_direction);
9175 GfxAction[x][y] = ACTION_PUSHING;
9177 if (mode == DF_SNAP)
9178 ContinueMoving(x, y);
9180 MovPos[x][y] = (dx != 0 ? dx : dy);
9182 Pushed[x][y] = TRUE;
9183 Pushed[nextx][nexty] = TRUE;
9185 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9186 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9188 player->push_delay_value = -1; /* get new value later */
9190 CheckTriggeredElementSideChange(x, y, element, dig_side,
9191 CE_OTHER_GETS_PUSHED);
9192 CheckElementSideChange(x, y, element, dig_side,
9193 CE_PUSHED_BY_PLAYER, -1);
9197 else if (IS_SWITCHABLE(element))
9199 if (PLAYER_SWITCHING(player, x, y))
9202 player->is_switching = TRUE;
9203 player->switch_x = x;
9204 player->switch_y = y;
9206 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9208 if (element == EL_ROBOT_WHEEL)
9210 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9214 DrawLevelField(x, y);
9216 else if (element == EL_SP_TERMINAL)
9220 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9222 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9224 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9225 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9228 else if (IS_BELT_SWITCH(element))
9230 ToggleBeltSwitch(x, y);
9232 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9233 element == EL_SWITCHGATE_SWITCH_DOWN)
9235 ToggleSwitchgateSwitch(x, y);
9237 else if (element == EL_LIGHT_SWITCH ||
9238 element == EL_LIGHT_SWITCH_ACTIVE)
9240 ToggleLightSwitch(x, y);
9243 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9244 SND_LIGHT_SWITCH_ACTIVATING :
9245 SND_LIGHT_SWITCH_DEACTIVATING);
9248 else if (element == EL_TIMEGATE_SWITCH)
9250 ActivateTimegateSwitch(x, y);
9252 else if (element == EL_BALLOON_SWITCH_LEFT ||
9253 element == EL_BALLOON_SWITCH_RIGHT ||
9254 element == EL_BALLOON_SWITCH_UP ||
9255 element == EL_BALLOON_SWITCH_DOWN ||
9256 element == EL_BALLOON_SWITCH_ANY)
9258 if (element == EL_BALLOON_SWITCH_ANY)
9259 game.balloon_dir = move_direction;
9261 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9262 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9263 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9264 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9267 else if (element == EL_LAMP)
9269 Feld[x][y] = EL_LAMP_ACTIVE;
9270 local_player->lights_still_needed--;
9272 DrawLevelField(x, y);
9274 else if (element == EL_TIME_ORB_FULL)
9276 Feld[x][y] = EL_TIME_ORB_EMPTY;
9278 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9280 DrawLevelField(x, y);
9283 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9291 if (!PLAYER_SWITCHING(player, x, y))
9293 player->is_switching = TRUE;
9294 player->switch_x = x;
9295 player->switch_y = y;
9297 CheckTriggeredElementSideChange(x, y, element, dig_side,
9298 CE_OTHER_IS_SWITCHING);
9299 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9302 CheckTriggeredElementSideChange(x, y, element, dig_side,
9303 CE_OTHER_GETS_PRESSED);
9304 CheckElementSideChange(x, y, element, dig_side,
9305 CE_PRESSED_BY_PLAYER, -1);
9308 return MF_NO_ACTION;
9311 player->push_delay = 0;
9313 if (Feld[x][y] != element) /* really digged/collected something */
9314 player->is_collecting = !player->is_digging;
9319 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9321 int jx = player->jx, jy = player->jy;
9322 int x = jx + dx, y = jy + dy;
9323 int snap_direction = (dx == -1 ? MV_LEFT :
9324 dx == +1 ? MV_RIGHT :
9326 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9328 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9331 if (!player->active || !IN_LEV_FIELD(x, y))
9339 if (player->MovPos == 0)
9340 player->is_pushing = FALSE;
9342 player->is_snapping = FALSE;
9344 if (player->MovPos == 0)
9346 player->is_moving = FALSE;
9347 player->is_digging = FALSE;
9348 player->is_collecting = FALSE;
9354 if (player->is_snapping)
9357 player->MovDir = snap_direction;
9360 if (player->MovPos == 0)
9363 player->is_moving = FALSE;
9364 player->is_digging = FALSE;
9365 player->is_collecting = FALSE;
9368 player->is_dropping = FALSE;
9370 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9373 player->is_snapping = TRUE;
9376 if (player->MovPos == 0)
9379 player->is_moving = FALSE;
9380 player->is_digging = FALSE;
9381 player->is_collecting = FALSE;
9384 DrawLevelField(x, y);
9390 boolean DropElement(struct PlayerInfo *player)
9392 int jx = player->jx, jy = player->jy;
9393 int old_element = Feld[jx][jy];
9396 /* check if player is active, not moving and ready to drop */
9397 if (!player->active || player->MovPos || player->drop_delay > 0)
9400 /* check if player has anything that can be dropped */
9401 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9404 /* check if anything can be dropped at the current position */
9405 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9408 /* collected custom elements can only be dropped on empty fields */
9409 if (player->inventory_size > 0 &&
9410 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9411 && old_element != EL_EMPTY)
9414 if (old_element != EL_EMPTY)
9415 Back[jx][jy] = old_element; /* store old element on this field */
9417 ResetGfxAnimation(jx, jy);
9418 ResetRandomAnimationValue(jx, jy);
9420 if (player->inventory_size > 0)
9422 player->inventory_size--;
9423 new_element = player->inventory_element[player->inventory_size];
9425 if (new_element == EL_DYNAMITE)
9426 new_element = EL_DYNAMITE_ACTIVE;
9427 else if (new_element == EL_SP_DISK_RED)
9428 new_element = EL_SP_DISK_RED_ACTIVE;
9430 Feld[jx][jy] = new_element;
9432 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9433 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9435 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9436 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9438 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9440 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9441 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9443 TestIfElementTouchesCustomElement(jx, jy);
9445 else /* player is dropping a dyna bomb */
9447 player->dynabombs_left--;
9448 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9450 Feld[jx][jy] = new_element;
9452 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9453 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9455 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9462 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9464 InitField(jx, jy, FALSE);
9465 if (CAN_MOVE(Feld[jx][jy]))
9469 new_element = Feld[jx][jy];
9471 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9472 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9474 int move_stepsize = element_info[new_element].move_stepsize;
9475 int direction, dx, dy, nextx, nexty;
9477 if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
9478 MovDir[jx][jy] = player->MovDir;
9480 direction = MovDir[jx][jy];
9481 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9482 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9486 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9489 WasJustMoving[jx][jy] = 3;
9491 InitMovingField(jx, jy, direction);
9492 ContinueMoving(jx, jy);
9497 Changed[jx][jy] = 0; /* allow another change */
9500 TestIfElementHitsCustomElement(jx, jy, direction);
9502 CheckElementSideChange(jx, jy, new_element,
9503 direction, CE_HITTING_SOMETHING, -1);
9507 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9511 player->drop_delay = 8 + 8 + 8;
9516 player->is_dropping = TRUE;
9522 /* ------------------------------------------------------------------------- */
9523 /* game sound playing functions */
9524 /* ------------------------------------------------------------------------- */
9526 static int *loop_sound_frame = NULL;
9527 static int *loop_sound_volume = NULL;
9529 void InitPlayLevelSound()
9531 int num_sounds = getSoundListSize();
9533 checked_free(loop_sound_frame);
9534 checked_free(loop_sound_volume);
9536 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9537 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9540 static void PlayLevelSound(int x, int y, int nr)
9542 int sx = SCREENX(x), sy = SCREENY(y);
9543 int volume, stereo_position;
9544 int max_distance = 8;
9545 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9547 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9548 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9551 if (!IN_LEV_FIELD(x, y) ||
9552 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9553 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9556 volume = SOUND_MAX_VOLUME;
9558 if (!IN_SCR_FIELD(sx, sy))
9560 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9561 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9563 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9566 stereo_position = (SOUND_MAX_LEFT +
9567 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9568 (SCR_FIELDX + 2 * max_distance));
9570 if (IS_LOOP_SOUND(nr))
9572 /* This assures that quieter loop sounds do not overwrite louder ones,
9573 while restarting sound volume comparison with each new game frame. */
9575 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9578 loop_sound_volume[nr] = volume;
9579 loop_sound_frame[nr] = FrameCounter;
9582 PlaySoundExt(nr, volume, stereo_position, type);
9585 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9587 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9588 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9589 y < LEVELY(BY1) ? LEVELY(BY1) :
9590 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9594 static void PlayLevelSoundAction(int x, int y, int action)
9596 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9599 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9601 int sound_effect = element_info[element].sound[action];
9603 if (sound_effect != SND_UNDEFINED)
9604 PlayLevelSound(x, y, sound_effect);
9607 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9610 int sound_effect = element_info[element].sound[action];
9612 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9613 PlayLevelSound(x, y, sound_effect);
9616 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9618 int sound_effect = element_info[Feld[x][y]].sound[action];
9620 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9621 PlayLevelSound(x, y, sound_effect);
9624 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9626 int sound_effect = element_info[Feld[x][y]].sound[action];
9628 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9629 StopSound(sound_effect);
9632 static void PlayLevelMusic()
9634 if (levelset.music[level_nr] != MUS_UNDEFINED)
9635 PlayMusic(levelset.music[level_nr]); /* from config file */
9637 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9640 void RaiseScore(int value)
9642 local_player->score += value;
9643 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9646 void RaiseScoreElement(int element)
9652 case EL_EMERALD_YELLOW:
9653 case EL_EMERALD_RED:
9654 case EL_EMERALD_PURPLE:
9655 case EL_SP_INFOTRON:
9656 RaiseScore(level.score[SC_EMERALD]);
9659 RaiseScore(level.score[SC_DIAMOND]);
9662 RaiseScore(level.score[SC_CRYSTAL]);
9665 RaiseScore(level.score[SC_PEARL]);
9668 case EL_BD_BUTTERFLY:
9669 case EL_SP_ELECTRON:
9670 RaiseScore(level.score[SC_BUG]);
9674 case EL_SP_SNIKSNAK:
9675 RaiseScore(level.score[SC_SPACESHIP]);
9678 case EL_DARK_YAMYAM:
9679 RaiseScore(level.score[SC_YAMYAM]);
9682 RaiseScore(level.score[SC_ROBOT]);
9685 RaiseScore(level.score[SC_PACMAN]);
9688 RaiseScore(level.score[SC_NUT]);
9691 case EL_SP_DISK_RED:
9692 case EL_DYNABOMB_INCREASE_NUMBER:
9693 case EL_DYNABOMB_INCREASE_SIZE:
9694 case EL_DYNABOMB_INCREASE_POWER:
9695 RaiseScore(level.score[SC_DYNAMITE]);
9697 case EL_SHIELD_NORMAL:
9698 case EL_SHIELD_DEADLY:
9699 RaiseScore(level.score[SC_SHIELD]);
9702 RaiseScore(level.score[SC_TIME_BONUS]);
9708 RaiseScore(level.score[SC_KEY]);
9711 RaiseScore(element_info[element].collect_score);
9716 void RequestQuitGame(boolean ask_if_really_quit)
9718 if (AllPlayersGone ||
9719 !ask_if_really_quit ||
9720 level_editor_test_game ||
9721 Request("Do you really want to quit the game ?",
9722 REQ_ASK | REQ_STAY_CLOSED))
9724 #if defined(PLATFORM_UNIX)
9725 if (options.network)
9726 SendToServer_StopPlaying();
9730 game_status = GAME_MODE_MAIN;
9736 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9741 /* ---------- new game button stuff ---------------------------------------- */
9743 /* graphic position values for game buttons */
9744 #define GAME_BUTTON_XSIZE 30
9745 #define GAME_BUTTON_YSIZE 30
9746 #define GAME_BUTTON_XPOS 5
9747 #define GAME_BUTTON_YPOS 215
9748 #define SOUND_BUTTON_XPOS 5
9749 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9751 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9752 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9753 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9754 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9755 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9756 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9763 } gamebutton_info[NUM_GAME_BUTTONS] =
9766 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9771 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9776 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9781 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9782 SOUND_CTRL_ID_MUSIC,
9783 "background music on/off"
9786 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9787 SOUND_CTRL_ID_LOOPS,
9788 "sound loops on/off"
9791 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9792 SOUND_CTRL_ID_SIMPLE,
9793 "normal sounds on/off"
9797 void CreateGameButtons()
9801 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9803 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9804 struct GadgetInfo *gi;
9807 unsigned long event_mask;
9808 int gd_xoffset, gd_yoffset;
9809 int gd_x1, gd_x2, gd_y1, gd_y2;
9812 gd_xoffset = gamebutton_info[i].x;
9813 gd_yoffset = gamebutton_info[i].y;
9814 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9815 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9817 if (id == GAME_CTRL_ID_STOP ||
9818 id == GAME_CTRL_ID_PAUSE ||
9819 id == GAME_CTRL_ID_PLAY)
9821 button_type = GD_TYPE_NORMAL_BUTTON;
9823 event_mask = GD_EVENT_RELEASED;
9824 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9825 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9829 button_type = GD_TYPE_CHECK_BUTTON;
9831 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9832 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9833 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9834 event_mask = GD_EVENT_PRESSED;
9835 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9836 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9839 gi = CreateGadget(GDI_CUSTOM_ID, id,
9840 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9841 GDI_X, DX + gd_xoffset,
9842 GDI_Y, DY + gd_yoffset,
9843 GDI_WIDTH, GAME_BUTTON_XSIZE,
9844 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9845 GDI_TYPE, button_type,
9846 GDI_STATE, GD_BUTTON_UNPRESSED,
9847 GDI_CHECKED, checked,
9848 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9849 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9850 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9851 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9852 GDI_EVENT_MASK, event_mask,
9853 GDI_CALLBACK_ACTION, HandleGameButtons,
9857 Error(ERR_EXIT, "cannot create gadget");
9859 game_gadget[id] = gi;
9863 void FreeGameButtons()
9867 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9868 FreeGadget(game_gadget[i]);
9871 static void MapGameButtons()
9875 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9876 MapGadget(game_gadget[i]);
9879 void UnmapGameButtons()
9883 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9884 UnmapGadget(game_gadget[i]);
9887 static void HandleGameButtons(struct GadgetInfo *gi)
9889 int id = gi->custom_id;
9891 if (game_status != GAME_MODE_PLAYING)
9896 case GAME_CTRL_ID_STOP:
9897 RequestQuitGame(TRUE);
9900 case GAME_CTRL_ID_PAUSE:
9901 if (options.network)
9903 #if defined(PLATFORM_UNIX)
9905 SendToServer_ContinuePlaying();
9907 SendToServer_PausePlaying();
9911 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9914 case GAME_CTRL_ID_PLAY:
9917 #if defined(PLATFORM_UNIX)
9918 if (options.network)
9919 SendToServer_ContinuePlaying();
9923 tape.pausing = FALSE;
9924 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9929 case SOUND_CTRL_ID_MUSIC:
9930 if (setup.sound_music)
9932 setup.sound_music = FALSE;
9935 else if (audio.music_available)
9937 setup.sound = setup.sound_music = TRUE;
9939 SetAudioMode(setup.sound);
9945 case SOUND_CTRL_ID_LOOPS:
9946 if (setup.sound_loops)
9947 setup.sound_loops = FALSE;
9948 else if (audio.loops_available)
9950 setup.sound = setup.sound_loops = TRUE;
9951 SetAudioMode(setup.sound);
9955 case SOUND_CTRL_ID_SIMPLE:
9956 if (setup.sound_simple)
9957 setup.sound_simple = FALSE;
9958 else if (audio.sound_available)
9960 setup.sound = setup.sound_simple = TRUE;
9961 SetAudioMode(setup.sound);