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_EMERALD_RED;
2505 Store[x][y] = EL_EMERALD;
2508 Store[x][y] = EL_EMERALD_PURPLE;
2512 Store[x][y] = EL_EMERALD_YELLOW;
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 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2660 element = Feld[x][y] = Back[x][y];
2663 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2664 GfxDir[x][y] = MV_NO_MOVING;
2665 ChangeDelay[x][y] = 0;
2666 ChangePage[x][y] = -1;
2668 InitField(x, y, FALSE);
2669 if (CAN_MOVE(element))
2671 DrawLevelField(x, y);
2673 TestIfElementTouchesCustomElement(x, y);
2675 if (GFX_CRUMBLED(element))
2676 DrawLevelFieldCrumbledSandNeighbours(x, y);
2678 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2679 StorePlayer[x][y] = 0;
2681 if (ELEM_IS_PLAYER(element))
2682 RelocatePlayer(x, y, element);
2684 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2687 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2689 int stored = Store[x][y];
2690 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2691 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2694 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2697 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2698 element_info[GfxElement[x][y]].token_name,
2703 DrawLevelFieldCrumbledSand(x, y);
2705 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2707 DrawLevelElement(x, y, Back[x][y]);
2708 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2710 else if (IS_WALKABLE_UNDER(Back[x][y]))
2712 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2713 DrawLevelElementThruMask(x, y, Back[x][y]);
2715 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2716 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2720 void DynaExplode(int ex, int ey)
2723 int dynabomb_element = Feld[ex][ey];
2724 int dynabomb_size = 1;
2725 boolean dynabomb_xl = FALSE;
2726 struct PlayerInfo *player;
2727 static int xy[4][2] =
2735 if (IS_ACTIVE_BOMB(dynabomb_element))
2737 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2738 dynabomb_size = player->dynabomb_size;
2739 dynabomb_xl = player->dynabomb_xl;
2740 player->dynabombs_left++;
2743 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2745 for (i = 0; i < 4; i++)
2747 for (j = 1; j <= dynabomb_size; j++)
2749 int x = ex + j * xy[i % 4][0];
2750 int y = ey + j * xy[i % 4][1];
2753 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2756 element = Feld[x][y];
2758 /* do not restart explosions of fields with active bombs */
2759 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2762 Explode(x, y, EX_PHASE_START, EX_BORDER);
2764 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2765 if (element != EL_EMPTY &&
2766 element != EL_SAND &&
2767 element != EL_EXPLOSION &&
2774 void Bang(int x, int y)
2777 int element = MovingOrBlocked2Element(x, y);
2779 int element = Feld[x][y];
2783 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2785 if (IS_PLAYER(x, y))
2788 struct PlayerInfo *player = PLAYERINFO(x, y);
2790 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2791 player->element_nr);
2796 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2798 if (game.emulation == EMU_SUPAPLEX)
2799 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2801 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2806 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2814 case EL_BD_BUTTERFLY:
2817 case EL_DARK_YAMYAM:
2821 RaiseScoreElement(element);
2822 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2824 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2825 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2826 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2827 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2828 case EL_DYNABOMB_INCREASE_NUMBER:
2829 case EL_DYNABOMB_INCREASE_SIZE:
2830 case EL_DYNABOMB_INCREASE_POWER:
2835 case EL_LAMP_ACTIVE:
2836 if (IS_PLAYER(x, y))
2837 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2839 Explode(x, y, EX_PHASE_START, EX_CENTER);
2842 if (CAN_EXPLODE_DYNA(element))
2844 else if (CAN_EXPLODE_1X1(element))
2845 Explode(x, y, EX_PHASE_START, EX_CENTER);
2847 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2851 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2854 void SplashAcid(int x, int y)
2856 int element = Feld[x][y];
2858 if (element != EL_ACID_SPLASH_LEFT &&
2859 element != EL_ACID_SPLASH_RIGHT)
2861 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2863 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2864 (!IN_LEV_FIELD(x-1, y-1) ||
2865 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2866 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2868 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2869 (!IN_LEV_FIELD(x+1, y-1) ||
2870 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2871 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2875 static void InitBeltMovement()
2877 static int belt_base_element[4] =
2879 EL_CONVEYOR_BELT_1_LEFT,
2880 EL_CONVEYOR_BELT_2_LEFT,
2881 EL_CONVEYOR_BELT_3_LEFT,
2882 EL_CONVEYOR_BELT_4_LEFT
2884 static int belt_base_active_element[4] =
2886 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2887 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2888 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2889 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2894 /* set frame order for belt animation graphic according to belt direction */
2895 for (i = 0; i < 4; i++)
2899 for (j = 0; j < 3; j++)
2901 int element = belt_base_active_element[belt_nr] + j;
2902 int graphic = el2img(element);
2904 if (game.belt_dir[i] == MV_LEFT)
2905 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2907 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2911 for (y = 0; y < lev_fieldy; y++)
2913 for (x = 0; x < lev_fieldx; x++)
2915 int element = Feld[x][y];
2917 for (i = 0; i < 4; i++)
2919 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2921 int e_belt_nr = getBeltNrFromBeltElement(element);
2924 if (e_belt_nr == belt_nr)
2926 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2928 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2936 static void ToggleBeltSwitch(int x, int y)
2938 static int belt_base_element[4] =
2940 EL_CONVEYOR_BELT_1_LEFT,
2941 EL_CONVEYOR_BELT_2_LEFT,
2942 EL_CONVEYOR_BELT_3_LEFT,
2943 EL_CONVEYOR_BELT_4_LEFT
2945 static int belt_base_active_element[4] =
2947 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2948 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2949 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2950 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2952 static int belt_base_switch_element[4] =
2954 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2955 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2956 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2957 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2959 static int belt_move_dir[4] =
2967 int element = Feld[x][y];
2968 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2969 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2970 int belt_dir = belt_move_dir[belt_dir_nr];
2973 if (!IS_BELT_SWITCH(element))
2976 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2977 game.belt_dir[belt_nr] = belt_dir;
2979 if (belt_dir_nr == 3)
2982 /* set frame order for belt animation graphic according to belt direction */
2983 for (i = 0; i < 3; i++)
2985 int element = belt_base_active_element[belt_nr] + i;
2986 int graphic = el2img(element);
2988 if (belt_dir == MV_LEFT)
2989 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2991 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2994 for (yy = 0; yy < lev_fieldy; yy++)
2996 for (xx = 0; xx < lev_fieldx; xx++)
2998 int element = Feld[xx][yy];
3000 if (IS_BELT_SWITCH(element))
3002 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3004 if (e_belt_nr == belt_nr)
3006 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3007 DrawLevelField(xx, yy);
3010 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3012 int e_belt_nr = getBeltNrFromBeltElement(element);
3014 if (e_belt_nr == belt_nr)
3016 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3018 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3019 DrawLevelField(xx, yy);
3022 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3024 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3026 if (e_belt_nr == belt_nr)
3028 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3030 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3031 DrawLevelField(xx, yy);
3038 static void ToggleSwitchgateSwitch(int x, int y)
3042 game.switchgate_pos = !game.switchgate_pos;
3044 for (yy = 0; yy < lev_fieldy; yy++)
3046 for (xx = 0; xx < lev_fieldx; xx++)
3048 int element = Feld[xx][yy];
3050 if (element == EL_SWITCHGATE_SWITCH_UP ||
3051 element == EL_SWITCHGATE_SWITCH_DOWN)
3053 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3054 DrawLevelField(xx, yy);
3056 else if (element == EL_SWITCHGATE_OPEN ||
3057 element == EL_SWITCHGATE_OPENING)
3059 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3061 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3063 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3066 else if (element == EL_SWITCHGATE_CLOSED ||
3067 element == EL_SWITCHGATE_CLOSING)
3069 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3071 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3073 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3080 static int getInvisibleActiveFromInvisibleElement(int element)
3082 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3083 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3084 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3088 static int getInvisibleFromInvisibleActiveElement(int element)
3090 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3091 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3092 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3096 static void RedrawAllLightSwitchesAndInvisibleElements()
3100 for (y = 0; y < lev_fieldy; y++)
3102 for (x = 0; x < lev_fieldx; x++)
3104 int element = Feld[x][y];
3106 if (element == EL_LIGHT_SWITCH &&
3107 game.light_time_left > 0)
3109 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3110 DrawLevelField(x, y);
3112 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3113 game.light_time_left == 0)
3115 Feld[x][y] = EL_LIGHT_SWITCH;
3116 DrawLevelField(x, y);
3118 else if (element == EL_INVISIBLE_STEELWALL ||
3119 element == EL_INVISIBLE_WALL ||
3120 element == EL_INVISIBLE_SAND)
3122 if (game.light_time_left > 0)
3123 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3125 DrawLevelField(x, y);
3127 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3128 element == EL_INVISIBLE_WALL_ACTIVE ||
3129 element == EL_INVISIBLE_SAND_ACTIVE)
3131 if (game.light_time_left == 0)
3132 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3134 DrawLevelField(x, y);
3140 static void ToggleLightSwitch(int x, int y)
3142 int element = Feld[x][y];
3144 game.light_time_left =
3145 (element == EL_LIGHT_SWITCH ?
3146 level.time_light * FRAMES_PER_SECOND : 0);
3148 RedrawAllLightSwitchesAndInvisibleElements();
3151 static void ActivateTimegateSwitch(int x, int y)
3155 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3157 for (yy = 0; yy < lev_fieldy; yy++)
3159 for (xx = 0; xx < lev_fieldx; xx++)
3161 int element = Feld[xx][yy];
3163 if (element == EL_TIMEGATE_CLOSED ||
3164 element == EL_TIMEGATE_CLOSING)
3166 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3167 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3171 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3173 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3174 DrawLevelField(xx, yy);
3181 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3184 inline static int getElementMoveStepsize(int x, int y)
3186 int element = Feld[x][y];
3187 int direction = MovDir[x][y];
3188 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3189 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3190 int horiz_move = (dx != 0);
3191 int sign = (horiz_move ? dx : dy);
3192 int step = sign * element_info[element].move_stepsize;
3194 /* special values for move stepsize for spring and things on conveyor belt */
3197 if (CAN_FALL(element) &&
3198 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3199 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3200 else if (element == EL_SPRING)
3201 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3207 void Impact(int x, int y)
3209 boolean lastline = (y == lev_fieldy-1);
3210 boolean object_hit = FALSE;
3211 boolean impact = (lastline || object_hit);
3212 int element = Feld[x][y];
3213 int smashed = EL_UNDEFINED;
3215 if (!lastline) /* check if element below was hit */
3217 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3220 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3221 MovDir[x][y + 1] != MV_DOWN ||
3222 MovPos[x][y + 1] <= TILEY / 2));
3225 object_hit = !IS_FREE(x, y + 1);
3228 /* do not smash moving elements that left the smashed field in time */
3229 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3230 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3234 smashed = MovingOrBlocked2Element(x, y + 1);
3236 impact = (lastline || object_hit);
3239 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3245 /* only reset graphic animation if graphic really changes after impact */
3247 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3249 ResetGfxAnimation(x, y);
3250 DrawLevelField(x, y);
3253 if (impact && CAN_EXPLODE_IMPACT(element))
3258 else if (impact && element == EL_PEARL)
3260 Feld[x][y] = EL_PEARL_BREAKING;
3261 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3264 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3266 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3271 if (impact && element == EL_AMOEBA_DROP)
3273 if (object_hit && IS_PLAYER(x, y + 1))
3274 KillHeroUnlessProtected(x, y + 1);
3275 else if (object_hit && smashed == EL_PENGUIN)
3279 Feld[x][y] = EL_AMOEBA_GROWING;
3280 Store[x][y] = EL_AMOEBA_WET;
3282 ResetRandomAnimationValue(x, y);
3287 if (object_hit) /* check which object was hit */
3289 if (CAN_PASS_MAGIC_WALL(element) &&
3290 (smashed == EL_MAGIC_WALL ||
3291 smashed == EL_BD_MAGIC_WALL))
3294 int activated_magic_wall =
3295 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3296 EL_BD_MAGIC_WALL_ACTIVE);
3298 /* activate magic wall / mill */
3299 for (yy = 0; yy < lev_fieldy; yy++)
3300 for (xx = 0; xx < lev_fieldx; xx++)
3301 if (Feld[xx][yy] == smashed)
3302 Feld[xx][yy] = activated_magic_wall;
3304 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3305 game.magic_wall_active = TRUE;
3307 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3308 SND_MAGIC_WALL_ACTIVATING :
3309 SND_BD_MAGIC_WALL_ACTIVATING));
3312 if (IS_PLAYER(x, y + 1))
3314 if (CAN_SMASH_PLAYER(element))
3316 KillHeroUnlessProtected(x, y + 1);
3320 else if (smashed == EL_PENGUIN)
3322 if (CAN_SMASH_PLAYER(element))
3328 else if (element == EL_BD_DIAMOND)
3330 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3336 else if ((element == EL_SP_INFOTRON ||
3337 element == EL_SP_ZONK) &&
3338 (smashed == EL_SP_SNIKSNAK ||
3339 smashed == EL_SP_ELECTRON ||
3340 smashed == EL_SP_DISK_ORANGE))
3346 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3352 else if (CAN_SMASH_EVERYTHING(element))
3354 if (IS_CLASSIC_ENEMY(smashed) ||
3355 CAN_EXPLODE_SMASHED(smashed))
3360 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3362 if (smashed == EL_LAMP ||
3363 smashed == EL_LAMP_ACTIVE)
3368 else if (smashed == EL_NUT)
3370 Feld[x][y + 1] = EL_NUT_BREAKING;
3371 PlayLevelSound(x, y, SND_NUT_BREAKING);
3372 RaiseScoreElement(EL_NUT);
3375 else if (smashed == EL_PEARL)
3377 Feld[x][y + 1] = EL_PEARL_BREAKING;
3378 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3381 else if (smashed == EL_DIAMOND)
3383 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3384 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3387 else if (IS_BELT_SWITCH(smashed))
3389 ToggleBeltSwitch(x, y + 1);
3391 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3392 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3394 ToggleSwitchgateSwitch(x, y + 1);
3396 else if (smashed == EL_LIGHT_SWITCH ||
3397 smashed == EL_LIGHT_SWITCH_ACTIVE)
3399 ToggleLightSwitch(x, y + 1);
3403 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3405 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3406 CE_OTHER_IS_SWITCHING);
3407 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3413 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3418 /* play sound of magic wall / mill */
3420 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3421 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3423 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3424 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3425 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3426 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3431 /* play sound of object that hits the ground */
3432 if (lastline || object_hit)
3433 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3436 inline static void TurnRoundExt(int x, int y)
3448 { 0, 0 }, { 0, 0 }, { 0, 0 },
3453 int left, right, back;
3457 { MV_DOWN, MV_UP, MV_RIGHT },
3458 { MV_UP, MV_DOWN, MV_LEFT },
3460 { MV_LEFT, MV_RIGHT, MV_DOWN },
3464 { MV_RIGHT, MV_LEFT, MV_UP }
3467 int element = Feld[x][y];
3468 int move_pattern = element_info[element].move_pattern;
3470 int old_move_dir = MovDir[x][y];
3471 int left_dir = turn[old_move_dir].left;
3472 int right_dir = turn[old_move_dir].right;
3473 int back_dir = turn[old_move_dir].back;
3475 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3476 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3477 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3478 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3480 int left_x = x + left_dx, left_y = y + left_dy;
3481 int right_x = x + right_dx, right_y = y + right_dy;
3482 int move_x = x + move_dx, move_y = y + move_dy;
3486 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3488 TestIfBadThingTouchesOtherBadThing(x, y);
3490 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3491 MovDir[x][y] = right_dir;
3492 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3493 MovDir[x][y] = left_dir;
3495 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3497 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3500 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3501 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3503 TestIfBadThingTouchesOtherBadThing(x, y);
3505 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3506 MovDir[x][y] = left_dir;
3507 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3508 MovDir[x][y] = right_dir;
3510 if ((element == EL_SPACESHIP ||
3511 element == EL_SP_SNIKSNAK ||
3512 element == EL_SP_ELECTRON)
3513 && MovDir[x][y] != old_move_dir)
3515 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3518 else if (element == EL_YAMYAM)
3520 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3521 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3523 if (can_turn_left && can_turn_right)
3524 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3525 else if (can_turn_left)
3526 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3527 else if (can_turn_right)
3528 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3530 MovDir[x][y] = back_dir;
3532 MovDelay[x][y] = 16 + 16 * RND(3);
3534 else if (element == EL_DARK_YAMYAM)
3536 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3537 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3539 if (can_turn_left && can_turn_right)
3540 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3541 else if (can_turn_left)
3542 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3543 else if (can_turn_right)
3544 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3546 MovDir[x][y] = back_dir;
3548 MovDelay[x][y] = 16 + 16 * RND(3);
3550 else if (element == EL_PACMAN)
3552 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3553 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3555 if (can_turn_left && can_turn_right)
3556 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3557 else if (can_turn_left)
3558 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3559 else if (can_turn_right)
3560 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3562 MovDir[x][y] = back_dir;
3564 MovDelay[x][y] = 6 + RND(40);
3566 else if (element == EL_PIG)
3568 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3569 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3570 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3571 boolean should_turn_left, should_turn_right, should_move_on;
3573 int rnd = RND(rnd_value);
3575 should_turn_left = (can_turn_left &&
3577 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3578 y + back_dy + left_dy)));
3579 should_turn_right = (can_turn_right &&
3581 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3582 y + back_dy + right_dy)));
3583 should_move_on = (can_move_on &&
3586 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3587 y + move_dy + left_dy) ||
3588 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3589 y + move_dy + right_dy)));
3591 if (should_turn_left || should_turn_right || should_move_on)
3593 if (should_turn_left && should_turn_right && should_move_on)
3594 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3595 rnd < 2 * rnd_value / 3 ? right_dir :
3597 else if (should_turn_left && should_turn_right)
3598 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3599 else if (should_turn_left && should_move_on)
3600 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3601 else if (should_turn_right && should_move_on)
3602 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3603 else if (should_turn_left)
3604 MovDir[x][y] = left_dir;
3605 else if (should_turn_right)
3606 MovDir[x][y] = right_dir;
3607 else if (should_move_on)
3608 MovDir[x][y] = old_move_dir;
3610 else if (can_move_on && rnd > rnd_value / 8)
3611 MovDir[x][y] = old_move_dir;
3612 else if (can_turn_left && can_turn_right)
3613 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3614 else if (can_turn_left && rnd > rnd_value / 8)
3615 MovDir[x][y] = left_dir;
3616 else if (can_turn_right && rnd > rnd_value/8)
3617 MovDir[x][y] = right_dir;
3619 MovDir[x][y] = back_dir;
3621 xx = x + move_xy[MovDir[x][y]].x;
3622 yy = y + move_xy[MovDir[x][y]].y;
3624 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3625 MovDir[x][y] = old_move_dir;
3629 else if (element == EL_DRAGON)
3631 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3632 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3633 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3635 int rnd = RND(rnd_value);
3638 if (FrameCounter < 1 && x == 0 && y == 29)
3639 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3642 if (can_move_on && rnd > rnd_value / 8)
3643 MovDir[x][y] = old_move_dir;
3644 else if (can_turn_left && can_turn_right)
3645 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3646 else if (can_turn_left && rnd > rnd_value / 8)
3647 MovDir[x][y] = left_dir;
3648 else if (can_turn_right && rnd > rnd_value / 8)
3649 MovDir[x][y] = right_dir;
3651 MovDir[x][y] = back_dir;
3653 xx = x + move_xy[MovDir[x][y]].x;
3654 yy = y + move_xy[MovDir[x][y]].y;
3657 if (FrameCounter < 1 && x == 0 && y == 29)
3658 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3659 xx, yy, Feld[xx][yy],
3664 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3665 MovDir[x][y] = old_move_dir;
3667 if (!IS_FREE(xx, yy))
3668 MovDir[x][y] = old_move_dir;
3672 if (FrameCounter < 1 && x == 0 && y == 29)
3673 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3678 else if (element == EL_MOLE)
3680 boolean can_move_on =
3681 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3682 IS_AMOEBOID(Feld[move_x][move_y]) ||
3683 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3686 boolean can_turn_left =
3687 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3688 IS_AMOEBOID(Feld[left_x][left_y])));
3690 boolean can_turn_right =
3691 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3692 IS_AMOEBOID(Feld[right_x][right_y])));
3694 if (can_turn_left && can_turn_right)
3695 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3696 else if (can_turn_left)
3697 MovDir[x][y] = left_dir;
3699 MovDir[x][y] = right_dir;
3702 if (MovDir[x][y] != old_move_dir)
3705 else if (element == EL_BALLOON)
3707 MovDir[x][y] = game.balloon_dir;
3710 else if (element == EL_SPRING)
3712 if (MovDir[x][y] & MV_HORIZONTAL &&
3713 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3714 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3715 MovDir[x][y] = MV_NO_MOVING;
3719 else if (element == EL_ROBOT ||
3720 element == EL_SATELLITE ||
3721 element == EL_PENGUIN)
3723 int attr_x = -1, attr_y = -1;
3734 for (i = 0; i < MAX_PLAYERS; i++)
3736 struct PlayerInfo *player = &stored_player[i];
3737 int jx = player->jx, jy = player->jy;
3739 if (!player->active)
3743 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3751 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3757 if (element == EL_PENGUIN)
3760 static int xy[4][2] =
3768 for (i = 0; i < 4; i++)
3770 int ex = x + xy[i % 4][0];
3771 int ey = y + xy[i % 4][1];
3773 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3782 MovDir[x][y] = MV_NO_MOVING;
3784 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3785 else if (attr_x > x)
3786 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3788 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3789 else if (attr_y > y)
3790 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3792 if (element == EL_ROBOT)
3796 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3797 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3798 Moving2Blocked(x, y, &newx, &newy);
3800 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3801 MovDelay[x][y] = 8 + 8 * !RND(3);
3803 MovDelay[x][y] = 16;
3805 else if (element == EL_PENGUIN)
3811 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3813 boolean first_horiz = RND(2);
3814 int new_move_dir = MovDir[x][y];
3817 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3818 Moving2Blocked(x, y, &newx, &newy);
3820 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3824 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3825 Moving2Blocked(x, y, &newx, &newy);
3827 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3830 MovDir[x][y] = old_move_dir;
3834 else /* (element == EL_SATELLITE) */
3840 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3842 boolean first_horiz = RND(2);
3843 int new_move_dir = MovDir[x][y];
3846 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3847 Moving2Blocked(x, y, &newx, &newy);
3849 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3853 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3854 Moving2Blocked(x, y, &newx, &newy);
3856 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3859 MovDir[x][y] = old_move_dir;
3864 else if (move_pattern == MV_TURNING_LEFT ||
3865 move_pattern == MV_TURNING_RIGHT ||
3866 move_pattern == MV_TURNING_LEFT_RIGHT ||
3867 move_pattern == MV_TURNING_RIGHT_LEFT ||
3868 move_pattern == MV_TURNING_RANDOM ||
3869 move_pattern == MV_ALL_DIRECTIONS)
3871 boolean can_turn_left =
3872 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3873 boolean can_turn_right =
3874 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3876 if (move_pattern == MV_TURNING_LEFT)
3877 MovDir[x][y] = left_dir;
3878 else if (move_pattern == MV_TURNING_RIGHT)
3879 MovDir[x][y] = right_dir;
3880 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3881 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3882 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3883 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3884 else if (move_pattern == MV_TURNING_RANDOM)
3885 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3886 can_turn_right && !can_turn_left ? right_dir :
3887 RND(2) ? left_dir : right_dir);
3888 else if (can_turn_left && can_turn_right)
3889 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3890 else if (can_turn_left)
3891 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3892 else if (can_turn_right)
3893 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3895 MovDir[x][y] = back_dir;
3897 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3899 else if (move_pattern == MV_HORIZONTAL ||
3900 move_pattern == MV_VERTICAL)
3902 if (move_pattern & old_move_dir)
3903 MovDir[x][y] = back_dir;
3904 else if (move_pattern == MV_HORIZONTAL)
3905 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3906 else if (move_pattern == MV_VERTICAL)
3907 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3909 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3911 else if (move_pattern & MV_ANY_DIRECTION)
3913 MovDir[x][y] = move_pattern;
3914 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3916 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3918 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3919 MovDir[x][y] = left_dir;
3920 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3921 MovDir[x][y] = right_dir;
3923 if (MovDir[x][y] != old_move_dir)
3924 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3926 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3928 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3929 MovDir[x][y] = right_dir;
3930 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3931 MovDir[x][y] = left_dir;
3933 if (MovDir[x][y] != old_move_dir)
3934 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3936 else if (move_pattern == MV_TOWARDS_PLAYER ||
3937 move_pattern == MV_AWAY_FROM_PLAYER)
3939 int attr_x = -1, attr_y = -1;
3941 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3952 for (i = 0; i < MAX_PLAYERS; i++)
3954 struct PlayerInfo *player = &stored_player[i];
3955 int jx = player->jx, jy = player->jy;
3957 if (!player->active)
3961 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3969 MovDir[x][y] = MV_NO_MOVING;
3971 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3972 else if (attr_x > x)
3973 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3975 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3976 else if (attr_y > y)
3977 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3979 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3981 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3983 boolean first_horiz = RND(2);
3984 int new_move_dir = MovDir[x][y];
3987 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3988 Moving2Blocked(x, y, &newx, &newy);
3990 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3994 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3995 Moving2Blocked(x, y, &newx, &newy);
3997 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4000 MovDir[x][y] = old_move_dir;
4003 else if (move_pattern == MV_WHEN_PUSHED ||
4004 move_pattern == MV_WHEN_DROPPED)
4006 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4007 MovDir[x][y] = MV_NO_MOVING;
4011 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4013 static int test_xy[7][2] =
4023 static int test_dir[7] =
4033 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4034 int move_preference = -1000000; /* start with very low preference */
4035 int new_move_dir = MV_NO_MOVING;
4036 int start_test = RND(4);
4039 for (i = 0; i < 4; i++)
4041 int move_dir = test_dir[start_test + i];
4042 int move_dir_preference;
4044 xx = x + test_xy[start_test + i][0];
4045 yy = y + test_xy[start_test + i][1];
4047 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4048 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4050 new_move_dir = move_dir;
4055 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4058 move_dir_preference = -1 * RunnerVisit[xx][yy];
4059 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4060 move_dir_preference = PlayerVisit[xx][yy];
4062 if (move_dir_preference > move_preference)
4064 /* prefer field that has not been visited for the longest time */
4065 move_preference = move_dir_preference;
4066 new_move_dir = move_dir;
4068 else if (move_dir_preference == move_preference &&
4069 move_dir == old_move_dir)
4071 /* prefer last direction when all directions are preferred equally */
4072 move_preference = move_dir_preference;
4073 new_move_dir = move_dir;
4077 MovDir[x][y] = new_move_dir;
4078 if (old_move_dir != new_move_dir)
4083 static void TurnRound(int x, int y)
4085 int direction = MovDir[x][y];
4088 GfxDir[x][y] = MovDir[x][y];
4094 GfxDir[x][y] = MovDir[x][y];
4097 if (direction != MovDir[x][y])
4102 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4105 GfxAction[x][y] = ACTION_WAITING;
4109 static boolean JustBeingPushed(int x, int y)
4113 for (i = 0; i < MAX_PLAYERS; i++)
4115 struct PlayerInfo *player = &stored_player[i];
4117 if (player->active && player->is_pushing && player->MovPos)
4119 int next_jx = player->jx + (player->jx - player->last_jx);
4120 int next_jy = player->jy + (player->jy - player->last_jy);
4122 if (x == next_jx && y == next_jy)
4130 void StartMoving(int x, int y)
4132 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4133 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4134 int element = Feld[x][y];
4140 if (MovDelay[x][y] == 0)
4141 GfxAction[x][y] = ACTION_DEFAULT;
4143 /* !!! this should be handled more generic (not only for mole) !!! */
4144 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4145 GfxAction[x][y] = ACTION_DEFAULT;
4148 if (CAN_FALL(element) && y < lev_fieldy - 1)
4150 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4151 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4152 if (JustBeingPushed(x, y))
4155 if (element == EL_QUICKSAND_FULL)
4157 if (IS_FREE(x, y + 1))
4159 InitMovingField(x, y, MV_DOWN);
4160 started_moving = TRUE;
4162 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4163 Store[x][y] = EL_ROCK;
4165 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4167 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4170 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4172 if (!MovDelay[x][y])
4173 MovDelay[x][y] = TILEY + 1;
4182 Feld[x][y] = EL_QUICKSAND_EMPTY;
4183 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4184 Store[x][y + 1] = Store[x][y];
4187 PlayLevelSoundAction(x, y, ACTION_FILLING);
4189 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4193 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4194 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4196 InitMovingField(x, y, MV_DOWN);
4197 started_moving = TRUE;
4199 Feld[x][y] = EL_QUICKSAND_FILLING;
4200 Store[x][y] = element;
4202 PlayLevelSoundAction(x, y, ACTION_FILLING);
4204 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4207 else if (element == EL_MAGIC_WALL_FULL)
4209 if (IS_FREE(x, y + 1))
4211 InitMovingField(x, y, MV_DOWN);
4212 started_moving = TRUE;
4214 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4215 Store[x][y] = EL_CHANGED(Store[x][y]);
4217 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4219 if (!MovDelay[x][y])
4220 MovDelay[x][y] = TILEY/4 + 1;
4229 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4230 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4231 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4235 else if (element == EL_BD_MAGIC_WALL_FULL)
4237 if (IS_FREE(x, y + 1))
4239 InitMovingField(x, y, MV_DOWN);
4240 started_moving = TRUE;
4242 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4243 Store[x][y] = EL_CHANGED2(Store[x][y]);
4245 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4247 if (!MovDelay[x][y])
4248 MovDelay[x][y] = TILEY/4 + 1;
4257 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4258 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4259 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4263 else if (CAN_PASS_MAGIC_WALL(element) &&
4264 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4265 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4267 InitMovingField(x, y, MV_DOWN);
4268 started_moving = TRUE;
4271 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4272 EL_BD_MAGIC_WALL_FILLING);
4273 Store[x][y] = element;
4276 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4278 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4283 InitMovingField(x, y, MV_DOWN);
4284 started_moving = TRUE;
4286 Store[x][y] = EL_ACID;
4288 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4289 GfxAction[x][y + 1] = ACTION_ACTIVE;
4293 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4294 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4295 (Feld[x][y + 1] == EL_BLOCKED)) ||
4296 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4297 CAN_SMASH(element) && WasJustFalling[x][y] &&
4298 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4302 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4303 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4304 WasJustMoving[x][y] && !Pushed[x][y + 1])
4306 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4307 WasJustMoving[x][y])
4312 /* this is needed for a special case not covered by calling "Impact()"
4313 from "ContinueMoving()": if an element moves to a tile directly below
4314 another element which was just falling on that tile (which was empty
4315 in the previous frame), the falling element above would just stop
4316 instead of smashing the element below (in previous version, the above
4317 element was just checked for "moving" instead of "falling", resulting
4318 in incorrect smashes caused by horizontal movement of the above
4319 element; also, the case of the player being the element to smash was
4320 simply not covered here... :-/ ) */
4323 WasJustMoving[x][y] = 0;
4324 WasJustFalling[x][y] = 0;
4329 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4331 if (MovDir[x][y] == MV_NO_MOVING)
4333 InitMovingField(x, y, MV_DOWN);
4334 started_moving = TRUE;
4337 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4339 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4340 MovDir[x][y] = MV_DOWN;
4342 InitMovingField(x, y, MV_DOWN);
4343 started_moving = TRUE;
4345 else if (element == EL_AMOEBA_DROP)
4347 Feld[x][y] = EL_AMOEBA_GROWING;
4348 Store[x][y] = EL_AMOEBA_WET;
4350 /* Store[x][y + 1] must be zero, because:
4351 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4354 #if OLD_GAME_BEHAVIOUR
4355 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4357 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4358 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4359 element != EL_DX_SUPABOMB)
4362 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4363 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4364 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4365 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4368 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4369 (IS_FREE(x - 1, y + 1) ||
4370 Feld[x - 1][y + 1] == EL_ACID));
4371 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4372 (IS_FREE(x + 1, y + 1) ||
4373 Feld[x + 1][y + 1] == EL_ACID));
4374 boolean can_fall_any = (can_fall_left || can_fall_right);
4375 boolean can_fall_both = (can_fall_left && can_fall_right);
4377 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4379 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4381 if (slippery_type == SLIPPERY_ONLY_LEFT)
4382 can_fall_right = FALSE;
4383 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4384 can_fall_left = FALSE;
4385 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4386 can_fall_right = FALSE;
4387 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4388 can_fall_left = FALSE;
4390 can_fall_any = (can_fall_left || can_fall_right);
4391 can_fall_both = (can_fall_left && can_fall_right);
4396 if (can_fall_both &&
4397 (game.emulation != EMU_BOULDERDASH &&
4398 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4399 can_fall_left = !(can_fall_right = RND(2));
4401 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4402 started_moving = TRUE;
4405 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4407 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4408 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4409 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4410 int belt_dir = game.belt_dir[belt_nr];
4412 if ((belt_dir == MV_LEFT && left_is_free) ||
4413 (belt_dir == MV_RIGHT && right_is_free))
4415 InitMovingField(x, y, belt_dir);
4416 started_moving = TRUE;
4418 GfxAction[x][y] = ACTION_DEFAULT;
4423 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4424 if (CAN_MOVE(element) && !started_moving)
4426 int move_pattern = element_info[element].move_pattern;
4429 Moving2Blocked(x, y, &newx, &newy);
4432 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4435 if ((element == EL_SATELLITE ||
4436 element == EL_BALLOON ||
4437 element == EL_SPRING)
4438 && JustBeingPushed(x, y))
4443 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4444 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4445 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4448 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4449 element, element_info[element].token_name,
4450 WasJustMoving[x][y],
4451 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4452 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4453 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4454 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4458 WasJustMoving[x][y] = 0;
4461 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4464 if (Feld[x][y] != element) /* element has changed */
4466 element = Feld[x][y];
4467 move_pattern = element_info[element].move_pattern;
4469 if (!CAN_MOVE(element))
4473 if (Feld[x][y] != element) /* element has changed */
4481 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4482 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4484 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4486 Moving2Blocked(x, y, &newx, &newy);
4487 if (Feld[newx][newy] == EL_BLOCKED)
4488 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4494 if (FrameCounter < 1 && x == 0 && y == 29)
4495 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4498 if (!MovDelay[x][y]) /* start new movement phase */
4500 /* all objects that can change their move direction after each step
4501 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4503 if (element != EL_YAMYAM &&
4504 element != EL_DARK_YAMYAM &&
4505 element != EL_PACMAN &&
4506 !(move_pattern & MV_ANY_DIRECTION) &&
4507 move_pattern != MV_TURNING_LEFT &&
4508 move_pattern != MV_TURNING_RIGHT &&
4509 move_pattern != MV_TURNING_LEFT_RIGHT &&
4510 move_pattern != MV_TURNING_RIGHT_LEFT &&
4511 move_pattern != MV_TURNING_RANDOM)
4516 if (FrameCounter < 1 && x == 0 && y == 29)
4517 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4520 if (MovDelay[x][y] && (element == EL_BUG ||
4521 element == EL_SPACESHIP ||
4522 element == EL_SP_SNIKSNAK ||
4523 element == EL_SP_ELECTRON ||
4524 element == EL_MOLE))
4525 DrawLevelField(x, y);
4529 if (MovDelay[x][y]) /* wait some time before next movement */
4534 if (element == EL_YAMYAM)
4537 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4538 DrawLevelElementAnimation(x, y, element);
4542 if (MovDelay[x][y]) /* element still has to wait some time */
4545 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4546 ResetGfxAnimation(x, y);
4550 if (GfxAction[x][y] != ACTION_WAITING)
4551 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4553 GfxAction[x][y] = ACTION_WAITING;
4557 if (element == EL_ROBOT ||
4559 element == EL_PACMAN ||
4561 element == EL_YAMYAM ||
4562 element == EL_DARK_YAMYAM)
4565 DrawLevelElementAnimation(x, y, element);
4567 DrawLevelElementAnimationIfNeeded(x, y, element);
4569 PlayLevelSoundAction(x, y, ACTION_WAITING);
4571 else if (element == EL_SP_ELECTRON)
4572 DrawLevelElementAnimationIfNeeded(x, y, element);
4573 else if (element == EL_DRAGON)
4576 int dir = MovDir[x][y];
4577 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4578 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4579 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4580 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4581 dir == MV_UP ? IMG_FLAMES_1_UP :
4582 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4583 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4586 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4589 GfxAction[x][y] = ACTION_ATTACKING;
4591 if (IS_PLAYER(x, y))
4592 DrawPlayerField(x, y);
4594 DrawLevelField(x, y);
4596 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4598 for (i = 1; i <= 3; i++)
4600 int xx = x + i * dx;
4601 int yy = y + i * dy;
4602 int sx = SCREENX(xx);
4603 int sy = SCREENY(yy);
4604 int flame_graphic = graphic + (i - 1);
4606 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4611 int flamed = MovingOrBlocked2Element(xx, yy);
4613 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4616 RemoveMovingField(xx, yy);
4618 Feld[xx][yy] = EL_FLAMES;
4619 if (IN_SCR_FIELD(sx, sy))
4621 DrawLevelFieldCrumbledSand(xx, yy);
4622 DrawGraphic(sx, sy, flame_graphic, frame);
4627 if (Feld[xx][yy] == EL_FLAMES)
4628 Feld[xx][yy] = EL_EMPTY;
4629 DrawLevelField(xx, yy);
4634 if (MovDelay[x][y]) /* element still has to wait some time */
4636 PlayLevelSoundAction(x, y, ACTION_WAITING);
4642 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4643 for all other elements GfxAction will be set by InitMovingField() */
4644 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4645 GfxAction[x][y] = ACTION_MOVING;
4649 /* now make next step */
4651 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4653 if (DONT_COLLIDE_WITH(element) &&
4654 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4655 !PLAYER_PROTECTED(newx, newy))
4658 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4662 /* player killed by element which is deadly when colliding with */
4664 KillHero(PLAYERINFO(newx, newy));
4669 else if ((element == EL_PENGUIN ||
4670 element == EL_ROBOT ||
4671 element == EL_SATELLITE ||
4672 element == EL_BALLOON ||
4673 IS_CUSTOM_ELEMENT(element)) &&
4674 IN_LEV_FIELD(newx, newy) &&
4675 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4678 Store[x][y] = EL_ACID;
4680 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4682 if (Feld[newx][newy] == EL_EXIT_OPEN)
4686 DrawLevelField(x, y);
4688 Feld[x][y] = EL_EMPTY;
4689 DrawLevelField(x, y);
4692 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4693 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4694 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4696 local_player->friends_still_needed--;
4697 if (!local_player->friends_still_needed &&
4698 !local_player->GameOver && AllPlayersGone)
4699 local_player->LevelSolved = local_player->GameOver = TRUE;
4703 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4705 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4706 DrawLevelField(newx, newy);
4708 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4710 else if (!IS_FREE(newx, newy))
4712 GfxAction[x][y] = ACTION_WAITING;
4714 if (IS_PLAYER(x, y))
4715 DrawPlayerField(x, y);
4717 DrawLevelField(x, y);
4722 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4724 if (IS_FOOD_PIG(Feld[newx][newy]))
4726 if (IS_MOVING(newx, newy))
4727 RemoveMovingField(newx, newy);
4730 Feld[newx][newy] = EL_EMPTY;
4731 DrawLevelField(newx, newy);
4734 PlayLevelSound(x, y, SND_PIG_DIGGING);
4736 else if (!IS_FREE(newx, newy))
4738 if (IS_PLAYER(x, y))
4739 DrawPlayerField(x, y);
4741 DrawLevelField(x, y);
4750 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4753 else if (IS_CUSTOM_ELEMENT(element) &&
4754 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4758 !IS_FREE(newx, newy)
4763 int new_element = Feld[newx][newy];
4766 printf("::: '%s' digs '%s' [%d]\n",
4767 element_info[element].token_name,
4768 element_info[Feld[newx][newy]].token_name,
4769 StorePlayer[newx][newy]);
4772 if (!IS_FREE(newx, newy))
4774 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4775 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4778 /* no element can dig solid indestructible elements */
4779 if (IS_INDESTRUCTIBLE(new_element) &&
4780 !IS_DIGGABLE(new_element) &&
4781 !IS_COLLECTIBLE(new_element))
4784 if (AmoebaNr[newx][newy] &&
4785 (new_element == EL_AMOEBA_FULL ||
4786 new_element == EL_BD_AMOEBA ||
4787 new_element == EL_AMOEBA_GROWING))
4789 AmoebaCnt[AmoebaNr[newx][newy]]--;
4790 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4793 if (IS_MOVING(newx, newy))
4794 RemoveMovingField(newx, newy);
4797 RemoveField(newx, newy);
4798 DrawLevelField(newx, newy);
4801 PlayLevelSoundAction(x, y, action);
4804 if (new_element == element_info[element].move_enter_element)
4805 element_info[element].can_leave_element = TRUE;
4807 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4809 RunnerVisit[x][y] = FrameCounter;
4810 PlayerVisit[x][y] /= 8; /* expire player visit path */
4816 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4818 if (!IS_FREE(newx, newy))
4820 if (IS_PLAYER(x, y))
4821 DrawPlayerField(x, y);
4823 DrawLevelField(x, y);
4829 boolean wanna_flame = !RND(10);
4830 int dx = newx - x, dy = newy - y;
4831 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4832 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4833 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4834 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4835 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4836 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4839 IS_CLASSIC_ENEMY(element1) ||
4840 IS_CLASSIC_ENEMY(element2)) &&
4841 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4842 element1 != EL_FLAMES && element2 != EL_FLAMES)
4845 ResetGfxAnimation(x, y);
4846 GfxAction[x][y] = ACTION_ATTACKING;
4849 if (IS_PLAYER(x, y))
4850 DrawPlayerField(x, y);
4852 DrawLevelField(x, y);
4854 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4856 MovDelay[x][y] = 50;
4858 Feld[newx][newy] = EL_FLAMES;
4859 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4860 Feld[newx1][newy1] = EL_FLAMES;
4861 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4862 Feld[newx2][newy2] = EL_FLAMES;
4868 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4869 Feld[newx][newy] == EL_DIAMOND)
4871 if (IS_MOVING(newx, newy))
4872 RemoveMovingField(newx, newy);
4875 Feld[newx][newy] = EL_EMPTY;
4876 DrawLevelField(newx, newy);
4879 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4881 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4882 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4884 if (AmoebaNr[newx][newy])
4886 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4887 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4888 Feld[newx][newy] == EL_BD_AMOEBA)
4889 AmoebaCnt[AmoebaNr[newx][newy]]--;
4892 if (IS_MOVING(newx, newy))
4893 RemoveMovingField(newx, newy);
4896 Feld[newx][newy] = EL_EMPTY;
4897 DrawLevelField(newx, newy);
4900 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4902 else if ((element == EL_PACMAN || element == EL_MOLE)
4903 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4905 if (AmoebaNr[newx][newy])
4907 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4908 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4909 Feld[newx][newy] == EL_BD_AMOEBA)
4910 AmoebaCnt[AmoebaNr[newx][newy]]--;
4913 if (element == EL_MOLE)
4915 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4916 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4918 ResetGfxAnimation(x, y);
4919 GfxAction[x][y] = ACTION_DIGGING;
4920 DrawLevelField(x, y);
4922 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4924 return; /* wait for shrinking amoeba */
4926 else /* element == EL_PACMAN */
4928 Feld[newx][newy] = EL_EMPTY;
4929 DrawLevelField(newx, newy);
4930 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4933 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4934 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4935 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4937 /* wait for shrinking amoeba to completely disappear */
4940 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4942 /* object was running against a wall */
4947 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4948 DrawLevelElementAnimation(x, y, element);
4950 if (element == EL_BUG ||
4951 element == EL_SPACESHIP ||
4952 element == EL_SP_SNIKSNAK)
4953 DrawLevelField(x, y);
4954 else if (element == EL_MOLE)
4955 DrawLevelField(x, y);
4956 else if (element == EL_BD_BUTTERFLY ||
4957 element == EL_BD_FIREFLY)
4958 DrawLevelElementAnimationIfNeeded(x, y, element);
4959 else if (element == EL_SATELLITE)
4960 DrawLevelElementAnimationIfNeeded(x, y, element);
4961 else if (element == EL_SP_ELECTRON)
4962 DrawLevelElementAnimationIfNeeded(x, y, element);
4965 if (DONT_TOUCH(element))
4966 TestIfBadThingTouchesHero(x, y);
4969 PlayLevelSoundAction(x, y, ACTION_WAITING);
4975 InitMovingField(x, y, MovDir[x][y]);
4977 PlayLevelSoundAction(x, y, ACTION_MOVING);
4981 ContinueMoving(x, y);
4984 void ContinueMoving(int x, int y)
4986 int element = Feld[x][y];
4987 struct ElementInfo *ei = &element_info[element];
4988 int direction = MovDir[x][y];
4989 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4990 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4991 int newx = x + dx, newy = y + dy;
4993 int nextx = newx + dx, nexty = newy + dy;
4995 boolean pushed = Pushed[x][y];
4997 MovPos[x][y] += getElementMoveStepsize(x, y);
4999 if (pushed) /* special case: moving object pushed by player */
5000 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5002 if (ABS(MovPos[x][y]) < TILEX)
5004 DrawLevelField(x, y);
5006 return; /* element is still moving */
5009 /* element reached destination field */
5011 Feld[x][y] = EL_EMPTY;
5012 Feld[newx][newy] = element;
5013 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5015 if (element == EL_MOLE)
5017 Feld[x][y] = EL_SAND;
5019 DrawLevelFieldCrumbledSandNeighbours(x, y);
5021 else if (element == EL_QUICKSAND_FILLING)
5023 element = Feld[newx][newy] = get_next_element(element);
5024 Store[newx][newy] = Store[x][y];
5026 else if (element == EL_QUICKSAND_EMPTYING)
5028 Feld[x][y] = get_next_element(element);
5029 element = Feld[newx][newy] = Store[x][y];
5031 else if (element == EL_MAGIC_WALL_FILLING)
5033 element = Feld[newx][newy] = get_next_element(element);
5034 if (!game.magic_wall_active)
5035 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5036 Store[newx][newy] = Store[x][y];
5038 else if (element == EL_MAGIC_WALL_EMPTYING)
5040 Feld[x][y] = get_next_element(element);
5041 if (!game.magic_wall_active)
5042 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5043 element = Feld[newx][newy] = Store[x][y];
5045 else if (element == EL_BD_MAGIC_WALL_FILLING)
5047 element = Feld[newx][newy] = get_next_element(element);
5048 if (!game.magic_wall_active)
5049 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5050 Store[newx][newy] = Store[x][y];
5052 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5054 Feld[x][y] = get_next_element(element);
5055 if (!game.magic_wall_active)
5056 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5057 element = Feld[newx][newy] = Store[x][y];
5059 else if (element == EL_AMOEBA_DROPPING)
5061 Feld[x][y] = get_next_element(element);
5062 element = Feld[newx][newy] = Store[x][y];
5064 else if (element == EL_SOKOBAN_OBJECT)
5067 Feld[x][y] = Back[x][y];
5069 if (Back[newx][newy])
5070 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5072 Back[x][y] = Back[newx][newy] = 0;
5074 else if (Store[x][y] == EL_ACID)
5076 element = Feld[newx][newy] = EL_ACID;
5080 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5081 MovDelay[newx][newy] = 0;
5083 /* copy element change control values to new field */
5084 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5085 ChangePage[newx][newy] = ChangePage[x][y];
5086 Changed[newx][newy] = Changed[x][y];
5087 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5089 ChangeDelay[x][y] = 0;
5090 ChangePage[x][y] = -1;
5091 Changed[x][y] = CE_BITMASK_DEFAULT;
5092 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5094 /* copy animation control values to new field */
5095 GfxFrame[newx][newy] = GfxFrame[x][y];
5096 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5097 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5098 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5100 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5102 ResetGfxAnimation(x, y); /* reset animation values for old field */
5105 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5106 ei->move_leave_element != EL_EMPTY &&
5107 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5108 ei->can_leave_element_last))
5110 Feld[x][y] = ei->move_leave_element;
5111 InitField(x, y, FALSE);
5113 if (GFX_CRUMBLED(Feld[x][y]))
5114 DrawLevelFieldCrumbledSandNeighbours(x, y);
5117 ei->can_leave_element_last = ei->can_leave_element;
5118 ei->can_leave_element = FALSE;
5122 /* 2.1.1 (does not work correctly for spring) */
5123 if (!CAN_MOVE(element))
5124 MovDir[newx][newy] = 0;
5128 /* (does not work for falling objects that slide horizontally) */
5129 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5130 MovDir[newx][newy] = 0;
5133 if (!CAN_MOVE(element) ||
5134 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5135 MovDir[newx][newy] = 0;
5138 if (!CAN_MOVE(element) ||
5139 (CAN_FALL(element) && direction == MV_DOWN))
5140 GfxDir[x][y] = MovDir[newx][newy] = 0;
5145 DrawLevelField(x, y);
5146 DrawLevelField(newx, newy);
5148 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5150 /* prevent pushed element from moving on in pushed direction */
5151 if (pushed && CAN_MOVE(element) &&
5152 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5153 !(element_info[element].move_pattern & direction))
5154 TurnRound(newx, newy);
5156 if (!pushed) /* special case: moving object pushed by player */
5158 WasJustMoving[newx][newy] = 3;
5160 if (CAN_FALL(element) && direction == MV_DOWN)
5161 WasJustFalling[newx][newy] = 3;
5164 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5166 TestIfBadThingTouchesHero(newx, newy);
5167 TestIfBadThingTouchesFriend(newx, newy);
5169 if (!IS_CUSTOM_ELEMENT(element))
5170 TestIfBadThingTouchesOtherBadThing(newx, newy);
5172 else if (element == EL_PENGUIN)
5173 TestIfFriendTouchesBadThing(newx, newy);
5175 if (CAN_FALL(element) && direction == MV_DOWN &&
5176 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5180 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5184 if (ChangePage[newx][newy] != -1) /* delayed change */
5185 ChangeElement(newx, newy, ChangePage[newx][newy]);
5190 TestIfElementHitsCustomElement(newx, newy, direction);
5194 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5196 int hitting_element = Feld[newx][newy];
5198 /* !!! fix side (direction) orientation here and elsewhere !!! */
5199 CheckElementSideChange(newx, newy, hitting_element,
5200 direction, CE_HITTING_SOMETHING, -1);
5203 if (IN_LEV_FIELD(nextx, nexty))
5205 static int opposite_directions[] =
5212 int move_dir_bit = MV_DIR_BIT(direction);
5213 int opposite_direction = opposite_directions[move_dir_bit];
5214 int hitting_side = direction;
5215 int touched_side = opposite_direction;
5216 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5217 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5218 MovDir[nextx][nexty] != direction ||
5219 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5225 CheckElementSideChange(nextx, nexty, touched_element,
5226 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5228 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5229 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5231 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5233 struct ElementChangeInfo *change =
5234 &element_info[hitting_element].change_page[i];
5236 if (change->can_change &&
5237 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5238 change->sides & touched_side &&
5239 change->trigger_element == touched_element)
5241 CheckElementSideChange(newx, newy, hitting_element,
5242 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5248 if (IS_CUSTOM_ELEMENT(touched_element) &&
5249 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5251 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5253 struct ElementChangeInfo *change =
5254 &element_info[touched_element].change_page[i];
5256 if (change->can_change &&
5257 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5258 change->sides & hitting_side &&
5259 change->trigger_element == hitting_element)
5261 CheckElementSideChange(nextx, nexty, touched_element,
5262 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5273 TestIfPlayerTouchesCustomElement(newx, newy);
5274 TestIfElementTouchesCustomElement(newx, newy);
5277 int AmoebeNachbarNr(int ax, int ay)
5280 int element = Feld[ax][ay];
5282 static int xy[4][2] =
5290 for (i = 0; i < 4; i++)
5292 int x = ax + xy[i][0];
5293 int y = ay + xy[i][1];
5295 if (!IN_LEV_FIELD(x, y))
5298 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5299 group_nr = AmoebaNr[x][y];
5305 void AmoebenVereinigen(int ax, int ay)
5307 int i, x, y, xx, yy;
5308 int new_group_nr = AmoebaNr[ax][ay];
5309 static int xy[4][2] =
5317 if (new_group_nr == 0)
5320 for (i = 0; i < 4; i++)
5325 if (!IN_LEV_FIELD(x, y))
5328 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5329 Feld[x][y] == EL_BD_AMOEBA ||
5330 Feld[x][y] == EL_AMOEBA_DEAD) &&
5331 AmoebaNr[x][y] != new_group_nr)
5333 int old_group_nr = AmoebaNr[x][y];
5335 if (old_group_nr == 0)
5338 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5339 AmoebaCnt[old_group_nr] = 0;
5340 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5341 AmoebaCnt2[old_group_nr] = 0;
5343 for (yy = 0; yy < lev_fieldy; yy++)
5345 for (xx = 0; xx < lev_fieldx; xx++)
5347 if (AmoebaNr[xx][yy] == old_group_nr)
5348 AmoebaNr[xx][yy] = new_group_nr;
5355 void AmoebeUmwandeln(int ax, int ay)
5359 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5361 int group_nr = AmoebaNr[ax][ay];
5366 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5367 printf("AmoebeUmwandeln(): This should never happen!\n");
5372 for (y = 0; y < lev_fieldy; y++)
5374 for (x = 0; x < lev_fieldx; x++)
5376 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5379 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5383 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5384 SND_AMOEBA_TURNING_TO_GEM :
5385 SND_AMOEBA_TURNING_TO_ROCK));
5390 static int xy[4][2] =
5398 for (i = 0; i < 4; i++)
5403 if (!IN_LEV_FIELD(x, y))
5406 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5408 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5409 SND_AMOEBA_TURNING_TO_GEM :
5410 SND_AMOEBA_TURNING_TO_ROCK));
5417 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5420 int group_nr = AmoebaNr[ax][ay];
5421 boolean done = FALSE;
5426 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5427 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5432 for (y = 0; y < lev_fieldy; y++)
5434 for (x = 0; x < lev_fieldx; x++)
5436 if (AmoebaNr[x][y] == group_nr &&
5437 (Feld[x][y] == EL_AMOEBA_DEAD ||
5438 Feld[x][y] == EL_BD_AMOEBA ||
5439 Feld[x][y] == EL_AMOEBA_GROWING))
5442 Feld[x][y] = new_element;
5443 InitField(x, y, FALSE);
5444 DrawLevelField(x, y);
5451 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5452 SND_BD_AMOEBA_TURNING_TO_ROCK :
5453 SND_BD_AMOEBA_TURNING_TO_GEM));
5456 void AmoebeWaechst(int x, int y)
5458 static unsigned long sound_delay = 0;
5459 static unsigned long sound_delay_value = 0;
5461 if (!MovDelay[x][y]) /* start new growing cycle */
5465 if (DelayReached(&sound_delay, sound_delay_value))
5468 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5470 if (Store[x][y] == EL_BD_AMOEBA)
5471 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5473 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5475 sound_delay_value = 30;
5479 if (MovDelay[x][y]) /* wait some time before growing bigger */
5482 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5484 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5485 6 - MovDelay[x][y]);
5487 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5490 if (!MovDelay[x][y])
5492 Feld[x][y] = Store[x][y];
5494 DrawLevelField(x, y);
5499 void AmoebaDisappearing(int x, int y)
5501 static unsigned long sound_delay = 0;
5502 static unsigned long sound_delay_value = 0;
5504 if (!MovDelay[x][y]) /* start new shrinking cycle */
5508 if (DelayReached(&sound_delay, sound_delay_value))
5509 sound_delay_value = 30;
5512 if (MovDelay[x][y]) /* wait some time before shrinking */
5515 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5517 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5518 6 - MovDelay[x][y]);
5520 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5523 if (!MovDelay[x][y])
5525 Feld[x][y] = EL_EMPTY;
5526 DrawLevelField(x, y);
5528 /* don't let mole enter this field in this cycle;
5529 (give priority to objects falling to this field from above) */
5535 void AmoebeAbleger(int ax, int ay)
5538 int element = Feld[ax][ay];
5539 int graphic = el2img(element);
5540 int newax = ax, neway = ay;
5541 static int xy[4][2] =
5549 if (!level.amoeba_speed)
5551 Feld[ax][ay] = EL_AMOEBA_DEAD;
5552 DrawLevelField(ax, ay);
5556 if (IS_ANIMATED(graphic))
5557 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5559 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5560 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5562 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5565 if (MovDelay[ax][ay])
5569 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5572 int x = ax + xy[start][0];
5573 int y = ay + xy[start][1];
5575 if (!IN_LEV_FIELD(x, y))
5578 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5579 if (IS_FREE(x, y) ||
5580 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5586 if (newax == ax && neway == ay)
5589 else /* normal or "filled" (BD style) amoeba */
5592 boolean waiting_for_player = FALSE;
5594 for (i = 0; i < 4; i++)
5596 int j = (start + i) % 4;
5597 int x = ax + xy[j][0];
5598 int y = ay + xy[j][1];
5600 if (!IN_LEV_FIELD(x, y))
5603 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5604 if (IS_FREE(x, y) ||
5605 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5611 else if (IS_PLAYER(x, y))
5612 waiting_for_player = TRUE;
5615 if (newax == ax && neway == ay) /* amoeba cannot grow */
5617 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5619 Feld[ax][ay] = EL_AMOEBA_DEAD;
5620 DrawLevelField(ax, ay);
5621 AmoebaCnt[AmoebaNr[ax][ay]]--;
5623 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5625 if (element == EL_AMOEBA_FULL)
5626 AmoebeUmwandeln(ax, ay);
5627 else if (element == EL_BD_AMOEBA)
5628 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5633 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5635 /* amoeba gets larger by growing in some direction */
5637 int new_group_nr = AmoebaNr[ax][ay];
5640 if (new_group_nr == 0)
5642 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5643 printf("AmoebeAbleger(): This should never happen!\n");
5648 AmoebaNr[newax][neway] = new_group_nr;
5649 AmoebaCnt[new_group_nr]++;
5650 AmoebaCnt2[new_group_nr]++;
5652 /* if amoeba touches other amoeba(s) after growing, unify them */
5653 AmoebenVereinigen(newax, neway);
5655 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5657 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5663 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5664 (neway == lev_fieldy - 1 && newax != ax))
5666 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5667 Store[newax][neway] = element;
5669 else if (neway == ay)
5671 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5673 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5675 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5680 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5681 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5682 Store[ax][ay] = EL_AMOEBA_DROP;
5683 ContinueMoving(ax, ay);
5687 DrawLevelField(newax, neway);
5690 void Life(int ax, int ay)
5693 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5695 int element = Feld[ax][ay];
5696 int graphic = el2img(element);
5697 boolean changed = FALSE;
5699 if (IS_ANIMATED(graphic))
5700 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5705 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5706 MovDelay[ax][ay] = life_time;
5708 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5711 if (MovDelay[ax][ay])
5715 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5717 int xx = ax+x1, yy = ay+y1;
5720 if (!IN_LEV_FIELD(xx, yy))
5723 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5725 int x = xx+x2, y = yy+y2;
5727 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5730 if (((Feld[x][y] == element ||
5731 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5733 (IS_FREE(x, y) && Stop[x][y]))
5737 if (xx == ax && yy == ay) /* field in the middle */
5739 if (nachbarn < life[0] || nachbarn > life[1])
5741 Feld[xx][yy] = EL_EMPTY;
5743 DrawLevelField(xx, yy);
5744 Stop[xx][yy] = TRUE;
5748 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5749 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5750 { /* free border field */
5751 if (nachbarn >= life[2] && nachbarn <= life[3])
5753 Feld[xx][yy] = element;
5754 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5756 DrawLevelField(xx, yy);
5757 Stop[xx][yy] = TRUE;
5764 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5765 SND_GAME_OF_LIFE_GROWING);
5768 static void InitRobotWheel(int x, int y)
5770 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5773 static void RunRobotWheel(int x, int y)
5775 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5778 static void StopRobotWheel(int x, int y)
5780 if (ZX == x && ZY == y)
5784 static void InitTimegateWheel(int x, int y)
5786 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5789 static void RunTimegateWheel(int x, int y)
5791 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5794 void CheckExit(int x, int y)
5796 if (local_player->gems_still_needed > 0 ||
5797 local_player->sokobanfields_still_needed > 0 ||
5798 local_player->lights_still_needed > 0)
5800 int element = Feld[x][y];
5801 int graphic = el2img(element);
5803 if (IS_ANIMATED(graphic))
5804 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5809 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5812 Feld[x][y] = EL_EXIT_OPENING;
5814 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5817 void CheckExitSP(int x, int y)
5819 if (local_player->gems_still_needed > 0)
5821 int element = Feld[x][y];
5822 int graphic = el2img(element);
5824 if (IS_ANIMATED(graphic))
5825 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5830 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5833 Feld[x][y] = EL_SP_EXIT_OPENING;
5835 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5838 static void CloseAllOpenTimegates()
5842 for (y = 0; y < lev_fieldy; y++)
5844 for (x = 0; x < lev_fieldx; x++)
5846 int element = Feld[x][y];
5848 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5850 Feld[x][y] = EL_TIMEGATE_CLOSING;
5852 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5854 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5861 void EdelsteinFunkeln(int x, int y)
5863 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5866 if (Feld[x][y] == EL_BD_DIAMOND)
5869 if (MovDelay[x][y] == 0) /* next animation frame */
5870 MovDelay[x][y] = 11 * !SimpleRND(500);
5872 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5876 if (setup.direct_draw && MovDelay[x][y])
5877 SetDrawtoField(DRAW_BUFFERED);
5879 DrawLevelElementAnimation(x, y, Feld[x][y]);
5881 if (MovDelay[x][y] != 0)
5883 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5884 10 - MovDelay[x][y]);
5886 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5888 if (setup.direct_draw)
5892 dest_x = FX + SCREENX(x) * TILEX;
5893 dest_y = FY + SCREENY(y) * TILEY;
5895 BlitBitmap(drawto_field, window,
5896 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5897 SetDrawtoField(DRAW_DIRECT);
5903 void MauerWaechst(int x, int y)
5907 if (!MovDelay[x][y]) /* next animation frame */
5908 MovDelay[x][y] = 3 * delay;
5910 if (MovDelay[x][y]) /* wait some time before next frame */
5914 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5916 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5917 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5919 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5922 if (!MovDelay[x][y])
5924 if (MovDir[x][y] == MV_LEFT)
5926 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5927 DrawLevelField(x - 1, y);
5929 else if (MovDir[x][y] == MV_RIGHT)
5931 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5932 DrawLevelField(x + 1, y);
5934 else if (MovDir[x][y] == MV_UP)
5936 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5937 DrawLevelField(x, y - 1);
5941 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5942 DrawLevelField(x, y + 1);
5945 Feld[x][y] = Store[x][y];
5947 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5948 DrawLevelField(x, y);
5953 void MauerAbleger(int ax, int ay)
5955 int element = Feld[ax][ay];
5956 int graphic = el2img(element);
5957 boolean oben_frei = FALSE, unten_frei = FALSE;
5958 boolean links_frei = FALSE, rechts_frei = FALSE;
5959 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5960 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5961 boolean new_wall = FALSE;
5963 if (IS_ANIMATED(graphic))
5964 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5966 if (!MovDelay[ax][ay]) /* start building new wall */
5967 MovDelay[ax][ay] = 6;
5969 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5972 if (MovDelay[ax][ay])
5976 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5978 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5980 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5982 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5985 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5986 element == EL_EXPANDABLE_WALL_ANY)
5990 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5991 Store[ax][ay-1] = element;
5992 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5993 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5994 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5995 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6000 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6001 Store[ax][ay+1] = element;
6002 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6003 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6004 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6005 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6010 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6011 element == EL_EXPANDABLE_WALL_ANY ||
6012 element == EL_EXPANDABLE_WALL)
6016 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6017 Store[ax-1][ay] = element;
6018 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6019 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6020 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6021 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6027 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6028 Store[ax+1][ay] = element;
6029 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6030 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6031 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6032 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6037 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6038 DrawLevelField(ax, ay);
6040 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6042 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6043 unten_massiv = TRUE;
6044 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6045 links_massiv = TRUE;
6046 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6047 rechts_massiv = TRUE;
6049 if (((oben_massiv && unten_massiv) ||
6050 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6051 element == EL_EXPANDABLE_WALL) &&
6052 ((links_massiv && rechts_massiv) ||
6053 element == EL_EXPANDABLE_WALL_VERTICAL))
6054 Feld[ax][ay] = EL_WALL;
6058 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6060 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6064 void CheckForDragon(int x, int y)
6067 boolean dragon_found = FALSE;
6068 static int xy[4][2] =
6076 for (i = 0; i < 4; i++)
6078 for (j = 0; j < 4; j++)
6080 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6082 if (IN_LEV_FIELD(xx, yy) &&
6083 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6085 if (Feld[xx][yy] == EL_DRAGON)
6086 dragon_found = TRUE;
6095 for (i = 0; i < 4; i++)
6097 for (j = 0; j < 3; j++)
6099 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6101 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6103 Feld[xx][yy] = EL_EMPTY;
6104 DrawLevelField(xx, yy);
6113 static void InitBuggyBase(int x, int y)
6115 int element = Feld[x][y];
6116 int activating_delay = FRAMES_PER_SECOND / 4;
6119 (element == EL_SP_BUGGY_BASE ?
6120 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6121 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6123 element == EL_SP_BUGGY_BASE_ACTIVE ?
6124 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6127 static void WarnBuggyBase(int x, int y)
6130 static int xy[4][2] =
6138 for (i = 0; i < 4; i++)
6140 int xx = x + xy[i][0], yy = y + xy[i][1];
6142 if (IS_PLAYER(xx, yy))
6144 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6151 static void InitTrap(int x, int y)
6153 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6156 static void ActivateTrap(int x, int y)
6158 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6161 static void ChangeActiveTrap(int x, int y)
6163 int graphic = IMG_TRAP_ACTIVE;
6165 /* if new animation frame was drawn, correct crumbled sand border */
6166 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6167 DrawLevelFieldCrumbledSand(x, y);
6170 static void ChangeElementNowExt(int x, int y, int target_element)
6172 int previous_move_direction = MovDir[x][y];
6174 /* check if element under player changes from accessible to unaccessible
6175 (needed for special case of dropping element which then changes) */
6176 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6177 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6184 Feld[x][y] = target_element;
6186 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6188 ResetGfxAnimation(x, y);
6189 ResetRandomAnimationValue(x, y);
6191 if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
6192 MovDir[x][y] = previous_move_direction;
6194 InitField(x, y, FALSE);
6195 if (CAN_MOVE(Feld[x][y]))
6198 DrawLevelField(x, y);
6200 if (GFX_CRUMBLED(Feld[x][y]))
6201 DrawLevelFieldCrumbledSandNeighbours(x, y);
6203 TestIfBadThingTouchesHero(x, y);
6204 TestIfPlayerTouchesCustomElement(x, y);
6205 TestIfElementTouchesCustomElement(x, y);
6207 if (ELEM_IS_PLAYER(target_element))
6208 RelocatePlayer(x, y, target_element);
6211 static boolean ChangeElementNow(int x, int y, int element, int page)
6213 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6215 /* always use default change event to prevent running into a loop */
6216 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6217 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6219 /* do not change already changed elements with same change event */
6221 if (Changed[x][y] & ChangeEvent[x][y])
6228 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6230 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6232 if (change->explode)
6239 if (change->use_content)
6241 boolean complete_change = TRUE;
6242 boolean can_change[3][3];
6245 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6247 boolean half_destructible;
6248 int ex = x + xx - 1;
6249 int ey = y + yy - 1;
6252 can_change[xx][yy] = TRUE;
6254 if (ex == x && ey == y) /* do not check changing element itself */
6257 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6259 can_change[xx][yy] = FALSE; /* do not change empty borders */
6264 if (!IN_LEV_FIELD(ex, ey))
6266 can_change[xx][yy] = FALSE;
6267 complete_change = FALSE;
6274 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6275 e = MovingOrBlocked2Element(ex, ey);
6277 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6279 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6280 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6281 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6283 can_change[xx][yy] = FALSE;
6284 complete_change = FALSE;
6288 if (!change->only_complete || complete_change)
6290 boolean something_has_changed = FALSE;
6292 if (change->only_complete && change->use_random_change &&
6293 RND(100) < change->random)
6296 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6298 int ex = x + xx - 1;
6299 int ey = y + yy - 1;
6301 if (can_change[xx][yy] && (!change->use_random_change ||
6302 RND(100) < change->random))
6304 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6305 RemoveMovingField(ex, ey);
6307 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6309 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6311 something_has_changed = TRUE;
6313 /* for symmetry reasons, freeze newly created border elements */
6314 if (ex != x || ey != y)
6315 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6319 if (something_has_changed)
6320 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6325 ChangeElementNowExt(x, y, change->target_element);
6327 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6333 static void ChangeElement(int x, int y, int page)
6335 int element = MovingOrBlocked2Element(x, y);
6336 struct ElementInfo *ei = &element_info[element];
6337 struct ElementChangeInfo *change = &ei->change_page[page];
6341 if (!CAN_CHANGE(element))
6344 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6345 x, y, element, element_info[element].token_name);
6346 printf("ChangeElement(): This should never happen!\n");
6352 if (ChangeDelay[x][y] == 0) /* initialize element change */
6354 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6355 RND(change->delay_random * change->delay_frames)) + 1;
6357 ResetGfxAnimation(x, y);
6358 ResetRandomAnimationValue(x, y);
6360 if (change->pre_change_function)
6361 change->pre_change_function(x, y);
6364 ChangeDelay[x][y]--;
6366 if (ChangeDelay[x][y] != 0) /* continue element change */
6368 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6370 if (IS_ANIMATED(graphic))
6371 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6373 if (change->change_function)
6374 change->change_function(x, y);
6376 else /* finish element change */
6378 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6380 page = ChangePage[x][y];
6381 ChangePage[x][y] = -1;
6385 if (IS_MOVING(x, y) && !change->explode)
6387 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6390 ChangeDelay[x][y] = 1; /* try change after next move step */
6391 ChangePage[x][y] = page; /* remember page to use for change */
6396 if (ChangeElementNow(x, y, element, page))
6398 if (change->post_change_function)
6399 change->post_change_function(x, y);
6404 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6405 int trigger_element,
6411 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6414 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6416 int element = EL_CUSTOM_START + i;
6418 boolean change_element = FALSE;
6421 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6424 for (j = 0; j < element_info[element].num_change_pages; j++)
6426 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6428 if (change->can_change &&
6430 change->events & CH_EVENT_BIT(trigger_event) &&
6432 change->sides & trigger_side &&
6434 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6436 change->trigger_element == trigger_element
6441 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6442 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6443 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6446 change_element = TRUE;
6453 if (!change_element)
6456 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6459 if (x == lx && y == ly) /* do not change trigger element itself */
6463 if (Feld[x][y] == element)
6465 ChangeDelay[x][y] = 1;
6466 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6467 ChangeElement(x, y, page);
6475 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6478 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6482 static boolean CheckElementSideChange(int x, int y, int element, int side,
6483 int trigger_event, int page)
6485 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6488 if (Feld[x][y] == EL_BLOCKED)
6490 Blocked2Moving(x, y, &x, &y);
6491 element = Feld[x][y];
6495 page = element_info[element].event_page_nr[trigger_event];
6497 if (!(element_info[element].change_page[page].sides & side))
6500 ChangeDelay[x][y] = 1;
6501 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6502 ChangeElement(x, y, page);
6507 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6509 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6512 static void PlayPlayerSound(struct PlayerInfo *player)
6514 int jx = player->jx, jy = player->jy;
6515 int element = player->element_nr;
6516 int last_action = player->last_action_waiting;
6517 int action = player->action_waiting;
6519 if (player->is_waiting)
6521 if (action != last_action)
6522 PlayLevelSoundElementAction(jx, jy, element, action);
6524 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6528 if (action != last_action)
6529 StopSound(element_info[element].sound[last_action]);
6531 if (last_action == ACTION_SLEEPING)
6532 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6536 static void PlayAllPlayersSound()
6540 for (i = 0; i < MAX_PLAYERS; i++)
6541 if (stored_player[i].active)
6542 PlayPlayerSound(&stored_player[i]);
6545 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6547 boolean last_waiting = player->is_waiting;
6548 int move_dir = player->MovDir;
6550 player->last_action_waiting = player->action_waiting;
6554 if (!last_waiting) /* not waiting -> waiting */
6556 player->is_waiting = TRUE;
6558 player->frame_counter_bored =
6560 game.player_boring_delay_fixed +
6561 SimpleRND(game.player_boring_delay_random);
6562 player->frame_counter_sleeping =
6564 game.player_sleeping_delay_fixed +
6565 SimpleRND(game.player_sleeping_delay_random);
6567 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6570 if (game.player_sleeping_delay_fixed +
6571 game.player_sleeping_delay_random > 0 &&
6572 player->anim_delay_counter == 0 &&
6573 player->post_delay_counter == 0 &&
6574 FrameCounter >= player->frame_counter_sleeping)
6575 player->is_sleeping = TRUE;
6576 else if (game.player_boring_delay_fixed +
6577 game.player_boring_delay_random > 0 &&
6578 FrameCounter >= player->frame_counter_bored)
6579 player->is_bored = TRUE;
6581 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6582 player->is_bored ? ACTION_BORING :
6585 if (player->is_sleeping)
6587 if (player->num_special_action_sleeping > 0)
6589 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6591 int last_special_action = player->special_action_sleeping;
6592 int num_special_action = player->num_special_action_sleeping;
6593 int special_action =
6594 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6595 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6596 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6597 last_special_action + 1 : ACTION_SLEEPING);
6598 int special_graphic =
6599 el_act_dir2img(player->element_nr, special_action, move_dir);
6601 player->anim_delay_counter =
6602 graphic_info[special_graphic].anim_delay_fixed +
6603 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6604 player->post_delay_counter =
6605 graphic_info[special_graphic].post_delay_fixed +
6606 SimpleRND(graphic_info[special_graphic].post_delay_random);
6608 player->special_action_sleeping = special_action;
6611 if (player->anim_delay_counter > 0)
6613 player->action_waiting = player->special_action_sleeping;
6614 player->anim_delay_counter--;
6616 else if (player->post_delay_counter > 0)
6618 player->post_delay_counter--;
6622 else if (player->is_bored)
6624 if (player->num_special_action_bored > 0)
6626 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6628 int special_action =
6629 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6630 int special_graphic =
6631 el_act_dir2img(player->element_nr, special_action, move_dir);
6633 player->anim_delay_counter =
6634 graphic_info[special_graphic].anim_delay_fixed +
6635 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6636 player->post_delay_counter =
6637 graphic_info[special_graphic].post_delay_fixed +
6638 SimpleRND(graphic_info[special_graphic].post_delay_random);
6640 player->special_action_bored = special_action;
6643 if (player->anim_delay_counter > 0)
6645 player->action_waiting = player->special_action_bored;
6646 player->anim_delay_counter--;
6648 else if (player->post_delay_counter > 0)
6650 player->post_delay_counter--;
6655 else if (last_waiting) /* waiting -> not waiting */
6657 player->is_waiting = FALSE;
6658 player->is_bored = FALSE;
6659 player->is_sleeping = FALSE;
6661 player->frame_counter_bored = -1;
6662 player->frame_counter_sleeping = -1;
6664 player->anim_delay_counter = 0;
6665 player->post_delay_counter = 0;
6667 player->action_waiting = ACTION_DEFAULT;
6669 player->special_action_bored = ACTION_DEFAULT;
6670 player->special_action_sleeping = ACTION_DEFAULT;
6675 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6678 static byte stored_player_action[MAX_PLAYERS];
6679 static int num_stored_actions = 0;
6681 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6682 int left = player_action & JOY_LEFT;
6683 int right = player_action & JOY_RIGHT;
6684 int up = player_action & JOY_UP;
6685 int down = player_action & JOY_DOWN;
6686 int button1 = player_action & JOY_BUTTON_1;
6687 int button2 = player_action & JOY_BUTTON_2;
6688 int dx = (left ? -1 : right ? 1 : 0);
6689 int dy = (up ? -1 : down ? 1 : 0);
6692 stored_player_action[player->index_nr] = 0;
6693 num_stored_actions++;
6697 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6700 if (!player->active || tape.pausing)
6704 printf("::: [%d %d %d %d] [%d %d]\n",
6705 left, right, up, down, button1, button2);
6711 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6715 snapped = SnapField(player, dx, dy);
6719 dropped = DropElement(player);
6721 moved = MovePlayer(player, dx, dy);
6724 if (tape.single_step && tape.recording && !tape.pausing)
6726 if (button1 || (dropped && !moved))
6728 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6729 SnapField(player, 0, 0); /* stop snapping */
6733 SetPlayerWaiting(player, FALSE);
6736 return player_action;
6738 stored_player_action[player->index_nr] = player_action;
6744 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6747 /* no actions for this player (no input at player's configured device) */
6749 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6750 SnapField(player, 0, 0);
6751 CheckGravityMovement(player);
6753 if (player->MovPos == 0)
6754 SetPlayerWaiting(player, TRUE);
6756 if (player->MovPos == 0) /* needed for tape.playing */
6757 player->is_moving = FALSE;
6759 player->is_dropping = FALSE;
6765 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6767 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6769 TapeRecordAction(stored_player_action);
6770 num_stored_actions = 0;
6777 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6779 static byte stored_player_action[MAX_PLAYERS];
6780 static int num_stored_actions = 0;
6781 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6782 int left = player_action & JOY_LEFT;
6783 int right = player_action & JOY_RIGHT;
6784 int up = player_action & JOY_UP;
6785 int down = player_action & JOY_DOWN;
6786 int button1 = player_action & JOY_BUTTON_1;
6787 int button2 = player_action & JOY_BUTTON_2;
6788 int dx = (left ? -1 : right ? 1 : 0);
6789 int dy = (up ? -1 : down ? 1 : 0);
6791 stored_player_action[player->index_nr] = 0;
6792 num_stored_actions++;
6794 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6796 if (!player->active || tape.pausing)
6801 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6804 snapped = SnapField(player, dx, dy);
6808 dropped = DropElement(player);
6810 moved = MovePlayer(player, dx, dy);
6813 if (tape.single_step && tape.recording && !tape.pausing)
6815 if (button1 || (dropped && !moved))
6817 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6818 SnapField(player, 0, 0); /* stop snapping */
6822 stored_player_action[player->index_nr] = player_action;
6826 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6828 /* no actions for this player (no input at player's configured device) */
6830 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6831 SnapField(player, 0, 0);
6832 CheckGravityMovement(player);
6834 if (player->MovPos == 0)
6835 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6837 if (player->MovPos == 0) /* needed for tape.playing */
6838 player->is_moving = FALSE;
6841 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6843 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6845 TapeRecordAction(stored_player_action);
6846 num_stored_actions = 0;
6853 static unsigned long action_delay = 0;
6854 unsigned long action_delay_value;
6855 int magic_wall_x = 0, magic_wall_y = 0;
6856 int i, x, y, element, graphic;
6857 byte *recorded_player_action;
6858 byte summarized_player_action = 0;
6860 byte tape_action[MAX_PLAYERS];
6863 if (game_status != GAME_MODE_PLAYING)
6866 action_delay_value =
6867 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6869 if (tape.playing && tape.index_search && !tape.pausing)
6870 action_delay_value = 0;
6872 /* ---------- main game synchronization point ---------- */
6874 WaitUntilDelayReached(&action_delay, action_delay_value);
6876 if (network_playing && !network_player_action_received)
6880 printf("DEBUG: try to get network player actions in time\n");
6884 #if defined(PLATFORM_UNIX)
6885 /* last chance to get network player actions without main loop delay */
6889 if (game_status != GAME_MODE_PLAYING)
6892 if (!network_player_action_received)
6896 printf("DEBUG: failed to get network player actions in time\n");
6907 printf("::: getting new tape action [%d]\n", FrameCounter);
6910 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6912 for (i = 0; i < MAX_PLAYERS; i++)
6914 summarized_player_action |= stored_player[i].action;
6916 if (!network_playing)
6917 stored_player[i].effective_action = stored_player[i].action;
6920 #if defined(PLATFORM_UNIX)
6921 if (network_playing)
6922 SendToServer_MovePlayer(summarized_player_action);
6925 if (!options.network && !setup.team_mode)
6926 local_player->effective_action = summarized_player_action;
6928 for (i = 0; i < MAX_PLAYERS; i++)
6930 int actual_player_action = stored_player[i].effective_action;
6932 if (stored_player[i].programmed_action)
6933 actual_player_action = stored_player[i].programmed_action;
6935 if (recorded_player_action)
6936 actual_player_action = recorded_player_action[i];
6938 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6940 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6941 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6943 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6948 TapeRecordAction(tape_action);
6951 network_player_action_received = FALSE;
6953 ScrollScreen(NULL, SCROLL_GO_ON);
6959 for (i = 0; i < MAX_PLAYERS; i++)
6960 stored_player[i].Frame++;
6964 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6966 for (i = 0; i < MAX_PLAYERS; i++)
6968 struct PlayerInfo *player = &stored_player[i];
6972 if (player->active && player->is_pushing && player->is_moving &&
6975 ContinueMoving(x, y);
6977 /* continue moving after pushing (this is actually a bug) */
6978 if (!IS_MOVING(x, y))
6987 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6989 Changed[x][y] = CE_BITMASK_DEFAULT;
6990 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6993 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6995 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6996 printf("GameActions(): This should never happen!\n");
6998 ChangePage[x][y] = -1;
7003 if (WasJustMoving[x][y] > 0)
7004 WasJustMoving[x][y]--;
7005 if (WasJustFalling[x][y] > 0)
7006 WasJustFalling[x][y]--;
7011 /* reset finished pushing action (not done in ContinueMoving() to allow
7012 continous pushing animation for elements with zero push delay) */
7013 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7015 ResetGfxAnimation(x, y);
7016 DrawLevelField(x, y);
7021 if (IS_BLOCKED(x, y))
7025 Blocked2Moving(x, y, &oldx, &oldy);
7026 if (!IS_MOVING(oldx, oldy))
7028 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7029 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7030 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7031 printf("GameActions(): This should never happen!\n");
7037 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7039 element = Feld[x][y];
7041 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7043 graphic = el2img(element);
7049 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7051 element = graphic = 0;
7055 if (graphic_info[graphic].anim_global_sync)
7056 GfxFrame[x][y] = FrameCounter;
7058 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7059 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7060 ResetRandomAnimationValue(x, y);
7062 SetRandomAnimationValue(x, y);
7065 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7068 if (IS_INACTIVE(element))
7070 if (IS_ANIMATED(graphic))
7071 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7077 /* this may take place after moving, so 'element' may have changed */
7079 if (IS_CHANGING(x, y))
7081 if (IS_CHANGING(x, y) &&
7082 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7086 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7087 element_info[element].event_page_nr[CE_DELAY]);
7089 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7092 element = Feld[x][y];
7093 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7097 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7102 element = Feld[x][y];
7103 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7105 if (element == EL_MOLE)
7106 printf("::: %d, %d, %d [%d]\n",
7107 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7111 if (element == EL_YAMYAM)
7112 printf("::: %d, %d, %d\n",
7113 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7117 if (IS_ANIMATED(graphic) &&
7121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7124 if (element == EL_BUG)
7125 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7129 if (element == EL_MOLE)
7130 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7134 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7135 EdelsteinFunkeln(x, y);
7137 else if ((element == EL_ACID ||
7138 element == EL_EXIT_OPEN ||
7139 element == EL_SP_EXIT_OPEN ||
7140 element == EL_SP_TERMINAL ||
7141 element == EL_SP_TERMINAL_ACTIVE ||
7142 element == EL_EXTRA_TIME ||
7143 element == EL_SHIELD_NORMAL ||
7144 element == EL_SHIELD_DEADLY) &&
7145 IS_ANIMATED(graphic))
7146 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7147 else if (IS_MOVING(x, y))
7148 ContinueMoving(x, y);
7149 else if (IS_ACTIVE_BOMB(element))
7150 CheckDynamite(x, y);
7152 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7153 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7155 else if (element == EL_AMOEBA_GROWING)
7156 AmoebeWaechst(x, y);
7157 else if (element == EL_AMOEBA_SHRINKING)
7158 AmoebaDisappearing(x, y);
7160 #if !USE_NEW_AMOEBA_CODE
7161 else if (IS_AMOEBALIVE(element))
7162 AmoebeAbleger(x, y);
7165 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7167 else if (element == EL_EXIT_CLOSED)
7169 else if (element == EL_SP_EXIT_CLOSED)
7171 else if (element == EL_EXPANDABLE_WALL_GROWING)
7173 else if (element == EL_EXPANDABLE_WALL ||
7174 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7175 element == EL_EXPANDABLE_WALL_VERTICAL ||
7176 element == EL_EXPANDABLE_WALL_ANY)
7178 else if (element == EL_FLAMES)
7179 CheckForDragon(x, y);
7181 else if (IS_AUTO_CHANGING(element))
7182 ChangeElement(x, y);
7184 else if (element == EL_EXPLOSION)
7185 ; /* drawing of correct explosion animation is handled separately */
7186 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7187 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7190 /* this may take place after moving, so 'element' may have changed */
7191 if (IS_AUTO_CHANGING(Feld[x][y]))
7192 ChangeElement(x, y);
7195 if (IS_BELT_ACTIVE(element))
7196 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7198 if (game.magic_wall_active)
7200 int jx = local_player->jx, jy = local_player->jy;
7202 /* play the element sound at the position nearest to the player */
7203 if ((element == EL_MAGIC_WALL_FULL ||
7204 element == EL_MAGIC_WALL_ACTIVE ||
7205 element == EL_MAGIC_WALL_EMPTYING ||
7206 element == EL_BD_MAGIC_WALL_FULL ||
7207 element == EL_BD_MAGIC_WALL_ACTIVE ||
7208 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7209 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7217 #if USE_NEW_AMOEBA_CODE
7218 /* new experimental amoeba growth stuff */
7220 if (!(FrameCounter % 8))
7223 static unsigned long random = 1684108901;
7225 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7228 x = (random >> 10) % lev_fieldx;
7229 y = (random >> 20) % lev_fieldy;
7231 x = RND(lev_fieldx);
7232 y = RND(lev_fieldy);
7234 element = Feld[x][y];
7236 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7237 if (!IS_PLAYER(x,y) &&
7238 (element == EL_EMPTY ||
7239 element == EL_SAND ||
7240 element == EL_QUICKSAND_EMPTY ||
7241 element == EL_ACID_SPLASH_LEFT ||
7242 element == EL_ACID_SPLASH_RIGHT))
7244 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7245 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7246 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7247 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7248 Feld[x][y] = EL_AMOEBA_DROP;
7251 random = random * 129 + 1;
7257 if (game.explosions_delayed)
7260 game.explosions_delayed = FALSE;
7262 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7264 element = Feld[x][y];
7266 if (ExplodeField[x][y])
7267 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7268 else if (element == EL_EXPLOSION)
7269 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7271 ExplodeField[x][y] = EX_NO_EXPLOSION;
7274 game.explosions_delayed = TRUE;
7277 if (game.magic_wall_active)
7279 if (!(game.magic_wall_time_left % 4))
7281 int element = Feld[magic_wall_x][magic_wall_y];
7283 if (element == EL_BD_MAGIC_WALL_FULL ||
7284 element == EL_BD_MAGIC_WALL_ACTIVE ||
7285 element == EL_BD_MAGIC_WALL_EMPTYING)
7286 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7288 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7291 if (game.magic_wall_time_left > 0)
7293 game.magic_wall_time_left--;
7294 if (!game.magic_wall_time_left)
7296 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7298 element = Feld[x][y];
7300 if (element == EL_MAGIC_WALL_ACTIVE ||
7301 element == EL_MAGIC_WALL_FULL)
7303 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7304 DrawLevelField(x, y);
7306 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7307 element == EL_BD_MAGIC_WALL_FULL)
7309 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7310 DrawLevelField(x, y);
7314 game.magic_wall_active = FALSE;
7319 if (game.light_time_left > 0)
7321 game.light_time_left--;
7323 if (game.light_time_left == 0)
7324 RedrawAllLightSwitchesAndInvisibleElements();
7327 if (game.timegate_time_left > 0)
7329 game.timegate_time_left--;
7331 if (game.timegate_time_left == 0)
7332 CloseAllOpenTimegates();
7335 for (i = 0; i < MAX_PLAYERS; i++)
7337 struct PlayerInfo *player = &stored_player[i];
7339 if (SHIELD_ON(player))
7341 if (player->shield_deadly_time_left)
7342 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7343 else if (player->shield_normal_time_left)
7344 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7348 if (TimeFrames >= FRAMES_PER_SECOND)
7353 for (i = 0; i < MAX_PLAYERS; i++)
7355 struct PlayerInfo *player = &stored_player[i];
7357 if (SHIELD_ON(player))
7359 player->shield_normal_time_left--;
7361 if (player->shield_deadly_time_left > 0)
7362 player->shield_deadly_time_left--;
7366 if (tape.recording || tape.playing)
7367 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7373 if (TimeLeft <= 10 && setup.time_limit)
7374 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7376 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7378 if (!TimeLeft && setup.time_limit)
7379 for (i = 0; i < MAX_PLAYERS; i++)
7380 KillHero(&stored_player[i]);
7382 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7383 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7387 PlayAllPlayersSound();
7389 if (options.debug) /* calculate frames per second */
7391 static unsigned long fps_counter = 0;
7392 static int fps_frames = 0;
7393 unsigned long fps_delay_ms = Counter() - fps_counter;
7397 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7399 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7402 fps_counter = Counter();
7405 redraw_mask |= REDRAW_FPS;
7409 if (stored_player[0].jx != stored_player[0].last_jx ||
7410 stored_player[0].jy != stored_player[0].last_jy)
7411 printf("::: %d, %d, %d, %d, %d\n",
7412 stored_player[0].MovDir,
7413 stored_player[0].MovPos,
7414 stored_player[0].GfxPos,
7415 stored_player[0].Frame,
7416 stored_player[0].StepFrame);
7423 for (i = 0; i < MAX_PLAYERS; i++)
7426 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7428 stored_player[i].Frame += move_frames;
7430 if (stored_player[i].MovPos != 0)
7431 stored_player[i].StepFrame += move_frames;
7433 if (stored_player[i].drop_delay > 0)
7434 stored_player[i].drop_delay--;
7439 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7441 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7443 local_player->show_envelope = 0;
7448 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7450 int min_x = x, min_y = y, max_x = x, max_y = y;
7453 for (i = 0; i < MAX_PLAYERS; i++)
7455 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7457 if (!stored_player[i].active || &stored_player[i] == player)
7460 min_x = MIN(min_x, jx);
7461 min_y = MIN(min_y, jy);
7462 max_x = MAX(max_x, jx);
7463 max_y = MAX(max_y, jy);
7466 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7469 static boolean AllPlayersInVisibleScreen()
7473 for (i = 0; i < MAX_PLAYERS; i++)
7475 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7477 if (!stored_player[i].active)
7480 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7487 void ScrollLevel(int dx, int dy)
7489 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7492 BlitBitmap(drawto_field, drawto_field,
7493 FX + TILEX * (dx == -1) - softscroll_offset,
7494 FY + TILEY * (dy == -1) - softscroll_offset,
7495 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7496 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7497 FX + TILEX * (dx == 1) - softscroll_offset,
7498 FY + TILEY * (dy == 1) - softscroll_offset);
7502 x = (dx == 1 ? BX1 : BX2);
7503 for (y = BY1; y <= BY2; y++)
7504 DrawScreenField(x, y);
7509 y = (dy == 1 ? BY1 : BY2);
7510 for (x = BX1; x <= BX2; x++)
7511 DrawScreenField(x, y);
7514 redraw_mask |= REDRAW_FIELD;
7517 static void CheckGravityMovement(struct PlayerInfo *player)
7519 if (game.gravity && !player->programmed_action)
7521 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7522 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7524 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7525 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7526 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7527 int jx = player->jx, jy = player->jy;
7528 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7529 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7530 int new_jx = jx + dx, new_jy = jy + dy;
7531 boolean field_under_player_is_free =
7532 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7533 boolean player_is_moving_to_valid_field =
7534 (IN_LEV_FIELD(new_jx, new_jy) &&
7535 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7536 Feld[new_jx][new_jy] == EL_SAND));
7537 /* !!! extend EL_SAND to anything diggable !!! */
7539 if (field_under_player_is_free &&
7540 !player_is_moving_to_valid_field &&
7541 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7542 player->programmed_action = MV_DOWN;
7548 -----------------------------------------------------------------------------
7549 dx, dy: direction (non-diagonal) to try to move the player to
7550 real_dx, real_dy: direction as read from input device (can be diagonal)
7553 boolean MovePlayerOneStep(struct PlayerInfo *player,
7554 int dx, int dy, int real_dx, int real_dy)
7557 static int change_sides[4][2] =
7559 /* enter side leave side */
7560 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7561 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7562 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7563 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7565 int move_direction = (dx == -1 ? MV_LEFT :
7566 dx == +1 ? MV_RIGHT :
7568 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7569 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7570 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7572 int jx = player->jx, jy = player->jy;
7573 int new_jx = jx + dx, new_jy = jy + dy;
7577 if (!player->active || (!dx && !dy))
7578 return MF_NO_ACTION;
7580 player->MovDir = (dx < 0 ? MV_LEFT :
7583 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7585 if (!IN_LEV_FIELD(new_jx, new_jy))
7586 return MF_NO_ACTION;
7588 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7589 return MF_NO_ACTION;
7592 element = MovingOrBlocked2Element(new_jx, new_jy);
7594 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7597 if (DONT_RUN_INTO(element))
7599 if (element == EL_ACID && dx == 0 && dy == 1)
7602 Feld[jx][jy] = EL_PLAYER_1;
7603 InitMovingField(jx, jy, MV_DOWN);
7604 Store[jx][jy] = EL_ACID;
7605 ContinueMoving(jx, jy);
7609 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7614 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7615 if (can_move != MF_MOVING)
7618 /* check if DigField() has caused relocation of the player */
7619 if (player->jx != jx || player->jy != jy)
7620 return MF_NO_ACTION;
7622 StorePlayer[jx][jy] = 0;
7623 player->last_jx = jx;
7624 player->last_jy = jy;
7625 player->jx = new_jx;
7626 player->jy = new_jy;
7627 StorePlayer[new_jx][new_jy] = player->element_nr;
7630 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7632 player->step_counter++;
7634 player->drop_delay = 0;
7636 PlayerVisit[jx][jy] = FrameCounter;
7638 ScrollPlayer(player, SCROLL_INIT);
7641 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7643 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7644 CE_OTHER_GETS_LEFT);
7645 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7646 CE_LEFT_BY_PLAYER, -1);
7649 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7651 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7652 enter_side, CE_OTHER_GETS_ENTERED);
7653 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7654 CE_ENTERED_BY_PLAYER, -1);
7661 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7663 int jx = player->jx, jy = player->jy;
7664 int old_jx = jx, old_jy = jy;
7665 int moved = MF_NO_ACTION;
7668 if (!player->active)
7673 if (player->MovPos == 0)
7675 player->is_moving = FALSE;
7676 player->is_digging = FALSE;
7677 player->is_collecting = FALSE;
7678 player->is_snapping = FALSE;
7679 player->is_pushing = FALSE;
7685 if (!player->active || (!dx && !dy))
7690 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7694 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7695 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7699 /* remove the last programmed player action */
7700 player->programmed_action = 0;
7704 /* should only happen if pre-1.2 tape recordings are played */
7705 /* this is only for backward compatibility */
7707 int original_move_delay_value = player->move_delay_value;
7710 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7714 /* scroll remaining steps with finest movement resolution */
7715 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7717 while (player->MovPos)
7719 ScrollPlayer(player, SCROLL_GO_ON);
7720 ScrollScreen(NULL, SCROLL_GO_ON);
7726 player->move_delay_value = original_move_delay_value;
7729 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7731 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7732 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7736 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7737 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7743 if (moved & MF_MOVING && !ScreenMovPos &&
7744 (player == local_player || !options.network))
7746 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7747 int offset = (setup.scroll_delay ? 3 : 0);
7749 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7751 /* actual player has left the screen -- scroll in that direction */
7752 if (jx != old_jx) /* player has moved horizontally */
7753 scroll_x += (jx - old_jx);
7754 else /* player has moved vertically */
7755 scroll_y += (jy - old_jy);
7759 if (jx != old_jx) /* player has moved horizontally */
7761 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7762 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7763 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7765 /* don't scroll over playfield boundaries */
7766 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7767 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7769 /* don't scroll more than one field at a time */
7770 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7772 /* don't scroll against the player's moving direction */
7773 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7774 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7775 scroll_x = old_scroll_x;
7777 else /* player has moved vertically */
7779 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7780 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7781 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7783 /* don't scroll over playfield boundaries */
7784 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7785 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7787 /* don't scroll more than one field at a time */
7788 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7790 /* don't scroll against the player's moving direction */
7791 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7792 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7793 scroll_y = old_scroll_y;
7797 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7799 if (!options.network && !AllPlayersInVisibleScreen())
7801 scroll_x = old_scroll_x;
7802 scroll_y = old_scroll_y;
7806 ScrollScreen(player, SCROLL_INIT);
7807 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7814 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7816 if (!(moved & MF_MOVING) && !player->is_pushing)
7821 player->StepFrame = 0;
7823 if (moved & MF_MOVING)
7825 if (old_jx != jx && old_jy == jy)
7826 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7827 else if (old_jx == jx && old_jy != jy)
7828 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7830 DrawLevelField(jx, jy); /* for "crumbled sand" */
7832 player->last_move_dir = player->MovDir;
7833 player->is_moving = TRUE;
7835 player->is_snapping = FALSE;
7839 player->is_switching = FALSE;
7842 player->is_dropping = FALSE;
7847 static int change_sides[4][2] =
7849 /* enter side leave side */
7850 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7851 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7852 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7853 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7855 int move_direction = player->MovDir;
7856 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7857 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7860 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7862 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7863 leave_side, CE_OTHER_GETS_LEFT);
7864 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7865 leave_side, CE_LEFT_BY_PLAYER, -1);
7868 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7870 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7871 enter_side, CE_OTHER_GETS_ENTERED);
7872 CheckElementSideChange(jx, jy, Feld[jx][jy],
7873 enter_side, CE_ENTERED_BY_PLAYER, -1);
7884 CheckGravityMovement(player);
7887 player->last_move_dir = MV_NO_MOVING;
7889 player->is_moving = FALSE;
7892 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7894 TestIfHeroTouchesBadThing(jx, jy);
7895 TestIfPlayerTouchesCustomElement(jx, jy);
7898 if (!player->active)
7904 void ScrollPlayer(struct PlayerInfo *player, int mode)
7906 int jx = player->jx, jy = player->jy;
7907 int last_jx = player->last_jx, last_jy = player->last_jy;
7908 int move_stepsize = TILEX / player->move_delay_value;
7910 if (!player->active || !player->MovPos)
7913 if (mode == SCROLL_INIT)
7915 player->actual_frame_counter = FrameCounter;
7916 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7918 if (Feld[last_jx][last_jy] == EL_EMPTY)
7919 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7926 else if (!FrameReached(&player->actual_frame_counter, 1))
7929 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7930 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7932 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7933 Feld[last_jx][last_jy] = EL_EMPTY;
7935 /* before DrawPlayer() to draw correct player graphic for this case */
7936 if (player->MovPos == 0)
7937 CheckGravityMovement(player);
7940 DrawPlayer(player); /* needed here only to cleanup last field */
7943 if (player->MovPos == 0) /* player reached destination field */
7946 if (player->move_delay_reset_counter > 0)
7948 player->move_delay_reset_counter--;
7950 if (player->move_delay_reset_counter == 0)
7952 /* continue with normal speed after quickly moving through gate */
7953 HALVE_PLAYER_SPEED(player);
7955 /* be able to make the next move without delay */
7956 player->move_delay = 0;
7960 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7962 /* continue with normal speed after quickly moving through gate */
7963 HALVE_PLAYER_SPEED(player);
7965 /* be able to make the next move without delay */
7966 player->move_delay = 0;
7970 player->last_jx = jx;
7971 player->last_jy = jy;
7973 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7974 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7975 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7977 DrawPlayer(player); /* needed here only to cleanup last field */
7980 if (local_player->friends_still_needed == 0 ||
7981 IS_SP_ELEMENT(Feld[jx][jy]))
7982 player->LevelSolved = player->GameOver = TRUE;
7985 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7987 TestIfHeroTouchesBadThing(jx, jy);
7988 TestIfPlayerTouchesCustomElement(jx, jy);
7990 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7993 if (!player->active)
7997 if (tape.single_step && tape.recording && !tape.pausing &&
7998 !player->programmed_action)
7999 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8003 void ScrollScreen(struct PlayerInfo *player, int mode)
8005 static unsigned long screen_frame_counter = 0;
8007 if (mode == SCROLL_INIT)
8009 /* set scrolling step size according to actual player's moving speed */
8010 ScrollStepSize = TILEX / player->move_delay_value;
8012 screen_frame_counter = FrameCounter;
8013 ScreenMovDir = player->MovDir;
8014 ScreenMovPos = player->MovPos;
8015 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8018 else if (!FrameReached(&screen_frame_counter, 1))
8023 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8024 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8025 redraw_mask |= REDRAW_FIELD;
8028 ScreenMovDir = MV_NO_MOVING;
8031 void TestIfPlayerTouchesCustomElement(int x, int y)
8033 static int xy[4][2] =
8040 static int change_sides[4][2] =
8042 /* center side border side */
8043 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8044 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8045 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8046 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8048 static int touch_dir[4] =
8055 int center_element = Feld[x][y]; /* should always be non-moving! */
8058 for (i = 0; i < 4; i++)
8060 int xx = x + xy[i][0];
8061 int yy = y + xy[i][1];
8062 int center_side = change_sides[i][0];
8063 int border_side = change_sides[i][1];
8066 if (!IN_LEV_FIELD(xx, yy))
8069 if (IS_PLAYER(x, y))
8071 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8072 border_element = Feld[xx][yy]; /* may be moving! */
8073 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8074 border_element = Feld[xx][yy];
8075 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8076 border_element = MovingOrBlocked2Element(xx, yy);
8078 continue; /* center and border element do not touch */
8080 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8081 CE_OTHER_GETS_TOUCHED);
8082 CheckElementSideChange(xx, yy, border_element, border_side,
8083 CE_TOUCHED_BY_PLAYER, -1);
8085 else if (IS_PLAYER(xx, yy))
8087 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8089 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8091 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8092 continue; /* center and border element do not touch */
8095 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8096 CE_OTHER_GETS_TOUCHED);
8097 CheckElementSideChange(x, y, center_element, center_side,
8098 CE_TOUCHED_BY_PLAYER, -1);
8105 void TestIfElementTouchesCustomElement(int x, int y)
8107 static int xy[4][2] =
8114 static int change_sides[4][2] =
8116 /* center side border side */
8117 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8118 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8119 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8120 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8122 static int touch_dir[4] =
8129 boolean change_center_element = FALSE;
8130 int center_element_change_page = 0;
8131 int center_element = Feld[x][y]; /* should always be non-moving! */
8134 for (i = 0; i < 4; i++)
8136 int xx = x + xy[i][0];
8137 int yy = y + xy[i][1];
8138 int center_side = change_sides[i][0];
8139 int border_side = change_sides[i][1];
8142 if (!IN_LEV_FIELD(xx, yy))
8145 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8146 border_element = Feld[xx][yy]; /* may be moving! */
8147 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8148 border_element = Feld[xx][yy];
8149 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8150 border_element = MovingOrBlocked2Element(xx, yy);
8152 continue; /* center and border element do not touch */
8154 /* check for change of center element (but change it only once) */
8155 if (IS_CUSTOM_ELEMENT(center_element) &&
8156 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8157 !change_center_element)
8159 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8161 struct ElementChangeInfo *change =
8162 &element_info[center_element].change_page[j];
8164 if (change->can_change &&
8165 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8166 change->sides & border_side &&
8168 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8170 change->trigger_element == border_element
8174 change_center_element = TRUE;
8175 center_element_change_page = j;
8182 /* check for change of border element */
8183 if (IS_CUSTOM_ELEMENT(border_element) &&
8184 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8186 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8188 struct ElementChangeInfo *change =
8189 &element_info[border_element].change_page[j];
8191 if (change->can_change &&
8192 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8193 change->sides & center_side &&
8195 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8197 change->trigger_element == center_element
8201 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8202 CE_OTHER_IS_TOUCHING, j);
8209 if (change_center_element)
8210 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8211 CE_OTHER_IS_TOUCHING, center_element_change_page);
8214 void TestIfElementHitsCustomElement(int x, int y, int direction)
8216 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8217 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8218 int hitx = x + dx, hity = y + dy;
8219 int hitting_element = Feld[x][y];
8221 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8222 !IS_FREE(hitx, hity) &&
8223 (!IS_MOVING(hitx, hity) ||
8224 MovDir[hitx][hity] != direction ||
8225 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8228 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8232 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8236 CheckElementSideChange(x, y, hitting_element,
8237 direction, CE_HITTING_SOMETHING, -1);
8239 if (IN_LEV_FIELD(hitx, hity))
8241 static int opposite_directions[] =
8248 int move_dir_bit = MV_DIR_BIT(direction);
8249 int opposite_direction = opposite_directions[move_dir_bit];
8250 int hitting_side = direction;
8251 int touched_side = opposite_direction;
8252 int touched_element = MovingOrBlocked2Element(hitx, hity);
8254 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8255 MovDir[hitx][hity] != direction ||
8256 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8265 CheckElementSideChange(hitx, hity, touched_element,
8266 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8268 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8269 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8271 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8273 struct ElementChangeInfo *change =
8274 &element_info[hitting_element].change_page[i];
8276 if (change->can_change &&
8277 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8278 change->sides & touched_side &&
8281 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8283 change->trigger_element == touched_element
8287 CheckElementSideChange(x, y, hitting_element,
8288 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8294 if (IS_CUSTOM_ELEMENT(touched_element) &&
8295 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8297 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8299 struct ElementChangeInfo *change =
8300 &element_info[touched_element].change_page[i];
8302 if (change->can_change &&
8303 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8304 change->sides & hitting_side &&
8306 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8308 change->trigger_element == hitting_element
8312 CheckElementSideChange(hitx, hity, touched_element,
8313 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8322 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8324 int i, kill_x = -1, kill_y = -1;
8325 static int test_xy[4][2] =
8332 static int test_dir[4] =
8340 for (i = 0; i < 4; i++)
8342 int test_x, test_y, test_move_dir, test_element;
8344 test_x = good_x + test_xy[i][0];
8345 test_y = good_y + test_xy[i][1];
8346 if (!IN_LEV_FIELD(test_x, test_y))
8350 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8353 test_element = Feld[test_x][test_y];
8355 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8358 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8359 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8361 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8362 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8370 if (kill_x != -1 || kill_y != -1)
8372 if (IS_PLAYER(good_x, good_y))
8374 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8376 if (player->shield_deadly_time_left > 0)
8377 Bang(kill_x, kill_y);
8378 else if (!PLAYER_PROTECTED(good_x, good_y))
8382 Bang(good_x, good_y);
8386 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8388 int i, kill_x = -1, kill_y = -1;
8389 int bad_element = Feld[bad_x][bad_y];
8390 static int test_xy[4][2] =
8397 static int touch_dir[4] =
8404 static int test_dir[4] =
8412 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8415 for (i = 0; i < 4; i++)
8417 int test_x, test_y, test_move_dir, test_element;
8419 test_x = bad_x + test_xy[i][0];
8420 test_y = bad_y + test_xy[i][1];
8421 if (!IN_LEV_FIELD(test_x, test_y))
8425 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8427 test_element = Feld[test_x][test_y];
8429 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8430 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8432 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8433 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8435 /* good thing is player or penguin that does not move away */
8436 if (IS_PLAYER(test_x, test_y))
8438 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8440 if (bad_element == EL_ROBOT && player->is_moving)
8441 continue; /* robot does not kill player if he is moving */
8443 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8445 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8446 continue; /* center and border element do not touch */
8453 else if (test_element == EL_PENGUIN)
8462 if (kill_x != -1 || kill_y != -1)
8464 if (IS_PLAYER(kill_x, kill_y))
8466 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8468 if (player->shield_deadly_time_left > 0)
8470 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8474 Bang(kill_x, kill_y);
8478 void TestIfHeroTouchesBadThing(int x, int y)
8480 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8483 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8485 TestIfGoodThingHitsBadThing(x, y, move_dir);
8488 void TestIfBadThingTouchesHero(int x, int y)
8490 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8493 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8495 TestIfBadThingHitsGoodThing(x, y, move_dir);
8498 void TestIfFriendTouchesBadThing(int x, int y)
8500 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8503 void TestIfBadThingTouchesFriend(int x, int y)
8505 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8508 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8510 int i, kill_x = bad_x, kill_y = bad_y;
8511 static int xy[4][2] =
8519 for (i = 0; i < 4; i++)
8523 x = bad_x + xy[i][0];
8524 y = bad_y + xy[i][1];
8525 if (!IN_LEV_FIELD(x, y))
8528 element = Feld[x][y];
8529 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8530 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8538 if (kill_x != bad_x || kill_y != bad_y)
8542 void KillHero(struct PlayerInfo *player)
8544 int jx = player->jx, jy = player->jy;
8546 if (!player->active)
8549 /* remove accessible field at the player's position */
8550 Feld[jx][jy] = EL_EMPTY;
8552 /* deactivate shield (else Bang()/Explode() would not work right) */
8553 player->shield_normal_time_left = 0;
8554 player->shield_deadly_time_left = 0;
8560 static void KillHeroUnlessProtected(int x, int y)
8562 if (!PLAYER_PROTECTED(x, y))
8563 KillHero(PLAYERINFO(x, y));
8566 void BuryHero(struct PlayerInfo *player)
8568 int jx = player->jx, jy = player->jy;
8570 if (!player->active)
8574 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8576 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8578 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8580 player->GameOver = TRUE;
8584 void RemoveHero(struct PlayerInfo *player)
8586 int jx = player->jx, jy = player->jy;
8587 int i, found = FALSE;
8589 player->present = FALSE;
8590 player->active = FALSE;
8592 if (!ExplodeField[jx][jy])
8593 StorePlayer[jx][jy] = 0;
8595 for (i = 0; i < MAX_PLAYERS; i++)
8596 if (stored_player[i].active)
8600 AllPlayersGone = TRUE;
8607 =============================================================================
8608 checkDiagonalPushing()
8609 -----------------------------------------------------------------------------
8610 check if diagonal input device direction results in pushing of object
8611 (by checking if the alternative direction is walkable, diggable, ...)
8612 =============================================================================
8615 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8616 int x, int y, int real_dx, int real_dy)
8618 int jx, jy, dx, dy, xx, yy;
8620 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8623 /* diagonal direction: check alternative direction */
8628 xx = jx + (dx == 0 ? real_dx : 0);
8629 yy = jy + (dy == 0 ? real_dy : 0);
8631 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8635 =============================================================================
8637 -----------------------------------------------------------------------------
8638 x, y: field next to player (non-diagonal) to try to dig to
8639 real_dx, real_dy: direction as read from input device (can be diagonal)
8640 =============================================================================
8643 int DigField(struct PlayerInfo *player,
8644 int x, int y, int real_dx, int real_dy, int mode)
8646 static int change_sides[4] =
8648 CH_SIDE_RIGHT, /* moving left */
8649 CH_SIDE_LEFT, /* moving right */
8650 CH_SIDE_BOTTOM, /* moving up */
8651 CH_SIDE_TOP, /* moving down */
8653 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8654 int jx = player->jx, jy = player->jy;
8655 int dx = x - jx, dy = y - jy;
8656 int nextx = x + dx, nexty = y + dy;
8657 int move_direction = (dx == -1 ? MV_LEFT :
8658 dx == +1 ? MV_RIGHT :
8660 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8661 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8664 if (player->MovPos == 0)
8666 player->is_digging = FALSE;
8667 player->is_collecting = FALSE;
8670 if (player->MovPos == 0) /* last pushing move finished */
8671 player->is_pushing = FALSE;
8673 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8675 player->is_switching = FALSE;
8676 player->push_delay = 0;
8678 return MF_NO_ACTION;
8681 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8682 return MF_NO_ACTION;
8685 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8687 if (IS_TUBE(Feld[jx][jy]) ||
8688 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8692 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8693 int tube_leave_directions[][2] =
8695 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8696 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8697 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8698 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8699 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8700 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8701 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8702 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8703 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8704 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8705 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8706 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8709 while (tube_leave_directions[i][0] != tube_element)
8712 if (tube_leave_directions[i][0] == -1) /* should not happen */
8716 if (!(tube_leave_directions[i][1] & move_direction))
8717 return MF_NO_ACTION; /* tube has no opening in this direction */
8720 element = Feld[x][y];
8722 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8723 game.engine_version >= VERSION_IDENT(2,2,0,0))
8724 return MF_NO_ACTION;
8728 case EL_SP_PORT_LEFT:
8729 case EL_SP_PORT_RIGHT:
8731 case EL_SP_PORT_DOWN:
8732 case EL_SP_PORT_HORIZONTAL:
8733 case EL_SP_PORT_VERTICAL:
8734 case EL_SP_PORT_ANY:
8735 case EL_SP_GRAVITY_PORT_LEFT:
8736 case EL_SP_GRAVITY_PORT_RIGHT:
8737 case EL_SP_GRAVITY_PORT_UP:
8738 case EL_SP_GRAVITY_PORT_DOWN:
8740 element != EL_SP_PORT_LEFT &&
8741 element != EL_SP_GRAVITY_PORT_LEFT &&
8742 element != EL_SP_PORT_HORIZONTAL &&
8743 element != EL_SP_PORT_ANY) ||
8745 element != EL_SP_PORT_RIGHT &&
8746 element != EL_SP_GRAVITY_PORT_RIGHT &&
8747 element != EL_SP_PORT_HORIZONTAL &&
8748 element != EL_SP_PORT_ANY) ||
8750 element != EL_SP_PORT_UP &&
8751 element != EL_SP_GRAVITY_PORT_UP &&
8752 element != EL_SP_PORT_VERTICAL &&
8753 element != EL_SP_PORT_ANY) ||
8755 element != EL_SP_PORT_DOWN &&
8756 element != EL_SP_GRAVITY_PORT_DOWN &&
8757 element != EL_SP_PORT_VERTICAL &&
8758 element != EL_SP_PORT_ANY) ||
8759 !IN_LEV_FIELD(nextx, nexty) ||
8760 !IS_FREE(nextx, nexty))
8761 return MF_NO_ACTION;
8763 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8764 element == EL_SP_GRAVITY_PORT_RIGHT ||
8765 element == EL_SP_GRAVITY_PORT_UP ||
8766 element == EL_SP_GRAVITY_PORT_DOWN)
8767 game.gravity = !game.gravity;
8769 /* automatically move to the next field with double speed */
8770 player->programmed_action = move_direction;
8772 if (player->move_delay_reset_counter == 0)
8774 player->move_delay_reset_counter = 2; /* two double speed steps */
8776 DOUBLE_PLAYER_SPEED(player);
8779 player->move_delay_reset_counter = 2;
8781 DOUBLE_PLAYER_SPEED(player);
8784 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8788 case EL_TUBE_VERTICAL:
8789 case EL_TUBE_HORIZONTAL:
8790 case EL_TUBE_VERTICAL_LEFT:
8791 case EL_TUBE_VERTICAL_RIGHT:
8792 case EL_TUBE_HORIZONTAL_UP:
8793 case EL_TUBE_HORIZONTAL_DOWN:
8794 case EL_TUBE_LEFT_UP:
8795 case EL_TUBE_LEFT_DOWN:
8796 case EL_TUBE_RIGHT_UP:
8797 case EL_TUBE_RIGHT_DOWN:
8800 int tube_enter_directions[][2] =
8802 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8803 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8804 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8805 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8806 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8807 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8808 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8809 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8810 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8811 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8812 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8813 { -1, MV_NO_MOVING }
8816 while (tube_enter_directions[i][0] != element)
8819 if (tube_enter_directions[i][0] == -1) /* should not happen */
8823 if (!(tube_enter_directions[i][1] & move_direction))
8824 return MF_NO_ACTION; /* tube has no opening in this direction */
8826 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8832 if (IS_WALKABLE(element))
8834 int sound_action = ACTION_WALKING;
8836 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8838 if (!player->key[element - EL_GATE_1])
8839 return MF_NO_ACTION;
8841 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8843 if (!player->key[element - EL_GATE_1_GRAY])
8844 return MF_NO_ACTION;
8846 else if (element == EL_EXIT_OPEN ||
8847 element == EL_SP_EXIT_OPEN ||
8848 element == EL_SP_EXIT_OPENING)
8850 sound_action = ACTION_PASSING; /* player is passing exit */
8852 else if (element == EL_EMPTY)
8854 sound_action = ACTION_MOVING; /* nothing to walk on */
8857 /* play sound from background or player, whatever is available */
8858 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8859 PlayLevelSoundElementAction(x, y, element, sound_action);
8861 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8865 else if (IS_PASSABLE(element))
8867 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8868 return MF_NO_ACTION;
8871 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8872 return MF_NO_ACTION;
8875 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8877 if (!player->key[element - EL_EM_GATE_1])
8878 return MF_NO_ACTION;
8880 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8882 if (!player->key[element - EL_EM_GATE_1_GRAY])
8883 return MF_NO_ACTION;
8886 /* automatically move to the next field with double speed */
8887 player->programmed_action = move_direction;
8889 if (player->move_delay_reset_counter == 0)
8891 player->move_delay_reset_counter = 2; /* two double speed steps */
8893 DOUBLE_PLAYER_SPEED(player);
8896 player->move_delay_reset_counter = 2;
8898 DOUBLE_PLAYER_SPEED(player);
8901 PlayLevelSoundAction(x, y, ACTION_PASSING);
8905 else if (IS_DIGGABLE(element))
8909 if (mode != DF_SNAP)
8912 GfxElement[x][y] = GFX_ELEMENT(element);
8915 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8917 player->is_digging = TRUE;
8920 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8922 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8925 if (mode == DF_SNAP)
8926 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8931 else if (IS_COLLECTIBLE(element))
8935 if (mode != DF_SNAP)
8937 GfxElement[x][y] = element;
8938 player->is_collecting = TRUE;
8941 if (element == EL_SPEED_PILL)
8942 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8943 else if (element == EL_EXTRA_TIME && level.time > 0)
8946 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8948 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8950 player->shield_normal_time_left += 10;
8951 if (element == EL_SHIELD_DEADLY)
8952 player->shield_deadly_time_left += 10;
8954 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8956 if (player->inventory_size < MAX_INVENTORY_SIZE)
8957 player->inventory_element[player->inventory_size++] = element;
8959 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8960 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8962 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8964 player->dynabomb_count++;
8965 player->dynabombs_left++;
8967 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8969 player->dynabomb_size++;
8971 else if (element == EL_DYNABOMB_INCREASE_POWER)
8973 player->dynabomb_xl = TRUE;
8975 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8976 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8978 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8979 element - EL_KEY_1 : element - EL_EM_KEY_1);
8981 player->key[key_nr] = TRUE;
8983 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8984 el2edimg(EL_KEY_1 + key_nr));
8985 redraw_mask |= REDRAW_DOOR_1;
8987 else if (IS_ENVELOPE(element))
8990 player->show_envelope = element;
8992 ShowEnvelope(element - EL_ENVELOPE_1);
8995 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8999 for (i = 0; i < element_info[element].collect_count; i++)
9000 if (player->inventory_size < MAX_INVENTORY_SIZE)
9001 player->inventory_element[player->inventory_size++] = element;
9003 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9004 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9006 else if (element_info[element].collect_count > 0)
9008 local_player->gems_still_needed -=
9009 element_info[element].collect_count;
9010 if (local_player->gems_still_needed < 0)
9011 local_player->gems_still_needed = 0;
9013 DrawText(DX_EMERALDS, DY_EMERALDS,
9014 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9017 RaiseScoreElement(element);
9018 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9020 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9023 if (mode == DF_SNAP)
9024 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9029 else if (IS_PUSHABLE(element))
9031 if (mode == DF_SNAP && element != EL_BD_ROCK)
9032 return MF_NO_ACTION;
9034 if (CAN_FALL(element) && dy)
9035 return MF_NO_ACTION;
9037 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9038 !(element == EL_SPRING && use_spring_bug))
9039 return MF_NO_ACTION;
9042 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9043 ((move_direction & MV_VERTICAL &&
9044 ((element_info[element].move_pattern & MV_LEFT &&
9045 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9046 (element_info[element].move_pattern & MV_RIGHT &&
9047 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9048 (move_direction & MV_HORIZONTAL &&
9049 ((element_info[element].move_pattern & MV_UP &&
9050 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9051 (element_info[element].move_pattern & MV_DOWN &&
9052 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9053 return MF_NO_ACTION;
9057 /* do not push elements already moving away faster than player */
9058 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9059 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9060 return MF_NO_ACTION;
9062 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9063 return MF_NO_ACTION;
9067 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9069 if (player->push_delay_value == -1)
9070 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9072 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9074 if (!player->is_pushing)
9075 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9079 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9080 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9081 !player_is_pushing))
9082 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9085 if (!player->is_pushing &&
9086 game.engine_version >= VERSION_IDENT(2,2,0,7))
9087 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9091 printf("::: push delay: %ld [%d, %d] [%d]\n",
9092 player->push_delay_value, FrameCounter, game.engine_version,
9093 player->is_pushing);
9096 player->is_pushing = TRUE;
9098 if (!(IN_LEV_FIELD(nextx, nexty) &&
9099 (IS_FREE(nextx, nexty) ||
9100 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9101 IS_SB_ELEMENT(element)))))
9102 return MF_NO_ACTION;
9104 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9105 return MF_NO_ACTION;
9107 if (player->push_delay == 0) /* new pushing; restart delay */
9108 player->push_delay = FrameCounter;
9110 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9111 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9112 element != EL_SPRING && element != EL_BALLOON)
9114 /* make sure that there is no move delay before next try to push */
9115 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9116 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9118 return MF_NO_ACTION;
9122 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9125 if (IS_SB_ELEMENT(element))
9127 if (element == EL_SOKOBAN_FIELD_FULL)
9129 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9130 local_player->sokobanfields_still_needed++;
9133 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9135 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9136 local_player->sokobanfields_still_needed--;
9139 Feld[x][y] = EL_SOKOBAN_OBJECT;
9141 if (Back[x][y] == Back[nextx][nexty])
9142 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9143 else if (Back[x][y] != 0)
9144 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9147 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9150 if (local_player->sokobanfields_still_needed == 0 &&
9151 game.emulation == EMU_SOKOBAN)
9153 player->LevelSolved = player->GameOver = TRUE;
9154 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9158 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9160 InitMovingField(x, y, move_direction);
9161 GfxAction[x][y] = ACTION_PUSHING;
9163 if (mode == DF_SNAP)
9164 ContinueMoving(x, y);
9166 MovPos[x][y] = (dx != 0 ? dx : dy);
9168 Pushed[x][y] = TRUE;
9169 Pushed[nextx][nexty] = TRUE;
9171 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9172 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9174 player->push_delay_value = -1; /* get new value later */
9176 CheckTriggeredElementSideChange(x, y, element, dig_side,
9177 CE_OTHER_GETS_PUSHED);
9178 CheckElementSideChange(x, y, element, dig_side,
9179 CE_PUSHED_BY_PLAYER, -1);
9183 else if (IS_SWITCHABLE(element))
9185 if (PLAYER_SWITCHING(player, x, y))
9188 player->is_switching = TRUE;
9189 player->switch_x = x;
9190 player->switch_y = y;
9192 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9194 if (element == EL_ROBOT_WHEEL)
9196 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9200 DrawLevelField(x, y);
9202 else if (element == EL_SP_TERMINAL)
9206 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9208 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9210 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9211 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9214 else if (IS_BELT_SWITCH(element))
9216 ToggleBeltSwitch(x, y);
9218 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9219 element == EL_SWITCHGATE_SWITCH_DOWN)
9221 ToggleSwitchgateSwitch(x, y);
9223 else if (element == EL_LIGHT_SWITCH ||
9224 element == EL_LIGHT_SWITCH_ACTIVE)
9226 ToggleLightSwitch(x, y);
9229 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9230 SND_LIGHT_SWITCH_ACTIVATING :
9231 SND_LIGHT_SWITCH_DEACTIVATING);
9234 else if (element == EL_TIMEGATE_SWITCH)
9236 ActivateTimegateSwitch(x, y);
9238 else if (element == EL_BALLOON_SWITCH_LEFT ||
9239 element == EL_BALLOON_SWITCH_RIGHT ||
9240 element == EL_BALLOON_SWITCH_UP ||
9241 element == EL_BALLOON_SWITCH_DOWN ||
9242 element == EL_BALLOON_SWITCH_ANY)
9244 if (element == EL_BALLOON_SWITCH_ANY)
9245 game.balloon_dir = move_direction;
9247 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9248 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9249 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9250 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9253 else if (element == EL_LAMP)
9255 Feld[x][y] = EL_LAMP_ACTIVE;
9256 local_player->lights_still_needed--;
9258 DrawLevelField(x, y);
9260 else if (element == EL_TIME_ORB_FULL)
9262 Feld[x][y] = EL_TIME_ORB_EMPTY;
9264 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9266 DrawLevelField(x, y);
9269 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9277 if (!PLAYER_SWITCHING(player, x, y))
9279 player->is_switching = TRUE;
9280 player->switch_x = x;
9281 player->switch_y = y;
9283 CheckTriggeredElementSideChange(x, y, element, dig_side,
9284 CE_OTHER_IS_SWITCHING);
9285 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9288 CheckTriggeredElementSideChange(x, y, element, dig_side,
9289 CE_OTHER_GETS_PRESSED);
9290 CheckElementSideChange(x, y, element, dig_side,
9291 CE_PRESSED_BY_PLAYER, -1);
9294 return MF_NO_ACTION;
9297 player->push_delay = 0;
9299 if (Feld[x][y] != element) /* really digged/collected something */
9300 player->is_collecting = !player->is_digging;
9305 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9307 int jx = player->jx, jy = player->jy;
9308 int x = jx + dx, y = jy + dy;
9309 int snap_direction = (dx == -1 ? MV_LEFT :
9310 dx == +1 ? MV_RIGHT :
9312 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9314 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9317 if (!player->active || !IN_LEV_FIELD(x, y))
9325 if (player->MovPos == 0)
9326 player->is_pushing = FALSE;
9328 player->is_snapping = FALSE;
9330 if (player->MovPos == 0)
9332 player->is_moving = FALSE;
9333 player->is_digging = FALSE;
9334 player->is_collecting = FALSE;
9340 if (player->is_snapping)
9343 player->MovDir = snap_direction;
9346 if (player->MovPos == 0)
9349 player->is_moving = FALSE;
9350 player->is_digging = FALSE;
9351 player->is_collecting = FALSE;
9354 player->is_dropping = FALSE;
9356 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9359 player->is_snapping = TRUE;
9362 if (player->MovPos == 0)
9365 player->is_moving = FALSE;
9366 player->is_digging = FALSE;
9367 player->is_collecting = FALSE;
9370 DrawLevelField(x, y);
9376 boolean DropElement(struct PlayerInfo *player)
9378 int jx = player->jx, jy = player->jy;
9379 int old_element = Feld[jx][jy];
9382 /* check if player is active, not moving and ready to drop */
9383 if (!player->active || player->MovPos || player->drop_delay > 0)
9386 /* check if player has anything that can be dropped */
9387 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9390 /* check if anything can be dropped at the current position */
9391 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9394 /* collected custom elements can only be dropped on empty fields */
9395 if (player->inventory_size > 0 &&
9396 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9397 && old_element != EL_EMPTY)
9400 if (old_element != EL_EMPTY)
9401 Back[jx][jy] = old_element; /* store old element on this field */
9403 ResetGfxAnimation(jx, jy);
9404 ResetRandomAnimationValue(jx, jy);
9406 if (player->inventory_size > 0)
9408 player->inventory_size--;
9409 new_element = player->inventory_element[player->inventory_size];
9411 if (new_element == EL_DYNAMITE)
9412 new_element = EL_DYNAMITE_ACTIVE;
9413 else if (new_element == EL_SP_DISK_RED)
9414 new_element = EL_SP_DISK_RED_ACTIVE;
9416 Feld[jx][jy] = new_element;
9418 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9419 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9421 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9422 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9424 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9426 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9427 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9429 TestIfElementTouchesCustomElement(jx, jy);
9431 else /* player is dropping a dyna bomb */
9433 player->dynabombs_left--;
9434 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9436 Feld[jx][jy] = new_element;
9438 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9439 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9441 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9448 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9450 InitField(jx, jy, FALSE);
9451 if (CAN_MOVE(Feld[jx][jy]))
9455 new_element = Feld[jx][jy];
9457 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9458 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9460 int move_stepsize = element_info[new_element].move_stepsize;
9461 int direction, dx, dy, nextx, nexty;
9463 if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
9464 MovDir[jx][jy] = player->MovDir;
9466 direction = MovDir[jx][jy];
9467 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9468 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9472 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9475 WasJustMoving[jx][jy] = 3;
9477 InitMovingField(jx, jy, direction);
9478 ContinueMoving(jx, jy);
9483 Changed[jx][jy] = 0; /* allow another change */
9486 TestIfElementHitsCustomElement(jx, jy, direction);
9488 CheckElementSideChange(jx, jy, new_element,
9489 direction, CE_HITTING_SOMETHING, -1);
9493 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9497 player->drop_delay = 8 + 8 + 8;
9502 player->is_dropping = TRUE;
9508 /* ------------------------------------------------------------------------- */
9509 /* game sound playing functions */
9510 /* ------------------------------------------------------------------------- */
9512 static int *loop_sound_frame = NULL;
9513 static int *loop_sound_volume = NULL;
9515 void InitPlayLevelSound()
9517 int num_sounds = getSoundListSize();
9519 checked_free(loop_sound_frame);
9520 checked_free(loop_sound_volume);
9522 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9523 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9526 static void PlayLevelSound(int x, int y, int nr)
9528 int sx = SCREENX(x), sy = SCREENY(y);
9529 int volume, stereo_position;
9530 int max_distance = 8;
9531 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9533 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9534 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9537 if (!IN_LEV_FIELD(x, y) ||
9538 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9539 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9542 volume = SOUND_MAX_VOLUME;
9544 if (!IN_SCR_FIELD(sx, sy))
9546 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9547 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9549 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9552 stereo_position = (SOUND_MAX_LEFT +
9553 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9554 (SCR_FIELDX + 2 * max_distance));
9556 if (IS_LOOP_SOUND(nr))
9558 /* This assures that quieter loop sounds do not overwrite louder ones,
9559 while restarting sound volume comparison with each new game frame. */
9561 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9564 loop_sound_volume[nr] = volume;
9565 loop_sound_frame[nr] = FrameCounter;
9568 PlaySoundExt(nr, volume, stereo_position, type);
9571 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9573 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9574 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9575 y < LEVELY(BY1) ? LEVELY(BY1) :
9576 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9580 static void PlayLevelSoundAction(int x, int y, int action)
9582 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9585 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9587 int sound_effect = element_info[element].sound[action];
9589 if (sound_effect != SND_UNDEFINED)
9590 PlayLevelSound(x, y, sound_effect);
9593 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9596 int sound_effect = element_info[element].sound[action];
9598 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9599 PlayLevelSound(x, y, sound_effect);
9602 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9604 int sound_effect = element_info[Feld[x][y]].sound[action];
9606 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9607 PlayLevelSound(x, y, sound_effect);
9610 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9612 int sound_effect = element_info[Feld[x][y]].sound[action];
9614 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9615 StopSound(sound_effect);
9618 static void PlayLevelMusic()
9620 if (levelset.music[level_nr] != MUS_UNDEFINED)
9621 PlayMusic(levelset.music[level_nr]); /* from config file */
9623 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9626 void RaiseScore(int value)
9628 local_player->score += value;
9629 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9632 void RaiseScoreElement(int element)
9638 case EL_EMERALD_YELLOW:
9639 case EL_EMERALD_RED:
9640 case EL_EMERALD_PURPLE:
9641 case EL_SP_INFOTRON:
9642 RaiseScore(level.score[SC_EMERALD]);
9645 RaiseScore(level.score[SC_DIAMOND]);
9648 RaiseScore(level.score[SC_CRYSTAL]);
9651 RaiseScore(level.score[SC_PEARL]);
9654 case EL_BD_BUTTERFLY:
9655 case EL_SP_ELECTRON:
9656 RaiseScore(level.score[SC_BUG]);
9660 case EL_SP_SNIKSNAK:
9661 RaiseScore(level.score[SC_SPACESHIP]);
9664 case EL_DARK_YAMYAM:
9665 RaiseScore(level.score[SC_YAMYAM]);
9668 RaiseScore(level.score[SC_ROBOT]);
9671 RaiseScore(level.score[SC_PACMAN]);
9674 RaiseScore(level.score[SC_NUT]);
9677 case EL_SP_DISK_RED:
9678 case EL_DYNABOMB_INCREASE_NUMBER:
9679 case EL_DYNABOMB_INCREASE_SIZE:
9680 case EL_DYNABOMB_INCREASE_POWER:
9681 RaiseScore(level.score[SC_DYNAMITE]);
9683 case EL_SHIELD_NORMAL:
9684 case EL_SHIELD_DEADLY:
9685 RaiseScore(level.score[SC_SHIELD]);
9688 RaiseScore(level.score[SC_TIME_BONUS]);
9694 RaiseScore(level.score[SC_KEY]);
9697 RaiseScore(element_info[element].collect_score);
9702 void RequestQuitGame(boolean ask_if_really_quit)
9704 if (AllPlayersGone ||
9705 !ask_if_really_quit ||
9706 level_editor_test_game ||
9707 Request("Do you really want to quit the game ?",
9708 REQ_ASK | REQ_STAY_CLOSED))
9710 #if defined(PLATFORM_UNIX)
9711 if (options.network)
9712 SendToServer_StopPlaying();
9716 game_status = GAME_MODE_MAIN;
9722 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9727 /* ---------- new game button stuff ---------------------------------------- */
9729 /* graphic position values for game buttons */
9730 #define GAME_BUTTON_XSIZE 30
9731 #define GAME_BUTTON_YSIZE 30
9732 #define GAME_BUTTON_XPOS 5
9733 #define GAME_BUTTON_YPOS 215
9734 #define SOUND_BUTTON_XPOS 5
9735 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9737 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9738 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9739 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9740 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9741 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9742 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9749 } gamebutton_info[NUM_GAME_BUTTONS] =
9752 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9757 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9762 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9767 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9768 SOUND_CTRL_ID_MUSIC,
9769 "background music on/off"
9772 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9773 SOUND_CTRL_ID_LOOPS,
9774 "sound loops on/off"
9777 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9778 SOUND_CTRL_ID_SIMPLE,
9779 "normal sounds on/off"
9783 void CreateGameButtons()
9787 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9789 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9790 struct GadgetInfo *gi;
9793 unsigned long event_mask;
9794 int gd_xoffset, gd_yoffset;
9795 int gd_x1, gd_x2, gd_y1, gd_y2;
9798 gd_xoffset = gamebutton_info[i].x;
9799 gd_yoffset = gamebutton_info[i].y;
9800 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9801 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9803 if (id == GAME_CTRL_ID_STOP ||
9804 id == GAME_CTRL_ID_PAUSE ||
9805 id == GAME_CTRL_ID_PLAY)
9807 button_type = GD_TYPE_NORMAL_BUTTON;
9809 event_mask = GD_EVENT_RELEASED;
9810 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9811 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9815 button_type = GD_TYPE_CHECK_BUTTON;
9817 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9818 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9819 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9820 event_mask = GD_EVENT_PRESSED;
9821 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9822 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9825 gi = CreateGadget(GDI_CUSTOM_ID, id,
9826 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9827 GDI_X, DX + gd_xoffset,
9828 GDI_Y, DY + gd_yoffset,
9829 GDI_WIDTH, GAME_BUTTON_XSIZE,
9830 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9831 GDI_TYPE, button_type,
9832 GDI_STATE, GD_BUTTON_UNPRESSED,
9833 GDI_CHECKED, checked,
9834 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9835 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9836 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9837 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9838 GDI_EVENT_MASK, event_mask,
9839 GDI_CALLBACK_ACTION, HandleGameButtons,
9843 Error(ERR_EXIT, "cannot create gadget");
9845 game_gadget[id] = gi;
9849 void FreeGameButtons()
9853 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9854 FreeGadget(game_gadget[i]);
9857 static void MapGameButtons()
9861 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9862 MapGadget(game_gadget[i]);
9865 void UnmapGameButtons()
9869 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9870 UnmapGadget(game_gadget[i]);
9873 static void HandleGameButtons(struct GadgetInfo *gi)
9875 int id = gi->custom_id;
9877 if (game_status != GAME_MODE_PLAYING)
9882 case GAME_CTRL_ID_STOP:
9883 RequestQuitGame(TRUE);
9886 case GAME_CTRL_ID_PAUSE:
9887 if (options.network)
9889 #if defined(PLATFORM_UNIX)
9891 SendToServer_ContinuePlaying();
9893 SendToServer_PausePlaying();
9897 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9900 case GAME_CTRL_ID_PLAY:
9903 #if defined(PLATFORM_UNIX)
9904 if (options.network)
9905 SendToServer_ContinuePlaying();
9909 tape.pausing = FALSE;
9910 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9915 case SOUND_CTRL_ID_MUSIC:
9916 if (setup.sound_music)
9918 setup.sound_music = FALSE;
9921 else if (audio.music_available)
9923 setup.sound = setup.sound_music = TRUE;
9925 SetAudioMode(setup.sound);
9931 case SOUND_CTRL_ID_LOOPS:
9932 if (setup.sound_loops)
9933 setup.sound_loops = FALSE;
9934 else if (audio.loops_available)
9936 setup.sound = setup.sound_loops = TRUE;
9937 SetAudioMode(setup.sound);
9941 case SOUND_CTRL_ID_SIMPLE:
9942 if (setup.sound_simple)
9943 setup.sound_simple = FALSE;
9944 else if (audio.sound_available)
9946 setup.sound = setup.sound_simple = TRUE;
9947 SetAudioMode(setup.sound);