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 ExplodeDelay[x][y] = 0;
1389 ExplodeField[x][y] = EX_NO_EXPLOSION;
1391 RunnerVisit[x][y] = 0;
1392 PlayerVisit[x][y] = 0;
1395 GfxRandom[x][y] = INIT_GFX_RANDOM();
1396 GfxElement[x][y] = EL_UNDEFINED;
1397 GfxAction[x][y] = ACTION_DEFAULT;
1398 GfxDir[x][y] = MV_NO_MOVING;
1402 for (y = 0; y < lev_fieldy; y++)
1404 for (x = 0; x < lev_fieldx; x++)
1406 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1408 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1410 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1413 InitField(x, y, TRUE);
1419 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1420 emulate_sb ? EMU_SOKOBAN :
1421 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1423 /* initialize explosion and ignition delay */
1424 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1426 if (!IS_CUSTOM_ELEMENT(i))
1429 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1430 int last_phase = num_phase * delay;
1431 int half_phase = (num_phase / 2) * delay;
1433 element_info[i].explosion_delay = last_phase;
1434 element_info[i].ignition_delay = half_phase;
1436 if (i == EL_BLACK_ORB)
1437 element_info[i].ignition_delay = 1;
1440 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1441 element_info[i].explosion_delay = 2;
1443 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1444 element_info[i].ignition_delay = 1;
1447 /* correct non-moving belts to start moving left */
1448 for (i = 0; i < 4; i++)
1449 if (game.belt_dir[i] == MV_NO_MOVING)
1450 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1452 /* check if any connected player was not found in playfield */
1453 for (i = 0; i < MAX_PLAYERS; i++)
1455 struct PlayerInfo *player = &stored_player[i];
1457 if (player->connected && !player->present)
1459 for (j = 0; j < MAX_PLAYERS; j++)
1461 struct PlayerInfo *some_player = &stored_player[j];
1462 int jx = some_player->jx, jy = some_player->jy;
1464 /* assign first free player found that is present in the playfield */
1465 if (some_player->present && !some_player->connected)
1467 player->present = TRUE;
1468 player->active = TRUE;
1470 some_player->present = FALSE;
1471 some_player->active = FALSE;
1473 StorePlayer[jx][jy] = player->element_nr;
1474 player->jx = player->last_jx = jx;
1475 player->jy = player->last_jy = jy;
1485 /* when playing a tape, eliminate all players which do not participate */
1487 for (i = 0; i < MAX_PLAYERS; i++)
1489 if (stored_player[i].active && !tape.player_participates[i])
1491 struct PlayerInfo *player = &stored_player[i];
1492 int jx = player->jx, jy = player->jy;
1494 player->active = FALSE;
1495 StorePlayer[jx][jy] = 0;
1496 Feld[jx][jy] = EL_EMPTY;
1500 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1502 /* when in single player mode, eliminate all but the first active player */
1504 for (i = 0; i < MAX_PLAYERS; i++)
1506 if (stored_player[i].active)
1508 for (j = i + 1; j < MAX_PLAYERS; j++)
1510 if (stored_player[j].active)
1512 struct PlayerInfo *player = &stored_player[j];
1513 int jx = player->jx, jy = player->jy;
1515 player->active = FALSE;
1516 player->present = FALSE;
1518 StorePlayer[jx][jy] = 0;
1519 Feld[jx][jy] = EL_EMPTY;
1526 /* when recording the game, store which players take part in the game */
1529 for (i = 0; i < MAX_PLAYERS; i++)
1530 if (stored_player[i].active)
1531 tape.player_participates[i] = TRUE;
1536 for (i = 0; i < MAX_PLAYERS; i++)
1538 struct PlayerInfo *player = &stored_player[i];
1540 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1545 if (local_player == player)
1546 printf("Player %d is local player.\n", i+1);
1550 if (BorderElement == EL_EMPTY)
1553 SBX_Right = lev_fieldx - SCR_FIELDX;
1555 SBY_Lower = lev_fieldy - SCR_FIELDY;
1560 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1562 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1565 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1566 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1568 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1569 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1571 /* if local player not found, look for custom element that might create
1572 the player (make some assumptions about the right custom element) */
1573 if (!local_player->present)
1575 int start_x = 0, start_y = 0;
1576 int found_rating = 0;
1577 int found_element = EL_UNDEFINED;
1579 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1581 int element = Feld[x][y];
1586 if (!IS_CUSTOM_ELEMENT(element))
1589 if (CAN_CHANGE(element))
1591 for (i = 0; i < element_info[element].num_change_pages; i++)
1593 content = element_info[element].change_page[i].target_element;
1594 is_player = ELEM_IS_PLAYER(content);
1596 if (is_player && (found_rating < 3 || element < found_element))
1602 found_element = element;
1607 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1609 content = element_info[element].content[xx][yy];
1610 is_player = ELEM_IS_PLAYER(content);
1612 if (is_player && (found_rating < 2 || element < found_element))
1614 start_x = x + xx - 1;
1615 start_y = y + yy - 1;
1618 found_element = element;
1621 if (!CAN_CHANGE(element))
1624 for (i = 0; i < element_info[element].num_change_pages; i++)
1626 content = element_info[element].change_page[i].content[xx][yy];
1627 is_player = ELEM_IS_PLAYER(content);
1629 if (is_player && (found_rating < 1 || element < found_element))
1631 start_x = x + xx - 1;
1632 start_y = y + yy - 1;
1635 found_element = element;
1641 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1642 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1645 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1646 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1652 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1653 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1654 local_player->jx - MIDPOSX);
1656 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1657 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1658 local_player->jy - MIDPOSY);
1660 scroll_x = SBX_Left;
1661 scroll_y = SBY_Upper;
1662 if (local_player->jx >= SBX_Left + MIDPOSX)
1663 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1664 local_player->jx - MIDPOSX :
1666 if (local_player->jy >= SBY_Upper + MIDPOSY)
1667 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1668 local_player->jy - MIDPOSY :
1673 CloseDoor(DOOR_CLOSE_1);
1678 /* after drawing the level, correct some elements */
1679 if (game.timegate_time_left == 0)
1680 CloseAllOpenTimegates();
1682 if (setup.soft_scrolling)
1683 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1685 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1688 /* copy default game door content to main double buffer */
1689 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1690 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1693 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1696 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1697 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1698 BlitBitmap(drawto, drawto,
1699 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1700 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1701 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1702 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1705 DrawGameDoorValues();
1709 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1710 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1711 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1715 /* copy actual game door content to door double buffer for OpenDoor() */
1716 BlitBitmap(drawto, bitmap_db_door,
1717 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1719 OpenDoor(DOOR_OPEN_ALL);
1721 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1723 if (setup.sound_music)
1726 KeyboardAutoRepeatOffUnlessAutoplay();
1730 for (i = 0; i < 4; i++)
1731 printf("Player %d %sactive.\n",
1732 i + 1, (stored_player[i].active ? "" : "not "));
1736 printf("::: starting game [%d]\n", FrameCounter);
1740 void InitMovDir(int x, int y)
1742 int i, element = Feld[x][y];
1743 static int xy[4][2] =
1750 static int direction[3][4] =
1752 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1753 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1754 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1763 Feld[x][y] = EL_BUG;
1764 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1767 case EL_SPACESHIP_RIGHT:
1768 case EL_SPACESHIP_UP:
1769 case EL_SPACESHIP_LEFT:
1770 case EL_SPACESHIP_DOWN:
1771 Feld[x][y] = EL_SPACESHIP;
1772 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1775 case EL_BD_BUTTERFLY_RIGHT:
1776 case EL_BD_BUTTERFLY_UP:
1777 case EL_BD_BUTTERFLY_LEFT:
1778 case EL_BD_BUTTERFLY_DOWN:
1779 Feld[x][y] = EL_BD_BUTTERFLY;
1780 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1783 case EL_BD_FIREFLY_RIGHT:
1784 case EL_BD_FIREFLY_UP:
1785 case EL_BD_FIREFLY_LEFT:
1786 case EL_BD_FIREFLY_DOWN:
1787 Feld[x][y] = EL_BD_FIREFLY;
1788 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1791 case EL_PACMAN_RIGHT:
1793 case EL_PACMAN_LEFT:
1794 case EL_PACMAN_DOWN:
1795 Feld[x][y] = EL_PACMAN;
1796 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1799 case EL_SP_SNIKSNAK:
1800 MovDir[x][y] = MV_UP;
1803 case EL_SP_ELECTRON:
1804 MovDir[x][y] = MV_LEFT;
1811 Feld[x][y] = EL_MOLE;
1812 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1816 if (IS_CUSTOM_ELEMENT(element))
1818 struct ElementInfo *ei = &element_info[element];
1819 int move_direction_initial = ei->move_direction_initial;
1820 int move_pattern = ei->move_pattern;
1822 if (move_direction_initial == MV_START_PREVIOUS)
1824 if (MovDir[x][y] != MV_NO_MOVING)
1827 move_direction_initial = MV_START_AUTOMATIC;
1830 if (move_direction_initial == MV_START_RANDOM)
1831 MovDir[x][y] = 1 << RND(4);
1832 else if (move_direction_initial & MV_ANY_DIRECTION)
1833 MovDir[x][y] = move_direction_initial;
1834 else if (move_pattern == MV_ALL_DIRECTIONS ||
1835 move_pattern == MV_TURNING_LEFT ||
1836 move_pattern == MV_TURNING_RIGHT ||
1837 move_pattern == MV_TURNING_LEFT_RIGHT ||
1838 move_pattern == MV_TURNING_RIGHT_LEFT ||
1839 move_pattern == MV_TURNING_RANDOM)
1840 MovDir[x][y] = 1 << RND(4);
1841 else if (move_pattern == MV_HORIZONTAL)
1842 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1843 else if (move_pattern == MV_VERTICAL)
1844 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1845 else if (move_pattern & MV_ANY_DIRECTION)
1846 MovDir[x][y] = element_info[element].move_pattern;
1847 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1848 move_pattern == MV_ALONG_RIGHT_SIDE)
1850 for (i = 0; i < 4; i++)
1852 int x1 = x + xy[i][0];
1853 int y1 = y + xy[i][1];
1855 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1857 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1858 MovDir[x][y] = direction[0][i];
1860 MovDir[x][y] = direction[1][i];
1869 MovDir[x][y] = 1 << RND(4);
1871 if (element != EL_BUG &&
1872 element != EL_SPACESHIP &&
1873 element != EL_BD_BUTTERFLY &&
1874 element != EL_BD_FIREFLY)
1877 for (i = 0; i < 4; i++)
1879 int x1 = x + xy[i][0];
1880 int y1 = y + xy[i][1];
1882 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1884 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1886 MovDir[x][y] = direction[0][i];
1889 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1890 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1892 MovDir[x][y] = direction[1][i];
1901 GfxDir[x][y] = MovDir[x][y];
1904 void InitAmoebaNr(int x, int y)
1907 int group_nr = AmoebeNachbarNr(x, y);
1911 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1913 if (AmoebaCnt[i] == 0)
1921 AmoebaNr[x][y] = group_nr;
1922 AmoebaCnt[group_nr]++;
1923 AmoebaCnt2[group_nr]++;
1929 boolean raise_level = FALSE;
1931 if (local_player->MovPos)
1935 if (tape.auto_play) /* tape might already be stopped here */
1936 tape.auto_play_level_solved = TRUE;
1938 if (tape.playing && tape.auto_play)
1939 tape.auto_play_level_solved = TRUE;
1942 local_player->LevelSolved = FALSE;
1944 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1948 if (!tape.playing && setup.sound_loops)
1949 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1950 SND_CTRL_PLAY_LOOP);
1952 while (TimeLeft > 0)
1954 if (!tape.playing && !setup.sound_loops)
1955 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1956 if (TimeLeft > 0 && !(TimeLeft % 10))
1957 RaiseScore(level.score[SC_TIME_BONUS]);
1958 if (TimeLeft > 100 && !(TimeLeft % 10))
1962 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1969 if (!tape.playing && setup.sound_loops)
1970 StopSound(SND_GAME_LEVELTIME_BONUS);
1972 else if (level.time == 0) /* level without time limit */
1974 if (!tape.playing && setup.sound_loops)
1975 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1976 SND_CTRL_PLAY_LOOP);
1978 while (TimePlayed < 999)
1980 if (!tape.playing && !setup.sound_loops)
1981 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1982 if (TimePlayed < 999 && !(TimePlayed % 10))
1983 RaiseScore(level.score[SC_TIME_BONUS]);
1984 if (TimePlayed < 900 && !(TimePlayed % 10))
1988 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1995 if (!tape.playing && setup.sound_loops)
1996 StopSound(SND_GAME_LEVELTIME_BONUS);
1999 /* close exit door after last player */
2000 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2001 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2003 int element = Feld[ExitX][ExitY];
2005 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2006 EL_SP_EXIT_CLOSING);
2008 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2011 /* Hero disappears */
2012 DrawLevelField(ExitX, ExitY);
2018 CloseDoor(DOOR_CLOSE_1);
2023 SaveTape(tape.level_nr); /* Ask to save tape */
2026 if (level_nr == leveldir_current->handicap_level)
2028 leveldir_current->handicap_level++;
2029 SaveLevelSetup_SeriesInfo();
2032 if (level_editor_test_game)
2033 local_player->score = -1; /* no highscore when playing from editor */
2034 else if (level_nr < leveldir_current->last_level)
2035 raise_level = TRUE; /* advance to next level */
2037 if ((hi_pos = NewHiScore()) >= 0)
2039 game_status = GAME_MODE_SCORES;
2040 DrawHallOfFame(hi_pos);
2049 game_status = GAME_MODE_MAIN;
2066 LoadScore(level_nr);
2068 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2069 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2072 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2074 if (local_player->score > highscore[k].Score)
2076 /* player has made it to the hall of fame */
2078 if (k < MAX_SCORE_ENTRIES - 1)
2080 int m = MAX_SCORE_ENTRIES - 1;
2083 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2084 if (!strcmp(setup.player_name, highscore[l].Name))
2086 if (m == k) /* player's new highscore overwrites his old one */
2090 for (l = m; l > k; l--)
2092 strcpy(highscore[l].Name, highscore[l - 1].Name);
2093 highscore[l].Score = highscore[l - 1].Score;
2100 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2101 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2102 highscore[k].Score = local_player->score;
2108 else if (!strncmp(setup.player_name, highscore[k].Name,
2109 MAX_PLAYER_NAME_LEN))
2110 break; /* player already there with a higher score */
2116 SaveScore(level_nr);
2121 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2123 if (player->GfxAction != action || player->GfxDir != dir)
2126 printf("Player frame reset! (%d => %d, %d => %d)\n",
2127 player->GfxAction, action, player->GfxDir, dir);
2130 player->GfxAction = action;
2131 player->GfxDir = dir;
2133 player->StepFrame = 0;
2137 static void ResetRandomAnimationValue(int x, int y)
2139 GfxRandom[x][y] = INIT_GFX_RANDOM();
2142 static void ResetGfxAnimation(int x, int y)
2145 GfxAction[x][y] = ACTION_DEFAULT;
2146 GfxDir[x][y] = MovDir[x][y];
2149 void InitMovingField(int x, int y, int direction)
2151 int element = Feld[x][y];
2152 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2153 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2157 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2158 ResetGfxAnimation(x, y);
2160 MovDir[newx][newy] = MovDir[x][y] = direction;
2161 GfxDir[x][y] = direction;
2163 if (Feld[newx][newy] == EL_EMPTY)
2164 Feld[newx][newy] = EL_BLOCKED;
2166 if (direction == MV_DOWN && CAN_FALL(element))
2167 GfxAction[x][y] = ACTION_FALLING;
2169 GfxAction[x][y] = ACTION_MOVING;
2171 GfxFrame[newx][newy] = GfxFrame[x][y];
2172 GfxRandom[newx][newy] = GfxRandom[x][y];
2173 GfxAction[newx][newy] = GfxAction[x][y];
2174 GfxDir[newx][newy] = GfxDir[x][y];
2177 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2179 int direction = MovDir[x][y];
2180 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2181 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2187 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2189 int oldx = x, oldy = y;
2190 int direction = MovDir[x][y];
2192 if (direction == MV_LEFT)
2194 else if (direction == MV_RIGHT)
2196 else if (direction == MV_UP)
2198 else if (direction == MV_DOWN)
2201 *comes_from_x = oldx;
2202 *comes_from_y = oldy;
2205 int MovingOrBlocked2Element(int x, int y)
2207 int element = Feld[x][y];
2209 if (element == EL_BLOCKED)
2213 Blocked2Moving(x, y, &oldx, &oldy);
2214 return Feld[oldx][oldy];
2220 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2222 /* like MovingOrBlocked2Element(), but if element is moving
2223 and (x,y) is the field the moving element is just leaving,
2224 return EL_BLOCKED instead of the element value */
2225 int element = Feld[x][y];
2227 if (IS_MOVING(x, y))
2229 if (element == EL_BLOCKED)
2233 Blocked2Moving(x, y, &oldx, &oldy);
2234 return Feld[oldx][oldy];
2243 static void RemoveField(int x, int y)
2245 Feld[x][y] = EL_EMPTY;
2252 ChangeDelay[x][y] = 0;
2253 ChangePage[x][y] = -1;
2254 Pushed[x][y] = FALSE;
2256 GfxElement[x][y] = EL_UNDEFINED;
2257 GfxAction[x][y] = ACTION_DEFAULT;
2258 GfxDir[x][y] = MV_NO_MOVING;
2261 void RemoveMovingField(int x, int y)
2263 int oldx = x, oldy = y, newx = x, newy = y;
2264 int element = Feld[x][y];
2265 int next_element = EL_UNDEFINED;
2267 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2270 if (IS_MOVING(x, y))
2272 Moving2Blocked(x, y, &newx, &newy);
2273 if (Feld[newx][newy] != EL_BLOCKED)
2276 else if (element == EL_BLOCKED)
2278 Blocked2Moving(x, y, &oldx, &oldy);
2279 if (!IS_MOVING(oldx, oldy))
2283 if (element == EL_BLOCKED &&
2284 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2285 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2286 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2287 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2288 next_element = get_next_element(Feld[oldx][oldy]);
2290 RemoveField(oldx, oldy);
2291 RemoveField(newx, newy);
2293 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2295 if (next_element != EL_UNDEFINED)
2296 Feld[oldx][oldy] = next_element;
2298 DrawLevelField(oldx, oldy);
2299 DrawLevelField(newx, newy);
2302 void DrawDynamite(int x, int y)
2304 int sx = SCREENX(x), sy = SCREENY(y);
2305 int graphic = el2img(Feld[x][y]);
2308 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2311 if (IS_WALKABLE_INSIDE(Back[x][y]))
2315 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2316 else if (Store[x][y])
2317 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2319 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2322 if (Back[x][y] || Store[x][y])
2323 DrawGraphicThruMask(sx, sy, graphic, frame);
2325 DrawGraphic(sx, sy, graphic, frame);
2327 if (game.emulation == EMU_SUPAPLEX)
2328 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2329 else if (Store[x][y])
2330 DrawGraphicThruMask(sx, sy, graphic, frame);
2332 DrawGraphic(sx, sy, graphic, frame);
2336 void CheckDynamite(int x, int y)
2338 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2342 if (MovDelay[x][y] != 0)
2345 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2352 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2354 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2355 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2356 StopSound(SND_DYNAMITE_ACTIVE);
2358 StopSound(SND_DYNABOMB_ACTIVE);
2364 void RelocatePlayer(int x, int y, int element)
2366 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2367 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2368 boolean no_delay = (tape.index_search);
2369 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2370 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2372 if (player->GameOver) /* do not reanimate dead player */
2376 RemoveField(x, y); /* temporarily remove newly placed player */
2377 DrawLevelField(x, y);
2380 if (player->present)
2382 while (player->MovPos)
2384 ScrollPlayer(player, SCROLL_GO_ON);
2385 ScrollScreen(NULL, SCROLL_GO_ON);
2391 Delay(wait_delay_value);
2394 DrawPlayer(player); /* needed here only to cleanup last field */
2395 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2397 player->is_moving = FALSE;
2400 Feld[x][y] = element;
2401 InitPlayerField(x, y, element, TRUE);
2403 if (player == local_player)
2405 int scroll_xx = -999, scroll_yy = -999;
2407 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2410 int fx = FX, fy = FY;
2412 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2413 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2414 local_player->jx - MIDPOSX);
2416 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2417 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2418 local_player->jy - MIDPOSY);
2420 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2421 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2426 fx += dx * TILEX / 2;
2427 fy += dy * TILEY / 2;
2429 ScrollLevel(dx, dy);
2432 /* scroll in two steps of half tile size to make things smoother */
2433 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2435 Delay(wait_delay_value);
2437 /* scroll second step to align at full tile size */
2439 Delay(wait_delay_value);
2444 void Explode(int ex, int ey, int phase, int mode)
2448 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2449 int last_phase = num_phase * delay;
2450 int half_phase = (num_phase / 2) * delay;
2451 int first_phase_after_start = EX_PHASE_START + 1;
2454 int last_phase_TEST = last_phase;
2456 if (game.explosions_delayed)
2458 ExplodeField[ex][ey] = mode;
2462 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2464 int center_element = Feld[ex][ey];
2467 /* --- This is only really needed (and now handled) in "Impact()". --- */
2468 /* do not explode moving elements that left the explode field in time */
2469 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2470 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2474 if (mode == EX_NORMAL || mode == EX_CENTER)
2475 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2477 /* remove things displayed in background while burning dynamite */
2478 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2481 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2483 /* put moving element to center field (and let it explode there) */
2484 center_element = MovingOrBlocked2Element(ex, ey);
2485 RemoveMovingField(ex, ey);
2486 Feld[ex][ey] = center_element;
2490 last_phase = element_info[center_element].explosion_delay;
2493 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2495 int xx = x - ex + 1;
2496 int yy = y - ey + 1;
2499 if (!IN_LEV_FIELD(x, y) ||
2500 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2501 (x != ex || y != ey)))
2504 element = Feld[x][y];
2506 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2508 element = MovingOrBlocked2Element(x, y);
2510 if (!IS_EXPLOSION_PROOF(element))
2511 RemoveMovingField(x, y);
2517 if (IS_EXPLOSION_PROOF(element))
2520 /* indestructible elements can only explode in center (but not flames) */
2521 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2522 element == EL_FLAMES)
2527 if ((IS_INDESTRUCTIBLE(element) &&
2528 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2529 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2530 element == EL_FLAMES)
2534 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2536 if (IS_ACTIVE_BOMB(element))
2538 /* re-activate things under the bomb like gate or penguin */
2539 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2546 /* save walkable background elements while explosion on same tile */
2548 if (IS_INDESTRUCTIBLE(element))
2549 Back[x][y] = element;
2551 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2552 Back[x][y] = element;
2555 /* ignite explodable elements reached by other explosion */
2556 if (element == EL_EXPLOSION)
2557 element = Store2[x][y];
2560 if (AmoebaNr[x][y] &&
2561 (element == EL_AMOEBA_FULL ||
2562 element == EL_BD_AMOEBA ||
2563 element == EL_AMOEBA_GROWING))
2565 AmoebaCnt[AmoebaNr[x][y]]--;
2566 AmoebaCnt2[AmoebaNr[x][y]]--;
2572 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2574 switch(StorePlayer[ex][ey])
2577 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2580 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2583 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2587 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2591 if (game.emulation == EMU_SUPAPLEX)
2592 Store[x][y] = EL_EMPTY;
2594 else if (center_element == EL_MOLE)
2595 Store[x][y] = EL_EMERALD_RED;
2596 else if (center_element == EL_PENGUIN)
2597 Store[x][y] = EL_EMERALD_PURPLE;
2598 else if (center_element == EL_BUG)
2599 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2600 else if (center_element == EL_BD_BUTTERFLY)
2601 Store[x][y] = EL_BD_DIAMOND;
2602 else if (center_element == EL_SP_ELECTRON)
2603 Store[x][y] = EL_SP_INFOTRON;
2604 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2605 Store[x][y] = level.amoeba_content;
2606 else if (center_element == EL_YAMYAM)
2607 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2608 else if (IS_CUSTOM_ELEMENT(center_element) &&
2609 element_info[center_element].content[xx][yy] != EL_EMPTY)
2610 Store[x][y] = element_info[center_element].content[xx][yy];
2611 else if (element == EL_WALL_EMERALD)
2612 Store[x][y] = EL_EMERALD;
2613 else if (element == EL_WALL_DIAMOND)
2614 Store[x][y] = EL_DIAMOND;
2615 else if (element == EL_WALL_BD_DIAMOND)
2616 Store[x][y] = EL_BD_DIAMOND;
2617 else if (element == EL_WALL_EMERALD_YELLOW)
2618 Store[x][y] = EL_EMERALD_YELLOW;
2619 else if (element == EL_WALL_EMERALD_RED)
2620 Store[x][y] = EL_EMERALD_RED;
2621 else if (element == EL_WALL_EMERALD_PURPLE)
2622 Store[x][y] = EL_EMERALD_PURPLE;
2623 else if (element == EL_WALL_PEARL)
2624 Store[x][y] = EL_PEARL;
2625 else if (element == EL_WALL_CRYSTAL)
2626 Store[x][y] = EL_CRYSTAL;
2627 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2628 Store[x][y] = element_info[element].content[1][1];
2630 Store[x][y] = EL_EMPTY;
2632 if (x != ex || y != ey ||
2633 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2634 Store2[x][y] = element;
2637 if (AmoebaNr[x][y] &&
2638 (element == EL_AMOEBA_FULL ||
2639 element == EL_BD_AMOEBA ||
2640 element == EL_AMOEBA_GROWING))
2642 AmoebaCnt[AmoebaNr[x][y]]--;
2643 AmoebaCnt2[AmoebaNr[x][y]]--;
2649 MovDir[x][y] = MovPos[x][y] = 0;
2650 GfxDir[x][y] = MovDir[x][y];
2655 Feld[x][y] = EL_EXPLOSION;
2657 GfxElement[x][y] = center_element;
2659 GfxElement[x][y] = EL_UNDEFINED;
2662 ExplodePhase[x][y] = 1;
2664 ExplodeDelay[x][y] = last_phase;
2669 if (center_element == EL_YAMYAM)
2670 game.yamyam_content_nr =
2671 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2683 last_phase = ExplodeDelay[x][y];
2686 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2690 /* activate this even in non-DEBUG version until cause for crash in
2691 getGraphicAnimationFrame() (see below) is found and eliminated */
2695 if (GfxElement[x][y] == EL_UNDEFINED)
2698 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2699 printf("Explode(): This should never happen!\n");
2702 GfxElement[x][y] = EL_EMPTY;
2708 border_element = Store2[x][y];
2709 if (IS_PLAYER(x, y))
2710 border_element = StorePlayer[x][y];
2712 if (phase == element_info[border_element].ignition_delay ||
2713 phase == last_phase)
2715 boolean border_explosion = FALSE;
2717 if (IS_PLAYER(x, y))
2719 KillHeroUnlessExplosionProtected(x, y);
2720 border_explosion = TRUE;
2722 if (phase == last_phase)
2723 printf("::: IS_PLAYER\n");
2725 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2727 Feld[x][y] = Store2[x][y];
2730 border_explosion = TRUE;
2732 if (phase == last_phase)
2733 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2735 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2737 AmoebeUmwandeln(x, y);
2739 border_explosion = TRUE;
2741 if (phase == last_phase)
2742 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2743 element_info[border_element].explosion_delay,
2744 element_info[border_element].ignition_delay,
2749 /* if an element just explodes due to another explosion (chain-reaction),
2750 do not immediately end the new explosion when it was the last frame of
2751 the explosion (as it would be done in the following "if"-statement!) */
2752 if (border_explosion && phase == last_phase)
2759 if (phase == first_phase_after_start)
2761 int element = Store2[x][y];
2763 if (element == EL_BLACK_ORB)
2765 Feld[x][y] = Store2[x][y];
2770 else if (phase == half_phase)
2772 int element = Store2[x][y];
2774 if (IS_PLAYER(x, y))
2775 KillHeroUnlessExplosionProtected(x, y);
2776 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2778 Feld[x][y] = Store2[x][y];
2782 else if (element == EL_AMOEBA_TO_DIAMOND)
2783 AmoebeUmwandeln(x, y);
2787 if (phase == last_phase)
2791 element = Feld[x][y] = Store[x][y];
2792 Store[x][y] = Store2[x][y] = 0;
2793 GfxElement[x][y] = EL_UNDEFINED;
2795 /* player can escape from explosions and might therefore be still alive */
2796 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2797 element <= EL_PLAYER_IS_EXPLODING_4)
2798 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2800 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2801 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2802 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2805 /* restore probably existing indestructible background element */
2806 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2807 element = Feld[x][y] = Back[x][y];
2810 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2811 GfxDir[x][y] = MV_NO_MOVING;
2812 ChangeDelay[x][y] = 0;
2813 ChangePage[x][y] = -1;
2815 InitField(x, y, FALSE);
2817 /* !!! not needed !!! */
2818 if (CAN_MOVE(element))
2821 DrawLevelField(x, y);
2823 TestIfElementTouchesCustomElement(x, y);
2825 if (GFX_CRUMBLED(element))
2826 DrawLevelFieldCrumbledSandNeighbours(x, y);
2828 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2829 StorePlayer[x][y] = 0;
2831 if (ELEM_IS_PLAYER(element))
2832 RelocatePlayer(x, y, element);
2835 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2837 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2841 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2843 int stored = Store[x][y];
2844 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2845 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2848 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2851 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2852 element_info[GfxElement[x][y]].token_name,
2857 DrawLevelFieldCrumbledSand(x, y);
2859 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2861 DrawLevelElement(x, y, Back[x][y]);
2862 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2864 else if (IS_WALKABLE_UNDER(Back[x][y]))
2866 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2867 DrawLevelElementThruMask(x, y, Back[x][y]);
2869 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2870 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2874 void DynaExplode(int ex, int ey)
2877 int dynabomb_element = Feld[ex][ey];
2878 int dynabomb_size = 1;
2879 boolean dynabomb_xl = FALSE;
2880 struct PlayerInfo *player;
2881 static int xy[4][2] =
2889 if (IS_ACTIVE_BOMB(dynabomb_element))
2891 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2892 dynabomb_size = player->dynabomb_size;
2893 dynabomb_xl = player->dynabomb_xl;
2894 player->dynabombs_left++;
2897 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2899 for (i = 0; i < 4; i++)
2901 for (j = 1; j <= dynabomb_size; j++)
2903 int x = ex + j * xy[i % 4][0];
2904 int y = ey + j * xy[i % 4][1];
2907 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2910 element = Feld[x][y];
2912 /* do not restart explosions of fields with active bombs */
2913 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2916 Explode(x, y, EX_PHASE_START, EX_BORDER);
2918 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2919 if (element != EL_EMPTY &&
2920 element != EL_SAND &&
2921 element != EL_EXPLOSION &&
2928 void Bang(int x, int y)
2931 int element = MovingOrBlocked2Element(x, y);
2933 int element = Feld[x][y];
2937 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2939 if (IS_PLAYER(x, y))
2942 struct PlayerInfo *player = PLAYERINFO(x, y);
2944 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2945 player->element_nr);
2950 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2952 if (game.emulation == EMU_SUPAPLEX)
2953 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2955 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2960 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2968 case EL_BD_BUTTERFLY:
2971 case EL_DARK_YAMYAM:
2975 RaiseScoreElement(element);
2976 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2978 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2979 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2980 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2981 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2982 case EL_DYNABOMB_INCREASE_NUMBER:
2983 case EL_DYNABOMB_INCREASE_SIZE:
2984 case EL_DYNABOMB_INCREASE_POWER:
2989 case EL_LAMP_ACTIVE:
2990 if (IS_PLAYER(x, y))
2991 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2993 Explode(x, y, EX_PHASE_START, EX_CENTER);
2996 if (CAN_EXPLODE_DYNA(element))
2998 else if (CAN_EXPLODE_1X1(element))
2999 Explode(x, y, EX_PHASE_START, EX_CENTER);
3001 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3005 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3008 void SplashAcid(int x, int y)
3010 int element = Feld[x][y];
3012 if (element != EL_ACID_SPLASH_LEFT &&
3013 element != EL_ACID_SPLASH_RIGHT)
3015 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3017 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3018 (!IN_LEV_FIELD(x-1, y-1) ||
3019 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3020 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3022 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3023 (!IN_LEV_FIELD(x+1, y-1) ||
3024 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3025 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3029 static void InitBeltMovement()
3031 static int belt_base_element[4] =
3033 EL_CONVEYOR_BELT_1_LEFT,
3034 EL_CONVEYOR_BELT_2_LEFT,
3035 EL_CONVEYOR_BELT_3_LEFT,
3036 EL_CONVEYOR_BELT_4_LEFT
3038 static int belt_base_active_element[4] =
3040 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3041 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3042 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3043 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3048 /* set frame order for belt animation graphic according to belt direction */
3049 for (i = 0; i < 4; i++)
3053 for (j = 0; j < 3; j++)
3055 int element = belt_base_active_element[belt_nr] + j;
3056 int graphic = el2img(element);
3058 if (game.belt_dir[i] == MV_LEFT)
3059 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3061 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3065 for (y = 0; y < lev_fieldy; y++)
3067 for (x = 0; x < lev_fieldx; x++)
3069 int element = Feld[x][y];
3071 for (i = 0; i < 4; i++)
3073 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3075 int e_belt_nr = getBeltNrFromBeltElement(element);
3078 if (e_belt_nr == belt_nr)
3080 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3082 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3090 static void ToggleBeltSwitch(int x, int y)
3092 static int belt_base_element[4] =
3094 EL_CONVEYOR_BELT_1_LEFT,
3095 EL_CONVEYOR_BELT_2_LEFT,
3096 EL_CONVEYOR_BELT_3_LEFT,
3097 EL_CONVEYOR_BELT_4_LEFT
3099 static int belt_base_active_element[4] =
3101 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3102 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3103 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3104 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3106 static int belt_base_switch_element[4] =
3108 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3109 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3110 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3111 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3113 static int belt_move_dir[4] =
3121 int element = Feld[x][y];
3122 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3123 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3124 int belt_dir = belt_move_dir[belt_dir_nr];
3127 if (!IS_BELT_SWITCH(element))
3130 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3131 game.belt_dir[belt_nr] = belt_dir;
3133 if (belt_dir_nr == 3)
3136 /* set frame order for belt animation graphic according to belt direction */
3137 for (i = 0; i < 3; i++)
3139 int element = belt_base_active_element[belt_nr] + i;
3140 int graphic = el2img(element);
3142 if (belt_dir == MV_LEFT)
3143 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3145 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3148 for (yy = 0; yy < lev_fieldy; yy++)
3150 for (xx = 0; xx < lev_fieldx; xx++)
3152 int element = Feld[xx][yy];
3154 if (IS_BELT_SWITCH(element))
3156 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3158 if (e_belt_nr == belt_nr)
3160 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3161 DrawLevelField(xx, yy);
3164 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3166 int e_belt_nr = getBeltNrFromBeltElement(element);
3168 if (e_belt_nr == belt_nr)
3170 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3172 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3173 DrawLevelField(xx, yy);
3176 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3178 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3180 if (e_belt_nr == belt_nr)
3182 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3184 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3185 DrawLevelField(xx, yy);
3192 static void ToggleSwitchgateSwitch(int x, int y)
3196 game.switchgate_pos = !game.switchgate_pos;
3198 for (yy = 0; yy < lev_fieldy; yy++)
3200 for (xx = 0; xx < lev_fieldx; xx++)
3202 int element = Feld[xx][yy];
3204 if (element == EL_SWITCHGATE_SWITCH_UP ||
3205 element == EL_SWITCHGATE_SWITCH_DOWN)
3207 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3208 DrawLevelField(xx, yy);
3210 else if (element == EL_SWITCHGATE_OPEN ||
3211 element == EL_SWITCHGATE_OPENING)
3213 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3215 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3217 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3220 else if (element == EL_SWITCHGATE_CLOSED ||
3221 element == EL_SWITCHGATE_CLOSING)
3223 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3225 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3227 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3234 static int getInvisibleActiveFromInvisibleElement(int element)
3236 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3237 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3238 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3242 static int getInvisibleFromInvisibleActiveElement(int element)
3244 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3245 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3246 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3250 static void RedrawAllLightSwitchesAndInvisibleElements()
3254 for (y = 0; y < lev_fieldy; y++)
3256 for (x = 0; x < lev_fieldx; x++)
3258 int element = Feld[x][y];
3260 if (element == EL_LIGHT_SWITCH &&
3261 game.light_time_left > 0)
3263 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3264 DrawLevelField(x, y);
3266 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3267 game.light_time_left == 0)
3269 Feld[x][y] = EL_LIGHT_SWITCH;
3270 DrawLevelField(x, y);
3272 else if (element == EL_INVISIBLE_STEELWALL ||
3273 element == EL_INVISIBLE_WALL ||
3274 element == EL_INVISIBLE_SAND)
3276 if (game.light_time_left > 0)
3277 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3279 DrawLevelField(x, y);
3281 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3282 element == EL_INVISIBLE_WALL_ACTIVE ||
3283 element == EL_INVISIBLE_SAND_ACTIVE)
3285 if (game.light_time_left == 0)
3286 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3288 DrawLevelField(x, y);
3294 static void ToggleLightSwitch(int x, int y)
3296 int element = Feld[x][y];
3298 game.light_time_left =
3299 (element == EL_LIGHT_SWITCH ?
3300 level.time_light * FRAMES_PER_SECOND : 0);
3302 RedrawAllLightSwitchesAndInvisibleElements();
3305 static void ActivateTimegateSwitch(int x, int y)
3309 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3311 for (yy = 0; yy < lev_fieldy; yy++)
3313 for (xx = 0; xx < lev_fieldx; xx++)
3315 int element = Feld[xx][yy];
3317 if (element == EL_TIMEGATE_CLOSED ||
3318 element == EL_TIMEGATE_CLOSING)
3320 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3321 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3325 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3327 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3328 DrawLevelField(xx, yy);
3335 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3338 inline static int getElementMoveStepsize(int x, int y)
3340 int element = Feld[x][y];
3341 int direction = MovDir[x][y];
3342 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3343 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3344 int horiz_move = (dx != 0);
3345 int sign = (horiz_move ? dx : dy);
3346 int step = sign * element_info[element].move_stepsize;
3348 /* special values for move stepsize for spring and things on conveyor belt */
3351 if (CAN_FALL(element) &&
3352 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3353 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3354 else if (element == EL_SPRING)
3355 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3361 void Impact(int x, int y)
3363 boolean lastline = (y == lev_fieldy-1);
3364 boolean object_hit = FALSE;
3365 boolean impact = (lastline || object_hit);
3366 int element = Feld[x][y];
3367 int smashed = EL_UNDEFINED;
3369 if (!lastline) /* check if element below was hit */
3371 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3374 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3375 MovDir[x][y + 1] != MV_DOWN ||
3376 MovPos[x][y + 1] <= TILEY / 2));
3379 object_hit = !IS_FREE(x, y + 1);
3382 /* do not smash moving elements that left the smashed field in time */
3383 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3384 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3388 smashed = MovingOrBlocked2Element(x, y + 1);
3390 impact = (lastline || object_hit);
3393 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3399 /* only reset graphic animation if graphic really changes after impact */
3401 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3403 ResetGfxAnimation(x, y);
3404 DrawLevelField(x, y);
3407 if (impact && CAN_EXPLODE_IMPACT(element))
3412 else if (impact && element == EL_PEARL)
3414 Feld[x][y] = EL_PEARL_BREAKING;
3415 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3418 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3420 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3425 if (impact && element == EL_AMOEBA_DROP)
3427 if (object_hit && IS_PLAYER(x, y + 1))
3428 KillHeroUnlessEnemyProtected(x, y + 1);
3429 else if (object_hit && smashed == EL_PENGUIN)
3433 Feld[x][y] = EL_AMOEBA_GROWING;
3434 Store[x][y] = EL_AMOEBA_WET;
3436 ResetRandomAnimationValue(x, y);
3441 if (object_hit) /* check which object was hit */
3443 if (CAN_PASS_MAGIC_WALL(element) &&
3444 (smashed == EL_MAGIC_WALL ||
3445 smashed == EL_BD_MAGIC_WALL))
3448 int activated_magic_wall =
3449 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3450 EL_BD_MAGIC_WALL_ACTIVE);
3452 /* activate magic wall / mill */
3453 for (yy = 0; yy < lev_fieldy; yy++)
3454 for (xx = 0; xx < lev_fieldx; xx++)
3455 if (Feld[xx][yy] == smashed)
3456 Feld[xx][yy] = activated_magic_wall;
3458 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3459 game.magic_wall_active = TRUE;
3461 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3462 SND_MAGIC_WALL_ACTIVATING :
3463 SND_BD_MAGIC_WALL_ACTIVATING));
3466 if (IS_PLAYER(x, y + 1))
3468 if (CAN_SMASH_PLAYER(element))
3470 KillHeroUnlessEnemyProtected(x, y + 1);
3474 else if (smashed == EL_PENGUIN)
3476 if (CAN_SMASH_PLAYER(element))
3482 else if (element == EL_BD_DIAMOND)
3484 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3490 else if (((element == EL_SP_INFOTRON ||
3491 element == EL_SP_ZONK) &&
3492 (smashed == EL_SP_SNIKSNAK ||
3493 smashed == EL_SP_ELECTRON ||
3494 smashed == EL_SP_DISK_ORANGE)) ||
3495 (element == EL_SP_INFOTRON &&
3496 smashed == EL_SP_DISK_YELLOW))
3502 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3508 else if (CAN_SMASH_EVERYTHING(element))
3510 if (IS_CLASSIC_ENEMY(smashed) ||
3511 CAN_EXPLODE_SMASHED(smashed))
3516 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3518 if (smashed == EL_LAMP ||
3519 smashed == EL_LAMP_ACTIVE)
3524 else if (smashed == EL_NUT)
3526 Feld[x][y + 1] = EL_NUT_BREAKING;
3527 PlayLevelSound(x, y, SND_NUT_BREAKING);
3528 RaiseScoreElement(EL_NUT);
3531 else if (smashed == EL_PEARL)
3533 Feld[x][y + 1] = EL_PEARL_BREAKING;
3534 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3537 else if (smashed == EL_DIAMOND)
3539 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3540 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3543 else if (IS_BELT_SWITCH(smashed))
3545 ToggleBeltSwitch(x, y + 1);
3547 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3548 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3550 ToggleSwitchgateSwitch(x, y + 1);
3552 else if (smashed == EL_LIGHT_SWITCH ||
3553 smashed == EL_LIGHT_SWITCH_ACTIVE)
3555 ToggleLightSwitch(x, y + 1);
3559 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3561 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3562 CE_OTHER_IS_SWITCHING);
3563 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3569 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3574 /* play sound of magic wall / mill */
3576 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3577 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3579 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3580 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3581 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3582 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3587 /* play sound of object that hits the ground */
3588 if (lastline || object_hit)
3589 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3592 inline static void TurnRoundExt(int x, int y)
3604 { 0, 0 }, { 0, 0 }, { 0, 0 },
3609 int left, right, back;
3613 { MV_DOWN, MV_UP, MV_RIGHT },
3614 { MV_UP, MV_DOWN, MV_LEFT },
3616 { MV_LEFT, MV_RIGHT, MV_DOWN },
3620 { MV_RIGHT, MV_LEFT, MV_UP }
3623 int element = Feld[x][y];
3624 int move_pattern = element_info[element].move_pattern;
3626 int old_move_dir = MovDir[x][y];
3627 int left_dir = turn[old_move_dir].left;
3628 int right_dir = turn[old_move_dir].right;
3629 int back_dir = turn[old_move_dir].back;
3631 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3632 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3633 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3634 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3636 int left_x = x + left_dx, left_y = y + left_dy;
3637 int right_x = x + right_dx, right_y = y + right_dy;
3638 int move_x = x + move_dx, move_y = y + move_dy;
3642 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3644 TestIfBadThingTouchesOtherBadThing(x, y);
3646 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3647 MovDir[x][y] = right_dir;
3648 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3649 MovDir[x][y] = left_dir;
3651 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3653 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3656 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3657 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3659 TestIfBadThingTouchesOtherBadThing(x, y);
3661 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3662 MovDir[x][y] = left_dir;
3663 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3664 MovDir[x][y] = right_dir;
3666 if ((element == EL_SPACESHIP ||
3667 element == EL_SP_SNIKSNAK ||
3668 element == EL_SP_ELECTRON)
3669 && MovDir[x][y] != old_move_dir)
3671 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3674 else if (element == EL_YAMYAM)
3676 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3677 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3679 if (can_turn_left && can_turn_right)
3680 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3681 else if (can_turn_left)
3682 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3683 else if (can_turn_right)
3684 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3686 MovDir[x][y] = back_dir;
3688 MovDelay[x][y] = 16 + 16 * RND(3);
3690 else if (element == EL_DARK_YAMYAM)
3692 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3693 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3695 if (can_turn_left && can_turn_right)
3696 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3697 else if (can_turn_left)
3698 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3699 else if (can_turn_right)
3700 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3702 MovDir[x][y] = back_dir;
3704 MovDelay[x][y] = 16 + 16 * RND(3);
3706 else if (element == EL_PACMAN)
3708 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3709 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3711 if (can_turn_left && can_turn_right)
3712 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3713 else if (can_turn_left)
3714 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3715 else if (can_turn_right)
3716 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3718 MovDir[x][y] = back_dir;
3720 MovDelay[x][y] = 6 + RND(40);
3722 else if (element == EL_PIG)
3724 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3725 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3726 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3727 boolean should_turn_left, should_turn_right, should_move_on;
3729 int rnd = RND(rnd_value);
3731 should_turn_left = (can_turn_left &&
3733 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3734 y + back_dy + left_dy)));
3735 should_turn_right = (can_turn_right &&
3737 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3738 y + back_dy + right_dy)));
3739 should_move_on = (can_move_on &&
3742 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3743 y + move_dy + left_dy) ||
3744 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3745 y + move_dy + right_dy)));
3747 if (should_turn_left || should_turn_right || should_move_on)
3749 if (should_turn_left && should_turn_right && should_move_on)
3750 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3751 rnd < 2 * rnd_value / 3 ? right_dir :
3753 else if (should_turn_left && should_turn_right)
3754 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3755 else if (should_turn_left && should_move_on)
3756 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3757 else if (should_turn_right && should_move_on)
3758 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3759 else if (should_turn_left)
3760 MovDir[x][y] = left_dir;
3761 else if (should_turn_right)
3762 MovDir[x][y] = right_dir;
3763 else if (should_move_on)
3764 MovDir[x][y] = old_move_dir;
3766 else if (can_move_on && rnd > rnd_value / 8)
3767 MovDir[x][y] = old_move_dir;
3768 else if (can_turn_left && can_turn_right)
3769 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3770 else if (can_turn_left && rnd > rnd_value / 8)
3771 MovDir[x][y] = left_dir;
3772 else if (can_turn_right && rnd > rnd_value/8)
3773 MovDir[x][y] = right_dir;
3775 MovDir[x][y] = back_dir;
3777 xx = x + move_xy[MovDir[x][y]].x;
3778 yy = y + move_xy[MovDir[x][y]].y;
3780 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3781 MovDir[x][y] = old_move_dir;
3785 else if (element == EL_DRAGON)
3787 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3788 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3789 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3791 int rnd = RND(rnd_value);
3794 if (FrameCounter < 1 && x == 0 && y == 29)
3795 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3798 if (can_move_on && rnd > rnd_value / 8)
3799 MovDir[x][y] = old_move_dir;
3800 else if (can_turn_left && can_turn_right)
3801 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3802 else if (can_turn_left && rnd > rnd_value / 8)
3803 MovDir[x][y] = left_dir;
3804 else if (can_turn_right && rnd > rnd_value / 8)
3805 MovDir[x][y] = right_dir;
3807 MovDir[x][y] = back_dir;
3809 xx = x + move_xy[MovDir[x][y]].x;
3810 yy = y + move_xy[MovDir[x][y]].y;
3813 if (FrameCounter < 1 && x == 0 && y == 29)
3814 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3815 xx, yy, Feld[xx][yy],
3820 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3821 MovDir[x][y] = old_move_dir;
3823 if (!IS_FREE(xx, yy))
3824 MovDir[x][y] = old_move_dir;
3828 if (FrameCounter < 1 && x == 0 && y == 29)
3829 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3834 else if (element == EL_MOLE)
3836 boolean can_move_on =
3837 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3838 IS_AMOEBOID(Feld[move_x][move_y]) ||
3839 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3842 boolean can_turn_left =
3843 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3844 IS_AMOEBOID(Feld[left_x][left_y])));
3846 boolean can_turn_right =
3847 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3848 IS_AMOEBOID(Feld[right_x][right_y])));
3850 if (can_turn_left && can_turn_right)
3851 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3852 else if (can_turn_left)
3853 MovDir[x][y] = left_dir;
3855 MovDir[x][y] = right_dir;
3858 if (MovDir[x][y] != old_move_dir)
3861 else if (element == EL_BALLOON)
3863 MovDir[x][y] = game.balloon_dir;
3866 else if (element == EL_SPRING)
3868 if (MovDir[x][y] & MV_HORIZONTAL &&
3869 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3870 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3871 MovDir[x][y] = MV_NO_MOVING;
3875 else if (element == EL_ROBOT ||
3876 element == EL_SATELLITE ||
3877 element == EL_PENGUIN)
3879 int attr_x = -1, attr_y = -1;
3890 for (i = 0; i < MAX_PLAYERS; i++)
3892 struct PlayerInfo *player = &stored_player[i];
3893 int jx = player->jx, jy = player->jy;
3895 if (!player->active)
3899 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3907 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3913 if (element == EL_PENGUIN)
3916 static int xy[4][2] =
3924 for (i = 0; i < 4; i++)
3926 int ex = x + xy[i % 4][0];
3927 int ey = y + xy[i % 4][1];
3929 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3938 MovDir[x][y] = MV_NO_MOVING;
3940 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3941 else if (attr_x > x)
3942 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3944 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3945 else if (attr_y > y)
3946 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3948 if (element == EL_ROBOT)
3952 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3953 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3954 Moving2Blocked(x, y, &newx, &newy);
3956 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3957 MovDelay[x][y] = 8 + 8 * !RND(3);
3959 MovDelay[x][y] = 16;
3961 else if (element == EL_PENGUIN)
3967 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3969 boolean first_horiz = RND(2);
3970 int new_move_dir = MovDir[x][y];
3973 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3974 Moving2Blocked(x, y, &newx, &newy);
3976 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3980 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3981 Moving2Blocked(x, y, &newx, &newy);
3983 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3986 MovDir[x][y] = old_move_dir;
3990 else /* (element == EL_SATELLITE) */
3996 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3998 boolean first_horiz = RND(2);
3999 int new_move_dir = MovDir[x][y];
4002 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4003 Moving2Blocked(x, y, &newx, &newy);
4005 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4009 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4010 Moving2Blocked(x, y, &newx, &newy);
4012 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4015 MovDir[x][y] = old_move_dir;
4020 else if (move_pattern == MV_TURNING_LEFT ||
4021 move_pattern == MV_TURNING_RIGHT ||
4022 move_pattern == MV_TURNING_LEFT_RIGHT ||
4023 move_pattern == MV_TURNING_RIGHT_LEFT ||
4024 move_pattern == MV_TURNING_RANDOM ||
4025 move_pattern == MV_ALL_DIRECTIONS)
4027 boolean can_turn_left =
4028 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4029 boolean can_turn_right =
4030 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4032 if (move_pattern == MV_TURNING_LEFT)
4033 MovDir[x][y] = left_dir;
4034 else if (move_pattern == MV_TURNING_RIGHT)
4035 MovDir[x][y] = right_dir;
4036 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4037 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4038 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4039 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4040 else if (move_pattern == MV_TURNING_RANDOM)
4041 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4042 can_turn_right && !can_turn_left ? right_dir :
4043 RND(2) ? left_dir : right_dir);
4044 else if (can_turn_left && can_turn_right)
4045 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4046 else if (can_turn_left)
4047 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4048 else if (can_turn_right)
4049 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4051 MovDir[x][y] = back_dir;
4053 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4055 else if (move_pattern == MV_HORIZONTAL ||
4056 move_pattern == MV_VERTICAL)
4058 if (move_pattern & old_move_dir)
4059 MovDir[x][y] = back_dir;
4060 else if (move_pattern == MV_HORIZONTAL)
4061 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4062 else if (move_pattern == MV_VERTICAL)
4063 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4065 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4067 else if (move_pattern & MV_ANY_DIRECTION)
4069 MovDir[x][y] = move_pattern;
4070 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4072 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4074 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4075 MovDir[x][y] = left_dir;
4076 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4077 MovDir[x][y] = right_dir;
4079 if (MovDir[x][y] != old_move_dir)
4080 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4082 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4084 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4085 MovDir[x][y] = right_dir;
4086 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4087 MovDir[x][y] = left_dir;
4089 if (MovDir[x][y] != old_move_dir)
4090 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4092 else if (move_pattern == MV_TOWARDS_PLAYER ||
4093 move_pattern == MV_AWAY_FROM_PLAYER)
4095 int attr_x = -1, attr_y = -1;
4097 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4108 for (i = 0; i < MAX_PLAYERS; i++)
4110 struct PlayerInfo *player = &stored_player[i];
4111 int jx = player->jx, jy = player->jy;
4113 if (!player->active)
4117 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4125 MovDir[x][y] = MV_NO_MOVING;
4127 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4128 else if (attr_x > x)
4129 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4131 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4132 else if (attr_y > y)
4133 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4135 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4137 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4139 boolean first_horiz = RND(2);
4140 int new_move_dir = MovDir[x][y];
4143 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4144 Moving2Blocked(x, y, &newx, &newy);
4146 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4150 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4151 Moving2Blocked(x, y, &newx, &newy);
4153 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4156 MovDir[x][y] = old_move_dir;
4159 else if (move_pattern == MV_WHEN_PUSHED ||
4160 move_pattern == MV_WHEN_DROPPED)
4162 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4163 MovDir[x][y] = MV_NO_MOVING;
4167 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4169 static int test_xy[7][2] =
4179 static int test_dir[7] =
4189 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4190 int move_preference = -1000000; /* start with very low preference */
4191 int new_move_dir = MV_NO_MOVING;
4192 int start_test = RND(4);
4195 for (i = 0; i < 4; i++)
4197 int move_dir = test_dir[start_test + i];
4198 int move_dir_preference;
4200 xx = x + test_xy[start_test + i][0];
4201 yy = y + test_xy[start_test + i][1];
4203 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4204 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4206 new_move_dir = move_dir;
4211 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4214 move_dir_preference = -1 * RunnerVisit[xx][yy];
4215 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4216 move_dir_preference = PlayerVisit[xx][yy];
4218 if (move_dir_preference > move_preference)
4220 /* prefer field that has not been visited for the longest time */
4221 move_preference = move_dir_preference;
4222 new_move_dir = move_dir;
4224 else if (move_dir_preference == move_preference &&
4225 move_dir == old_move_dir)
4227 /* prefer last direction when all directions are preferred equally */
4228 move_preference = move_dir_preference;
4229 new_move_dir = move_dir;
4233 MovDir[x][y] = new_move_dir;
4234 if (old_move_dir != new_move_dir)
4239 static void TurnRound(int x, int y)
4241 int direction = MovDir[x][y];
4244 GfxDir[x][y] = MovDir[x][y];
4250 GfxDir[x][y] = MovDir[x][y];
4253 if (direction != MovDir[x][y])
4258 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4261 GfxAction[x][y] = ACTION_WAITING;
4265 static boolean JustBeingPushed(int x, int y)
4269 for (i = 0; i < MAX_PLAYERS; i++)
4271 struct PlayerInfo *player = &stored_player[i];
4273 if (player->active && player->is_pushing && player->MovPos)
4275 int next_jx = player->jx + (player->jx - player->last_jx);
4276 int next_jy = player->jy + (player->jy - player->last_jy);
4278 if (x == next_jx && y == next_jy)
4286 void StartMoving(int x, int y)
4288 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4289 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4290 int element = Feld[x][y];
4296 if (MovDelay[x][y] == 0)
4297 GfxAction[x][y] = ACTION_DEFAULT;
4299 /* !!! this should be handled more generic (not only for mole) !!! */
4300 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4301 GfxAction[x][y] = ACTION_DEFAULT;
4304 if (CAN_FALL(element) && y < lev_fieldy - 1)
4306 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4307 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4308 if (JustBeingPushed(x, y))
4311 if (element == EL_QUICKSAND_FULL)
4313 if (IS_FREE(x, y + 1))
4315 InitMovingField(x, y, MV_DOWN);
4316 started_moving = TRUE;
4318 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4319 Store[x][y] = EL_ROCK;
4321 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4323 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4326 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4328 if (!MovDelay[x][y])
4329 MovDelay[x][y] = TILEY + 1;
4338 Feld[x][y] = EL_QUICKSAND_EMPTY;
4339 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4340 Store[x][y + 1] = Store[x][y];
4343 PlayLevelSoundAction(x, y, ACTION_FILLING);
4345 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4349 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4350 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4352 InitMovingField(x, y, MV_DOWN);
4353 started_moving = TRUE;
4355 Feld[x][y] = EL_QUICKSAND_FILLING;
4356 Store[x][y] = element;
4358 PlayLevelSoundAction(x, y, ACTION_FILLING);
4360 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4363 else if (element == EL_MAGIC_WALL_FULL)
4365 if (IS_FREE(x, y + 1))
4367 InitMovingField(x, y, MV_DOWN);
4368 started_moving = TRUE;
4370 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4371 Store[x][y] = EL_CHANGED(Store[x][y]);
4373 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4375 if (!MovDelay[x][y])
4376 MovDelay[x][y] = TILEY/4 + 1;
4385 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4386 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4387 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4391 else if (element == EL_BD_MAGIC_WALL_FULL)
4393 if (IS_FREE(x, y + 1))
4395 InitMovingField(x, y, MV_DOWN);
4396 started_moving = TRUE;
4398 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4399 Store[x][y] = EL_CHANGED2(Store[x][y]);
4401 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4403 if (!MovDelay[x][y])
4404 MovDelay[x][y] = TILEY/4 + 1;
4413 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4414 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4415 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4419 else if (CAN_PASS_MAGIC_WALL(element) &&
4420 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4421 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4423 InitMovingField(x, y, MV_DOWN);
4424 started_moving = TRUE;
4427 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4428 EL_BD_MAGIC_WALL_FILLING);
4429 Store[x][y] = element;
4432 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4434 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4439 InitMovingField(x, y, MV_DOWN);
4440 started_moving = TRUE;
4442 Store[x][y] = EL_ACID;
4444 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4445 GfxAction[x][y + 1] = ACTION_ACTIVE;
4449 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4450 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4451 (Feld[x][y + 1] == EL_BLOCKED)) ||
4452 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4453 CAN_SMASH(element) && WasJustFalling[x][y] &&
4454 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4458 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4459 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4460 WasJustMoving[x][y] && !Pushed[x][y + 1])
4462 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4463 WasJustMoving[x][y])
4468 /* this is needed for a special case not covered by calling "Impact()"
4469 from "ContinueMoving()": if an element moves to a tile directly below
4470 another element which was just falling on that tile (which was empty
4471 in the previous frame), the falling element above would just stop
4472 instead of smashing the element below (in previous version, the above
4473 element was just checked for "moving" instead of "falling", resulting
4474 in incorrect smashes caused by horizontal movement of the above
4475 element; also, the case of the player being the element to smash was
4476 simply not covered here... :-/ ) */
4479 WasJustMoving[x][y] = 0;
4480 WasJustFalling[x][y] = 0;
4485 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4487 if (MovDir[x][y] == MV_NO_MOVING)
4489 InitMovingField(x, y, MV_DOWN);
4490 started_moving = TRUE;
4493 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4495 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4496 MovDir[x][y] = MV_DOWN;
4498 InitMovingField(x, y, MV_DOWN);
4499 started_moving = TRUE;
4501 else if (element == EL_AMOEBA_DROP)
4503 Feld[x][y] = EL_AMOEBA_GROWING;
4504 Store[x][y] = EL_AMOEBA_WET;
4506 /* Store[x][y + 1] must be zero, because:
4507 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4510 #if OLD_GAME_BEHAVIOUR
4511 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4513 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4514 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4515 element != EL_DX_SUPABOMB)
4518 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4519 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4520 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4521 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4524 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4525 (IS_FREE(x - 1, y + 1) ||
4526 Feld[x - 1][y + 1] == EL_ACID));
4527 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4528 (IS_FREE(x + 1, y + 1) ||
4529 Feld[x + 1][y + 1] == EL_ACID));
4530 boolean can_fall_any = (can_fall_left || can_fall_right);
4531 boolean can_fall_both = (can_fall_left && can_fall_right);
4533 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4535 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4537 if (slippery_type == SLIPPERY_ONLY_LEFT)
4538 can_fall_right = FALSE;
4539 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4540 can_fall_left = FALSE;
4541 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4542 can_fall_right = FALSE;
4543 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4544 can_fall_left = FALSE;
4546 can_fall_any = (can_fall_left || can_fall_right);
4547 can_fall_both = (can_fall_left && can_fall_right);
4552 if (can_fall_both &&
4553 (game.emulation != EMU_BOULDERDASH &&
4554 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4555 can_fall_left = !(can_fall_right = RND(2));
4557 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4558 started_moving = TRUE;
4561 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4563 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4564 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4565 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4566 int belt_dir = game.belt_dir[belt_nr];
4568 if ((belt_dir == MV_LEFT && left_is_free) ||
4569 (belt_dir == MV_RIGHT && right_is_free))
4571 InitMovingField(x, y, belt_dir);
4572 started_moving = TRUE;
4574 GfxAction[x][y] = ACTION_DEFAULT;
4579 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4580 if (CAN_MOVE(element) && !started_moving)
4582 int move_pattern = element_info[element].move_pattern;
4585 Moving2Blocked(x, y, &newx, &newy);
4588 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4591 if ((element == EL_SATELLITE ||
4592 element == EL_BALLOON ||
4593 element == EL_SPRING)
4594 && JustBeingPushed(x, y))
4599 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4600 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4601 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4604 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4605 element, element_info[element].token_name,
4606 WasJustMoving[x][y],
4607 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4608 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4609 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4610 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4614 WasJustMoving[x][y] = 0;
4617 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4620 if (Feld[x][y] != element) /* element has changed */
4622 element = Feld[x][y];
4623 move_pattern = element_info[element].move_pattern;
4625 if (!CAN_MOVE(element))
4629 if (Feld[x][y] != element) /* element has changed */
4637 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4638 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4640 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4642 Moving2Blocked(x, y, &newx, &newy);
4643 if (Feld[newx][newy] == EL_BLOCKED)
4644 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4650 if (FrameCounter < 1 && x == 0 && y == 29)
4651 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4654 if (!MovDelay[x][y]) /* start new movement phase */
4656 /* all objects that can change their move direction after each step
4657 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4659 if (element != EL_YAMYAM &&
4660 element != EL_DARK_YAMYAM &&
4661 element != EL_PACMAN &&
4662 !(move_pattern & MV_ANY_DIRECTION) &&
4663 move_pattern != MV_TURNING_LEFT &&
4664 move_pattern != MV_TURNING_RIGHT &&
4665 move_pattern != MV_TURNING_LEFT_RIGHT &&
4666 move_pattern != MV_TURNING_RIGHT_LEFT &&
4667 move_pattern != MV_TURNING_RANDOM)
4672 if (FrameCounter < 1 && x == 0 && y == 29)
4673 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4676 if (MovDelay[x][y] && (element == EL_BUG ||
4677 element == EL_SPACESHIP ||
4678 element == EL_SP_SNIKSNAK ||
4679 element == EL_SP_ELECTRON ||
4680 element == EL_MOLE))
4681 DrawLevelField(x, y);
4685 if (MovDelay[x][y]) /* wait some time before next movement */
4690 if (element == EL_YAMYAM)
4693 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4694 DrawLevelElementAnimation(x, y, element);
4698 if (MovDelay[x][y]) /* element still has to wait some time */
4701 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4702 ResetGfxAnimation(x, y);
4706 if (GfxAction[x][y] != ACTION_WAITING)
4707 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4709 GfxAction[x][y] = ACTION_WAITING;
4713 if (element == EL_ROBOT ||
4715 element == EL_PACMAN ||
4717 element == EL_YAMYAM ||
4718 element == EL_DARK_YAMYAM)
4721 DrawLevelElementAnimation(x, y, element);
4723 DrawLevelElementAnimationIfNeeded(x, y, element);
4725 PlayLevelSoundAction(x, y, ACTION_WAITING);
4727 else if (element == EL_SP_ELECTRON)
4728 DrawLevelElementAnimationIfNeeded(x, y, element);
4729 else if (element == EL_DRAGON)
4732 int dir = MovDir[x][y];
4733 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4734 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4735 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4736 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4737 dir == MV_UP ? IMG_FLAMES_1_UP :
4738 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4739 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4742 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4745 GfxAction[x][y] = ACTION_ATTACKING;
4747 if (IS_PLAYER(x, y))
4748 DrawPlayerField(x, y);
4750 DrawLevelField(x, y);
4752 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4754 for (i = 1; i <= 3; i++)
4756 int xx = x + i * dx;
4757 int yy = y + i * dy;
4758 int sx = SCREENX(xx);
4759 int sy = SCREENY(yy);
4760 int flame_graphic = graphic + (i - 1);
4762 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4767 int flamed = MovingOrBlocked2Element(xx, yy);
4769 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4772 RemoveMovingField(xx, yy);
4774 Feld[xx][yy] = EL_FLAMES;
4775 if (IN_SCR_FIELD(sx, sy))
4777 DrawLevelFieldCrumbledSand(xx, yy);
4778 DrawGraphic(sx, sy, flame_graphic, frame);
4783 if (Feld[xx][yy] == EL_FLAMES)
4784 Feld[xx][yy] = EL_EMPTY;
4785 DrawLevelField(xx, yy);
4790 if (MovDelay[x][y]) /* element still has to wait some time */
4792 PlayLevelSoundAction(x, y, ACTION_WAITING);
4798 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4799 for all other elements GfxAction will be set by InitMovingField() */
4800 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4801 GfxAction[x][y] = ACTION_MOVING;
4805 /* now make next step */
4807 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4809 if (DONT_COLLIDE_WITH(element) &&
4810 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4811 !PLAYER_ENEMY_PROTECTED(newx, newy))
4814 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4818 /* player killed by element which is deadly when colliding with */
4820 KillHero(PLAYERINFO(newx, newy));
4825 else if ((element == EL_PENGUIN ||
4826 element == EL_ROBOT ||
4827 element == EL_SATELLITE ||
4828 element == EL_BALLOON ||
4829 IS_CUSTOM_ELEMENT(element)) &&
4830 IN_LEV_FIELD(newx, newy) &&
4831 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4834 Store[x][y] = EL_ACID;
4836 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4838 if (Feld[newx][newy] == EL_EXIT_OPEN)
4842 DrawLevelField(x, y);
4844 Feld[x][y] = EL_EMPTY;
4845 DrawLevelField(x, y);
4848 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4849 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4850 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4852 local_player->friends_still_needed--;
4853 if (!local_player->friends_still_needed &&
4854 !local_player->GameOver && AllPlayersGone)
4855 local_player->LevelSolved = local_player->GameOver = TRUE;
4859 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4861 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4862 DrawLevelField(newx, newy);
4864 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4866 else if (!IS_FREE(newx, newy))
4868 GfxAction[x][y] = ACTION_WAITING;
4870 if (IS_PLAYER(x, y))
4871 DrawPlayerField(x, y);
4873 DrawLevelField(x, y);
4878 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4880 if (IS_FOOD_PIG(Feld[newx][newy]))
4882 if (IS_MOVING(newx, newy))
4883 RemoveMovingField(newx, newy);
4886 Feld[newx][newy] = EL_EMPTY;
4887 DrawLevelField(newx, newy);
4890 PlayLevelSound(x, y, SND_PIG_DIGGING);
4892 else if (!IS_FREE(newx, newy))
4894 if (IS_PLAYER(x, y))
4895 DrawPlayerField(x, y);
4897 DrawLevelField(x, y);
4906 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4909 else if (IS_CUSTOM_ELEMENT(element) &&
4910 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4914 !IS_FREE(newx, newy)
4919 int new_element = Feld[newx][newy];
4922 printf("::: '%s' digs '%s' [%d]\n",
4923 element_info[element].token_name,
4924 element_info[Feld[newx][newy]].token_name,
4925 StorePlayer[newx][newy]);
4928 if (!IS_FREE(newx, newy))
4930 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4931 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4934 /* no element can dig solid indestructible elements */
4935 if (IS_INDESTRUCTIBLE(new_element) &&
4936 !IS_DIGGABLE(new_element) &&
4937 !IS_COLLECTIBLE(new_element))
4940 if (AmoebaNr[newx][newy] &&
4941 (new_element == EL_AMOEBA_FULL ||
4942 new_element == EL_BD_AMOEBA ||
4943 new_element == EL_AMOEBA_GROWING))
4945 AmoebaCnt[AmoebaNr[newx][newy]]--;
4946 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4949 if (IS_MOVING(newx, newy))
4950 RemoveMovingField(newx, newy);
4953 RemoveField(newx, newy);
4954 DrawLevelField(newx, newy);
4957 PlayLevelSoundAction(x, y, action);
4960 if (new_element == element_info[element].move_enter_element)
4961 element_info[element].can_leave_element = TRUE;
4963 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4965 RunnerVisit[x][y] = FrameCounter;
4966 PlayerVisit[x][y] /= 8; /* expire player visit path */
4972 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4974 if (!IS_FREE(newx, newy))
4976 if (IS_PLAYER(x, y))
4977 DrawPlayerField(x, y);
4979 DrawLevelField(x, y);
4985 boolean wanna_flame = !RND(10);
4986 int dx = newx - x, dy = newy - y;
4987 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4988 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4989 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4990 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4991 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4992 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4995 IS_CLASSIC_ENEMY(element1) ||
4996 IS_CLASSIC_ENEMY(element2)) &&
4997 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4998 element1 != EL_FLAMES && element2 != EL_FLAMES)
5001 ResetGfxAnimation(x, y);
5002 GfxAction[x][y] = ACTION_ATTACKING;
5005 if (IS_PLAYER(x, y))
5006 DrawPlayerField(x, y);
5008 DrawLevelField(x, y);
5010 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5012 MovDelay[x][y] = 50;
5014 Feld[newx][newy] = EL_FLAMES;
5015 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5016 Feld[newx1][newy1] = EL_FLAMES;
5017 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5018 Feld[newx2][newy2] = EL_FLAMES;
5024 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5025 Feld[newx][newy] == EL_DIAMOND)
5027 if (IS_MOVING(newx, newy))
5028 RemoveMovingField(newx, newy);
5031 Feld[newx][newy] = EL_EMPTY;
5032 DrawLevelField(newx, newy);
5035 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5037 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5038 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5040 if (AmoebaNr[newx][newy])
5042 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5043 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5044 Feld[newx][newy] == EL_BD_AMOEBA)
5045 AmoebaCnt[AmoebaNr[newx][newy]]--;
5048 if (IS_MOVING(newx, newy))
5049 RemoveMovingField(newx, newy);
5052 Feld[newx][newy] = EL_EMPTY;
5053 DrawLevelField(newx, newy);
5056 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5058 else if ((element == EL_PACMAN || element == EL_MOLE)
5059 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5061 if (AmoebaNr[newx][newy])
5063 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5064 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5065 Feld[newx][newy] == EL_BD_AMOEBA)
5066 AmoebaCnt[AmoebaNr[newx][newy]]--;
5069 if (element == EL_MOLE)
5071 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5072 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5074 ResetGfxAnimation(x, y);
5075 GfxAction[x][y] = ACTION_DIGGING;
5076 DrawLevelField(x, y);
5078 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5080 return; /* wait for shrinking amoeba */
5082 else /* element == EL_PACMAN */
5084 Feld[newx][newy] = EL_EMPTY;
5085 DrawLevelField(newx, newy);
5086 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5089 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5090 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5091 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5093 /* wait for shrinking amoeba to completely disappear */
5096 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5098 /* object was running against a wall */
5103 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5104 DrawLevelElementAnimation(x, y, element);
5106 if (element == EL_BUG ||
5107 element == EL_SPACESHIP ||
5108 element == EL_SP_SNIKSNAK)
5109 DrawLevelField(x, y);
5110 else if (element == EL_MOLE)
5111 DrawLevelField(x, y);
5112 else if (element == EL_BD_BUTTERFLY ||
5113 element == EL_BD_FIREFLY)
5114 DrawLevelElementAnimationIfNeeded(x, y, element);
5115 else if (element == EL_SATELLITE)
5116 DrawLevelElementAnimationIfNeeded(x, y, element);
5117 else if (element == EL_SP_ELECTRON)
5118 DrawLevelElementAnimationIfNeeded(x, y, element);
5121 if (DONT_TOUCH(element))
5122 TestIfBadThingTouchesHero(x, y);
5125 PlayLevelSoundAction(x, y, ACTION_WAITING);
5131 InitMovingField(x, y, MovDir[x][y]);
5133 PlayLevelSoundAction(x, y, ACTION_MOVING);
5137 ContinueMoving(x, y);
5140 void ContinueMoving(int x, int y)
5142 int element = Feld[x][y];
5143 struct ElementInfo *ei = &element_info[element];
5144 int direction = MovDir[x][y];
5145 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5146 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5147 int newx = x + dx, newy = y + dy;
5149 int nextx = newx + dx, nexty = newy + dy;
5151 boolean pushed = Pushed[x][y];
5153 MovPos[x][y] += getElementMoveStepsize(x, y);
5155 if (pushed) /* special case: moving object pushed by player */
5156 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5158 if (ABS(MovPos[x][y]) < TILEX)
5160 DrawLevelField(x, y);
5162 return; /* element is still moving */
5165 /* element reached destination field */
5167 Feld[x][y] = EL_EMPTY;
5168 Feld[newx][newy] = element;
5169 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5171 if (element == EL_MOLE)
5173 Feld[x][y] = EL_SAND;
5175 DrawLevelFieldCrumbledSandNeighbours(x, y);
5177 else if (element == EL_QUICKSAND_FILLING)
5179 element = Feld[newx][newy] = get_next_element(element);
5180 Store[newx][newy] = Store[x][y];
5182 else if (element == EL_QUICKSAND_EMPTYING)
5184 Feld[x][y] = get_next_element(element);
5185 element = Feld[newx][newy] = Store[x][y];
5187 else if (element == EL_MAGIC_WALL_FILLING)
5189 element = Feld[newx][newy] = get_next_element(element);
5190 if (!game.magic_wall_active)
5191 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5192 Store[newx][newy] = Store[x][y];
5194 else if (element == EL_MAGIC_WALL_EMPTYING)
5196 Feld[x][y] = get_next_element(element);
5197 if (!game.magic_wall_active)
5198 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5199 element = Feld[newx][newy] = Store[x][y];
5201 else if (element == EL_BD_MAGIC_WALL_FILLING)
5203 element = Feld[newx][newy] = get_next_element(element);
5204 if (!game.magic_wall_active)
5205 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5206 Store[newx][newy] = Store[x][y];
5208 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5210 Feld[x][y] = get_next_element(element);
5211 if (!game.magic_wall_active)
5212 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5213 element = Feld[newx][newy] = Store[x][y];
5215 else if (element == EL_AMOEBA_DROPPING)
5217 Feld[x][y] = get_next_element(element);
5218 element = Feld[newx][newy] = Store[x][y];
5220 else if (element == EL_SOKOBAN_OBJECT)
5223 Feld[x][y] = Back[x][y];
5225 if (Back[newx][newy])
5226 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5228 Back[x][y] = Back[newx][newy] = 0;
5230 else if (Store[x][y] == EL_ACID)
5232 element = Feld[newx][newy] = EL_ACID;
5236 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5237 MovDelay[newx][newy] = 0;
5239 /* copy element change control values to new field */
5240 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5241 ChangePage[newx][newy] = ChangePage[x][y];
5242 Changed[newx][newy] = Changed[x][y];
5243 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5245 ChangeDelay[x][y] = 0;
5246 ChangePage[x][y] = -1;
5247 Changed[x][y] = CE_BITMASK_DEFAULT;
5248 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5250 /* copy animation control values to new field */
5251 GfxFrame[newx][newy] = GfxFrame[x][y];
5252 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5253 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5254 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5256 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5258 ResetGfxAnimation(x, y); /* reset animation values for old field */
5261 /* some elements can leave other elements behind after moving */
5262 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5263 ei->move_leave_element != EL_EMPTY &&
5264 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5265 ei->can_leave_element_last))
5267 Feld[x][y] = ei->move_leave_element;
5268 InitField(x, y, FALSE);
5270 if (GFX_CRUMBLED(Feld[x][y]))
5271 DrawLevelFieldCrumbledSandNeighbours(x, y);
5274 ei->can_leave_element_last = ei->can_leave_element;
5275 ei->can_leave_element = FALSE;
5279 /* 2.1.1 (does not work correctly for spring) */
5280 if (!CAN_MOVE(element))
5281 MovDir[newx][newy] = 0;
5285 /* (does not work for falling objects that slide horizontally) */
5286 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5287 MovDir[newx][newy] = 0;
5290 if (!CAN_MOVE(element) ||
5291 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5292 MovDir[newx][newy] = 0;
5295 if (!CAN_MOVE(element) ||
5296 (CAN_FALL(element) && direction == MV_DOWN))
5297 GfxDir[x][y] = MovDir[newx][newy] = 0;
5302 DrawLevelField(x, y);
5303 DrawLevelField(newx, newy);
5305 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5307 /* prevent pushed element from moving on in pushed direction */
5308 if (pushed && CAN_MOVE(element) &&
5309 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5310 !(element_info[element].move_pattern & direction))
5311 TurnRound(newx, newy);
5313 if (!pushed) /* special case: moving object pushed by player */
5315 WasJustMoving[newx][newy] = 3;
5317 if (CAN_FALL(element) && direction == MV_DOWN)
5318 WasJustFalling[newx][newy] = 3;
5321 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5323 TestIfBadThingTouchesHero(newx, newy);
5324 TestIfBadThingTouchesFriend(newx, newy);
5326 if (!IS_CUSTOM_ELEMENT(element))
5327 TestIfBadThingTouchesOtherBadThing(newx, newy);
5329 else if (element == EL_PENGUIN)
5330 TestIfFriendTouchesBadThing(newx, newy);
5332 if (CAN_FALL(element) && direction == MV_DOWN &&
5333 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5337 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5341 if (ChangePage[newx][newy] != -1) /* delayed change */
5342 ChangeElement(newx, newy, ChangePage[newx][newy]);
5347 TestIfElementHitsCustomElement(newx, newy, direction);
5351 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5353 int hitting_element = Feld[newx][newy];
5355 /* !!! fix side (direction) orientation here and elsewhere !!! */
5356 CheckElementSideChange(newx, newy, hitting_element,
5357 direction, CE_HITTING_SOMETHING, -1);
5360 if (IN_LEV_FIELD(nextx, nexty))
5362 int opposite_direction = MV_DIR_OPPOSITE(direction);
5363 int hitting_side = direction;
5364 int touched_side = opposite_direction;
5365 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5366 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5367 MovDir[nextx][nexty] != direction ||
5368 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5374 CheckElementSideChange(nextx, nexty, touched_element,
5375 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5377 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5378 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5380 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5382 struct ElementChangeInfo *change =
5383 &element_info[hitting_element].change_page[i];
5385 if (change->can_change &&
5386 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5387 change->sides & touched_side &&
5388 change->trigger_element == touched_element)
5390 CheckElementSideChange(newx, newy, hitting_element,
5391 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5397 if (IS_CUSTOM_ELEMENT(touched_element) &&
5398 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5400 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5402 struct ElementChangeInfo *change =
5403 &element_info[touched_element].change_page[i];
5405 if (change->can_change &&
5406 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5407 change->sides & hitting_side &&
5408 change->trigger_element == hitting_element)
5410 CheckElementSideChange(nextx, nexty, touched_element,
5411 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5422 TestIfPlayerTouchesCustomElement(newx, newy);
5423 TestIfElementTouchesCustomElement(newx, newy);
5426 int AmoebeNachbarNr(int ax, int ay)
5429 int element = Feld[ax][ay];
5431 static int xy[4][2] =
5439 for (i = 0; i < 4; i++)
5441 int x = ax + xy[i][0];
5442 int y = ay + xy[i][1];
5444 if (!IN_LEV_FIELD(x, y))
5447 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5448 group_nr = AmoebaNr[x][y];
5454 void AmoebenVereinigen(int ax, int ay)
5456 int i, x, y, xx, yy;
5457 int new_group_nr = AmoebaNr[ax][ay];
5458 static int xy[4][2] =
5466 if (new_group_nr == 0)
5469 for (i = 0; i < 4; i++)
5474 if (!IN_LEV_FIELD(x, y))
5477 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5478 Feld[x][y] == EL_BD_AMOEBA ||
5479 Feld[x][y] == EL_AMOEBA_DEAD) &&
5480 AmoebaNr[x][y] != new_group_nr)
5482 int old_group_nr = AmoebaNr[x][y];
5484 if (old_group_nr == 0)
5487 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5488 AmoebaCnt[old_group_nr] = 0;
5489 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5490 AmoebaCnt2[old_group_nr] = 0;
5492 for (yy = 0; yy < lev_fieldy; yy++)
5494 for (xx = 0; xx < lev_fieldx; xx++)
5496 if (AmoebaNr[xx][yy] == old_group_nr)
5497 AmoebaNr[xx][yy] = new_group_nr;
5504 void AmoebeUmwandeln(int ax, int ay)
5508 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5510 int group_nr = AmoebaNr[ax][ay];
5515 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5516 printf("AmoebeUmwandeln(): This should never happen!\n");
5521 for (y = 0; y < lev_fieldy; y++)
5523 for (x = 0; x < lev_fieldx; x++)
5525 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5528 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5532 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5533 SND_AMOEBA_TURNING_TO_GEM :
5534 SND_AMOEBA_TURNING_TO_ROCK));
5539 static int xy[4][2] =
5547 for (i = 0; i < 4; i++)
5552 if (!IN_LEV_FIELD(x, y))
5555 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5557 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5558 SND_AMOEBA_TURNING_TO_GEM :
5559 SND_AMOEBA_TURNING_TO_ROCK));
5566 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5569 int group_nr = AmoebaNr[ax][ay];
5570 boolean done = FALSE;
5575 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5576 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5581 for (y = 0; y < lev_fieldy; y++)
5583 for (x = 0; x < lev_fieldx; x++)
5585 if (AmoebaNr[x][y] == group_nr &&
5586 (Feld[x][y] == EL_AMOEBA_DEAD ||
5587 Feld[x][y] == EL_BD_AMOEBA ||
5588 Feld[x][y] == EL_AMOEBA_GROWING))
5591 Feld[x][y] = new_element;
5592 InitField(x, y, FALSE);
5593 DrawLevelField(x, y);
5600 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5601 SND_BD_AMOEBA_TURNING_TO_ROCK :
5602 SND_BD_AMOEBA_TURNING_TO_GEM));
5605 void AmoebeWaechst(int x, int y)
5607 static unsigned long sound_delay = 0;
5608 static unsigned long sound_delay_value = 0;
5610 if (!MovDelay[x][y]) /* start new growing cycle */
5614 if (DelayReached(&sound_delay, sound_delay_value))
5617 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5619 if (Store[x][y] == EL_BD_AMOEBA)
5620 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5622 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5624 sound_delay_value = 30;
5628 if (MovDelay[x][y]) /* wait some time before growing bigger */
5631 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5633 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5634 6 - MovDelay[x][y]);
5636 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5639 if (!MovDelay[x][y])
5641 Feld[x][y] = Store[x][y];
5643 DrawLevelField(x, y);
5648 void AmoebaDisappearing(int x, int y)
5650 static unsigned long sound_delay = 0;
5651 static unsigned long sound_delay_value = 0;
5653 if (!MovDelay[x][y]) /* start new shrinking cycle */
5657 if (DelayReached(&sound_delay, sound_delay_value))
5658 sound_delay_value = 30;
5661 if (MovDelay[x][y]) /* wait some time before shrinking */
5664 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5666 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5667 6 - MovDelay[x][y]);
5669 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5672 if (!MovDelay[x][y])
5674 Feld[x][y] = EL_EMPTY;
5675 DrawLevelField(x, y);
5677 /* don't let mole enter this field in this cycle;
5678 (give priority to objects falling to this field from above) */
5684 void AmoebeAbleger(int ax, int ay)
5687 int element = Feld[ax][ay];
5688 int graphic = el2img(element);
5689 int newax = ax, neway = ay;
5690 static int xy[4][2] =
5698 if (!level.amoeba_speed)
5700 Feld[ax][ay] = EL_AMOEBA_DEAD;
5701 DrawLevelField(ax, ay);
5705 if (IS_ANIMATED(graphic))
5706 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5708 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5709 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5711 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5714 if (MovDelay[ax][ay])
5718 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5721 int x = ax + xy[start][0];
5722 int y = ay + xy[start][1];
5724 if (!IN_LEV_FIELD(x, y))
5727 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5728 if (IS_FREE(x, y) ||
5729 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5735 if (newax == ax && neway == ay)
5738 else /* normal or "filled" (BD style) amoeba */
5741 boolean waiting_for_player = FALSE;
5743 for (i = 0; i < 4; i++)
5745 int j = (start + i) % 4;
5746 int x = ax + xy[j][0];
5747 int y = ay + xy[j][1];
5749 if (!IN_LEV_FIELD(x, y))
5752 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5753 if (IS_FREE(x, y) ||
5754 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5760 else if (IS_PLAYER(x, y))
5761 waiting_for_player = TRUE;
5764 if (newax == ax && neway == ay) /* amoeba cannot grow */
5766 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5768 Feld[ax][ay] = EL_AMOEBA_DEAD;
5769 DrawLevelField(ax, ay);
5770 AmoebaCnt[AmoebaNr[ax][ay]]--;
5772 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5774 if (element == EL_AMOEBA_FULL)
5775 AmoebeUmwandeln(ax, ay);
5776 else if (element == EL_BD_AMOEBA)
5777 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5782 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5784 /* amoeba gets larger by growing in some direction */
5786 int new_group_nr = AmoebaNr[ax][ay];
5789 if (new_group_nr == 0)
5791 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5792 printf("AmoebeAbleger(): This should never happen!\n");
5797 AmoebaNr[newax][neway] = new_group_nr;
5798 AmoebaCnt[new_group_nr]++;
5799 AmoebaCnt2[new_group_nr]++;
5801 /* if amoeba touches other amoeba(s) after growing, unify them */
5802 AmoebenVereinigen(newax, neway);
5804 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5806 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5812 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5813 (neway == lev_fieldy - 1 && newax != ax))
5815 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5816 Store[newax][neway] = element;
5818 else if (neway == ay)
5820 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5822 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5824 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5829 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5830 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5831 Store[ax][ay] = EL_AMOEBA_DROP;
5832 ContinueMoving(ax, ay);
5836 DrawLevelField(newax, neway);
5839 void Life(int ax, int ay)
5842 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5844 int element = Feld[ax][ay];
5845 int graphic = el2img(element);
5846 boolean changed = FALSE;
5848 if (IS_ANIMATED(graphic))
5849 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5854 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5855 MovDelay[ax][ay] = life_time;
5857 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5860 if (MovDelay[ax][ay])
5864 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5866 int xx = ax+x1, yy = ay+y1;
5869 if (!IN_LEV_FIELD(xx, yy))
5872 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5874 int x = xx+x2, y = yy+y2;
5876 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5879 if (((Feld[x][y] == element ||
5880 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5882 (IS_FREE(x, y) && Stop[x][y]))
5886 if (xx == ax && yy == ay) /* field in the middle */
5888 if (nachbarn < life[0] || nachbarn > life[1])
5890 Feld[xx][yy] = EL_EMPTY;
5892 DrawLevelField(xx, yy);
5893 Stop[xx][yy] = TRUE;
5897 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5898 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5899 { /* free border field */
5900 if (nachbarn >= life[2] && nachbarn <= life[3])
5902 Feld[xx][yy] = element;
5903 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5905 DrawLevelField(xx, yy);
5906 Stop[xx][yy] = TRUE;
5913 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5914 SND_GAME_OF_LIFE_GROWING);
5917 static void InitRobotWheel(int x, int y)
5919 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5922 static void RunRobotWheel(int x, int y)
5924 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5927 static void StopRobotWheel(int x, int y)
5929 if (ZX == x && ZY == y)
5933 static void InitTimegateWheel(int x, int y)
5935 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5938 static void RunTimegateWheel(int x, int y)
5940 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5943 void CheckExit(int x, int y)
5945 if (local_player->gems_still_needed > 0 ||
5946 local_player->sokobanfields_still_needed > 0 ||
5947 local_player->lights_still_needed > 0)
5949 int element = Feld[x][y];
5950 int graphic = el2img(element);
5952 if (IS_ANIMATED(graphic))
5953 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5958 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5961 Feld[x][y] = EL_EXIT_OPENING;
5963 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5966 void CheckExitSP(int x, int y)
5968 if (local_player->gems_still_needed > 0)
5970 int element = Feld[x][y];
5971 int graphic = el2img(element);
5973 if (IS_ANIMATED(graphic))
5974 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5979 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5982 Feld[x][y] = EL_SP_EXIT_OPENING;
5984 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5987 static void CloseAllOpenTimegates()
5991 for (y = 0; y < lev_fieldy; y++)
5993 for (x = 0; x < lev_fieldx; x++)
5995 int element = Feld[x][y];
5997 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5999 Feld[x][y] = EL_TIMEGATE_CLOSING;
6001 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6003 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6010 void EdelsteinFunkeln(int x, int y)
6012 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6015 if (Feld[x][y] == EL_BD_DIAMOND)
6018 if (MovDelay[x][y] == 0) /* next animation frame */
6019 MovDelay[x][y] = 11 * !SimpleRND(500);
6021 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6025 if (setup.direct_draw && MovDelay[x][y])
6026 SetDrawtoField(DRAW_BUFFERED);
6028 DrawLevelElementAnimation(x, y, Feld[x][y]);
6030 if (MovDelay[x][y] != 0)
6032 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6033 10 - MovDelay[x][y]);
6035 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6037 if (setup.direct_draw)
6041 dest_x = FX + SCREENX(x) * TILEX;
6042 dest_y = FY + SCREENY(y) * TILEY;
6044 BlitBitmap(drawto_field, window,
6045 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6046 SetDrawtoField(DRAW_DIRECT);
6052 void MauerWaechst(int x, int y)
6056 if (!MovDelay[x][y]) /* next animation frame */
6057 MovDelay[x][y] = 3 * delay;
6059 if (MovDelay[x][y]) /* wait some time before next frame */
6063 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6065 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6066 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6068 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6071 if (!MovDelay[x][y])
6073 if (MovDir[x][y] == MV_LEFT)
6075 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6076 DrawLevelField(x - 1, y);
6078 else if (MovDir[x][y] == MV_RIGHT)
6080 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6081 DrawLevelField(x + 1, y);
6083 else if (MovDir[x][y] == MV_UP)
6085 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6086 DrawLevelField(x, y - 1);
6090 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6091 DrawLevelField(x, y + 1);
6094 Feld[x][y] = Store[x][y];
6096 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6097 DrawLevelField(x, y);
6102 void MauerAbleger(int ax, int ay)
6104 int element = Feld[ax][ay];
6105 int graphic = el2img(element);
6106 boolean oben_frei = FALSE, unten_frei = FALSE;
6107 boolean links_frei = FALSE, rechts_frei = FALSE;
6108 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6109 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6110 boolean new_wall = FALSE;
6112 if (IS_ANIMATED(graphic))
6113 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6115 if (!MovDelay[ax][ay]) /* start building new wall */
6116 MovDelay[ax][ay] = 6;
6118 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6121 if (MovDelay[ax][ay])
6125 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6127 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6129 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6131 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6134 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6135 element == EL_EXPANDABLE_WALL_ANY)
6139 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6140 Store[ax][ay-1] = element;
6141 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6142 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6143 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6144 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6149 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6150 Store[ax][ay+1] = element;
6151 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6152 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6153 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6154 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6159 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6160 element == EL_EXPANDABLE_WALL_ANY ||
6161 element == EL_EXPANDABLE_WALL)
6165 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6166 Store[ax-1][ay] = element;
6167 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6168 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6169 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6170 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6176 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6177 Store[ax+1][ay] = element;
6178 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6179 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6180 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6181 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6186 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6187 DrawLevelField(ax, ay);
6189 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6191 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6192 unten_massiv = TRUE;
6193 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6194 links_massiv = TRUE;
6195 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6196 rechts_massiv = TRUE;
6198 if (((oben_massiv && unten_massiv) ||
6199 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6200 element == EL_EXPANDABLE_WALL) &&
6201 ((links_massiv && rechts_massiv) ||
6202 element == EL_EXPANDABLE_WALL_VERTICAL))
6203 Feld[ax][ay] = EL_WALL;
6207 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6209 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6213 void CheckForDragon(int x, int y)
6216 boolean dragon_found = FALSE;
6217 static int xy[4][2] =
6225 for (i = 0; i < 4; i++)
6227 for (j = 0; j < 4; j++)
6229 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6231 if (IN_LEV_FIELD(xx, yy) &&
6232 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6234 if (Feld[xx][yy] == EL_DRAGON)
6235 dragon_found = TRUE;
6244 for (i = 0; i < 4; i++)
6246 for (j = 0; j < 3; j++)
6248 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6250 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6252 Feld[xx][yy] = EL_EMPTY;
6253 DrawLevelField(xx, yy);
6262 static void InitBuggyBase(int x, int y)
6264 int element = Feld[x][y];
6265 int activating_delay = FRAMES_PER_SECOND / 4;
6268 (element == EL_SP_BUGGY_BASE ?
6269 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6270 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6272 element == EL_SP_BUGGY_BASE_ACTIVE ?
6273 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6276 static void WarnBuggyBase(int x, int y)
6279 static int xy[4][2] =
6287 for (i = 0; i < 4; i++)
6289 int xx = x + xy[i][0], yy = y + xy[i][1];
6291 if (IS_PLAYER(xx, yy))
6293 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6300 static void InitTrap(int x, int y)
6302 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6305 static void ActivateTrap(int x, int y)
6307 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6310 static void ChangeActiveTrap(int x, int y)
6312 int graphic = IMG_TRAP_ACTIVE;
6314 /* if new animation frame was drawn, correct crumbled sand border */
6315 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6316 DrawLevelFieldCrumbledSand(x, y);
6319 static void ChangeElementNowExt(int x, int y, int target_element)
6321 int previous_move_direction = MovDir[x][y];
6323 /* check if element under player changes from accessible to unaccessible
6324 (needed for special case of dropping element which then changes) */
6325 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6326 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6333 Feld[x][y] = target_element;
6335 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6337 ResetGfxAnimation(x, y);
6338 ResetRandomAnimationValue(x, y);
6340 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6341 MovDir[x][y] = previous_move_direction;
6343 InitField(x, y, FALSE);
6344 if (CAN_MOVE(Feld[x][y]))
6347 DrawLevelField(x, y);
6349 if (GFX_CRUMBLED(Feld[x][y]))
6350 DrawLevelFieldCrumbledSandNeighbours(x, y);
6352 TestIfBadThingTouchesHero(x, y);
6353 TestIfPlayerTouchesCustomElement(x, y);
6354 TestIfElementTouchesCustomElement(x, y);
6356 if (ELEM_IS_PLAYER(target_element))
6357 RelocatePlayer(x, y, target_element);
6360 static boolean ChangeElementNow(int x, int y, int element, int page)
6362 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6364 /* always use default change event to prevent running into a loop */
6365 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6366 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6368 /* do not change already changed elements with same change event */
6370 if (Changed[x][y] & ChangeEvent[x][y])
6377 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6379 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6381 if (change->explode)
6388 if (change->use_content)
6390 boolean complete_change = TRUE;
6391 boolean can_change[3][3];
6394 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6396 boolean half_destructible;
6397 int ex = x + xx - 1;
6398 int ey = y + yy - 1;
6401 can_change[xx][yy] = TRUE;
6403 if (ex == x && ey == y) /* do not check changing element itself */
6406 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6408 can_change[xx][yy] = FALSE; /* do not change empty borders */
6413 if (!IN_LEV_FIELD(ex, ey))
6415 can_change[xx][yy] = FALSE;
6416 complete_change = FALSE;
6423 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6424 e = MovingOrBlocked2Element(ex, ey);
6426 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6428 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6429 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6430 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6432 can_change[xx][yy] = FALSE;
6433 complete_change = FALSE;
6437 if (!change->only_complete || complete_change)
6439 boolean something_has_changed = FALSE;
6441 if (change->only_complete && change->use_random_change &&
6442 RND(100) < change->random)
6445 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6447 int ex = x + xx - 1;
6448 int ey = y + yy - 1;
6450 if (can_change[xx][yy] && (!change->use_random_change ||
6451 RND(100) < change->random))
6453 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6454 RemoveMovingField(ex, ey);
6456 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6458 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6460 something_has_changed = TRUE;
6462 /* for symmetry reasons, freeze newly created border elements */
6463 if (ex != x || ey != y)
6464 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6468 if (something_has_changed)
6469 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6474 ChangeElementNowExt(x, y, change->target_element);
6476 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6482 static void ChangeElement(int x, int y, int page)
6484 int element = MovingOrBlocked2Element(x, y);
6485 struct ElementInfo *ei = &element_info[element];
6486 struct ElementChangeInfo *change = &ei->change_page[page];
6490 if (!CAN_CHANGE(element))
6493 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6494 x, y, element, element_info[element].token_name);
6495 printf("ChangeElement(): This should never happen!\n");
6501 if (ChangeDelay[x][y] == 0) /* initialize element change */
6503 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6504 RND(change->delay_random * change->delay_frames)) + 1;
6506 ResetGfxAnimation(x, y);
6507 ResetRandomAnimationValue(x, y);
6509 if (change->pre_change_function)
6510 change->pre_change_function(x, y);
6513 ChangeDelay[x][y]--;
6515 if (ChangeDelay[x][y] != 0) /* continue element change */
6517 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6519 if (IS_ANIMATED(graphic))
6520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6522 if (change->change_function)
6523 change->change_function(x, y);
6525 else /* finish element change */
6527 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6529 page = ChangePage[x][y];
6530 ChangePage[x][y] = -1;
6534 if (IS_MOVING(x, y) && !change->explode)
6536 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6539 ChangeDelay[x][y] = 1; /* try change after next move step */
6540 ChangePage[x][y] = page; /* remember page to use for change */
6545 if (ChangeElementNow(x, y, element, page))
6547 if (change->post_change_function)
6548 change->post_change_function(x, y);
6553 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6554 int trigger_element,
6560 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6563 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6565 int element = EL_CUSTOM_START + i;
6567 boolean change_element = FALSE;
6570 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6573 for (j = 0; j < element_info[element].num_change_pages; j++)
6575 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6577 if (change->can_change &&
6579 change->events & CH_EVENT_BIT(trigger_event) &&
6581 change->sides & trigger_side &&
6583 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6585 change->trigger_element == trigger_element
6590 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6591 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6592 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6595 change_element = TRUE;
6602 if (!change_element)
6605 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6608 if (x == lx && y == ly) /* do not change trigger element itself */
6612 if (Feld[x][y] == element)
6614 ChangeDelay[x][y] = 1;
6615 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6616 ChangeElement(x, y, page);
6624 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6627 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6631 static boolean CheckElementSideChange(int x, int y, int element, int side,
6632 int trigger_event, int page)
6634 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6637 if (Feld[x][y] == EL_BLOCKED)
6639 Blocked2Moving(x, y, &x, &y);
6640 element = Feld[x][y];
6644 page = element_info[element].event_page_nr[trigger_event];
6646 if (!(element_info[element].change_page[page].sides & side))
6649 ChangeDelay[x][y] = 1;
6650 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6651 ChangeElement(x, y, page);
6656 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6658 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6661 static void PlayPlayerSound(struct PlayerInfo *player)
6663 int jx = player->jx, jy = player->jy;
6664 int element = player->element_nr;
6665 int last_action = player->last_action_waiting;
6666 int action = player->action_waiting;
6668 if (player->is_waiting)
6670 if (action != last_action)
6671 PlayLevelSoundElementAction(jx, jy, element, action);
6673 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6677 if (action != last_action)
6678 StopSound(element_info[element].sound[last_action]);
6680 if (last_action == ACTION_SLEEPING)
6681 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6685 static void PlayAllPlayersSound()
6689 for (i = 0; i < MAX_PLAYERS; i++)
6690 if (stored_player[i].active)
6691 PlayPlayerSound(&stored_player[i]);
6694 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6696 boolean last_waiting = player->is_waiting;
6697 int move_dir = player->MovDir;
6699 player->last_action_waiting = player->action_waiting;
6703 if (!last_waiting) /* not waiting -> waiting */
6705 player->is_waiting = TRUE;
6707 player->frame_counter_bored =
6709 game.player_boring_delay_fixed +
6710 SimpleRND(game.player_boring_delay_random);
6711 player->frame_counter_sleeping =
6713 game.player_sleeping_delay_fixed +
6714 SimpleRND(game.player_sleeping_delay_random);
6716 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6719 if (game.player_sleeping_delay_fixed +
6720 game.player_sleeping_delay_random > 0 &&
6721 player->anim_delay_counter == 0 &&
6722 player->post_delay_counter == 0 &&
6723 FrameCounter >= player->frame_counter_sleeping)
6724 player->is_sleeping = TRUE;
6725 else if (game.player_boring_delay_fixed +
6726 game.player_boring_delay_random > 0 &&
6727 FrameCounter >= player->frame_counter_bored)
6728 player->is_bored = TRUE;
6730 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6731 player->is_bored ? ACTION_BORING :
6734 if (player->is_sleeping)
6736 if (player->num_special_action_sleeping > 0)
6738 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6740 int last_special_action = player->special_action_sleeping;
6741 int num_special_action = player->num_special_action_sleeping;
6742 int special_action =
6743 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6744 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6745 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6746 last_special_action + 1 : ACTION_SLEEPING);
6747 int special_graphic =
6748 el_act_dir2img(player->element_nr, special_action, move_dir);
6750 player->anim_delay_counter =
6751 graphic_info[special_graphic].anim_delay_fixed +
6752 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6753 player->post_delay_counter =
6754 graphic_info[special_graphic].post_delay_fixed +
6755 SimpleRND(graphic_info[special_graphic].post_delay_random);
6757 player->special_action_sleeping = special_action;
6760 if (player->anim_delay_counter > 0)
6762 player->action_waiting = player->special_action_sleeping;
6763 player->anim_delay_counter--;
6765 else if (player->post_delay_counter > 0)
6767 player->post_delay_counter--;
6771 else if (player->is_bored)
6773 if (player->num_special_action_bored > 0)
6775 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6777 int special_action =
6778 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6779 int special_graphic =
6780 el_act_dir2img(player->element_nr, special_action, move_dir);
6782 player->anim_delay_counter =
6783 graphic_info[special_graphic].anim_delay_fixed +
6784 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6785 player->post_delay_counter =
6786 graphic_info[special_graphic].post_delay_fixed +
6787 SimpleRND(graphic_info[special_graphic].post_delay_random);
6789 player->special_action_bored = special_action;
6792 if (player->anim_delay_counter > 0)
6794 player->action_waiting = player->special_action_bored;
6795 player->anim_delay_counter--;
6797 else if (player->post_delay_counter > 0)
6799 player->post_delay_counter--;
6804 else if (last_waiting) /* waiting -> not waiting */
6806 player->is_waiting = FALSE;
6807 player->is_bored = FALSE;
6808 player->is_sleeping = FALSE;
6810 player->frame_counter_bored = -1;
6811 player->frame_counter_sleeping = -1;
6813 player->anim_delay_counter = 0;
6814 player->post_delay_counter = 0;
6816 player->action_waiting = ACTION_DEFAULT;
6818 player->special_action_bored = ACTION_DEFAULT;
6819 player->special_action_sleeping = ACTION_DEFAULT;
6824 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6827 static byte stored_player_action[MAX_PLAYERS];
6828 static int num_stored_actions = 0;
6830 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6831 int left = player_action & JOY_LEFT;
6832 int right = player_action & JOY_RIGHT;
6833 int up = player_action & JOY_UP;
6834 int down = player_action & JOY_DOWN;
6835 int button1 = player_action & JOY_BUTTON_1;
6836 int button2 = player_action & JOY_BUTTON_2;
6837 int dx = (left ? -1 : right ? 1 : 0);
6838 int dy = (up ? -1 : down ? 1 : 0);
6841 stored_player_action[player->index_nr] = 0;
6842 num_stored_actions++;
6846 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6849 if (!player->active || tape.pausing)
6853 printf("::: [%d %d %d %d] [%d %d]\n",
6854 left, right, up, down, button1, button2);
6860 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6864 snapped = SnapField(player, dx, dy);
6868 dropped = DropElement(player);
6870 moved = MovePlayer(player, dx, dy);
6873 if (tape.single_step && tape.recording && !tape.pausing)
6875 if (button1 || (dropped && !moved))
6877 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6878 SnapField(player, 0, 0); /* stop snapping */
6882 SetPlayerWaiting(player, FALSE);
6885 return player_action;
6887 stored_player_action[player->index_nr] = player_action;
6893 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6896 /* no actions for this player (no input at player's configured device) */
6898 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6899 SnapField(player, 0, 0);
6900 CheckGravityMovement(player);
6902 if (player->MovPos == 0)
6903 SetPlayerWaiting(player, TRUE);
6905 if (player->MovPos == 0) /* needed for tape.playing */
6906 player->is_moving = FALSE;
6908 player->is_dropping = FALSE;
6914 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6916 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6918 TapeRecordAction(stored_player_action);
6919 num_stored_actions = 0;
6926 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6928 static byte stored_player_action[MAX_PLAYERS];
6929 static int num_stored_actions = 0;
6930 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6931 int left = player_action & JOY_LEFT;
6932 int right = player_action & JOY_RIGHT;
6933 int up = player_action & JOY_UP;
6934 int down = player_action & JOY_DOWN;
6935 int button1 = player_action & JOY_BUTTON_1;
6936 int button2 = player_action & JOY_BUTTON_2;
6937 int dx = (left ? -1 : right ? 1 : 0);
6938 int dy = (up ? -1 : down ? 1 : 0);
6940 stored_player_action[player->index_nr] = 0;
6941 num_stored_actions++;
6943 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6945 if (!player->active || tape.pausing)
6950 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6953 snapped = SnapField(player, dx, dy);
6957 dropped = DropElement(player);
6959 moved = MovePlayer(player, dx, dy);
6962 if (tape.single_step && tape.recording && !tape.pausing)
6964 if (button1 || (dropped && !moved))
6966 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6967 SnapField(player, 0, 0); /* stop snapping */
6971 stored_player_action[player->index_nr] = player_action;
6975 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6977 /* no actions for this player (no input at player's configured device) */
6979 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6980 SnapField(player, 0, 0);
6981 CheckGravityMovement(player);
6983 if (player->MovPos == 0)
6984 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6986 if (player->MovPos == 0) /* needed for tape.playing */
6987 player->is_moving = FALSE;
6990 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6992 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6994 TapeRecordAction(stored_player_action);
6995 num_stored_actions = 0;
7002 static unsigned long action_delay = 0;
7003 unsigned long action_delay_value;
7004 int magic_wall_x = 0, magic_wall_y = 0;
7005 int i, x, y, element, graphic;
7006 byte *recorded_player_action;
7007 byte summarized_player_action = 0;
7009 byte tape_action[MAX_PLAYERS];
7012 if (game_status != GAME_MODE_PLAYING)
7015 action_delay_value =
7016 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7018 if (tape.playing && tape.index_search && !tape.pausing)
7019 action_delay_value = 0;
7021 /* ---------- main game synchronization point ---------- */
7023 WaitUntilDelayReached(&action_delay, action_delay_value);
7025 if (network_playing && !network_player_action_received)
7029 printf("DEBUG: try to get network player actions in time\n");
7033 #if defined(PLATFORM_UNIX)
7034 /* last chance to get network player actions without main loop delay */
7038 if (game_status != GAME_MODE_PLAYING)
7041 if (!network_player_action_received)
7045 printf("DEBUG: failed to get network player actions in time\n");
7056 printf("::: getting new tape action [%d]\n", FrameCounter);
7059 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7061 for (i = 0; i < MAX_PLAYERS; i++)
7063 summarized_player_action |= stored_player[i].action;
7065 if (!network_playing)
7066 stored_player[i].effective_action = stored_player[i].action;
7069 #if defined(PLATFORM_UNIX)
7070 if (network_playing)
7071 SendToServer_MovePlayer(summarized_player_action);
7074 if (!options.network && !setup.team_mode)
7075 local_player->effective_action = summarized_player_action;
7077 for (i = 0; i < MAX_PLAYERS; i++)
7079 int actual_player_action = stored_player[i].effective_action;
7081 if (stored_player[i].programmed_action)
7082 actual_player_action = stored_player[i].programmed_action;
7084 if (recorded_player_action)
7085 actual_player_action = recorded_player_action[i];
7087 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7089 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7090 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7092 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7097 TapeRecordAction(tape_action);
7100 network_player_action_received = FALSE;
7102 ScrollScreen(NULL, SCROLL_GO_ON);
7108 for (i = 0; i < MAX_PLAYERS; i++)
7109 stored_player[i].Frame++;
7113 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7115 for (i = 0; i < MAX_PLAYERS; i++)
7117 struct PlayerInfo *player = &stored_player[i];
7121 if (player->active && player->is_pushing && player->is_moving &&
7124 ContinueMoving(x, y);
7126 /* continue moving after pushing (this is actually a bug) */
7127 if (!IS_MOVING(x, y))
7136 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7138 Changed[x][y] = CE_BITMASK_DEFAULT;
7139 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7142 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7144 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7145 printf("GameActions(): This should never happen!\n");
7147 ChangePage[x][y] = -1;
7152 if (WasJustMoving[x][y] > 0)
7153 WasJustMoving[x][y]--;
7154 if (WasJustFalling[x][y] > 0)
7155 WasJustFalling[x][y]--;
7160 /* reset finished pushing action (not done in ContinueMoving() to allow
7161 continous pushing animation for elements with zero push delay) */
7162 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7164 ResetGfxAnimation(x, y);
7165 DrawLevelField(x, y);
7170 if (IS_BLOCKED(x, y))
7174 Blocked2Moving(x, y, &oldx, &oldy);
7175 if (!IS_MOVING(oldx, oldy))
7177 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7178 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7179 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7180 printf("GameActions(): This should never happen!\n");
7186 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7188 element = Feld[x][y];
7190 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7192 graphic = el2img(element);
7198 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7200 element = graphic = 0;
7204 if (graphic_info[graphic].anim_global_sync)
7205 GfxFrame[x][y] = FrameCounter;
7207 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7208 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7209 ResetRandomAnimationValue(x, y);
7211 SetRandomAnimationValue(x, y);
7214 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7217 if (IS_INACTIVE(element))
7219 if (IS_ANIMATED(graphic))
7220 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7226 /* this may take place after moving, so 'element' may have changed */
7228 if (IS_CHANGING(x, y))
7230 if (IS_CHANGING(x, y) &&
7231 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7235 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7236 element_info[element].event_page_nr[CE_DELAY]);
7238 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7241 element = Feld[x][y];
7242 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7246 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7251 element = Feld[x][y];
7252 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7254 if (element == EL_MOLE)
7255 printf("::: %d, %d, %d [%d]\n",
7256 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7260 if (element == EL_YAMYAM)
7261 printf("::: %d, %d, %d\n",
7262 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7266 if (IS_ANIMATED(graphic) &&
7270 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7273 if (element == EL_BUG)
7274 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7278 if (element == EL_MOLE)
7279 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7283 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7284 EdelsteinFunkeln(x, y);
7286 else if ((element == EL_ACID ||
7287 element == EL_EXIT_OPEN ||
7288 element == EL_SP_EXIT_OPEN ||
7289 element == EL_SP_TERMINAL ||
7290 element == EL_SP_TERMINAL_ACTIVE ||
7291 element == EL_EXTRA_TIME ||
7292 element == EL_SHIELD_NORMAL ||
7293 element == EL_SHIELD_DEADLY) &&
7294 IS_ANIMATED(graphic))
7295 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7296 else if (IS_MOVING(x, y))
7297 ContinueMoving(x, y);
7298 else if (IS_ACTIVE_BOMB(element))
7299 CheckDynamite(x, y);
7301 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7302 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7304 else if (element == EL_AMOEBA_GROWING)
7305 AmoebeWaechst(x, y);
7306 else if (element == EL_AMOEBA_SHRINKING)
7307 AmoebaDisappearing(x, y);
7309 #if !USE_NEW_AMOEBA_CODE
7310 else if (IS_AMOEBALIVE(element))
7311 AmoebeAbleger(x, y);
7314 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7316 else if (element == EL_EXIT_CLOSED)
7318 else if (element == EL_SP_EXIT_CLOSED)
7320 else if (element == EL_EXPANDABLE_WALL_GROWING)
7322 else if (element == EL_EXPANDABLE_WALL ||
7323 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7324 element == EL_EXPANDABLE_WALL_VERTICAL ||
7325 element == EL_EXPANDABLE_WALL_ANY)
7327 else if (element == EL_FLAMES)
7328 CheckForDragon(x, y);
7330 else if (IS_AUTO_CHANGING(element))
7331 ChangeElement(x, y);
7333 else if (element == EL_EXPLOSION)
7334 ; /* drawing of correct explosion animation is handled separately */
7335 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7336 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7339 /* this may take place after moving, so 'element' may have changed */
7340 if (IS_AUTO_CHANGING(Feld[x][y]))
7341 ChangeElement(x, y);
7344 if (IS_BELT_ACTIVE(element))
7345 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7347 if (game.magic_wall_active)
7349 int jx = local_player->jx, jy = local_player->jy;
7351 /* play the element sound at the position nearest to the player */
7352 if ((element == EL_MAGIC_WALL_FULL ||
7353 element == EL_MAGIC_WALL_ACTIVE ||
7354 element == EL_MAGIC_WALL_EMPTYING ||
7355 element == EL_BD_MAGIC_WALL_FULL ||
7356 element == EL_BD_MAGIC_WALL_ACTIVE ||
7357 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7358 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7366 #if USE_NEW_AMOEBA_CODE
7367 /* new experimental amoeba growth stuff */
7369 if (!(FrameCounter % 8))
7372 static unsigned long random = 1684108901;
7374 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7377 x = (random >> 10) % lev_fieldx;
7378 y = (random >> 20) % lev_fieldy;
7380 x = RND(lev_fieldx);
7381 y = RND(lev_fieldy);
7383 element = Feld[x][y];
7385 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7386 if (!IS_PLAYER(x,y) &&
7387 (element == EL_EMPTY ||
7388 element == EL_SAND ||
7389 element == EL_QUICKSAND_EMPTY ||
7390 element == EL_ACID_SPLASH_LEFT ||
7391 element == EL_ACID_SPLASH_RIGHT))
7393 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7394 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7395 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7396 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7397 Feld[x][y] = EL_AMOEBA_DROP;
7400 random = random * 129 + 1;
7406 if (game.explosions_delayed)
7409 game.explosions_delayed = FALSE;
7411 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7413 element = Feld[x][y];
7415 if (ExplodeField[x][y])
7416 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7417 else if (element == EL_EXPLOSION)
7418 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7420 ExplodeField[x][y] = EX_NO_EXPLOSION;
7423 game.explosions_delayed = TRUE;
7426 if (game.magic_wall_active)
7428 if (!(game.magic_wall_time_left % 4))
7430 int element = Feld[magic_wall_x][magic_wall_y];
7432 if (element == EL_BD_MAGIC_WALL_FULL ||
7433 element == EL_BD_MAGIC_WALL_ACTIVE ||
7434 element == EL_BD_MAGIC_WALL_EMPTYING)
7435 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7437 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7440 if (game.magic_wall_time_left > 0)
7442 game.magic_wall_time_left--;
7443 if (!game.magic_wall_time_left)
7445 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7447 element = Feld[x][y];
7449 if (element == EL_MAGIC_WALL_ACTIVE ||
7450 element == EL_MAGIC_WALL_FULL)
7452 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7453 DrawLevelField(x, y);
7455 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7456 element == EL_BD_MAGIC_WALL_FULL)
7458 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7459 DrawLevelField(x, y);
7463 game.magic_wall_active = FALSE;
7468 if (game.light_time_left > 0)
7470 game.light_time_left--;
7472 if (game.light_time_left == 0)
7473 RedrawAllLightSwitchesAndInvisibleElements();
7476 if (game.timegate_time_left > 0)
7478 game.timegate_time_left--;
7480 if (game.timegate_time_left == 0)
7481 CloseAllOpenTimegates();
7484 for (i = 0; i < MAX_PLAYERS; i++)
7486 struct PlayerInfo *player = &stored_player[i];
7488 if (SHIELD_ON(player))
7490 if (player->shield_deadly_time_left)
7491 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7492 else if (player->shield_normal_time_left)
7493 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7497 if (TimeFrames >= FRAMES_PER_SECOND)
7502 for (i = 0; i < MAX_PLAYERS; i++)
7504 struct PlayerInfo *player = &stored_player[i];
7506 if (SHIELD_ON(player))
7508 player->shield_normal_time_left--;
7510 if (player->shield_deadly_time_left > 0)
7511 player->shield_deadly_time_left--;
7515 if (tape.recording || tape.playing)
7516 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7522 if (TimeLeft <= 10 && setup.time_limit)
7523 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7525 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7527 if (!TimeLeft && setup.time_limit)
7528 for (i = 0; i < MAX_PLAYERS; i++)
7529 KillHero(&stored_player[i]);
7531 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7532 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7536 PlayAllPlayersSound();
7538 if (options.debug) /* calculate frames per second */
7540 static unsigned long fps_counter = 0;
7541 static int fps_frames = 0;
7542 unsigned long fps_delay_ms = Counter() - fps_counter;
7546 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7548 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7551 fps_counter = Counter();
7554 redraw_mask |= REDRAW_FPS;
7558 if (stored_player[0].jx != stored_player[0].last_jx ||
7559 stored_player[0].jy != stored_player[0].last_jy)
7560 printf("::: %d, %d, %d, %d, %d\n",
7561 stored_player[0].MovDir,
7562 stored_player[0].MovPos,
7563 stored_player[0].GfxPos,
7564 stored_player[0].Frame,
7565 stored_player[0].StepFrame);
7572 for (i = 0; i < MAX_PLAYERS; i++)
7575 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7577 stored_player[i].Frame += move_frames;
7579 if (stored_player[i].MovPos != 0)
7580 stored_player[i].StepFrame += move_frames;
7582 if (stored_player[i].drop_delay > 0)
7583 stored_player[i].drop_delay--;
7588 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7590 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7592 local_player->show_envelope = 0;
7597 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7599 int min_x = x, min_y = y, max_x = x, max_y = y;
7602 for (i = 0; i < MAX_PLAYERS; i++)
7604 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7606 if (!stored_player[i].active || &stored_player[i] == player)
7609 min_x = MIN(min_x, jx);
7610 min_y = MIN(min_y, jy);
7611 max_x = MAX(max_x, jx);
7612 max_y = MAX(max_y, jy);
7615 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7618 static boolean AllPlayersInVisibleScreen()
7622 for (i = 0; i < MAX_PLAYERS; i++)
7624 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7626 if (!stored_player[i].active)
7629 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7636 void ScrollLevel(int dx, int dy)
7638 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7641 BlitBitmap(drawto_field, drawto_field,
7642 FX + TILEX * (dx == -1) - softscroll_offset,
7643 FY + TILEY * (dy == -1) - softscroll_offset,
7644 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7645 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7646 FX + TILEX * (dx == 1) - softscroll_offset,
7647 FY + TILEY * (dy == 1) - softscroll_offset);
7651 x = (dx == 1 ? BX1 : BX2);
7652 for (y = BY1; y <= BY2; y++)
7653 DrawScreenField(x, y);
7658 y = (dy == 1 ? BY1 : BY2);
7659 for (x = BX1; x <= BX2; x++)
7660 DrawScreenField(x, y);
7663 redraw_mask |= REDRAW_FIELD;
7666 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7668 int nextx = x + dx, nexty = y + dy;
7669 int element = Feld[x][y];
7672 element != EL_SP_PORT_LEFT &&
7673 element != EL_SP_GRAVITY_PORT_LEFT &&
7674 element != EL_SP_PORT_HORIZONTAL &&
7675 element != EL_SP_PORT_ANY) ||
7677 element != EL_SP_PORT_RIGHT &&
7678 element != EL_SP_GRAVITY_PORT_RIGHT &&
7679 element != EL_SP_PORT_HORIZONTAL &&
7680 element != EL_SP_PORT_ANY) ||
7682 element != EL_SP_PORT_UP &&
7683 element != EL_SP_GRAVITY_PORT_UP &&
7684 element != EL_SP_PORT_VERTICAL &&
7685 element != EL_SP_PORT_ANY) ||
7687 element != EL_SP_PORT_DOWN &&
7688 element != EL_SP_GRAVITY_PORT_DOWN &&
7689 element != EL_SP_PORT_VERTICAL &&
7690 element != EL_SP_PORT_ANY) ||
7691 !IN_LEV_FIELD(nextx, nexty) ||
7692 !IS_FREE(nextx, nexty))
7698 static void CheckGravityMovement(struct PlayerInfo *player)
7700 if (game.gravity && !player->programmed_action)
7702 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7703 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7705 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7706 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7707 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7708 int jx = player->jx, jy = player->jy;
7709 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7710 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7711 int new_jx = jx + dx, new_jy = jy + dy;
7712 boolean field_under_player_is_free =
7713 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7714 boolean player_is_moving_to_valid_field =
7715 (IN_LEV_FIELD(new_jx, new_jy) &&
7716 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7717 Feld[new_jx][new_jy] == EL_SAND ||
7718 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7719 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7720 /* !!! extend EL_SAND to anything diggable !!! */
7722 if (field_under_player_is_free &&
7723 !player_is_moving_to_valid_field &&
7724 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7725 player->programmed_action = MV_DOWN;
7731 -----------------------------------------------------------------------------
7732 dx, dy: direction (non-diagonal) to try to move the player to
7733 real_dx, real_dy: direction as read from input device (can be diagonal)
7736 boolean MovePlayerOneStep(struct PlayerInfo *player,
7737 int dx, int dy, int real_dx, int real_dy)
7740 static int change_sides[4][2] =
7742 /* enter side leave side */
7743 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7744 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7745 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7746 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7748 int move_direction = (dx == -1 ? MV_LEFT :
7749 dx == +1 ? MV_RIGHT :
7751 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7752 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7753 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7755 int jx = player->jx, jy = player->jy;
7756 int new_jx = jx + dx, new_jy = jy + dy;
7760 if (!player->active || (!dx && !dy))
7761 return MF_NO_ACTION;
7763 player->MovDir = (dx < 0 ? MV_LEFT :
7766 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7768 if (!IN_LEV_FIELD(new_jx, new_jy))
7769 return MF_NO_ACTION;
7771 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7772 return MF_NO_ACTION;
7775 element = MovingOrBlocked2Element(new_jx, new_jy);
7777 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7780 if (DONT_RUN_INTO(element))
7782 if (element == EL_ACID && dx == 0 && dy == 1)
7785 Feld[jx][jy] = EL_PLAYER_1;
7786 InitMovingField(jx, jy, MV_DOWN);
7787 Store[jx][jy] = EL_ACID;
7788 ContinueMoving(jx, jy);
7792 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7797 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7798 if (can_move != MF_MOVING)
7801 /* check if DigField() has caused relocation of the player */
7802 if (player->jx != jx || player->jy != jy)
7803 return MF_NO_ACTION;
7805 StorePlayer[jx][jy] = 0;
7806 player->last_jx = jx;
7807 player->last_jy = jy;
7808 player->jx = new_jx;
7809 player->jy = new_jy;
7810 StorePlayer[new_jx][new_jy] = player->element_nr;
7813 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7815 player->step_counter++;
7817 player->drop_delay = 0;
7819 PlayerVisit[jx][jy] = FrameCounter;
7821 ScrollPlayer(player, SCROLL_INIT);
7824 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7826 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7827 CE_OTHER_GETS_LEFT);
7828 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7829 CE_LEFT_BY_PLAYER, -1);
7832 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7834 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7835 enter_side, CE_OTHER_GETS_ENTERED);
7836 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7837 CE_ENTERED_BY_PLAYER, -1);
7844 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7846 int jx = player->jx, jy = player->jy;
7847 int old_jx = jx, old_jy = jy;
7848 int moved = MF_NO_ACTION;
7851 if (!player->active)
7856 if (player->MovPos == 0)
7858 player->is_moving = FALSE;
7859 player->is_digging = FALSE;
7860 player->is_collecting = FALSE;
7861 player->is_snapping = FALSE;
7862 player->is_pushing = FALSE;
7868 if (!player->active || (!dx && !dy))
7873 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7877 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7878 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7882 /* remove the last programmed player action */
7883 player->programmed_action = 0;
7887 /* should only happen if pre-1.2 tape recordings are played */
7888 /* this is only for backward compatibility */
7890 int original_move_delay_value = player->move_delay_value;
7893 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7897 /* scroll remaining steps with finest movement resolution */
7898 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7900 while (player->MovPos)
7902 ScrollPlayer(player, SCROLL_GO_ON);
7903 ScrollScreen(NULL, SCROLL_GO_ON);
7909 player->move_delay_value = original_move_delay_value;
7912 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7914 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7915 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7919 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7920 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7926 if (moved & MF_MOVING && !ScreenMovPos &&
7927 (player == local_player || !options.network))
7929 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7930 int offset = (setup.scroll_delay ? 3 : 0);
7932 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7934 /* actual player has left the screen -- scroll in that direction */
7935 if (jx != old_jx) /* player has moved horizontally */
7936 scroll_x += (jx - old_jx);
7937 else /* player has moved vertically */
7938 scroll_y += (jy - old_jy);
7942 if (jx != old_jx) /* player has moved horizontally */
7944 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7945 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7946 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7948 /* don't scroll over playfield boundaries */
7949 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7950 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7952 /* don't scroll more than one field at a time */
7953 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7955 /* don't scroll against the player's moving direction */
7956 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7957 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7958 scroll_x = old_scroll_x;
7960 else /* player has moved vertically */
7962 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7963 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7964 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7966 /* don't scroll over playfield boundaries */
7967 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7968 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7970 /* don't scroll more than one field at a time */
7971 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7973 /* don't scroll against the player's moving direction */
7974 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7975 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7976 scroll_y = old_scroll_y;
7980 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7982 if (!options.network && !AllPlayersInVisibleScreen())
7984 scroll_x = old_scroll_x;
7985 scroll_y = old_scroll_y;
7989 ScrollScreen(player, SCROLL_INIT);
7990 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7997 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7999 if (!(moved & MF_MOVING) && !player->is_pushing)
8004 player->StepFrame = 0;
8006 if (moved & MF_MOVING)
8008 if (old_jx != jx && old_jy == jy)
8009 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8010 else if (old_jx == jx && old_jy != jy)
8011 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8013 DrawLevelField(jx, jy); /* for "crumbled sand" */
8015 player->last_move_dir = player->MovDir;
8016 player->is_moving = TRUE;
8018 player->is_snapping = FALSE;
8022 player->is_switching = FALSE;
8025 player->is_dropping = FALSE;
8030 static int change_sides[4][2] =
8032 /* enter side leave side */
8033 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8034 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8035 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8036 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8038 int move_direction = player->MovDir;
8039 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8040 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8043 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8045 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8046 leave_side, CE_OTHER_GETS_LEFT);
8047 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8048 leave_side, CE_LEFT_BY_PLAYER, -1);
8051 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8053 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8054 enter_side, CE_OTHER_GETS_ENTERED);
8055 CheckElementSideChange(jx, jy, Feld[jx][jy],
8056 enter_side, CE_ENTERED_BY_PLAYER, -1);
8067 CheckGravityMovement(player);
8070 player->last_move_dir = MV_NO_MOVING;
8072 player->is_moving = FALSE;
8075 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8077 TestIfHeroTouchesBadThing(jx, jy);
8078 TestIfPlayerTouchesCustomElement(jx, jy);
8081 if (!player->active)
8087 void ScrollPlayer(struct PlayerInfo *player, int mode)
8089 int jx = player->jx, jy = player->jy;
8090 int last_jx = player->last_jx, last_jy = player->last_jy;
8091 int move_stepsize = TILEX / player->move_delay_value;
8093 if (!player->active || !player->MovPos)
8096 if (mode == SCROLL_INIT)
8098 player->actual_frame_counter = FrameCounter;
8099 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8101 if (Feld[last_jx][last_jy] == EL_EMPTY)
8102 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8110 else if (!FrameReached(&player->actual_frame_counter, 1))
8113 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8114 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8116 if (!player->block_last_field &&
8117 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8118 Feld[last_jx][last_jy] = EL_EMPTY;
8120 /* before DrawPlayer() to draw correct player graphic for this case */
8121 if (player->MovPos == 0)
8122 CheckGravityMovement(player);
8125 DrawPlayer(player); /* needed here only to cleanup last field */
8128 if (player->MovPos == 0) /* player reached destination field */
8131 if (player->move_delay_reset_counter > 0)
8133 player->move_delay_reset_counter--;
8135 if (player->move_delay_reset_counter == 0)
8137 /* continue with normal speed after quickly moving through gate */
8138 HALVE_PLAYER_SPEED(player);
8140 /* be able to make the next move without delay */
8141 player->move_delay = 0;
8145 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8147 /* continue with normal speed after quickly moving through gate */
8148 HALVE_PLAYER_SPEED(player);
8150 /* be able to make the next move without delay */
8151 player->move_delay = 0;
8155 if (player->block_last_field &&
8156 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8157 Feld[last_jx][last_jy] = EL_EMPTY;
8159 player->last_jx = jx;
8160 player->last_jy = jy;
8162 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8163 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8164 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8166 DrawPlayer(player); /* needed here only to cleanup last field */
8169 if (local_player->friends_still_needed == 0 ||
8170 IS_SP_ELEMENT(Feld[jx][jy]))
8171 player->LevelSolved = player->GameOver = TRUE;
8174 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8176 TestIfHeroTouchesBadThing(jx, jy);
8177 TestIfPlayerTouchesCustomElement(jx, jy);
8179 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8182 if (!player->active)
8186 if (tape.single_step && tape.recording && !tape.pausing &&
8187 !player->programmed_action)
8188 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8192 void ScrollScreen(struct PlayerInfo *player, int mode)
8194 static unsigned long screen_frame_counter = 0;
8196 if (mode == SCROLL_INIT)
8198 /* set scrolling step size according to actual player's moving speed */
8199 ScrollStepSize = TILEX / player->move_delay_value;
8201 screen_frame_counter = FrameCounter;
8202 ScreenMovDir = player->MovDir;
8203 ScreenMovPos = player->MovPos;
8204 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8207 else if (!FrameReached(&screen_frame_counter, 1))
8212 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8213 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8214 redraw_mask |= REDRAW_FIELD;
8217 ScreenMovDir = MV_NO_MOVING;
8220 void TestIfPlayerTouchesCustomElement(int x, int y)
8222 static int xy[4][2] =
8229 static int change_sides[4][2] =
8231 /* center side border side */
8232 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8233 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8234 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8235 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8237 static int touch_dir[4] =
8244 int center_element = Feld[x][y]; /* should always be non-moving! */
8247 for (i = 0; i < 4; i++)
8249 int xx = x + xy[i][0];
8250 int yy = y + xy[i][1];
8251 int center_side = change_sides[i][0];
8252 int border_side = change_sides[i][1];
8255 if (!IN_LEV_FIELD(xx, yy))
8258 if (IS_PLAYER(x, y))
8260 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8261 border_element = Feld[xx][yy]; /* may be moving! */
8262 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8263 border_element = Feld[xx][yy];
8264 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8265 border_element = MovingOrBlocked2Element(xx, yy);
8267 continue; /* center and border element do not touch */
8269 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8270 CE_OTHER_GETS_TOUCHED);
8271 CheckElementSideChange(xx, yy, border_element, border_side,
8272 CE_TOUCHED_BY_PLAYER, -1);
8274 else if (IS_PLAYER(xx, yy))
8276 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8278 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8280 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8281 continue; /* center and border element do not touch */
8284 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8285 CE_OTHER_GETS_TOUCHED);
8286 CheckElementSideChange(x, y, center_element, center_side,
8287 CE_TOUCHED_BY_PLAYER, -1);
8294 void TestIfElementTouchesCustomElement(int x, int y)
8296 static int xy[4][2] =
8303 static int change_sides[4][2] =
8305 /* center side border side */
8306 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8307 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8308 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8309 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8311 static int touch_dir[4] =
8318 boolean change_center_element = FALSE;
8319 int center_element_change_page = 0;
8320 int center_element = Feld[x][y]; /* should always be non-moving! */
8323 for (i = 0; i < 4; i++)
8325 int xx = x + xy[i][0];
8326 int yy = y + xy[i][1];
8327 int center_side = change_sides[i][0];
8328 int border_side = change_sides[i][1];
8331 if (!IN_LEV_FIELD(xx, yy))
8334 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8335 border_element = Feld[xx][yy]; /* may be moving! */
8336 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8337 border_element = Feld[xx][yy];
8338 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8339 border_element = MovingOrBlocked2Element(xx, yy);
8341 continue; /* center and border element do not touch */
8343 /* check for change of center element (but change it only once) */
8344 if (IS_CUSTOM_ELEMENT(center_element) &&
8345 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8346 !change_center_element)
8348 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8350 struct ElementChangeInfo *change =
8351 &element_info[center_element].change_page[j];
8353 if (change->can_change &&
8354 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8355 change->sides & border_side &&
8357 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8359 change->trigger_element == border_element
8363 change_center_element = TRUE;
8364 center_element_change_page = j;
8371 /* check for change of border element */
8372 if (IS_CUSTOM_ELEMENT(border_element) &&
8373 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8375 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8377 struct ElementChangeInfo *change =
8378 &element_info[border_element].change_page[j];
8380 if (change->can_change &&
8381 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8382 change->sides & center_side &&
8384 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8386 change->trigger_element == center_element
8390 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8391 CE_OTHER_IS_TOUCHING, j);
8398 if (change_center_element)
8399 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8400 CE_OTHER_IS_TOUCHING, center_element_change_page);
8403 void TestIfElementHitsCustomElement(int x, int y, int direction)
8405 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8406 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8407 int hitx = x + dx, hity = y + dy;
8408 int hitting_element = Feld[x][y];
8410 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8411 !IS_FREE(hitx, hity) &&
8412 (!IS_MOVING(hitx, hity) ||
8413 MovDir[hitx][hity] != direction ||
8414 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8417 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8421 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8425 CheckElementSideChange(x, y, hitting_element,
8426 direction, CE_HITTING_SOMETHING, -1);
8428 if (IN_LEV_FIELD(hitx, hity))
8430 int opposite_direction = MV_DIR_OPPOSITE(direction);
8431 int hitting_side = direction;
8432 int touched_side = opposite_direction;
8433 int touched_element = MovingOrBlocked2Element(hitx, hity);
8435 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8436 MovDir[hitx][hity] != direction ||
8437 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8446 CheckElementSideChange(hitx, hity, touched_element,
8447 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8449 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8450 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8452 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8454 struct ElementChangeInfo *change =
8455 &element_info[hitting_element].change_page[i];
8457 if (change->can_change &&
8458 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8459 change->sides & touched_side &&
8462 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8464 change->trigger_element == touched_element
8468 CheckElementSideChange(x, y, hitting_element,
8469 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8475 if (IS_CUSTOM_ELEMENT(touched_element) &&
8476 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8478 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8480 struct ElementChangeInfo *change =
8481 &element_info[touched_element].change_page[i];
8483 if (change->can_change &&
8484 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8485 change->sides & hitting_side &&
8487 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8489 change->trigger_element == hitting_element
8493 CheckElementSideChange(hitx, hity, touched_element,
8494 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8503 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8505 int i, kill_x = -1, kill_y = -1;
8506 static int test_xy[4][2] =
8513 static int test_dir[4] =
8521 for (i = 0; i < 4; i++)
8523 int test_x, test_y, test_move_dir, test_element;
8525 test_x = good_x + test_xy[i][0];
8526 test_y = good_y + test_xy[i][1];
8527 if (!IN_LEV_FIELD(test_x, test_y))
8531 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8534 test_element = Feld[test_x][test_y];
8536 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8539 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8540 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8542 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8543 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8551 if (kill_x != -1 || kill_y != -1)
8553 if (IS_PLAYER(good_x, good_y))
8555 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8557 if (player->shield_deadly_time_left > 0)
8558 Bang(kill_x, kill_y);
8559 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8563 Bang(good_x, good_y);
8567 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8569 int i, kill_x = -1, kill_y = -1;
8570 int bad_element = Feld[bad_x][bad_y];
8571 static int test_xy[4][2] =
8578 static int touch_dir[4] =
8585 static int test_dir[4] =
8593 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8596 for (i = 0; i < 4; i++)
8598 int test_x, test_y, test_move_dir, test_element;
8600 test_x = bad_x + test_xy[i][0];
8601 test_y = bad_y + test_xy[i][1];
8602 if (!IN_LEV_FIELD(test_x, test_y))
8606 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8608 test_element = Feld[test_x][test_y];
8610 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8611 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8613 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8614 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8616 /* good thing is player or penguin that does not move away */
8617 if (IS_PLAYER(test_x, test_y))
8619 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8621 if (bad_element == EL_ROBOT && player->is_moving)
8622 continue; /* robot does not kill player if he is moving */
8624 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8626 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8627 continue; /* center and border element do not touch */
8634 else if (test_element == EL_PENGUIN)
8643 if (kill_x != -1 || kill_y != -1)
8645 if (IS_PLAYER(kill_x, kill_y))
8647 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8649 if (player->shield_deadly_time_left > 0)
8651 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8655 Bang(kill_x, kill_y);
8659 void TestIfHeroTouchesBadThing(int x, int y)
8661 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8664 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8666 TestIfGoodThingHitsBadThing(x, y, move_dir);
8669 void TestIfBadThingTouchesHero(int x, int y)
8671 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8674 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8676 TestIfBadThingHitsGoodThing(x, y, move_dir);
8679 void TestIfFriendTouchesBadThing(int x, int y)
8681 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8684 void TestIfBadThingTouchesFriend(int x, int y)
8686 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8689 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8691 int i, kill_x = bad_x, kill_y = bad_y;
8692 static int xy[4][2] =
8700 for (i = 0; i < 4; i++)
8704 x = bad_x + xy[i][0];
8705 y = bad_y + xy[i][1];
8706 if (!IN_LEV_FIELD(x, y))
8709 element = Feld[x][y];
8710 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8711 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8719 if (kill_x != bad_x || kill_y != bad_y)
8723 void KillHero(struct PlayerInfo *player)
8725 int jx = player->jx, jy = player->jy;
8727 if (!player->active)
8730 /* remove accessible field at the player's position */
8731 Feld[jx][jy] = EL_EMPTY;
8733 /* deactivate shield (else Bang()/Explode() would not work right) */
8734 player->shield_normal_time_left = 0;
8735 player->shield_deadly_time_left = 0;
8741 static void KillHeroUnlessEnemyProtected(int x, int y)
8743 if (!PLAYER_ENEMY_PROTECTED(x, y))
8744 KillHero(PLAYERINFO(x, y));
8747 static void KillHeroUnlessExplosionProtected(int x, int y)
8749 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8750 KillHero(PLAYERINFO(x, y));
8753 void BuryHero(struct PlayerInfo *player)
8755 int jx = player->jx, jy = player->jy;
8757 if (!player->active)
8761 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8763 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8765 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8767 player->GameOver = TRUE;
8771 void RemoveHero(struct PlayerInfo *player)
8773 int jx = player->jx, jy = player->jy;
8774 int i, found = FALSE;
8776 player->present = FALSE;
8777 player->active = FALSE;
8779 if (!ExplodeField[jx][jy])
8780 StorePlayer[jx][jy] = 0;
8782 for (i = 0; i < MAX_PLAYERS; i++)
8783 if (stored_player[i].active)
8787 AllPlayersGone = TRUE;
8794 =============================================================================
8795 checkDiagonalPushing()
8796 -----------------------------------------------------------------------------
8797 check if diagonal input device direction results in pushing of object
8798 (by checking if the alternative direction is walkable, diggable, ...)
8799 =============================================================================
8802 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8803 int x, int y, int real_dx, int real_dy)
8805 int jx, jy, dx, dy, xx, yy;
8807 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8810 /* diagonal direction: check alternative direction */
8815 xx = jx + (dx == 0 ? real_dx : 0);
8816 yy = jy + (dy == 0 ? real_dy : 0);
8818 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8822 =============================================================================
8824 -----------------------------------------------------------------------------
8825 x, y: field next to player (non-diagonal) to try to dig to
8826 real_dx, real_dy: direction as read from input device (can be diagonal)
8827 =============================================================================
8830 int DigField(struct PlayerInfo *player,
8831 int x, int y, int real_dx, int real_dy, int mode)
8833 static int change_sides[4] =
8835 CH_SIDE_RIGHT, /* moving left */
8836 CH_SIDE_LEFT, /* moving right */
8837 CH_SIDE_BOTTOM, /* moving up */
8838 CH_SIDE_TOP, /* moving down */
8840 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8841 int jx = player->jx, jy = player->jy;
8842 int dx = x - jx, dy = y - jy;
8843 int nextx = x + dx, nexty = y + dy;
8844 int move_direction = (dx == -1 ? MV_LEFT :
8845 dx == +1 ? MV_RIGHT :
8847 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8848 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8851 if (player->MovPos == 0)
8853 player->is_digging = FALSE;
8854 player->is_collecting = FALSE;
8857 if (player->MovPos == 0) /* last pushing move finished */
8858 player->is_pushing = FALSE;
8860 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8862 player->is_switching = FALSE;
8863 player->push_delay = 0;
8865 return MF_NO_ACTION;
8868 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8869 return MF_NO_ACTION;
8872 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8874 if (IS_TUBE(Feld[jx][jy]) ||
8875 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8879 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8880 int tube_leave_directions[][2] =
8882 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8883 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8884 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8885 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8886 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8887 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8888 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8889 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8890 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8891 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8892 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8893 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8896 while (tube_leave_directions[i][0] != tube_element)
8899 if (tube_leave_directions[i][0] == -1) /* should not happen */
8903 if (!(tube_leave_directions[i][1] & move_direction))
8904 return MF_NO_ACTION; /* tube has no opening in this direction */
8907 element = Feld[x][y];
8909 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8910 game.engine_version >= VERSION_IDENT(2,2,0,0))
8911 return MF_NO_ACTION;
8915 case EL_SP_PORT_LEFT:
8916 case EL_SP_PORT_RIGHT:
8918 case EL_SP_PORT_DOWN:
8919 case EL_SP_PORT_HORIZONTAL:
8920 case EL_SP_PORT_VERTICAL:
8921 case EL_SP_PORT_ANY:
8922 case EL_SP_GRAVITY_PORT_LEFT:
8923 case EL_SP_GRAVITY_PORT_RIGHT:
8924 case EL_SP_GRAVITY_PORT_UP:
8925 case EL_SP_GRAVITY_PORT_DOWN:
8927 if (!canEnterSupaplexPort(x, y, dx, dy))
8928 return MF_NO_ACTION;
8931 element != EL_SP_PORT_LEFT &&
8932 element != EL_SP_GRAVITY_PORT_LEFT &&
8933 element != EL_SP_PORT_HORIZONTAL &&
8934 element != EL_SP_PORT_ANY) ||
8936 element != EL_SP_PORT_RIGHT &&
8937 element != EL_SP_GRAVITY_PORT_RIGHT &&
8938 element != EL_SP_PORT_HORIZONTAL &&
8939 element != EL_SP_PORT_ANY) ||
8941 element != EL_SP_PORT_UP &&
8942 element != EL_SP_GRAVITY_PORT_UP &&
8943 element != EL_SP_PORT_VERTICAL &&
8944 element != EL_SP_PORT_ANY) ||
8946 element != EL_SP_PORT_DOWN &&
8947 element != EL_SP_GRAVITY_PORT_DOWN &&
8948 element != EL_SP_PORT_VERTICAL &&
8949 element != EL_SP_PORT_ANY) ||
8950 !IN_LEV_FIELD(nextx, nexty) ||
8951 !IS_FREE(nextx, nexty))
8952 return MF_NO_ACTION;
8955 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8956 element == EL_SP_GRAVITY_PORT_RIGHT ||
8957 element == EL_SP_GRAVITY_PORT_UP ||
8958 element == EL_SP_GRAVITY_PORT_DOWN)
8959 game.gravity = !game.gravity;
8961 /* automatically move to the next field with double speed */
8962 player->programmed_action = move_direction;
8964 if (player->move_delay_reset_counter == 0)
8966 player->move_delay_reset_counter = 2; /* two double speed steps */
8968 DOUBLE_PLAYER_SPEED(player);
8971 player->move_delay_reset_counter = 2;
8973 DOUBLE_PLAYER_SPEED(player);
8976 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8980 case EL_TUBE_VERTICAL:
8981 case EL_TUBE_HORIZONTAL:
8982 case EL_TUBE_VERTICAL_LEFT:
8983 case EL_TUBE_VERTICAL_RIGHT:
8984 case EL_TUBE_HORIZONTAL_UP:
8985 case EL_TUBE_HORIZONTAL_DOWN:
8986 case EL_TUBE_LEFT_UP:
8987 case EL_TUBE_LEFT_DOWN:
8988 case EL_TUBE_RIGHT_UP:
8989 case EL_TUBE_RIGHT_DOWN:
8992 int tube_enter_directions[][2] =
8994 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8995 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8996 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8997 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8998 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8999 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9000 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9001 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9002 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9003 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9004 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9005 { -1, MV_NO_MOVING }
9008 while (tube_enter_directions[i][0] != element)
9011 if (tube_enter_directions[i][0] == -1) /* should not happen */
9015 if (!(tube_enter_directions[i][1] & move_direction))
9016 return MF_NO_ACTION; /* tube has no opening in this direction */
9018 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9024 if (IS_WALKABLE(element))
9026 int sound_action = ACTION_WALKING;
9028 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9030 if (!player->key[element - EL_GATE_1])
9031 return MF_NO_ACTION;
9033 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9035 if (!player->key[element - EL_GATE_1_GRAY])
9036 return MF_NO_ACTION;
9038 else if (element == EL_EXIT_OPEN ||
9039 element == EL_SP_EXIT_OPEN ||
9040 element == EL_SP_EXIT_OPENING)
9042 sound_action = ACTION_PASSING; /* player is passing exit */
9044 else if (element == EL_EMPTY)
9046 sound_action = ACTION_MOVING; /* nothing to walk on */
9049 /* play sound from background or player, whatever is available */
9050 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9051 PlayLevelSoundElementAction(x, y, element, sound_action);
9053 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9057 else if (IS_PASSABLE(element))
9059 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9060 return MF_NO_ACTION;
9063 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9064 return MF_NO_ACTION;
9067 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9069 if (!player->key[element - EL_EM_GATE_1])
9070 return MF_NO_ACTION;
9072 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9074 if (!player->key[element - EL_EM_GATE_1_GRAY])
9075 return MF_NO_ACTION;
9078 /* automatically move to the next field with double speed */
9079 player->programmed_action = move_direction;
9081 if (player->move_delay_reset_counter == 0)
9083 player->move_delay_reset_counter = 2; /* two double speed steps */
9085 DOUBLE_PLAYER_SPEED(player);
9088 player->move_delay_reset_counter = 2;
9090 DOUBLE_PLAYER_SPEED(player);
9093 PlayLevelSoundAction(x, y, ACTION_PASSING);
9097 else if (IS_DIGGABLE(element))
9101 if (mode != DF_SNAP)
9104 GfxElement[x][y] = GFX_ELEMENT(element);
9107 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9109 player->is_digging = TRUE;
9112 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9114 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9117 if (mode == DF_SNAP)
9118 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9123 else if (IS_COLLECTIBLE(element))
9127 if (mode != DF_SNAP)
9129 GfxElement[x][y] = element;
9130 player->is_collecting = TRUE;
9133 if (element == EL_SPEED_PILL)
9134 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9135 else if (element == EL_EXTRA_TIME && level.time > 0)
9138 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9140 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9142 player->shield_normal_time_left += 10;
9143 if (element == EL_SHIELD_DEADLY)
9144 player->shield_deadly_time_left += 10;
9146 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9148 if (player->inventory_size < MAX_INVENTORY_SIZE)
9149 player->inventory_element[player->inventory_size++] = element;
9151 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9152 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9154 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9156 player->dynabomb_count++;
9157 player->dynabombs_left++;
9159 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9161 player->dynabomb_size++;
9163 else if (element == EL_DYNABOMB_INCREASE_POWER)
9165 player->dynabomb_xl = TRUE;
9167 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9168 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9170 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9171 element - EL_KEY_1 : element - EL_EM_KEY_1);
9173 player->key[key_nr] = TRUE;
9175 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9176 el2edimg(EL_KEY_1 + key_nr));
9177 redraw_mask |= REDRAW_DOOR_1;
9179 else if (IS_ENVELOPE(element))
9182 player->show_envelope = element;
9184 ShowEnvelope(element - EL_ENVELOPE_1);
9187 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9191 for (i = 0; i < element_info[element].collect_count; i++)
9192 if (player->inventory_size < MAX_INVENTORY_SIZE)
9193 player->inventory_element[player->inventory_size++] = element;
9195 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9196 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9198 else if (element_info[element].collect_count > 0)
9200 local_player->gems_still_needed -=
9201 element_info[element].collect_count;
9202 if (local_player->gems_still_needed < 0)
9203 local_player->gems_still_needed = 0;
9205 DrawText(DX_EMERALDS, DY_EMERALDS,
9206 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9209 RaiseScoreElement(element);
9210 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9212 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9215 if (mode == DF_SNAP)
9216 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9221 else if (IS_PUSHABLE(element))
9223 if (mode == DF_SNAP && element != EL_BD_ROCK)
9224 return MF_NO_ACTION;
9226 if (CAN_FALL(element) && dy)
9227 return MF_NO_ACTION;
9229 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9230 !(element == EL_SPRING && use_spring_bug))
9231 return MF_NO_ACTION;
9234 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9235 ((move_direction & MV_VERTICAL &&
9236 ((element_info[element].move_pattern & MV_LEFT &&
9237 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9238 (element_info[element].move_pattern & MV_RIGHT &&
9239 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9240 (move_direction & MV_HORIZONTAL &&
9241 ((element_info[element].move_pattern & MV_UP &&
9242 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9243 (element_info[element].move_pattern & MV_DOWN &&
9244 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9245 return MF_NO_ACTION;
9249 /* do not push elements already moving away faster than player */
9250 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9251 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9252 return MF_NO_ACTION;
9254 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9255 return MF_NO_ACTION;
9259 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9261 if (player->push_delay_value == -1)
9262 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9264 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9266 if (!player->is_pushing)
9267 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9271 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9272 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9273 !player_is_pushing))
9274 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9277 if (!player->is_pushing &&
9278 game.engine_version >= VERSION_IDENT(2,2,0,7))
9279 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9283 printf("::: push delay: %ld [%d, %d] [%d]\n",
9284 player->push_delay_value, FrameCounter, game.engine_version,
9285 player->is_pushing);
9288 player->is_pushing = TRUE;
9290 if (!(IN_LEV_FIELD(nextx, nexty) &&
9291 (IS_FREE(nextx, nexty) ||
9292 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9293 IS_SB_ELEMENT(element)))))
9294 return MF_NO_ACTION;
9296 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9297 return MF_NO_ACTION;
9299 if (player->push_delay == 0) /* new pushing; restart delay */
9300 player->push_delay = FrameCounter;
9302 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9303 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9304 element != EL_SPRING && element != EL_BALLOON)
9306 /* make sure that there is no move delay before next try to push */
9307 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9308 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9310 return MF_NO_ACTION;
9314 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9317 if (IS_SB_ELEMENT(element))
9319 if (element == EL_SOKOBAN_FIELD_FULL)
9321 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9322 local_player->sokobanfields_still_needed++;
9325 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9327 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9328 local_player->sokobanfields_still_needed--;
9331 Feld[x][y] = EL_SOKOBAN_OBJECT;
9333 if (Back[x][y] == Back[nextx][nexty])
9334 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9335 else if (Back[x][y] != 0)
9336 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9339 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9342 if (local_player->sokobanfields_still_needed == 0 &&
9343 game.emulation == EMU_SOKOBAN)
9345 player->LevelSolved = player->GameOver = TRUE;
9346 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9350 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9352 InitMovingField(x, y, move_direction);
9353 GfxAction[x][y] = ACTION_PUSHING;
9355 if (mode == DF_SNAP)
9356 ContinueMoving(x, y);
9358 MovPos[x][y] = (dx != 0 ? dx : dy);
9360 Pushed[x][y] = TRUE;
9361 Pushed[nextx][nexty] = TRUE;
9363 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9364 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9366 player->push_delay_value = -1; /* get new value later */
9368 CheckTriggeredElementSideChange(x, y, element, dig_side,
9369 CE_OTHER_GETS_PUSHED);
9370 CheckElementSideChange(x, y, element, dig_side,
9371 CE_PUSHED_BY_PLAYER, -1);
9375 else if (IS_SWITCHABLE(element))
9377 if (PLAYER_SWITCHING(player, x, y))
9380 player->is_switching = TRUE;
9381 player->switch_x = x;
9382 player->switch_y = y;
9384 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9386 if (element == EL_ROBOT_WHEEL)
9388 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9392 DrawLevelField(x, y);
9394 else if (element == EL_SP_TERMINAL)
9398 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9400 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9402 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9403 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9406 else if (IS_BELT_SWITCH(element))
9408 ToggleBeltSwitch(x, y);
9410 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9411 element == EL_SWITCHGATE_SWITCH_DOWN)
9413 ToggleSwitchgateSwitch(x, y);
9415 else if (element == EL_LIGHT_SWITCH ||
9416 element == EL_LIGHT_SWITCH_ACTIVE)
9418 ToggleLightSwitch(x, y);
9421 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9422 SND_LIGHT_SWITCH_ACTIVATING :
9423 SND_LIGHT_SWITCH_DEACTIVATING);
9426 else if (element == EL_TIMEGATE_SWITCH)
9428 ActivateTimegateSwitch(x, y);
9430 else if (element == EL_BALLOON_SWITCH_LEFT ||
9431 element == EL_BALLOON_SWITCH_RIGHT ||
9432 element == EL_BALLOON_SWITCH_UP ||
9433 element == EL_BALLOON_SWITCH_DOWN ||
9434 element == EL_BALLOON_SWITCH_ANY)
9436 if (element == EL_BALLOON_SWITCH_ANY)
9437 game.balloon_dir = move_direction;
9439 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9440 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9441 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9442 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9445 else if (element == EL_LAMP)
9447 Feld[x][y] = EL_LAMP_ACTIVE;
9448 local_player->lights_still_needed--;
9450 DrawLevelField(x, y);
9452 else if (element == EL_TIME_ORB_FULL)
9454 Feld[x][y] = EL_TIME_ORB_EMPTY;
9456 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9458 DrawLevelField(x, y);
9461 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9469 if (!PLAYER_SWITCHING(player, x, y))
9471 player->is_switching = TRUE;
9472 player->switch_x = x;
9473 player->switch_y = y;
9475 CheckTriggeredElementSideChange(x, y, element, dig_side,
9476 CE_OTHER_IS_SWITCHING);
9477 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9480 CheckTriggeredElementSideChange(x, y, element, dig_side,
9481 CE_OTHER_GETS_PRESSED);
9482 CheckElementSideChange(x, y, element, dig_side,
9483 CE_PRESSED_BY_PLAYER, -1);
9486 return MF_NO_ACTION;
9489 player->push_delay = 0;
9491 if (Feld[x][y] != element) /* really digged/collected something */
9492 player->is_collecting = !player->is_digging;
9497 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9499 int jx = player->jx, jy = player->jy;
9500 int x = jx + dx, y = jy + dy;
9501 int snap_direction = (dx == -1 ? MV_LEFT :
9502 dx == +1 ? MV_RIGHT :
9504 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9506 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9509 if (!player->active || !IN_LEV_FIELD(x, y))
9517 if (player->MovPos == 0)
9518 player->is_pushing = FALSE;
9520 player->is_snapping = FALSE;
9522 if (player->MovPos == 0)
9524 player->is_moving = FALSE;
9525 player->is_digging = FALSE;
9526 player->is_collecting = FALSE;
9532 if (player->is_snapping)
9535 player->MovDir = snap_direction;
9538 if (player->MovPos == 0)
9541 player->is_moving = FALSE;
9542 player->is_digging = FALSE;
9543 player->is_collecting = FALSE;
9546 player->is_dropping = FALSE;
9548 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9551 player->is_snapping = TRUE;
9554 if (player->MovPos == 0)
9557 player->is_moving = FALSE;
9558 player->is_digging = FALSE;
9559 player->is_collecting = FALSE;
9562 DrawLevelField(x, y);
9568 boolean DropElement(struct PlayerInfo *player)
9570 int jx = player->jx, jy = player->jy;
9571 int old_element = Feld[jx][jy];
9574 /* check if player is active, not moving and ready to drop */
9575 if (!player->active || player->MovPos || player->drop_delay > 0)
9578 /* check if player has anything that can be dropped */
9579 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9582 /* check if anything can be dropped at the current position */
9583 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9586 /* collected custom elements can only be dropped on empty fields */
9587 if (player->inventory_size > 0 &&
9588 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9589 && old_element != EL_EMPTY)
9592 if (old_element != EL_EMPTY)
9593 Back[jx][jy] = old_element; /* store old element on this field */
9595 ResetGfxAnimation(jx, jy);
9596 ResetRandomAnimationValue(jx, jy);
9598 if (player->inventory_size > 0)
9600 player->inventory_size--;
9601 new_element = player->inventory_element[player->inventory_size];
9603 if (new_element == EL_DYNAMITE)
9604 new_element = EL_DYNAMITE_ACTIVE;
9605 else if (new_element == EL_SP_DISK_RED)
9606 new_element = EL_SP_DISK_RED_ACTIVE;
9608 Feld[jx][jy] = new_element;
9610 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9611 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9613 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9614 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9616 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9619 /* needed if previous element just changed to "empty" in the last frame */
9620 Changed[jx][jy] = 0; /* allow another change */
9623 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9624 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9626 TestIfElementTouchesCustomElement(jx, jy);
9628 else /* player is dropping a dyna bomb */
9630 player->dynabombs_left--;
9631 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9633 Feld[jx][jy] = new_element;
9635 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9636 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9638 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9645 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9647 InitField(jx, jy, FALSE);
9648 if (CAN_MOVE(Feld[jx][jy]))
9652 new_element = Feld[jx][jy];
9654 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9655 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9657 int move_stepsize = element_info[new_element].move_stepsize;
9658 int direction, dx, dy, nextx, nexty;
9660 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9661 MovDir[jx][jy] = player->MovDir;
9663 direction = MovDir[jx][jy];
9664 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9665 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9669 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9672 WasJustMoving[jx][jy] = 3;
9674 InitMovingField(jx, jy, direction);
9675 ContinueMoving(jx, jy);
9680 Changed[jx][jy] = 0; /* allow another change */
9683 TestIfElementHitsCustomElement(jx, jy, direction);
9685 CheckElementSideChange(jx, jy, new_element,
9686 direction, CE_HITTING_SOMETHING, -1);
9690 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9694 player->drop_delay = 8 + 8 + 8;
9699 player->is_dropping = TRUE;
9705 /* ------------------------------------------------------------------------- */
9706 /* game sound playing functions */
9707 /* ------------------------------------------------------------------------- */
9709 static int *loop_sound_frame = NULL;
9710 static int *loop_sound_volume = NULL;
9712 void InitPlayLevelSound()
9714 int num_sounds = getSoundListSize();
9716 checked_free(loop_sound_frame);
9717 checked_free(loop_sound_volume);
9719 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9720 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9723 static void PlayLevelSound(int x, int y, int nr)
9725 int sx = SCREENX(x), sy = SCREENY(y);
9726 int volume, stereo_position;
9727 int max_distance = 8;
9728 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9730 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9731 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9734 if (!IN_LEV_FIELD(x, y) ||
9735 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9736 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9739 volume = SOUND_MAX_VOLUME;
9741 if (!IN_SCR_FIELD(sx, sy))
9743 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9744 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9746 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9749 stereo_position = (SOUND_MAX_LEFT +
9750 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9751 (SCR_FIELDX + 2 * max_distance));
9753 if (IS_LOOP_SOUND(nr))
9755 /* This assures that quieter loop sounds do not overwrite louder ones,
9756 while restarting sound volume comparison with each new game frame. */
9758 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9761 loop_sound_volume[nr] = volume;
9762 loop_sound_frame[nr] = FrameCounter;
9765 PlaySoundExt(nr, volume, stereo_position, type);
9768 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9770 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9771 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9772 y < LEVELY(BY1) ? LEVELY(BY1) :
9773 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9777 static void PlayLevelSoundAction(int x, int y, int action)
9779 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9782 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9784 int sound_effect = element_info[element].sound[action];
9786 if (sound_effect != SND_UNDEFINED)
9787 PlayLevelSound(x, y, sound_effect);
9790 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9793 int sound_effect = element_info[element].sound[action];
9795 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9796 PlayLevelSound(x, y, sound_effect);
9799 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9801 int sound_effect = element_info[Feld[x][y]].sound[action];
9803 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9804 PlayLevelSound(x, y, sound_effect);
9807 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9809 int sound_effect = element_info[Feld[x][y]].sound[action];
9811 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9812 StopSound(sound_effect);
9815 static void PlayLevelMusic()
9817 if (levelset.music[level_nr] != MUS_UNDEFINED)
9818 PlayMusic(levelset.music[level_nr]); /* from config file */
9820 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9823 void RaiseScore(int value)
9825 local_player->score += value;
9826 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9829 void RaiseScoreElement(int element)
9835 case EL_EMERALD_YELLOW:
9836 case EL_EMERALD_RED:
9837 case EL_EMERALD_PURPLE:
9838 case EL_SP_INFOTRON:
9839 RaiseScore(level.score[SC_EMERALD]);
9842 RaiseScore(level.score[SC_DIAMOND]);
9845 RaiseScore(level.score[SC_CRYSTAL]);
9848 RaiseScore(level.score[SC_PEARL]);
9851 case EL_BD_BUTTERFLY:
9852 case EL_SP_ELECTRON:
9853 RaiseScore(level.score[SC_BUG]);
9857 case EL_SP_SNIKSNAK:
9858 RaiseScore(level.score[SC_SPACESHIP]);
9861 case EL_DARK_YAMYAM:
9862 RaiseScore(level.score[SC_YAMYAM]);
9865 RaiseScore(level.score[SC_ROBOT]);
9868 RaiseScore(level.score[SC_PACMAN]);
9871 RaiseScore(level.score[SC_NUT]);
9874 case EL_SP_DISK_RED:
9875 case EL_DYNABOMB_INCREASE_NUMBER:
9876 case EL_DYNABOMB_INCREASE_SIZE:
9877 case EL_DYNABOMB_INCREASE_POWER:
9878 RaiseScore(level.score[SC_DYNAMITE]);
9880 case EL_SHIELD_NORMAL:
9881 case EL_SHIELD_DEADLY:
9882 RaiseScore(level.score[SC_SHIELD]);
9885 RaiseScore(level.score[SC_TIME_BONUS]);
9891 RaiseScore(level.score[SC_KEY]);
9894 RaiseScore(element_info[element].collect_score);
9899 void RequestQuitGame(boolean ask_if_really_quit)
9901 if (AllPlayersGone ||
9902 !ask_if_really_quit ||
9903 level_editor_test_game ||
9904 Request("Do you really want to quit the game ?",
9905 REQ_ASK | REQ_STAY_CLOSED))
9907 #if defined(PLATFORM_UNIX)
9908 if (options.network)
9909 SendToServer_StopPlaying();
9913 game_status = GAME_MODE_MAIN;
9919 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9924 /* ---------- new game button stuff ---------------------------------------- */
9926 /* graphic position values for game buttons */
9927 #define GAME_BUTTON_XSIZE 30
9928 #define GAME_BUTTON_YSIZE 30
9929 #define GAME_BUTTON_XPOS 5
9930 #define GAME_BUTTON_YPOS 215
9931 #define SOUND_BUTTON_XPOS 5
9932 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9934 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9935 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9936 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9937 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9938 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9939 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9946 } gamebutton_info[NUM_GAME_BUTTONS] =
9949 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9954 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9959 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9964 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9965 SOUND_CTRL_ID_MUSIC,
9966 "background music on/off"
9969 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9970 SOUND_CTRL_ID_LOOPS,
9971 "sound loops on/off"
9974 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9975 SOUND_CTRL_ID_SIMPLE,
9976 "normal sounds on/off"
9980 void CreateGameButtons()
9984 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9986 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9987 struct GadgetInfo *gi;
9990 unsigned long event_mask;
9991 int gd_xoffset, gd_yoffset;
9992 int gd_x1, gd_x2, gd_y1, gd_y2;
9995 gd_xoffset = gamebutton_info[i].x;
9996 gd_yoffset = gamebutton_info[i].y;
9997 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9998 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10000 if (id == GAME_CTRL_ID_STOP ||
10001 id == GAME_CTRL_ID_PAUSE ||
10002 id == GAME_CTRL_ID_PLAY)
10004 button_type = GD_TYPE_NORMAL_BUTTON;
10006 event_mask = GD_EVENT_RELEASED;
10007 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10008 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10012 button_type = GD_TYPE_CHECK_BUTTON;
10014 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10015 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10016 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10017 event_mask = GD_EVENT_PRESSED;
10018 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10019 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10022 gi = CreateGadget(GDI_CUSTOM_ID, id,
10023 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10024 GDI_X, DX + gd_xoffset,
10025 GDI_Y, DY + gd_yoffset,
10026 GDI_WIDTH, GAME_BUTTON_XSIZE,
10027 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10028 GDI_TYPE, button_type,
10029 GDI_STATE, GD_BUTTON_UNPRESSED,
10030 GDI_CHECKED, checked,
10031 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10032 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10033 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10034 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10035 GDI_EVENT_MASK, event_mask,
10036 GDI_CALLBACK_ACTION, HandleGameButtons,
10040 Error(ERR_EXIT, "cannot create gadget");
10042 game_gadget[id] = gi;
10046 void FreeGameButtons()
10050 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10051 FreeGadget(game_gadget[i]);
10054 static void MapGameButtons()
10058 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10059 MapGadget(game_gadget[i]);
10062 void UnmapGameButtons()
10066 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10067 UnmapGadget(game_gadget[i]);
10070 static void HandleGameButtons(struct GadgetInfo *gi)
10072 int id = gi->custom_id;
10074 if (game_status != GAME_MODE_PLAYING)
10079 case GAME_CTRL_ID_STOP:
10080 RequestQuitGame(TRUE);
10083 case GAME_CTRL_ID_PAUSE:
10084 if (options.network)
10086 #if defined(PLATFORM_UNIX)
10088 SendToServer_ContinuePlaying();
10090 SendToServer_PausePlaying();
10094 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10097 case GAME_CTRL_ID_PLAY:
10100 #if defined(PLATFORM_UNIX)
10101 if (options.network)
10102 SendToServer_ContinuePlaying();
10106 tape.pausing = FALSE;
10107 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10112 case SOUND_CTRL_ID_MUSIC:
10113 if (setup.sound_music)
10115 setup.sound_music = FALSE;
10118 else if (audio.music_available)
10120 setup.sound = setup.sound_music = TRUE;
10122 SetAudioMode(setup.sound);
10128 case SOUND_CTRL_ID_LOOPS:
10129 if (setup.sound_loops)
10130 setup.sound_loops = FALSE;
10131 else if (audio.loops_available)
10133 setup.sound = setup.sound_loops = TRUE;
10134 SetAudioMode(setup.sound);
10138 case SOUND_CTRL_ID_SIMPLE:
10139 if (setup.sound_simple)
10140 setup.sound_simple = FALSE;
10141 else if (audio.sound_available)
10143 setup.sound = setup.sound_simple = TRUE;
10144 SetAudioMode(setup.sound);