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_ENEMY_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 KillHeroUnlessEnemyProtected(int, int);
220 static void KillHeroUnlessExplosionProtected(int, int);
222 static void TestIfPlayerTouchesCustomElement(int, int);
223 static void TestIfElementTouchesCustomElement(int, int);
224 static void TestIfElementHitsCustomElement(int, int, int);
226 static void ChangeElement(int, int, int);
227 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
228 static boolean CheckTriggeredElementChange(int, int, int, int);
229 static boolean CheckElementSideChange(int, int, int, int, int, int);
230 static boolean CheckElementChange(int, int, int, int);
232 static void PlayLevelSound(int, int, int);
233 static void PlayLevelSoundNearest(int, int, int);
234 static void PlayLevelSoundAction(int, int, int);
235 static void PlayLevelSoundElementAction(int, int, int, int);
236 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
237 static void PlayLevelSoundActionIfLoop(int, int, int);
238 static void StopLevelSoundActionIfLoop(int, int, int);
239 static void PlayLevelMusic();
241 static void MapGameButtons();
242 static void HandleGameButtons(struct GadgetInfo *);
244 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
247 /* ------------------------------------------------------------------------- */
248 /* definition of elements that automatically change to other elements after */
249 /* a specified time, eventually calling a function when changing */
250 /* ------------------------------------------------------------------------- */
252 /* forward declaration for changer functions */
253 static void InitBuggyBase(int x, int y);
254 static void WarnBuggyBase(int x, int y);
256 static void InitTrap(int x, int y);
257 static void ActivateTrap(int x, int y);
258 static void ChangeActiveTrap(int x, int y);
260 static void InitRobotWheel(int x, int y);
261 static void RunRobotWheel(int x, int y);
262 static void StopRobotWheel(int x, int y);
264 static void InitTimegateWheel(int x, int y);
265 static void RunTimegateWheel(int x, int y);
267 struct ChangingElementInfo
272 void (*pre_change_function)(int x, int y);
273 void (*change_function)(int x, int y);
274 void (*post_change_function)(int x, int y);
277 static struct ChangingElementInfo change_delay_list[] =
328 EL_SWITCHGATE_OPENING,
336 EL_SWITCHGATE_CLOSING,
337 EL_SWITCHGATE_CLOSED,
369 EL_ACID_SPLASH_RIGHT,
378 EL_SP_BUGGY_BASE_ACTIVATING,
385 EL_SP_BUGGY_BASE_ACTIVATING,
386 EL_SP_BUGGY_BASE_ACTIVE,
393 EL_SP_BUGGY_BASE_ACTIVE,
417 EL_ROBOT_WHEEL_ACTIVE,
425 EL_TIMEGATE_SWITCH_ACTIVE,
446 int push_delay_fixed, push_delay_random;
451 { EL_BALLOON, 0, 0 },
453 { EL_SOKOBAN_OBJECT, 2, 0 },
454 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
455 { EL_SATELLITE, 2, 0 },
456 { EL_SP_DISK_YELLOW, 2, 0 },
458 { EL_UNDEFINED, 0, 0 },
466 move_stepsize_list[] =
468 { EL_AMOEBA_DROP, 2 },
469 { EL_AMOEBA_DROPPING, 2 },
470 { EL_QUICKSAND_FILLING, 1 },
471 { EL_QUICKSAND_EMPTYING, 1 },
472 { EL_MAGIC_WALL_FILLING, 2 },
473 { EL_BD_MAGIC_WALL_FILLING, 2 },
474 { EL_MAGIC_WALL_EMPTYING, 2 },
475 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
485 collect_count_list[] =
488 { EL_BD_DIAMOND, 1 },
489 { EL_EMERALD_YELLOW, 1 },
490 { EL_EMERALD_RED, 1 },
491 { EL_EMERALD_PURPLE, 1 },
493 { EL_SP_INFOTRON, 1 },
500 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
502 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
503 CH_EVENT_BIT(CE_DELAY))
504 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
505 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
506 IS_JUST_CHANGING(x, y))
508 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
511 void GetPlayerConfig()
513 if (!audio.sound_available)
514 setup.sound_simple = FALSE;
516 if (!audio.loops_available)
517 setup.sound_loops = FALSE;
519 if (!audio.music_available)
520 setup.sound_music = FALSE;
522 if (!video.fullscreen_available)
523 setup.fullscreen = FALSE;
525 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
527 SetAudioMode(setup.sound);
531 static int getBeltNrFromBeltElement(int element)
533 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
534 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
535 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
538 static int getBeltNrFromBeltActiveElement(int element)
540 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
541 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
542 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
545 static int getBeltNrFromBeltSwitchElement(int element)
547 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
548 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
549 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
552 static int getBeltDirNrFromBeltSwitchElement(int element)
554 static int belt_base_element[4] =
556 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
557 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
558 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
559 EL_CONVEYOR_BELT_4_SWITCH_LEFT
562 int belt_nr = getBeltNrFromBeltSwitchElement(element);
563 int belt_dir_nr = element - belt_base_element[belt_nr];
565 return (belt_dir_nr % 3);
568 static int getBeltDirFromBeltSwitchElement(int element)
570 static int belt_move_dir[3] =
577 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
579 return belt_move_dir[belt_dir_nr];
582 static void InitPlayerField(int x, int y, int element, boolean init_game)
584 if (element == EL_SP_MURPHY)
588 if (stored_player[0].present)
590 Feld[x][y] = EL_SP_MURPHY_CLONE;
596 stored_player[0].use_murphy_graphic = TRUE;
599 Feld[x][y] = EL_PLAYER_1;
605 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
606 int jx = player->jx, jy = player->jy;
608 player->present = TRUE;
610 player->block_last_field = (element == EL_SP_MURPHY ?
611 level.sp_block_last_field :
612 level.block_last_field);
614 if (!options.network || player->connected)
616 player->active = TRUE;
618 /* remove potentially duplicate players */
619 if (StorePlayer[jx][jy] == Feld[x][y])
620 StorePlayer[jx][jy] = 0;
622 StorePlayer[x][y] = Feld[x][y];
626 printf("Player %d activated.\n", player->element_nr);
627 printf("[Local player is %d and currently %s.]\n",
628 local_player->element_nr,
629 local_player->active ? "active" : "not active");
633 Feld[x][y] = EL_EMPTY;
634 player->jx = player->last_jx = x;
635 player->jy = player->last_jy = y;
639 static void InitField(int x, int y, boolean init_game)
641 int element = Feld[x][y];
650 InitPlayerField(x, y, element, init_game);
653 case EL_SOKOBAN_FIELD_PLAYER:
654 element = Feld[x][y] = EL_PLAYER_1;
655 InitField(x, y, init_game);
657 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
658 InitField(x, y, init_game);
661 case EL_SOKOBAN_FIELD_EMPTY:
662 local_player->sokobanfields_still_needed++;
666 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
667 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
668 else if (x > 0 && Feld[x-1][y] == EL_ACID)
669 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
670 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
671 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
672 else if (y > 0 && Feld[x][y-1] == EL_ACID)
673 Feld[x][y] = EL_ACID_POOL_BOTTOM;
674 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
675 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
683 case EL_SPACESHIP_RIGHT:
684 case EL_SPACESHIP_UP:
685 case EL_SPACESHIP_LEFT:
686 case EL_SPACESHIP_DOWN:
688 case EL_BD_BUTTERFLY_RIGHT:
689 case EL_BD_BUTTERFLY_UP:
690 case EL_BD_BUTTERFLY_LEFT:
691 case EL_BD_BUTTERFLY_DOWN:
692 case EL_BD_BUTTERFLY:
693 case EL_BD_FIREFLY_RIGHT:
694 case EL_BD_FIREFLY_UP:
695 case EL_BD_FIREFLY_LEFT:
696 case EL_BD_FIREFLY_DOWN:
698 case EL_PACMAN_RIGHT:
722 if (y == lev_fieldy - 1)
724 Feld[x][y] = EL_AMOEBA_GROWING;
725 Store[x][y] = EL_AMOEBA_WET;
729 case EL_DYNAMITE_ACTIVE:
730 case EL_SP_DISK_RED_ACTIVE:
731 case EL_DYNABOMB_PLAYER_1_ACTIVE:
732 case EL_DYNABOMB_PLAYER_2_ACTIVE:
733 case EL_DYNABOMB_PLAYER_3_ACTIVE:
734 case EL_DYNABOMB_PLAYER_4_ACTIVE:
739 local_player->lights_still_needed++;
743 local_player->friends_still_needed++;
748 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
753 Feld[x][y] = EL_EMPTY;
758 case EL_EM_KEY_1_FILE:
759 Feld[x][y] = EL_EM_KEY_1;
761 case EL_EM_KEY_2_FILE:
762 Feld[x][y] = EL_EM_KEY_2;
764 case EL_EM_KEY_3_FILE:
765 Feld[x][y] = EL_EM_KEY_3;
767 case EL_EM_KEY_4_FILE:
768 Feld[x][y] = EL_EM_KEY_4;
772 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
773 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
774 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
775 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
776 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
777 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
778 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
779 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
780 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
781 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
782 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
783 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
786 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
787 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
788 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
790 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
792 game.belt_dir[belt_nr] = belt_dir;
793 game.belt_dir_nr[belt_nr] = belt_dir_nr;
795 else /* more than one switch -- set it like the first switch */
797 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
802 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
804 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
807 case EL_LIGHT_SWITCH_ACTIVE:
809 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
813 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
815 else if (IS_GROUP_ELEMENT(element))
817 struct ElementGroupInfo *group = element_info[element].group;
818 int last_anim_random_frame = gfx.anim_random_frame;
821 if (group->choice_mode == ANIM_RANDOM)
822 gfx.anim_random_frame = RND(group->num_elements_resolved);
824 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
825 group->choice_mode, 0,
828 if (group->choice_mode == ANIM_RANDOM)
829 gfx.anim_random_frame = last_anim_random_frame;
833 Feld[x][y] = group->element_resolved[element_pos];
835 InitField(x, y, init_game);
841 void DrawGameDoorValues()
845 for (i = 0; i < MAX_PLAYERS; i++)
846 for (j = 0; j < 4; j++)
847 if (stored_player[i].key[j])
848 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
849 el2edimg(EL_KEY_1 + j));
851 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
852 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
853 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
854 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
855 DrawText(DX + XX_SCORE, DY + YY_SCORE,
856 int2str(local_player->score, 5), FONT_TEXT_2);
857 DrawText(DX + XX_TIME, DY + YY_TIME,
858 int2str(TimeLeft, 3), FONT_TEXT_2);
861 static void resolve_group_element(int group_element, int recursion_depth)
864 static struct ElementGroupInfo *group;
865 struct ElementGroupInfo *actual_group = element_info[group_element].group;
868 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
870 Error(ERR_WARN, "recursion too deep when resolving group element %d",
871 group_element - EL_GROUP_START + 1);
873 /* replace element which caused too deep recursion by question mark */
874 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
879 if (recursion_depth == 0) /* initialization */
881 group = element_info[group_element].group;
882 group_nr = group_element - EL_GROUP_START;
884 group->num_elements_resolved = 0;
885 group->choice_pos = 0;
888 for (i = 0; i < actual_group->num_elements; i++)
890 int element = actual_group->element[i];
892 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
895 if (IS_GROUP_ELEMENT(element))
896 resolve_group_element(element, recursion_depth + 1);
899 group->element_resolved[group->num_elements_resolved++] = element;
900 element_info[element].in_group[group_nr] = TRUE;
905 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
907 printf("::: group %d: %d resolved elements\n",
908 group_element - EL_GROUP_START, group->num_elements_resolved);
909 for (i = 0; i < group->num_elements_resolved; i++)
910 printf("::: - %d ['%s']\n", group->element_resolved[i],
911 element_info[group->element_resolved[i]].token_name);
918 =============================================================================
920 -----------------------------------------------------------------------------
921 initialize game engine due to level / tape version number
922 =============================================================================
925 static void InitGameEngine()
929 /* set game engine from tape file when re-playing, else from level file */
930 game.engine_version = (tape.playing ? tape.engine_version :
933 /* dynamically adjust element properties according to game engine version */
934 InitElementPropertiesEngine(game.engine_version);
937 printf("level %d: level version == %06d\n", level_nr, level.game_version);
938 printf(" tape version == %06d [%s] [file: %06d]\n",
939 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
941 printf(" => game.engine_version == %06d\n", game.engine_version);
944 /* ---------- recursively resolve group elements ------------------------- */
946 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
947 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
948 element_info[i].in_group[j] = FALSE;
950 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
951 resolve_group_element(EL_GROUP_START + i, 0);
953 /* ---------- initialize player's initial move delay --------------------- */
955 /* dynamically adjust player properties according to game engine version */
956 game.initial_move_delay =
957 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
958 INITIAL_MOVE_DELAY_OFF);
960 /* dynamically adjust player properties according to level information */
961 game.initial_move_delay_value =
962 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
964 /* ---------- initialize player's initial push delay --------------------- */
966 /* dynamically adjust player properties according to game engine version */
967 game.initial_push_delay_value =
968 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
970 /* ---------- initialize changing elements ------------------------------- */
972 /* initialize changing elements information */
973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
975 struct ElementInfo *ei = &element_info[i];
977 /* this pointer might have been changed in the level editor */
978 ei->change = &ei->change_page[0];
980 if (!IS_CUSTOM_ELEMENT(i))
982 ei->change->target_element = EL_EMPTY_SPACE;
983 ei->change->delay_fixed = 0;
984 ei->change->delay_random = 0;
985 ei->change->delay_frames = 1;
988 ei->change_events = CE_BITMASK_DEFAULT;
989 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
991 ei->event_page_nr[j] = 0;
992 ei->event_page[j] = &ei->change_page[0];
996 /* add changing elements from pre-defined list */
997 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
999 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1000 struct ElementInfo *ei = &element_info[ch_delay->element];
1002 ei->change->target_element = ch_delay->target_element;
1003 ei->change->delay_fixed = ch_delay->change_delay;
1005 ei->change->pre_change_function = ch_delay->pre_change_function;
1006 ei->change->change_function = ch_delay->change_function;
1007 ei->change->post_change_function = ch_delay->post_change_function;
1009 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1013 /* add change events from custom element configuration */
1014 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1016 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1018 for (j = 0; j < ei->num_change_pages; j++)
1020 if (!ei->change_page[j].can_change)
1023 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1025 /* only add event page for the first page found with this event */
1026 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1027 !(ei->change_events & CH_EVENT_BIT(k)))
1029 ei->change_events |= CH_EVENT_BIT(k);
1030 ei->event_page_nr[k] = j;
1031 ei->event_page[k] = &ei->change_page[j];
1039 /* add change events from custom element configuration */
1040 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1042 int element = EL_CUSTOM_START + i;
1044 /* only add custom elements that change after fixed/random frame delay */
1045 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1046 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1050 /* ---------- initialize trigger events ---------------------------------- */
1052 /* initialize trigger events information */
1053 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1054 trigger_events[i] = EP_BITMASK_DEFAULT;
1057 /* add trigger events from element change event properties */
1058 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1060 struct ElementInfo *ei = &element_info[i];
1062 for (j = 0; j < ei->num_change_pages; j++)
1064 if (!ei->change_page[j].can_change)
1067 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1069 int trigger_element = ei->change_page[j].trigger_element;
1071 if (IS_GROUP_ELEMENT(trigger_element))
1073 struct ElementGroupInfo *group = element_info[trigger_element].group;
1075 for (k = 0; k < group->num_elements_resolved; k++)
1076 trigger_events[group->element_resolved[k]]
1077 |= ei->change_page[j].events;
1080 trigger_events[trigger_element] |= ei->change_page[j].events;
1085 /* add trigger events from element change event properties */
1086 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1087 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1088 trigger_events[element_info[i].change->trigger_element] |=
1089 element_info[i].change->events;
1092 /* ---------- initialize push delay -------------------------------------- */
1094 /* initialize push delay values to default */
1095 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1097 if (!IS_CUSTOM_ELEMENT(i))
1099 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1100 element_info[i].push_delay_random = game.default_push_delay_random;
1104 /* set push delay value for certain elements from pre-defined list */
1105 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1107 int e = push_delay_list[i].element;
1109 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1110 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1113 /* set push delay value for Supaplex elements for newer engine versions */
1114 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1118 if (IS_SP_ELEMENT(i))
1120 element_info[i].push_delay_fixed = 6;
1121 element_info[i].push_delay_random = 0;
1126 /* ---------- initialize move stepsize ----------------------------------- */
1128 /* initialize move stepsize values to default */
1129 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1130 if (!IS_CUSTOM_ELEMENT(i))
1131 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1133 /* set move stepsize value for certain elements from pre-defined list */
1134 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1136 int e = move_stepsize_list[i].element;
1138 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1141 /* ---------- initialize move dig/leave ---------------------------------- */
1143 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1145 element_info[i].can_leave_element = FALSE;
1146 element_info[i].can_leave_element_last = FALSE;
1149 /* ---------- initialize gem count --------------------------------------- */
1151 /* initialize gem count values for each element */
1152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1153 if (!IS_CUSTOM_ELEMENT(i))
1154 element_info[i].collect_count = 0;
1156 /* add gem count values for all elements from pre-defined list */
1157 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1158 element_info[collect_count_list[i].element].collect_count =
1159 collect_count_list[i].count;
1164 =============================================================================
1166 -----------------------------------------------------------------------------
1167 initialize and start new game
1168 =============================================================================
1173 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1174 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1175 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1182 #if USE_NEW_AMOEBA_CODE
1183 printf("Using new amoeba code.\n");
1185 printf("Using old amoeba code.\n");
1190 /* don't play tapes over network */
1191 network_playing = (options.network && !tape.playing);
1193 for (i = 0; i < MAX_PLAYERS; i++)
1195 struct PlayerInfo *player = &stored_player[i];
1197 player->index_nr = i;
1198 player->element_nr = EL_PLAYER_1 + i;
1200 player->present = FALSE;
1201 player->active = FALSE;
1204 player->effective_action = 0;
1205 player->programmed_action = 0;
1208 player->gems_still_needed = level.gems_needed;
1209 player->sokobanfields_still_needed = 0;
1210 player->lights_still_needed = 0;
1211 player->friends_still_needed = 0;
1213 for (j = 0; j < 4; j++)
1214 player->key[j] = FALSE;
1216 player->dynabomb_count = 0;
1217 player->dynabomb_size = 1;
1218 player->dynabombs_left = 0;
1219 player->dynabomb_xl = FALSE;
1221 player->MovDir = MV_NO_MOVING;
1224 player->GfxDir = MV_NO_MOVING;
1225 player->GfxAction = ACTION_DEFAULT;
1227 player->StepFrame = 0;
1229 player->use_murphy_graphic = FALSE;
1231 player->block_last_field = FALSE;
1233 player->actual_frame_counter = 0;
1235 player->step_counter = 0;
1237 player->last_move_dir = MV_NO_MOVING;
1239 player->is_waiting = FALSE;
1240 player->is_moving = FALSE;
1241 player->is_digging = FALSE;
1242 player->is_snapping = FALSE;
1243 player->is_collecting = FALSE;
1244 player->is_pushing = FALSE;
1245 player->is_switching = FALSE;
1246 player->is_dropping = FALSE;
1248 player->is_bored = FALSE;
1249 player->is_sleeping = FALSE;
1251 player->frame_counter_bored = -1;
1252 player->frame_counter_sleeping = -1;
1254 player->anim_delay_counter = 0;
1255 player->post_delay_counter = 0;
1257 player->action_waiting = ACTION_DEFAULT;
1258 player->last_action_waiting = ACTION_DEFAULT;
1259 player->special_action_bored = ACTION_DEFAULT;
1260 player->special_action_sleeping = ACTION_DEFAULT;
1262 player->num_special_action_bored = 0;
1263 player->num_special_action_sleeping = 0;
1265 /* determine number of special actions for bored and sleeping animation */
1266 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1268 boolean found = FALSE;
1270 for (k = 0; k < NUM_DIRECTIONS; k++)
1271 if (el_act_dir2img(player->element_nr, j, k) !=
1272 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1276 player->num_special_action_bored++;
1280 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1282 boolean found = FALSE;
1284 for (k = 0; k < NUM_DIRECTIONS; k++)
1285 if (el_act_dir2img(player->element_nr, j, k) !=
1286 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1290 player->num_special_action_sleeping++;
1295 player->switch_x = -1;
1296 player->switch_y = -1;
1298 player->show_envelope = 0;
1300 player->move_delay = game.initial_move_delay;
1301 player->move_delay_value = game.initial_move_delay_value;
1303 player->move_delay_reset_counter = 0;
1305 player->push_delay = 0;
1306 player->push_delay_value = game.initial_push_delay_value;
1308 player->drop_delay = 0;
1310 player->last_jx = player->last_jy = 0;
1311 player->jx = player->jy = 0;
1313 player->shield_normal_time_left = 0;
1314 player->shield_deadly_time_left = 0;
1316 player->inventory_size = 0;
1318 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1319 SnapField(player, 0, 0);
1321 player->LevelSolved = FALSE;
1322 player->GameOver = FALSE;
1325 network_player_action_received = FALSE;
1327 #if defined(PLATFORM_UNIX)
1328 /* initial null action */
1329 if (network_playing)
1330 SendToServer_MovePlayer(MV_NO_MOVING);
1338 TimeLeft = level.time;
1340 ScreenMovDir = MV_NO_MOVING;
1344 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1346 AllPlayersGone = FALSE;
1348 game.yamyam_content_nr = 0;
1349 game.magic_wall_active = FALSE;
1350 game.magic_wall_time_left = 0;
1351 game.light_time_left = 0;
1352 game.timegate_time_left = 0;
1353 game.switchgate_pos = 0;
1354 game.balloon_dir = MV_NO_MOVING;
1355 game.gravity = level.initial_gravity;
1356 game.explosions_delayed = TRUE;
1358 game.envelope_active = FALSE;
1360 for (i = 0; i < 4; i++)
1362 game.belt_dir[i] = MV_NO_MOVING;
1363 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1366 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1367 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1369 for (x = 0; x < lev_fieldx; x++)
1371 for (y = 0; y < lev_fieldy; y++)
1373 Feld[x][y] = level.field[x][y];
1374 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1375 ChangeDelay[x][y] = 0;
1376 ChangePage[x][y] = -1;
1377 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1379 WasJustMoving[x][y] = 0;
1380 WasJustFalling[x][y] = 0;
1382 Pushed[x][y] = FALSE;
1384 Changed[x][y] = CE_BITMASK_DEFAULT;
1385 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1387 ExplodePhase[x][y] = 0;
1388 ExplodeField[x][y] = EX_NO_EXPLOSION;
1390 RunnerVisit[x][y] = 0;
1391 PlayerVisit[x][y] = 0;
1394 GfxRandom[x][y] = INIT_GFX_RANDOM();
1395 GfxElement[x][y] = EL_UNDEFINED;
1396 GfxAction[x][y] = ACTION_DEFAULT;
1397 GfxDir[x][y] = MV_NO_MOVING;
1401 for (y = 0; y < lev_fieldy; y++)
1403 for (x = 0; x < lev_fieldx; x++)
1405 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1407 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1409 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1412 InitField(x, y, TRUE);
1418 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1419 emulate_sb ? EMU_SOKOBAN :
1420 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1422 /* correct non-moving belts to start moving left */
1423 for (i = 0; i < 4; i++)
1424 if (game.belt_dir[i] == MV_NO_MOVING)
1425 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1427 /* check if any connected player was not found in playfield */
1428 for (i = 0; i < MAX_PLAYERS; i++)
1430 struct PlayerInfo *player = &stored_player[i];
1432 if (player->connected && !player->present)
1434 for (j = 0; j < MAX_PLAYERS; j++)
1436 struct PlayerInfo *some_player = &stored_player[j];
1437 int jx = some_player->jx, jy = some_player->jy;
1439 /* assign first free player found that is present in the playfield */
1440 if (some_player->present && !some_player->connected)
1442 player->present = TRUE;
1443 player->active = TRUE;
1445 some_player->present = FALSE;
1446 some_player->active = FALSE;
1448 StorePlayer[jx][jy] = player->element_nr;
1449 player->jx = player->last_jx = jx;
1450 player->jy = player->last_jy = jy;
1460 /* when playing a tape, eliminate all players which do not participate */
1462 for (i = 0; i < MAX_PLAYERS; i++)
1464 if (stored_player[i].active && !tape.player_participates[i])
1466 struct PlayerInfo *player = &stored_player[i];
1467 int jx = player->jx, jy = player->jy;
1469 player->active = FALSE;
1470 StorePlayer[jx][jy] = 0;
1471 Feld[jx][jy] = EL_EMPTY;
1475 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1477 /* when in single player mode, eliminate all but the first active player */
1479 for (i = 0; i < MAX_PLAYERS; i++)
1481 if (stored_player[i].active)
1483 for (j = i + 1; j < MAX_PLAYERS; j++)
1485 if (stored_player[j].active)
1487 struct PlayerInfo *player = &stored_player[j];
1488 int jx = player->jx, jy = player->jy;
1490 player->active = FALSE;
1491 player->present = FALSE;
1493 StorePlayer[jx][jy] = 0;
1494 Feld[jx][jy] = EL_EMPTY;
1501 /* when recording the game, store which players take part in the game */
1504 for (i = 0; i < MAX_PLAYERS; i++)
1505 if (stored_player[i].active)
1506 tape.player_participates[i] = TRUE;
1511 for (i = 0; i < MAX_PLAYERS; i++)
1513 struct PlayerInfo *player = &stored_player[i];
1515 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1520 if (local_player == player)
1521 printf("Player %d is local player.\n", i+1);
1525 if (BorderElement == EL_EMPTY)
1528 SBX_Right = lev_fieldx - SCR_FIELDX;
1530 SBY_Lower = lev_fieldy - SCR_FIELDY;
1535 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1537 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1540 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1541 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1543 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1544 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1546 /* if local player not found, look for custom element that might create
1547 the player (make some assumptions about the right custom element) */
1548 if (!local_player->present)
1550 int start_x = 0, start_y = 0;
1551 int found_rating = 0;
1552 int found_element = EL_UNDEFINED;
1554 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1556 int element = Feld[x][y];
1561 if (!IS_CUSTOM_ELEMENT(element))
1564 if (CAN_CHANGE(element))
1566 for (i = 0; i < element_info[element].num_change_pages; i++)
1568 content = element_info[element].change_page[i].target_element;
1569 is_player = ELEM_IS_PLAYER(content);
1571 if (is_player && (found_rating < 3 || element < found_element))
1577 found_element = element;
1582 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1584 content = element_info[element].content[xx][yy];
1585 is_player = ELEM_IS_PLAYER(content);
1587 if (is_player && (found_rating < 2 || element < found_element))
1589 start_x = x + xx - 1;
1590 start_y = y + yy - 1;
1593 found_element = element;
1596 if (!CAN_CHANGE(element))
1599 for (i = 0; i < element_info[element].num_change_pages; i++)
1601 content = element_info[element].change_page[i].content[xx][yy];
1602 is_player = ELEM_IS_PLAYER(content);
1604 if (is_player && (found_rating < 1 || element < found_element))
1606 start_x = x + xx - 1;
1607 start_y = y + yy - 1;
1610 found_element = element;
1616 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1617 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1620 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1621 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1627 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1628 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1629 local_player->jx - MIDPOSX);
1631 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1632 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1633 local_player->jy - MIDPOSY);
1635 scroll_x = SBX_Left;
1636 scroll_y = SBY_Upper;
1637 if (local_player->jx >= SBX_Left + MIDPOSX)
1638 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1639 local_player->jx - MIDPOSX :
1641 if (local_player->jy >= SBY_Upper + MIDPOSY)
1642 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1643 local_player->jy - MIDPOSY :
1648 CloseDoor(DOOR_CLOSE_1);
1653 /* after drawing the level, correct some elements */
1654 if (game.timegate_time_left == 0)
1655 CloseAllOpenTimegates();
1657 if (setup.soft_scrolling)
1658 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1660 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1663 /* copy default game door content to main double buffer */
1664 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1665 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1668 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1671 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1672 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1673 BlitBitmap(drawto, drawto,
1674 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1675 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1676 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1677 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1680 DrawGameDoorValues();
1684 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1685 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1686 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1690 /* copy actual game door content to door double buffer for OpenDoor() */
1691 BlitBitmap(drawto, bitmap_db_door,
1692 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1694 OpenDoor(DOOR_OPEN_ALL);
1696 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1698 if (setup.sound_music)
1701 KeyboardAutoRepeatOffUnlessAutoplay();
1705 for (i = 0; i < 4; i++)
1706 printf("Player %d %sactive.\n",
1707 i + 1, (stored_player[i].active ? "" : "not "));
1711 printf("::: starting game [%d]\n", FrameCounter);
1715 void InitMovDir(int x, int y)
1717 int i, element = Feld[x][y];
1718 static int xy[4][2] =
1725 static int direction[3][4] =
1727 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1728 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1729 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1738 Feld[x][y] = EL_BUG;
1739 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1742 case EL_SPACESHIP_RIGHT:
1743 case EL_SPACESHIP_UP:
1744 case EL_SPACESHIP_LEFT:
1745 case EL_SPACESHIP_DOWN:
1746 Feld[x][y] = EL_SPACESHIP;
1747 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1750 case EL_BD_BUTTERFLY_RIGHT:
1751 case EL_BD_BUTTERFLY_UP:
1752 case EL_BD_BUTTERFLY_LEFT:
1753 case EL_BD_BUTTERFLY_DOWN:
1754 Feld[x][y] = EL_BD_BUTTERFLY;
1755 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1758 case EL_BD_FIREFLY_RIGHT:
1759 case EL_BD_FIREFLY_UP:
1760 case EL_BD_FIREFLY_LEFT:
1761 case EL_BD_FIREFLY_DOWN:
1762 Feld[x][y] = EL_BD_FIREFLY;
1763 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1766 case EL_PACMAN_RIGHT:
1768 case EL_PACMAN_LEFT:
1769 case EL_PACMAN_DOWN:
1770 Feld[x][y] = EL_PACMAN;
1771 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1774 case EL_SP_SNIKSNAK:
1775 MovDir[x][y] = MV_UP;
1778 case EL_SP_ELECTRON:
1779 MovDir[x][y] = MV_LEFT;
1786 Feld[x][y] = EL_MOLE;
1787 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1791 if (IS_CUSTOM_ELEMENT(element))
1793 struct ElementInfo *ei = &element_info[element];
1794 int move_direction_initial = ei->move_direction_initial;
1795 int move_pattern = ei->move_pattern;
1797 if (move_direction_initial == MV_START_PREVIOUS)
1799 if (MovDir[x][y] != MV_NO_MOVING)
1802 move_direction_initial = MV_START_AUTOMATIC;
1805 if (move_direction_initial == MV_START_RANDOM)
1806 MovDir[x][y] = 1 << RND(4);
1807 else if (move_direction_initial & MV_ANY_DIRECTION)
1808 MovDir[x][y] = move_direction_initial;
1809 else if (move_pattern == MV_ALL_DIRECTIONS ||
1810 move_pattern == MV_TURNING_LEFT ||
1811 move_pattern == MV_TURNING_RIGHT ||
1812 move_pattern == MV_TURNING_LEFT_RIGHT ||
1813 move_pattern == MV_TURNING_RIGHT_LEFT ||
1814 move_pattern == MV_TURNING_RANDOM)
1815 MovDir[x][y] = 1 << RND(4);
1816 else if (move_pattern == MV_HORIZONTAL)
1817 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1818 else if (move_pattern == MV_VERTICAL)
1819 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1820 else if (move_pattern & MV_ANY_DIRECTION)
1821 MovDir[x][y] = element_info[element].move_pattern;
1822 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1823 move_pattern == MV_ALONG_RIGHT_SIDE)
1825 for (i = 0; i < 4; i++)
1827 int x1 = x + xy[i][0];
1828 int y1 = y + xy[i][1];
1830 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1832 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1833 MovDir[x][y] = direction[0][i];
1835 MovDir[x][y] = direction[1][i];
1844 MovDir[x][y] = 1 << RND(4);
1846 if (element != EL_BUG &&
1847 element != EL_SPACESHIP &&
1848 element != EL_BD_BUTTERFLY &&
1849 element != EL_BD_FIREFLY)
1852 for (i = 0; i < 4; i++)
1854 int x1 = x + xy[i][0];
1855 int y1 = y + xy[i][1];
1857 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1859 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1861 MovDir[x][y] = direction[0][i];
1864 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1865 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1867 MovDir[x][y] = direction[1][i];
1876 GfxDir[x][y] = MovDir[x][y];
1879 void InitAmoebaNr(int x, int y)
1882 int group_nr = AmoebeNachbarNr(x, y);
1886 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1888 if (AmoebaCnt[i] == 0)
1896 AmoebaNr[x][y] = group_nr;
1897 AmoebaCnt[group_nr]++;
1898 AmoebaCnt2[group_nr]++;
1904 boolean raise_level = FALSE;
1906 if (local_player->MovPos)
1910 if (tape.auto_play) /* tape might already be stopped here */
1911 tape.auto_play_level_solved = TRUE;
1913 if (tape.playing && tape.auto_play)
1914 tape.auto_play_level_solved = TRUE;
1917 local_player->LevelSolved = FALSE;
1919 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1923 if (!tape.playing && setup.sound_loops)
1924 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1925 SND_CTRL_PLAY_LOOP);
1927 while (TimeLeft > 0)
1929 if (!tape.playing && !setup.sound_loops)
1930 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1931 if (TimeLeft > 0 && !(TimeLeft % 10))
1932 RaiseScore(level.score[SC_TIME_BONUS]);
1933 if (TimeLeft > 100 && !(TimeLeft % 10))
1937 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1944 if (!tape.playing && setup.sound_loops)
1945 StopSound(SND_GAME_LEVELTIME_BONUS);
1947 else if (level.time == 0) /* level without time limit */
1949 if (!tape.playing && setup.sound_loops)
1950 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1951 SND_CTRL_PLAY_LOOP);
1953 while (TimePlayed < 999)
1955 if (!tape.playing && !setup.sound_loops)
1956 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1957 if (TimePlayed < 999 && !(TimePlayed % 10))
1958 RaiseScore(level.score[SC_TIME_BONUS]);
1959 if (TimePlayed < 900 && !(TimePlayed % 10))
1963 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1970 if (!tape.playing && setup.sound_loops)
1971 StopSound(SND_GAME_LEVELTIME_BONUS);
1974 /* close exit door after last player */
1975 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1976 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1978 int element = Feld[ExitX][ExitY];
1980 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1981 EL_SP_EXIT_CLOSING);
1983 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1986 /* Hero disappears */
1987 DrawLevelField(ExitX, ExitY);
1993 CloseDoor(DOOR_CLOSE_1);
1998 SaveTape(tape.level_nr); /* Ask to save tape */
2001 if (level_nr == leveldir_current->handicap_level)
2003 leveldir_current->handicap_level++;
2004 SaveLevelSetup_SeriesInfo();
2007 if (level_editor_test_game)
2008 local_player->score = -1; /* no highscore when playing from editor */
2009 else if (level_nr < leveldir_current->last_level)
2010 raise_level = TRUE; /* advance to next level */
2012 if ((hi_pos = NewHiScore()) >= 0)
2014 game_status = GAME_MODE_SCORES;
2015 DrawHallOfFame(hi_pos);
2024 game_status = GAME_MODE_MAIN;
2041 LoadScore(level_nr);
2043 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2044 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2047 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2049 if (local_player->score > highscore[k].Score)
2051 /* player has made it to the hall of fame */
2053 if (k < MAX_SCORE_ENTRIES - 1)
2055 int m = MAX_SCORE_ENTRIES - 1;
2058 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2059 if (!strcmp(setup.player_name, highscore[l].Name))
2061 if (m == k) /* player's new highscore overwrites his old one */
2065 for (l = m; l > k; l--)
2067 strcpy(highscore[l].Name, highscore[l - 1].Name);
2068 highscore[l].Score = highscore[l - 1].Score;
2075 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2076 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2077 highscore[k].Score = local_player->score;
2083 else if (!strncmp(setup.player_name, highscore[k].Name,
2084 MAX_PLAYER_NAME_LEN))
2085 break; /* player already there with a higher score */
2091 SaveScore(level_nr);
2096 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2098 if (player->GfxAction != action || player->GfxDir != dir)
2101 printf("Player frame reset! (%d => %d, %d => %d)\n",
2102 player->GfxAction, action, player->GfxDir, dir);
2105 player->GfxAction = action;
2106 player->GfxDir = dir;
2108 player->StepFrame = 0;
2112 static void ResetRandomAnimationValue(int x, int y)
2114 GfxRandom[x][y] = INIT_GFX_RANDOM();
2117 static void ResetGfxAnimation(int x, int y)
2120 GfxAction[x][y] = ACTION_DEFAULT;
2121 GfxDir[x][y] = MovDir[x][y];
2124 void InitMovingField(int x, int y, int direction)
2126 int element = Feld[x][y];
2127 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2128 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2132 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2133 ResetGfxAnimation(x, y);
2135 MovDir[newx][newy] = MovDir[x][y] = direction;
2136 GfxDir[x][y] = direction;
2138 if (Feld[newx][newy] == EL_EMPTY)
2139 Feld[newx][newy] = EL_BLOCKED;
2141 if (direction == MV_DOWN && CAN_FALL(element))
2142 GfxAction[x][y] = ACTION_FALLING;
2144 GfxAction[x][y] = ACTION_MOVING;
2146 GfxFrame[newx][newy] = GfxFrame[x][y];
2147 GfxRandom[newx][newy] = GfxRandom[x][y];
2148 GfxAction[newx][newy] = GfxAction[x][y];
2149 GfxDir[newx][newy] = GfxDir[x][y];
2152 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2154 int direction = MovDir[x][y];
2155 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2156 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2162 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2164 int oldx = x, oldy = y;
2165 int direction = MovDir[x][y];
2167 if (direction == MV_LEFT)
2169 else if (direction == MV_RIGHT)
2171 else if (direction == MV_UP)
2173 else if (direction == MV_DOWN)
2176 *comes_from_x = oldx;
2177 *comes_from_y = oldy;
2180 int MovingOrBlocked2Element(int x, int y)
2182 int element = Feld[x][y];
2184 if (element == EL_BLOCKED)
2188 Blocked2Moving(x, y, &oldx, &oldy);
2189 return Feld[oldx][oldy];
2195 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2197 /* like MovingOrBlocked2Element(), but if element is moving
2198 and (x,y) is the field the moving element is just leaving,
2199 return EL_BLOCKED instead of the element value */
2200 int element = Feld[x][y];
2202 if (IS_MOVING(x, y))
2204 if (element == EL_BLOCKED)
2208 Blocked2Moving(x, y, &oldx, &oldy);
2209 return Feld[oldx][oldy];
2218 static void RemoveField(int x, int y)
2220 Feld[x][y] = EL_EMPTY;
2227 ChangeDelay[x][y] = 0;
2228 ChangePage[x][y] = -1;
2229 Pushed[x][y] = FALSE;
2231 GfxElement[x][y] = EL_UNDEFINED;
2232 GfxAction[x][y] = ACTION_DEFAULT;
2233 GfxDir[x][y] = MV_NO_MOVING;
2236 void RemoveMovingField(int x, int y)
2238 int oldx = x, oldy = y, newx = x, newy = y;
2239 int element = Feld[x][y];
2240 int next_element = EL_UNDEFINED;
2242 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2245 if (IS_MOVING(x, y))
2247 Moving2Blocked(x, y, &newx, &newy);
2248 if (Feld[newx][newy] != EL_BLOCKED)
2251 else if (element == EL_BLOCKED)
2253 Blocked2Moving(x, y, &oldx, &oldy);
2254 if (!IS_MOVING(oldx, oldy))
2258 if (element == EL_BLOCKED &&
2259 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2260 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2261 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2262 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2263 next_element = get_next_element(Feld[oldx][oldy]);
2265 RemoveField(oldx, oldy);
2266 RemoveField(newx, newy);
2268 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2270 if (next_element != EL_UNDEFINED)
2271 Feld[oldx][oldy] = next_element;
2273 DrawLevelField(oldx, oldy);
2274 DrawLevelField(newx, newy);
2277 void DrawDynamite(int x, int y)
2279 int sx = SCREENX(x), sy = SCREENY(y);
2280 int graphic = el2img(Feld[x][y]);
2283 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2286 if (IS_WALKABLE_INSIDE(Back[x][y]))
2290 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2291 else if (Store[x][y])
2292 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2294 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2297 if (Back[x][y] || Store[x][y])
2298 DrawGraphicThruMask(sx, sy, graphic, frame);
2300 DrawGraphic(sx, sy, graphic, frame);
2302 if (game.emulation == EMU_SUPAPLEX)
2303 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2304 else if (Store[x][y])
2305 DrawGraphicThruMask(sx, sy, graphic, frame);
2307 DrawGraphic(sx, sy, graphic, frame);
2311 void CheckDynamite(int x, int y)
2313 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2317 if (MovDelay[x][y] != 0)
2320 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2327 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2329 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2330 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2331 StopSound(SND_DYNAMITE_ACTIVE);
2333 StopSound(SND_DYNABOMB_ACTIVE);
2339 void RelocatePlayer(int x, int y, int element)
2341 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2342 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2343 boolean no_delay = (tape.index_search);
2344 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2345 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2347 if (player->GameOver) /* do not reanimate dead player */
2351 RemoveField(x, y); /* temporarily remove newly placed player */
2352 DrawLevelField(x, y);
2355 if (player->present)
2357 while (player->MovPos)
2359 ScrollPlayer(player, SCROLL_GO_ON);
2360 ScrollScreen(NULL, SCROLL_GO_ON);
2366 Delay(wait_delay_value);
2369 DrawPlayer(player); /* needed here only to cleanup last field */
2370 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2372 player->is_moving = FALSE;
2375 Feld[x][y] = element;
2376 InitPlayerField(x, y, element, TRUE);
2378 if (player == local_player)
2380 int scroll_xx = -999, scroll_yy = -999;
2382 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2385 int fx = FX, fy = FY;
2387 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2388 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2389 local_player->jx - MIDPOSX);
2391 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2392 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2393 local_player->jy - MIDPOSY);
2395 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2396 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2401 fx += dx * TILEX / 2;
2402 fy += dy * TILEY / 2;
2404 ScrollLevel(dx, dy);
2407 /* scroll in two steps of half tile size to make things smoother */
2408 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2410 Delay(wait_delay_value);
2412 /* scroll second step to align at full tile size */
2414 Delay(wait_delay_value);
2419 void Explode(int ex, int ey, int phase, int mode)
2423 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2424 int last_phase = num_phase * delay;
2425 int half_phase = (num_phase / 2) * delay;
2426 int first_phase_after_start = EX_PHASE_START + 1;
2428 if (game.explosions_delayed)
2430 ExplodeField[ex][ey] = mode;
2434 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2436 int center_element = Feld[ex][ey];
2439 /* --- This is only really needed (and now handled) in "Impact()". --- */
2440 /* do not explode moving elements that left the explode field in time */
2441 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2442 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2446 if (mode == EX_NORMAL || mode == EX_CENTER)
2447 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2449 /* remove things displayed in background while burning dynamite */
2450 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2453 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2455 /* put moving element to center field (and let it explode there) */
2456 center_element = MovingOrBlocked2Element(ex, ey);
2457 RemoveMovingField(ex, ey);
2458 Feld[ex][ey] = center_element;
2461 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2463 int xx = x - ex + 1;
2464 int yy = y - ey + 1;
2467 if (!IN_LEV_FIELD(x, y) ||
2468 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2469 (x != ex || y != ey)))
2472 element = Feld[x][y];
2474 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2476 element = MovingOrBlocked2Element(x, y);
2478 if (!IS_EXPLOSION_PROOF(element))
2479 RemoveMovingField(x, y);
2485 if (IS_EXPLOSION_PROOF(element))
2488 /* indestructible elements can only explode in center (but not flames) */
2489 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2490 element == EL_FLAMES)
2495 if ((IS_INDESTRUCTIBLE(element) &&
2496 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2497 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2498 element == EL_FLAMES)
2502 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2504 if (IS_ACTIVE_BOMB(element))
2506 /* re-activate things under the bomb like gate or penguin */
2507 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2514 /* save walkable background elements while explosion on same tile */
2516 if (IS_INDESTRUCTIBLE(element))
2517 Back[x][y] = element;
2519 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2520 Back[x][y] = element;
2523 /* ignite explodable elements reached by other explosion */
2524 if (element == EL_EXPLOSION)
2525 element = Store2[x][y];
2528 if (AmoebaNr[x][y] &&
2529 (element == EL_AMOEBA_FULL ||
2530 element == EL_BD_AMOEBA ||
2531 element == EL_AMOEBA_GROWING))
2533 AmoebaCnt[AmoebaNr[x][y]]--;
2534 AmoebaCnt2[AmoebaNr[x][y]]--;
2540 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2542 switch(StorePlayer[ex][ey])
2545 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2548 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2551 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2555 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2559 if (game.emulation == EMU_SUPAPLEX)
2560 Store[x][y] = EL_EMPTY;
2562 else if (center_element == EL_MOLE)
2563 Store[x][y] = EL_EMERALD_RED;
2564 else if (center_element == EL_PENGUIN)
2565 Store[x][y] = EL_EMERALD_PURPLE;
2566 else if (center_element == EL_BUG)
2567 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2568 else if (center_element == EL_BD_BUTTERFLY)
2569 Store[x][y] = EL_BD_DIAMOND;
2570 else if (center_element == EL_SP_ELECTRON)
2571 Store[x][y] = EL_SP_INFOTRON;
2572 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2573 Store[x][y] = level.amoeba_content;
2574 else if (center_element == EL_YAMYAM)
2575 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2576 else if (IS_CUSTOM_ELEMENT(center_element) &&
2577 element_info[center_element].content[xx][yy] != EL_EMPTY)
2578 Store[x][y] = element_info[center_element].content[xx][yy];
2579 else if (element == EL_WALL_EMERALD)
2580 Store[x][y] = EL_EMERALD;
2581 else if (element == EL_WALL_DIAMOND)
2582 Store[x][y] = EL_DIAMOND;
2583 else if (element == EL_WALL_BD_DIAMOND)
2584 Store[x][y] = EL_BD_DIAMOND;
2585 else if (element == EL_WALL_EMERALD_YELLOW)
2586 Store[x][y] = EL_EMERALD_YELLOW;
2587 else if (element == EL_WALL_EMERALD_RED)
2588 Store[x][y] = EL_EMERALD_RED;
2589 else if (element == EL_WALL_EMERALD_PURPLE)
2590 Store[x][y] = EL_EMERALD_PURPLE;
2591 else if (element == EL_WALL_PEARL)
2592 Store[x][y] = EL_PEARL;
2593 else if (element == EL_WALL_CRYSTAL)
2594 Store[x][y] = EL_CRYSTAL;
2595 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2596 Store[x][y] = element_info[element].content[1][1];
2598 Store[x][y] = EL_EMPTY;
2600 if (x != ex || y != ey ||
2601 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2602 Store2[x][y] = element;
2605 if (AmoebaNr[x][y] &&
2606 (element == EL_AMOEBA_FULL ||
2607 element == EL_BD_AMOEBA ||
2608 element == EL_AMOEBA_GROWING))
2610 AmoebaCnt[AmoebaNr[x][y]]--;
2611 AmoebaCnt2[AmoebaNr[x][y]]--;
2617 MovDir[x][y] = MovPos[x][y] = 0;
2618 GfxDir[x][y] = MovDir[x][y];
2623 Feld[x][y] = EL_EXPLOSION;
2625 GfxElement[x][y] = center_element;
2627 GfxElement[x][y] = EL_UNDEFINED;
2630 ExplodePhase[x][y] = 1;
2634 if (center_element == EL_YAMYAM)
2635 game.yamyam_content_nr =
2636 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2647 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2651 /* activate this even in non-DEBUG version until cause for crash in
2652 getGraphicAnimationFrame() (see below) is found and eliminated */
2656 if (GfxElement[x][y] == EL_UNDEFINED)
2659 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2660 printf("Explode(): This should never happen!\n");
2663 GfxElement[x][y] = EL_EMPTY;
2667 if (phase == first_phase_after_start)
2669 int element = Store2[x][y];
2671 if (element == EL_BLACK_ORB)
2673 Feld[x][y] = Store2[x][y];
2678 else if (phase == half_phase)
2680 int element = Store2[x][y];
2682 if (IS_PLAYER(x, y))
2683 KillHeroUnlessExplosionProtected(x, y);
2684 else if (CAN_EXPLODE_BY_FIRE(element))
2686 Feld[x][y] = Store2[x][y];
2690 else if (element == EL_AMOEBA_TO_DIAMOND)
2691 AmoebeUmwandeln(x, y);
2694 if (phase == last_phase)
2698 element = Feld[x][y] = Store[x][y];
2699 Store[x][y] = Store2[x][y] = 0;
2700 GfxElement[x][y] = EL_UNDEFINED;
2702 /* player can escape from explosions and might therefore be still alive */
2703 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2704 element <= EL_PLAYER_IS_EXPLODING_4)
2705 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2707 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2708 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2709 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2712 /* restore probably existing indestructible background element */
2713 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2714 element = Feld[x][y] = Back[x][y];
2717 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2718 GfxDir[x][y] = MV_NO_MOVING;
2719 ChangeDelay[x][y] = 0;
2720 ChangePage[x][y] = -1;
2722 InitField(x, y, FALSE);
2724 /* !!! not needed !!! */
2725 if (CAN_MOVE(element))
2728 DrawLevelField(x, y);
2730 TestIfElementTouchesCustomElement(x, y);
2732 if (GFX_CRUMBLED(element))
2733 DrawLevelFieldCrumbledSandNeighbours(x, y);
2735 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2736 StorePlayer[x][y] = 0;
2738 if (ELEM_IS_PLAYER(element))
2739 RelocatePlayer(x, y, element);
2741 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2744 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2746 int stored = Store[x][y];
2747 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2748 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2751 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2754 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2755 element_info[GfxElement[x][y]].token_name,
2760 DrawLevelFieldCrumbledSand(x, y);
2762 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2764 DrawLevelElement(x, y, Back[x][y]);
2765 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2767 else if (IS_WALKABLE_UNDER(Back[x][y]))
2769 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2770 DrawLevelElementThruMask(x, y, Back[x][y]);
2772 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2773 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2777 void DynaExplode(int ex, int ey)
2780 int dynabomb_element = Feld[ex][ey];
2781 int dynabomb_size = 1;
2782 boolean dynabomb_xl = FALSE;
2783 struct PlayerInfo *player;
2784 static int xy[4][2] =
2792 if (IS_ACTIVE_BOMB(dynabomb_element))
2794 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2795 dynabomb_size = player->dynabomb_size;
2796 dynabomb_xl = player->dynabomb_xl;
2797 player->dynabombs_left++;
2800 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2802 for (i = 0; i < 4; i++)
2804 for (j = 1; j <= dynabomb_size; j++)
2806 int x = ex + j * xy[i % 4][0];
2807 int y = ey + j * xy[i % 4][1];
2810 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2813 element = Feld[x][y];
2815 /* do not restart explosions of fields with active bombs */
2816 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2819 Explode(x, y, EX_PHASE_START, EX_BORDER);
2821 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2822 if (element != EL_EMPTY &&
2823 element != EL_SAND &&
2824 element != EL_EXPLOSION &&
2831 void Bang(int x, int y)
2834 int element = MovingOrBlocked2Element(x, y);
2836 int element = Feld[x][y];
2840 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2842 if (IS_PLAYER(x, y))
2845 struct PlayerInfo *player = PLAYERINFO(x, y);
2847 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2848 player->element_nr);
2853 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2855 if (game.emulation == EMU_SUPAPLEX)
2856 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2858 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2863 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2871 case EL_BD_BUTTERFLY:
2874 case EL_DARK_YAMYAM:
2878 RaiseScoreElement(element);
2879 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2881 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2882 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2883 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2884 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2885 case EL_DYNABOMB_INCREASE_NUMBER:
2886 case EL_DYNABOMB_INCREASE_SIZE:
2887 case EL_DYNABOMB_INCREASE_POWER:
2892 case EL_LAMP_ACTIVE:
2893 if (IS_PLAYER(x, y))
2894 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2896 Explode(x, y, EX_PHASE_START, EX_CENTER);
2899 if (CAN_EXPLODE_DYNA(element))
2901 else if (CAN_EXPLODE_1X1(element))
2902 Explode(x, y, EX_PHASE_START, EX_CENTER);
2904 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2908 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2911 void SplashAcid(int x, int y)
2913 int element = Feld[x][y];
2915 if (element != EL_ACID_SPLASH_LEFT &&
2916 element != EL_ACID_SPLASH_RIGHT)
2918 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2920 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2921 (!IN_LEV_FIELD(x-1, y-1) ||
2922 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2923 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2925 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2926 (!IN_LEV_FIELD(x+1, y-1) ||
2927 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2928 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2932 static void InitBeltMovement()
2934 static int belt_base_element[4] =
2936 EL_CONVEYOR_BELT_1_LEFT,
2937 EL_CONVEYOR_BELT_2_LEFT,
2938 EL_CONVEYOR_BELT_3_LEFT,
2939 EL_CONVEYOR_BELT_4_LEFT
2941 static int belt_base_active_element[4] =
2943 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2944 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2945 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2946 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2951 /* set frame order for belt animation graphic according to belt direction */
2952 for (i = 0; i < 4; i++)
2956 for (j = 0; j < 3; j++)
2958 int element = belt_base_active_element[belt_nr] + j;
2959 int graphic = el2img(element);
2961 if (game.belt_dir[i] == MV_LEFT)
2962 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2964 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2968 for (y = 0; y < lev_fieldy; y++)
2970 for (x = 0; x < lev_fieldx; x++)
2972 int element = Feld[x][y];
2974 for (i = 0; i < 4; i++)
2976 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2978 int e_belt_nr = getBeltNrFromBeltElement(element);
2981 if (e_belt_nr == belt_nr)
2983 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2985 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2993 static void ToggleBeltSwitch(int x, int y)
2995 static int belt_base_element[4] =
2997 EL_CONVEYOR_BELT_1_LEFT,
2998 EL_CONVEYOR_BELT_2_LEFT,
2999 EL_CONVEYOR_BELT_3_LEFT,
3000 EL_CONVEYOR_BELT_4_LEFT
3002 static int belt_base_active_element[4] =
3004 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3005 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3006 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3007 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3009 static int belt_base_switch_element[4] =
3011 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3012 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3013 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3014 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3016 static int belt_move_dir[4] =
3024 int element = Feld[x][y];
3025 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3026 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3027 int belt_dir = belt_move_dir[belt_dir_nr];
3030 if (!IS_BELT_SWITCH(element))
3033 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3034 game.belt_dir[belt_nr] = belt_dir;
3036 if (belt_dir_nr == 3)
3039 /* set frame order for belt animation graphic according to belt direction */
3040 for (i = 0; i < 3; i++)
3042 int element = belt_base_active_element[belt_nr] + i;
3043 int graphic = el2img(element);
3045 if (belt_dir == MV_LEFT)
3046 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3048 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3051 for (yy = 0; yy < lev_fieldy; yy++)
3053 for (xx = 0; xx < lev_fieldx; xx++)
3055 int element = Feld[xx][yy];
3057 if (IS_BELT_SWITCH(element))
3059 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3061 if (e_belt_nr == belt_nr)
3063 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3064 DrawLevelField(xx, yy);
3067 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3069 int e_belt_nr = getBeltNrFromBeltElement(element);
3071 if (e_belt_nr == belt_nr)
3073 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3075 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3076 DrawLevelField(xx, yy);
3079 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3081 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3083 if (e_belt_nr == belt_nr)
3085 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3087 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3088 DrawLevelField(xx, yy);
3095 static void ToggleSwitchgateSwitch(int x, int y)
3099 game.switchgate_pos = !game.switchgate_pos;
3101 for (yy = 0; yy < lev_fieldy; yy++)
3103 for (xx = 0; xx < lev_fieldx; xx++)
3105 int element = Feld[xx][yy];
3107 if (element == EL_SWITCHGATE_SWITCH_UP ||
3108 element == EL_SWITCHGATE_SWITCH_DOWN)
3110 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3111 DrawLevelField(xx, yy);
3113 else if (element == EL_SWITCHGATE_OPEN ||
3114 element == EL_SWITCHGATE_OPENING)
3116 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3118 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3120 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3123 else if (element == EL_SWITCHGATE_CLOSED ||
3124 element == EL_SWITCHGATE_CLOSING)
3126 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3128 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3130 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3137 static int getInvisibleActiveFromInvisibleElement(int element)
3139 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3140 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3141 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3145 static int getInvisibleFromInvisibleActiveElement(int element)
3147 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3148 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3149 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3153 static void RedrawAllLightSwitchesAndInvisibleElements()
3157 for (y = 0; y < lev_fieldy; y++)
3159 for (x = 0; x < lev_fieldx; x++)
3161 int element = Feld[x][y];
3163 if (element == EL_LIGHT_SWITCH &&
3164 game.light_time_left > 0)
3166 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3167 DrawLevelField(x, y);
3169 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3170 game.light_time_left == 0)
3172 Feld[x][y] = EL_LIGHT_SWITCH;
3173 DrawLevelField(x, y);
3175 else if (element == EL_INVISIBLE_STEELWALL ||
3176 element == EL_INVISIBLE_WALL ||
3177 element == EL_INVISIBLE_SAND)
3179 if (game.light_time_left > 0)
3180 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3182 DrawLevelField(x, y);
3184 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3185 element == EL_INVISIBLE_WALL_ACTIVE ||
3186 element == EL_INVISIBLE_SAND_ACTIVE)
3188 if (game.light_time_left == 0)
3189 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3191 DrawLevelField(x, y);
3197 static void ToggleLightSwitch(int x, int y)
3199 int element = Feld[x][y];
3201 game.light_time_left =
3202 (element == EL_LIGHT_SWITCH ?
3203 level.time_light * FRAMES_PER_SECOND : 0);
3205 RedrawAllLightSwitchesAndInvisibleElements();
3208 static void ActivateTimegateSwitch(int x, int y)
3212 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3214 for (yy = 0; yy < lev_fieldy; yy++)
3216 for (xx = 0; xx < lev_fieldx; xx++)
3218 int element = Feld[xx][yy];
3220 if (element == EL_TIMEGATE_CLOSED ||
3221 element == EL_TIMEGATE_CLOSING)
3223 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3224 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3228 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3230 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3231 DrawLevelField(xx, yy);
3238 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3241 inline static int getElementMoveStepsize(int x, int y)
3243 int element = Feld[x][y];
3244 int direction = MovDir[x][y];
3245 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3246 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3247 int horiz_move = (dx != 0);
3248 int sign = (horiz_move ? dx : dy);
3249 int step = sign * element_info[element].move_stepsize;
3251 /* special values for move stepsize for spring and things on conveyor belt */
3254 if (CAN_FALL(element) &&
3255 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3256 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3257 else if (element == EL_SPRING)
3258 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3264 void Impact(int x, int y)
3266 boolean lastline = (y == lev_fieldy-1);
3267 boolean object_hit = FALSE;
3268 boolean impact = (lastline || object_hit);
3269 int element = Feld[x][y];
3270 int smashed = EL_UNDEFINED;
3272 if (!lastline) /* check if element below was hit */
3274 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3277 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3278 MovDir[x][y + 1] != MV_DOWN ||
3279 MovPos[x][y + 1] <= TILEY / 2));
3282 object_hit = !IS_FREE(x, y + 1);
3285 /* do not smash moving elements that left the smashed field in time */
3286 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3287 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3291 smashed = MovingOrBlocked2Element(x, y + 1);
3293 impact = (lastline || object_hit);
3296 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3302 /* only reset graphic animation if graphic really changes after impact */
3304 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3306 ResetGfxAnimation(x, y);
3307 DrawLevelField(x, y);
3310 if (impact && CAN_EXPLODE_IMPACT(element))
3315 else if (impact && element == EL_PEARL)
3317 Feld[x][y] = EL_PEARL_BREAKING;
3318 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3321 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3323 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3328 if (impact && element == EL_AMOEBA_DROP)
3330 if (object_hit && IS_PLAYER(x, y + 1))
3331 KillHeroUnlessEnemyProtected(x, y + 1);
3332 else if (object_hit && smashed == EL_PENGUIN)
3336 Feld[x][y] = EL_AMOEBA_GROWING;
3337 Store[x][y] = EL_AMOEBA_WET;
3339 ResetRandomAnimationValue(x, y);
3344 if (object_hit) /* check which object was hit */
3346 if (CAN_PASS_MAGIC_WALL(element) &&
3347 (smashed == EL_MAGIC_WALL ||
3348 smashed == EL_BD_MAGIC_WALL))
3351 int activated_magic_wall =
3352 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3353 EL_BD_MAGIC_WALL_ACTIVE);
3355 /* activate magic wall / mill */
3356 for (yy = 0; yy < lev_fieldy; yy++)
3357 for (xx = 0; xx < lev_fieldx; xx++)
3358 if (Feld[xx][yy] == smashed)
3359 Feld[xx][yy] = activated_magic_wall;
3361 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3362 game.magic_wall_active = TRUE;
3364 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3365 SND_MAGIC_WALL_ACTIVATING :
3366 SND_BD_MAGIC_WALL_ACTIVATING));
3369 if (IS_PLAYER(x, y + 1))
3371 if (CAN_SMASH_PLAYER(element))
3373 KillHeroUnlessEnemyProtected(x, y + 1);
3377 else if (smashed == EL_PENGUIN)
3379 if (CAN_SMASH_PLAYER(element))
3385 else if (element == EL_BD_DIAMOND)
3387 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3393 else if (((element == EL_SP_INFOTRON ||
3394 element == EL_SP_ZONK) &&
3395 (smashed == EL_SP_SNIKSNAK ||
3396 smashed == EL_SP_ELECTRON ||
3397 smashed == EL_SP_DISK_ORANGE)) ||
3398 (element == EL_SP_INFOTRON &&
3399 smashed == EL_SP_DISK_YELLOW))
3405 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3411 else if (CAN_SMASH_EVERYTHING(element))
3413 if (IS_CLASSIC_ENEMY(smashed) ||
3414 CAN_EXPLODE_SMASHED(smashed))
3419 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3421 if (smashed == EL_LAMP ||
3422 smashed == EL_LAMP_ACTIVE)
3427 else if (smashed == EL_NUT)
3429 Feld[x][y + 1] = EL_NUT_BREAKING;
3430 PlayLevelSound(x, y, SND_NUT_BREAKING);
3431 RaiseScoreElement(EL_NUT);
3434 else if (smashed == EL_PEARL)
3436 Feld[x][y + 1] = EL_PEARL_BREAKING;
3437 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3440 else if (smashed == EL_DIAMOND)
3442 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3443 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3446 else if (IS_BELT_SWITCH(smashed))
3448 ToggleBeltSwitch(x, y + 1);
3450 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3451 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3453 ToggleSwitchgateSwitch(x, y + 1);
3455 else if (smashed == EL_LIGHT_SWITCH ||
3456 smashed == EL_LIGHT_SWITCH_ACTIVE)
3458 ToggleLightSwitch(x, y + 1);
3462 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3464 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3465 CE_OTHER_IS_SWITCHING);
3466 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3472 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3477 /* play sound of magic wall / mill */
3479 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3480 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3482 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3483 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3484 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3485 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3490 /* play sound of object that hits the ground */
3491 if (lastline || object_hit)
3492 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3495 inline static void TurnRoundExt(int x, int y)
3507 { 0, 0 }, { 0, 0 }, { 0, 0 },
3512 int left, right, back;
3516 { MV_DOWN, MV_UP, MV_RIGHT },
3517 { MV_UP, MV_DOWN, MV_LEFT },
3519 { MV_LEFT, MV_RIGHT, MV_DOWN },
3523 { MV_RIGHT, MV_LEFT, MV_UP }
3526 int element = Feld[x][y];
3527 int move_pattern = element_info[element].move_pattern;
3529 int old_move_dir = MovDir[x][y];
3530 int left_dir = turn[old_move_dir].left;
3531 int right_dir = turn[old_move_dir].right;
3532 int back_dir = turn[old_move_dir].back;
3534 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3535 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3536 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3537 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3539 int left_x = x + left_dx, left_y = y + left_dy;
3540 int right_x = x + right_dx, right_y = y + right_dy;
3541 int move_x = x + move_dx, move_y = y + move_dy;
3545 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3547 TestIfBadThingTouchesOtherBadThing(x, y);
3549 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3550 MovDir[x][y] = right_dir;
3551 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3552 MovDir[x][y] = left_dir;
3554 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3556 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3559 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3560 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3562 TestIfBadThingTouchesOtherBadThing(x, y);
3564 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3565 MovDir[x][y] = left_dir;
3566 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3567 MovDir[x][y] = right_dir;
3569 if ((element == EL_SPACESHIP ||
3570 element == EL_SP_SNIKSNAK ||
3571 element == EL_SP_ELECTRON)
3572 && MovDir[x][y] != old_move_dir)
3574 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3577 else if (element == EL_YAMYAM)
3579 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3580 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3582 if (can_turn_left && can_turn_right)
3583 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3584 else if (can_turn_left)
3585 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3586 else if (can_turn_right)
3587 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3589 MovDir[x][y] = back_dir;
3591 MovDelay[x][y] = 16 + 16 * RND(3);
3593 else if (element == EL_DARK_YAMYAM)
3595 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3596 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3598 if (can_turn_left && can_turn_right)
3599 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3600 else if (can_turn_left)
3601 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3602 else if (can_turn_right)
3603 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3605 MovDir[x][y] = back_dir;
3607 MovDelay[x][y] = 16 + 16 * RND(3);
3609 else if (element == EL_PACMAN)
3611 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3612 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3614 if (can_turn_left && can_turn_right)
3615 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3616 else if (can_turn_left)
3617 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3618 else if (can_turn_right)
3619 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3621 MovDir[x][y] = back_dir;
3623 MovDelay[x][y] = 6 + RND(40);
3625 else if (element == EL_PIG)
3627 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3628 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3629 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3630 boolean should_turn_left, should_turn_right, should_move_on;
3632 int rnd = RND(rnd_value);
3634 should_turn_left = (can_turn_left &&
3636 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3637 y + back_dy + left_dy)));
3638 should_turn_right = (can_turn_right &&
3640 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3641 y + back_dy + right_dy)));
3642 should_move_on = (can_move_on &&
3645 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3646 y + move_dy + left_dy) ||
3647 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3648 y + move_dy + right_dy)));
3650 if (should_turn_left || should_turn_right || should_move_on)
3652 if (should_turn_left && should_turn_right && should_move_on)
3653 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3654 rnd < 2 * rnd_value / 3 ? right_dir :
3656 else if (should_turn_left && should_turn_right)
3657 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3658 else if (should_turn_left && should_move_on)
3659 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3660 else if (should_turn_right && should_move_on)
3661 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3662 else if (should_turn_left)
3663 MovDir[x][y] = left_dir;
3664 else if (should_turn_right)
3665 MovDir[x][y] = right_dir;
3666 else if (should_move_on)
3667 MovDir[x][y] = old_move_dir;
3669 else if (can_move_on && rnd > rnd_value / 8)
3670 MovDir[x][y] = old_move_dir;
3671 else if (can_turn_left && can_turn_right)
3672 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3673 else if (can_turn_left && rnd > rnd_value / 8)
3674 MovDir[x][y] = left_dir;
3675 else if (can_turn_right && rnd > rnd_value/8)
3676 MovDir[x][y] = right_dir;
3678 MovDir[x][y] = back_dir;
3680 xx = x + move_xy[MovDir[x][y]].x;
3681 yy = y + move_xy[MovDir[x][y]].y;
3683 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3684 MovDir[x][y] = old_move_dir;
3688 else if (element == EL_DRAGON)
3690 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3691 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3692 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3694 int rnd = RND(rnd_value);
3697 if (FrameCounter < 1 && x == 0 && y == 29)
3698 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3701 if (can_move_on && rnd > rnd_value / 8)
3702 MovDir[x][y] = old_move_dir;
3703 else if (can_turn_left && can_turn_right)
3704 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3705 else if (can_turn_left && rnd > rnd_value / 8)
3706 MovDir[x][y] = left_dir;
3707 else if (can_turn_right && rnd > rnd_value / 8)
3708 MovDir[x][y] = right_dir;
3710 MovDir[x][y] = back_dir;
3712 xx = x + move_xy[MovDir[x][y]].x;
3713 yy = y + move_xy[MovDir[x][y]].y;
3716 if (FrameCounter < 1 && x == 0 && y == 29)
3717 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3718 xx, yy, Feld[xx][yy],
3723 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3724 MovDir[x][y] = old_move_dir;
3726 if (!IS_FREE(xx, yy))
3727 MovDir[x][y] = old_move_dir;
3731 if (FrameCounter < 1 && x == 0 && y == 29)
3732 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3737 else if (element == EL_MOLE)
3739 boolean can_move_on =
3740 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3741 IS_AMOEBOID(Feld[move_x][move_y]) ||
3742 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3745 boolean can_turn_left =
3746 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3747 IS_AMOEBOID(Feld[left_x][left_y])));
3749 boolean can_turn_right =
3750 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3751 IS_AMOEBOID(Feld[right_x][right_y])));
3753 if (can_turn_left && can_turn_right)
3754 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3755 else if (can_turn_left)
3756 MovDir[x][y] = left_dir;
3758 MovDir[x][y] = right_dir;
3761 if (MovDir[x][y] != old_move_dir)
3764 else if (element == EL_BALLOON)
3766 MovDir[x][y] = game.balloon_dir;
3769 else if (element == EL_SPRING)
3771 if (MovDir[x][y] & MV_HORIZONTAL &&
3772 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3773 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3774 MovDir[x][y] = MV_NO_MOVING;
3778 else if (element == EL_ROBOT ||
3779 element == EL_SATELLITE ||
3780 element == EL_PENGUIN)
3782 int attr_x = -1, attr_y = -1;
3793 for (i = 0; i < MAX_PLAYERS; i++)
3795 struct PlayerInfo *player = &stored_player[i];
3796 int jx = player->jx, jy = player->jy;
3798 if (!player->active)
3802 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3810 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3816 if (element == EL_PENGUIN)
3819 static int xy[4][2] =
3827 for (i = 0; i < 4; i++)
3829 int ex = x + xy[i % 4][0];
3830 int ey = y + xy[i % 4][1];
3832 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3841 MovDir[x][y] = MV_NO_MOVING;
3843 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3844 else if (attr_x > x)
3845 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3847 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3848 else if (attr_y > y)
3849 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3851 if (element == EL_ROBOT)
3855 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3856 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3857 Moving2Blocked(x, y, &newx, &newy);
3859 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3860 MovDelay[x][y] = 8 + 8 * !RND(3);
3862 MovDelay[x][y] = 16;
3864 else if (element == EL_PENGUIN)
3870 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3872 boolean first_horiz = RND(2);
3873 int new_move_dir = MovDir[x][y];
3876 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3877 Moving2Blocked(x, y, &newx, &newy);
3879 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3883 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3884 Moving2Blocked(x, y, &newx, &newy);
3886 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3889 MovDir[x][y] = old_move_dir;
3893 else /* (element == EL_SATELLITE) */
3899 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3901 boolean first_horiz = RND(2);
3902 int new_move_dir = MovDir[x][y];
3905 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3906 Moving2Blocked(x, y, &newx, &newy);
3908 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3912 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3913 Moving2Blocked(x, y, &newx, &newy);
3915 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3918 MovDir[x][y] = old_move_dir;
3923 else if (move_pattern == MV_TURNING_LEFT ||
3924 move_pattern == MV_TURNING_RIGHT ||
3925 move_pattern == MV_TURNING_LEFT_RIGHT ||
3926 move_pattern == MV_TURNING_RIGHT_LEFT ||
3927 move_pattern == MV_TURNING_RANDOM ||
3928 move_pattern == MV_ALL_DIRECTIONS)
3930 boolean can_turn_left =
3931 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3932 boolean can_turn_right =
3933 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3935 if (move_pattern == MV_TURNING_LEFT)
3936 MovDir[x][y] = left_dir;
3937 else if (move_pattern == MV_TURNING_RIGHT)
3938 MovDir[x][y] = right_dir;
3939 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3940 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3941 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3942 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3943 else if (move_pattern == MV_TURNING_RANDOM)
3944 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3945 can_turn_right && !can_turn_left ? right_dir :
3946 RND(2) ? left_dir : right_dir);
3947 else if (can_turn_left && can_turn_right)
3948 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3949 else if (can_turn_left)
3950 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3951 else if (can_turn_right)
3952 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3954 MovDir[x][y] = back_dir;
3956 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3958 else if (move_pattern == MV_HORIZONTAL ||
3959 move_pattern == MV_VERTICAL)
3961 if (move_pattern & old_move_dir)
3962 MovDir[x][y] = back_dir;
3963 else if (move_pattern == MV_HORIZONTAL)
3964 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3965 else if (move_pattern == MV_VERTICAL)
3966 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3968 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3970 else if (move_pattern & MV_ANY_DIRECTION)
3972 MovDir[x][y] = move_pattern;
3973 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3975 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3977 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3978 MovDir[x][y] = left_dir;
3979 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3980 MovDir[x][y] = right_dir;
3982 if (MovDir[x][y] != old_move_dir)
3983 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3985 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3987 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3988 MovDir[x][y] = right_dir;
3989 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3990 MovDir[x][y] = left_dir;
3992 if (MovDir[x][y] != old_move_dir)
3993 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3995 else if (move_pattern == MV_TOWARDS_PLAYER ||
3996 move_pattern == MV_AWAY_FROM_PLAYER)
3998 int attr_x = -1, attr_y = -1;
4000 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4011 for (i = 0; i < MAX_PLAYERS; i++)
4013 struct PlayerInfo *player = &stored_player[i];
4014 int jx = player->jx, jy = player->jy;
4016 if (!player->active)
4020 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4028 MovDir[x][y] = MV_NO_MOVING;
4030 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4031 else if (attr_x > x)
4032 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4034 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4035 else if (attr_y > y)
4036 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4038 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4040 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4042 boolean first_horiz = RND(2);
4043 int new_move_dir = MovDir[x][y];
4046 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4047 Moving2Blocked(x, y, &newx, &newy);
4049 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4053 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4054 Moving2Blocked(x, y, &newx, &newy);
4056 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4059 MovDir[x][y] = old_move_dir;
4062 else if (move_pattern == MV_WHEN_PUSHED ||
4063 move_pattern == MV_WHEN_DROPPED)
4065 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4066 MovDir[x][y] = MV_NO_MOVING;
4070 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4072 static int test_xy[7][2] =
4082 static int test_dir[7] =
4092 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4093 int move_preference = -1000000; /* start with very low preference */
4094 int new_move_dir = MV_NO_MOVING;
4095 int start_test = RND(4);
4098 for (i = 0; i < 4; i++)
4100 int move_dir = test_dir[start_test + i];
4101 int move_dir_preference;
4103 xx = x + test_xy[start_test + i][0];
4104 yy = y + test_xy[start_test + i][1];
4106 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4107 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4109 new_move_dir = move_dir;
4114 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4117 move_dir_preference = -1 * RunnerVisit[xx][yy];
4118 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4119 move_dir_preference = PlayerVisit[xx][yy];
4121 if (move_dir_preference > move_preference)
4123 /* prefer field that has not been visited for the longest time */
4124 move_preference = move_dir_preference;
4125 new_move_dir = move_dir;
4127 else if (move_dir_preference == move_preference &&
4128 move_dir == old_move_dir)
4130 /* prefer last direction when all directions are preferred equally */
4131 move_preference = move_dir_preference;
4132 new_move_dir = move_dir;
4136 MovDir[x][y] = new_move_dir;
4137 if (old_move_dir != new_move_dir)
4142 static void TurnRound(int x, int y)
4144 int direction = MovDir[x][y];
4147 GfxDir[x][y] = MovDir[x][y];
4153 GfxDir[x][y] = MovDir[x][y];
4156 if (direction != MovDir[x][y])
4161 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4164 GfxAction[x][y] = ACTION_WAITING;
4168 static boolean JustBeingPushed(int x, int y)
4172 for (i = 0; i < MAX_PLAYERS; i++)
4174 struct PlayerInfo *player = &stored_player[i];
4176 if (player->active && player->is_pushing && player->MovPos)
4178 int next_jx = player->jx + (player->jx - player->last_jx);
4179 int next_jy = player->jy + (player->jy - player->last_jy);
4181 if (x == next_jx && y == next_jy)
4189 void StartMoving(int x, int y)
4191 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4192 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4193 int element = Feld[x][y];
4199 if (MovDelay[x][y] == 0)
4200 GfxAction[x][y] = ACTION_DEFAULT;
4202 /* !!! this should be handled more generic (not only for mole) !!! */
4203 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4204 GfxAction[x][y] = ACTION_DEFAULT;
4207 if (CAN_FALL(element) && y < lev_fieldy - 1)
4209 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4210 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4211 if (JustBeingPushed(x, y))
4214 if (element == EL_QUICKSAND_FULL)
4216 if (IS_FREE(x, y + 1))
4218 InitMovingField(x, y, MV_DOWN);
4219 started_moving = TRUE;
4221 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4222 Store[x][y] = EL_ROCK;
4224 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4226 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4229 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4231 if (!MovDelay[x][y])
4232 MovDelay[x][y] = TILEY + 1;
4241 Feld[x][y] = EL_QUICKSAND_EMPTY;
4242 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4243 Store[x][y + 1] = Store[x][y];
4246 PlayLevelSoundAction(x, y, ACTION_FILLING);
4248 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4252 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4253 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4255 InitMovingField(x, y, MV_DOWN);
4256 started_moving = TRUE;
4258 Feld[x][y] = EL_QUICKSAND_FILLING;
4259 Store[x][y] = element;
4261 PlayLevelSoundAction(x, y, ACTION_FILLING);
4263 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4266 else if (element == EL_MAGIC_WALL_FULL)
4268 if (IS_FREE(x, y + 1))
4270 InitMovingField(x, y, MV_DOWN);
4271 started_moving = TRUE;
4273 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4274 Store[x][y] = EL_CHANGED(Store[x][y]);
4276 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4278 if (!MovDelay[x][y])
4279 MovDelay[x][y] = TILEY/4 + 1;
4288 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4289 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4290 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4294 else if (element == EL_BD_MAGIC_WALL_FULL)
4296 if (IS_FREE(x, y + 1))
4298 InitMovingField(x, y, MV_DOWN);
4299 started_moving = TRUE;
4301 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4302 Store[x][y] = EL_CHANGED2(Store[x][y]);
4304 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4306 if (!MovDelay[x][y])
4307 MovDelay[x][y] = TILEY/4 + 1;
4316 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4317 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4318 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4322 else if (CAN_PASS_MAGIC_WALL(element) &&
4323 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4324 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4326 InitMovingField(x, y, MV_DOWN);
4327 started_moving = TRUE;
4330 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4331 EL_BD_MAGIC_WALL_FILLING);
4332 Store[x][y] = element;
4335 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4337 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4342 InitMovingField(x, y, MV_DOWN);
4343 started_moving = TRUE;
4345 Store[x][y] = EL_ACID;
4347 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4348 GfxAction[x][y + 1] = ACTION_ACTIVE;
4352 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4353 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4354 (Feld[x][y + 1] == EL_BLOCKED)) ||
4355 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4356 CAN_SMASH(element) && WasJustFalling[x][y] &&
4357 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4361 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4362 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4363 WasJustMoving[x][y] && !Pushed[x][y + 1])
4365 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4366 WasJustMoving[x][y])
4371 /* this is needed for a special case not covered by calling "Impact()"
4372 from "ContinueMoving()": if an element moves to a tile directly below
4373 another element which was just falling on that tile (which was empty
4374 in the previous frame), the falling element above would just stop
4375 instead of smashing the element below (in previous version, the above
4376 element was just checked for "moving" instead of "falling", resulting
4377 in incorrect smashes caused by horizontal movement of the above
4378 element; also, the case of the player being the element to smash was
4379 simply not covered here... :-/ ) */
4382 WasJustMoving[x][y] = 0;
4383 WasJustFalling[x][y] = 0;
4388 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4390 if (MovDir[x][y] == MV_NO_MOVING)
4392 InitMovingField(x, y, MV_DOWN);
4393 started_moving = TRUE;
4396 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4398 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4399 MovDir[x][y] = MV_DOWN;
4401 InitMovingField(x, y, MV_DOWN);
4402 started_moving = TRUE;
4404 else if (element == EL_AMOEBA_DROP)
4406 Feld[x][y] = EL_AMOEBA_GROWING;
4407 Store[x][y] = EL_AMOEBA_WET;
4409 /* Store[x][y + 1] must be zero, because:
4410 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4413 #if OLD_GAME_BEHAVIOUR
4414 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4416 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4417 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4418 element != EL_DX_SUPABOMB)
4421 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4422 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4423 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4424 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4427 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4428 (IS_FREE(x - 1, y + 1) ||
4429 Feld[x - 1][y + 1] == EL_ACID));
4430 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4431 (IS_FREE(x + 1, y + 1) ||
4432 Feld[x + 1][y + 1] == EL_ACID));
4433 boolean can_fall_any = (can_fall_left || can_fall_right);
4434 boolean can_fall_both = (can_fall_left && can_fall_right);
4436 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4438 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4440 if (slippery_type == SLIPPERY_ONLY_LEFT)
4441 can_fall_right = FALSE;
4442 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4443 can_fall_left = FALSE;
4444 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4445 can_fall_right = FALSE;
4446 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4447 can_fall_left = FALSE;
4449 can_fall_any = (can_fall_left || can_fall_right);
4450 can_fall_both = (can_fall_left && can_fall_right);
4455 if (can_fall_both &&
4456 (game.emulation != EMU_BOULDERDASH &&
4457 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4458 can_fall_left = !(can_fall_right = RND(2));
4460 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4461 started_moving = TRUE;
4464 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4466 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4467 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4468 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4469 int belt_dir = game.belt_dir[belt_nr];
4471 if ((belt_dir == MV_LEFT && left_is_free) ||
4472 (belt_dir == MV_RIGHT && right_is_free))
4474 InitMovingField(x, y, belt_dir);
4475 started_moving = TRUE;
4477 GfxAction[x][y] = ACTION_DEFAULT;
4482 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4483 if (CAN_MOVE(element) && !started_moving)
4485 int move_pattern = element_info[element].move_pattern;
4488 Moving2Blocked(x, y, &newx, &newy);
4491 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4494 if ((element == EL_SATELLITE ||
4495 element == EL_BALLOON ||
4496 element == EL_SPRING)
4497 && JustBeingPushed(x, y))
4502 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4503 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4504 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4507 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4508 element, element_info[element].token_name,
4509 WasJustMoving[x][y],
4510 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4511 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4512 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4513 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4517 WasJustMoving[x][y] = 0;
4520 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4523 if (Feld[x][y] != element) /* element has changed */
4525 element = Feld[x][y];
4526 move_pattern = element_info[element].move_pattern;
4528 if (!CAN_MOVE(element))
4532 if (Feld[x][y] != element) /* element has changed */
4540 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4541 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4543 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4545 Moving2Blocked(x, y, &newx, &newy);
4546 if (Feld[newx][newy] == EL_BLOCKED)
4547 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4553 if (FrameCounter < 1 && x == 0 && y == 29)
4554 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4557 if (!MovDelay[x][y]) /* start new movement phase */
4559 /* all objects that can change their move direction after each step
4560 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4562 if (element != EL_YAMYAM &&
4563 element != EL_DARK_YAMYAM &&
4564 element != EL_PACMAN &&
4565 !(move_pattern & MV_ANY_DIRECTION) &&
4566 move_pattern != MV_TURNING_LEFT &&
4567 move_pattern != MV_TURNING_RIGHT &&
4568 move_pattern != MV_TURNING_LEFT_RIGHT &&
4569 move_pattern != MV_TURNING_RIGHT_LEFT &&
4570 move_pattern != MV_TURNING_RANDOM)
4575 if (FrameCounter < 1 && x == 0 && y == 29)
4576 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4579 if (MovDelay[x][y] && (element == EL_BUG ||
4580 element == EL_SPACESHIP ||
4581 element == EL_SP_SNIKSNAK ||
4582 element == EL_SP_ELECTRON ||
4583 element == EL_MOLE))
4584 DrawLevelField(x, y);
4588 if (MovDelay[x][y]) /* wait some time before next movement */
4593 if (element == EL_YAMYAM)
4596 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4597 DrawLevelElementAnimation(x, y, element);
4601 if (MovDelay[x][y]) /* element still has to wait some time */
4604 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4605 ResetGfxAnimation(x, y);
4609 if (GfxAction[x][y] != ACTION_WAITING)
4610 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4612 GfxAction[x][y] = ACTION_WAITING;
4616 if (element == EL_ROBOT ||
4618 element == EL_PACMAN ||
4620 element == EL_YAMYAM ||
4621 element == EL_DARK_YAMYAM)
4624 DrawLevelElementAnimation(x, y, element);
4626 DrawLevelElementAnimationIfNeeded(x, y, element);
4628 PlayLevelSoundAction(x, y, ACTION_WAITING);
4630 else if (element == EL_SP_ELECTRON)
4631 DrawLevelElementAnimationIfNeeded(x, y, element);
4632 else if (element == EL_DRAGON)
4635 int dir = MovDir[x][y];
4636 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4637 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4638 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4639 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4640 dir == MV_UP ? IMG_FLAMES_1_UP :
4641 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4642 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4645 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4648 GfxAction[x][y] = ACTION_ATTACKING;
4650 if (IS_PLAYER(x, y))
4651 DrawPlayerField(x, y);
4653 DrawLevelField(x, y);
4655 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4657 for (i = 1; i <= 3; i++)
4659 int xx = x + i * dx;
4660 int yy = y + i * dy;
4661 int sx = SCREENX(xx);
4662 int sy = SCREENY(yy);
4663 int flame_graphic = graphic + (i - 1);
4665 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4670 int flamed = MovingOrBlocked2Element(xx, yy);
4672 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4675 RemoveMovingField(xx, yy);
4677 Feld[xx][yy] = EL_FLAMES;
4678 if (IN_SCR_FIELD(sx, sy))
4680 DrawLevelFieldCrumbledSand(xx, yy);
4681 DrawGraphic(sx, sy, flame_graphic, frame);
4686 if (Feld[xx][yy] == EL_FLAMES)
4687 Feld[xx][yy] = EL_EMPTY;
4688 DrawLevelField(xx, yy);
4693 if (MovDelay[x][y]) /* element still has to wait some time */
4695 PlayLevelSoundAction(x, y, ACTION_WAITING);
4701 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4702 for all other elements GfxAction will be set by InitMovingField() */
4703 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4704 GfxAction[x][y] = ACTION_MOVING;
4708 /* now make next step */
4710 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4712 if (DONT_COLLIDE_WITH(element) &&
4713 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4714 !PLAYER_ENEMY_PROTECTED(newx, newy))
4717 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4721 /* player killed by element which is deadly when colliding with */
4723 KillHero(PLAYERINFO(newx, newy));
4728 else if ((element == EL_PENGUIN ||
4729 element == EL_ROBOT ||
4730 element == EL_SATELLITE ||
4731 element == EL_BALLOON ||
4732 IS_CUSTOM_ELEMENT(element)) &&
4733 IN_LEV_FIELD(newx, newy) &&
4734 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4737 Store[x][y] = EL_ACID;
4739 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4741 if (Feld[newx][newy] == EL_EXIT_OPEN)
4745 DrawLevelField(x, y);
4747 Feld[x][y] = EL_EMPTY;
4748 DrawLevelField(x, y);
4751 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4752 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4753 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4755 local_player->friends_still_needed--;
4756 if (!local_player->friends_still_needed &&
4757 !local_player->GameOver && AllPlayersGone)
4758 local_player->LevelSolved = local_player->GameOver = TRUE;
4762 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4764 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4765 DrawLevelField(newx, newy);
4767 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4769 else if (!IS_FREE(newx, newy))
4771 GfxAction[x][y] = ACTION_WAITING;
4773 if (IS_PLAYER(x, y))
4774 DrawPlayerField(x, y);
4776 DrawLevelField(x, y);
4781 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4783 if (IS_FOOD_PIG(Feld[newx][newy]))
4785 if (IS_MOVING(newx, newy))
4786 RemoveMovingField(newx, newy);
4789 Feld[newx][newy] = EL_EMPTY;
4790 DrawLevelField(newx, newy);
4793 PlayLevelSound(x, y, SND_PIG_DIGGING);
4795 else if (!IS_FREE(newx, newy))
4797 if (IS_PLAYER(x, y))
4798 DrawPlayerField(x, y);
4800 DrawLevelField(x, y);
4809 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4812 else if (IS_CUSTOM_ELEMENT(element) &&
4813 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4817 !IS_FREE(newx, newy)
4822 int new_element = Feld[newx][newy];
4825 printf("::: '%s' digs '%s' [%d]\n",
4826 element_info[element].token_name,
4827 element_info[Feld[newx][newy]].token_name,
4828 StorePlayer[newx][newy]);
4831 if (!IS_FREE(newx, newy))
4833 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4834 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4837 /* no element can dig solid indestructible elements */
4838 if (IS_INDESTRUCTIBLE(new_element) &&
4839 !IS_DIGGABLE(new_element) &&
4840 !IS_COLLECTIBLE(new_element))
4843 if (AmoebaNr[newx][newy] &&
4844 (new_element == EL_AMOEBA_FULL ||
4845 new_element == EL_BD_AMOEBA ||
4846 new_element == EL_AMOEBA_GROWING))
4848 AmoebaCnt[AmoebaNr[newx][newy]]--;
4849 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4852 if (IS_MOVING(newx, newy))
4853 RemoveMovingField(newx, newy);
4856 RemoveField(newx, newy);
4857 DrawLevelField(newx, newy);
4860 PlayLevelSoundAction(x, y, action);
4863 if (new_element == element_info[element].move_enter_element)
4864 element_info[element].can_leave_element = TRUE;
4866 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4868 RunnerVisit[x][y] = FrameCounter;
4869 PlayerVisit[x][y] /= 8; /* expire player visit path */
4875 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4877 if (!IS_FREE(newx, newy))
4879 if (IS_PLAYER(x, y))
4880 DrawPlayerField(x, y);
4882 DrawLevelField(x, y);
4888 boolean wanna_flame = !RND(10);
4889 int dx = newx - x, dy = newy - y;
4890 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4891 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4892 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4893 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4894 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4895 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4898 IS_CLASSIC_ENEMY(element1) ||
4899 IS_CLASSIC_ENEMY(element2)) &&
4900 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4901 element1 != EL_FLAMES && element2 != EL_FLAMES)
4904 ResetGfxAnimation(x, y);
4905 GfxAction[x][y] = ACTION_ATTACKING;
4908 if (IS_PLAYER(x, y))
4909 DrawPlayerField(x, y);
4911 DrawLevelField(x, y);
4913 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4915 MovDelay[x][y] = 50;
4917 Feld[newx][newy] = EL_FLAMES;
4918 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4919 Feld[newx1][newy1] = EL_FLAMES;
4920 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4921 Feld[newx2][newy2] = EL_FLAMES;
4927 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4928 Feld[newx][newy] == EL_DIAMOND)
4930 if (IS_MOVING(newx, newy))
4931 RemoveMovingField(newx, newy);
4934 Feld[newx][newy] = EL_EMPTY;
4935 DrawLevelField(newx, newy);
4938 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4940 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4941 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4943 if (AmoebaNr[newx][newy])
4945 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4946 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4947 Feld[newx][newy] == EL_BD_AMOEBA)
4948 AmoebaCnt[AmoebaNr[newx][newy]]--;
4951 if (IS_MOVING(newx, newy))
4952 RemoveMovingField(newx, newy);
4955 Feld[newx][newy] = EL_EMPTY;
4956 DrawLevelField(newx, newy);
4959 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4961 else if ((element == EL_PACMAN || element == EL_MOLE)
4962 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4964 if (AmoebaNr[newx][newy])
4966 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4967 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4968 Feld[newx][newy] == EL_BD_AMOEBA)
4969 AmoebaCnt[AmoebaNr[newx][newy]]--;
4972 if (element == EL_MOLE)
4974 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4975 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4977 ResetGfxAnimation(x, y);
4978 GfxAction[x][y] = ACTION_DIGGING;
4979 DrawLevelField(x, y);
4981 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4983 return; /* wait for shrinking amoeba */
4985 else /* element == EL_PACMAN */
4987 Feld[newx][newy] = EL_EMPTY;
4988 DrawLevelField(newx, newy);
4989 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4992 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4993 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4994 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4996 /* wait for shrinking amoeba to completely disappear */
4999 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5001 /* object was running against a wall */
5006 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5007 DrawLevelElementAnimation(x, y, element);
5009 if (element == EL_BUG ||
5010 element == EL_SPACESHIP ||
5011 element == EL_SP_SNIKSNAK)
5012 DrawLevelField(x, y);
5013 else if (element == EL_MOLE)
5014 DrawLevelField(x, y);
5015 else if (element == EL_BD_BUTTERFLY ||
5016 element == EL_BD_FIREFLY)
5017 DrawLevelElementAnimationIfNeeded(x, y, element);
5018 else if (element == EL_SATELLITE)
5019 DrawLevelElementAnimationIfNeeded(x, y, element);
5020 else if (element == EL_SP_ELECTRON)
5021 DrawLevelElementAnimationIfNeeded(x, y, element);
5024 if (DONT_TOUCH(element))
5025 TestIfBadThingTouchesHero(x, y);
5028 PlayLevelSoundAction(x, y, ACTION_WAITING);
5034 InitMovingField(x, y, MovDir[x][y]);
5036 PlayLevelSoundAction(x, y, ACTION_MOVING);
5040 ContinueMoving(x, y);
5043 void ContinueMoving(int x, int y)
5045 int element = Feld[x][y];
5046 struct ElementInfo *ei = &element_info[element];
5047 int direction = MovDir[x][y];
5048 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5049 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5050 int newx = x + dx, newy = y + dy;
5052 int nextx = newx + dx, nexty = newy + dy;
5054 boolean pushed = Pushed[x][y];
5056 MovPos[x][y] += getElementMoveStepsize(x, y);
5058 if (pushed) /* special case: moving object pushed by player */
5059 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5061 if (ABS(MovPos[x][y]) < TILEX)
5063 DrawLevelField(x, y);
5065 return; /* element is still moving */
5068 /* element reached destination field */
5070 Feld[x][y] = EL_EMPTY;
5071 Feld[newx][newy] = element;
5072 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5074 if (element == EL_MOLE)
5076 Feld[x][y] = EL_SAND;
5078 DrawLevelFieldCrumbledSandNeighbours(x, y);
5080 else if (element == EL_QUICKSAND_FILLING)
5082 element = Feld[newx][newy] = get_next_element(element);
5083 Store[newx][newy] = Store[x][y];
5085 else if (element == EL_QUICKSAND_EMPTYING)
5087 Feld[x][y] = get_next_element(element);
5088 element = Feld[newx][newy] = Store[x][y];
5090 else if (element == EL_MAGIC_WALL_FILLING)
5092 element = Feld[newx][newy] = get_next_element(element);
5093 if (!game.magic_wall_active)
5094 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5095 Store[newx][newy] = Store[x][y];
5097 else if (element == EL_MAGIC_WALL_EMPTYING)
5099 Feld[x][y] = get_next_element(element);
5100 if (!game.magic_wall_active)
5101 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5102 element = Feld[newx][newy] = Store[x][y];
5104 else if (element == EL_BD_MAGIC_WALL_FILLING)
5106 element = Feld[newx][newy] = get_next_element(element);
5107 if (!game.magic_wall_active)
5108 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5109 Store[newx][newy] = Store[x][y];
5111 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5113 Feld[x][y] = get_next_element(element);
5114 if (!game.magic_wall_active)
5115 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5116 element = Feld[newx][newy] = Store[x][y];
5118 else if (element == EL_AMOEBA_DROPPING)
5120 Feld[x][y] = get_next_element(element);
5121 element = Feld[newx][newy] = Store[x][y];
5123 else if (element == EL_SOKOBAN_OBJECT)
5126 Feld[x][y] = Back[x][y];
5128 if (Back[newx][newy])
5129 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5131 Back[x][y] = Back[newx][newy] = 0;
5133 else if (Store[x][y] == EL_ACID)
5135 element = Feld[newx][newy] = EL_ACID;
5139 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5140 MovDelay[newx][newy] = 0;
5142 /* copy element change control values to new field */
5143 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5144 ChangePage[newx][newy] = ChangePage[x][y];
5145 Changed[newx][newy] = Changed[x][y];
5146 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5148 ChangeDelay[x][y] = 0;
5149 ChangePage[x][y] = -1;
5150 Changed[x][y] = CE_BITMASK_DEFAULT;
5151 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5153 /* copy animation control values to new field */
5154 GfxFrame[newx][newy] = GfxFrame[x][y];
5155 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5156 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5157 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5159 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5161 ResetGfxAnimation(x, y); /* reset animation values for old field */
5164 /* some elements can leave other elements behind after moving */
5165 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5166 ei->move_leave_element != EL_EMPTY &&
5167 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5168 ei->can_leave_element_last))
5170 Feld[x][y] = ei->move_leave_element;
5171 InitField(x, y, FALSE);
5173 if (GFX_CRUMBLED(Feld[x][y]))
5174 DrawLevelFieldCrumbledSandNeighbours(x, y);
5177 ei->can_leave_element_last = ei->can_leave_element;
5178 ei->can_leave_element = FALSE;
5182 /* 2.1.1 (does not work correctly for spring) */
5183 if (!CAN_MOVE(element))
5184 MovDir[newx][newy] = 0;
5188 /* (does not work for falling objects that slide horizontally) */
5189 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5190 MovDir[newx][newy] = 0;
5193 if (!CAN_MOVE(element) ||
5194 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5195 MovDir[newx][newy] = 0;
5198 if (!CAN_MOVE(element) ||
5199 (CAN_FALL(element) && direction == MV_DOWN))
5200 GfxDir[x][y] = MovDir[newx][newy] = 0;
5205 DrawLevelField(x, y);
5206 DrawLevelField(newx, newy);
5208 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5210 /* prevent pushed element from moving on in pushed direction */
5211 if (pushed && CAN_MOVE(element) &&
5212 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5213 !(element_info[element].move_pattern & direction))
5214 TurnRound(newx, newy);
5216 if (!pushed) /* special case: moving object pushed by player */
5218 WasJustMoving[newx][newy] = 3;
5220 if (CAN_FALL(element) && direction == MV_DOWN)
5221 WasJustFalling[newx][newy] = 3;
5224 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5226 TestIfBadThingTouchesHero(newx, newy);
5227 TestIfBadThingTouchesFriend(newx, newy);
5229 if (!IS_CUSTOM_ELEMENT(element))
5230 TestIfBadThingTouchesOtherBadThing(newx, newy);
5232 else if (element == EL_PENGUIN)
5233 TestIfFriendTouchesBadThing(newx, newy);
5235 if (CAN_FALL(element) && direction == MV_DOWN &&
5236 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5240 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5244 if (ChangePage[newx][newy] != -1) /* delayed change */
5245 ChangeElement(newx, newy, ChangePage[newx][newy]);
5250 TestIfElementHitsCustomElement(newx, newy, direction);
5254 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5256 int hitting_element = Feld[newx][newy];
5258 /* !!! fix side (direction) orientation here and elsewhere !!! */
5259 CheckElementSideChange(newx, newy, hitting_element,
5260 direction, CE_HITTING_SOMETHING, -1);
5263 if (IN_LEV_FIELD(nextx, nexty))
5265 int opposite_direction = MV_DIR_OPPOSITE(direction);
5266 int hitting_side = direction;
5267 int touched_side = opposite_direction;
5268 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5269 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5270 MovDir[nextx][nexty] != direction ||
5271 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5277 CheckElementSideChange(nextx, nexty, touched_element,
5278 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5280 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5281 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5283 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5285 struct ElementChangeInfo *change =
5286 &element_info[hitting_element].change_page[i];
5288 if (change->can_change &&
5289 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5290 change->sides & touched_side &&
5291 change->trigger_element == touched_element)
5293 CheckElementSideChange(newx, newy, hitting_element,
5294 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5300 if (IS_CUSTOM_ELEMENT(touched_element) &&
5301 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5303 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5305 struct ElementChangeInfo *change =
5306 &element_info[touched_element].change_page[i];
5308 if (change->can_change &&
5309 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5310 change->sides & hitting_side &&
5311 change->trigger_element == hitting_element)
5313 CheckElementSideChange(nextx, nexty, touched_element,
5314 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5325 TestIfPlayerTouchesCustomElement(newx, newy);
5326 TestIfElementTouchesCustomElement(newx, newy);
5329 int AmoebeNachbarNr(int ax, int ay)
5332 int element = Feld[ax][ay];
5334 static int xy[4][2] =
5342 for (i = 0; i < 4; i++)
5344 int x = ax + xy[i][0];
5345 int y = ay + xy[i][1];
5347 if (!IN_LEV_FIELD(x, y))
5350 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5351 group_nr = AmoebaNr[x][y];
5357 void AmoebenVereinigen(int ax, int ay)
5359 int i, x, y, xx, yy;
5360 int new_group_nr = AmoebaNr[ax][ay];
5361 static int xy[4][2] =
5369 if (new_group_nr == 0)
5372 for (i = 0; i < 4; i++)
5377 if (!IN_LEV_FIELD(x, y))
5380 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5381 Feld[x][y] == EL_BD_AMOEBA ||
5382 Feld[x][y] == EL_AMOEBA_DEAD) &&
5383 AmoebaNr[x][y] != new_group_nr)
5385 int old_group_nr = AmoebaNr[x][y];
5387 if (old_group_nr == 0)
5390 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5391 AmoebaCnt[old_group_nr] = 0;
5392 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5393 AmoebaCnt2[old_group_nr] = 0;
5395 for (yy = 0; yy < lev_fieldy; yy++)
5397 for (xx = 0; xx < lev_fieldx; xx++)
5399 if (AmoebaNr[xx][yy] == old_group_nr)
5400 AmoebaNr[xx][yy] = new_group_nr;
5407 void AmoebeUmwandeln(int ax, int ay)
5411 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5413 int group_nr = AmoebaNr[ax][ay];
5418 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5419 printf("AmoebeUmwandeln(): This should never happen!\n");
5424 for (y = 0; y < lev_fieldy; y++)
5426 for (x = 0; x < lev_fieldx; x++)
5428 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5431 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5435 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5436 SND_AMOEBA_TURNING_TO_GEM :
5437 SND_AMOEBA_TURNING_TO_ROCK));
5442 static int xy[4][2] =
5450 for (i = 0; i < 4; i++)
5455 if (!IN_LEV_FIELD(x, y))
5458 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5460 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5461 SND_AMOEBA_TURNING_TO_GEM :
5462 SND_AMOEBA_TURNING_TO_ROCK));
5469 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5472 int group_nr = AmoebaNr[ax][ay];
5473 boolean done = FALSE;
5478 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5479 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5484 for (y = 0; y < lev_fieldy; y++)
5486 for (x = 0; x < lev_fieldx; x++)
5488 if (AmoebaNr[x][y] == group_nr &&
5489 (Feld[x][y] == EL_AMOEBA_DEAD ||
5490 Feld[x][y] == EL_BD_AMOEBA ||
5491 Feld[x][y] == EL_AMOEBA_GROWING))
5494 Feld[x][y] = new_element;
5495 InitField(x, y, FALSE);
5496 DrawLevelField(x, y);
5503 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5504 SND_BD_AMOEBA_TURNING_TO_ROCK :
5505 SND_BD_AMOEBA_TURNING_TO_GEM));
5508 void AmoebeWaechst(int x, int y)
5510 static unsigned long sound_delay = 0;
5511 static unsigned long sound_delay_value = 0;
5513 if (!MovDelay[x][y]) /* start new growing cycle */
5517 if (DelayReached(&sound_delay, sound_delay_value))
5520 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5522 if (Store[x][y] == EL_BD_AMOEBA)
5523 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5525 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5527 sound_delay_value = 30;
5531 if (MovDelay[x][y]) /* wait some time before growing bigger */
5534 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5536 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5537 6 - MovDelay[x][y]);
5539 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5542 if (!MovDelay[x][y])
5544 Feld[x][y] = Store[x][y];
5546 DrawLevelField(x, y);
5551 void AmoebaDisappearing(int x, int y)
5553 static unsigned long sound_delay = 0;
5554 static unsigned long sound_delay_value = 0;
5556 if (!MovDelay[x][y]) /* start new shrinking cycle */
5560 if (DelayReached(&sound_delay, sound_delay_value))
5561 sound_delay_value = 30;
5564 if (MovDelay[x][y]) /* wait some time before shrinking */
5567 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5569 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5570 6 - MovDelay[x][y]);
5572 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5575 if (!MovDelay[x][y])
5577 Feld[x][y] = EL_EMPTY;
5578 DrawLevelField(x, y);
5580 /* don't let mole enter this field in this cycle;
5581 (give priority to objects falling to this field from above) */
5587 void AmoebeAbleger(int ax, int ay)
5590 int element = Feld[ax][ay];
5591 int graphic = el2img(element);
5592 int newax = ax, neway = ay;
5593 static int xy[4][2] =
5601 if (!level.amoeba_speed)
5603 Feld[ax][ay] = EL_AMOEBA_DEAD;
5604 DrawLevelField(ax, ay);
5608 if (IS_ANIMATED(graphic))
5609 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5611 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5612 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5614 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5617 if (MovDelay[ax][ay])
5621 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5624 int x = ax + xy[start][0];
5625 int y = ay + xy[start][1];
5627 if (!IN_LEV_FIELD(x, y))
5630 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5631 if (IS_FREE(x, y) ||
5632 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5638 if (newax == ax && neway == ay)
5641 else /* normal or "filled" (BD style) amoeba */
5644 boolean waiting_for_player = FALSE;
5646 for (i = 0; i < 4; i++)
5648 int j = (start + i) % 4;
5649 int x = ax + xy[j][0];
5650 int y = ay + xy[j][1];
5652 if (!IN_LEV_FIELD(x, y))
5655 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5656 if (IS_FREE(x, y) ||
5657 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5663 else if (IS_PLAYER(x, y))
5664 waiting_for_player = TRUE;
5667 if (newax == ax && neway == ay) /* amoeba cannot grow */
5669 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5671 Feld[ax][ay] = EL_AMOEBA_DEAD;
5672 DrawLevelField(ax, ay);
5673 AmoebaCnt[AmoebaNr[ax][ay]]--;
5675 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5677 if (element == EL_AMOEBA_FULL)
5678 AmoebeUmwandeln(ax, ay);
5679 else if (element == EL_BD_AMOEBA)
5680 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5685 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5687 /* amoeba gets larger by growing in some direction */
5689 int new_group_nr = AmoebaNr[ax][ay];
5692 if (new_group_nr == 0)
5694 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5695 printf("AmoebeAbleger(): This should never happen!\n");
5700 AmoebaNr[newax][neway] = new_group_nr;
5701 AmoebaCnt[new_group_nr]++;
5702 AmoebaCnt2[new_group_nr]++;
5704 /* if amoeba touches other amoeba(s) after growing, unify them */
5705 AmoebenVereinigen(newax, neway);
5707 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5709 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5715 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5716 (neway == lev_fieldy - 1 && newax != ax))
5718 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5719 Store[newax][neway] = element;
5721 else if (neway == ay)
5723 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5725 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5727 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5732 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5733 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5734 Store[ax][ay] = EL_AMOEBA_DROP;
5735 ContinueMoving(ax, ay);
5739 DrawLevelField(newax, neway);
5742 void Life(int ax, int ay)
5745 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5747 int element = Feld[ax][ay];
5748 int graphic = el2img(element);
5749 boolean changed = FALSE;
5751 if (IS_ANIMATED(graphic))
5752 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5757 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5758 MovDelay[ax][ay] = life_time;
5760 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5763 if (MovDelay[ax][ay])
5767 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5769 int xx = ax+x1, yy = ay+y1;
5772 if (!IN_LEV_FIELD(xx, yy))
5775 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5777 int x = xx+x2, y = yy+y2;
5779 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5782 if (((Feld[x][y] == element ||
5783 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5785 (IS_FREE(x, y) && Stop[x][y]))
5789 if (xx == ax && yy == ay) /* field in the middle */
5791 if (nachbarn < life[0] || nachbarn > life[1])
5793 Feld[xx][yy] = EL_EMPTY;
5795 DrawLevelField(xx, yy);
5796 Stop[xx][yy] = TRUE;
5800 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5801 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5802 { /* free border field */
5803 if (nachbarn >= life[2] && nachbarn <= life[3])
5805 Feld[xx][yy] = element;
5806 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5808 DrawLevelField(xx, yy);
5809 Stop[xx][yy] = TRUE;
5816 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5817 SND_GAME_OF_LIFE_GROWING);
5820 static void InitRobotWheel(int x, int y)
5822 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5825 static void RunRobotWheel(int x, int y)
5827 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5830 static void StopRobotWheel(int x, int y)
5832 if (ZX == x && ZY == y)
5836 static void InitTimegateWheel(int x, int y)
5838 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5841 static void RunTimegateWheel(int x, int y)
5843 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5846 void CheckExit(int x, int y)
5848 if (local_player->gems_still_needed > 0 ||
5849 local_player->sokobanfields_still_needed > 0 ||
5850 local_player->lights_still_needed > 0)
5852 int element = Feld[x][y];
5853 int graphic = el2img(element);
5855 if (IS_ANIMATED(graphic))
5856 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5861 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5864 Feld[x][y] = EL_EXIT_OPENING;
5866 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5869 void CheckExitSP(int x, int y)
5871 if (local_player->gems_still_needed > 0)
5873 int element = Feld[x][y];
5874 int graphic = el2img(element);
5876 if (IS_ANIMATED(graphic))
5877 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5882 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5885 Feld[x][y] = EL_SP_EXIT_OPENING;
5887 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5890 static void CloseAllOpenTimegates()
5894 for (y = 0; y < lev_fieldy; y++)
5896 for (x = 0; x < lev_fieldx; x++)
5898 int element = Feld[x][y];
5900 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5902 Feld[x][y] = EL_TIMEGATE_CLOSING;
5904 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5906 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5913 void EdelsteinFunkeln(int x, int y)
5915 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5918 if (Feld[x][y] == EL_BD_DIAMOND)
5921 if (MovDelay[x][y] == 0) /* next animation frame */
5922 MovDelay[x][y] = 11 * !SimpleRND(500);
5924 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5928 if (setup.direct_draw && MovDelay[x][y])
5929 SetDrawtoField(DRAW_BUFFERED);
5931 DrawLevelElementAnimation(x, y, Feld[x][y]);
5933 if (MovDelay[x][y] != 0)
5935 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5936 10 - MovDelay[x][y]);
5938 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5940 if (setup.direct_draw)
5944 dest_x = FX + SCREENX(x) * TILEX;
5945 dest_y = FY + SCREENY(y) * TILEY;
5947 BlitBitmap(drawto_field, window,
5948 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5949 SetDrawtoField(DRAW_DIRECT);
5955 void MauerWaechst(int x, int y)
5959 if (!MovDelay[x][y]) /* next animation frame */
5960 MovDelay[x][y] = 3 * delay;
5962 if (MovDelay[x][y]) /* wait some time before next frame */
5966 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5968 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5969 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5971 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5974 if (!MovDelay[x][y])
5976 if (MovDir[x][y] == MV_LEFT)
5978 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5979 DrawLevelField(x - 1, y);
5981 else if (MovDir[x][y] == MV_RIGHT)
5983 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5984 DrawLevelField(x + 1, y);
5986 else if (MovDir[x][y] == MV_UP)
5988 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5989 DrawLevelField(x, y - 1);
5993 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5994 DrawLevelField(x, y + 1);
5997 Feld[x][y] = Store[x][y];
5999 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6000 DrawLevelField(x, y);
6005 void MauerAbleger(int ax, int ay)
6007 int element = Feld[ax][ay];
6008 int graphic = el2img(element);
6009 boolean oben_frei = FALSE, unten_frei = FALSE;
6010 boolean links_frei = FALSE, rechts_frei = FALSE;
6011 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6012 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6013 boolean new_wall = FALSE;
6015 if (IS_ANIMATED(graphic))
6016 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6018 if (!MovDelay[ax][ay]) /* start building new wall */
6019 MovDelay[ax][ay] = 6;
6021 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6024 if (MovDelay[ax][ay])
6028 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6030 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6032 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6034 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6037 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6038 element == EL_EXPANDABLE_WALL_ANY)
6042 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6043 Store[ax][ay-1] = element;
6044 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6045 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6046 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6047 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6052 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6053 Store[ax][ay+1] = element;
6054 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6055 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6056 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6057 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6062 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6063 element == EL_EXPANDABLE_WALL_ANY ||
6064 element == EL_EXPANDABLE_WALL)
6068 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6069 Store[ax-1][ay] = element;
6070 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6071 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6072 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6073 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6079 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6080 Store[ax+1][ay] = element;
6081 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6082 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6083 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6084 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6089 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6090 DrawLevelField(ax, ay);
6092 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6094 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6095 unten_massiv = TRUE;
6096 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6097 links_massiv = TRUE;
6098 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6099 rechts_massiv = TRUE;
6101 if (((oben_massiv && unten_massiv) ||
6102 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6103 element == EL_EXPANDABLE_WALL) &&
6104 ((links_massiv && rechts_massiv) ||
6105 element == EL_EXPANDABLE_WALL_VERTICAL))
6106 Feld[ax][ay] = EL_WALL;
6110 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6112 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6116 void CheckForDragon(int x, int y)
6119 boolean dragon_found = FALSE;
6120 static int xy[4][2] =
6128 for (i = 0; i < 4; i++)
6130 for (j = 0; j < 4; j++)
6132 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6134 if (IN_LEV_FIELD(xx, yy) &&
6135 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6137 if (Feld[xx][yy] == EL_DRAGON)
6138 dragon_found = TRUE;
6147 for (i = 0; i < 4; i++)
6149 for (j = 0; j < 3; j++)
6151 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6153 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6155 Feld[xx][yy] = EL_EMPTY;
6156 DrawLevelField(xx, yy);
6165 static void InitBuggyBase(int x, int y)
6167 int element = Feld[x][y];
6168 int activating_delay = FRAMES_PER_SECOND / 4;
6171 (element == EL_SP_BUGGY_BASE ?
6172 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6173 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6175 element == EL_SP_BUGGY_BASE_ACTIVE ?
6176 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6179 static void WarnBuggyBase(int x, int y)
6182 static int xy[4][2] =
6190 for (i = 0; i < 4; i++)
6192 int xx = x + xy[i][0], yy = y + xy[i][1];
6194 if (IS_PLAYER(xx, yy))
6196 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6203 static void InitTrap(int x, int y)
6205 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6208 static void ActivateTrap(int x, int y)
6210 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6213 static void ChangeActiveTrap(int x, int y)
6215 int graphic = IMG_TRAP_ACTIVE;
6217 /* if new animation frame was drawn, correct crumbled sand border */
6218 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6219 DrawLevelFieldCrumbledSand(x, y);
6222 static void ChangeElementNowExt(int x, int y, int target_element)
6224 int previous_move_direction = MovDir[x][y];
6226 /* check if element under player changes from accessible to unaccessible
6227 (needed for special case of dropping element which then changes) */
6228 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6229 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6236 Feld[x][y] = target_element;
6238 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6240 ResetGfxAnimation(x, y);
6241 ResetRandomAnimationValue(x, y);
6243 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6244 MovDir[x][y] = previous_move_direction;
6246 InitField(x, y, FALSE);
6247 if (CAN_MOVE(Feld[x][y]))
6250 DrawLevelField(x, y);
6252 if (GFX_CRUMBLED(Feld[x][y]))
6253 DrawLevelFieldCrumbledSandNeighbours(x, y);
6255 TestIfBadThingTouchesHero(x, y);
6256 TestIfPlayerTouchesCustomElement(x, y);
6257 TestIfElementTouchesCustomElement(x, y);
6259 if (ELEM_IS_PLAYER(target_element))
6260 RelocatePlayer(x, y, target_element);
6263 static boolean ChangeElementNow(int x, int y, int element, int page)
6265 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6267 /* always use default change event to prevent running into a loop */
6268 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6269 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6271 /* do not change already changed elements with same change event */
6273 if (Changed[x][y] & ChangeEvent[x][y])
6280 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6282 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6284 if (change->explode)
6291 if (change->use_content)
6293 boolean complete_change = TRUE;
6294 boolean can_change[3][3];
6297 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6299 boolean half_destructible;
6300 int ex = x + xx - 1;
6301 int ey = y + yy - 1;
6304 can_change[xx][yy] = TRUE;
6306 if (ex == x && ey == y) /* do not check changing element itself */
6309 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6311 can_change[xx][yy] = FALSE; /* do not change empty borders */
6316 if (!IN_LEV_FIELD(ex, ey))
6318 can_change[xx][yy] = FALSE;
6319 complete_change = FALSE;
6326 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6327 e = MovingOrBlocked2Element(ex, ey);
6329 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6331 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6332 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6333 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6335 can_change[xx][yy] = FALSE;
6336 complete_change = FALSE;
6340 if (!change->only_complete || complete_change)
6342 boolean something_has_changed = FALSE;
6344 if (change->only_complete && change->use_random_change &&
6345 RND(100) < change->random)
6348 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6350 int ex = x + xx - 1;
6351 int ey = y + yy - 1;
6353 if (can_change[xx][yy] && (!change->use_random_change ||
6354 RND(100) < change->random))
6356 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6357 RemoveMovingField(ex, ey);
6359 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6361 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6363 something_has_changed = TRUE;
6365 /* for symmetry reasons, freeze newly created border elements */
6366 if (ex != x || ey != y)
6367 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6371 if (something_has_changed)
6372 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6377 ChangeElementNowExt(x, y, change->target_element);
6379 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6385 static void ChangeElement(int x, int y, int page)
6387 int element = MovingOrBlocked2Element(x, y);
6388 struct ElementInfo *ei = &element_info[element];
6389 struct ElementChangeInfo *change = &ei->change_page[page];
6393 if (!CAN_CHANGE(element))
6396 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6397 x, y, element, element_info[element].token_name);
6398 printf("ChangeElement(): This should never happen!\n");
6404 if (ChangeDelay[x][y] == 0) /* initialize element change */
6406 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6407 RND(change->delay_random * change->delay_frames)) + 1;
6409 ResetGfxAnimation(x, y);
6410 ResetRandomAnimationValue(x, y);
6412 if (change->pre_change_function)
6413 change->pre_change_function(x, y);
6416 ChangeDelay[x][y]--;
6418 if (ChangeDelay[x][y] != 0) /* continue element change */
6420 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6422 if (IS_ANIMATED(graphic))
6423 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6425 if (change->change_function)
6426 change->change_function(x, y);
6428 else /* finish element change */
6430 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6432 page = ChangePage[x][y];
6433 ChangePage[x][y] = -1;
6437 if (IS_MOVING(x, y) && !change->explode)
6439 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6442 ChangeDelay[x][y] = 1; /* try change after next move step */
6443 ChangePage[x][y] = page; /* remember page to use for change */
6448 if (ChangeElementNow(x, y, element, page))
6450 if (change->post_change_function)
6451 change->post_change_function(x, y);
6456 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6457 int trigger_element,
6463 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6466 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6468 int element = EL_CUSTOM_START + i;
6470 boolean change_element = FALSE;
6473 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6476 for (j = 0; j < element_info[element].num_change_pages; j++)
6478 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6480 if (change->can_change &&
6482 change->events & CH_EVENT_BIT(trigger_event) &&
6484 change->sides & trigger_side &&
6486 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6488 change->trigger_element == trigger_element
6493 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6494 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6495 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6498 change_element = TRUE;
6505 if (!change_element)
6508 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6511 if (x == lx && y == ly) /* do not change trigger element itself */
6515 if (Feld[x][y] == element)
6517 ChangeDelay[x][y] = 1;
6518 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6519 ChangeElement(x, y, page);
6527 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6530 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6534 static boolean CheckElementSideChange(int x, int y, int element, int side,
6535 int trigger_event, int page)
6537 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6540 if (Feld[x][y] == EL_BLOCKED)
6542 Blocked2Moving(x, y, &x, &y);
6543 element = Feld[x][y];
6547 page = element_info[element].event_page_nr[trigger_event];
6549 if (!(element_info[element].change_page[page].sides & side))
6552 ChangeDelay[x][y] = 1;
6553 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6554 ChangeElement(x, y, page);
6559 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6561 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6564 static void PlayPlayerSound(struct PlayerInfo *player)
6566 int jx = player->jx, jy = player->jy;
6567 int element = player->element_nr;
6568 int last_action = player->last_action_waiting;
6569 int action = player->action_waiting;
6571 if (player->is_waiting)
6573 if (action != last_action)
6574 PlayLevelSoundElementAction(jx, jy, element, action);
6576 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6580 if (action != last_action)
6581 StopSound(element_info[element].sound[last_action]);
6583 if (last_action == ACTION_SLEEPING)
6584 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6588 static void PlayAllPlayersSound()
6592 for (i = 0; i < MAX_PLAYERS; i++)
6593 if (stored_player[i].active)
6594 PlayPlayerSound(&stored_player[i]);
6597 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6599 boolean last_waiting = player->is_waiting;
6600 int move_dir = player->MovDir;
6602 player->last_action_waiting = player->action_waiting;
6606 if (!last_waiting) /* not waiting -> waiting */
6608 player->is_waiting = TRUE;
6610 player->frame_counter_bored =
6612 game.player_boring_delay_fixed +
6613 SimpleRND(game.player_boring_delay_random);
6614 player->frame_counter_sleeping =
6616 game.player_sleeping_delay_fixed +
6617 SimpleRND(game.player_sleeping_delay_random);
6619 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6622 if (game.player_sleeping_delay_fixed +
6623 game.player_sleeping_delay_random > 0 &&
6624 player->anim_delay_counter == 0 &&
6625 player->post_delay_counter == 0 &&
6626 FrameCounter >= player->frame_counter_sleeping)
6627 player->is_sleeping = TRUE;
6628 else if (game.player_boring_delay_fixed +
6629 game.player_boring_delay_random > 0 &&
6630 FrameCounter >= player->frame_counter_bored)
6631 player->is_bored = TRUE;
6633 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6634 player->is_bored ? ACTION_BORING :
6637 if (player->is_sleeping)
6639 if (player->num_special_action_sleeping > 0)
6641 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6643 int last_special_action = player->special_action_sleeping;
6644 int num_special_action = player->num_special_action_sleeping;
6645 int special_action =
6646 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6647 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6648 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6649 last_special_action + 1 : ACTION_SLEEPING);
6650 int special_graphic =
6651 el_act_dir2img(player->element_nr, special_action, move_dir);
6653 player->anim_delay_counter =
6654 graphic_info[special_graphic].anim_delay_fixed +
6655 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6656 player->post_delay_counter =
6657 graphic_info[special_graphic].post_delay_fixed +
6658 SimpleRND(graphic_info[special_graphic].post_delay_random);
6660 player->special_action_sleeping = special_action;
6663 if (player->anim_delay_counter > 0)
6665 player->action_waiting = player->special_action_sleeping;
6666 player->anim_delay_counter--;
6668 else if (player->post_delay_counter > 0)
6670 player->post_delay_counter--;
6674 else if (player->is_bored)
6676 if (player->num_special_action_bored > 0)
6678 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6680 int special_action =
6681 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6682 int special_graphic =
6683 el_act_dir2img(player->element_nr, special_action, move_dir);
6685 player->anim_delay_counter =
6686 graphic_info[special_graphic].anim_delay_fixed +
6687 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6688 player->post_delay_counter =
6689 graphic_info[special_graphic].post_delay_fixed +
6690 SimpleRND(graphic_info[special_graphic].post_delay_random);
6692 player->special_action_bored = special_action;
6695 if (player->anim_delay_counter > 0)
6697 player->action_waiting = player->special_action_bored;
6698 player->anim_delay_counter--;
6700 else if (player->post_delay_counter > 0)
6702 player->post_delay_counter--;
6707 else if (last_waiting) /* waiting -> not waiting */
6709 player->is_waiting = FALSE;
6710 player->is_bored = FALSE;
6711 player->is_sleeping = FALSE;
6713 player->frame_counter_bored = -1;
6714 player->frame_counter_sleeping = -1;
6716 player->anim_delay_counter = 0;
6717 player->post_delay_counter = 0;
6719 player->action_waiting = ACTION_DEFAULT;
6721 player->special_action_bored = ACTION_DEFAULT;
6722 player->special_action_sleeping = ACTION_DEFAULT;
6727 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6730 static byte stored_player_action[MAX_PLAYERS];
6731 static int num_stored_actions = 0;
6733 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6734 int left = player_action & JOY_LEFT;
6735 int right = player_action & JOY_RIGHT;
6736 int up = player_action & JOY_UP;
6737 int down = player_action & JOY_DOWN;
6738 int button1 = player_action & JOY_BUTTON_1;
6739 int button2 = player_action & JOY_BUTTON_2;
6740 int dx = (left ? -1 : right ? 1 : 0);
6741 int dy = (up ? -1 : down ? 1 : 0);
6744 stored_player_action[player->index_nr] = 0;
6745 num_stored_actions++;
6749 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6752 if (!player->active || tape.pausing)
6756 printf("::: [%d %d %d %d] [%d %d]\n",
6757 left, right, up, down, button1, button2);
6763 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6767 snapped = SnapField(player, dx, dy);
6771 dropped = DropElement(player);
6773 moved = MovePlayer(player, dx, dy);
6776 if (tape.single_step && tape.recording && !tape.pausing)
6778 if (button1 || (dropped && !moved))
6780 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6781 SnapField(player, 0, 0); /* stop snapping */
6785 SetPlayerWaiting(player, FALSE);
6788 return player_action;
6790 stored_player_action[player->index_nr] = player_action;
6796 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6799 /* no actions for this player (no input at player's configured device) */
6801 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6802 SnapField(player, 0, 0);
6803 CheckGravityMovement(player);
6805 if (player->MovPos == 0)
6806 SetPlayerWaiting(player, TRUE);
6808 if (player->MovPos == 0) /* needed for tape.playing */
6809 player->is_moving = FALSE;
6811 player->is_dropping = FALSE;
6817 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6819 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6821 TapeRecordAction(stored_player_action);
6822 num_stored_actions = 0;
6829 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6831 static byte stored_player_action[MAX_PLAYERS];
6832 static int num_stored_actions = 0;
6833 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6834 int left = player_action & JOY_LEFT;
6835 int right = player_action & JOY_RIGHT;
6836 int up = player_action & JOY_UP;
6837 int down = player_action & JOY_DOWN;
6838 int button1 = player_action & JOY_BUTTON_1;
6839 int button2 = player_action & JOY_BUTTON_2;
6840 int dx = (left ? -1 : right ? 1 : 0);
6841 int dy = (up ? -1 : down ? 1 : 0);
6843 stored_player_action[player->index_nr] = 0;
6844 num_stored_actions++;
6846 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6848 if (!player->active || tape.pausing)
6853 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6856 snapped = SnapField(player, dx, dy);
6860 dropped = DropElement(player);
6862 moved = MovePlayer(player, dx, dy);
6865 if (tape.single_step && tape.recording && !tape.pausing)
6867 if (button1 || (dropped && !moved))
6869 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6870 SnapField(player, 0, 0); /* stop snapping */
6874 stored_player_action[player->index_nr] = player_action;
6878 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6880 /* no actions for this player (no input at player's configured device) */
6882 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6883 SnapField(player, 0, 0);
6884 CheckGravityMovement(player);
6886 if (player->MovPos == 0)
6887 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6889 if (player->MovPos == 0) /* needed for tape.playing */
6890 player->is_moving = FALSE;
6893 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6895 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6897 TapeRecordAction(stored_player_action);
6898 num_stored_actions = 0;
6905 static unsigned long action_delay = 0;
6906 unsigned long action_delay_value;
6907 int magic_wall_x = 0, magic_wall_y = 0;
6908 int i, x, y, element, graphic;
6909 byte *recorded_player_action;
6910 byte summarized_player_action = 0;
6912 byte tape_action[MAX_PLAYERS];
6915 if (game_status != GAME_MODE_PLAYING)
6918 action_delay_value =
6919 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6921 if (tape.playing && tape.index_search && !tape.pausing)
6922 action_delay_value = 0;
6924 /* ---------- main game synchronization point ---------- */
6926 WaitUntilDelayReached(&action_delay, action_delay_value);
6928 if (network_playing && !network_player_action_received)
6932 printf("DEBUG: try to get network player actions in time\n");
6936 #if defined(PLATFORM_UNIX)
6937 /* last chance to get network player actions without main loop delay */
6941 if (game_status != GAME_MODE_PLAYING)
6944 if (!network_player_action_received)
6948 printf("DEBUG: failed to get network player actions in time\n");
6959 printf("::: getting new tape action [%d]\n", FrameCounter);
6962 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6964 for (i = 0; i < MAX_PLAYERS; i++)
6966 summarized_player_action |= stored_player[i].action;
6968 if (!network_playing)
6969 stored_player[i].effective_action = stored_player[i].action;
6972 #if defined(PLATFORM_UNIX)
6973 if (network_playing)
6974 SendToServer_MovePlayer(summarized_player_action);
6977 if (!options.network && !setup.team_mode)
6978 local_player->effective_action = summarized_player_action;
6980 for (i = 0; i < MAX_PLAYERS; i++)
6982 int actual_player_action = stored_player[i].effective_action;
6984 if (stored_player[i].programmed_action)
6985 actual_player_action = stored_player[i].programmed_action;
6987 if (recorded_player_action)
6988 actual_player_action = recorded_player_action[i];
6990 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6992 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6993 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6995 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7000 TapeRecordAction(tape_action);
7003 network_player_action_received = FALSE;
7005 ScrollScreen(NULL, SCROLL_GO_ON);
7011 for (i = 0; i < MAX_PLAYERS; i++)
7012 stored_player[i].Frame++;
7016 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7018 for (i = 0; i < MAX_PLAYERS; i++)
7020 struct PlayerInfo *player = &stored_player[i];
7024 if (player->active && player->is_pushing && player->is_moving &&
7027 ContinueMoving(x, y);
7029 /* continue moving after pushing (this is actually a bug) */
7030 if (!IS_MOVING(x, y))
7039 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7041 Changed[x][y] = CE_BITMASK_DEFAULT;
7042 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7045 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7047 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7048 printf("GameActions(): This should never happen!\n");
7050 ChangePage[x][y] = -1;
7055 if (WasJustMoving[x][y] > 0)
7056 WasJustMoving[x][y]--;
7057 if (WasJustFalling[x][y] > 0)
7058 WasJustFalling[x][y]--;
7063 /* reset finished pushing action (not done in ContinueMoving() to allow
7064 continous pushing animation for elements with zero push delay) */
7065 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7067 ResetGfxAnimation(x, y);
7068 DrawLevelField(x, y);
7073 if (IS_BLOCKED(x, y))
7077 Blocked2Moving(x, y, &oldx, &oldy);
7078 if (!IS_MOVING(oldx, oldy))
7080 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7081 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7082 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7083 printf("GameActions(): This should never happen!\n");
7089 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7091 element = Feld[x][y];
7093 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7095 graphic = el2img(element);
7101 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7103 element = graphic = 0;
7107 if (graphic_info[graphic].anim_global_sync)
7108 GfxFrame[x][y] = FrameCounter;
7110 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7111 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7112 ResetRandomAnimationValue(x, y);
7114 SetRandomAnimationValue(x, y);
7117 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7120 if (IS_INACTIVE(element))
7122 if (IS_ANIMATED(graphic))
7123 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7129 /* this may take place after moving, so 'element' may have changed */
7131 if (IS_CHANGING(x, y))
7133 if (IS_CHANGING(x, y) &&
7134 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7138 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7139 element_info[element].event_page_nr[CE_DELAY]);
7141 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7144 element = Feld[x][y];
7145 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7149 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7154 element = Feld[x][y];
7155 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7157 if (element == EL_MOLE)
7158 printf("::: %d, %d, %d [%d]\n",
7159 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7163 if (element == EL_YAMYAM)
7164 printf("::: %d, %d, %d\n",
7165 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7169 if (IS_ANIMATED(graphic) &&
7173 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7176 if (element == EL_BUG)
7177 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7181 if (element == EL_MOLE)
7182 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7186 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7187 EdelsteinFunkeln(x, y);
7189 else if ((element == EL_ACID ||
7190 element == EL_EXIT_OPEN ||
7191 element == EL_SP_EXIT_OPEN ||
7192 element == EL_SP_TERMINAL ||
7193 element == EL_SP_TERMINAL_ACTIVE ||
7194 element == EL_EXTRA_TIME ||
7195 element == EL_SHIELD_NORMAL ||
7196 element == EL_SHIELD_DEADLY) &&
7197 IS_ANIMATED(graphic))
7198 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7199 else if (IS_MOVING(x, y))
7200 ContinueMoving(x, y);
7201 else if (IS_ACTIVE_BOMB(element))
7202 CheckDynamite(x, y);
7204 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7205 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7207 else if (element == EL_AMOEBA_GROWING)
7208 AmoebeWaechst(x, y);
7209 else if (element == EL_AMOEBA_SHRINKING)
7210 AmoebaDisappearing(x, y);
7212 #if !USE_NEW_AMOEBA_CODE
7213 else if (IS_AMOEBALIVE(element))
7214 AmoebeAbleger(x, y);
7217 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7219 else if (element == EL_EXIT_CLOSED)
7221 else if (element == EL_SP_EXIT_CLOSED)
7223 else if (element == EL_EXPANDABLE_WALL_GROWING)
7225 else if (element == EL_EXPANDABLE_WALL ||
7226 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7227 element == EL_EXPANDABLE_WALL_VERTICAL ||
7228 element == EL_EXPANDABLE_WALL_ANY)
7230 else if (element == EL_FLAMES)
7231 CheckForDragon(x, y);
7233 else if (IS_AUTO_CHANGING(element))
7234 ChangeElement(x, y);
7236 else if (element == EL_EXPLOSION)
7237 ; /* drawing of correct explosion animation is handled separately */
7238 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7239 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7242 /* this may take place after moving, so 'element' may have changed */
7243 if (IS_AUTO_CHANGING(Feld[x][y]))
7244 ChangeElement(x, y);
7247 if (IS_BELT_ACTIVE(element))
7248 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7250 if (game.magic_wall_active)
7252 int jx = local_player->jx, jy = local_player->jy;
7254 /* play the element sound at the position nearest to the player */
7255 if ((element == EL_MAGIC_WALL_FULL ||
7256 element == EL_MAGIC_WALL_ACTIVE ||
7257 element == EL_MAGIC_WALL_EMPTYING ||
7258 element == EL_BD_MAGIC_WALL_FULL ||
7259 element == EL_BD_MAGIC_WALL_ACTIVE ||
7260 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7261 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7269 #if USE_NEW_AMOEBA_CODE
7270 /* new experimental amoeba growth stuff */
7272 if (!(FrameCounter % 8))
7275 static unsigned long random = 1684108901;
7277 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7280 x = (random >> 10) % lev_fieldx;
7281 y = (random >> 20) % lev_fieldy;
7283 x = RND(lev_fieldx);
7284 y = RND(lev_fieldy);
7286 element = Feld[x][y];
7288 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7289 if (!IS_PLAYER(x,y) &&
7290 (element == EL_EMPTY ||
7291 element == EL_SAND ||
7292 element == EL_QUICKSAND_EMPTY ||
7293 element == EL_ACID_SPLASH_LEFT ||
7294 element == EL_ACID_SPLASH_RIGHT))
7296 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7297 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7298 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7299 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7300 Feld[x][y] = EL_AMOEBA_DROP;
7303 random = random * 129 + 1;
7309 if (game.explosions_delayed)
7312 game.explosions_delayed = FALSE;
7314 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7316 element = Feld[x][y];
7318 if (ExplodeField[x][y])
7319 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7320 else if (element == EL_EXPLOSION)
7321 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7323 ExplodeField[x][y] = EX_NO_EXPLOSION;
7326 game.explosions_delayed = TRUE;
7329 if (game.magic_wall_active)
7331 if (!(game.magic_wall_time_left % 4))
7333 int element = Feld[magic_wall_x][magic_wall_y];
7335 if (element == EL_BD_MAGIC_WALL_FULL ||
7336 element == EL_BD_MAGIC_WALL_ACTIVE ||
7337 element == EL_BD_MAGIC_WALL_EMPTYING)
7338 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7340 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7343 if (game.magic_wall_time_left > 0)
7345 game.magic_wall_time_left--;
7346 if (!game.magic_wall_time_left)
7348 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7350 element = Feld[x][y];
7352 if (element == EL_MAGIC_WALL_ACTIVE ||
7353 element == EL_MAGIC_WALL_FULL)
7355 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7356 DrawLevelField(x, y);
7358 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7359 element == EL_BD_MAGIC_WALL_FULL)
7361 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7362 DrawLevelField(x, y);
7366 game.magic_wall_active = FALSE;
7371 if (game.light_time_left > 0)
7373 game.light_time_left--;
7375 if (game.light_time_left == 0)
7376 RedrawAllLightSwitchesAndInvisibleElements();
7379 if (game.timegate_time_left > 0)
7381 game.timegate_time_left--;
7383 if (game.timegate_time_left == 0)
7384 CloseAllOpenTimegates();
7387 for (i = 0; i < MAX_PLAYERS; i++)
7389 struct PlayerInfo *player = &stored_player[i];
7391 if (SHIELD_ON(player))
7393 if (player->shield_deadly_time_left)
7394 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7395 else if (player->shield_normal_time_left)
7396 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7400 if (TimeFrames >= FRAMES_PER_SECOND)
7405 for (i = 0; i < MAX_PLAYERS; i++)
7407 struct PlayerInfo *player = &stored_player[i];
7409 if (SHIELD_ON(player))
7411 player->shield_normal_time_left--;
7413 if (player->shield_deadly_time_left > 0)
7414 player->shield_deadly_time_left--;
7418 if (tape.recording || tape.playing)
7419 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7425 if (TimeLeft <= 10 && setup.time_limit)
7426 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7428 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7430 if (!TimeLeft && setup.time_limit)
7431 for (i = 0; i < MAX_PLAYERS; i++)
7432 KillHero(&stored_player[i]);
7434 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7435 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7439 PlayAllPlayersSound();
7441 if (options.debug) /* calculate frames per second */
7443 static unsigned long fps_counter = 0;
7444 static int fps_frames = 0;
7445 unsigned long fps_delay_ms = Counter() - fps_counter;
7449 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7451 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7454 fps_counter = Counter();
7457 redraw_mask |= REDRAW_FPS;
7461 if (stored_player[0].jx != stored_player[0].last_jx ||
7462 stored_player[0].jy != stored_player[0].last_jy)
7463 printf("::: %d, %d, %d, %d, %d\n",
7464 stored_player[0].MovDir,
7465 stored_player[0].MovPos,
7466 stored_player[0].GfxPos,
7467 stored_player[0].Frame,
7468 stored_player[0].StepFrame);
7475 for (i = 0; i < MAX_PLAYERS; i++)
7478 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7480 stored_player[i].Frame += move_frames;
7482 if (stored_player[i].MovPos != 0)
7483 stored_player[i].StepFrame += move_frames;
7485 if (stored_player[i].drop_delay > 0)
7486 stored_player[i].drop_delay--;
7491 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7493 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7495 local_player->show_envelope = 0;
7500 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7502 int min_x = x, min_y = y, max_x = x, max_y = y;
7505 for (i = 0; i < MAX_PLAYERS; i++)
7507 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7509 if (!stored_player[i].active || &stored_player[i] == player)
7512 min_x = MIN(min_x, jx);
7513 min_y = MIN(min_y, jy);
7514 max_x = MAX(max_x, jx);
7515 max_y = MAX(max_y, jy);
7518 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7521 static boolean AllPlayersInVisibleScreen()
7525 for (i = 0; i < MAX_PLAYERS; i++)
7527 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7529 if (!stored_player[i].active)
7532 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7539 void ScrollLevel(int dx, int dy)
7541 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7544 BlitBitmap(drawto_field, drawto_field,
7545 FX + TILEX * (dx == -1) - softscroll_offset,
7546 FY + TILEY * (dy == -1) - softscroll_offset,
7547 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7548 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7549 FX + TILEX * (dx == 1) - softscroll_offset,
7550 FY + TILEY * (dy == 1) - softscroll_offset);
7554 x = (dx == 1 ? BX1 : BX2);
7555 for (y = BY1; y <= BY2; y++)
7556 DrawScreenField(x, y);
7561 y = (dy == 1 ? BY1 : BY2);
7562 for (x = BX1; x <= BX2; x++)
7563 DrawScreenField(x, y);
7566 redraw_mask |= REDRAW_FIELD;
7569 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7571 int nextx = x + dx, nexty = y + dy;
7572 int element = Feld[x][y];
7575 element != EL_SP_PORT_LEFT &&
7576 element != EL_SP_GRAVITY_PORT_LEFT &&
7577 element != EL_SP_PORT_HORIZONTAL &&
7578 element != EL_SP_PORT_ANY) ||
7580 element != EL_SP_PORT_RIGHT &&
7581 element != EL_SP_GRAVITY_PORT_RIGHT &&
7582 element != EL_SP_PORT_HORIZONTAL &&
7583 element != EL_SP_PORT_ANY) ||
7585 element != EL_SP_PORT_UP &&
7586 element != EL_SP_GRAVITY_PORT_UP &&
7587 element != EL_SP_PORT_VERTICAL &&
7588 element != EL_SP_PORT_ANY) ||
7590 element != EL_SP_PORT_DOWN &&
7591 element != EL_SP_GRAVITY_PORT_DOWN &&
7592 element != EL_SP_PORT_VERTICAL &&
7593 element != EL_SP_PORT_ANY) ||
7594 !IN_LEV_FIELD(nextx, nexty) ||
7595 !IS_FREE(nextx, nexty))
7601 static void CheckGravityMovement(struct PlayerInfo *player)
7603 if (game.gravity && !player->programmed_action)
7605 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7606 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7608 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7609 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7610 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7611 int jx = player->jx, jy = player->jy;
7612 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7613 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7614 int new_jx = jx + dx, new_jy = jy + dy;
7615 boolean field_under_player_is_free =
7616 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7617 boolean player_is_moving_to_valid_field =
7618 (IN_LEV_FIELD(new_jx, new_jy) &&
7619 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7620 Feld[new_jx][new_jy] == EL_SAND ||
7621 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7622 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7623 /* !!! extend EL_SAND to anything diggable !!! */
7625 if (field_under_player_is_free &&
7626 !player_is_moving_to_valid_field &&
7627 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7628 player->programmed_action = MV_DOWN;
7634 -----------------------------------------------------------------------------
7635 dx, dy: direction (non-diagonal) to try to move the player to
7636 real_dx, real_dy: direction as read from input device (can be diagonal)
7639 boolean MovePlayerOneStep(struct PlayerInfo *player,
7640 int dx, int dy, int real_dx, int real_dy)
7643 static int change_sides[4][2] =
7645 /* enter side leave side */
7646 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7647 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7648 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7649 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7651 int move_direction = (dx == -1 ? MV_LEFT :
7652 dx == +1 ? MV_RIGHT :
7654 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7655 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7656 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7658 int jx = player->jx, jy = player->jy;
7659 int new_jx = jx + dx, new_jy = jy + dy;
7663 if (!player->active || (!dx && !dy))
7664 return MF_NO_ACTION;
7666 player->MovDir = (dx < 0 ? MV_LEFT :
7669 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7671 if (!IN_LEV_FIELD(new_jx, new_jy))
7672 return MF_NO_ACTION;
7674 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7675 return MF_NO_ACTION;
7678 element = MovingOrBlocked2Element(new_jx, new_jy);
7680 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7683 if (DONT_RUN_INTO(element))
7685 if (element == EL_ACID && dx == 0 && dy == 1)
7688 Feld[jx][jy] = EL_PLAYER_1;
7689 InitMovingField(jx, jy, MV_DOWN);
7690 Store[jx][jy] = EL_ACID;
7691 ContinueMoving(jx, jy);
7695 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7700 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7701 if (can_move != MF_MOVING)
7704 /* check if DigField() has caused relocation of the player */
7705 if (player->jx != jx || player->jy != jy)
7706 return MF_NO_ACTION;
7708 StorePlayer[jx][jy] = 0;
7709 player->last_jx = jx;
7710 player->last_jy = jy;
7711 player->jx = new_jx;
7712 player->jy = new_jy;
7713 StorePlayer[new_jx][new_jy] = player->element_nr;
7716 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7718 player->step_counter++;
7720 player->drop_delay = 0;
7722 PlayerVisit[jx][jy] = FrameCounter;
7724 ScrollPlayer(player, SCROLL_INIT);
7727 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7729 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7730 CE_OTHER_GETS_LEFT);
7731 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7732 CE_LEFT_BY_PLAYER, -1);
7735 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7737 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7738 enter_side, CE_OTHER_GETS_ENTERED);
7739 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7740 CE_ENTERED_BY_PLAYER, -1);
7747 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7749 int jx = player->jx, jy = player->jy;
7750 int old_jx = jx, old_jy = jy;
7751 int moved = MF_NO_ACTION;
7754 if (!player->active)
7759 if (player->MovPos == 0)
7761 player->is_moving = FALSE;
7762 player->is_digging = FALSE;
7763 player->is_collecting = FALSE;
7764 player->is_snapping = FALSE;
7765 player->is_pushing = FALSE;
7771 if (!player->active || (!dx && !dy))
7776 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7780 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7781 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7785 /* remove the last programmed player action */
7786 player->programmed_action = 0;
7790 /* should only happen if pre-1.2 tape recordings are played */
7791 /* this is only for backward compatibility */
7793 int original_move_delay_value = player->move_delay_value;
7796 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7800 /* scroll remaining steps with finest movement resolution */
7801 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7803 while (player->MovPos)
7805 ScrollPlayer(player, SCROLL_GO_ON);
7806 ScrollScreen(NULL, SCROLL_GO_ON);
7812 player->move_delay_value = original_move_delay_value;
7815 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7817 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7818 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7822 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7823 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7829 if (moved & MF_MOVING && !ScreenMovPos &&
7830 (player == local_player || !options.network))
7832 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7833 int offset = (setup.scroll_delay ? 3 : 0);
7835 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7837 /* actual player has left the screen -- scroll in that direction */
7838 if (jx != old_jx) /* player has moved horizontally */
7839 scroll_x += (jx - old_jx);
7840 else /* player has moved vertically */
7841 scroll_y += (jy - old_jy);
7845 if (jx != old_jx) /* player has moved horizontally */
7847 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7848 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7849 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7851 /* don't scroll over playfield boundaries */
7852 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7853 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7855 /* don't scroll more than one field at a time */
7856 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7858 /* don't scroll against the player's moving direction */
7859 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7860 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7861 scroll_x = old_scroll_x;
7863 else /* player has moved vertically */
7865 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7866 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7867 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7869 /* don't scroll over playfield boundaries */
7870 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7871 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7873 /* don't scroll more than one field at a time */
7874 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7876 /* don't scroll against the player's moving direction */
7877 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7878 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7879 scroll_y = old_scroll_y;
7883 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7885 if (!options.network && !AllPlayersInVisibleScreen())
7887 scroll_x = old_scroll_x;
7888 scroll_y = old_scroll_y;
7892 ScrollScreen(player, SCROLL_INIT);
7893 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7900 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7902 if (!(moved & MF_MOVING) && !player->is_pushing)
7907 player->StepFrame = 0;
7909 if (moved & MF_MOVING)
7911 if (old_jx != jx && old_jy == jy)
7912 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7913 else if (old_jx == jx && old_jy != jy)
7914 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7916 DrawLevelField(jx, jy); /* for "crumbled sand" */
7918 player->last_move_dir = player->MovDir;
7919 player->is_moving = TRUE;
7921 player->is_snapping = FALSE;
7925 player->is_switching = FALSE;
7928 player->is_dropping = FALSE;
7933 static int change_sides[4][2] =
7935 /* enter side leave side */
7936 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7937 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7938 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7939 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7941 int move_direction = player->MovDir;
7942 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7943 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7946 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7948 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7949 leave_side, CE_OTHER_GETS_LEFT);
7950 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7951 leave_side, CE_LEFT_BY_PLAYER, -1);
7954 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7956 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7957 enter_side, CE_OTHER_GETS_ENTERED);
7958 CheckElementSideChange(jx, jy, Feld[jx][jy],
7959 enter_side, CE_ENTERED_BY_PLAYER, -1);
7970 CheckGravityMovement(player);
7973 player->last_move_dir = MV_NO_MOVING;
7975 player->is_moving = FALSE;
7978 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7980 TestIfHeroTouchesBadThing(jx, jy);
7981 TestIfPlayerTouchesCustomElement(jx, jy);
7984 if (!player->active)
7990 void ScrollPlayer(struct PlayerInfo *player, int mode)
7992 int jx = player->jx, jy = player->jy;
7993 int last_jx = player->last_jx, last_jy = player->last_jy;
7994 int move_stepsize = TILEX / player->move_delay_value;
7996 if (!player->active || !player->MovPos)
7999 if (mode == SCROLL_INIT)
8001 player->actual_frame_counter = FrameCounter;
8002 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8004 if (Feld[last_jx][last_jy] == EL_EMPTY)
8005 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8013 else if (!FrameReached(&player->actual_frame_counter, 1))
8016 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8017 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8019 if (!player->block_last_field &&
8020 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8021 Feld[last_jx][last_jy] = EL_EMPTY;
8023 /* before DrawPlayer() to draw correct player graphic for this case */
8024 if (player->MovPos == 0)
8025 CheckGravityMovement(player);
8028 DrawPlayer(player); /* needed here only to cleanup last field */
8031 if (player->MovPos == 0) /* player reached destination field */
8034 if (player->move_delay_reset_counter > 0)
8036 player->move_delay_reset_counter--;
8038 if (player->move_delay_reset_counter == 0)
8040 /* continue with normal speed after quickly moving through gate */
8041 HALVE_PLAYER_SPEED(player);
8043 /* be able to make the next move without delay */
8044 player->move_delay = 0;
8048 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8050 /* continue with normal speed after quickly moving through gate */
8051 HALVE_PLAYER_SPEED(player);
8053 /* be able to make the next move without delay */
8054 player->move_delay = 0;
8058 if (player->block_last_field &&
8059 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8060 Feld[last_jx][last_jy] = EL_EMPTY;
8062 player->last_jx = jx;
8063 player->last_jy = jy;
8065 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8066 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8067 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8069 DrawPlayer(player); /* needed here only to cleanup last field */
8072 if (local_player->friends_still_needed == 0 ||
8073 IS_SP_ELEMENT(Feld[jx][jy]))
8074 player->LevelSolved = player->GameOver = TRUE;
8077 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8079 TestIfHeroTouchesBadThing(jx, jy);
8080 TestIfPlayerTouchesCustomElement(jx, jy);
8082 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8085 if (!player->active)
8089 if (tape.single_step && tape.recording && !tape.pausing &&
8090 !player->programmed_action)
8091 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8095 void ScrollScreen(struct PlayerInfo *player, int mode)
8097 static unsigned long screen_frame_counter = 0;
8099 if (mode == SCROLL_INIT)
8101 /* set scrolling step size according to actual player's moving speed */
8102 ScrollStepSize = TILEX / player->move_delay_value;
8104 screen_frame_counter = FrameCounter;
8105 ScreenMovDir = player->MovDir;
8106 ScreenMovPos = player->MovPos;
8107 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8110 else if (!FrameReached(&screen_frame_counter, 1))
8115 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8116 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8117 redraw_mask |= REDRAW_FIELD;
8120 ScreenMovDir = MV_NO_MOVING;
8123 void TestIfPlayerTouchesCustomElement(int x, int y)
8125 static int xy[4][2] =
8132 static int change_sides[4][2] =
8134 /* center side border side */
8135 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8136 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8137 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8138 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8140 static int touch_dir[4] =
8147 int center_element = Feld[x][y]; /* should always be non-moving! */
8150 for (i = 0; i < 4; i++)
8152 int xx = x + xy[i][0];
8153 int yy = y + xy[i][1];
8154 int center_side = change_sides[i][0];
8155 int border_side = change_sides[i][1];
8158 if (!IN_LEV_FIELD(xx, yy))
8161 if (IS_PLAYER(x, y))
8163 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8164 border_element = Feld[xx][yy]; /* may be moving! */
8165 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8166 border_element = Feld[xx][yy];
8167 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8168 border_element = MovingOrBlocked2Element(xx, yy);
8170 continue; /* center and border element do not touch */
8172 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8173 CE_OTHER_GETS_TOUCHED);
8174 CheckElementSideChange(xx, yy, border_element, border_side,
8175 CE_TOUCHED_BY_PLAYER, -1);
8177 else if (IS_PLAYER(xx, yy))
8179 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8181 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8183 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8184 continue; /* center and border element do not touch */
8187 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8188 CE_OTHER_GETS_TOUCHED);
8189 CheckElementSideChange(x, y, center_element, center_side,
8190 CE_TOUCHED_BY_PLAYER, -1);
8197 void TestIfElementTouchesCustomElement(int x, int y)
8199 static int xy[4][2] =
8206 static int change_sides[4][2] =
8208 /* center side border side */
8209 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8210 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8211 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8212 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8214 static int touch_dir[4] =
8221 boolean change_center_element = FALSE;
8222 int center_element_change_page = 0;
8223 int center_element = Feld[x][y]; /* should always be non-moving! */
8226 for (i = 0; i < 4; i++)
8228 int xx = x + xy[i][0];
8229 int yy = y + xy[i][1];
8230 int center_side = change_sides[i][0];
8231 int border_side = change_sides[i][1];
8234 if (!IN_LEV_FIELD(xx, yy))
8237 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8238 border_element = Feld[xx][yy]; /* may be moving! */
8239 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8240 border_element = Feld[xx][yy];
8241 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8242 border_element = MovingOrBlocked2Element(xx, yy);
8244 continue; /* center and border element do not touch */
8246 /* check for change of center element (but change it only once) */
8247 if (IS_CUSTOM_ELEMENT(center_element) &&
8248 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8249 !change_center_element)
8251 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8253 struct ElementChangeInfo *change =
8254 &element_info[center_element].change_page[j];
8256 if (change->can_change &&
8257 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8258 change->sides & border_side &&
8260 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8262 change->trigger_element == border_element
8266 change_center_element = TRUE;
8267 center_element_change_page = j;
8274 /* check for change of border element */
8275 if (IS_CUSTOM_ELEMENT(border_element) &&
8276 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8278 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8280 struct ElementChangeInfo *change =
8281 &element_info[border_element].change_page[j];
8283 if (change->can_change &&
8284 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8285 change->sides & center_side &&
8287 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8289 change->trigger_element == center_element
8293 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8294 CE_OTHER_IS_TOUCHING, j);
8301 if (change_center_element)
8302 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8303 CE_OTHER_IS_TOUCHING, center_element_change_page);
8306 void TestIfElementHitsCustomElement(int x, int y, int direction)
8308 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8309 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8310 int hitx = x + dx, hity = y + dy;
8311 int hitting_element = Feld[x][y];
8313 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8314 !IS_FREE(hitx, hity) &&
8315 (!IS_MOVING(hitx, hity) ||
8316 MovDir[hitx][hity] != direction ||
8317 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8320 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8324 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8328 CheckElementSideChange(x, y, hitting_element,
8329 direction, CE_HITTING_SOMETHING, -1);
8331 if (IN_LEV_FIELD(hitx, hity))
8333 int opposite_direction = MV_DIR_OPPOSITE(direction);
8334 int hitting_side = direction;
8335 int touched_side = opposite_direction;
8336 int touched_element = MovingOrBlocked2Element(hitx, hity);
8338 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8339 MovDir[hitx][hity] != direction ||
8340 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8349 CheckElementSideChange(hitx, hity, touched_element,
8350 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8352 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8353 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8355 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8357 struct ElementChangeInfo *change =
8358 &element_info[hitting_element].change_page[i];
8360 if (change->can_change &&
8361 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8362 change->sides & touched_side &&
8365 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8367 change->trigger_element == touched_element
8371 CheckElementSideChange(x, y, hitting_element,
8372 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8378 if (IS_CUSTOM_ELEMENT(touched_element) &&
8379 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8381 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8383 struct ElementChangeInfo *change =
8384 &element_info[touched_element].change_page[i];
8386 if (change->can_change &&
8387 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8388 change->sides & hitting_side &&
8390 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8392 change->trigger_element == hitting_element
8396 CheckElementSideChange(hitx, hity, touched_element,
8397 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8406 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8408 int i, kill_x = -1, kill_y = -1;
8409 static int test_xy[4][2] =
8416 static int test_dir[4] =
8424 for (i = 0; i < 4; i++)
8426 int test_x, test_y, test_move_dir, test_element;
8428 test_x = good_x + test_xy[i][0];
8429 test_y = good_y + test_xy[i][1];
8430 if (!IN_LEV_FIELD(test_x, test_y))
8434 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8437 test_element = Feld[test_x][test_y];
8439 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8442 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8443 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8445 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8446 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8454 if (kill_x != -1 || kill_y != -1)
8456 if (IS_PLAYER(good_x, good_y))
8458 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8460 if (player->shield_deadly_time_left > 0)
8461 Bang(kill_x, kill_y);
8462 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8466 Bang(good_x, good_y);
8470 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8472 int i, kill_x = -1, kill_y = -1;
8473 int bad_element = Feld[bad_x][bad_y];
8474 static int test_xy[4][2] =
8481 static int touch_dir[4] =
8488 static int test_dir[4] =
8496 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8499 for (i = 0; i < 4; i++)
8501 int test_x, test_y, test_move_dir, test_element;
8503 test_x = bad_x + test_xy[i][0];
8504 test_y = bad_y + test_xy[i][1];
8505 if (!IN_LEV_FIELD(test_x, test_y))
8509 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8511 test_element = Feld[test_x][test_y];
8513 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8514 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8516 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8517 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8519 /* good thing is player or penguin that does not move away */
8520 if (IS_PLAYER(test_x, test_y))
8522 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8524 if (bad_element == EL_ROBOT && player->is_moving)
8525 continue; /* robot does not kill player if he is moving */
8527 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8529 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8530 continue; /* center and border element do not touch */
8537 else if (test_element == EL_PENGUIN)
8546 if (kill_x != -1 || kill_y != -1)
8548 if (IS_PLAYER(kill_x, kill_y))
8550 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8552 if (player->shield_deadly_time_left > 0)
8554 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8558 Bang(kill_x, kill_y);
8562 void TestIfHeroTouchesBadThing(int x, int y)
8564 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8567 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8569 TestIfGoodThingHitsBadThing(x, y, move_dir);
8572 void TestIfBadThingTouchesHero(int x, int y)
8574 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8577 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8579 TestIfBadThingHitsGoodThing(x, y, move_dir);
8582 void TestIfFriendTouchesBadThing(int x, int y)
8584 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8587 void TestIfBadThingTouchesFriend(int x, int y)
8589 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8592 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8594 int i, kill_x = bad_x, kill_y = bad_y;
8595 static int xy[4][2] =
8603 for (i = 0; i < 4; i++)
8607 x = bad_x + xy[i][0];
8608 y = bad_y + xy[i][1];
8609 if (!IN_LEV_FIELD(x, y))
8612 element = Feld[x][y];
8613 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8614 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8622 if (kill_x != bad_x || kill_y != bad_y)
8626 void KillHero(struct PlayerInfo *player)
8628 int jx = player->jx, jy = player->jy;
8630 if (!player->active)
8633 /* remove accessible field at the player's position */
8634 Feld[jx][jy] = EL_EMPTY;
8636 /* deactivate shield (else Bang()/Explode() would not work right) */
8637 player->shield_normal_time_left = 0;
8638 player->shield_deadly_time_left = 0;
8644 static void KillHeroUnlessEnemyProtected(int x, int y)
8646 if (!PLAYER_ENEMY_PROTECTED(x, y))
8647 KillHero(PLAYERINFO(x, y));
8650 static void KillHeroUnlessExplosionProtected(int x, int y)
8652 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8653 KillHero(PLAYERINFO(x, y));
8656 void BuryHero(struct PlayerInfo *player)
8658 int jx = player->jx, jy = player->jy;
8660 if (!player->active)
8664 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8666 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8668 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8670 player->GameOver = TRUE;
8674 void RemoveHero(struct PlayerInfo *player)
8676 int jx = player->jx, jy = player->jy;
8677 int i, found = FALSE;
8679 player->present = FALSE;
8680 player->active = FALSE;
8682 if (!ExplodeField[jx][jy])
8683 StorePlayer[jx][jy] = 0;
8685 for (i = 0; i < MAX_PLAYERS; i++)
8686 if (stored_player[i].active)
8690 AllPlayersGone = TRUE;
8697 =============================================================================
8698 checkDiagonalPushing()
8699 -----------------------------------------------------------------------------
8700 check if diagonal input device direction results in pushing of object
8701 (by checking if the alternative direction is walkable, diggable, ...)
8702 =============================================================================
8705 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8706 int x, int y, int real_dx, int real_dy)
8708 int jx, jy, dx, dy, xx, yy;
8710 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8713 /* diagonal direction: check alternative direction */
8718 xx = jx + (dx == 0 ? real_dx : 0);
8719 yy = jy + (dy == 0 ? real_dy : 0);
8721 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8725 =============================================================================
8727 -----------------------------------------------------------------------------
8728 x, y: field next to player (non-diagonal) to try to dig to
8729 real_dx, real_dy: direction as read from input device (can be diagonal)
8730 =============================================================================
8733 int DigField(struct PlayerInfo *player,
8734 int x, int y, int real_dx, int real_dy, int mode)
8736 static int change_sides[4] =
8738 CH_SIDE_RIGHT, /* moving left */
8739 CH_SIDE_LEFT, /* moving right */
8740 CH_SIDE_BOTTOM, /* moving up */
8741 CH_SIDE_TOP, /* moving down */
8743 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8744 int jx = player->jx, jy = player->jy;
8745 int dx = x - jx, dy = y - jy;
8746 int nextx = x + dx, nexty = y + dy;
8747 int move_direction = (dx == -1 ? MV_LEFT :
8748 dx == +1 ? MV_RIGHT :
8750 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8751 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8754 if (player->MovPos == 0)
8756 player->is_digging = FALSE;
8757 player->is_collecting = FALSE;
8760 if (player->MovPos == 0) /* last pushing move finished */
8761 player->is_pushing = FALSE;
8763 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8765 player->is_switching = FALSE;
8766 player->push_delay = 0;
8768 return MF_NO_ACTION;
8771 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8772 return MF_NO_ACTION;
8775 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8777 if (IS_TUBE(Feld[jx][jy]) ||
8778 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8782 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8783 int tube_leave_directions[][2] =
8785 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8786 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8787 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8788 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8789 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8790 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8791 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8792 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8793 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8794 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8795 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8796 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8799 while (tube_leave_directions[i][0] != tube_element)
8802 if (tube_leave_directions[i][0] == -1) /* should not happen */
8806 if (!(tube_leave_directions[i][1] & move_direction))
8807 return MF_NO_ACTION; /* tube has no opening in this direction */
8810 element = Feld[x][y];
8812 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8813 game.engine_version >= VERSION_IDENT(2,2,0,0))
8814 return MF_NO_ACTION;
8818 case EL_SP_PORT_LEFT:
8819 case EL_SP_PORT_RIGHT:
8821 case EL_SP_PORT_DOWN:
8822 case EL_SP_PORT_HORIZONTAL:
8823 case EL_SP_PORT_VERTICAL:
8824 case EL_SP_PORT_ANY:
8825 case EL_SP_GRAVITY_PORT_LEFT:
8826 case EL_SP_GRAVITY_PORT_RIGHT:
8827 case EL_SP_GRAVITY_PORT_UP:
8828 case EL_SP_GRAVITY_PORT_DOWN:
8830 if (!canEnterSupaplexPort(x, y, dx, dy))
8831 return MF_NO_ACTION;
8834 element != EL_SP_PORT_LEFT &&
8835 element != EL_SP_GRAVITY_PORT_LEFT &&
8836 element != EL_SP_PORT_HORIZONTAL &&
8837 element != EL_SP_PORT_ANY) ||
8839 element != EL_SP_PORT_RIGHT &&
8840 element != EL_SP_GRAVITY_PORT_RIGHT &&
8841 element != EL_SP_PORT_HORIZONTAL &&
8842 element != EL_SP_PORT_ANY) ||
8844 element != EL_SP_PORT_UP &&
8845 element != EL_SP_GRAVITY_PORT_UP &&
8846 element != EL_SP_PORT_VERTICAL &&
8847 element != EL_SP_PORT_ANY) ||
8849 element != EL_SP_PORT_DOWN &&
8850 element != EL_SP_GRAVITY_PORT_DOWN &&
8851 element != EL_SP_PORT_VERTICAL &&
8852 element != EL_SP_PORT_ANY) ||
8853 !IN_LEV_FIELD(nextx, nexty) ||
8854 !IS_FREE(nextx, nexty))
8855 return MF_NO_ACTION;
8858 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8859 element == EL_SP_GRAVITY_PORT_RIGHT ||
8860 element == EL_SP_GRAVITY_PORT_UP ||
8861 element == EL_SP_GRAVITY_PORT_DOWN)
8862 game.gravity = !game.gravity;
8864 /* automatically move to the next field with double speed */
8865 player->programmed_action = move_direction;
8867 if (player->move_delay_reset_counter == 0)
8869 player->move_delay_reset_counter = 2; /* two double speed steps */
8871 DOUBLE_PLAYER_SPEED(player);
8874 player->move_delay_reset_counter = 2;
8876 DOUBLE_PLAYER_SPEED(player);
8879 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8883 case EL_TUBE_VERTICAL:
8884 case EL_TUBE_HORIZONTAL:
8885 case EL_TUBE_VERTICAL_LEFT:
8886 case EL_TUBE_VERTICAL_RIGHT:
8887 case EL_TUBE_HORIZONTAL_UP:
8888 case EL_TUBE_HORIZONTAL_DOWN:
8889 case EL_TUBE_LEFT_UP:
8890 case EL_TUBE_LEFT_DOWN:
8891 case EL_TUBE_RIGHT_UP:
8892 case EL_TUBE_RIGHT_DOWN:
8895 int tube_enter_directions[][2] =
8897 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8898 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8899 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8900 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8901 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8902 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8903 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8904 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8905 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8906 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8907 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8908 { -1, MV_NO_MOVING }
8911 while (tube_enter_directions[i][0] != element)
8914 if (tube_enter_directions[i][0] == -1) /* should not happen */
8918 if (!(tube_enter_directions[i][1] & move_direction))
8919 return MF_NO_ACTION; /* tube has no opening in this direction */
8921 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8927 if (IS_WALKABLE(element))
8929 int sound_action = ACTION_WALKING;
8931 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8933 if (!player->key[element - EL_GATE_1])
8934 return MF_NO_ACTION;
8936 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8938 if (!player->key[element - EL_GATE_1_GRAY])
8939 return MF_NO_ACTION;
8941 else if (element == EL_EXIT_OPEN ||
8942 element == EL_SP_EXIT_OPEN ||
8943 element == EL_SP_EXIT_OPENING)
8945 sound_action = ACTION_PASSING; /* player is passing exit */
8947 else if (element == EL_EMPTY)
8949 sound_action = ACTION_MOVING; /* nothing to walk on */
8952 /* play sound from background or player, whatever is available */
8953 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8954 PlayLevelSoundElementAction(x, y, element, sound_action);
8956 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8960 else if (IS_PASSABLE(element))
8962 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8963 return MF_NO_ACTION;
8966 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8967 return MF_NO_ACTION;
8970 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8972 if (!player->key[element - EL_EM_GATE_1])
8973 return MF_NO_ACTION;
8975 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8977 if (!player->key[element - EL_EM_GATE_1_GRAY])
8978 return MF_NO_ACTION;
8981 /* automatically move to the next field with double speed */
8982 player->programmed_action = move_direction;
8984 if (player->move_delay_reset_counter == 0)
8986 player->move_delay_reset_counter = 2; /* two double speed steps */
8988 DOUBLE_PLAYER_SPEED(player);
8991 player->move_delay_reset_counter = 2;
8993 DOUBLE_PLAYER_SPEED(player);
8996 PlayLevelSoundAction(x, y, ACTION_PASSING);
9000 else if (IS_DIGGABLE(element))
9004 if (mode != DF_SNAP)
9007 GfxElement[x][y] = GFX_ELEMENT(element);
9010 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9012 player->is_digging = TRUE;
9015 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9017 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9020 if (mode == DF_SNAP)
9021 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9026 else if (IS_COLLECTIBLE(element))
9030 if (mode != DF_SNAP)
9032 GfxElement[x][y] = element;
9033 player->is_collecting = TRUE;
9036 if (element == EL_SPEED_PILL)
9037 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9038 else if (element == EL_EXTRA_TIME && level.time > 0)
9041 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9043 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9045 player->shield_normal_time_left += 10;
9046 if (element == EL_SHIELD_DEADLY)
9047 player->shield_deadly_time_left += 10;
9049 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9051 if (player->inventory_size < MAX_INVENTORY_SIZE)
9052 player->inventory_element[player->inventory_size++] = element;
9054 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9055 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9057 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9059 player->dynabomb_count++;
9060 player->dynabombs_left++;
9062 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9064 player->dynabomb_size++;
9066 else if (element == EL_DYNABOMB_INCREASE_POWER)
9068 player->dynabomb_xl = TRUE;
9070 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9071 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9073 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9074 element - EL_KEY_1 : element - EL_EM_KEY_1);
9076 player->key[key_nr] = TRUE;
9078 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9079 el2edimg(EL_KEY_1 + key_nr));
9080 redraw_mask |= REDRAW_DOOR_1;
9082 else if (IS_ENVELOPE(element))
9085 player->show_envelope = element;
9087 ShowEnvelope(element - EL_ENVELOPE_1);
9090 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9094 for (i = 0; i < element_info[element].collect_count; i++)
9095 if (player->inventory_size < MAX_INVENTORY_SIZE)
9096 player->inventory_element[player->inventory_size++] = element;
9098 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9099 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9101 else if (element_info[element].collect_count > 0)
9103 local_player->gems_still_needed -=
9104 element_info[element].collect_count;
9105 if (local_player->gems_still_needed < 0)
9106 local_player->gems_still_needed = 0;
9108 DrawText(DX_EMERALDS, DY_EMERALDS,
9109 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9112 RaiseScoreElement(element);
9113 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9115 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9118 if (mode == DF_SNAP)
9119 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9124 else if (IS_PUSHABLE(element))
9126 if (mode == DF_SNAP && element != EL_BD_ROCK)
9127 return MF_NO_ACTION;
9129 if (CAN_FALL(element) && dy)
9130 return MF_NO_ACTION;
9132 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9133 !(element == EL_SPRING && use_spring_bug))
9134 return MF_NO_ACTION;
9137 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9138 ((move_direction & MV_VERTICAL &&
9139 ((element_info[element].move_pattern & MV_LEFT &&
9140 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9141 (element_info[element].move_pattern & MV_RIGHT &&
9142 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9143 (move_direction & MV_HORIZONTAL &&
9144 ((element_info[element].move_pattern & MV_UP &&
9145 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9146 (element_info[element].move_pattern & MV_DOWN &&
9147 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9148 return MF_NO_ACTION;
9152 /* do not push elements already moving away faster than player */
9153 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9154 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9155 return MF_NO_ACTION;
9157 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9158 return MF_NO_ACTION;
9162 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9164 if (player->push_delay_value == -1)
9165 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9167 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9169 if (!player->is_pushing)
9170 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9174 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9175 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9176 !player_is_pushing))
9177 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9180 if (!player->is_pushing &&
9181 game.engine_version >= VERSION_IDENT(2,2,0,7))
9182 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9186 printf("::: push delay: %ld [%d, %d] [%d]\n",
9187 player->push_delay_value, FrameCounter, game.engine_version,
9188 player->is_pushing);
9191 player->is_pushing = TRUE;
9193 if (!(IN_LEV_FIELD(nextx, nexty) &&
9194 (IS_FREE(nextx, nexty) ||
9195 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9196 IS_SB_ELEMENT(element)))))
9197 return MF_NO_ACTION;
9199 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9200 return MF_NO_ACTION;
9202 if (player->push_delay == 0) /* new pushing; restart delay */
9203 player->push_delay = FrameCounter;
9205 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9206 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9207 element != EL_SPRING && element != EL_BALLOON)
9209 /* make sure that there is no move delay before next try to push */
9210 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9211 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9213 return MF_NO_ACTION;
9217 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9220 if (IS_SB_ELEMENT(element))
9222 if (element == EL_SOKOBAN_FIELD_FULL)
9224 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9225 local_player->sokobanfields_still_needed++;
9228 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9230 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9231 local_player->sokobanfields_still_needed--;
9234 Feld[x][y] = EL_SOKOBAN_OBJECT;
9236 if (Back[x][y] == Back[nextx][nexty])
9237 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9238 else if (Back[x][y] != 0)
9239 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9242 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9245 if (local_player->sokobanfields_still_needed == 0 &&
9246 game.emulation == EMU_SOKOBAN)
9248 player->LevelSolved = player->GameOver = TRUE;
9249 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9253 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9255 InitMovingField(x, y, move_direction);
9256 GfxAction[x][y] = ACTION_PUSHING;
9258 if (mode == DF_SNAP)
9259 ContinueMoving(x, y);
9261 MovPos[x][y] = (dx != 0 ? dx : dy);
9263 Pushed[x][y] = TRUE;
9264 Pushed[nextx][nexty] = TRUE;
9266 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9267 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9269 player->push_delay_value = -1; /* get new value later */
9271 CheckTriggeredElementSideChange(x, y, element, dig_side,
9272 CE_OTHER_GETS_PUSHED);
9273 CheckElementSideChange(x, y, element, dig_side,
9274 CE_PUSHED_BY_PLAYER, -1);
9278 else if (IS_SWITCHABLE(element))
9280 if (PLAYER_SWITCHING(player, x, y))
9283 player->is_switching = TRUE;
9284 player->switch_x = x;
9285 player->switch_y = y;
9287 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9289 if (element == EL_ROBOT_WHEEL)
9291 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9295 DrawLevelField(x, y);
9297 else if (element == EL_SP_TERMINAL)
9301 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9303 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9305 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9306 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9309 else if (IS_BELT_SWITCH(element))
9311 ToggleBeltSwitch(x, y);
9313 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9314 element == EL_SWITCHGATE_SWITCH_DOWN)
9316 ToggleSwitchgateSwitch(x, y);
9318 else if (element == EL_LIGHT_SWITCH ||
9319 element == EL_LIGHT_SWITCH_ACTIVE)
9321 ToggleLightSwitch(x, y);
9324 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9325 SND_LIGHT_SWITCH_ACTIVATING :
9326 SND_LIGHT_SWITCH_DEACTIVATING);
9329 else if (element == EL_TIMEGATE_SWITCH)
9331 ActivateTimegateSwitch(x, y);
9333 else if (element == EL_BALLOON_SWITCH_LEFT ||
9334 element == EL_BALLOON_SWITCH_RIGHT ||
9335 element == EL_BALLOON_SWITCH_UP ||
9336 element == EL_BALLOON_SWITCH_DOWN ||
9337 element == EL_BALLOON_SWITCH_ANY)
9339 if (element == EL_BALLOON_SWITCH_ANY)
9340 game.balloon_dir = move_direction;
9342 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9343 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9344 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9345 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9348 else if (element == EL_LAMP)
9350 Feld[x][y] = EL_LAMP_ACTIVE;
9351 local_player->lights_still_needed--;
9353 DrawLevelField(x, y);
9355 else if (element == EL_TIME_ORB_FULL)
9357 Feld[x][y] = EL_TIME_ORB_EMPTY;
9359 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9361 DrawLevelField(x, y);
9364 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9372 if (!PLAYER_SWITCHING(player, x, y))
9374 player->is_switching = TRUE;
9375 player->switch_x = x;
9376 player->switch_y = y;
9378 CheckTriggeredElementSideChange(x, y, element, dig_side,
9379 CE_OTHER_IS_SWITCHING);
9380 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9383 CheckTriggeredElementSideChange(x, y, element, dig_side,
9384 CE_OTHER_GETS_PRESSED);
9385 CheckElementSideChange(x, y, element, dig_side,
9386 CE_PRESSED_BY_PLAYER, -1);
9389 return MF_NO_ACTION;
9392 player->push_delay = 0;
9394 if (Feld[x][y] != element) /* really digged/collected something */
9395 player->is_collecting = !player->is_digging;
9400 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9402 int jx = player->jx, jy = player->jy;
9403 int x = jx + dx, y = jy + dy;
9404 int snap_direction = (dx == -1 ? MV_LEFT :
9405 dx == +1 ? MV_RIGHT :
9407 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9409 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9412 if (!player->active || !IN_LEV_FIELD(x, y))
9420 if (player->MovPos == 0)
9421 player->is_pushing = FALSE;
9423 player->is_snapping = FALSE;
9425 if (player->MovPos == 0)
9427 player->is_moving = FALSE;
9428 player->is_digging = FALSE;
9429 player->is_collecting = FALSE;
9435 if (player->is_snapping)
9438 player->MovDir = snap_direction;
9441 if (player->MovPos == 0)
9444 player->is_moving = FALSE;
9445 player->is_digging = FALSE;
9446 player->is_collecting = FALSE;
9449 player->is_dropping = FALSE;
9451 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9454 player->is_snapping = TRUE;
9457 if (player->MovPos == 0)
9460 player->is_moving = FALSE;
9461 player->is_digging = FALSE;
9462 player->is_collecting = FALSE;
9465 DrawLevelField(x, y);
9471 boolean DropElement(struct PlayerInfo *player)
9473 int jx = player->jx, jy = player->jy;
9474 int old_element = Feld[jx][jy];
9477 /* check if player is active, not moving and ready to drop */
9478 if (!player->active || player->MovPos || player->drop_delay > 0)
9481 /* check if player has anything that can be dropped */
9482 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9485 /* check if anything can be dropped at the current position */
9486 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9489 /* collected custom elements can only be dropped on empty fields */
9490 if (player->inventory_size > 0 &&
9491 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9492 && old_element != EL_EMPTY)
9495 if (old_element != EL_EMPTY)
9496 Back[jx][jy] = old_element; /* store old element on this field */
9498 ResetGfxAnimation(jx, jy);
9499 ResetRandomAnimationValue(jx, jy);
9501 if (player->inventory_size > 0)
9503 player->inventory_size--;
9504 new_element = player->inventory_element[player->inventory_size];
9506 if (new_element == EL_DYNAMITE)
9507 new_element = EL_DYNAMITE_ACTIVE;
9508 else if (new_element == EL_SP_DISK_RED)
9509 new_element = EL_SP_DISK_RED_ACTIVE;
9511 Feld[jx][jy] = new_element;
9513 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9514 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9516 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9517 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9519 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9522 /* needed if previous element just changed to "empty" in the last frame */
9523 Changed[jx][jy] = 0; /* allow another change */
9526 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9527 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9529 TestIfElementTouchesCustomElement(jx, jy);
9531 else /* player is dropping a dyna bomb */
9533 player->dynabombs_left--;
9534 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9536 Feld[jx][jy] = new_element;
9538 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9539 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9541 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9548 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9550 InitField(jx, jy, FALSE);
9551 if (CAN_MOVE(Feld[jx][jy]))
9555 new_element = Feld[jx][jy];
9557 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9558 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9560 int move_stepsize = element_info[new_element].move_stepsize;
9561 int direction, dx, dy, nextx, nexty;
9563 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9564 MovDir[jx][jy] = player->MovDir;
9566 direction = MovDir[jx][jy];
9567 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9568 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9572 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9575 WasJustMoving[jx][jy] = 3;
9577 InitMovingField(jx, jy, direction);
9578 ContinueMoving(jx, jy);
9583 Changed[jx][jy] = 0; /* allow another change */
9586 TestIfElementHitsCustomElement(jx, jy, direction);
9588 CheckElementSideChange(jx, jy, new_element,
9589 direction, CE_HITTING_SOMETHING, -1);
9593 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9597 player->drop_delay = 8 + 8 + 8;
9602 player->is_dropping = TRUE;
9608 /* ------------------------------------------------------------------------- */
9609 /* game sound playing functions */
9610 /* ------------------------------------------------------------------------- */
9612 static int *loop_sound_frame = NULL;
9613 static int *loop_sound_volume = NULL;
9615 void InitPlayLevelSound()
9617 int num_sounds = getSoundListSize();
9619 checked_free(loop_sound_frame);
9620 checked_free(loop_sound_volume);
9622 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9623 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9626 static void PlayLevelSound(int x, int y, int nr)
9628 int sx = SCREENX(x), sy = SCREENY(y);
9629 int volume, stereo_position;
9630 int max_distance = 8;
9631 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9633 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9634 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9637 if (!IN_LEV_FIELD(x, y) ||
9638 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9639 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9642 volume = SOUND_MAX_VOLUME;
9644 if (!IN_SCR_FIELD(sx, sy))
9646 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9647 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9649 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9652 stereo_position = (SOUND_MAX_LEFT +
9653 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9654 (SCR_FIELDX + 2 * max_distance));
9656 if (IS_LOOP_SOUND(nr))
9658 /* This assures that quieter loop sounds do not overwrite louder ones,
9659 while restarting sound volume comparison with each new game frame. */
9661 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9664 loop_sound_volume[nr] = volume;
9665 loop_sound_frame[nr] = FrameCounter;
9668 PlaySoundExt(nr, volume, stereo_position, type);
9671 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9673 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9674 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9675 y < LEVELY(BY1) ? LEVELY(BY1) :
9676 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9680 static void PlayLevelSoundAction(int x, int y, int action)
9682 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9685 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9687 int sound_effect = element_info[element].sound[action];
9689 if (sound_effect != SND_UNDEFINED)
9690 PlayLevelSound(x, y, sound_effect);
9693 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9696 int sound_effect = element_info[element].sound[action];
9698 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9699 PlayLevelSound(x, y, sound_effect);
9702 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9704 int sound_effect = element_info[Feld[x][y]].sound[action];
9706 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9707 PlayLevelSound(x, y, sound_effect);
9710 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9712 int sound_effect = element_info[Feld[x][y]].sound[action];
9714 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9715 StopSound(sound_effect);
9718 static void PlayLevelMusic()
9720 if (levelset.music[level_nr] != MUS_UNDEFINED)
9721 PlayMusic(levelset.music[level_nr]); /* from config file */
9723 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9726 void RaiseScore(int value)
9728 local_player->score += value;
9729 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9732 void RaiseScoreElement(int element)
9738 case EL_EMERALD_YELLOW:
9739 case EL_EMERALD_RED:
9740 case EL_EMERALD_PURPLE:
9741 case EL_SP_INFOTRON:
9742 RaiseScore(level.score[SC_EMERALD]);
9745 RaiseScore(level.score[SC_DIAMOND]);
9748 RaiseScore(level.score[SC_CRYSTAL]);
9751 RaiseScore(level.score[SC_PEARL]);
9754 case EL_BD_BUTTERFLY:
9755 case EL_SP_ELECTRON:
9756 RaiseScore(level.score[SC_BUG]);
9760 case EL_SP_SNIKSNAK:
9761 RaiseScore(level.score[SC_SPACESHIP]);
9764 case EL_DARK_YAMYAM:
9765 RaiseScore(level.score[SC_YAMYAM]);
9768 RaiseScore(level.score[SC_ROBOT]);
9771 RaiseScore(level.score[SC_PACMAN]);
9774 RaiseScore(level.score[SC_NUT]);
9777 case EL_SP_DISK_RED:
9778 case EL_DYNABOMB_INCREASE_NUMBER:
9779 case EL_DYNABOMB_INCREASE_SIZE:
9780 case EL_DYNABOMB_INCREASE_POWER:
9781 RaiseScore(level.score[SC_DYNAMITE]);
9783 case EL_SHIELD_NORMAL:
9784 case EL_SHIELD_DEADLY:
9785 RaiseScore(level.score[SC_SHIELD]);
9788 RaiseScore(level.score[SC_TIME_BONUS]);
9794 RaiseScore(level.score[SC_KEY]);
9797 RaiseScore(element_info[element].collect_score);
9802 void RequestQuitGame(boolean ask_if_really_quit)
9804 if (AllPlayersGone ||
9805 !ask_if_really_quit ||
9806 level_editor_test_game ||
9807 Request("Do you really want to quit the game ?",
9808 REQ_ASK | REQ_STAY_CLOSED))
9810 #if defined(PLATFORM_UNIX)
9811 if (options.network)
9812 SendToServer_StopPlaying();
9816 game_status = GAME_MODE_MAIN;
9822 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9827 /* ---------- new game button stuff ---------------------------------------- */
9829 /* graphic position values for game buttons */
9830 #define GAME_BUTTON_XSIZE 30
9831 #define GAME_BUTTON_YSIZE 30
9832 #define GAME_BUTTON_XPOS 5
9833 #define GAME_BUTTON_YPOS 215
9834 #define SOUND_BUTTON_XPOS 5
9835 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9837 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9838 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9839 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9840 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9841 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9842 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9849 } gamebutton_info[NUM_GAME_BUTTONS] =
9852 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9857 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9862 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9867 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9868 SOUND_CTRL_ID_MUSIC,
9869 "background music on/off"
9872 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9873 SOUND_CTRL_ID_LOOPS,
9874 "sound loops on/off"
9877 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9878 SOUND_CTRL_ID_SIMPLE,
9879 "normal sounds on/off"
9883 void CreateGameButtons()
9887 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9889 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9890 struct GadgetInfo *gi;
9893 unsigned long event_mask;
9894 int gd_xoffset, gd_yoffset;
9895 int gd_x1, gd_x2, gd_y1, gd_y2;
9898 gd_xoffset = gamebutton_info[i].x;
9899 gd_yoffset = gamebutton_info[i].y;
9900 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9901 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9903 if (id == GAME_CTRL_ID_STOP ||
9904 id == GAME_CTRL_ID_PAUSE ||
9905 id == GAME_CTRL_ID_PLAY)
9907 button_type = GD_TYPE_NORMAL_BUTTON;
9909 event_mask = GD_EVENT_RELEASED;
9910 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9911 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9915 button_type = GD_TYPE_CHECK_BUTTON;
9917 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9918 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9919 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9920 event_mask = GD_EVENT_PRESSED;
9921 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9922 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9925 gi = CreateGadget(GDI_CUSTOM_ID, id,
9926 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9927 GDI_X, DX + gd_xoffset,
9928 GDI_Y, DY + gd_yoffset,
9929 GDI_WIDTH, GAME_BUTTON_XSIZE,
9930 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9931 GDI_TYPE, button_type,
9932 GDI_STATE, GD_BUTTON_UNPRESSED,
9933 GDI_CHECKED, checked,
9934 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9935 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9936 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9937 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9938 GDI_EVENT_MASK, event_mask,
9939 GDI_CALLBACK_ACTION, HandleGameButtons,
9943 Error(ERR_EXIT, "cannot create gadget");
9945 game_gadget[id] = gi;
9949 void FreeGameButtons()
9953 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9954 FreeGadget(game_gadget[i]);
9957 static void MapGameButtons()
9961 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9962 MapGadget(game_gadget[i]);
9965 void UnmapGameButtons()
9969 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9970 UnmapGadget(game_gadget[i]);
9973 static void HandleGameButtons(struct GadgetInfo *gi)
9975 int id = gi->custom_id;
9977 if (game_status != GAME_MODE_PLAYING)
9982 case GAME_CTRL_ID_STOP:
9983 RequestQuitGame(TRUE);
9986 case GAME_CTRL_ID_PAUSE:
9987 if (options.network)
9989 #if defined(PLATFORM_UNIX)
9991 SendToServer_ContinuePlaying();
9993 SendToServer_PausePlaying();
9997 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10000 case GAME_CTRL_ID_PLAY:
10003 #if defined(PLATFORM_UNIX)
10004 if (options.network)
10005 SendToServer_ContinuePlaying();
10009 tape.pausing = FALSE;
10010 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10015 case SOUND_CTRL_ID_MUSIC:
10016 if (setup.sound_music)
10018 setup.sound_music = FALSE;
10021 else if (audio.music_available)
10023 setup.sound = setup.sound_music = TRUE;
10025 SetAudioMode(setup.sound);
10031 case SOUND_CTRL_ID_LOOPS:
10032 if (setup.sound_loops)
10033 setup.sound_loops = FALSE;
10034 else if (audio.loops_available)
10036 setup.sound = setup.sound_loops = TRUE;
10037 SetAudioMode(setup.sound);
10041 case SOUND_CTRL_ID_SIMPLE:
10042 if (setup.sound_simple)
10043 setup.sound_simple = FALSE;
10044 else if (audio.sound_available)
10046 setup.sound = setup.sound_simple = TRUE;
10047 SetAudioMode(setup.sound);