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_FIRE(border_element))
2727 Feld[x][y] = Store2[x][y];
2730 border_explosion = TRUE;
2732 if (phase == last_phase)
2733 printf("::: CAN_EXPLODE_BY_FIRE\n");
2735 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2737 AmoebeUmwandeln(x, y);
2738 border_explosion = TRUE;
2740 if (phase == last_phase)
2741 printf("::: EL_AMOEBA_TO_DIAMOND\n");
2745 if (border_explosion && phase == last_phase)
2752 if (phase == first_phase_after_start)
2754 int element = Store2[x][y];
2756 if (element == EL_BLACK_ORB)
2758 Feld[x][y] = Store2[x][y];
2763 else if (phase == half_phase)
2765 int element = Store2[x][y];
2767 if (IS_PLAYER(x, y))
2768 KillHeroUnlessExplosionProtected(x, y);
2769 else if (CAN_EXPLODE_BY_FIRE(element))
2771 Feld[x][y] = Store2[x][y];
2775 else if (element == EL_AMOEBA_TO_DIAMOND)
2776 AmoebeUmwandeln(x, y);
2780 if (phase == last_phase)
2784 element = Feld[x][y] = Store[x][y];
2785 Store[x][y] = Store2[x][y] = 0;
2786 GfxElement[x][y] = EL_UNDEFINED;
2788 /* player can escape from explosions and might therefore be still alive */
2789 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2790 element <= EL_PLAYER_IS_EXPLODING_4)
2791 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2793 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2794 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2795 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2798 /* restore probably existing indestructible background element */
2799 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2800 element = Feld[x][y] = Back[x][y];
2803 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2804 GfxDir[x][y] = MV_NO_MOVING;
2805 ChangeDelay[x][y] = 0;
2806 ChangePage[x][y] = -1;
2808 InitField(x, y, FALSE);
2810 /* !!! not needed !!! */
2811 if (CAN_MOVE(element))
2814 DrawLevelField(x, y);
2816 TestIfElementTouchesCustomElement(x, y);
2818 if (GFX_CRUMBLED(element))
2819 DrawLevelFieldCrumbledSandNeighbours(x, y);
2821 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2822 StorePlayer[x][y] = 0;
2824 if (ELEM_IS_PLAYER(element))
2825 RelocatePlayer(x, y, element);
2828 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2830 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2834 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2836 int stored = Store[x][y];
2837 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2838 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2841 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2844 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2845 element_info[GfxElement[x][y]].token_name,
2850 DrawLevelFieldCrumbledSand(x, y);
2852 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2854 DrawLevelElement(x, y, Back[x][y]);
2855 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2857 else if (IS_WALKABLE_UNDER(Back[x][y]))
2859 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2860 DrawLevelElementThruMask(x, y, Back[x][y]);
2862 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2863 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2867 void DynaExplode(int ex, int ey)
2870 int dynabomb_element = Feld[ex][ey];
2871 int dynabomb_size = 1;
2872 boolean dynabomb_xl = FALSE;
2873 struct PlayerInfo *player;
2874 static int xy[4][2] =
2882 if (IS_ACTIVE_BOMB(dynabomb_element))
2884 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2885 dynabomb_size = player->dynabomb_size;
2886 dynabomb_xl = player->dynabomb_xl;
2887 player->dynabombs_left++;
2890 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2892 for (i = 0; i < 4; i++)
2894 for (j = 1; j <= dynabomb_size; j++)
2896 int x = ex + j * xy[i % 4][0];
2897 int y = ey + j * xy[i % 4][1];
2900 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2903 element = Feld[x][y];
2905 /* do not restart explosions of fields with active bombs */
2906 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2909 Explode(x, y, EX_PHASE_START, EX_BORDER);
2911 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2912 if (element != EL_EMPTY &&
2913 element != EL_SAND &&
2914 element != EL_EXPLOSION &&
2921 void Bang(int x, int y)
2924 int element = MovingOrBlocked2Element(x, y);
2926 int element = Feld[x][y];
2930 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2932 if (IS_PLAYER(x, y))
2935 struct PlayerInfo *player = PLAYERINFO(x, y);
2937 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2938 player->element_nr);
2943 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2945 if (game.emulation == EMU_SUPAPLEX)
2946 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2948 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2953 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2961 case EL_BD_BUTTERFLY:
2964 case EL_DARK_YAMYAM:
2968 RaiseScoreElement(element);
2969 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2971 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2972 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2973 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2974 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2975 case EL_DYNABOMB_INCREASE_NUMBER:
2976 case EL_DYNABOMB_INCREASE_SIZE:
2977 case EL_DYNABOMB_INCREASE_POWER:
2982 case EL_LAMP_ACTIVE:
2983 if (IS_PLAYER(x, y))
2984 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2986 Explode(x, y, EX_PHASE_START, EX_CENTER);
2989 if (CAN_EXPLODE_DYNA(element))
2991 else if (CAN_EXPLODE_1X1(element))
2992 Explode(x, y, EX_PHASE_START, EX_CENTER);
2994 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2998 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3001 void SplashAcid(int x, int y)
3003 int element = Feld[x][y];
3005 if (element != EL_ACID_SPLASH_LEFT &&
3006 element != EL_ACID_SPLASH_RIGHT)
3008 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3010 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3011 (!IN_LEV_FIELD(x-1, y-1) ||
3012 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3013 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3015 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3016 (!IN_LEV_FIELD(x+1, y-1) ||
3017 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3018 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3022 static void InitBeltMovement()
3024 static int belt_base_element[4] =
3026 EL_CONVEYOR_BELT_1_LEFT,
3027 EL_CONVEYOR_BELT_2_LEFT,
3028 EL_CONVEYOR_BELT_3_LEFT,
3029 EL_CONVEYOR_BELT_4_LEFT
3031 static int belt_base_active_element[4] =
3033 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3034 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3035 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3036 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3041 /* set frame order for belt animation graphic according to belt direction */
3042 for (i = 0; i < 4; i++)
3046 for (j = 0; j < 3; j++)
3048 int element = belt_base_active_element[belt_nr] + j;
3049 int graphic = el2img(element);
3051 if (game.belt_dir[i] == MV_LEFT)
3052 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3054 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3058 for (y = 0; y < lev_fieldy; y++)
3060 for (x = 0; x < lev_fieldx; x++)
3062 int element = Feld[x][y];
3064 for (i = 0; i < 4; i++)
3066 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3068 int e_belt_nr = getBeltNrFromBeltElement(element);
3071 if (e_belt_nr == belt_nr)
3073 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3075 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3083 static void ToggleBeltSwitch(int x, int y)
3085 static int belt_base_element[4] =
3087 EL_CONVEYOR_BELT_1_LEFT,
3088 EL_CONVEYOR_BELT_2_LEFT,
3089 EL_CONVEYOR_BELT_3_LEFT,
3090 EL_CONVEYOR_BELT_4_LEFT
3092 static int belt_base_active_element[4] =
3094 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3095 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3096 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3097 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3099 static int belt_base_switch_element[4] =
3101 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3102 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3103 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3104 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3106 static int belt_move_dir[4] =
3114 int element = Feld[x][y];
3115 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3116 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3117 int belt_dir = belt_move_dir[belt_dir_nr];
3120 if (!IS_BELT_SWITCH(element))
3123 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3124 game.belt_dir[belt_nr] = belt_dir;
3126 if (belt_dir_nr == 3)
3129 /* set frame order for belt animation graphic according to belt direction */
3130 for (i = 0; i < 3; i++)
3132 int element = belt_base_active_element[belt_nr] + i;
3133 int graphic = el2img(element);
3135 if (belt_dir == MV_LEFT)
3136 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3138 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3141 for (yy = 0; yy < lev_fieldy; yy++)
3143 for (xx = 0; xx < lev_fieldx; xx++)
3145 int element = Feld[xx][yy];
3147 if (IS_BELT_SWITCH(element))
3149 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3151 if (e_belt_nr == belt_nr)
3153 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3154 DrawLevelField(xx, yy);
3157 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3159 int e_belt_nr = getBeltNrFromBeltElement(element);
3161 if (e_belt_nr == belt_nr)
3163 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3165 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3166 DrawLevelField(xx, yy);
3169 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3171 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3173 if (e_belt_nr == belt_nr)
3175 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3177 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3178 DrawLevelField(xx, yy);
3185 static void ToggleSwitchgateSwitch(int x, int y)
3189 game.switchgate_pos = !game.switchgate_pos;
3191 for (yy = 0; yy < lev_fieldy; yy++)
3193 for (xx = 0; xx < lev_fieldx; xx++)
3195 int element = Feld[xx][yy];
3197 if (element == EL_SWITCHGATE_SWITCH_UP ||
3198 element == EL_SWITCHGATE_SWITCH_DOWN)
3200 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3201 DrawLevelField(xx, yy);
3203 else if (element == EL_SWITCHGATE_OPEN ||
3204 element == EL_SWITCHGATE_OPENING)
3206 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3208 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3210 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3213 else if (element == EL_SWITCHGATE_CLOSED ||
3214 element == EL_SWITCHGATE_CLOSING)
3216 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3218 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3220 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3227 static int getInvisibleActiveFromInvisibleElement(int element)
3229 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3230 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3231 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3235 static int getInvisibleFromInvisibleActiveElement(int element)
3237 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3238 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3239 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3243 static void RedrawAllLightSwitchesAndInvisibleElements()
3247 for (y = 0; y < lev_fieldy; y++)
3249 for (x = 0; x < lev_fieldx; x++)
3251 int element = Feld[x][y];
3253 if (element == EL_LIGHT_SWITCH &&
3254 game.light_time_left > 0)
3256 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3257 DrawLevelField(x, y);
3259 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3260 game.light_time_left == 0)
3262 Feld[x][y] = EL_LIGHT_SWITCH;
3263 DrawLevelField(x, y);
3265 else if (element == EL_INVISIBLE_STEELWALL ||
3266 element == EL_INVISIBLE_WALL ||
3267 element == EL_INVISIBLE_SAND)
3269 if (game.light_time_left > 0)
3270 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3272 DrawLevelField(x, y);
3274 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3275 element == EL_INVISIBLE_WALL_ACTIVE ||
3276 element == EL_INVISIBLE_SAND_ACTIVE)
3278 if (game.light_time_left == 0)
3279 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3281 DrawLevelField(x, y);
3287 static void ToggleLightSwitch(int x, int y)
3289 int element = Feld[x][y];
3291 game.light_time_left =
3292 (element == EL_LIGHT_SWITCH ?
3293 level.time_light * FRAMES_PER_SECOND : 0);
3295 RedrawAllLightSwitchesAndInvisibleElements();
3298 static void ActivateTimegateSwitch(int x, int y)
3302 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3304 for (yy = 0; yy < lev_fieldy; yy++)
3306 for (xx = 0; xx < lev_fieldx; xx++)
3308 int element = Feld[xx][yy];
3310 if (element == EL_TIMEGATE_CLOSED ||
3311 element == EL_TIMEGATE_CLOSING)
3313 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3314 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3318 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3320 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3321 DrawLevelField(xx, yy);
3328 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3331 inline static int getElementMoveStepsize(int x, int y)
3333 int element = Feld[x][y];
3334 int direction = MovDir[x][y];
3335 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3336 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3337 int horiz_move = (dx != 0);
3338 int sign = (horiz_move ? dx : dy);
3339 int step = sign * element_info[element].move_stepsize;
3341 /* special values for move stepsize for spring and things on conveyor belt */
3344 if (CAN_FALL(element) &&
3345 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3346 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3347 else if (element == EL_SPRING)
3348 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3354 void Impact(int x, int y)
3356 boolean lastline = (y == lev_fieldy-1);
3357 boolean object_hit = FALSE;
3358 boolean impact = (lastline || object_hit);
3359 int element = Feld[x][y];
3360 int smashed = EL_UNDEFINED;
3362 if (!lastline) /* check if element below was hit */
3364 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3367 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3368 MovDir[x][y + 1] != MV_DOWN ||
3369 MovPos[x][y + 1] <= TILEY / 2));
3372 object_hit = !IS_FREE(x, y + 1);
3375 /* do not smash moving elements that left the smashed field in time */
3376 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3377 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3381 smashed = MovingOrBlocked2Element(x, y + 1);
3383 impact = (lastline || object_hit);
3386 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3392 /* only reset graphic animation if graphic really changes after impact */
3394 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3396 ResetGfxAnimation(x, y);
3397 DrawLevelField(x, y);
3400 if (impact && CAN_EXPLODE_IMPACT(element))
3405 else if (impact && element == EL_PEARL)
3407 Feld[x][y] = EL_PEARL_BREAKING;
3408 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3411 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3413 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3418 if (impact && element == EL_AMOEBA_DROP)
3420 if (object_hit && IS_PLAYER(x, y + 1))
3421 KillHeroUnlessEnemyProtected(x, y + 1);
3422 else if (object_hit && smashed == EL_PENGUIN)
3426 Feld[x][y] = EL_AMOEBA_GROWING;
3427 Store[x][y] = EL_AMOEBA_WET;
3429 ResetRandomAnimationValue(x, y);
3434 if (object_hit) /* check which object was hit */
3436 if (CAN_PASS_MAGIC_WALL(element) &&
3437 (smashed == EL_MAGIC_WALL ||
3438 smashed == EL_BD_MAGIC_WALL))
3441 int activated_magic_wall =
3442 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3443 EL_BD_MAGIC_WALL_ACTIVE);
3445 /* activate magic wall / mill */
3446 for (yy = 0; yy < lev_fieldy; yy++)
3447 for (xx = 0; xx < lev_fieldx; xx++)
3448 if (Feld[xx][yy] == smashed)
3449 Feld[xx][yy] = activated_magic_wall;
3451 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3452 game.magic_wall_active = TRUE;
3454 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3455 SND_MAGIC_WALL_ACTIVATING :
3456 SND_BD_MAGIC_WALL_ACTIVATING));
3459 if (IS_PLAYER(x, y + 1))
3461 if (CAN_SMASH_PLAYER(element))
3463 KillHeroUnlessEnemyProtected(x, y + 1);
3467 else if (smashed == EL_PENGUIN)
3469 if (CAN_SMASH_PLAYER(element))
3475 else if (element == EL_BD_DIAMOND)
3477 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3483 else if (((element == EL_SP_INFOTRON ||
3484 element == EL_SP_ZONK) &&
3485 (smashed == EL_SP_SNIKSNAK ||
3486 smashed == EL_SP_ELECTRON ||
3487 smashed == EL_SP_DISK_ORANGE)) ||
3488 (element == EL_SP_INFOTRON &&
3489 smashed == EL_SP_DISK_YELLOW))
3495 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3501 else if (CAN_SMASH_EVERYTHING(element))
3503 if (IS_CLASSIC_ENEMY(smashed) ||
3504 CAN_EXPLODE_SMASHED(smashed))
3509 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3511 if (smashed == EL_LAMP ||
3512 smashed == EL_LAMP_ACTIVE)
3517 else if (smashed == EL_NUT)
3519 Feld[x][y + 1] = EL_NUT_BREAKING;
3520 PlayLevelSound(x, y, SND_NUT_BREAKING);
3521 RaiseScoreElement(EL_NUT);
3524 else if (smashed == EL_PEARL)
3526 Feld[x][y + 1] = EL_PEARL_BREAKING;
3527 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3530 else if (smashed == EL_DIAMOND)
3532 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3533 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3536 else if (IS_BELT_SWITCH(smashed))
3538 ToggleBeltSwitch(x, y + 1);
3540 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3541 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3543 ToggleSwitchgateSwitch(x, y + 1);
3545 else if (smashed == EL_LIGHT_SWITCH ||
3546 smashed == EL_LIGHT_SWITCH_ACTIVE)
3548 ToggleLightSwitch(x, y + 1);
3552 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3554 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3555 CE_OTHER_IS_SWITCHING);
3556 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3562 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3567 /* play sound of magic wall / mill */
3569 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3570 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3572 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3573 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3574 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3575 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3580 /* play sound of object that hits the ground */
3581 if (lastline || object_hit)
3582 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3585 inline static void TurnRoundExt(int x, int y)
3597 { 0, 0 }, { 0, 0 }, { 0, 0 },
3602 int left, right, back;
3606 { MV_DOWN, MV_UP, MV_RIGHT },
3607 { MV_UP, MV_DOWN, MV_LEFT },
3609 { MV_LEFT, MV_RIGHT, MV_DOWN },
3613 { MV_RIGHT, MV_LEFT, MV_UP }
3616 int element = Feld[x][y];
3617 int move_pattern = element_info[element].move_pattern;
3619 int old_move_dir = MovDir[x][y];
3620 int left_dir = turn[old_move_dir].left;
3621 int right_dir = turn[old_move_dir].right;
3622 int back_dir = turn[old_move_dir].back;
3624 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3625 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3626 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3627 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3629 int left_x = x + left_dx, left_y = y + left_dy;
3630 int right_x = x + right_dx, right_y = y + right_dy;
3631 int move_x = x + move_dx, move_y = y + move_dy;
3635 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3637 TestIfBadThingTouchesOtherBadThing(x, y);
3639 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3640 MovDir[x][y] = right_dir;
3641 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3642 MovDir[x][y] = left_dir;
3644 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3646 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3649 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3650 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3652 TestIfBadThingTouchesOtherBadThing(x, y);
3654 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3655 MovDir[x][y] = left_dir;
3656 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3657 MovDir[x][y] = right_dir;
3659 if ((element == EL_SPACESHIP ||
3660 element == EL_SP_SNIKSNAK ||
3661 element == EL_SP_ELECTRON)
3662 && MovDir[x][y] != old_move_dir)
3664 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3667 else if (element == EL_YAMYAM)
3669 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3670 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3672 if (can_turn_left && can_turn_right)
3673 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3674 else if (can_turn_left)
3675 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3676 else if (can_turn_right)
3677 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3679 MovDir[x][y] = back_dir;
3681 MovDelay[x][y] = 16 + 16 * RND(3);
3683 else if (element == EL_DARK_YAMYAM)
3685 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3686 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3688 if (can_turn_left && can_turn_right)
3689 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3690 else if (can_turn_left)
3691 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3692 else if (can_turn_right)
3693 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3695 MovDir[x][y] = back_dir;
3697 MovDelay[x][y] = 16 + 16 * RND(3);
3699 else if (element == EL_PACMAN)
3701 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3702 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3704 if (can_turn_left && can_turn_right)
3705 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3706 else if (can_turn_left)
3707 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3708 else if (can_turn_right)
3709 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3711 MovDir[x][y] = back_dir;
3713 MovDelay[x][y] = 6 + RND(40);
3715 else if (element == EL_PIG)
3717 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3718 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3719 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3720 boolean should_turn_left, should_turn_right, should_move_on;
3722 int rnd = RND(rnd_value);
3724 should_turn_left = (can_turn_left &&
3726 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3727 y + back_dy + left_dy)));
3728 should_turn_right = (can_turn_right &&
3730 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3731 y + back_dy + right_dy)));
3732 should_move_on = (can_move_on &&
3735 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3736 y + move_dy + left_dy) ||
3737 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3738 y + move_dy + right_dy)));
3740 if (should_turn_left || should_turn_right || should_move_on)
3742 if (should_turn_left && should_turn_right && should_move_on)
3743 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3744 rnd < 2 * rnd_value / 3 ? right_dir :
3746 else if (should_turn_left && should_turn_right)
3747 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3748 else if (should_turn_left && should_move_on)
3749 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3750 else if (should_turn_right && should_move_on)
3751 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3752 else if (should_turn_left)
3753 MovDir[x][y] = left_dir;
3754 else if (should_turn_right)
3755 MovDir[x][y] = right_dir;
3756 else if (should_move_on)
3757 MovDir[x][y] = old_move_dir;
3759 else if (can_move_on && rnd > rnd_value / 8)
3760 MovDir[x][y] = old_move_dir;
3761 else if (can_turn_left && can_turn_right)
3762 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3763 else if (can_turn_left && rnd > rnd_value / 8)
3764 MovDir[x][y] = left_dir;
3765 else if (can_turn_right && rnd > rnd_value/8)
3766 MovDir[x][y] = right_dir;
3768 MovDir[x][y] = back_dir;
3770 xx = x + move_xy[MovDir[x][y]].x;
3771 yy = y + move_xy[MovDir[x][y]].y;
3773 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3774 MovDir[x][y] = old_move_dir;
3778 else if (element == EL_DRAGON)
3780 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3781 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3782 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3784 int rnd = RND(rnd_value);
3787 if (FrameCounter < 1 && x == 0 && y == 29)
3788 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3791 if (can_move_on && rnd > rnd_value / 8)
3792 MovDir[x][y] = old_move_dir;
3793 else if (can_turn_left && can_turn_right)
3794 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3795 else if (can_turn_left && rnd > rnd_value / 8)
3796 MovDir[x][y] = left_dir;
3797 else if (can_turn_right && rnd > rnd_value / 8)
3798 MovDir[x][y] = right_dir;
3800 MovDir[x][y] = back_dir;
3802 xx = x + move_xy[MovDir[x][y]].x;
3803 yy = y + move_xy[MovDir[x][y]].y;
3806 if (FrameCounter < 1 && x == 0 && y == 29)
3807 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3808 xx, yy, Feld[xx][yy],
3813 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3814 MovDir[x][y] = old_move_dir;
3816 if (!IS_FREE(xx, yy))
3817 MovDir[x][y] = old_move_dir;
3821 if (FrameCounter < 1 && x == 0 && y == 29)
3822 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3827 else if (element == EL_MOLE)
3829 boolean can_move_on =
3830 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3831 IS_AMOEBOID(Feld[move_x][move_y]) ||
3832 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3835 boolean can_turn_left =
3836 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3837 IS_AMOEBOID(Feld[left_x][left_y])));
3839 boolean can_turn_right =
3840 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3841 IS_AMOEBOID(Feld[right_x][right_y])));
3843 if (can_turn_left && can_turn_right)
3844 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3845 else if (can_turn_left)
3846 MovDir[x][y] = left_dir;
3848 MovDir[x][y] = right_dir;
3851 if (MovDir[x][y] != old_move_dir)
3854 else if (element == EL_BALLOON)
3856 MovDir[x][y] = game.balloon_dir;
3859 else if (element == EL_SPRING)
3861 if (MovDir[x][y] & MV_HORIZONTAL &&
3862 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3863 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3864 MovDir[x][y] = MV_NO_MOVING;
3868 else if (element == EL_ROBOT ||
3869 element == EL_SATELLITE ||
3870 element == EL_PENGUIN)
3872 int attr_x = -1, attr_y = -1;
3883 for (i = 0; i < MAX_PLAYERS; i++)
3885 struct PlayerInfo *player = &stored_player[i];
3886 int jx = player->jx, jy = player->jy;
3888 if (!player->active)
3892 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3900 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3906 if (element == EL_PENGUIN)
3909 static int xy[4][2] =
3917 for (i = 0; i < 4; i++)
3919 int ex = x + xy[i % 4][0];
3920 int ey = y + xy[i % 4][1];
3922 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3931 MovDir[x][y] = MV_NO_MOVING;
3933 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3934 else if (attr_x > x)
3935 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3937 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3938 else if (attr_y > y)
3939 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3941 if (element == EL_ROBOT)
3945 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3946 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3947 Moving2Blocked(x, y, &newx, &newy);
3949 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3950 MovDelay[x][y] = 8 + 8 * !RND(3);
3952 MovDelay[x][y] = 16;
3954 else if (element == EL_PENGUIN)
3960 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3962 boolean first_horiz = RND(2);
3963 int new_move_dir = MovDir[x][y];
3966 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3967 Moving2Blocked(x, y, &newx, &newy);
3969 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3973 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3974 Moving2Blocked(x, y, &newx, &newy);
3976 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3979 MovDir[x][y] = old_move_dir;
3983 else /* (element == EL_SATELLITE) */
3989 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3991 boolean first_horiz = RND(2);
3992 int new_move_dir = MovDir[x][y];
3995 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3996 Moving2Blocked(x, y, &newx, &newy);
3998 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
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))
4008 MovDir[x][y] = old_move_dir;
4013 else if (move_pattern == MV_TURNING_LEFT ||
4014 move_pattern == MV_TURNING_RIGHT ||
4015 move_pattern == MV_TURNING_LEFT_RIGHT ||
4016 move_pattern == MV_TURNING_RIGHT_LEFT ||
4017 move_pattern == MV_TURNING_RANDOM ||
4018 move_pattern == MV_ALL_DIRECTIONS)
4020 boolean can_turn_left =
4021 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4022 boolean can_turn_right =
4023 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4025 if (move_pattern == MV_TURNING_LEFT)
4026 MovDir[x][y] = left_dir;
4027 else if (move_pattern == MV_TURNING_RIGHT)
4028 MovDir[x][y] = right_dir;
4029 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4030 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4031 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4032 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4033 else if (move_pattern == MV_TURNING_RANDOM)
4034 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4035 can_turn_right && !can_turn_left ? right_dir :
4036 RND(2) ? left_dir : right_dir);
4037 else if (can_turn_left && can_turn_right)
4038 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4039 else if (can_turn_left)
4040 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4041 else if (can_turn_right)
4042 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4044 MovDir[x][y] = back_dir;
4046 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4048 else if (move_pattern == MV_HORIZONTAL ||
4049 move_pattern == MV_VERTICAL)
4051 if (move_pattern & old_move_dir)
4052 MovDir[x][y] = back_dir;
4053 else if (move_pattern == MV_HORIZONTAL)
4054 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4055 else if (move_pattern == MV_VERTICAL)
4056 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4058 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4060 else if (move_pattern & MV_ANY_DIRECTION)
4062 MovDir[x][y] = move_pattern;
4063 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4065 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4067 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4068 MovDir[x][y] = left_dir;
4069 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4070 MovDir[x][y] = right_dir;
4072 if (MovDir[x][y] != old_move_dir)
4073 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4075 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4077 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4078 MovDir[x][y] = right_dir;
4079 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4080 MovDir[x][y] = left_dir;
4082 if (MovDir[x][y] != old_move_dir)
4083 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4085 else if (move_pattern == MV_TOWARDS_PLAYER ||
4086 move_pattern == MV_AWAY_FROM_PLAYER)
4088 int attr_x = -1, attr_y = -1;
4090 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4101 for (i = 0; i < MAX_PLAYERS; i++)
4103 struct PlayerInfo *player = &stored_player[i];
4104 int jx = player->jx, jy = player->jy;
4106 if (!player->active)
4110 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4118 MovDir[x][y] = MV_NO_MOVING;
4120 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4121 else if (attr_x > x)
4122 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4124 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4125 else if (attr_y > y)
4126 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4128 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4130 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4132 boolean first_horiz = RND(2);
4133 int new_move_dir = MovDir[x][y];
4136 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4137 Moving2Blocked(x, y, &newx, &newy);
4139 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
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))
4149 MovDir[x][y] = old_move_dir;
4152 else if (move_pattern == MV_WHEN_PUSHED ||
4153 move_pattern == MV_WHEN_DROPPED)
4155 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4156 MovDir[x][y] = MV_NO_MOVING;
4160 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4162 static int test_xy[7][2] =
4172 static int test_dir[7] =
4182 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4183 int move_preference = -1000000; /* start with very low preference */
4184 int new_move_dir = MV_NO_MOVING;
4185 int start_test = RND(4);
4188 for (i = 0; i < 4; i++)
4190 int move_dir = test_dir[start_test + i];
4191 int move_dir_preference;
4193 xx = x + test_xy[start_test + i][0];
4194 yy = y + test_xy[start_test + i][1];
4196 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4197 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4199 new_move_dir = move_dir;
4204 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4207 move_dir_preference = -1 * RunnerVisit[xx][yy];
4208 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4209 move_dir_preference = PlayerVisit[xx][yy];
4211 if (move_dir_preference > move_preference)
4213 /* prefer field that has not been visited for the longest time */
4214 move_preference = move_dir_preference;
4215 new_move_dir = move_dir;
4217 else if (move_dir_preference == move_preference &&
4218 move_dir == old_move_dir)
4220 /* prefer last direction when all directions are preferred equally */
4221 move_preference = move_dir_preference;
4222 new_move_dir = move_dir;
4226 MovDir[x][y] = new_move_dir;
4227 if (old_move_dir != new_move_dir)
4232 static void TurnRound(int x, int y)
4234 int direction = MovDir[x][y];
4237 GfxDir[x][y] = MovDir[x][y];
4243 GfxDir[x][y] = MovDir[x][y];
4246 if (direction != MovDir[x][y])
4251 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4254 GfxAction[x][y] = ACTION_WAITING;
4258 static boolean JustBeingPushed(int x, int y)
4262 for (i = 0; i < MAX_PLAYERS; i++)
4264 struct PlayerInfo *player = &stored_player[i];
4266 if (player->active && player->is_pushing && player->MovPos)
4268 int next_jx = player->jx + (player->jx - player->last_jx);
4269 int next_jy = player->jy + (player->jy - player->last_jy);
4271 if (x == next_jx && y == next_jy)
4279 void StartMoving(int x, int y)
4281 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4282 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4283 int element = Feld[x][y];
4289 if (MovDelay[x][y] == 0)
4290 GfxAction[x][y] = ACTION_DEFAULT;
4292 /* !!! this should be handled more generic (not only for mole) !!! */
4293 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4294 GfxAction[x][y] = ACTION_DEFAULT;
4297 if (CAN_FALL(element) && y < lev_fieldy - 1)
4299 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4300 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4301 if (JustBeingPushed(x, y))
4304 if (element == EL_QUICKSAND_FULL)
4306 if (IS_FREE(x, y + 1))
4308 InitMovingField(x, y, MV_DOWN);
4309 started_moving = TRUE;
4311 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4312 Store[x][y] = EL_ROCK;
4314 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4316 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4319 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4321 if (!MovDelay[x][y])
4322 MovDelay[x][y] = TILEY + 1;
4331 Feld[x][y] = EL_QUICKSAND_EMPTY;
4332 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4333 Store[x][y + 1] = Store[x][y];
4336 PlayLevelSoundAction(x, y, ACTION_FILLING);
4338 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4342 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4343 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4345 InitMovingField(x, y, MV_DOWN);
4346 started_moving = TRUE;
4348 Feld[x][y] = EL_QUICKSAND_FILLING;
4349 Store[x][y] = element;
4351 PlayLevelSoundAction(x, y, ACTION_FILLING);
4353 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4356 else if (element == EL_MAGIC_WALL_FULL)
4358 if (IS_FREE(x, y + 1))
4360 InitMovingField(x, y, MV_DOWN);
4361 started_moving = TRUE;
4363 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4364 Store[x][y] = EL_CHANGED(Store[x][y]);
4366 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4368 if (!MovDelay[x][y])
4369 MovDelay[x][y] = TILEY/4 + 1;
4378 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4379 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4380 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4384 else if (element == EL_BD_MAGIC_WALL_FULL)
4386 if (IS_FREE(x, y + 1))
4388 InitMovingField(x, y, MV_DOWN);
4389 started_moving = TRUE;
4391 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4392 Store[x][y] = EL_CHANGED2(Store[x][y]);
4394 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4396 if (!MovDelay[x][y])
4397 MovDelay[x][y] = TILEY/4 + 1;
4406 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4407 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4408 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4412 else if (CAN_PASS_MAGIC_WALL(element) &&
4413 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4414 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4416 InitMovingField(x, y, MV_DOWN);
4417 started_moving = TRUE;
4420 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4421 EL_BD_MAGIC_WALL_FILLING);
4422 Store[x][y] = element;
4425 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4427 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4432 InitMovingField(x, y, MV_DOWN);
4433 started_moving = TRUE;
4435 Store[x][y] = EL_ACID;
4437 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4438 GfxAction[x][y + 1] = ACTION_ACTIVE;
4442 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4443 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4444 (Feld[x][y + 1] == EL_BLOCKED)) ||
4445 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4446 CAN_SMASH(element) && WasJustFalling[x][y] &&
4447 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4451 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4452 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4453 WasJustMoving[x][y] && !Pushed[x][y + 1])
4455 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4456 WasJustMoving[x][y])
4461 /* this is needed for a special case not covered by calling "Impact()"
4462 from "ContinueMoving()": if an element moves to a tile directly below
4463 another element which was just falling on that tile (which was empty
4464 in the previous frame), the falling element above would just stop
4465 instead of smashing the element below (in previous version, the above
4466 element was just checked for "moving" instead of "falling", resulting
4467 in incorrect smashes caused by horizontal movement of the above
4468 element; also, the case of the player being the element to smash was
4469 simply not covered here... :-/ ) */
4472 WasJustMoving[x][y] = 0;
4473 WasJustFalling[x][y] = 0;
4478 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4480 if (MovDir[x][y] == MV_NO_MOVING)
4482 InitMovingField(x, y, MV_DOWN);
4483 started_moving = TRUE;
4486 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4488 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4489 MovDir[x][y] = MV_DOWN;
4491 InitMovingField(x, y, MV_DOWN);
4492 started_moving = TRUE;
4494 else if (element == EL_AMOEBA_DROP)
4496 Feld[x][y] = EL_AMOEBA_GROWING;
4497 Store[x][y] = EL_AMOEBA_WET;
4499 /* Store[x][y + 1] must be zero, because:
4500 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4503 #if OLD_GAME_BEHAVIOUR
4504 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4506 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4507 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4508 element != EL_DX_SUPABOMB)
4511 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4512 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4513 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4514 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4517 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4518 (IS_FREE(x - 1, y + 1) ||
4519 Feld[x - 1][y + 1] == EL_ACID));
4520 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4521 (IS_FREE(x + 1, y + 1) ||
4522 Feld[x + 1][y + 1] == EL_ACID));
4523 boolean can_fall_any = (can_fall_left || can_fall_right);
4524 boolean can_fall_both = (can_fall_left && can_fall_right);
4526 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4528 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4530 if (slippery_type == SLIPPERY_ONLY_LEFT)
4531 can_fall_right = FALSE;
4532 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4533 can_fall_left = FALSE;
4534 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4535 can_fall_right = FALSE;
4536 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4537 can_fall_left = FALSE;
4539 can_fall_any = (can_fall_left || can_fall_right);
4540 can_fall_both = (can_fall_left && can_fall_right);
4545 if (can_fall_both &&
4546 (game.emulation != EMU_BOULDERDASH &&
4547 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4548 can_fall_left = !(can_fall_right = RND(2));
4550 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4551 started_moving = TRUE;
4554 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4556 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4557 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4558 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4559 int belt_dir = game.belt_dir[belt_nr];
4561 if ((belt_dir == MV_LEFT && left_is_free) ||
4562 (belt_dir == MV_RIGHT && right_is_free))
4564 InitMovingField(x, y, belt_dir);
4565 started_moving = TRUE;
4567 GfxAction[x][y] = ACTION_DEFAULT;
4572 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4573 if (CAN_MOVE(element) && !started_moving)
4575 int move_pattern = element_info[element].move_pattern;
4578 Moving2Blocked(x, y, &newx, &newy);
4581 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4584 if ((element == EL_SATELLITE ||
4585 element == EL_BALLOON ||
4586 element == EL_SPRING)
4587 && JustBeingPushed(x, y))
4592 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4593 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4594 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4597 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4598 element, element_info[element].token_name,
4599 WasJustMoving[x][y],
4600 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4601 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4602 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4603 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4607 WasJustMoving[x][y] = 0;
4610 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4613 if (Feld[x][y] != element) /* element has changed */
4615 element = Feld[x][y];
4616 move_pattern = element_info[element].move_pattern;
4618 if (!CAN_MOVE(element))
4622 if (Feld[x][y] != element) /* element has changed */
4630 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4631 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4633 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4635 Moving2Blocked(x, y, &newx, &newy);
4636 if (Feld[newx][newy] == EL_BLOCKED)
4637 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4643 if (FrameCounter < 1 && x == 0 && y == 29)
4644 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4647 if (!MovDelay[x][y]) /* start new movement phase */
4649 /* all objects that can change their move direction after each step
4650 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4652 if (element != EL_YAMYAM &&
4653 element != EL_DARK_YAMYAM &&
4654 element != EL_PACMAN &&
4655 !(move_pattern & MV_ANY_DIRECTION) &&
4656 move_pattern != MV_TURNING_LEFT &&
4657 move_pattern != MV_TURNING_RIGHT &&
4658 move_pattern != MV_TURNING_LEFT_RIGHT &&
4659 move_pattern != MV_TURNING_RIGHT_LEFT &&
4660 move_pattern != MV_TURNING_RANDOM)
4665 if (FrameCounter < 1 && x == 0 && y == 29)
4666 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4669 if (MovDelay[x][y] && (element == EL_BUG ||
4670 element == EL_SPACESHIP ||
4671 element == EL_SP_SNIKSNAK ||
4672 element == EL_SP_ELECTRON ||
4673 element == EL_MOLE))
4674 DrawLevelField(x, y);
4678 if (MovDelay[x][y]) /* wait some time before next movement */
4683 if (element == EL_YAMYAM)
4686 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4687 DrawLevelElementAnimation(x, y, element);
4691 if (MovDelay[x][y]) /* element still has to wait some time */
4694 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4695 ResetGfxAnimation(x, y);
4699 if (GfxAction[x][y] != ACTION_WAITING)
4700 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4702 GfxAction[x][y] = ACTION_WAITING;
4706 if (element == EL_ROBOT ||
4708 element == EL_PACMAN ||
4710 element == EL_YAMYAM ||
4711 element == EL_DARK_YAMYAM)
4714 DrawLevelElementAnimation(x, y, element);
4716 DrawLevelElementAnimationIfNeeded(x, y, element);
4718 PlayLevelSoundAction(x, y, ACTION_WAITING);
4720 else if (element == EL_SP_ELECTRON)
4721 DrawLevelElementAnimationIfNeeded(x, y, element);
4722 else if (element == EL_DRAGON)
4725 int dir = MovDir[x][y];
4726 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4727 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4728 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4729 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4730 dir == MV_UP ? IMG_FLAMES_1_UP :
4731 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4732 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4735 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4738 GfxAction[x][y] = ACTION_ATTACKING;
4740 if (IS_PLAYER(x, y))
4741 DrawPlayerField(x, y);
4743 DrawLevelField(x, y);
4745 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4747 for (i = 1; i <= 3; i++)
4749 int xx = x + i * dx;
4750 int yy = y + i * dy;
4751 int sx = SCREENX(xx);
4752 int sy = SCREENY(yy);
4753 int flame_graphic = graphic + (i - 1);
4755 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4760 int flamed = MovingOrBlocked2Element(xx, yy);
4762 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4765 RemoveMovingField(xx, yy);
4767 Feld[xx][yy] = EL_FLAMES;
4768 if (IN_SCR_FIELD(sx, sy))
4770 DrawLevelFieldCrumbledSand(xx, yy);
4771 DrawGraphic(sx, sy, flame_graphic, frame);
4776 if (Feld[xx][yy] == EL_FLAMES)
4777 Feld[xx][yy] = EL_EMPTY;
4778 DrawLevelField(xx, yy);
4783 if (MovDelay[x][y]) /* element still has to wait some time */
4785 PlayLevelSoundAction(x, y, ACTION_WAITING);
4791 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4792 for all other elements GfxAction will be set by InitMovingField() */
4793 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4794 GfxAction[x][y] = ACTION_MOVING;
4798 /* now make next step */
4800 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4802 if (DONT_COLLIDE_WITH(element) &&
4803 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4804 !PLAYER_ENEMY_PROTECTED(newx, newy))
4807 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4811 /* player killed by element which is deadly when colliding with */
4813 KillHero(PLAYERINFO(newx, newy));
4818 else if ((element == EL_PENGUIN ||
4819 element == EL_ROBOT ||
4820 element == EL_SATELLITE ||
4821 element == EL_BALLOON ||
4822 IS_CUSTOM_ELEMENT(element)) &&
4823 IN_LEV_FIELD(newx, newy) &&
4824 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4827 Store[x][y] = EL_ACID;
4829 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4831 if (Feld[newx][newy] == EL_EXIT_OPEN)
4835 DrawLevelField(x, y);
4837 Feld[x][y] = EL_EMPTY;
4838 DrawLevelField(x, y);
4841 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4842 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4843 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4845 local_player->friends_still_needed--;
4846 if (!local_player->friends_still_needed &&
4847 !local_player->GameOver && AllPlayersGone)
4848 local_player->LevelSolved = local_player->GameOver = TRUE;
4852 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4854 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4855 DrawLevelField(newx, newy);
4857 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4859 else if (!IS_FREE(newx, newy))
4861 GfxAction[x][y] = ACTION_WAITING;
4863 if (IS_PLAYER(x, y))
4864 DrawPlayerField(x, y);
4866 DrawLevelField(x, y);
4871 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4873 if (IS_FOOD_PIG(Feld[newx][newy]))
4875 if (IS_MOVING(newx, newy))
4876 RemoveMovingField(newx, newy);
4879 Feld[newx][newy] = EL_EMPTY;
4880 DrawLevelField(newx, newy);
4883 PlayLevelSound(x, y, SND_PIG_DIGGING);
4885 else if (!IS_FREE(newx, newy))
4887 if (IS_PLAYER(x, y))
4888 DrawPlayerField(x, y);
4890 DrawLevelField(x, y);
4899 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4902 else if (IS_CUSTOM_ELEMENT(element) &&
4903 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4907 !IS_FREE(newx, newy)
4912 int new_element = Feld[newx][newy];
4915 printf("::: '%s' digs '%s' [%d]\n",
4916 element_info[element].token_name,
4917 element_info[Feld[newx][newy]].token_name,
4918 StorePlayer[newx][newy]);
4921 if (!IS_FREE(newx, newy))
4923 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4924 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4927 /* no element can dig solid indestructible elements */
4928 if (IS_INDESTRUCTIBLE(new_element) &&
4929 !IS_DIGGABLE(new_element) &&
4930 !IS_COLLECTIBLE(new_element))
4933 if (AmoebaNr[newx][newy] &&
4934 (new_element == EL_AMOEBA_FULL ||
4935 new_element == EL_BD_AMOEBA ||
4936 new_element == EL_AMOEBA_GROWING))
4938 AmoebaCnt[AmoebaNr[newx][newy]]--;
4939 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4942 if (IS_MOVING(newx, newy))
4943 RemoveMovingField(newx, newy);
4946 RemoveField(newx, newy);
4947 DrawLevelField(newx, newy);
4950 PlayLevelSoundAction(x, y, action);
4953 if (new_element == element_info[element].move_enter_element)
4954 element_info[element].can_leave_element = TRUE;
4956 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4958 RunnerVisit[x][y] = FrameCounter;
4959 PlayerVisit[x][y] /= 8; /* expire player visit path */
4965 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4967 if (!IS_FREE(newx, newy))
4969 if (IS_PLAYER(x, y))
4970 DrawPlayerField(x, y);
4972 DrawLevelField(x, y);
4978 boolean wanna_flame = !RND(10);
4979 int dx = newx - x, dy = newy - y;
4980 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4981 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4982 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4983 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4984 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4985 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4988 IS_CLASSIC_ENEMY(element1) ||
4989 IS_CLASSIC_ENEMY(element2)) &&
4990 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4991 element1 != EL_FLAMES && element2 != EL_FLAMES)
4994 ResetGfxAnimation(x, y);
4995 GfxAction[x][y] = ACTION_ATTACKING;
4998 if (IS_PLAYER(x, y))
4999 DrawPlayerField(x, y);
5001 DrawLevelField(x, y);
5003 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5005 MovDelay[x][y] = 50;
5007 Feld[newx][newy] = EL_FLAMES;
5008 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5009 Feld[newx1][newy1] = EL_FLAMES;
5010 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5011 Feld[newx2][newy2] = EL_FLAMES;
5017 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5018 Feld[newx][newy] == EL_DIAMOND)
5020 if (IS_MOVING(newx, newy))
5021 RemoveMovingField(newx, newy);
5024 Feld[newx][newy] = EL_EMPTY;
5025 DrawLevelField(newx, newy);
5028 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5030 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5031 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5033 if (AmoebaNr[newx][newy])
5035 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5036 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5037 Feld[newx][newy] == EL_BD_AMOEBA)
5038 AmoebaCnt[AmoebaNr[newx][newy]]--;
5041 if (IS_MOVING(newx, newy))
5042 RemoveMovingField(newx, newy);
5045 Feld[newx][newy] = EL_EMPTY;
5046 DrawLevelField(newx, newy);
5049 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5051 else if ((element == EL_PACMAN || element == EL_MOLE)
5052 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5054 if (AmoebaNr[newx][newy])
5056 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5057 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5058 Feld[newx][newy] == EL_BD_AMOEBA)
5059 AmoebaCnt[AmoebaNr[newx][newy]]--;
5062 if (element == EL_MOLE)
5064 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5065 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5067 ResetGfxAnimation(x, y);
5068 GfxAction[x][y] = ACTION_DIGGING;
5069 DrawLevelField(x, y);
5071 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5073 return; /* wait for shrinking amoeba */
5075 else /* element == EL_PACMAN */
5077 Feld[newx][newy] = EL_EMPTY;
5078 DrawLevelField(newx, newy);
5079 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5082 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5083 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5084 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5086 /* wait for shrinking amoeba to completely disappear */
5089 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5091 /* object was running against a wall */
5096 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5097 DrawLevelElementAnimation(x, y, element);
5099 if (element == EL_BUG ||
5100 element == EL_SPACESHIP ||
5101 element == EL_SP_SNIKSNAK)
5102 DrawLevelField(x, y);
5103 else if (element == EL_MOLE)
5104 DrawLevelField(x, y);
5105 else if (element == EL_BD_BUTTERFLY ||
5106 element == EL_BD_FIREFLY)
5107 DrawLevelElementAnimationIfNeeded(x, y, element);
5108 else if (element == EL_SATELLITE)
5109 DrawLevelElementAnimationIfNeeded(x, y, element);
5110 else if (element == EL_SP_ELECTRON)
5111 DrawLevelElementAnimationIfNeeded(x, y, element);
5114 if (DONT_TOUCH(element))
5115 TestIfBadThingTouchesHero(x, y);
5118 PlayLevelSoundAction(x, y, ACTION_WAITING);
5124 InitMovingField(x, y, MovDir[x][y]);
5126 PlayLevelSoundAction(x, y, ACTION_MOVING);
5130 ContinueMoving(x, y);
5133 void ContinueMoving(int x, int y)
5135 int element = Feld[x][y];
5136 struct ElementInfo *ei = &element_info[element];
5137 int direction = MovDir[x][y];
5138 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5139 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5140 int newx = x + dx, newy = y + dy;
5142 int nextx = newx + dx, nexty = newy + dy;
5144 boolean pushed = Pushed[x][y];
5146 MovPos[x][y] += getElementMoveStepsize(x, y);
5148 if (pushed) /* special case: moving object pushed by player */
5149 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5151 if (ABS(MovPos[x][y]) < TILEX)
5153 DrawLevelField(x, y);
5155 return; /* element is still moving */
5158 /* element reached destination field */
5160 Feld[x][y] = EL_EMPTY;
5161 Feld[newx][newy] = element;
5162 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5164 if (element == EL_MOLE)
5166 Feld[x][y] = EL_SAND;
5168 DrawLevelFieldCrumbledSandNeighbours(x, y);
5170 else if (element == EL_QUICKSAND_FILLING)
5172 element = Feld[newx][newy] = get_next_element(element);
5173 Store[newx][newy] = Store[x][y];
5175 else if (element == EL_QUICKSAND_EMPTYING)
5177 Feld[x][y] = get_next_element(element);
5178 element = Feld[newx][newy] = Store[x][y];
5180 else if (element == EL_MAGIC_WALL_FILLING)
5182 element = Feld[newx][newy] = get_next_element(element);
5183 if (!game.magic_wall_active)
5184 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5185 Store[newx][newy] = Store[x][y];
5187 else if (element == EL_MAGIC_WALL_EMPTYING)
5189 Feld[x][y] = get_next_element(element);
5190 if (!game.magic_wall_active)
5191 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5192 element = Feld[newx][newy] = Store[x][y];
5194 else if (element == EL_BD_MAGIC_WALL_FILLING)
5196 element = Feld[newx][newy] = get_next_element(element);
5197 if (!game.magic_wall_active)
5198 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5199 Store[newx][newy] = Store[x][y];
5201 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5203 Feld[x][y] = get_next_element(element);
5204 if (!game.magic_wall_active)
5205 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5206 element = Feld[newx][newy] = Store[x][y];
5208 else if (element == EL_AMOEBA_DROPPING)
5210 Feld[x][y] = get_next_element(element);
5211 element = Feld[newx][newy] = Store[x][y];
5213 else if (element == EL_SOKOBAN_OBJECT)
5216 Feld[x][y] = Back[x][y];
5218 if (Back[newx][newy])
5219 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5221 Back[x][y] = Back[newx][newy] = 0;
5223 else if (Store[x][y] == EL_ACID)
5225 element = Feld[newx][newy] = EL_ACID;
5229 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5230 MovDelay[newx][newy] = 0;
5232 /* copy element change control values to new field */
5233 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5234 ChangePage[newx][newy] = ChangePage[x][y];
5235 Changed[newx][newy] = Changed[x][y];
5236 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5238 ChangeDelay[x][y] = 0;
5239 ChangePage[x][y] = -1;
5240 Changed[x][y] = CE_BITMASK_DEFAULT;
5241 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5243 /* copy animation control values to new field */
5244 GfxFrame[newx][newy] = GfxFrame[x][y];
5245 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5246 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5247 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5249 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5251 ResetGfxAnimation(x, y); /* reset animation values for old field */
5254 /* some elements can leave other elements behind after moving */
5255 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5256 ei->move_leave_element != EL_EMPTY &&
5257 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5258 ei->can_leave_element_last))
5260 Feld[x][y] = ei->move_leave_element;
5261 InitField(x, y, FALSE);
5263 if (GFX_CRUMBLED(Feld[x][y]))
5264 DrawLevelFieldCrumbledSandNeighbours(x, y);
5267 ei->can_leave_element_last = ei->can_leave_element;
5268 ei->can_leave_element = FALSE;
5272 /* 2.1.1 (does not work correctly for spring) */
5273 if (!CAN_MOVE(element))
5274 MovDir[newx][newy] = 0;
5278 /* (does not work for falling objects that slide horizontally) */
5279 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5280 MovDir[newx][newy] = 0;
5283 if (!CAN_MOVE(element) ||
5284 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5285 MovDir[newx][newy] = 0;
5288 if (!CAN_MOVE(element) ||
5289 (CAN_FALL(element) && direction == MV_DOWN))
5290 GfxDir[x][y] = MovDir[newx][newy] = 0;
5295 DrawLevelField(x, y);
5296 DrawLevelField(newx, newy);
5298 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5300 /* prevent pushed element from moving on in pushed direction */
5301 if (pushed && CAN_MOVE(element) &&
5302 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5303 !(element_info[element].move_pattern & direction))
5304 TurnRound(newx, newy);
5306 if (!pushed) /* special case: moving object pushed by player */
5308 WasJustMoving[newx][newy] = 3;
5310 if (CAN_FALL(element) && direction == MV_DOWN)
5311 WasJustFalling[newx][newy] = 3;
5314 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5316 TestIfBadThingTouchesHero(newx, newy);
5317 TestIfBadThingTouchesFriend(newx, newy);
5319 if (!IS_CUSTOM_ELEMENT(element))
5320 TestIfBadThingTouchesOtherBadThing(newx, newy);
5322 else if (element == EL_PENGUIN)
5323 TestIfFriendTouchesBadThing(newx, newy);
5325 if (CAN_FALL(element) && direction == MV_DOWN &&
5326 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5330 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5334 if (ChangePage[newx][newy] != -1) /* delayed change */
5335 ChangeElement(newx, newy, ChangePage[newx][newy]);
5340 TestIfElementHitsCustomElement(newx, newy, direction);
5344 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5346 int hitting_element = Feld[newx][newy];
5348 /* !!! fix side (direction) orientation here and elsewhere !!! */
5349 CheckElementSideChange(newx, newy, hitting_element,
5350 direction, CE_HITTING_SOMETHING, -1);
5353 if (IN_LEV_FIELD(nextx, nexty))
5355 int opposite_direction = MV_DIR_OPPOSITE(direction);
5356 int hitting_side = direction;
5357 int touched_side = opposite_direction;
5358 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5359 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5360 MovDir[nextx][nexty] != direction ||
5361 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5367 CheckElementSideChange(nextx, nexty, touched_element,
5368 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5370 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5371 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5373 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5375 struct ElementChangeInfo *change =
5376 &element_info[hitting_element].change_page[i];
5378 if (change->can_change &&
5379 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5380 change->sides & touched_side &&
5381 change->trigger_element == touched_element)
5383 CheckElementSideChange(newx, newy, hitting_element,
5384 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5390 if (IS_CUSTOM_ELEMENT(touched_element) &&
5391 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5393 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5395 struct ElementChangeInfo *change =
5396 &element_info[touched_element].change_page[i];
5398 if (change->can_change &&
5399 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5400 change->sides & hitting_side &&
5401 change->trigger_element == hitting_element)
5403 CheckElementSideChange(nextx, nexty, touched_element,
5404 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5415 TestIfPlayerTouchesCustomElement(newx, newy);
5416 TestIfElementTouchesCustomElement(newx, newy);
5419 int AmoebeNachbarNr(int ax, int ay)
5422 int element = Feld[ax][ay];
5424 static int xy[4][2] =
5432 for (i = 0; i < 4; i++)
5434 int x = ax + xy[i][0];
5435 int y = ay + xy[i][1];
5437 if (!IN_LEV_FIELD(x, y))
5440 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5441 group_nr = AmoebaNr[x][y];
5447 void AmoebenVereinigen(int ax, int ay)
5449 int i, x, y, xx, yy;
5450 int new_group_nr = AmoebaNr[ax][ay];
5451 static int xy[4][2] =
5459 if (new_group_nr == 0)
5462 for (i = 0; i < 4; i++)
5467 if (!IN_LEV_FIELD(x, y))
5470 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5471 Feld[x][y] == EL_BD_AMOEBA ||
5472 Feld[x][y] == EL_AMOEBA_DEAD) &&
5473 AmoebaNr[x][y] != new_group_nr)
5475 int old_group_nr = AmoebaNr[x][y];
5477 if (old_group_nr == 0)
5480 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5481 AmoebaCnt[old_group_nr] = 0;
5482 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5483 AmoebaCnt2[old_group_nr] = 0;
5485 for (yy = 0; yy < lev_fieldy; yy++)
5487 for (xx = 0; xx < lev_fieldx; xx++)
5489 if (AmoebaNr[xx][yy] == old_group_nr)
5490 AmoebaNr[xx][yy] = new_group_nr;
5497 void AmoebeUmwandeln(int ax, int ay)
5501 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5503 int group_nr = AmoebaNr[ax][ay];
5508 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5509 printf("AmoebeUmwandeln(): This should never happen!\n");
5514 for (y = 0; y < lev_fieldy; y++)
5516 for (x = 0; x < lev_fieldx; x++)
5518 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5521 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5525 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5526 SND_AMOEBA_TURNING_TO_GEM :
5527 SND_AMOEBA_TURNING_TO_ROCK));
5532 static int xy[4][2] =
5540 for (i = 0; i < 4; i++)
5545 if (!IN_LEV_FIELD(x, y))
5548 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5550 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5551 SND_AMOEBA_TURNING_TO_GEM :
5552 SND_AMOEBA_TURNING_TO_ROCK));
5559 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5562 int group_nr = AmoebaNr[ax][ay];
5563 boolean done = FALSE;
5568 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5569 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5574 for (y = 0; y < lev_fieldy; y++)
5576 for (x = 0; x < lev_fieldx; x++)
5578 if (AmoebaNr[x][y] == group_nr &&
5579 (Feld[x][y] == EL_AMOEBA_DEAD ||
5580 Feld[x][y] == EL_BD_AMOEBA ||
5581 Feld[x][y] == EL_AMOEBA_GROWING))
5584 Feld[x][y] = new_element;
5585 InitField(x, y, FALSE);
5586 DrawLevelField(x, y);
5593 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5594 SND_BD_AMOEBA_TURNING_TO_ROCK :
5595 SND_BD_AMOEBA_TURNING_TO_GEM));
5598 void AmoebeWaechst(int x, int y)
5600 static unsigned long sound_delay = 0;
5601 static unsigned long sound_delay_value = 0;
5603 if (!MovDelay[x][y]) /* start new growing cycle */
5607 if (DelayReached(&sound_delay, sound_delay_value))
5610 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5612 if (Store[x][y] == EL_BD_AMOEBA)
5613 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5615 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5617 sound_delay_value = 30;
5621 if (MovDelay[x][y]) /* wait some time before growing bigger */
5624 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5626 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5627 6 - MovDelay[x][y]);
5629 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5632 if (!MovDelay[x][y])
5634 Feld[x][y] = Store[x][y];
5636 DrawLevelField(x, y);
5641 void AmoebaDisappearing(int x, int y)
5643 static unsigned long sound_delay = 0;
5644 static unsigned long sound_delay_value = 0;
5646 if (!MovDelay[x][y]) /* start new shrinking cycle */
5650 if (DelayReached(&sound_delay, sound_delay_value))
5651 sound_delay_value = 30;
5654 if (MovDelay[x][y]) /* wait some time before shrinking */
5657 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5659 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5660 6 - MovDelay[x][y]);
5662 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5665 if (!MovDelay[x][y])
5667 Feld[x][y] = EL_EMPTY;
5668 DrawLevelField(x, y);
5670 /* don't let mole enter this field in this cycle;
5671 (give priority to objects falling to this field from above) */
5677 void AmoebeAbleger(int ax, int ay)
5680 int element = Feld[ax][ay];
5681 int graphic = el2img(element);
5682 int newax = ax, neway = ay;
5683 static int xy[4][2] =
5691 if (!level.amoeba_speed)
5693 Feld[ax][ay] = EL_AMOEBA_DEAD;
5694 DrawLevelField(ax, ay);
5698 if (IS_ANIMATED(graphic))
5699 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5701 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5702 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5704 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5707 if (MovDelay[ax][ay])
5711 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5714 int x = ax + xy[start][0];
5715 int y = ay + xy[start][1];
5717 if (!IN_LEV_FIELD(x, y))
5720 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5721 if (IS_FREE(x, y) ||
5722 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5728 if (newax == ax && neway == ay)
5731 else /* normal or "filled" (BD style) amoeba */
5734 boolean waiting_for_player = FALSE;
5736 for (i = 0; i < 4; i++)
5738 int j = (start + i) % 4;
5739 int x = ax + xy[j][0];
5740 int y = ay + xy[j][1];
5742 if (!IN_LEV_FIELD(x, y))
5745 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5746 if (IS_FREE(x, y) ||
5747 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5753 else if (IS_PLAYER(x, y))
5754 waiting_for_player = TRUE;
5757 if (newax == ax && neway == ay) /* amoeba cannot grow */
5759 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5761 Feld[ax][ay] = EL_AMOEBA_DEAD;
5762 DrawLevelField(ax, ay);
5763 AmoebaCnt[AmoebaNr[ax][ay]]--;
5765 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5767 if (element == EL_AMOEBA_FULL)
5768 AmoebeUmwandeln(ax, ay);
5769 else if (element == EL_BD_AMOEBA)
5770 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5775 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5777 /* amoeba gets larger by growing in some direction */
5779 int new_group_nr = AmoebaNr[ax][ay];
5782 if (new_group_nr == 0)
5784 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5785 printf("AmoebeAbleger(): This should never happen!\n");
5790 AmoebaNr[newax][neway] = new_group_nr;
5791 AmoebaCnt[new_group_nr]++;
5792 AmoebaCnt2[new_group_nr]++;
5794 /* if amoeba touches other amoeba(s) after growing, unify them */
5795 AmoebenVereinigen(newax, neway);
5797 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5799 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5805 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5806 (neway == lev_fieldy - 1 && newax != ax))
5808 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5809 Store[newax][neway] = element;
5811 else if (neway == ay)
5813 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5815 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5817 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5822 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5823 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5824 Store[ax][ay] = EL_AMOEBA_DROP;
5825 ContinueMoving(ax, ay);
5829 DrawLevelField(newax, neway);
5832 void Life(int ax, int ay)
5835 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5837 int element = Feld[ax][ay];
5838 int graphic = el2img(element);
5839 boolean changed = FALSE;
5841 if (IS_ANIMATED(graphic))
5842 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5847 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5848 MovDelay[ax][ay] = life_time;
5850 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5853 if (MovDelay[ax][ay])
5857 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5859 int xx = ax+x1, yy = ay+y1;
5862 if (!IN_LEV_FIELD(xx, yy))
5865 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5867 int x = xx+x2, y = yy+y2;
5869 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5872 if (((Feld[x][y] == element ||
5873 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5875 (IS_FREE(x, y) && Stop[x][y]))
5879 if (xx == ax && yy == ay) /* field in the middle */
5881 if (nachbarn < life[0] || nachbarn > life[1])
5883 Feld[xx][yy] = EL_EMPTY;
5885 DrawLevelField(xx, yy);
5886 Stop[xx][yy] = TRUE;
5890 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5891 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5892 { /* free border field */
5893 if (nachbarn >= life[2] && nachbarn <= life[3])
5895 Feld[xx][yy] = element;
5896 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5898 DrawLevelField(xx, yy);
5899 Stop[xx][yy] = TRUE;
5906 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5907 SND_GAME_OF_LIFE_GROWING);
5910 static void InitRobotWheel(int x, int y)
5912 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5915 static void RunRobotWheel(int x, int y)
5917 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5920 static void StopRobotWheel(int x, int y)
5922 if (ZX == x && ZY == y)
5926 static void InitTimegateWheel(int x, int y)
5928 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5931 static void RunTimegateWheel(int x, int y)
5933 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5936 void CheckExit(int x, int y)
5938 if (local_player->gems_still_needed > 0 ||
5939 local_player->sokobanfields_still_needed > 0 ||
5940 local_player->lights_still_needed > 0)
5942 int element = Feld[x][y];
5943 int graphic = el2img(element);
5945 if (IS_ANIMATED(graphic))
5946 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5951 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5954 Feld[x][y] = EL_EXIT_OPENING;
5956 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5959 void CheckExitSP(int x, int y)
5961 if (local_player->gems_still_needed > 0)
5963 int element = Feld[x][y];
5964 int graphic = el2img(element);
5966 if (IS_ANIMATED(graphic))
5967 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5972 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5975 Feld[x][y] = EL_SP_EXIT_OPENING;
5977 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5980 static void CloseAllOpenTimegates()
5984 for (y = 0; y < lev_fieldy; y++)
5986 for (x = 0; x < lev_fieldx; x++)
5988 int element = Feld[x][y];
5990 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5992 Feld[x][y] = EL_TIMEGATE_CLOSING;
5994 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5996 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6003 void EdelsteinFunkeln(int x, int y)
6005 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6008 if (Feld[x][y] == EL_BD_DIAMOND)
6011 if (MovDelay[x][y] == 0) /* next animation frame */
6012 MovDelay[x][y] = 11 * !SimpleRND(500);
6014 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6018 if (setup.direct_draw && MovDelay[x][y])
6019 SetDrawtoField(DRAW_BUFFERED);
6021 DrawLevelElementAnimation(x, y, Feld[x][y]);
6023 if (MovDelay[x][y] != 0)
6025 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6026 10 - MovDelay[x][y]);
6028 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6030 if (setup.direct_draw)
6034 dest_x = FX + SCREENX(x) * TILEX;
6035 dest_y = FY + SCREENY(y) * TILEY;
6037 BlitBitmap(drawto_field, window,
6038 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6039 SetDrawtoField(DRAW_DIRECT);
6045 void MauerWaechst(int x, int y)
6049 if (!MovDelay[x][y]) /* next animation frame */
6050 MovDelay[x][y] = 3 * delay;
6052 if (MovDelay[x][y]) /* wait some time before next frame */
6056 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6058 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6059 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6061 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6064 if (!MovDelay[x][y])
6066 if (MovDir[x][y] == MV_LEFT)
6068 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6069 DrawLevelField(x - 1, y);
6071 else if (MovDir[x][y] == MV_RIGHT)
6073 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6074 DrawLevelField(x + 1, y);
6076 else if (MovDir[x][y] == MV_UP)
6078 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6079 DrawLevelField(x, y - 1);
6083 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6084 DrawLevelField(x, y + 1);
6087 Feld[x][y] = Store[x][y];
6089 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6090 DrawLevelField(x, y);
6095 void MauerAbleger(int ax, int ay)
6097 int element = Feld[ax][ay];
6098 int graphic = el2img(element);
6099 boolean oben_frei = FALSE, unten_frei = FALSE;
6100 boolean links_frei = FALSE, rechts_frei = FALSE;
6101 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6102 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6103 boolean new_wall = FALSE;
6105 if (IS_ANIMATED(graphic))
6106 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6108 if (!MovDelay[ax][ay]) /* start building new wall */
6109 MovDelay[ax][ay] = 6;
6111 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6114 if (MovDelay[ax][ay])
6118 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6120 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6122 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6124 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6127 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6128 element == EL_EXPANDABLE_WALL_ANY)
6132 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6133 Store[ax][ay-1] = element;
6134 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6135 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6136 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6137 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6142 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6143 Store[ax][ay+1] = element;
6144 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6145 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6146 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6147 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6152 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6153 element == EL_EXPANDABLE_WALL_ANY ||
6154 element == EL_EXPANDABLE_WALL)
6158 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6159 Store[ax-1][ay] = element;
6160 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6161 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6162 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6163 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6169 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6170 Store[ax+1][ay] = element;
6171 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6172 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6173 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6174 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6179 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6180 DrawLevelField(ax, ay);
6182 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6184 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6185 unten_massiv = TRUE;
6186 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6187 links_massiv = TRUE;
6188 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6189 rechts_massiv = TRUE;
6191 if (((oben_massiv && unten_massiv) ||
6192 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6193 element == EL_EXPANDABLE_WALL) &&
6194 ((links_massiv && rechts_massiv) ||
6195 element == EL_EXPANDABLE_WALL_VERTICAL))
6196 Feld[ax][ay] = EL_WALL;
6200 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6202 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6206 void CheckForDragon(int x, int y)
6209 boolean dragon_found = FALSE;
6210 static int xy[4][2] =
6218 for (i = 0; i < 4; i++)
6220 for (j = 0; j < 4; j++)
6222 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6224 if (IN_LEV_FIELD(xx, yy) &&
6225 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6227 if (Feld[xx][yy] == EL_DRAGON)
6228 dragon_found = TRUE;
6237 for (i = 0; i < 4; i++)
6239 for (j = 0; j < 3; j++)
6241 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6243 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6245 Feld[xx][yy] = EL_EMPTY;
6246 DrawLevelField(xx, yy);
6255 static void InitBuggyBase(int x, int y)
6257 int element = Feld[x][y];
6258 int activating_delay = FRAMES_PER_SECOND / 4;
6261 (element == EL_SP_BUGGY_BASE ?
6262 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6263 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6265 element == EL_SP_BUGGY_BASE_ACTIVE ?
6266 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6269 static void WarnBuggyBase(int x, int y)
6272 static int xy[4][2] =
6280 for (i = 0; i < 4; i++)
6282 int xx = x + xy[i][0], yy = y + xy[i][1];
6284 if (IS_PLAYER(xx, yy))
6286 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6293 static void InitTrap(int x, int y)
6295 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6298 static void ActivateTrap(int x, int y)
6300 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6303 static void ChangeActiveTrap(int x, int y)
6305 int graphic = IMG_TRAP_ACTIVE;
6307 /* if new animation frame was drawn, correct crumbled sand border */
6308 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6309 DrawLevelFieldCrumbledSand(x, y);
6312 static void ChangeElementNowExt(int x, int y, int target_element)
6314 int previous_move_direction = MovDir[x][y];
6316 /* check if element under player changes from accessible to unaccessible
6317 (needed for special case of dropping element which then changes) */
6318 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6319 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6326 Feld[x][y] = target_element;
6328 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6330 ResetGfxAnimation(x, y);
6331 ResetRandomAnimationValue(x, y);
6333 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6334 MovDir[x][y] = previous_move_direction;
6336 InitField(x, y, FALSE);
6337 if (CAN_MOVE(Feld[x][y]))
6340 DrawLevelField(x, y);
6342 if (GFX_CRUMBLED(Feld[x][y]))
6343 DrawLevelFieldCrumbledSandNeighbours(x, y);
6345 TestIfBadThingTouchesHero(x, y);
6346 TestIfPlayerTouchesCustomElement(x, y);
6347 TestIfElementTouchesCustomElement(x, y);
6349 if (ELEM_IS_PLAYER(target_element))
6350 RelocatePlayer(x, y, target_element);
6353 static boolean ChangeElementNow(int x, int y, int element, int page)
6355 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6357 /* always use default change event to prevent running into a loop */
6358 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6359 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6361 /* do not change already changed elements with same change event */
6363 if (Changed[x][y] & ChangeEvent[x][y])
6370 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6372 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6374 if (change->explode)
6381 if (change->use_content)
6383 boolean complete_change = TRUE;
6384 boolean can_change[3][3];
6387 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6389 boolean half_destructible;
6390 int ex = x + xx - 1;
6391 int ey = y + yy - 1;
6394 can_change[xx][yy] = TRUE;
6396 if (ex == x && ey == y) /* do not check changing element itself */
6399 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6401 can_change[xx][yy] = FALSE; /* do not change empty borders */
6406 if (!IN_LEV_FIELD(ex, ey))
6408 can_change[xx][yy] = FALSE;
6409 complete_change = FALSE;
6416 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6417 e = MovingOrBlocked2Element(ex, ey);
6419 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6421 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6422 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6423 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6425 can_change[xx][yy] = FALSE;
6426 complete_change = FALSE;
6430 if (!change->only_complete || complete_change)
6432 boolean something_has_changed = FALSE;
6434 if (change->only_complete && change->use_random_change &&
6435 RND(100) < change->random)
6438 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6440 int ex = x + xx - 1;
6441 int ey = y + yy - 1;
6443 if (can_change[xx][yy] && (!change->use_random_change ||
6444 RND(100) < change->random))
6446 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6447 RemoveMovingField(ex, ey);
6449 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6451 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6453 something_has_changed = TRUE;
6455 /* for symmetry reasons, freeze newly created border elements */
6456 if (ex != x || ey != y)
6457 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6461 if (something_has_changed)
6462 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6467 ChangeElementNowExt(x, y, change->target_element);
6469 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6475 static void ChangeElement(int x, int y, int page)
6477 int element = MovingOrBlocked2Element(x, y);
6478 struct ElementInfo *ei = &element_info[element];
6479 struct ElementChangeInfo *change = &ei->change_page[page];
6483 if (!CAN_CHANGE(element))
6486 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6487 x, y, element, element_info[element].token_name);
6488 printf("ChangeElement(): This should never happen!\n");
6494 if (ChangeDelay[x][y] == 0) /* initialize element change */
6496 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6497 RND(change->delay_random * change->delay_frames)) + 1;
6499 ResetGfxAnimation(x, y);
6500 ResetRandomAnimationValue(x, y);
6502 if (change->pre_change_function)
6503 change->pre_change_function(x, y);
6506 ChangeDelay[x][y]--;
6508 if (ChangeDelay[x][y] != 0) /* continue element change */
6510 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6512 if (IS_ANIMATED(graphic))
6513 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6515 if (change->change_function)
6516 change->change_function(x, y);
6518 else /* finish element change */
6520 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6522 page = ChangePage[x][y];
6523 ChangePage[x][y] = -1;
6527 if (IS_MOVING(x, y) && !change->explode)
6529 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6532 ChangeDelay[x][y] = 1; /* try change after next move step */
6533 ChangePage[x][y] = page; /* remember page to use for change */
6538 if (ChangeElementNow(x, y, element, page))
6540 if (change->post_change_function)
6541 change->post_change_function(x, y);
6546 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6547 int trigger_element,
6553 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6556 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6558 int element = EL_CUSTOM_START + i;
6560 boolean change_element = FALSE;
6563 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6566 for (j = 0; j < element_info[element].num_change_pages; j++)
6568 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6570 if (change->can_change &&
6572 change->events & CH_EVENT_BIT(trigger_event) &&
6574 change->sides & trigger_side &&
6576 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6578 change->trigger_element == trigger_element
6583 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6584 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6585 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6588 change_element = TRUE;
6595 if (!change_element)
6598 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6601 if (x == lx && y == ly) /* do not change trigger element itself */
6605 if (Feld[x][y] == element)
6607 ChangeDelay[x][y] = 1;
6608 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6609 ChangeElement(x, y, page);
6617 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6620 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6624 static boolean CheckElementSideChange(int x, int y, int element, int side,
6625 int trigger_event, int page)
6627 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6630 if (Feld[x][y] == EL_BLOCKED)
6632 Blocked2Moving(x, y, &x, &y);
6633 element = Feld[x][y];
6637 page = element_info[element].event_page_nr[trigger_event];
6639 if (!(element_info[element].change_page[page].sides & side))
6642 ChangeDelay[x][y] = 1;
6643 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6644 ChangeElement(x, y, page);
6649 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6651 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6654 static void PlayPlayerSound(struct PlayerInfo *player)
6656 int jx = player->jx, jy = player->jy;
6657 int element = player->element_nr;
6658 int last_action = player->last_action_waiting;
6659 int action = player->action_waiting;
6661 if (player->is_waiting)
6663 if (action != last_action)
6664 PlayLevelSoundElementAction(jx, jy, element, action);
6666 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6670 if (action != last_action)
6671 StopSound(element_info[element].sound[last_action]);
6673 if (last_action == ACTION_SLEEPING)
6674 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6678 static void PlayAllPlayersSound()
6682 for (i = 0; i < MAX_PLAYERS; i++)
6683 if (stored_player[i].active)
6684 PlayPlayerSound(&stored_player[i]);
6687 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6689 boolean last_waiting = player->is_waiting;
6690 int move_dir = player->MovDir;
6692 player->last_action_waiting = player->action_waiting;
6696 if (!last_waiting) /* not waiting -> waiting */
6698 player->is_waiting = TRUE;
6700 player->frame_counter_bored =
6702 game.player_boring_delay_fixed +
6703 SimpleRND(game.player_boring_delay_random);
6704 player->frame_counter_sleeping =
6706 game.player_sleeping_delay_fixed +
6707 SimpleRND(game.player_sleeping_delay_random);
6709 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6712 if (game.player_sleeping_delay_fixed +
6713 game.player_sleeping_delay_random > 0 &&
6714 player->anim_delay_counter == 0 &&
6715 player->post_delay_counter == 0 &&
6716 FrameCounter >= player->frame_counter_sleeping)
6717 player->is_sleeping = TRUE;
6718 else if (game.player_boring_delay_fixed +
6719 game.player_boring_delay_random > 0 &&
6720 FrameCounter >= player->frame_counter_bored)
6721 player->is_bored = TRUE;
6723 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6724 player->is_bored ? ACTION_BORING :
6727 if (player->is_sleeping)
6729 if (player->num_special_action_sleeping > 0)
6731 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6733 int last_special_action = player->special_action_sleeping;
6734 int num_special_action = player->num_special_action_sleeping;
6735 int special_action =
6736 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6737 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6738 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6739 last_special_action + 1 : ACTION_SLEEPING);
6740 int special_graphic =
6741 el_act_dir2img(player->element_nr, special_action, move_dir);
6743 player->anim_delay_counter =
6744 graphic_info[special_graphic].anim_delay_fixed +
6745 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6746 player->post_delay_counter =
6747 graphic_info[special_graphic].post_delay_fixed +
6748 SimpleRND(graphic_info[special_graphic].post_delay_random);
6750 player->special_action_sleeping = special_action;
6753 if (player->anim_delay_counter > 0)
6755 player->action_waiting = player->special_action_sleeping;
6756 player->anim_delay_counter--;
6758 else if (player->post_delay_counter > 0)
6760 player->post_delay_counter--;
6764 else if (player->is_bored)
6766 if (player->num_special_action_bored > 0)
6768 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6770 int special_action =
6771 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6772 int special_graphic =
6773 el_act_dir2img(player->element_nr, special_action, move_dir);
6775 player->anim_delay_counter =
6776 graphic_info[special_graphic].anim_delay_fixed +
6777 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6778 player->post_delay_counter =
6779 graphic_info[special_graphic].post_delay_fixed +
6780 SimpleRND(graphic_info[special_graphic].post_delay_random);
6782 player->special_action_bored = special_action;
6785 if (player->anim_delay_counter > 0)
6787 player->action_waiting = player->special_action_bored;
6788 player->anim_delay_counter--;
6790 else if (player->post_delay_counter > 0)
6792 player->post_delay_counter--;
6797 else if (last_waiting) /* waiting -> not waiting */
6799 player->is_waiting = FALSE;
6800 player->is_bored = FALSE;
6801 player->is_sleeping = FALSE;
6803 player->frame_counter_bored = -1;
6804 player->frame_counter_sleeping = -1;
6806 player->anim_delay_counter = 0;
6807 player->post_delay_counter = 0;
6809 player->action_waiting = ACTION_DEFAULT;
6811 player->special_action_bored = ACTION_DEFAULT;
6812 player->special_action_sleeping = ACTION_DEFAULT;
6817 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6820 static byte stored_player_action[MAX_PLAYERS];
6821 static int num_stored_actions = 0;
6823 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6824 int left = player_action & JOY_LEFT;
6825 int right = player_action & JOY_RIGHT;
6826 int up = player_action & JOY_UP;
6827 int down = player_action & JOY_DOWN;
6828 int button1 = player_action & JOY_BUTTON_1;
6829 int button2 = player_action & JOY_BUTTON_2;
6830 int dx = (left ? -1 : right ? 1 : 0);
6831 int dy = (up ? -1 : down ? 1 : 0);
6834 stored_player_action[player->index_nr] = 0;
6835 num_stored_actions++;
6839 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6842 if (!player->active || tape.pausing)
6846 printf("::: [%d %d %d %d] [%d %d]\n",
6847 left, right, up, down, button1, button2);
6853 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6857 snapped = SnapField(player, dx, dy);
6861 dropped = DropElement(player);
6863 moved = MovePlayer(player, dx, dy);
6866 if (tape.single_step && tape.recording && !tape.pausing)
6868 if (button1 || (dropped && !moved))
6870 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6871 SnapField(player, 0, 0); /* stop snapping */
6875 SetPlayerWaiting(player, FALSE);
6878 return player_action;
6880 stored_player_action[player->index_nr] = player_action;
6886 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6889 /* no actions for this player (no input at player's configured device) */
6891 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6892 SnapField(player, 0, 0);
6893 CheckGravityMovement(player);
6895 if (player->MovPos == 0)
6896 SetPlayerWaiting(player, TRUE);
6898 if (player->MovPos == 0) /* needed for tape.playing */
6899 player->is_moving = FALSE;
6901 player->is_dropping = FALSE;
6907 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6909 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6911 TapeRecordAction(stored_player_action);
6912 num_stored_actions = 0;
6919 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6921 static byte stored_player_action[MAX_PLAYERS];
6922 static int num_stored_actions = 0;
6923 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6924 int left = player_action & JOY_LEFT;
6925 int right = player_action & JOY_RIGHT;
6926 int up = player_action & JOY_UP;
6927 int down = player_action & JOY_DOWN;
6928 int button1 = player_action & JOY_BUTTON_1;
6929 int button2 = player_action & JOY_BUTTON_2;
6930 int dx = (left ? -1 : right ? 1 : 0);
6931 int dy = (up ? -1 : down ? 1 : 0);
6933 stored_player_action[player->index_nr] = 0;
6934 num_stored_actions++;
6936 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6938 if (!player->active || tape.pausing)
6943 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6946 snapped = SnapField(player, dx, dy);
6950 dropped = DropElement(player);
6952 moved = MovePlayer(player, dx, dy);
6955 if (tape.single_step && tape.recording && !tape.pausing)
6957 if (button1 || (dropped && !moved))
6959 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6960 SnapField(player, 0, 0); /* stop snapping */
6964 stored_player_action[player->index_nr] = player_action;
6968 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6970 /* no actions for this player (no input at player's configured device) */
6972 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6973 SnapField(player, 0, 0);
6974 CheckGravityMovement(player);
6976 if (player->MovPos == 0)
6977 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6979 if (player->MovPos == 0) /* needed for tape.playing */
6980 player->is_moving = FALSE;
6983 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6985 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6987 TapeRecordAction(stored_player_action);
6988 num_stored_actions = 0;
6995 static unsigned long action_delay = 0;
6996 unsigned long action_delay_value;
6997 int magic_wall_x = 0, magic_wall_y = 0;
6998 int i, x, y, element, graphic;
6999 byte *recorded_player_action;
7000 byte summarized_player_action = 0;
7002 byte tape_action[MAX_PLAYERS];
7005 if (game_status != GAME_MODE_PLAYING)
7008 action_delay_value =
7009 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7011 if (tape.playing && tape.index_search && !tape.pausing)
7012 action_delay_value = 0;
7014 /* ---------- main game synchronization point ---------- */
7016 WaitUntilDelayReached(&action_delay, action_delay_value);
7018 if (network_playing && !network_player_action_received)
7022 printf("DEBUG: try to get network player actions in time\n");
7026 #if defined(PLATFORM_UNIX)
7027 /* last chance to get network player actions without main loop delay */
7031 if (game_status != GAME_MODE_PLAYING)
7034 if (!network_player_action_received)
7038 printf("DEBUG: failed to get network player actions in time\n");
7049 printf("::: getting new tape action [%d]\n", FrameCounter);
7052 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7054 for (i = 0; i < MAX_PLAYERS; i++)
7056 summarized_player_action |= stored_player[i].action;
7058 if (!network_playing)
7059 stored_player[i].effective_action = stored_player[i].action;
7062 #if defined(PLATFORM_UNIX)
7063 if (network_playing)
7064 SendToServer_MovePlayer(summarized_player_action);
7067 if (!options.network && !setup.team_mode)
7068 local_player->effective_action = summarized_player_action;
7070 for (i = 0; i < MAX_PLAYERS; i++)
7072 int actual_player_action = stored_player[i].effective_action;
7074 if (stored_player[i].programmed_action)
7075 actual_player_action = stored_player[i].programmed_action;
7077 if (recorded_player_action)
7078 actual_player_action = recorded_player_action[i];
7080 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7082 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7083 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7085 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7090 TapeRecordAction(tape_action);
7093 network_player_action_received = FALSE;
7095 ScrollScreen(NULL, SCROLL_GO_ON);
7101 for (i = 0; i < MAX_PLAYERS; i++)
7102 stored_player[i].Frame++;
7106 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7108 for (i = 0; i < MAX_PLAYERS; i++)
7110 struct PlayerInfo *player = &stored_player[i];
7114 if (player->active && player->is_pushing && player->is_moving &&
7117 ContinueMoving(x, y);
7119 /* continue moving after pushing (this is actually a bug) */
7120 if (!IS_MOVING(x, y))
7129 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7131 Changed[x][y] = CE_BITMASK_DEFAULT;
7132 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7135 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7137 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7138 printf("GameActions(): This should never happen!\n");
7140 ChangePage[x][y] = -1;
7145 if (WasJustMoving[x][y] > 0)
7146 WasJustMoving[x][y]--;
7147 if (WasJustFalling[x][y] > 0)
7148 WasJustFalling[x][y]--;
7153 /* reset finished pushing action (not done in ContinueMoving() to allow
7154 continous pushing animation for elements with zero push delay) */
7155 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7157 ResetGfxAnimation(x, y);
7158 DrawLevelField(x, y);
7163 if (IS_BLOCKED(x, y))
7167 Blocked2Moving(x, y, &oldx, &oldy);
7168 if (!IS_MOVING(oldx, oldy))
7170 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7171 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7172 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7173 printf("GameActions(): This should never happen!\n");
7179 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7181 element = Feld[x][y];
7183 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7185 graphic = el2img(element);
7191 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7193 element = graphic = 0;
7197 if (graphic_info[graphic].anim_global_sync)
7198 GfxFrame[x][y] = FrameCounter;
7200 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7201 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7202 ResetRandomAnimationValue(x, y);
7204 SetRandomAnimationValue(x, y);
7207 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7210 if (IS_INACTIVE(element))
7212 if (IS_ANIMATED(graphic))
7213 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7219 /* this may take place after moving, so 'element' may have changed */
7221 if (IS_CHANGING(x, y))
7223 if (IS_CHANGING(x, y) &&
7224 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7228 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7229 element_info[element].event_page_nr[CE_DELAY]);
7231 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7234 element = Feld[x][y];
7235 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7239 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7244 element = Feld[x][y];
7245 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7247 if (element == EL_MOLE)
7248 printf("::: %d, %d, %d [%d]\n",
7249 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7253 if (element == EL_YAMYAM)
7254 printf("::: %d, %d, %d\n",
7255 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7259 if (IS_ANIMATED(graphic) &&
7263 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7266 if (element == EL_BUG)
7267 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7271 if (element == EL_MOLE)
7272 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7276 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7277 EdelsteinFunkeln(x, y);
7279 else if ((element == EL_ACID ||
7280 element == EL_EXIT_OPEN ||
7281 element == EL_SP_EXIT_OPEN ||
7282 element == EL_SP_TERMINAL ||
7283 element == EL_SP_TERMINAL_ACTIVE ||
7284 element == EL_EXTRA_TIME ||
7285 element == EL_SHIELD_NORMAL ||
7286 element == EL_SHIELD_DEADLY) &&
7287 IS_ANIMATED(graphic))
7288 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7289 else if (IS_MOVING(x, y))
7290 ContinueMoving(x, y);
7291 else if (IS_ACTIVE_BOMB(element))
7292 CheckDynamite(x, y);
7294 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7295 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7297 else if (element == EL_AMOEBA_GROWING)
7298 AmoebeWaechst(x, y);
7299 else if (element == EL_AMOEBA_SHRINKING)
7300 AmoebaDisappearing(x, y);
7302 #if !USE_NEW_AMOEBA_CODE
7303 else if (IS_AMOEBALIVE(element))
7304 AmoebeAbleger(x, y);
7307 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7309 else if (element == EL_EXIT_CLOSED)
7311 else if (element == EL_SP_EXIT_CLOSED)
7313 else if (element == EL_EXPANDABLE_WALL_GROWING)
7315 else if (element == EL_EXPANDABLE_WALL ||
7316 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7317 element == EL_EXPANDABLE_WALL_VERTICAL ||
7318 element == EL_EXPANDABLE_WALL_ANY)
7320 else if (element == EL_FLAMES)
7321 CheckForDragon(x, y);
7323 else if (IS_AUTO_CHANGING(element))
7324 ChangeElement(x, y);
7326 else if (element == EL_EXPLOSION)
7327 ; /* drawing of correct explosion animation is handled separately */
7328 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7329 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7332 /* this may take place after moving, so 'element' may have changed */
7333 if (IS_AUTO_CHANGING(Feld[x][y]))
7334 ChangeElement(x, y);
7337 if (IS_BELT_ACTIVE(element))
7338 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7340 if (game.magic_wall_active)
7342 int jx = local_player->jx, jy = local_player->jy;
7344 /* play the element sound at the position nearest to the player */
7345 if ((element == EL_MAGIC_WALL_FULL ||
7346 element == EL_MAGIC_WALL_ACTIVE ||
7347 element == EL_MAGIC_WALL_EMPTYING ||
7348 element == EL_BD_MAGIC_WALL_FULL ||
7349 element == EL_BD_MAGIC_WALL_ACTIVE ||
7350 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7351 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7359 #if USE_NEW_AMOEBA_CODE
7360 /* new experimental amoeba growth stuff */
7362 if (!(FrameCounter % 8))
7365 static unsigned long random = 1684108901;
7367 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7370 x = (random >> 10) % lev_fieldx;
7371 y = (random >> 20) % lev_fieldy;
7373 x = RND(lev_fieldx);
7374 y = RND(lev_fieldy);
7376 element = Feld[x][y];
7378 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7379 if (!IS_PLAYER(x,y) &&
7380 (element == EL_EMPTY ||
7381 element == EL_SAND ||
7382 element == EL_QUICKSAND_EMPTY ||
7383 element == EL_ACID_SPLASH_LEFT ||
7384 element == EL_ACID_SPLASH_RIGHT))
7386 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7387 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7388 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7389 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7390 Feld[x][y] = EL_AMOEBA_DROP;
7393 random = random * 129 + 1;
7399 if (game.explosions_delayed)
7402 game.explosions_delayed = FALSE;
7404 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7406 element = Feld[x][y];
7408 if (ExplodeField[x][y])
7409 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7410 else if (element == EL_EXPLOSION)
7411 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7413 ExplodeField[x][y] = EX_NO_EXPLOSION;
7416 game.explosions_delayed = TRUE;
7419 if (game.magic_wall_active)
7421 if (!(game.magic_wall_time_left % 4))
7423 int element = Feld[magic_wall_x][magic_wall_y];
7425 if (element == EL_BD_MAGIC_WALL_FULL ||
7426 element == EL_BD_MAGIC_WALL_ACTIVE ||
7427 element == EL_BD_MAGIC_WALL_EMPTYING)
7428 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7430 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7433 if (game.magic_wall_time_left > 0)
7435 game.magic_wall_time_left--;
7436 if (!game.magic_wall_time_left)
7438 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7440 element = Feld[x][y];
7442 if (element == EL_MAGIC_WALL_ACTIVE ||
7443 element == EL_MAGIC_WALL_FULL)
7445 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7446 DrawLevelField(x, y);
7448 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7449 element == EL_BD_MAGIC_WALL_FULL)
7451 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7452 DrawLevelField(x, y);
7456 game.magic_wall_active = FALSE;
7461 if (game.light_time_left > 0)
7463 game.light_time_left--;
7465 if (game.light_time_left == 0)
7466 RedrawAllLightSwitchesAndInvisibleElements();
7469 if (game.timegate_time_left > 0)
7471 game.timegate_time_left--;
7473 if (game.timegate_time_left == 0)
7474 CloseAllOpenTimegates();
7477 for (i = 0; i < MAX_PLAYERS; i++)
7479 struct PlayerInfo *player = &stored_player[i];
7481 if (SHIELD_ON(player))
7483 if (player->shield_deadly_time_left)
7484 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7485 else if (player->shield_normal_time_left)
7486 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7490 if (TimeFrames >= FRAMES_PER_SECOND)
7495 for (i = 0; i < MAX_PLAYERS; i++)
7497 struct PlayerInfo *player = &stored_player[i];
7499 if (SHIELD_ON(player))
7501 player->shield_normal_time_left--;
7503 if (player->shield_deadly_time_left > 0)
7504 player->shield_deadly_time_left--;
7508 if (tape.recording || tape.playing)
7509 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7515 if (TimeLeft <= 10 && setup.time_limit)
7516 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7518 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7520 if (!TimeLeft && setup.time_limit)
7521 for (i = 0; i < MAX_PLAYERS; i++)
7522 KillHero(&stored_player[i]);
7524 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7525 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7529 PlayAllPlayersSound();
7531 if (options.debug) /* calculate frames per second */
7533 static unsigned long fps_counter = 0;
7534 static int fps_frames = 0;
7535 unsigned long fps_delay_ms = Counter() - fps_counter;
7539 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7541 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7544 fps_counter = Counter();
7547 redraw_mask |= REDRAW_FPS;
7551 if (stored_player[0].jx != stored_player[0].last_jx ||
7552 stored_player[0].jy != stored_player[0].last_jy)
7553 printf("::: %d, %d, %d, %d, %d\n",
7554 stored_player[0].MovDir,
7555 stored_player[0].MovPos,
7556 stored_player[0].GfxPos,
7557 stored_player[0].Frame,
7558 stored_player[0].StepFrame);
7565 for (i = 0; i < MAX_PLAYERS; i++)
7568 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7570 stored_player[i].Frame += move_frames;
7572 if (stored_player[i].MovPos != 0)
7573 stored_player[i].StepFrame += move_frames;
7575 if (stored_player[i].drop_delay > 0)
7576 stored_player[i].drop_delay--;
7581 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7583 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7585 local_player->show_envelope = 0;
7590 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7592 int min_x = x, min_y = y, max_x = x, max_y = y;
7595 for (i = 0; i < MAX_PLAYERS; i++)
7597 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7599 if (!stored_player[i].active || &stored_player[i] == player)
7602 min_x = MIN(min_x, jx);
7603 min_y = MIN(min_y, jy);
7604 max_x = MAX(max_x, jx);
7605 max_y = MAX(max_y, jy);
7608 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7611 static boolean AllPlayersInVisibleScreen()
7615 for (i = 0; i < MAX_PLAYERS; i++)
7617 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7619 if (!stored_player[i].active)
7622 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7629 void ScrollLevel(int dx, int dy)
7631 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7634 BlitBitmap(drawto_field, drawto_field,
7635 FX + TILEX * (dx == -1) - softscroll_offset,
7636 FY + TILEY * (dy == -1) - softscroll_offset,
7637 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7638 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7639 FX + TILEX * (dx == 1) - softscroll_offset,
7640 FY + TILEY * (dy == 1) - softscroll_offset);
7644 x = (dx == 1 ? BX1 : BX2);
7645 for (y = BY1; y <= BY2; y++)
7646 DrawScreenField(x, y);
7651 y = (dy == 1 ? BY1 : BY2);
7652 for (x = BX1; x <= BX2; x++)
7653 DrawScreenField(x, y);
7656 redraw_mask |= REDRAW_FIELD;
7659 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7661 int nextx = x + dx, nexty = y + dy;
7662 int element = Feld[x][y];
7665 element != EL_SP_PORT_LEFT &&
7666 element != EL_SP_GRAVITY_PORT_LEFT &&
7667 element != EL_SP_PORT_HORIZONTAL &&
7668 element != EL_SP_PORT_ANY) ||
7670 element != EL_SP_PORT_RIGHT &&
7671 element != EL_SP_GRAVITY_PORT_RIGHT &&
7672 element != EL_SP_PORT_HORIZONTAL &&
7673 element != EL_SP_PORT_ANY) ||
7675 element != EL_SP_PORT_UP &&
7676 element != EL_SP_GRAVITY_PORT_UP &&
7677 element != EL_SP_PORT_VERTICAL &&
7678 element != EL_SP_PORT_ANY) ||
7680 element != EL_SP_PORT_DOWN &&
7681 element != EL_SP_GRAVITY_PORT_DOWN &&
7682 element != EL_SP_PORT_VERTICAL &&
7683 element != EL_SP_PORT_ANY) ||
7684 !IN_LEV_FIELD(nextx, nexty) ||
7685 !IS_FREE(nextx, nexty))
7691 static void CheckGravityMovement(struct PlayerInfo *player)
7693 if (game.gravity && !player->programmed_action)
7695 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7696 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7698 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7699 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7700 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7701 int jx = player->jx, jy = player->jy;
7702 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7703 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7704 int new_jx = jx + dx, new_jy = jy + dy;
7705 boolean field_under_player_is_free =
7706 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7707 boolean player_is_moving_to_valid_field =
7708 (IN_LEV_FIELD(new_jx, new_jy) &&
7709 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7710 Feld[new_jx][new_jy] == EL_SAND ||
7711 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7712 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7713 /* !!! extend EL_SAND to anything diggable !!! */
7715 if (field_under_player_is_free &&
7716 !player_is_moving_to_valid_field &&
7717 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7718 player->programmed_action = MV_DOWN;
7724 -----------------------------------------------------------------------------
7725 dx, dy: direction (non-diagonal) to try to move the player to
7726 real_dx, real_dy: direction as read from input device (can be diagonal)
7729 boolean MovePlayerOneStep(struct PlayerInfo *player,
7730 int dx, int dy, int real_dx, int real_dy)
7733 static int change_sides[4][2] =
7735 /* enter side leave side */
7736 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7737 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7738 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7739 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7741 int move_direction = (dx == -1 ? MV_LEFT :
7742 dx == +1 ? MV_RIGHT :
7744 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7745 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7746 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7748 int jx = player->jx, jy = player->jy;
7749 int new_jx = jx + dx, new_jy = jy + dy;
7753 if (!player->active || (!dx && !dy))
7754 return MF_NO_ACTION;
7756 player->MovDir = (dx < 0 ? MV_LEFT :
7759 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7761 if (!IN_LEV_FIELD(new_jx, new_jy))
7762 return MF_NO_ACTION;
7764 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7765 return MF_NO_ACTION;
7768 element = MovingOrBlocked2Element(new_jx, new_jy);
7770 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7773 if (DONT_RUN_INTO(element))
7775 if (element == EL_ACID && dx == 0 && dy == 1)
7778 Feld[jx][jy] = EL_PLAYER_1;
7779 InitMovingField(jx, jy, MV_DOWN);
7780 Store[jx][jy] = EL_ACID;
7781 ContinueMoving(jx, jy);
7785 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7790 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7791 if (can_move != MF_MOVING)
7794 /* check if DigField() has caused relocation of the player */
7795 if (player->jx != jx || player->jy != jy)
7796 return MF_NO_ACTION;
7798 StorePlayer[jx][jy] = 0;
7799 player->last_jx = jx;
7800 player->last_jy = jy;
7801 player->jx = new_jx;
7802 player->jy = new_jy;
7803 StorePlayer[new_jx][new_jy] = player->element_nr;
7806 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7808 player->step_counter++;
7810 player->drop_delay = 0;
7812 PlayerVisit[jx][jy] = FrameCounter;
7814 ScrollPlayer(player, SCROLL_INIT);
7817 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7819 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7820 CE_OTHER_GETS_LEFT);
7821 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7822 CE_LEFT_BY_PLAYER, -1);
7825 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7827 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7828 enter_side, CE_OTHER_GETS_ENTERED);
7829 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7830 CE_ENTERED_BY_PLAYER, -1);
7837 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7839 int jx = player->jx, jy = player->jy;
7840 int old_jx = jx, old_jy = jy;
7841 int moved = MF_NO_ACTION;
7844 if (!player->active)
7849 if (player->MovPos == 0)
7851 player->is_moving = FALSE;
7852 player->is_digging = FALSE;
7853 player->is_collecting = FALSE;
7854 player->is_snapping = FALSE;
7855 player->is_pushing = FALSE;
7861 if (!player->active || (!dx && !dy))
7866 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7870 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7871 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7875 /* remove the last programmed player action */
7876 player->programmed_action = 0;
7880 /* should only happen if pre-1.2 tape recordings are played */
7881 /* this is only for backward compatibility */
7883 int original_move_delay_value = player->move_delay_value;
7886 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7890 /* scroll remaining steps with finest movement resolution */
7891 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7893 while (player->MovPos)
7895 ScrollPlayer(player, SCROLL_GO_ON);
7896 ScrollScreen(NULL, SCROLL_GO_ON);
7902 player->move_delay_value = original_move_delay_value;
7905 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7907 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7908 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7912 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7913 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7919 if (moved & MF_MOVING && !ScreenMovPos &&
7920 (player == local_player || !options.network))
7922 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7923 int offset = (setup.scroll_delay ? 3 : 0);
7925 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7927 /* actual player has left the screen -- scroll in that direction */
7928 if (jx != old_jx) /* player has moved horizontally */
7929 scroll_x += (jx - old_jx);
7930 else /* player has moved vertically */
7931 scroll_y += (jy - old_jy);
7935 if (jx != old_jx) /* player has moved horizontally */
7937 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7938 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7939 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7941 /* don't scroll over playfield boundaries */
7942 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7943 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7945 /* don't scroll more than one field at a time */
7946 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7948 /* don't scroll against the player's moving direction */
7949 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7950 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7951 scroll_x = old_scroll_x;
7953 else /* player has moved vertically */
7955 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7956 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7957 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7959 /* don't scroll over playfield boundaries */
7960 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7961 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7963 /* don't scroll more than one field at a time */
7964 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7966 /* don't scroll against the player's moving direction */
7967 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7968 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7969 scroll_y = old_scroll_y;
7973 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7975 if (!options.network && !AllPlayersInVisibleScreen())
7977 scroll_x = old_scroll_x;
7978 scroll_y = old_scroll_y;
7982 ScrollScreen(player, SCROLL_INIT);
7983 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7990 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7992 if (!(moved & MF_MOVING) && !player->is_pushing)
7997 player->StepFrame = 0;
7999 if (moved & MF_MOVING)
8001 if (old_jx != jx && old_jy == jy)
8002 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8003 else if (old_jx == jx && old_jy != jy)
8004 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8006 DrawLevelField(jx, jy); /* for "crumbled sand" */
8008 player->last_move_dir = player->MovDir;
8009 player->is_moving = TRUE;
8011 player->is_snapping = FALSE;
8015 player->is_switching = FALSE;
8018 player->is_dropping = FALSE;
8023 static int change_sides[4][2] =
8025 /* enter side leave side */
8026 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8027 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8028 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8029 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8031 int move_direction = player->MovDir;
8032 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8033 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8036 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8038 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8039 leave_side, CE_OTHER_GETS_LEFT);
8040 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8041 leave_side, CE_LEFT_BY_PLAYER, -1);
8044 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8046 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8047 enter_side, CE_OTHER_GETS_ENTERED);
8048 CheckElementSideChange(jx, jy, Feld[jx][jy],
8049 enter_side, CE_ENTERED_BY_PLAYER, -1);
8060 CheckGravityMovement(player);
8063 player->last_move_dir = MV_NO_MOVING;
8065 player->is_moving = FALSE;
8068 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8070 TestIfHeroTouchesBadThing(jx, jy);
8071 TestIfPlayerTouchesCustomElement(jx, jy);
8074 if (!player->active)
8080 void ScrollPlayer(struct PlayerInfo *player, int mode)
8082 int jx = player->jx, jy = player->jy;
8083 int last_jx = player->last_jx, last_jy = player->last_jy;
8084 int move_stepsize = TILEX / player->move_delay_value;
8086 if (!player->active || !player->MovPos)
8089 if (mode == SCROLL_INIT)
8091 player->actual_frame_counter = FrameCounter;
8092 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8094 if (Feld[last_jx][last_jy] == EL_EMPTY)
8095 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8103 else if (!FrameReached(&player->actual_frame_counter, 1))
8106 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8107 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8109 if (!player->block_last_field &&
8110 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8111 Feld[last_jx][last_jy] = EL_EMPTY;
8113 /* before DrawPlayer() to draw correct player graphic for this case */
8114 if (player->MovPos == 0)
8115 CheckGravityMovement(player);
8118 DrawPlayer(player); /* needed here only to cleanup last field */
8121 if (player->MovPos == 0) /* player reached destination field */
8124 if (player->move_delay_reset_counter > 0)
8126 player->move_delay_reset_counter--;
8128 if (player->move_delay_reset_counter == 0)
8130 /* continue with normal speed after quickly moving through gate */
8131 HALVE_PLAYER_SPEED(player);
8133 /* be able to make the next move without delay */
8134 player->move_delay = 0;
8138 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8140 /* continue with normal speed after quickly moving through gate */
8141 HALVE_PLAYER_SPEED(player);
8143 /* be able to make the next move without delay */
8144 player->move_delay = 0;
8148 if (player->block_last_field &&
8149 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8150 Feld[last_jx][last_jy] = EL_EMPTY;
8152 player->last_jx = jx;
8153 player->last_jy = jy;
8155 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8156 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8157 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8159 DrawPlayer(player); /* needed here only to cleanup last field */
8162 if (local_player->friends_still_needed == 0 ||
8163 IS_SP_ELEMENT(Feld[jx][jy]))
8164 player->LevelSolved = player->GameOver = TRUE;
8167 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8169 TestIfHeroTouchesBadThing(jx, jy);
8170 TestIfPlayerTouchesCustomElement(jx, jy);
8172 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8175 if (!player->active)
8179 if (tape.single_step && tape.recording && !tape.pausing &&
8180 !player->programmed_action)
8181 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8185 void ScrollScreen(struct PlayerInfo *player, int mode)
8187 static unsigned long screen_frame_counter = 0;
8189 if (mode == SCROLL_INIT)
8191 /* set scrolling step size according to actual player's moving speed */
8192 ScrollStepSize = TILEX / player->move_delay_value;
8194 screen_frame_counter = FrameCounter;
8195 ScreenMovDir = player->MovDir;
8196 ScreenMovPos = player->MovPos;
8197 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8200 else if (!FrameReached(&screen_frame_counter, 1))
8205 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8206 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8207 redraw_mask |= REDRAW_FIELD;
8210 ScreenMovDir = MV_NO_MOVING;
8213 void TestIfPlayerTouchesCustomElement(int x, int y)
8215 static int xy[4][2] =
8222 static int change_sides[4][2] =
8224 /* center side border side */
8225 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8226 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8227 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8228 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8230 static int touch_dir[4] =
8237 int center_element = Feld[x][y]; /* should always be non-moving! */
8240 for (i = 0; i < 4; i++)
8242 int xx = x + xy[i][0];
8243 int yy = y + xy[i][1];
8244 int center_side = change_sides[i][0];
8245 int border_side = change_sides[i][1];
8248 if (!IN_LEV_FIELD(xx, yy))
8251 if (IS_PLAYER(x, y))
8253 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8254 border_element = Feld[xx][yy]; /* may be moving! */
8255 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8256 border_element = Feld[xx][yy];
8257 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8258 border_element = MovingOrBlocked2Element(xx, yy);
8260 continue; /* center and border element do not touch */
8262 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8263 CE_OTHER_GETS_TOUCHED);
8264 CheckElementSideChange(xx, yy, border_element, border_side,
8265 CE_TOUCHED_BY_PLAYER, -1);
8267 else if (IS_PLAYER(xx, yy))
8269 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8271 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8273 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8274 continue; /* center and border element do not touch */
8277 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8278 CE_OTHER_GETS_TOUCHED);
8279 CheckElementSideChange(x, y, center_element, center_side,
8280 CE_TOUCHED_BY_PLAYER, -1);
8287 void TestIfElementTouchesCustomElement(int x, int y)
8289 static int xy[4][2] =
8296 static int change_sides[4][2] =
8298 /* center side border side */
8299 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8300 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8301 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8302 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8304 static int touch_dir[4] =
8311 boolean change_center_element = FALSE;
8312 int center_element_change_page = 0;
8313 int center_element = Feld[x][y]; /* should always be non-moving! */
8316 for (i = 0; i < 4; i++)
8318 int xx = x + xy[i][0];
8319 int yy = y + xy[i][1];
8320 int center_side = change_sides[i][0];
8321 int border_side = change_sides[i][1];
8324 if (!IN_LEV_FIELD(xx, yy))
8327 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8328 border_element = Feld[xx][yy]; /* may be moving! */
8329 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8330 border_element = Feld[xx][yy];
8331 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8332 border_element = MovingOrBlocked2Element(xx, yy);
8334 continue; /* center and border element do not touch */
8336 /* check for change of center element (but change it only once) */
8337 if (IS_CUSTOM_ELEMENT(center_element) &&
8338 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8339 !change_center_element)
8341 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8343 struct ElementChangeInfo *change =
8344 &element_info[center_element].change_page[j];
8346 if (change->can_change &&
8347 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8348 change->sides & border_side &&
8350 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8352 change->trigger_element == border_element
8356 change_center_element = TRUE;
8357 center_element_change_page = j;
8364 /* check for change of border element */
8365 if (IS_CUSTOM_ELEMENT(border_element) &&
8366 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8368 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8370 struct ElementChangeInfo *change =
8371 &element_info[border_element].change_page[j];
8373 if (change->can_change &&
8374 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8375 change->sides & center_side &&
8377 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8379 change->trigger_element == center_element
8383 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8384 CE_OTHER_IS_TOUCHING, j);
8391 if (change_center_element)
8392 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8393 CE_OTHER_IS_TOUCHING, center_element_change_page);
8396 void TestIfElementHitsCustomElement(int x, int y, int direction)
8398 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8399 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8400 int hitx = x + dx, hity = y + dy;
8401 int hitting_element = Feld[x][y];
8403 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8404 !IS_FREE(hitx, hity) &&
8405 (!IS_MOVING(hitx, hity) ||
8406 MovDir[hitx][hity] != direction ||
8407 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8410 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8414 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8418 CheckElementSideChange(x, y, hitting_element,
8419 direction, CE_HITTING_SOMETHING, -1);
8421 if (IN_LEV_FIELD(hitx, hity))
8423 int opposite_direction = MV_DIR_OPPOSITE(direction);
8424 int hitting_side = direction;
8425 int touched_side = opposite_direction;
8426 int touched_element = MovingOrBlocked2Element(hitx, hity);
8428 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8429 MovDir[hitx][hity] != direction ||
8430 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8439 CheckElementSideChange(hitx, hity, touched_element,
8440 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8442 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8443 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8445 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8447 struct ElementChangeInfo *change =
8448 &element_info[hitting_element].change_page[i];
8450 if (change->can_change &&
8451 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8452 change->sides & touched_side &&
8455 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8457 change->trigger_element == touched_element
8461 CheckElementSideChange(x, y, hitting_element,
8462 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8468 if (IS_CUSTOM_ELEMENT(touched_element) &&
8469 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8471 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8473 struct ElementChangeInfo *change =
8474 &element_info[touched_element].change_page[i];
8476 if (change->can_change &&
8477 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8478 change->sides & hitting_side &&
8480 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8482 change->trigger_element == hitting_element
8486 CheckElementSideChange(hitx, hity, touched_element,
8487 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8496 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8498 int i, kill_x = -1, kill_y = -1;
8499 static int test_xy[4][2] =
8506 static int test_dir[4] =
8514 for (i = 0; i < 4; i++)
8516 int test_x, test_y, test_move_dir, test_element;
8518 test_x = good_x + test_xy[i][0];
8519 test_y = good_y + test_xy[i][1];
8520 if (!IN_LEV_FIELD(test_x, test_y))
8524 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8527 test_element = Feld[test_x][test_y];
8529 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8532 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8533 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8535 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8536 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8544 if (kill_x != -1 || kill_y != -1)
8546 if (IS_PLAYER(good_x, good_y))
8548 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8550 if (player->shield_deadly_time_left > 0)
8551 Bang(kill_x, kill_y);
8552 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8556 Bang(good_x, good_y);
8560 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8562 int i, kill_x = -1, kill_y = -1;
8563 int bad_element = Feld[bad_x][bad_y];
8564 static int test_xy[4][2] =
8571 static int touch_dir[4] =
8578 static int test_dir[4] =
8586 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8589 for (i = 0; i < 4; i++)
8591 int test_x, test_y, test_move_dir, test_element;
8593 test_x = bad_x + test_xy[i][0];
8594 test_y = bad_y + test_xy[i][1];
8595 if (!IN_LEV_FIELD(test_x, test_y))
8599 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8601 test_element = Feld[test_x][test_y];
8603 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8604 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8606 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8607 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8609 /* good thing is player or penguin that does not move away */
8610 if (IS_PLAYER(test_x, test_y))
8612 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8614 if (bad_element == EL_ROBOT && player->is_moving)
8615 continue; /* robot does not kill player if he is moving */
8617 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8619 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8620 continue; /* center and border element do not touch */
8627 else if (test_element == EL_PENGUIN)
8636 if (kill_x != -1 || kill_y != -1)
8638 if (IS_PLAYER(kill_x, kill_y))
8640 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8642 if (player->shield_deadly_time_left > 0)
8644 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8648 Bang(kill_x, kill_y);
8652 void TestIfHeroTouchesBadThing(int x, int y)
8654 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8657 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8659 TestIfGoodThingHitsBadThing(x, y, move_dir);
8662 void TestIfBadThingTouchesHero(int x, int y)
8664 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8667 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8669 TestIfBadThingHitsGoodThing(x, y, move_dir);
8672 void TestIfFriendTouchesBadThing(int x, int y)
8674 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8677 void TestIfBadThingTouchesFriend(int x, int y)
8679 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8682 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8684 int i, kill_x = bad_x, kill_y = bad_y;
8685 static int xy[4][2] =
8693 for (i = 0; i < 4; i++)
8697 x = bad_x + xy[i][0];
8698 y = bad_y + xy[i][1];
8699 if (!IN_LEV_FIELD(x, y))
8702 element = Feld[x][y];
8703 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8704 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8712 if (kill_x != bad_x || kill_y != bad_y)
8716 void KillHero(struct PlayerInfo *player)
8718 int jx = player->jx, jy = player->jy;
8720 if (!player->active)
8723 /* remove accessible field at the player's position */
8724 Feld[jx][jy] = EL_EMPTY;
8726 /* deactivate shield (else Bang()/Explode() would not work right) */
8727 player->shield_normal_time_left = 0;
8728 player->shield_deadly_time_left = 0;
8734 static void KillHeroUnlessEnemyProtected(int x, int y)
8736 if (!PLAYER_ENEMY_PROTECTED(x, y))
8737 KillHero(PLAYERINFO(x, y));
8740 static void KillHeroUnlessExplosionProtected(int x, int y)
8742 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8743 KillHero(PLAYERINFO(x, y));
8746 void BuryHero(struct PlayerInfo *player)
8748 int jx = player->jx, jy = player->jy;
8750 if (!player->active)
8754 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8756 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8758 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8760 player->GameOver = TRUE;
8764 void RemoveHero(struct PlayerInfo *player)
8766 int jx = player->jx, jy = player->jy;
8767 int i, found = FALSE;
8769 player->present = FALSE;
8770 player->active = FALSE;
8772 if (!ExplodeField[jx][jy])
8773 StorePlayer[jx][jy] = 0;
8775 for (i = 0; i < MAX_PLAYERS; i++)
8776 if (stored_player[i].active)
8780 AllPlayersGone = TRUE;
8787 =============================================================================
8788 checkDiagonalPushing()
8789 -----------------------------------------------------------------------------
8790 check if diagonal input device direction results in pushing of object
8791 (by checking if the alternative direction is walkable, diggable, ...)
8792 =============================================================================
8795 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8796 int x, int y, int real_dx, int real_dy)
8798 int jx, jy, dx, dy, xx, yy;
8800 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8803 /* diagonal direction: check alternative direction */
8808 xx = jx + (dx == 0 ? real_dx : 0);
8809 yy = jy + (dy == 0 ? real_dy : 0);
8811 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8815 =============================================================================
8817 -----------------------------------------------------------------------------
8818 x, y: field next to player (non-diagonal) to try to dig to
8819 real_dx, real_dy: direction as read from input device (can be diagonal)
8820 =============================================================================
8823 int DigField(struct PlayerInfo *player,
8824 int x, int y, int real_dx, int real_dy, int mode)
8826 static int change_sides[4] =
8828 CH_SIDE_RIGHT, /* moving left */
8829 CH_SIDE_LEFT, /* moving right */
8830 CH_SIDE_BOTTOM, /* moving up */
8831 CH_SIDE_TOP, /* moving down */
8833 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8834 int jx = player->jx, jy = player->jy;
8835 int dx = x - jx, dy = y - jy;
8836 int nextx = x + dx, nexty = y + dy;
8837 int move_direction = (dx == -1 ? MV_LEFT :
8838 dx == +1 ? MV_RIGHT :
8840 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8841 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8844 if (player->MovPos == 0)
8846 player->is_digging = FALSE;
8847 player->is_collecting = FALSE;
8850 if (player->MovPos == 0) /* last pushing move finished */
8851 player->is_pushing = FALSE;
8853 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8855 player->is_switching = FALSE;
8856 player->push_delay = 0;
8858 return MF_NO_ACTION;
8861 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8862 return MF_NO_ACTION;
8865 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8867 if (IS_TUBE(Feld[jx][jy]) ||
8868 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8872 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8873 int tube_leave_directions[][2] =
8875 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8876 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8877 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8878 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8879 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8880 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8881 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8882 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8883 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8884 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8885 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8886 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8889 while (tube_leave_directions[i][0] != tube_element)
8892 if (tube_leave_directions[i][0] == -1) /* should not happen */
8896 if (!(tube_leave_directions[i][1] & move_direction))
8897 return MF_NO_ACTION; /* tube has no opening in this direction */
8900 element = Feld[x][y];
8902 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8903 game.engine_version >= VERSION_IDENT(2,2,0,0))
8904 return MF_NO_ACTION;
8908 case EL_SP_PORT_LEFT:
8909 case EL_SP_PORT_RIGHT:
8911 case EL_SP_PORT_DOWN:
8912 case EL_SP_PORT_HORIZONTAL:
8913 case EL_SP_PORT_VERTICAL:
8914 case EL_SP_PORT_ANY:
8915 case EL_SP_GRAVITY_PORT_LEFT:
8916 case EL_SP_GRAVITY_PORT_RIGHT:
8917 case EL_SP_GRAVITY_PORT_UP:
8918 case EL_SP_GRAVITY_PORT_DOWN:
8920 if (!canEnterSupaplexPort(x, y, dx, dy))
8921 return MF_NO_ACTION;
8924 element != EL_SP_PORT_LEFT &&
8925 element != EL_SP_GRAVITY_PORT_LEFT &&
8926 element != EL_SP_PORT_HORIZONTAL &&
8927 element != EL_SP_PORT_ANY) ||
8929 element != EL_SP_PORT_RIGHT &&
8930 element != EL_SP_GRAVITY_PORT_RIGHT &&
8931 element != EL_SP_PORT_HORIZONTAL &&
8932 element != EL_SP_PORT_ANY) ||
8934 element != EL_SP_PORT_UP &&
8935 element != EL_SP_GRAVITY_PORT_UP &&
8936 element != EL_SP_PORT_VERTICAL &&
8937 element != EL_SP_PORT_ANY) ||
8939 element != EL_SP_PORT_DOWN &&
8940 element != EL_SP_GRAVITY_PORT_DOWN &&
8941 element != EL_SP_PORT_VERTICAL &&
8942 element != EL_SP_PORT_ANY) ||
8943 !IN_LEV_FIELD(nextx, nexty) ||
8944 !IS_FREE(nextx, nexty))
8945 return MF_NO_ACTION;
8948 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8949 element == EL_SP_GRAVITY_PORT_RIGHT ||
8950 element == EL_SP_GRAVITY_PORT_UP ||
8951 element == EL_SP_GRAVITY_PORT_DOWN)
8952 game.gravity = !game.gravity;
8954 /* automatically move to the next field with double speed */
8955 player->programmed_action = move_direction;
8957 if (player->move_delay_reset_counter == 0)
8959 player->move_delay_reset_counter = 2; /* two double speed steps */
8961 DOUBLE_PLAYER_SPEED(player);
8964 player->move_delay_reset_counter = 2;
8966 DOUBLE_PLAYER_SPEED(player);
8969 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8973 case EL_TUBE_VERTICAL:
8974 case EL_TUBE_HORIZONTAL:
8975 case EL_TUBE_VERTICAL_LEFT:
8976 case EL_TUBE_VERTICAL_RIGHT:
8977 case EL_TUBE_HORIZONTAL_UP:
8978 case EL_TUBE_HORIZONTAL_DOWN:
8979 case EL_TUBE_LEFT_UP:
8980 case EL_TUBE_LEFT_DOWN:
8981 case EL_TUBE_RIGHT_UP:
8982 case EL_TUBE_RIGHT_DOWN:
8985 int tube_enter_directions[][2] =
8987 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8988 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8989 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8990 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8991 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8992 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8993 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8994 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8995 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8996 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8997 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8998 { -1, MV_NO_MOVING }
9001 while (tube_enter_directions[i][0] != element)
9004 if (tube_enter_directions[i][0] == -1) /* should not happen */
9008 if (!(tube_enter_directions[i][1] & move_direction))
9009 return MF_NO_ACTION; /* tube has no opening in this direction */
9011 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9017 if (IS_WALKABLE(element))
9019 int sound_action = ACTION_WALKING;
9021 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9023 if (!player->key[element - EL_GATE_1])
9024 return MF_NO_ACTION;
9026 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9028 if (!player->key[element - EL_GATE_1_GRAY])
9029 return MF_NO_ACTION;
9031 else if (element == EL_EXIT_OPEN ||
9032 element == EL_SP_EXIT_OPEN ||
9033 element == EL_SP_EXIT_OPENING)
9035 sound_action = ACTION_PASSING; /* player is passing exit */
9037 else if (element == EL_EMPTY)
9039 sound_action = ACTION_MOVING; /* nothing to walk on */
9042 /* play sound from background or player, whatever is available */
9043 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9044 PlayLevelSoundElementAction(x, y, element, sound_action);
9046 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9050 else if (IS_PASSABLE(element))
9052 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9053 return MF_NO_ACTION;
9056 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9057 return MF_NO_ACTION;
9060 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9062 if (!player->key[element - EL_EM_GATE_1])
9063 return MF_NO_ACTION;
9065 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9067 if (!player->key[element - EL_EM_GATE_1_GRAY])
9068 return MF_NO_ACTION;
9071 /* automatically move to the next field with double speed */
9072 player->programmed_action = move_direction;
9074 if (player->move_delay_reset_counter == 0)
9076 player->move_delay_reset_counter = 2; /* two double speed steps */
9078 DOUBLE_PLAYER_SPEED(player);
9081 player->move_delay_reset_counter = 2;
9083 DOUBLE_PLAYER_SPEED(player);
9086 PlayLevelSoundAction(x, y, ACTION_PASSING);
9090 else if (IS_DIGGABLE(element))
9094 if (mode != DF_SNAP)
9097 GfxElement[x][y] = GFX_ELEMENT(element);
9100 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9102 player->is_digging = TRUE;
9105 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9107 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9110 if (mode == DF_SNAP)
9111 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9116 else if (IS_COLLECTIBLE(element))
9120 if (mode != DF_SNAP)
9122 GfxElement[x][y] = element;
9123 player->is_collecting = TRUE;
9126 if (element == EL_SPEED_PILL)
9127 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9128 else if (element == EL_EXTRA_TIME && level.time > 0)
9131 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9133 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9135 player->shield_normal_time_left += 10;
9136 if (element == EL_SHIELD_DEADLY)
9137 player->shield_deadly_time_left += 10;
9139 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9141 if (player->inventory_size < MAX_INVENTORY_SIZE)
9142 player->inventory_element[player->inventory_size++] = element;
9144 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9145 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9147 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9149 player->dynabomb_count++;
9150 player->dynabombs_left++;
9152 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9154 player->dynabomb_size++;
9156 else if (element == EL_DYNABOMB_INCREASE_POWER)
9158 player->dynabomb_xl = TRUE;
9160 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9161 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9163 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9164 element - EL_KEY_1 : element - EL_EM_KEY_1);
9166 player->key[key_nr] = TRUE;
9168 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9169 el2edimg(EL_KEY_1 + key_nr));
9170 redraw_mask |= REDRAW_DOOR_1;
9172 else if (IS_ENVELOPE(element))
9175 player->show_envelope = element;
9177 ShowEnvelope(element - EL_ENVELOPE_1);
9180 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9184 for (i = 0; i < element_info[element].collect_count; i++)
9185 if (player->inventory_size < MAX_INVENTORY_SIZE)
9186 player->inventory_element[player->inventory_size++] = element;
9188 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9189 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9191 else if (element_info[element].collect_count > 0)
9193 local_player->gems_still_needed -=
9194 element_info[element].collect_count;
9195 if (local_player->gems_still_needed < 0)
9196 local_player->gems_still_needed = 0;
9198 DrawText(DX_EMERALDS, DY_EMERALDS,
9199 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9202 RaiseScoreElement(element);
9203 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9205 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9208 if (mode == DF_SNAP)
9209 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9214 else if (IS_PUSHABLE(element))
9216 if (mode == DF_SNAP && element != EL_BD_ROCK)
9217 return MF_NO_ACTION;
9219 if (CAN_FALL(element) && dy)
9220 return MF_NO_ACTION;
9222 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9223 !(element == EL_SPRING && use_spring_bug))
9224 return MF_NO_ACTION;
9227 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9228 ((move_direction & MV_VERTICAL &&
9229 ((element_info[element].move_pattern & MV_LEFT &&
9230 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9231 (element_info[element].move_pattern & MV_RIGHT &&
9232 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9233 (move_direction & MV_HORIZONTAL &&
9234 ((element_info[element].move_pattern & MV_UP &&
9235 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9236 (element_info[element].move_pattern & MV_DOWN &&
9237 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9238 return MF_NO_ACTION;
9242 /* do not push elements already moving away faster than player */
9243 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9244 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9245 return MF_NO_ACTION;
9247 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9248 return MF_NO_ACTION;
9252 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9254 if (player->push_delay_value == -1)
9255 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9257 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9259 if (!player->is_pushing)
9260 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9264 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9265 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9266 !player_is_pushing))
9267 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9270 if (!player->is_pushing &&
9271 game.engine_version >= VERSION_IDENT(2,2,0,7))
9272 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9276 printf("::: push delay: %ld [%d, %d] [%d]\n",
9277 player->push_delay_value, FrameCounter, game.engine_version,
9278 player->is_pushing);
9281 player->is_pushing = TRUE;
9283 if (!(IN_LEV_FIELD(nextx, nexty) &&
9284 (IS_FREE(nextx, nexty) ||
9285 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9286 IS_SB_ELEMENT(element)))))
9287 return MF_NO_ACTION;
9289 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9290 return MF_NO_ACTION;
9292 if (player->push_delay == 0) /* new pushing; restart delay */
9293 player->push_delay = FrameCounter;
9295 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9296 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9297 element != EL_SPRING && element != EL_BALLOON)
9299 /* make sure that there is no move delay before next try to push */
9300 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9301 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9303 return MF_NO_ACTION;
9307 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9310 if (IS_SB_ELEMENT(element))
9312 if (element == EL_SOKOBAN_FIELD_FULL)
9314 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9315 local_player->sokobanfields_still_needed++;
9318 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9320 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9321 local_player->sokobanfields_still_needed--;
9324 Feld[x][y] = EL_SOKOBAN_OBJECT;
9326 if (Back[x][y] == Back[nextx][nexty])
9327 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9328 else if (Back[x][y] != 0)
9329 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9332 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9335 if (local_player->sokobanfields_still_needed == 0 &&
9336 game.emulation == EMU_SOKOBAN)
9338 player->LevelSolved = player->GameOver = TRUE;
9339 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9343 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9345 InitMovingField(x, y, move_direction);
9346 GfxAction[x][y] = ACTION_PUSHING;
9348 if (mode == DF_SNAP)
9349 ContinueMoving(x, y);
9351 MovPos[x][y] = (dx != 0 ? dx : dy);
9353 Pushed[x][y] = TRUE;
9354 Pushed[nextx][nexty] = TRUE;
9356 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9357 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9359 player->push_delay_value = -1; /* get new value later */
9361 CheckTriggeredElementSideChange(x, y, element, dig_side,
9362 CE_OTHER_GETS_PUSHED);
9363 CheckElementSideChange(x, y, element, dig_side,
9364 CE_PUSHED_BY_PLAYER, -1);
9368 else if (IS_SWITCHABLE(element))
9370 if (PLAYER_SWITCHING(player, x, y))
9373 player->is_switching = TRUE;
9374 player->switch_x = x;
9375 player->switch_y = y;
9377 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9379 if (element == EL_ROBOT_WHEEL)
9381 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9385 DrawLevelField(x, y);
9387 else if (element == EL_SP_TERMINAL)
9391 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9393 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9395 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9396 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9399 else if (IS_BELT_SWITCH(element))
9401 ToggleBeltSwitch(x, y);
9403 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9404 element == EL_SWITCHGATE_SWITCH_DOWN)
9406 ToggleSwitchgateSwitch(x, y);
9408 else if (element == EL_LIGHT_SWITCH ||
9409 element == EL_LIGHT_SWITCH_ACTIVE)
9411 ToggleLightSwitch(x, y);
9414 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9415 SND_LIGHT_SWITCH_ACTIVATING :
9416 SND_LIGHT_SWITCH_DEACTIVATING);
9419 else if (element == EL_TIMEGATE_SWITCH)
9421 ActivateTimegateSwitch(x, y);
9423 else if (element == EL_BALLOON_SWITCH_LEFT ||
9424 element == EL_BALLOON_SWITCH_RIGHT ||
9425 element == EL_BALLOON_SWITCH_UP ||
9426 element == EL_BALLOON_SWITCH_DOWN ||
9427 element == EL_BALLOON_SWITCH_ANY)
9429 if (element == EL_BALLOON_SWITCH_ANY)
9430 game.balloon_dir = move_direction;
9432 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9433 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9434 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9435 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9438 else if (element == EL_LAMP)
9440 Feld[x][y] = EL_LAMP_ACTIVE;
9441 local_player->lights_still_needed--;
9443 DrawLevelField(x, y);
9445 else if (element == EL_TIME_ORB_FULL)
9447 Feld[x][y] = EL_TIME_ORB_EMPTY;
9449 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9451 DrawLevelField(x, y);
9454 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9462 if (!PLAYER_SWITCHING(player, x, y))
9464 player->is_switching = TRUE;
9465 player->switch_x = x;
9466 player->switch_y = y;
9468 CheckTriggeredElementSideChange(x, y, element, dig_side,
9469 CE_OTHER_IS_SWITCHING);
9470 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9473 CheckTriggeredElementSideChange(x, y, element, dig_side,
9474 CE_OTHER_GETS_PRESSED);
9475 CheckElementSideChange(x, y, element, dig_side,
9476 CE_PRESSED_BY_PLAYER, -1);
9479 return MF_NO_ACTION;
9482 player->push_delay = 0;
9484 if (Feld[x][y] != element) /* really digged/collected something */
9485 player->is_collecting = !player->is_digging;
9490 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9492 int jx = player->jx, jy = player->jy;
9493 int x = jx + dx, y = jy + dy;
9494 int snap_direction = (dx == -1 ? MV_LEFT :
9495 dx == +1 ? MV_RIGHT :
9497 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9499 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9502 if (!player->active || !IN_LEV_FIELD(x, y))
9510 if (player->MovPos == 0)
9511 player->is_pushing = FALSE;
9513 player->is_snapping = FALSE;
9515 if (player->MovPos == 0)
9517 player->is_moving = FALSE;
9518 player->is_digging = FALSE;
9519 player->is_collecting = FALSE;
9525 if (player->is_snapping)
9528 player->MovDir = snap_direction;
9531 if (player->MovPos == 0)
9534 player->is_moving = FALSE;
9535 player->is_digging = FALSE;
9536 player->is_collecting = FALSE;
9539 player->is_dropping = FALSE;
9541 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9544 player->is_snapping = TRUE;
9547 if (player->MovPos == 0)
9550 player->is_moving = FALSE;
9551 player->is_digging = FALSE;
9552 player->is_collecting = FALSE;
9555 DrawLevelField(x, y);
9561 boolean DropElement(struct PlayerInfo *player)
9563 int jx = player->jx, jy = player->jy;
9564 int old_element = Feld[jx][jy];
9567 /* check if player is active, not moving and ready to drop */
9568 if (!player->active || player->MovPos || player->drop_delay > 0)
9571 /* check if player has anything that can be dropped */
9572 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9575 /* check if anything can be dropped at the current position */
9576 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9579 /* collected custom elements can only be dropped on empty fields */
9580 if (player->inventory_size > 0 &&
9581 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9582 && old_element != EL_EMPTY)
9585 if (old_element != EL_EMPTY)
9586 Back[jx][jy] = old_element; /* store old element on this field */
9588 ResetGfxAnimation(jx, jy);
9589 ResetRandomAnimationValue(jx, jy);
9591 if (player->inventory_size > 0)
9593 player->inventory_size--;
9594 new_element = player->inventory_element[player->inventory_size];
9596 if (new_element == EL_DYNAMITE)
9597 new_element = EL_DYNAMITE_ACTIVE;
9598 else if (new_element == EL_SP_DISK_RED)
9599 new_element = EL_SP_DISK_RED_ACTIVE;
9601 Feld[jx][jy] = new_element;
9603 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9604 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9606 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9607 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9609 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9612 /* needed if previous element just changed to "empty" in the last frame */
9613 Changed[jx][jy] = 0; /* allow another change */
9616 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9617 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9619 TestIfElementTouchesCustomElement(jx, jy);
9621 else /* player is dropping a dyna bomb */
9623 player->dynabombs_left--;
9624 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9626 Feld[jx][jy] = new_element;
9628 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9629 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9631 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9638 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9640 InitField(jx, jy, FALSE);
9641 if (CAN_MOVE(Feld[jx][jy]))
9645 new_element = Feld[jx][jy];
9647 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9648 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9650 int move_stepsize = element_info[new_element].move_stepsize;
9651 int direction, dx, dy, nextx, nexty;
9653 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9654 MovDir[jx][jy] = player->MovDir;
9656 direction = MovDir[jx][jy];
9657 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9658 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9662 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9665 WasJustMoving[jx][jy] = 3;
9667 InitMovingField(jx, jy, direction);
9668 ContinueMoving(jx, jy);
9673 Changed[jx][jy] = 0; /* allow another change */
9676 TestIfElementHitsCustomElement(jx, jy, direction);
9678 CheckElementSideChange(jx, jy, new_element,
9679 direction, CE_HITTING_SOMETHING, -1);
9683 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9687 player->drop_delay = 8 + 8 + 8;
9692 player->is_dropping = TRUE;
9698 /* ------------------------------------------------------------------------- */
9699 /* game sound playing functions */
9700 /* ------------------------------------------------------------------------- */
9702 static int *loop_sound_frame = NULL;
9703 static int *loop_sound_volume = NULL;
9705 void InitPlayLevelSound()
9707 int num_sounds = getSoundListSize();
9709 checked_free(loop_sound_frame);
9710 checked_free(loop_sound_volume);
9712 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9713 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9716 static void PlayLevelSound(int x, int y, int nr)
9718 int sx = SCREENX(x), sy = SCREENY(y);
9719 int volume, stereo_position;
9720 int max_distance = 8;
9721 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9723 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9724 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9727 if (!IN_LEV_FIELD(x, y) ||
9728 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9729 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9732 volume = SOUND_MAX_VOLUME;
9734 if (!IN_SCR_FIELD(sx, sy))
9736 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9737 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9739 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9742 stereo_position = (SOUND_MAX_LEFT +
9743 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9744 (SCR_FIELDX + 2 * max_distance));
9746 if (IS_LOOP_SOUND(nr))
9748 /* This assures that quieter loop sounds do not overwrite louder ones,
9749 while restarting sound volume comparison with each new game frame. */
9751 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9754 loop_sound_volume[nr] = volume;
9755 loop_sound_frame[nr] = FrameCounter;
9758 PlaySoundExt(nr, volume, stereo_position, type);
9761 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9763 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9764 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9765 y < LEVELY(BY1) ? LEVELY(BY1) :
9766 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9770 static void PlayLevelSoundAction(int x, int y, int action)
9772 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9775 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9777 int sound_effect = element_info[element].sound[action];
9779 if (sound_effect != SND_UNDEFINED)
9780 PlayLevelSound(x, y, sound_effect);
9783 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9786 int sound_effect = element_info[element].sound[action];
9788 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9789 PlayLevelSound(x, y, sound_effect);
9792 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9794 int sound_effect = element_info[Feld[x][y]].sound[action];
9796 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9797 PlayLevelSound(x, y, sound_effect);
9800 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9802 int sound_effect = element_info[Feld[x][y]].sound[action];
9804 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9805 StopSound(sound_effect);
9808 static void PlayLevelMusic()
9810 if (levelset.music[level_nr] != MUS_UNDEFINED)
9811 PlayMusic(levelset.music[level_nr]); /* from config file */
9813 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9816 void RaiseScore(int value)
9818 local_player->score += value;
9819 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9822 void RaiseScoreElement(int element)
9828 case EL_EMERALD_YELLOW:
9829 case EL_EMERALD_RED:
9830 case EL_EMERALD_PURPLE:
9831 case EL_SP_INFOTRON:
9832 RaiseScore(level.score[SC_EMERALD]);
9835 RaiseScore(level.score[SC_DIAMOND]);
9838 RaiseScore(level.score[SC_CRYSTAL]);
9841 RaiseScore(level.score[SC_PEARL]);
9844 case EL_BD_BUTTERFLY:
9845 case EL_SP_ELECTRON:
9846 RaiseScore(level.score[SC_BUG]);
9850 case EL_SP_SNIKSNAK:
9851 RaiseScore(level.score[SC_SPACESHIP]);
9854 case EL_DARK_YAMYAM:
9855 RaiseScore(level.score[SC_YAMYAM]);
9858 RaiseScore(level.score[SC_ROBOT]);
9861 RaiseScore(level.score[SC_PACMAN]);
9864 RaiseScore(level.score[SC_NUT]);
9867 case EL_SP_DISK_RED:
9868 case EL_DYNABOMB_INCREASE_NUMBER:
9869 case EL_DYNABOMB_INCREASE_SIZE:
9870 case EL_DYNABOMB_INCREASE_POWER:
9871 RaiseScore(level.score[SC_DYNAMITE]);
9873 case EL_SHIELD_NORMAL:
9874 case EL_SHIELD_DEADLY:
9875 RaiseScore(level.score[SC_SHIELD]);
9878 RaiseScore(level.score[SC_TIME_BONUS]);
9884 RaiseScore(level.score[SC_KEY]);
9887 RaiseScore(element_info[element].collect_score);
9892 void RequestQuitGame(boolean ask_if_really_quit)
9894 if (AllPlayersGone ||
9895 !ask_if_really_quit ||
9896 level_editor_test_game ||
9897 Request("Do you really want to quit the game ?",
9898 REQ_ASK | REQ_STAY_CLOSED))
9900 #if defined(PLATFORM_UNIX)
9901 if (options.network)
9902 SendToServer_StopPlaying();
9906 game_status = GAME_MODE_MAIN;
9912 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9917 /* ---------- new game button stuff ---------------------------------------- */
9919 /* graphic position values for game buttons */
9920 #define GAME_BUTTON_XSIZE 30
9921 #define GAME_BUTTON_YSIZE 30
9922 #define GAME_BUTTON_XPOS 5
9923 #define GAME_BUTTON_YPOS 215
9924 #define SOUND_BUTTON_XPOS 5
9925 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9927 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9928 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9929 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9930 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9931 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9932 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9939 } gamebutton_info[NUM_GAME_BUTTONS] =
9942 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9947 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9952 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9957 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9958 SOUND_CTRL_ID_MUSIC,
9959 "background music on/off"
9962 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9963 SOUND_CTRL_ID_LOOPS,
9964 "sound loops on/off"
9967 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9968 SOUND_CTRL_ID_SIMPLE,
9969 "normal sounds on/off"
9973 void CreateGameButtons()
9977 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9979 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9980 struct GadgetInfo *gi;
9983 unsigned long event_mask;
9984 int gd_xoffset, gd_yoffset;
9985 int gd_x1, gd_x2, gd_y1, gd_y2;
9988 gd_xoffset = gamebutton_info[i].x;
9989 gd_yoffset = gamebutton_info[i].y;
9990 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9991 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9993 if (id == GAME_CTRL_ID_STOP ||
9994 id == GAME_CTRL_ID_PAUSE ||
9995 id == GAME_CTRL_ID_PLAY)
9997 button_type = GD_TYPE_NORMAL_BUTTON;
9999 event_mask = GD_EVENT_RELEASED;
10000 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10001 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10005 button_type = GD_TYPE_CHECK_BUTTON;
10007 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10008 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10009 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10010 event_mask = GD_EVENT_PRESSED;
10011 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10012 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10015 gi = CreateGadget(GDI_CUSTOM_ID, id,
10016 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10017 GDI_X, DX + gd_xoffset,
10018 GDI_Y, DY + gd_yoffset,
10019 GDI_WIDTH, GAME_BUTTON_XSIZE,
10020 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10021 GDI_TYPE, button_type,
10022 GDI_STATE, GD_BUTTON_UNPRESSED,
10023 GDI_CHECKED, checked,
10024 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10025 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10026 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10027 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10028 GDI_EVENT_MASK, event_mask,
10029 GDI_CALLBACK_ACTION, HandleGameButtons,
10033 Error(ERR_EXIT, "cannot create gadget");
10035 game_gadget[id] = gi;
10039 void FreeGameButtons()
10043 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10044 FreeGadget(game_gadget[i]);
10047 static void MapGameButtons()
10051 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10052 MapGadget(game_gadget[i]);
10055 void UnmapGameButtons()
10059 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10060 UnmapGadget(game_gadget[i]);
10063 static void HandleGameButtons(struct GadgetInfo *gi)
10065 int id = gi->custom_id;
10067 if (game_status != GAME_MODE_PLAYING)
10072 case GAME_CTRL_ID_STOP:
10073 RequestQuitGame(TRUE);
10076 case GAME_CTRL_ID_PAUSE:
10077 if (options.network)
10079 #if defined(PLATFORM_UNIX)
10081 SendToServer_ContinuePlaying();
10083 SendToServer_PausePlaying();
10087 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10090 case GAME_CTRL_ID_PLAY:
10093 #if defined(PLATFORM_UNIX)
10094 if (options.network)
10095 SendToServer_ContinuePlaying();
10099 tape.pausing = FALSE;
10100 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10105 case SOUND_CTRL_ID_MUSIC:
10106 if (setup.sound_music)
10108 setup.sound_music = FALSE;
10111 else if (audio.music_available)
10113 setup.sound = setup.sound_music = TRUE;
10115 SetAudioMode(setup.sound);
10121 case SOUND_CTRL_ID_LOOPS:
10122 if (setup.sound_loops)
10123 setup.sound_loops = FALSE;
10124 else if (audio.loops_available)
10126 setup.sound = setup.sound_loops = TRUE;
10127 SetAudioMode(setup.sound);
10131 case SOUND_CTRL_ID_SIMPLE:
10132 if (setup.sound_simple)
10133 setup.sound_simple = FALSE;
10134 else if (audio.sound_available)
10136 setup.sound = setup.sound_simple = TRUE;
10137 SetAudioMode(setup.sound);