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_raw)
2366 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2367 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2368 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2369 boolean no_delay = (tape.index_search);
2370 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2371 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2373 if (player->GameOver) /* do not reanimate dead player */
2377 RemoveField(x, y); /* temporarily remove newly placed player */
2378 DrawLevelField(x, y);
2381 if (player->present)
2383 while (player->MovPos)
2385 ScrollPlayer(player, SCROLL_GO_ON);
2386 ScrollScreen(NULL, SCROLL_GO_ON);
2392 Delay(wait_delay_value);
2395 DrawPlayer(player); /* needed here only to cleanup last field */
2396 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2398 player->is_moving = FALSE;
2401 Feld[x][y] = element;
2402 InitPlayerField(x, y, element, TRUE);
2404 if (player == local_player)
2406 int scroll_xx = -999, scroll_yy = -999;
2408 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2411 int fx = FX, fy = FY;
2413 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2414 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2415 local_player->jx - MIDPOSX);
2417 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2418 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2419 local_player->jy - MIDPOSY);
2421 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2422 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2427 fx += dx * TILEX / 2;
2428 fy += dy * TILEY / 2;
2430 ScrollLevel(dx, dy);
2433 /* scroll in two steps of half tile size to make things smoother */
2434 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2436 Delay(wait_delay_value);
2438 /* scroll second step to align at full tile size */
2440 Delay(wait_delay_value);
2445 void Explode(int ex, int ey, int phase, int mode)
2452 /* !!! eliminate this variable !!! */
2453 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2458 int last_phase = num_phase * delay;
2459 int half_phase = (num_phase / 2) * delay;
2460 int first_phase_after_start = EX_PHASE_START + 1;
2464 if (game.explosions_delayed)
2466 ExplodeField[ex][ey] = mode;
2470 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2472 int center_element = Feld[ex][ey];
2475 /* --- This is only really needed (and now handled) in "Impact()". --- */
2476 /* do not explode moving elements that left the explode field in time */
2477 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2478 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2482 if (mode == EX_NORMAL || mode == EX_CENTER)
2483 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2485 /* remove things displayed in background while burning dynamite */
2486 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2489 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2491 /* put moving element to center field (and let it explode there) */
2492 center_element = MovingOrBlocked2Element(ex, ey);
2493 RemoveMovingField(ex, ey);
2494 Feld[ex][ey] = center_element;
2498 last_phase = element_info[center_element].explosion_delay;
2501 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2503 int xx = x - ex + 1;
2504 int yy = y - ey + 1;
2507 if (!IN_LEV_FIELD(x, y) ||
2508 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2509 (x != ex || y != ey)))
2512 element = Feld[x][y];
2514 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2516 element = MovingOrBlocked2Element(x, y);
2518 if (!IS_EXPLOSION_PROOF(element))
2519 RemoveMovingField(x, y);
2525 if (IS_EXPLOSION_PROOF(element))
2528 /* indestructible elements can only explode in center (but not flames) */
2529 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2530 element == EL_FLAMES)
2535 if ((IS_INDESTRUCTIBLE(element) &&
2536 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2537 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2538 element == EL_FLAMES)
2542 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2544 if (IS_ACTIVE_BOMB(element))
2546 /* re-activate things under the bomb like gate or penguin */
2547 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2554 /* save walkable background elements while explosion on same tile */
2556 if (IS_INDESTRUCTIBLE(element))
2557 Back[x][y] = element;
2559 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2560 Back[x][y] = element;
2563 /* ignite explodable elements reached by other explosion */
2564 if (element == EL_EXPLOSION)
2565 element = Store2[x][y];
2568 if (AmoebaNr[x][y] &&
2569 (element == EL_AMOEBA_FULL ||
2570 element == EL_BD_AMOEBA ||
2571 element == EL_AMOEBA_GROWING))
2573 AmoebaCnt[AmoebaNr[x][y]]--;
2574 AmoebaCnt2[AmoebaNr[x][y]]--;
2580 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2582 switch(StorePlayer[ex][ey])
2585 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2588 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2591 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2595 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2599 if (game.emulation == EMU_SUPAPLEX)
2600 Store[x][y] = EL_EMPTY;
2602 else if (center_element == EL_MOLE)
2603 Store[x][y] = EL_EMERALD_RED;
2604 else if (center_element == EL_PENGUIN)
2605 Store[x][y] = EL_EMERALD_PURPLE;
2606 else if (center_element == EL_BUG)
2607 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2608 else if (center_element == EL_BD_BUTTERFLY)
2609 Store[x][y] = EL_BD_DIAMOND;
2610 else if (center_element == EL_SP_ELECTRON)
2611 Store[x][y] = EL_SP_INFOTRON;
2612 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2613 Store[x][y] = level.amoeba_content;
2614 else if (center_element == EL_YAMYAM)
2615 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2616 else if (IS_CUSTOM_ELEMENT(center_element) &&
2617 element_info[center_element].content[xx][yy] != EL_EMPTY)
2618 Store[x][y] = element_info[center_element].content[xx][yy];
2619 else if (element == EL_WALL_EMERALD)
2620 Store[x][y] = EL_EMERALD;
2621 else if (element == EL_WALL_DIAMOND)
2622 Store[x][y] = EL_DIAMOND;
2623 else if (element == EL_WALL_BD_DIAMOND)
2624 Store[x][y] = EL_BD_DIAMOND;
2625 else if (element == EL_WALL_EMERALD_YELLOW)
2626 Store[x][y] = EL_EMERALD_YELLOW;
2627 else if (element == EL_WALL_EMERALD_RED)
2628 Store[x][y] = EL_EMERALD_RED;
2629 else if (element == EL_WALL_EMERALD_PURPLE)
2630 Store[x][y] = EL_EMERALD_PURPLE;
2631 else if (element == EL_WALL_PEARL)
2632 Store[x][y] = EL_PEARL;
2633 else if (element == EL_WALL_CRYSTAL)
2634 Store[x][y] = EL_CRYSTAL;
2635 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2636 Store[x][y] = element_info[element].content[1][1];
2638 Store[x][y] = EL_EMPTY;
2640 if (x != ex || y != ey ||
2641 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2642 Store2[x][y] = element;
2645 if (AmoebaNr[x][y] &&
2646 (element == EL_AMOEBA_FULL ||
2647 element == EL_BD_AMOEBA ||
2648 element == EL_AMOEBA_GROWING))
2650 AmoebaCnt[AmoebaNr[x][y]]--;
2651 AmoebaCnt2[AmoebaNr[x][y]]--;
2657 MovDir[x][y] = MovPos[x][y] = 0;
2658 GfxDir[x][y] = MovDir[x][y];
2663 Feld[x][y] = EL_EXPLOSION;
2665 GfxElement[x][y] = center_element;
2667 GfxElement[x][y] = EL_UNDEFINED;
2670 ExplodePhase[x][y] = 1;
2672 ExplodeDelay[x][y] = last_phase;
2677 if (center_element == EL_YAMYAM)
2678 game.yamyam_content_nr =
2679 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2691 last_phase = ExplodeDelay[x][y];
2694 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2698 /* activate this even in non-DEBUG version until cause for crash in
2699 getGraphicAnimationFrame() (see below) is found and eliminated */
2703 if (GfxElement[x][y] == EL_UNDEFINED)
2706 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2707 printf("Explode(): This should never happen!\n");
2710 GfxElement[x][y] = EL_EMPTY;
2716 border_element = Store2[x][y];
2717 if (IS_PLAYER(x, y))
2718 border_element = StorePlayer[x][y];
2720 if (phase == element_info[border_element].ignition_delay ||
2721 phase == last_phase)
2723 boolean border_explosion = FALSE;
2726 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2728 if (IS_PLAYER(x, y))
2731 KillHeroUnlessExplosionProtected(x, y);
2732 border_explosion = TRUE;
2734 if (phase == last_phase)
2735 printf("::: IS_PLAYER\n");
2737 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2739 Feld[x][y] = Store2[x][y];
2742 border_explosion = TRUE;
2744 if (phase == last_phase)
2745 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2747 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2749 AmoebeUmwandeln(x, y);
2751 border_explosion = TRUE;
2753 if (phase == last_phase)
2754 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2755 element_info[border_element].explosion_delay,
2756 element_info[border_element].ignition_delay,
2761 /* if an element just explodes due to another explosion (chain-reaction),
2762 do not immediately end the new explosion when it was the last frame of
2763 the explosion (as it would be done in the following "if"-statement!) */
2764 if (border_explosion && phase == last_phase)
2771 if (phase == first_phase_after_start)
2773 int element = Store2[x][y];
2775 if (element == EL_BLACK_ORB)
2777 Feld[x][y] = Store2[x][y];
2782 else if (phase == half_phase)
2784 int element = Store2[x][y];
2786 if (IS_PLAYER(x, y))
2787 KillHeroUnlessExplosionProtected(x, y);
2788 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2790 Feld[x][y] = Store2[x][y];
2794 else if (element == EL_AMOEBA_TO_DIAMOND)
2795 AmoebeUmwandeln(x, y);
2799 if (phase == last_phase)
2803 element = Feld[x][y] = Store[x][y];
2804 Store[x][y] = Store2[x][y] = 0;
2805 GfxElement[x][y] = EL_UNDEFINED;
2807 /* player can escape from explosions and might therefore be still alive */
2808 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2809 element <= EL_PLAYER_IS_EXPLODING_4)
2810 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2812 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2813 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2814 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2817 /* restore probably existing indestructible background element */
2818 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2819 element = Feld[x][y] = Back[x][y];
2822 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2823 GfxDir[x][y] = MV_NO_MOVING;
2824 ChangeDelay[x][y] = 0;
2825 ChangePage[x][y] = -1;
2827 InitField(x, y, FALSE);
2829 /* !!! not needed !!! */
2830 if (CAN_MOVE(element))
2833 DrawLevelField(x, y);
2835 TestIfElementTouchesCustomElement(x, y);
2837 if (GFX_CRUMBLED(element))
2838 DrawLevelFieldCrumbledSandNeighbours(x, y);
2840 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
2841 StorePlayer[x][y] = 0;
2843 if (ELEM_IS_PLAYER(element))
2844 RelocatePlayer(x, y, element);
2847 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2849 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2853 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2855 int stored = Store[x][y];
2856 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2857 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2860 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2863 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2864 element_info[GfxElement[x][y]].token_name,
2869 DrawLevelFieldCrumbledSand(x, y);
2871 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2873 DrawLevelElement(x, y, Back[x][y]);
2874 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2876 else if (IS_WALKABLE_UNDER(Back[x][y]))
2878 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2879 DrawLevelElementThruMask(x, y, Back[x][y]);
2881 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2882 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2886 void DynaExplode(int ex, int ey)
2889 int dynabomb_element = Feld[ex][ey];
2890 int dynabomb_size = 1;
2891 boolean dynabomb_xl = FALSE;
2892 struct PlayerInfo *player;
2893 static int xy[4][2] =
2901 if (IS_ACTIVE_BOMB(dynabomb_element))
2903 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2904 dynabomb_size = player->dynabomb_size;
2905 dynabomb_xl = player->dynabomb_xl;
2906 player->dynabombs_left++;
2909 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2911 for (i = 0; i < 4; i++)
2913 for (j = 1; j <= dynabomb_size; j++)
2915 int x = ex + j * xy[i % 4][0];
2916 int y = ey + j * xy[i % 4][1];
2919 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2922 element = Feld[x][y];
2924 /* do not restart explosions of fields with active bombs */
2925 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2928 Explode(x, y, EX_PHASE_START, EX_BORDER);
2930 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2931 if (element != EL_EMPTY &&
2932 element != EL_SAND &&
2933 element != EL_EXPLOSION &&
2940 void Bang(int x, int y)
2943 int element = MovingOrBlocked2Element(x, y);
2945 int element = Feld[x][y];
2949 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2951 if (IS_PLAYER(x, y))
2954 struct PlayerInfo *player = PLAYERINFO(x, y);
2956 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2957 player->element_nr);
2962 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2964 if (game.emulation == EMU_SUPAPLEX)
2965 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2967 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2972 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2980 case EL_BD_BUTTERFLY:
2983 case EL_DARK_YAMYAM:
2987 RaiseScoreElement(element);
2988 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2990 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2991 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2992 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2993 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2994 case EL_DYNABOMB_INCREASE_NUMBER:
2995 case EL_DYNABOMB_INCREASE_SIZE:
2996 case EL_DYNABOMB_INCREASE_POWER:
3001 case EL_LAMP_ACTIVE:
3002 if (IS_PLAYER(x, y))
3003 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3005 Explode(x, y, EX_PHASE_START, EX_CENTER);
3008 if (CAN_EXPLODE_DYNA(element))
3010 else if (CAN_EXPLODE_1X1(element))
3011 Explode(x, y, EX_PHASE_START, EX_CENTER);
3013 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3017 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3020 void SplashAcid(int x, int y)
3022 int element = Feld[x][y];
3024 if (element != EL_ACID_SPLASH_LEFT &&
3025 element != EL_ACID_SPLASH_RIGHT)
3027 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3029 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3030 (!IN_LEV_FIELD(x-1, y-1) ||
3031 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3032 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3034 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3035 (!IN_LEV_FIELD(x+1, y-1) ||
3036 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3037 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3041 static void InitBeltMovement()
3043 static int belt_base_element[4] =
3045 EL_CONVEYOR_BELT_1_LEFT,
3046 EL_CONVEYOR_BELT_2_LEFT,
3047 EL_CONVEYOR_BELT_3_LEFT,
3048 EL_CONVEYOR_BELT_4_LEFT
3050 static int belt_base_active_element[4] =
3052 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3053 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3054 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3055 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3060 /* set frame order for belt animation graphic according to belt direction */
3061 for (i = 0; i < 4; i++)
3065 for (j = 0; j < 3; j++)
3067 int element = belt_base_active_element[belt_nr] + j;
3068 int graphic = el2img(element);
3070 if (game.belt_dir[i] == MV_LEFT)
3071 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3073 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3077 for (y = 0; y < lev_fieldy; y++)
3079 for (x = 0; x < lev_fieldx; x++)
3081 int element = Feld[x][y];
3083 for (i = 0; i < 4; i++)
3085 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3087 int e_belt_nr = getBeltNrFromBeltElement(element);
3090 if (e_belt_nr == belt_nr)
3092 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3094 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3102 static void ToggleBeltSwitch(int x, int y)
3104 static int belt_base_element[4] =
3106 EL_CONVEYOR_BELT_1_LEFT,
3107 EL_CONVEYOR_BELT_2_LEFT,
3108 EL_CONVEYOR_BELT_3_LEFT,
3109 EL_CONVEYOR_BELT_4_LEFT
3111 static int belt_base_active_element[4] =
3113 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3114 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3115 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3116 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3118 static int belt_base_switch_element[4] =
3120 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3121 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3122 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3123 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3125 static int belt_move_dir[4] =
3133 int element = Feld[x][y];
3134 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3135 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3136 int belt_dir = belt_move_dir[belt_dir_nr];
3139 if (!IS_BELT_SWITCH(element))
3142 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3143 game.belt_dir[belt_nr] = belt_dir;
3145 if (belt_dir_nr == 3)
3148 /* set frame order for belt animation graphic according to belt direction */
3149 for (i = 0; i < 3; i++)
3151 int element = belt_base_active_element[belt_nr] + i;
3152 int graphic = el2img(element);
3154 if (belt_dir == MV_LEFT)
3155 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3157 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3160 for (yy = 0; yy < lev_fieldy; yy++)
3162 for (xx = 0; xx < lev_fieldx; xx++)
3164 int element = Feld[xx][yy];
3166 if (IS_BELT_SWITCH(element))
3168 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3170 if (e_belt_nr == belt_nr)
3172 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3173 DrawLevelField(xx, yy);
3176 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3178 int e_belt_nr = getBeltNrFromBeltElement(element);
3180 if (e_belt_nr == belt_nr)
3182 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3184 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3185 DrawLevelField(xx, yy);
3188 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3190 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3192 if (e_belt_nr == belt_nr)
3194 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3196 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3197 DrawLevelField(xx, yy);
3204 static void ToggleSwitchgateSwitch(int x, int y)
3208 game.switchgate_pos = !game.switchgate_pos;
3210 for (yy = 0; yy < lev_fieldy; yy++)
3212 for (xx = 0; xx < lev_fieldx; xx++)
3214 int element = Feld[xx][yy];
3216 if (element == EL_SWITCHGATE_SWITCH_UP ||
3217 element == EL_SWITCHGATE_SWITCH_DOWN)
3219 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3220 DrawLevelField(xx, yy);
3222 else if (element == EL_SWITCHGATE_OPEN ||
3223 element == EL_SWITCHGATE_OPENING)
3225 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3227 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3229 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3232 else if (element == EL_SWITCHGATE_CLOSED ||
3233 element == EL_SWITCHGATE_CLOSING)
3235 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3237 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3239 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3246 static int getInvisibleActiveFromInvisibleElement(int element)
3248 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3249 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3250 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3254 static int getInvisibleFromInvisibleActiveElement(int element)
3256 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3257 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3258 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3262 static void RedrawAllLightSwitchesAndInvisibleElements()
3266 for (y = 0; y < lev_fieldy; y++)
3268 for (x = 0; x < lev_fieldx; x++)
3270 int element = Feld[x][y];
3272 if (element == EL_LIGHT_SWITCH &&
3273 game.light_time_left > 0)
3275 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3276 DrawLevelField(x, y);
3278 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3279 game.light_time_left == 0)
3281 Feld[x][y] = EL_LIGHT_SWITCH;
3282 DrawLevelField(x, y);
3284 else if (element == EL_INVISIBLE_STEELWALL ||
3285 element == EL_INVISIBLE_WALL ||
3286 element == EL_INVISIBLE_SAND)
3288 if (game.light_time_left > 0)
3289 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3291 DrawLevelField(x, y);
3293 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3294 element == EL_INVISIBLE_WALL_ACTIVE ||
3295 element == EL_INVISIBLE_SAND_ACTIVE)
3297 if (game.light_time_left == 0)
3298 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3300 DrawLevelField(x, y);
3306 static void ToggleLightSwitch(int x, int y)
3308 int element = Feld[x][y];
3310 game.light_time_left =
3311 (element == EL_LIGHT_SWITCH ?
3312 level.time_light * FRAMES_PER_SECOND : 0);
3314 RedrawAllLightSwitchesAndInvisibleElements();
3317 static void ActivateTimegateSwitch(int x, int y)
3321 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3323 for (yy = 0; yy < lev_fieldy; yy++)
3325 for (xx = 0; xx < lev_fieldx; xx++)
3327 int element = Feld[xx][yy];
3329 if (element == EL_TIMEGATE_CLOSED ||
3330 element == EL_TIMEGATE_CLOSING)
3332 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3333 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3337 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3339 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3340 DrawLevelField(xx, yy);
3347 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3350 inline static int getElementMoveStepsize(int x, int y)
3352 int element = Feld[x][y];
3353 int direction = MovDir[x][y];
3354 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3355 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3356 int horiz_move = (dx != 0);
3357 int sign = (horiz_move ? dx : dy);
3358 int step = sign * element_info[element].move_stepsize;
3360 /* special values for move stepsize for spring and things on conveyor belt */
3364 if (element == EL_SPRING)
3365 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3366 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3367 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3368 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3370 if (CAN_FALL(element) &&
3371 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3372 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3373 else if (element == EL_SPRING)
3374 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3381 void Impact(int x, int y)
3383 boolean lastline = (y == lev_fieldy-1);
3384 boolean object_hit = FALSE;
3385 boolean impact = (lastline || object_hit);
3386 int element = Feld[x][y];
3387 int smashed = EL_UNDEFINED;
3389 if (!lastline) /* check if element below was hit */
3391 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3394 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3395 MovDir[x][y + 1] != MV_DOWN ||
3396 MovPos[x][y + 1] <= TILEY / 2));
3399 object_hit = !IS_FREE(x, y + 1);
3402 /* do not smash moving elements that left the smashed field in time */
3403 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3404 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3408 smashed = MovingOrBlocked2Element(x, y + 1);
3410 impact = (lastline || object_hit);
3413 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3419 /* only reset graphic animation if graphic really changes after impact */
3421 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3423 ResetGfxAnimation(x, y);
3424 DrawLevelField(x, y);
3427 if (impact && CAN_EXPLODE_IMPACT(element))
3432 else if (impact && element == EL_PEARL)
3434 Feld[x][y] = EL_PEARL_BREAKING;
3435 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3438 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3440 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3445 if (impact && element == EL_AMOEBA_DROP)
3447 if (object_hit && IS_PLAYER(x, y + 1))
3448 KillHeroUnlessEnemyProtected(x, y + 1);
3449 else if (object_hit && smashed == EL_PENGUIN)
3453 Feld[x][y] = EL_AMOEBA_GROWING;
3454 Store[x][y] = EL_AMOEBA_WET;
3456 ResetRandomAnimationValue(x, y);
3461 if (object_hit) /* check which object was hit */
3463 if (CAN_PASS_MAGIC_WALL(element) &&
3464 (smashed == EL_MAGIC_WALL ||
3465 smashed == EL_BD_MAGIC_WALL))
3468 int activated_magic_wall =
3469 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3470 EL_BD_MAGIC_WALL_ACTIVE);
3472 /* activate magic wall / mill */
3473 for (yy = 0; yy < lev_fieldy; yy++)
3474 for (xx = 0; xx < lev_fieldx; xx++)
3475 if (Feld[xx][yy] == smashed)
3476 Feld[xx][yy] = activated_magic_wall;
3478 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3479 game.magic_wall_active = TRUE;
3481 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3482 SND_MAGIC_WALL_ACTIVATING :
3483 SND_BD_MAGIC_WALL_ACTIVATING));
3486 if (IS_PLAYER(x, y + 1))
3488 if (CAN_SMASH_PLAYER(element))
3490 KillHeroUnlessEnemyProtected(x, y + 1);
3494 else if (smashed == EL_PENGUIN)
3496 if (CAN_SMASH_PLAYER(element))
3502 else if (element == EL_BD_DIAMOND)
3504 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3510 else if (((element == EL_SP_INFOTRON ||
3511 element == EL_SP_ZONK) &&
3512 (smashed == EL_SP_SNIKSNAK ||
3513 smashed == EL_SP_ELECTRON ||
3514 smashed == EL_SP_DISK_ORANGE)) ||
3515 (element == EL_SP_INFOTRON &&
3516 smashed == EL_SP_DISK_YELLOW))
3522 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3528 else if (CAN_SMASH_EVERYTHING(element))
3530 if (IS_CLASSIC_ENEMY(smashed) ||
3531 CAN_EXPLODE_SMASHED(smashed))
3536 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3538 if (smashed == EL_LAMP ||
3539 smashed == EL_LAMP_ACTIVE)
3544 else if (smashed == EL_NUT)
3546 Feld[x][y + 1] = EL_NUT_BREAKING;
3547 PlayLevelSound(x, y, SND_NUT_BREAKING);
3548 RaiseScoreElement(EL_NUT);
3551 else if (smashed == EL_PEARL)
3553 Feld[x][y + 1] = EL_PEARL_BREAKING;
3554 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3557 else if (smashed == EL_DIAMOND)
3559 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3560 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3563 else if (IS_BELT_SWITCH(smashed))
3565 ToggleBeltSwitch(x, y + 1);
3567 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3568 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3570 ToggleSwitchgateSwitch(x, y + 1);
3572 else if (smashed == EL_LIGHT_SWITCH ||
3573 smashed == EL_LIGHT_SWITCH_ACTIVE)
3575 ToggleLightSwitch(x, y + 1);
3579 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3581 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3582 CE_OTHER_IS_SWITCHING);
3583 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3589 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3594 /* play sound of magic wall / mill */
3596 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3597 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3599 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3600 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3601 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3602 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3607 /* play sound of object that hits the ground */
3608 if (lastline || object_hit)
3609 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3612 inline static void TurnRoundExt(int x, int y)
3624 { 0, 0 }, { 0, 0 }, { 0, 0 },
3629 int left, right, back;
3633 { MV_DOWN, MV_UP, MV_RIGHT },
3634 { MV_UP, MV_DOWN, MV_LEFT },
3636 { MV_LEFT, MV_RIGHT, MV_DOWN },
3640 { MV_RIGHT, MV_LEFT, MV_UP }
3643 int element = Feld[x][y];
3644 int move_pattern = element_info[element].move_pattern;
3646 int old_move_dir = MovDir[x][y];
3647 int left_dir = turn[old_move_dir].left;
3648 int right_dir = turn[old_move_dir].right;
3649 int back_dir = turn[old_move_dir].back;
3651 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3652 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3653 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3654 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3656 int left_x = x + left_dx, left_y = y + left_dy;
3657 int right_x = x + right_dx, right_y = y + right_dy;
3658 int move_x = x + move_dx, move_y = y + move_dy;
3662 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3664 TestIfBadThingTouchesOtherBadThing(x, y);
3666 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3667 MovDir[x][y] = right_dir;
3668 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3669 MovDir[x][y] = left_dir;
3671 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3673 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3676 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3677 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3679 TestIfBadThingTouchesOtherBadThing(x, y);
3681 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3682 MovDir[x][y] = left_dir;
3683 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3684 MovDir[x][y] = right_dir;
3686 if ((element == EL_SPACESHIP ||
3687 element == EL_SP_SNIKSNAK ||
3688 element == EL_SP_ELECTRON)
3689 && MovDir[x][y] != old_move_dir)
3691 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3694 else if (element == EL_YAMYAM)
3696 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3697 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3699 if (can_turn_left && can_turn_right)
3700 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3701 else if (can_turn_left)
3702 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3703 else if (can_turn_right)
3704 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3706 MovDir[x][y] = back_dir;
3708 MovDelay[x][y] = 16 + 16 * RND(3);
3710 else if (element == EL_DARK_YAMYAM)
3712 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3713 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3715 if (can_turn_left && can_turn_right)
3716 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3717 else if (can_turn_left)
3718 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3719 else if (can_turn_right)
3720 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3722 MovDir[x][y] = back_dir;
3724 MovDelay[x][y] = 16 + 16 * RND(3);
3726 else if (element == EL_PACMAN)
3728 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3729 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3731 if (can_turn_left && can_turn_right)
3732 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3733 else if (can_turn_left)
3734 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3735 else if (can_turn_right)
3736 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3738 MovDir[x][y] = back_dir;
3740 MovDelay[x][y] = 6 + RND(40);
3742 else if (element == EL_PIG)
3744 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3745 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3746 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3747 boolean should_turn_left, should_turn_right, should_move_on;
3749 int rnd = RND(rnd_value);
3751 should_turn_left = (can_turn_left &&
3753 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3754 y + back_dy + left_dy)));
3755 should_turn_right = (can_turn_right &&
3757 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3758 y + back_dy + right_dy)));
3759 should_move_on = (can_move_on &&
3762 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3763 y + move_dy + left_dy) ||
3764 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3765 y + move_dy + right_dy)));
3767 if (should_turn_left || should_turn_right || should_move_on)
3769 if (should_turn_left && should_turn_right && should_move_on)
3770 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3771 rnd < 2 * rnd_value / 3 ? right_dir :
3773 else if (should_turn_left && should_turn_right)
3774 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3775 else if (should_turn_left && should_move_on)
3776 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3777 else if (should_turn_right && should_move_on)
3778 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3779 else if (should_turn_left)
3780 MovDir[x][y] = left_dir;
3781 else if (should_turn_right)
3782 MovDir[x][y] = right_dir;
3783 else if (should_move_on)
3784 MovDir[x][y] = old_move_dir;
3786 else if (can_move_on && rnd > rnd_value / 8)
3787 MovDir[x][y] = old_move_dir;
3788 else if (can_turn_left && can_turn_right)
3789 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3790 else if (can_turn_left && rnd > rnd_value / 8)
3791 MovDir[x][y] = left_dir;
3792 else if (can_turn_right && rnd > rnd_value/8)
3793 MovDir[x][y] = right_dir;
3795 MovDir[x][y] = back_dir;
3797 xx = x + move_xy[MovDir[x][y]].x;
3798 yy = y + move_xy[MovDir[x][y]].y;
3800 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3801 MovDir[x][y] = old_move_dir;
3805 else if (element == EL_DRAGON)
3807 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3808 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3809 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3811 int rnd = RND(rnd_value);
3814 if (FrameCounter < 1 && x == 0 && y == 29)
3815 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3818 if (can_move_on && rnd > rnd_value / 8)
3819 MovDir[x][y] = old_move_dir;
3820 else if (can_turn_left && can_turn_right)
3821 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3822 else if (can_turn_left && rnd > rnd_value / 8)
3823 MovDir[x][y] = left_dir;
3824 else if (can_turn_right && rnd > rnd_value / 8)
3825 MovDir[x][y] = right_dir;
3827 MovDir[x][y] = back_dir;
3829 xx = x + move_xy[MovDir[x][y]].x;
3830 yy = y + move_xy[MovDir[x][y]].y;
3833 if (FrameCounter < 1 && x == 0 && y == 29)
3834 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3835 xx, yy, Feld[xx][yy],
3840 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3841 MovDir[x][y] = old_move_dir;
3843 if (!IS_FREE(xx, yy))
3844 MovDir[x][y] = old_move_dir;
3848 if (FrameCounter < 1 && x == 0 && y == 29)
3849 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3854 else if (element == EL_MOLE)
3856 boolean can_move_on =
3857 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3858 IS_AMOEBOID(Feld[move_x][move_y]) ||
3859 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3862 boolean can_turn_left =
3863 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3864 IS_AMOEBOID(Feld[left_x][left_y])));
3866 boolean can_turn_right =
3867 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3868 IS_AMOEBOID(Feld[right_x][right_y])));
3870 if (can_turn_left && can_turn_right)
3871 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3872 else if (can_turn_left)
3873 MovDir[x][y] = left_dir;
3875 MovDir[x][y] = right_dir;
3878 if (MovDir[x][y] != old_move_dir)
3881 else if (element == EL_BALLOON)
3883 MovDir[x][y] = game.balloon_dir;
3886 else if (element == EL_SPRING)
3888 if (MovDir[x][y] & MV_HORIZONTAL &&
3889 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3890 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3891 MovDir[x][y] = MV_NO_MOVING;
3895 else if (element == EL_ROBOT ||
3896 element == EL_SATELLITE ||
3897 element == EL_PENGUIN)
3899 int attr_x = -1, attr_y = -1;
3910 for (i = 0; i < MAX_PLAYERS; i++)
3912 struct PlayerInfo *player = &stored_player[i];
3913 int jx = player->jx, jy = player->jy;
3915 if (!player->active)
3919 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3927 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3933 if (element == EL_PENGUIN)
3936 static int xy[4][2] =
3944 for (i = 0; i < 4; i++)
3946 int ex = x + xy[i % 4][0];
3947 int ey = y + xy[i % 4][1];
3949 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3958 MovDir[x][y] = MV_NO_MOVING;
3960 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3961 else if (attr_x > x)
3962 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3964 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3965 else if (attr_y > y)
3966 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3968 if (element == EL_ROBOT)
3972 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3973 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3974 Moving2Blocked(x, y, &newx, &newy);
3976 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3977 MovDelay[x][y] = 8 + 8 * !RND(3);
3979 MovDelay[x][y] = 16;
3981 else if (element == EL_PENGUIN)
3987 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3989 boolean first_horiz = RND(2);
3990 int new_move_dir = MovDir[x][y];
3993 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3994 Moving2Blocked(x, y, &newx, &newy);
3996 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4000 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4001 Moving2Blocked(x, y, &newx, &newy);
4003 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4006 MovDir[x][y] = old_move_dir;
4010 else /* (element == EL_SATELLITE) */
4016 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4018 boolean first_horiz = RND(2);
4019 int new_move_dir = MovDir[x][y];
4022 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4023 Moving2Blocked(x, y, &newx, &newy);
4025 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4029 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4030 Moving2Blocked(x, y, &newx, &newy);
4032 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4035 MovDir[x][y] = old_move_dir;
4040 else if (move_pattern == MV_TURNING_LEFT ||
4041 move_pattern == MV_TURNING_RIGHT ||
4042 move_pattern == MV_TURNING_LEFT_RIGHT ||
4043 move_pattern == MV_TURNING_RIGHT_LEFT ||
4044 move_pattern == MV_TURNING_RANDOM ||
4045 move_pattern == MV_ALL_DIRECTIONS)
4047 boolean can_turn_left =
4048 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4049 boolean can_turn_right =
4050 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4052 if (move_pattern == MV_TURNING_LEFT)
4053 MovDir[x][y] = left_dir;
4054 else if (move_pattern == MV_TURNING_RIGHT)
4055 MovDir[x][y] = right_dir;
4056 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4057 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4058 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4059 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4060 else if (move_pattern == MV_TURNING_RANDOM)
4061 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4062 can_turn_right && !can_turn_left ? right_dir :
4063 RND(2) ? left_dir : right_dir);
4064 else if (can_turn_left && can_turn_right)
4065 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4066 else if (can_turn_left)
4067 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4068 else if (can_turn_right)
4069 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4071 MovDir[x][y] = back_dir;
4073 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4075 else if (move_pattern == MV_HORIZONTAL ||
4076 move_pattern == MV_VERTICAL)
4078 if (move_pattern & old_move_dir)
4079 MovDir[x][y] = back_dir;
4080 else if (move_pattern == MV_HORIZONTAL)
4081 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4082 else if (move_pattern == MV_VERTICAL)
4083 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4085 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4087 else if (move_pattern & MV_ANY_DIRECTION)
4089 MovDir[x][y] = move_pattern;
4090 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4092 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4094 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4095 MovDir[x][y] = left_dir;
4096 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4097 MovDir[x][y] = right_dir;
4099 if (MovDir[x][y] != old_move_dir)
4100 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4102 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4104 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4105 MovDir[x][y] = right_dir;
4106 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4107 MovDir[x][y] = left_dir;
4109 if (MovDir[x][y] != old_move_dir)
4110 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4112 else if (move_pattern == MV_TOWARDS_PLAYER ||
4113 move_pattern == MV_AWAY_FROM_PLAYER)
4115 int attr_x = -1, attr_y = -1;
4117 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4128 for (i = 0; i < MAX_PLAYERS; i++)
4130 struct PlayerInfo *player = &stored_player[i];
4131 int jx = player->jx, jy = player->jy;
4133 if (!player->active)
4137 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4145 MovDir[x][y] = MV_NO_MOVING;
4147 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4148 else if (attr_x > x)
4149 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4151 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4152 else if (attr_y > y)
4153 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4155 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4157 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4159 boolean first_horiz = RND(2);
4160 int new_move_dir = MovDir[x][y];
4163 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4164 Moving2Blocked(x, y, &newx, &newy);
4166 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4170 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4171 Moving2Blocked(x, y, &newx, &newy);
4173 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4176 MovDir[x][y] = old_move_dir;
4179 else if (move_pattern == MV_WHEN_PUSHED ||
4180 move_pattern == MV_WHEN_DROPPED)
4182 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4183 MovDir[x][y] = MV_NO_MOVING;
4187 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4189 static int test_xy[7][2] =
4199 static int test_dir[7] =
4209 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4210 int move_preference = -1000000; /* start with very low preference */
4211 int new_move_dir = MV_NO_MOVING;
4212 int start_test = RND(4);
4215 for (i = 0; i < 4; i++)
4217 int move_dir = test_dir[start_test + i];
4218 int move_dir_preference;
4220 xx = x + test_xy[start_test + i][0];
4221 yy = y + test_xy[start_test + i][1];
4223 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4224 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4226 new_move_dir = move_dir;
4231 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4234 move_dir_preference = -1 * RunnerVisit[xx][yy];
4235 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4236 move_dir_preference = PlayerVisit[xx][yy];
4238 if (move_dir_preference > move_preference)
4240 /* prefer field that has not been visited for the longest time */
4241 move_preference = move_dir_preference;
4242 new_move_dir = move_dir;
4244 else if (move_dir_preference == move_preference &&
4245 move_dir == old_move_dir)
4247 /* prefer last direction when all directions are preferred equally */
4248 move_preference = move_dir_preference;
4249 new_move_dir = move_dir;
4253 MovDir[x][y] = new_move_dir;
4254 if (old_move_dir != new_move_dir)
4259 static void TurnRound(int x, int y)
4261 int direction = MovDir[x][y];
4264 GfxDir[x][y] = MovDir[x][y];
4270 GfxDir[x][y] = MovDir[x][y];
4273 if (direction != MovDir[x][y])
4278 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4281 GfxAction[x][y] = ACTION_WAITING;
4285 static boolean JustBeingPushed(int x, int y)
4289 for (i = 0; i < MAX_PLAYERS; i++)
4291 struct PlayerInfo *player = &stored_player[i];
4293 if (player->active && player->is_pushing && player->MovPos)
4295 int next_jx = player->jx + (player->jx - player->last_jx);
4296 int next_jy = player->jy + (player->jy - player->last_jy);
4298 if (x == next_jx && y == next_jy)
4306 void StartMoving(int x, int y)
4308 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4309 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4310 int element = Feld[x][y];
4316 if (MovDelay[x][y] == 0)
4317 GfxAction[x][y] = ACTION_DEFAULT;
4319 /* !!! this should be handled more generic (not only for mole) !!! */
4320 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4321 GfxAction[x][y] = ACTION_DEFAULT;
4324 if (CAN_FALL(element) && y < lev_fieldy - 1)
4326 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4327 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4328 if (JustBeingPushed(x, y))
4331 if (element == EL_QUICKSAND_FULL)
4333 if (IS_FREE(x, y + 1))
4335 InitMovingField(x, y, MV_DOWN);
4336 started_moving = TRUE;
4338 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4339 Store[x][y] = EL_ROCK;
4341 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4343 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4346 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4348 if (!MovDelay[x][y])
4349 MovDelay[x][y] = TILEY + 1;
4358 Feld[x][y] = EL_QUICKSAND_EMPTY;
4359 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4360 Store[x][y + 1] = Store[x][y];
4363 PlayLevelSoundAction(x, y, ACTION_FILLING);
4365 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4369 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4370 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4372 InitMovingField(x, y, MV_DOWN);
4373 started_moving = TRUE;
4375 Feld[x][y] = EL_QUICKSAND_FILLING;
4376 Store[x][y] = element;
4378 PlayLevelSoundAction(x, y, ACTION_FILLING);
4380 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4383 else if (element == EL_MAGIC_WALL_FULL)
4385 if (IS_FREE(x, y + 1))
4387 InitMovingField(x, y, MV_DOWN);
4388 started_moving = TRUE;
4390 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4391 Store[x][y] = EL_CHANGED(Store[x][y]);
4393 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4395 if (!MovDelay[x][y])
4396 MovDelay[x][y] = TILEY/4 + 1;
4405 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4406 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4407 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4411 else if (element == EL_BD_MAGIC_WALL_FULL)
4413 if (IS_FREE(x, y + 1))
4415 InitMovingField(x, y, MV_DOWN);
4416 started_moving = TRUE;
4418 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4419 Store[x][y] = EL_CHANGED2(Store[x][y]);
4421 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4423 if (!MovDelay[x][y])
4424 MovDelay[x][y] = TILEY/4 + 1;
4433 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4434 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4435 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4439 else if (CAN_PASS_MAGIC_WALL(element) &&
4440 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4441 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4443 InitMovingField(x, y, MV_DOWN);
4444 started_moving = TRUE;
4447 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4448 EL_BD_MAGIC_WALL_FILLING);
4449 Store[x][y] = element;
4452 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4454 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4459 InitMovingField(x, y, MV_DOWN);
4460 started_moving = TRUE;
4462 Store[x][y] = EL_ACID;
4464 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4465 GfxAction[x][y + 1] = ACTION_ACTIVE;
4469 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4470 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4471 (Feld[x][y + 1] == EL_BLOCKED)) ||
4472 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4473 CAN_SMASH(element) && WasJustFalling[x][y] &&
4474 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4478 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4479 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4480 WasJustMoving[x][y] && !Pushed[x][y + 1])
4482 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4483 WasJustMoving[x][y])
4488 /* this is needed for a special case not covered by calling "Impact()"
4489 from "ContinueMoving()": if an element moves to a tile directly below
4490 another element which was just falling on that tile (which was empty
4491 in the previous frame), the falling element above would just stop
4492 instead of smashing the element below (in previous version, the above
4493 element was just checked for "moving" instead of "falling", resulting
4494 in incorrect smashes caused by horizontal movement of the above
4495 element; also, the case of the player being the element to smash was
4496 simply not covered here... :-/ ) */
4499 WasJustMoving[x][y] = 0;
4500 WasJustFalling[x][y] = 0;
4505 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4507 if (MovDir[x][y] == MV_NO_MOVING)
4509 InitMovingField(x, y, MV_DOWN);
4510 started_moving = TRUE;
4513 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4515 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4516 MovDir[x][y] = MV_DOWN;
4518 InitMovingField(x, y, MV_DOWN);
4519 started_moving = TRUE;
4521 else if (element == EL_AMOEBA_DROP)
4523 Feld[x][y] = EL_AMOEBA_GROWING;
4524 Store[x][y] = EL_AMOEBA_WET;
4526 /* Store[x][y + 1] must be zero, because:
4527 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4530 #if OLD_GAME_BEHAVIOUR
4531 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4533 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4534 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4535 element != EL_DX_SUPABOMB)
4538 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4539 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4540 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4541 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4544 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4545 (IS_FREE(x - 1, y + 1) ||
4546 Feld[x - 1][y + 1] == EL_ACID));
4547 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4548 (IS_FREE(x + 1, y + 1) ||
4549 Feld[x + 1][y + 1] == EL_ACID));
4550 boolean can_fall_any = (can_fall_left || can_fall_right);
4551 boolean can_fall_both = (can_fall_left && can_fall_right);
4553 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4555 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4557 if (slippery_type == SLIPPERY_ONLY_LEFT)
4558 can_fall_right = FALSE;
4559 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4560 can_fall_left = FALSE;
4561 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4562 can_fall_right = FALSE;
4563 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4564 can_fall_left = FALSE;
4566 can_fall_any = (can_fall_left || can_fall_right);
4567 can_fall_both = (can_fall_left && can_fall_right);
4572 if (can_fall_both &&
4573 (game.emulation != EMU_BOULDERDASH &&
4574 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4575 can_fall_left = !(can_fall_right = RND(2));
4577 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4578 started_moving = TRUE;
4582 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4584 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4587 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4588 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4589 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4590 int belt_dir = game.belt_dir[belt_nr];
4592 if ((belt_dir == MV_LEFT && left_is_free) ||
4593 (belt_dir == MV_RIGHT && right_is_free))
4595 InitMovingField(x, y, belt_dir);
4596 started_moving = TRUE;
4598 GfxAction[x][y] = ACTION_DEFAULT;
4603 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4604 if (CAN_MOVE(element) && !started_moving)
4606 int move_pattern = element_info[element].move_pattern;
4609 Moving2Blocked(x, y, &newx, &newy);
4612 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4615 if ((element == EL_SATELLITE ||
4616 element == EL_BALLOON ||
4617 element == EL_SPRING)
4618 && JustBeingPushed(x, y))
4623 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4624 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4625 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4628 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4629 element, element_info[element].token_name,
4630 WasJustMoving[x][y],
4631 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4632 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4633 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4634 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4638 WasJustMoving[x][y] = 0;
4641 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4644 if (Feld[x][y] != element) /* element has changed */
4646 element = Feld[x][y];
4647 move_pattern = element_info[element].move_pattern;
4649 if (!CAN_MOVE(element))
4653 if (Feld[x][y] != element) /* element has changed */
4661 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4662 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4664 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4666 Moving2Blocked(x, y, &newx, &newy);
4667 if (Feld[newx][newy] == EL_BLOCKED)
4668 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4674 if (FrameCounter < 1 && x == 0 && y == 29)
4675 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4678 if (!MovDelay[x][y]) /* start new movement phase */
4680 /* all objects that can change their move direction after each step
4681 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4683 if (element != EL_YAMYAM &&
4684 element != EL_DARK_YAMYAM &&
4685 element != EL_PACMAN &&
4686 !(move_pattern & MV_ANY_DIRECTION) &&
4687 move_pattern != MV_TURNING_LEFT &&
4688 move_pattern != MV_TURNING_RIGHT &&
4689 move_pattern != MV_TURNING_LEFT_RIGHT &&
4690 move_pattern != MV_TURNING_RIGHT_LEFT &&
4691 move_pattern != MV_TURNING_RANDOM)
4696 if (FrameCounter < 1 && x == 0 && y == 29)
4697 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4700 if (MovDelay[x][y] && (element == EL_BUG ||
4701 element == EL_SPACESHIP ||
4702 element == EL_SP_SNIKSNAK ||
4703 element == EL_SP_ELECTRON ||
4704 element == EL_MOLE))
4705 DrawLevelField(x, y);
4709 if (MovDelay[x][y]) /* wait some time before next movement */
4714 if (element == EL_YAMYAM)
4717 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4718 DrawLevelElementAnimation(x, y, element);
4722 if (MovDelay[x][y]) /* element still has to wait some time */
4725 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4726 ResetGfxAnimation(x, y);
4730 if (GfxAction[x][y] != ACTION_WAITING)
4731 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4733 GfxAction[x][y] = ACTION_WAITING;
4737 if (element == EL_ROBOT ||
4739 element == EL_PACMAN ||
4741 element == EL_YAMYAM ||
4742 element == EL_DARK_YAMYAM)
4745 DrawLevelElementAnimation(x, y, element);
4747 DrawLevelElementAnimationIfNeeded(x, y, element);
4749 PlayLevelSoundAction(x, y, ACTION_WAITING);
4751 else if (element == EL_SP_ELECTRON)
4752 DrawLevelElementAnimationIfNeeded(x, y, element);
4753 else if (element == EL_DRAGON)
4756 int dir = MovDir[x][y];
4757 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4758 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4759 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4760 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4761 dir == MV_UP ? IMG_FLAMES_1_UP :
4762 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4763 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4766 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4769 GfxAction[x][y] = ACTION_ATTACKING;
4771 if (IS_PLAYER(x, y))
4772 DrawPlayerField(x, y);
4774 DrawLevelField(x, y);
4776 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4778 for (i = 1; i <= 3; i++)
4780 int xx = x + i * dx;
4781 int yy = y + i * dy;
4782 int sx = SCREENX(xx);
4783 int sy = SCREENY(yy);
4784 int flame_graphic = graphic + (i - 1);
4786 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4791 int flamed = MovingOrBlocked2Element(xx, yy);
4793 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4796 RemoveMovingField(xx, yy);
4798 Feld[xx][yy] = EL_FLAMES;
4799 if (IN_SCR_FIELD(sx, sy))
4801 DrawLevelFieldCrumbledSand(xx, yy);
4802 DrawGraphic(sx, sy, flame_graphic, frame);
4807 if (Feld[xx][yy] == EL_FLAMES)
4808 Feld[xx][yy] = EL_EMPTY;
4809 DrawLevelField(xx, yy);
4814 if (MovDelay[x][y]) /* element still has to wait some time */
4816 PlayLevelSoundAction(x, y, ACTION_WAITING);
4822 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4823 for all other elements GfxAction will be set by InitMovingField() */
4824 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4825 GfxAction[x][y] = ACTION_MOVING;
4829 /* now make next step */
4831 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4833 if (DONT_COLLIDE_WITH(element) &&
4834 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4835 !PLAYER_ENEMY_PROTECTED(newx, newy))
4838 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4842 /* player killed by element which is deadly when colliding with */
4844 KillHero(PLAYERINFO(newx, newy));
4849 else if ((element == EL_PENGUIN ||
4850 element == EL_ROBOT ||
4851 element == EL_SATELLITE ||
4852 element == EL_BALLOON ||
4853 IS_CUSTOM_ELEMENT(element)) &&
4854 IN_LEV_FIELD(newx, newy) &&
4855 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4858 Store[x][y] = EL_ACID;
4860 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4862 if (Feld[newx][newy] == EL_EXIT_OPEN)
4866 DrawLevelField(x, y);
4868 Feld[x][y] = EL_EMPTY;
4869 DrawLevelField(x, y);
4872 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4873 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4874 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4876 local_player->friends_still_needed--;
4877 if (!local_player->friends_still_needed &&
4878 !local_player->GameOver && AllPlayersGone)
4879 local_player->LevelSolved = local_player->GameOver = TRUE;
4883 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4885 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4886 DrawLevelField(newx, newy);
4888 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4890 else if (!IS_FREE(newx, newy))
4892 GfxAction[x][y] = ACTION_WAITING;
4894 if (IS_PLAYER(x, y))
4895 DrawPlayerField(x, y);
4897 DrawLevelField(x, y);
4902 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4904 if (IS_FOOD_PIG(Feld[newx][newy]))
4906 if (IS_MOVING(newx, newy))
4907 RemoveMovingField(newx, newy);
4910 Feld[newx][newy] = EL_EMPTY;
4911 DrawLevelField(newx, newy);
4914 PlayLevelSound(x, y, SND_PIG_DIGGING);
4916 else if (!IS_FREE(newx, newy))
4918 if (IS_PLAYER(x, y))
4919 DrawPlayerField(x, y);
4921 DrawLevelField(x, y);
4930 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4933 else if (IS_CUSTOM_ELEMENT(element) &&
4934 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4938 !IS_FREE(newx, newy)
4943 int new_element = Feld[newx][newy];
4946 printf("::: '%s' digs '%s' [%d]\n",
4947 element_info[element].token_name,
4948 element_info[Feld[newx][newy]].token_name,
4949 StorePlayer[newx][newy]);
4952 if (!IS_FREE(newx, newy))
4954 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4955 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4958 /* no element can dig solid indestructible elements */
4959 if (IS_INDESTRUCTIBLE(new_element) &&
4960 !IS_DIGGABLE(new_element) &&
4961 !IS_COLLECTIBLE(new_element))
4964 if (AmoebaNr[newx][newy] &&
4965 (new_element == EL_AMOEBA_FULL ||
4966 new_element == EL_BD_AMOEBA ||
4967 new_element == EL_AMOEBA_GROWING))
4969 AmoebaCnt[AmoebaNr[newx][newy]]--;
4970 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4973 if (IS_MOVING(newx, newy))
4974 RemoveMovingField(newx, newy);
4977 RemoveField(newx, newy);
4978 DrawLevelField(newx, newy);
4981 PlayLevelSoundAction(x, y, action);
4984 if (new_element == element_info[element].move_enter_element)
4985 element_info[element].can_leave_element = TRUE;
4987 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4989 RunnerVisit[x][y] = FrameCounter;
4990 PlayerVisit[x][y] /= 8; /* expire player visit path */
4996 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4998 if (!IS_FREE(newx, newy))
5000 if (IS_PLAYER(x, y))
5001 DrawPlayerField(x, y);
5003 DrawLevelField(x, y);
5009 boolean wanna_flame = !RND(10);
5010 int dx = newx - x, dy = newy - y;
5011 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5012 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5013 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5014 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5015 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5016 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5019 IS_CLASSIC_ENEMY(element1) ||
5020 IS_CLASSIC_ENEMY(element2)) &&
5021 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5022 element1 != EL_FLAMES && element2 != EL_FLAMES)
5025 ResetGfxAnimation(x, y);
5026 GfxAction[x][y] = ACTION_ATTACKING;
5029 if (IS_PLAYER(x, y))
5030 DrawPlayerField(x, y);
5032 DrawLevelField(x, y);
5034 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5036 MovDelay[x][y] = 50;
5038 Feld[newx][newy] = EL_FLAMES;
5039 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5040 Feld[newx1][newy1] = EL_FLAMES;
5041 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5042 Feld[newx2][newy2] = EL_FLAMES;
5048 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5049 Feld[newx][newy] == EL_DIAMOND)
5051 if (IS_MOVING(newx, newy))
5052 RemoveMovingField(newx, newy);
5055 Feld[newx][newy] = EL_EMPTY;
5056 DrawLevelField(newx, newy);
5059 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5061 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5062 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5064 if (AmoebaNr[newx][newy])
5066 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5067 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5068 Feld[newx][newy] == EL_BD_AMOEBA)
5069 AmoebaCnt[AmoebaNr[newx][newy]]--;
5072 if (IS_MOVING(newx, newy))
5073 RemoveMovingField(newx, newy);
5076 Feld[newx][newy] = EL_EMPTY;
5077 DrawLevelField(newx, newy);
5080 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5082 else if ((element == EL_PACMAN || element == EL_MOLE)
5083 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5085 if (AmoebaNr[newx][newy])
5087 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5088 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5089 Feld[newx][newy] == EL_BD_AMOEBA)
5090 AmoebaCnt[AmoebaNr[newx][newy]]--;
5093 if (element == EL_MOLE)
5095 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5096 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5098 ResetGfxAnimation(x, y);
5099 GfxAction[x][y] = ACTION_DIGGING;
5100 DrawLevelField(x, y);
5102 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5104 return; /* wait for shrinking amoeba */
5106 else /* element == EL_PACMAN */
5108 Feld[newx][newy] = EL_EMPTY;
5109 DrawLevelField(newx, newy);
5110 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5113 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5114 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5115 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5117 /* wait for shrinking amoeba to completely disappear */
5120 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5122 /* object was running against a wall */
5127 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5128 DrawLevelElementAnimation(x, y, element);
5130 if (element == EL_BUG ||
5131 element == EL_SPACESHIP ||
5132 element == EL_SP_SNIKSNAK)
5133 DrawLevelField(x, y);
5134 else if (element == EL_MOLE)
5135 DrawLevelField(x, y);
5136 else if (element == EL_BD_BUTTERFLY ||
5137 element == EL_BD_FIREFLY)
5138 DrawLevelElementAnimationIfNeeded(x, y, element);
5139 else if (element == EL_SATELLITE)
5140 DrawLevelElementAnimationIfNeeded(x, y, element);
5141 else if (element == EL_SP_ELECTRON)
5142 DrawLevelElementAnimationIfNeeded(x, y, element);
5145 if (DONT_TOUCH(element))
5146 TestIfBadThingTouchesHero(x, y);
5149 PlayLevelSoundAction(x, y, ACTION_WAITING);
5155 InitMovingField(x, y, MovDir[x][y]);
5157 PlayLevelSoundAction(x, y, ACTION_MOVING);
5161 ContinueMoving(x, y);
5164 void ContinueMoving(int x, int y)
5166 int element = Feld[x][y];
5167 struct ElementInfo *ei = &element_info[element];
5168 int direction = MovDir[x][y];
5169 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5170 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5171 int newx = x + dx, newy = y + dy;
5173 int nextx = newx + dx, nexty = newy + dy;
5175 boolean pushed = Pushed[x][y];
5177 MovPos[x][y] += getElementMoveStepsize(x, y);
5179 if (pushed) /* special case: moving object pushed by player */
5180 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5182 if (ABS(MovPos[x][y]) < TILEX)
5184 DrawLevelField(x, y);
5186 return; /* element is still moving */
5189 /* element reached destination field */
5191 Feld[x][y] = EL_EMPTY;
5192 Feld[newx][newy] = element;
5193 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5195 if (element == EL_MOLE)
5197 Feld[x][y] = EL_SAND;
5199 DrawLevelFieldCrumbledSandNeighbours(x, y);
5201 else if (element == EL_QUICKSAND_FILLING)
5203 element = Feld[newx][newy] = get_next_element(element);
5204 Store[newx][newy] = Store[x][y];
5206 else if (element == EL_QUICKSAND_EMPTYING)
5208 Feld[x][y] = get_next_element(element);
5209 element = Feld[newx][newy] = Store[x][y];
5211 else if (element == EL_MAGIC_WALL_FILLING)
5213 element = Feld[newx][newy] = get_next_element(element);
5214 if (!game.magic_wall_active)
5215 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5216 Store[newx][newy] = Store[x][y];
5218 else if (element == EL_MAGIC_WALL_EMPTYING)
5220 Feld[x][y] = get_next_element(element);
5221 if (!game.magic_wall_active)
5222 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5223 element = Feld[newx][newy] = Store[x][y];
5225 else if (element == EL_BD_MAGIC_WALL_FILLING)
5227 element = Feld[newx][newy] = get_next_element(element);
5228 if (!game.magic_wall_active)
5229 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5230 Store[newx][newy] = Store[x][y];
5232 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5234 Feld[x][y] = get_next_element(element);
5235 if (!game.magic_wall_active)
5236 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5237 element = Feld[newx][newy] = Store[x][y];
5239 else if (element == EL_AMOEBA_DROPPING)
5241 Feld[x][y] = get_next_element(element);
5242 element = Feld[newx][newy] = Store[x][y];
5244 else if (element == EL_SOKOBAN_OBJECT)
5247 Feld[x][y] = Back[x][y];
5249 if (Back[newx][newy])
5250 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5252 Back[x][y] = Back[newx][newy] = 0;
5254 else if (Store[x][y] == EL_ACID)
5256 element = Feld[newx][newy] = EL_ACID;
5260 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5261 MovDelay[newx][newy] = 0;
5263 /* copy element change control values to new field */
5264 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5265 ChangePage[newx][newy] = ChangePage[x][y];
5266 Changed[newx][newy] = Changed[x][y];
5267 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5269 ChangeDelay[x][y] = 0;
5270 ChangePage[x][y] = -1;
5271 Changed[x][y] = CE_BITMASK_DEFAULT;
5272 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5274 /* copy animation control values to new field */
5275 GfxFrame[newx][newy] = GfxFrame[x][y];
5276 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5277 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5278 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5280 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5282 ResetGfxAnimation(x, y); /* reset animation values for old field */
5285 /* some elements can leave other elements behind after moving */
5286 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5287 ei->move_leave_element != EL_EMPTY &&
5288 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5289 ei->can_leave_element_last))
5291 Feld[x][y] = ei->move_leave_element;
5292 InitField(x, y, FALSE);
5294 if (GFX_CRUMBLED(Feld[x][y]))
5295 DrawLevelFieldCrumbledSandNeighbours(x, y);
5298 ei->can_leave_element_last = ei->can_leave_element;
5299 ei->can_leave_element = FALSE;
5303 /* 2.1.1 (does not work correctly for spring) */
5304 if (!CAN_MOVE(element))
5305 MovDir[newx][newy] = 0;
5309 /* (does not work for falling objects that slide horizontally) */
5310 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5311 MovDir[newx][newy] = 0;
5314 if (!CAN_MOVE(element) ||
5315 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5316 MovDir[newx][newy] = 0;
5319 if (!CAN_MOVE(element) ||
5320 (CAN_FALL(element) && direction == MV_DOWN))
5321 GfxDir[x][y] = MovDir[newx][newy] = 0;
5326 DrawLevelField(x, y);
5327 DrawLevelField(newx, newy);
5329 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5331 /* prevent pushed element from moving on in pushed direction */
5332 if (pushed && CAN_MOVE(element) &&
5333 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5334 !(element_info[element].move_pattern & direction))
5335 TurnRound(newx, newy);
5337 if (!pushed) /* special case: moving object pushed by player */
5339 WasJustMoving[newx][newy] = 3;
5341 if (CAN_FALL(element) && direction == MV_DOWN)
5342 WasJustFalling[newx][newy] = 3;
5345 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5347 TestIfBadThingTouchesHero(newx, newy);
5348 TestIfBadThingTouchesFriend(newx, newy);
5350 if (!IS_CUSTOM_ELEMENT(element))
5351 TestIfBadThingTouchesOtherBadThing(newx, newy);
5353 else if (element == EL_PENGUIN)
5354 TestIfFriendTouchesBadThing(newx, newy);
5356 if (CAN_FALL(element) && direction == MV_DOWN &&
5357 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5361 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5365 if (ChangePage[newx][newy] != -1) /* delayed change */
5366 ChangeElement(newx, newy, ChangePage[newx][newy]);
5371 TestIfElementHitsCustomElement(newx, newy, direction);
5375 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5377 int hitting_element = Feld[newx][newy];
5379 /* !!! fix side (direction) orientation here and elsewhere !!! */
5380 CheckElementSideChange(newx, newy, hitting_element,
5381 direction, CE_HITTING_SOMETHING, -1);
5384 if (IN_LEV_FIELD(nextx, nexty))
5386 int opposite_direction = MV_DIR_OPPOSITE(direction);
5387 int hitting_side = direction;
5388 int touched_side = opposite_direction;
5389 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5390 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5391 MovDir[nextx][nexty] != direction ||
5392 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5398 CheckElementSideChange(nextx, nexty, touched_element,
5399 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5401 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5402 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5404 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5406 struct ElementChangeInfo *change =
5407 &element_info[hitting_element].change_page[i];
5409 if (change->can_change &&
5410 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5411 change->sides & touched_side &&
5412 change->trigger_element == touched_element)
5414 CheckElementSideChange(newx, newy, hitting_element,
5415 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5421 if (IS_CUSTOM_ELEMENT(touched_element) &&
5422 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5424 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5426 struct ElementChangeInfo *change =
5427 &element_info[touched_element].change_page[i];
5429 if (change->can_change &&
5430 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5431 change->sides & hitting_side &&
5432 change->trigger_element == hitting_element)
5434 CheckElementSideChange(nextx, nexty, touched_element,
5435 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5446 TestIfPlayerTouchesCustomElement(newx, newy);
5447 TestIfElementTouchesCustomElement(newx, newy);
5450 int AmoebeNachbarNr(int ax, int ay)
5453 int element = Feld[ax][ay];
5455 static int xy[4][2] =
5463 for (i = 0; i < 4; i++)
5465 int x = ax + xy[i][0];
5466 int y = ay + xy[i][1];
5468 if (!IN_LEV_FIELD(x, y))
5471 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5472 group_nr = AmoebaNr[x][y];
5478 void AmoebenVereinigen(int ax, int ay)
5480 int i, x, y, xx, yy;
5481 int new_group_nr = AmoebaNr[ax][ay];
5482 static int xy[4][2] =
5490 if (new_group_nr == 0)
5493 for (i = 0; i < 4; i++)
5498 if (!IN_LEV_FIELD(x, y))
5501 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5502 Feld[x][y] == EL_BD_AMOEBA ||
5503 Feld[x][y] == EL_AMOEBA_DEAD) &&
5504 AmoebaNr[x][y] != new_group_nr)
5506 int old_group_nr = AmoebaNr[x][y];
5508 if (old_group_nr == 0)
5511 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5512 AmoebaCnt[old_group_nr] = 0;
5513 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5514 AmoebaCnt2[old_group_nr] = 0;
5516 for (yy = 0; yy < lev_fieldy; yy++)
5518 for (xx = 0; xx < lev_fieldx; xx++)
5520 if (AmoebaNr[xx][yy] == old_group_nr)
5521 AmoebaNr[xx][yy] = new_group_nr;
5528 void AmoebeUmwandeln(int ax, int ay)
5532 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5534 int group_nr = AmoebaNr[ax][ay];
5539 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5540 printf("AmoebeUmwandeln(): This should never happen!\n");
5545 for (y = 0; y < lev_fieldy; y++)
5547 for (x = 0; x < lev_fieldx; x++)
5549 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5552 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5556 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5557 SND_AMOEBA_TURNING_TO_GEM :
5558 SND_AMOEBA_TURNING_TO_ROCK));
5563 static int xy[4][2] =
5571 for (i = 0; i < 4; i++)
5576 if (!IN_LEV_FIELD(x, y))
5579 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5581 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5582 SND_AMOEBA_TURNING_TO_GEM :
5583 SND_AMOEBA_TURNING_TO_ROCK));
5590 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5593 int group_nr = AmoebaNr[ax][ay];
5594 boolean done = FALSE;
5599 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5600 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5605 for (y = 0; y < lev_fieldy; y++)
5607 for (x = 0; x < lev_fieldx; x++)
5609 if (AmoebaNr[x][y] == group_nr &&
5610 (Feld[x][y] == EL_AMOEBA_DEAD ||
5611 Feld[x][y] == EL_BD_AMOEBA ||
5612 Feld[x][y] == EL_AMOEBA_GROWING))
5615 Feld[x][y] = new_element;
5616 InitField(x, y, FALSE);
5617 DrawLevelField(x, y);
5624 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5625 SND_BD_AMOEBA_TURNING_TO_ROCK :
5626 SND_BD_AMOEBA_TURNING_TO_GEM));
5629 void AmoebeWaechst(int x, int y)
5631 static unsigned long sound_delay = 0;
5632 static unsigned long sound_delay_value = 0;
5634 if (!MovDelay[x][y]) /* start new growing cycle */
5638 if (DelayReached(&sound_delay, sound_delay_value))
5641 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5643 if (Store[x][y] == EL_BD_AMOEBA)
5644 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5646 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5648 sound_delay_value = 30;
5652 if (MovDelay[x][y]) /* wait some time before growing bigger */
5655 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5657 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5658 6 - MovDelay[x][y]);
5660 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5663 if (!MovDelay[x][y])
5665 Feld[x][y] = Store[x][y];
5667 DrawLevelField(x, y);
5672 void AmoebaDisappearing(int x, int y)
5674 static unsigned long sound_delay = 0;
5675 static unsigned long sound_delay_value = 0;
5677 if (!MovDelay[x][y]) /* start new shrinking cycle */
5681 if (DelayReached(&sound_delay, sound_delay_value))
5682 sound_delay_value = 30;
5685 if (MovDelay[x][y]) /* wait some time before shrinking */
5688 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5690 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5691 6 - MovDelay[x][y]);
5693 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5696 if (!MovDelay[x][y])
5698 Feld[x][y] = EL_EMPTY;
5699 DrawLevelField(x, y);
5701 /* don't let mole enter this field in this cycle;
5702 (give priority to objects falling to this field from above) */
5708 void AmoebeAbleger(int ax, int ay)
5711 int element = Feld[ax][ay];
5712 int graphic = el2img(element);
5713 int newax = ax, neway = ay;
5714 static int xy[4][2] =
5722 if (!level.amoeba_speed)
5724 Feld[ax][ay] = EL_AMOEBA_DEAD;
5725 DrawLevelField(ax, ay);
5729 if (IS_ANIMATED(graphic))
5730 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5732 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5733 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5735 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5738 if (MovDelay[ax][ay])
5742 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5745 int x = ax + xy[start][0];
5746 int y = ay + xy[start][1];
5748 if (!IN_LEV_FIELD(x, y))
5751 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5752 if (IS_FREE(x, y) ||
5753 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5759 if (newax == ax && neway == ay)
5762 else /* normal or "filled" (BD style) amoeba */
5765 boolean waiting_for_player = FALSE;
5767 for (i = 0; i < 4; i++)
5769 int j = (start + i) % 4;
5770 int x = ax + xy[j][0];
5771 int y = ay + xy[j][1];
5773 if (!IN_LEV_FIELD(x, y))
5776 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5777 if (IS_FREE(x, y) ||
5778 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5784 else if (IS_PLAYER(x, y))
5785 waiting_for_player = TRUE;
5788 if (newax == ax && neway == ay) /* amoeba cannot grow */
5790 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5792 Feld[ax][ay] = EL_AMOEBA_DEAD;
5793 DrawLevelField(ax, ay);
5794 AmoebaCnt[AmoebaNr[ax][ay]]--;
5796 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5798 if (element == EL_AMOEBA_FULL)
5799 AmoebeUmwandeln(ax, ay);
5800 else if (element == EL_BD_AMOEBA)
5801 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5806 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5808 /* amoeba gets larger by growing in some direction */
5810 int new_group_nr = AmoebaNr[ax][ay];
5813 if (new_group_nr == 0)
5815 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5816 printf("AmoebeAbleger(): This should never happen!\n");
5821 AmoebaNr[newax][neway] = new_group_nr;
5822 AmoebaCnt[new_group_nr]++;
5823 AmoebaCnt2[new_group_nr]++;
5825 /* if amoeba touches other amoeba(s) after growing, unify them */
5826 AmoebenVereinigen(newax, neway);
5828 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5830 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5836 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5837 (neway == lev_fieldy - 1 && newax != ax))
5839 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5840 Store[newax][neway] = element;
5842 else if (neway == ay)
5844 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5846 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5848 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5853 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5854 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5855 Store[ax][ay] = EL_AMOEBA_DROP;
5856 ContinueMoving(ax, ay);
5860 DrawLevelField(newax, neway);
5863 void Life(int ax, int ay)
5866 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5868 int element = Feld[ax][ay];
5869 int graphic = el2img(element);
5870 boolean changed = FALSE;
5872 if (IS_ANIMATED(graphic))
5873 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5878 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5879 MovDelay[ax][ay] = life_time;
5881 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5884 if (MovDelay[ax][ay])
5888 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5890 int xx = ax+x1, yy = ay+y1;
5893 if (!IN_LEV_FIELD(xx, yy))
5896 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5898 int x = xx+x2, y = yy+y2;
5900 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5903 if (((Feld[x][y] == element ||
5904 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5906 (IS_FREE(x, y) && Stop[x][y]))
5910 if (xx == ax && yy == ay) /* field in the middle */
5912 if (nachbarn < life[0] || nachbarn > life[1])
5914 Feld[xx][yy] = EL_EMPTY;
5916 DrawLevelField(xx, yy);
5917 Stop[xx][yy] = TRUE;
5921 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5922 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5923 { /* free border field */
5924 if (nachbarn >= life[2] && nachbarn <= life[3])
5926 Feld[xx][yy] = element;
5927 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5929 DrawLevelField(xx, yy);
5930 Stop[xx][yy] = TRUE;
5937 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5938 SND_GAME_OF_LIFE_GROWING);
5941 static void InitRobotWheel(int x, int y)
5943 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5946 static void RunRobotWheel(int x, int y)
5948 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5951 static void StopRobotWheel(int x, int y)
5953 if (ZX == x && ZY == y)
5957 static void InitTimegateWheel(int x, int y)
5959 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5962 static void RunTimegateWheel(int x, int y)
5964 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5967 void CheckExit(int x, int y)
5969 if (local_player->gems_still_needed > 0 ||
5970 local_player->sokobanfields_still_needed > 0 ||
5971 local_player->lights_still_needed > 0)
5973 int element = Feld[x][y];
5974 int graphic = el2img(element);
5976 if (IS_ANIMATED(graphic))
5977 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5982 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5985 Feld[x][y] = EL_EXIT_OPENING;
5987 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5990 void CheckExitSP(int x, int y)
5992 if (local_player->gems_still_needed > 0)
5994 int element = Feld[x][y];
5995 int graphic = el2img(element);
5997 if (IS_ANIMATED(graphic))
5998 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6003 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6006 Feld[x][y] = EL_SP_EXIT_OPENING;
6008 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6011 static void CloseAllOpenTimegates()
6015 for (y = 0; y < lev_fieldy; y++)
6017 for (x = 0; x < lev_fieldx; x++)
6019 int element = Feld[x][y];
6021 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6023 Feld[x][y] = EL_TIMEGATE_CLOSING;
6025 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6027 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6034 void EdelsteinFunkeln(int x, int y)
6036 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6039 if (Feld[x][y] == EL_BD_DIAMOND)
6042 if (MovDelay[x][y] == 0) /* next animation frame */
6043 MovDelay[x][y] = 11 * !SimpleRND(500);
6045 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6049 if (setup.direct_draw && MovDelay[x][y])
6050 SetDrawtoField(DRAW_BUFFERED);
6052 DrawLevelElementAnimation(x, y, Feld[x][y]);
6054 if (MovDelay[x][y] != 0)
6056 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6057 10 - MovDelay[x][y]);
6059 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6061 if (setup.direct_draw)
6065 dest_x = FX + SCREENX(x) * TILEX;
6066 dest_y = FY + SCREENY(y) * TILEY;
6068 BlitBitmap(drawto_field, window,
6069 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6070 SetDrawtoField(DRAW_DIRECT);
6076 void MauerWaechst(int x, int y)
6080 if (!MovDelay[x][y]) /* next animation frame */
6081 MovDelay[x][y] = 3 * delay;
6083 if (MovDelay[x][y]) /* wait some time before next frame */
6087 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6089 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6090 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6092 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6095 if (!MovDelay[x][y])
6097 if (MovDir[x][y] == MV_LEFT)
6099 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6100 DrawLevelField(x - 1, y);
6102 else if (MovDir[x][y] == MV_RIGHT)
6104 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6105 DrawLevelField(x + 1, y);
6107 else if (MovDir[x][y] == MV_UP)
6109 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6110 DrawLevelField(x, y - 1);
6114 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6115 DrawLevelField(x, y + 1);
6118 Feld[x][y] = Store[x][y];
6120 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6121 DrawLevelField(x, y);
6126 void MauerAbleger(int ax, int ay)
6128 int element = Feld[ax][ay];
6129 int graphic = el2img(element);
6130 boolean oben_frei = FALSE, unten_frei = FALSE;
6131 boolean links_frei = FALSE, rechts_frei = FALSE;
6132 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6133 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6134 boolean new_wall = FALSE;
6136 if (IS_ANIMATED(graphic))
6137 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6139 if (!MovDelay[ax][ay]) /* start building new wall */
6140 MovDelay[ax][ay] = 6;
6142 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6145 if (MovDelay[ax][ay])
6149 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6151 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6153 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6155 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6158 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6159 element == EL_EXPANDABLE_WALL_ANY)
6163 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6164 Store[ax][ay-1] = element;
6165 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6166 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6167 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6168 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6173 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6174 Store[ax][ay+1] = element;
6175 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6176 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6177 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6178 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6183 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6184 element == EL_EXPANDABLE_WALL_ANY ||
6185 element == EL_EXPANDABLE_WALL)
6189 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6190 Store[ax-1][ay] = element;
6191 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6192 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6193 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6194 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6200 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6201 Store[ax+1][ay] = element;
6202 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6203 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6204 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6205 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6210 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6211 DrawLevelField(ax, ay);
6213 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6215 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6216 unten_massiv = TRUE;
6217 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6218 links_massiv = TRUE;
6219 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6220 rechts_massiv = TRUE;
6222 if (((oben_massiv && unten_massiv) ||
6223 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6224 element == EL_EXPANDABLE_WALL) &&
6225 ((links_massiv && rechts_massiv) ||
6226 element == EL_EXPANDABLE_WALL_VERTICAL))
6227 Feld[ax][ay] = EL_WALL;
6231 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6233 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6237 void CheckForDragon(int x, int y)
6240 boolean dragon_found = FALSE;
6241 static int xy[4][2] =
6249 for (i = 0; i < 4; i++)
6251 for (j = 0; j < 4; j++)
6253 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6255 if (IN_LEV_FIELD(xx, yy) &&
6256 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6258 if (Feld[xx][yy] == EL_DRAGON)
6259 dragon_found = TRUE;
6268 for (i = 0; i < 4; i++)
6270 for (j = 0; j < 3; j++)
6272 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6274 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6276 Feld[xx][yy] = EL_EMPTY;
6277 DrawLevelField(xx, yy);
6286 static void InitBuggyBase(int x, int y)
6288 int element = Feld[x][y];
6289 int activating_delay = FRAMES_PER_SECOND / 4;
6292 (element == EL_SP_BUGGY_BASE ?
6293 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6294 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6296 element == EL_SP_BUGGY_BASE_ACTIVE ?
6297 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6300 static void WarnBuggyBase(int x, int y)
6303 static int xy[4][2] =
6311 for (i = 0; i < 4; i++)
6313 int xx = x + xy[i][0], yy = y + xy[i][1];
6315 if (IS_PLAYER(xx, yy))
6317 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6324 static void InitTrap(int x, int y)
6326 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6329 static void ActivateTrap(int x, int y)
6331 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6334 static void ChangeActiveTrap(int x, int y)
6336 int graphic = IMG_TRAP_ACTIVE;
6338 /* if new animation frame was drawn, correct crumbled sand border */
6339 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6340 DrawLevelFieldCrumbledSand(x, y);
6343 static void ChangeElementNowExt(int x, int y, int target_element)
6345 int previous_move_direction = MovDir[x][y];
6347 /* check if element under player changes from accessible to unaccessible
6348 (needed for special case of dropping element which then changes) */
6349 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6350 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6357 Feld[x][y] = target_element;
6359 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6361 ResetGfxAnimation(x, y);
6362 ResetRandomAnimationValue(x, y);
6364 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6365 MovDir[x][y] = previous_move_direction;
6367 InitField(x, y, FALSE);
6368 if (CAN_MOVE(Feld[x][y]))
6371 DrawLevelField(x, y);
6373 if (GFX_CRUMBLED(Feld[x][y]))
6374 DrawLevelFieldCrumbledSandNeighbours(x, y);
6376 TestIfBadThingTouchesHero(x, y);
6377 TestIfPlayerTouchesCustomElement(x, y);
6378 TestIfElementTouchesCustomElement(x, y);
6380 if (ELEM_IS_PLAYER(target_element))
6381 RelocatePlayer(x, y, target_element);
6384 static boolean ChangeElementNow(int x, int y, int element, int page)
6386 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6388 /* always use default change event to prevent running into a loop */
6389 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6390 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6392 /* do not change already changed elements with same change event */
6394 if (Changed[x][y] & ChangeEvent[x][y])
6401 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6403 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6405 if (change->explode)
6412 if (change->use_content)
6414 boolean complete_change = TRUE;
6415 boolean can_change[3][3];
6418 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6420 boolean half_destructible;
6421 int ex = x + xx - 1;
6422 int ey = y + yy - 1;
6425 can_change[xx][yy] = TRUE;
6427 if (ex == x && ey == y) /* do not check changing element itself */
6430 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6432 can_change[xx][yy] = FALSE; /* do not change empty borders */
6437 if (!IN_LEV_FIELD(ex, ey))
6439 can_change[xx][yy] = FALSE;
6440 complete_change = FALSE;
6447 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6448 e = MovingOrBlocked2Element(ex, ey);
6450 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6452 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6453 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6454 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6456 can_change[xx][yy] = FALSE;
6457 complete_change = FALSE;
6461 if (!change->only_complete || complete_change)
6463 boolean something_has_changed = FALSE;
6465 if (change->only_complete && change->use_random_change &&
6466 RND(100) < change->random)
6469 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6471 int ex = x + xx - 1;
6472 int ey = y + yy - 1;
6474 if (can_change[xx][yy] && (!change->use_random_change ||
6475 RND(100) < change->random))
6477 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6478 RemoveMovingField(ex, ey);
6480 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6482 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6484 something_has_changed = TRUE;
6486 /* for symmetry reasons, freeze newly created border elements */
6487 if (ex != x || ey != y)
6488 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6492 if (something_has_changed)
6493 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6498 ChangeElementNowExt(x, y, change->target_element);
6500 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6506 static void ChangeElement(int x, int y, int page)
6508 int element = MovingOrBlocked2Element(x, y);
6509 struct ElementInfo *ei = &element_info[element];
6510 struct ElementChangeInfo *change = &ei->change_page[page];
6514 if (!CAN_CHANGE(element))
6517 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6518 x, y, element, element_info[element].token_name);
6519 printf("ChangeElement(): This should never happen!\n");
6525 if (ChangeDelay[x][y] == 0) /* initialize element change */
6527 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6528 RND(change->delay_random * change->delay_frames)) + 1;
6530 ResetGfxAnimation(x, y);
6531 ResetRandomAnimationValue(x, y);
6533 if (change->pre_change_function)
6534 change->pre_change_function(x, y);
6537 ChangeDelay[x][y]--;
6539 if (ChangeDelay[x][y] != 0) /* continue element change */
6541 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6543 if (IS_ANIMATED(graphic))
6544 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6546 if (change->change_function)
6547 change->change_function(x, y);
6549 else /* finish element change */
6551 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6553 page = ChangePage[x][y];
6554 ChangePage[x][y] = -1;
6558 if (IS_MOVING(x, y) && !change->explode)
6560 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6563 ChangeDelay[x][y] = 1; /* try change after next move step */
6564 ChangePage[x][y] = page; /* remember page to use for change */
6569 if (ChangeElementNow(x, y, element, page))
6571 if (change->post_change_function)
6572 change->post_change_function(x, y);
6577 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6578 int trigger_element,
6584 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6587 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6589 int element = EL_CUSTOM_START + i;
6591 boolean change_element = FALSE;
6594 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6597 for (j = 0; j < element_info[element].num_change_pages; j++)
6599 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6601 if (change->can_change &&
6603 change->events & CH_EVENT_BIT(trigger_event) &&
6605 change->sides & trigger_side &&
6607 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6609 change->trigger_element == trigger_element
6614 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6615 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6616 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6619 change_element = TRUE;
6626 if (!change_element)
6629 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6632 if (x == lx && y == ly) /* do not change trigger element itself */
6636 if (Feld[x][y] == element)
6638 ChangeDelay[x][y] = 1;
6639 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6640 ChangeElement(x, y, page);
6648 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6651 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6655 static boolean CheckElementSideChange(int x, int y, int element, int side,
6656 int trigger_event, int page)
6658 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6661 if (Feld[x][y] == EL_BLOCKED)
6663 Blocked2Moving(x, y, &x, &y);
6664 element = Feld[x][y];
6670 boolean change_element = FALSE;
6673 for (i = 0; i < element_info[element].num_change_pages; i++)
6675 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6677 if (change->can_change &&
6678 change->events & CH_EVENT_BIT(trigger_event) &&
6679 change->sides & side)
6681 change_element = TRUE;
6688 if (!change_element)
6694 /* !!! this check misses pages with same event, but different side !!! */
6697 page = element_info[element].event_page_nr[trigger_event];
6699 if (!(element_info[element].change_page[page].sides & side))
6703 ChangeDelay[x][y] = 1;
6704 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6705 ChangeElement(x, y, page);
6710 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6712 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6715 static void PlayPlayerSound(struct PlayerInfo *player)
6717 int jx = player->jx, jy = player->jy;
6718 int element = player->element_nr;
6719 int last_action = player->last_action_waiting;
6720 int action = player->action_waiting;
6722 if (player->is_waiting)
6724 if (action != last_action)
6725 PlayLevelSoundElementAction(jx, jy, element, action);
6727 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6731 if (action != last_action)
6732 StopSound(element_info[element].sound[last_action]);
6734 if (last_action == ACTION_SLEEPING)
6735 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6739 static void PlayAllPlayersSound()
6743 for (i = 0; i < MAX_PLAYERS; i++)
6744 if (stored_player[i].active)
6745 PlayPlayerSound(&stored_player[i]);
6748 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6750 boolean last_waiting = player->is_waiting;
6751 int move_dir = player->MovDir;
6753 player->last_action_waiting = player->action_waiting;
6757 if (!last_waiting) /* not waiting -> waiting */
6759 player->is_waiting = TRUE;
6761 player->frame_counter_bored =
6763 game.player_boring_delay_fixed +
6764 SimpleRND(game.player_boring_delay_random);
6765 player->frame_counter_sleeping =
6767 game.player_sleeping_delay_fixed +
6768 SimpleRND(game.player_sleeping_delay_random);
6770 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6773 if (game.player_sleeping_delay_fixed +
6774 game.player_sleeping_delay_random > 0 &&
6775 player->anim_delay_counter == 0 &&
6776 player->post_delay_counter == 0 &&
6777 FrameCounter >= player->frame_counter_sleeping)
6778 player->is_sleeping = TRUE;
6779 else if (game.player_boring_delay_fixed +
6780 game.player_boring_delay_random > 0 &&
6781 FrameCounter >= player->frame_counter_bored)
6782 player->is_bored = TRUE;
6784 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6785 player->is_bored ? ACTION_BORING :
6788 if (player->is_sleeping)
6790 if (player->num_special_action_sleeping > 0)
6792 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6794 int last_special_action = player->special_action_sleeping;
6795 int num_special_action = player->num_special_action_sleeping;
6796 int special_action =
6797 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6798 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6799 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6800 last_special_action + 1 : ACTION_SLEEPING);
6801 int special_graphic =
6802 el_act_dir2img(player->element_nr, special_action, move_dir);
6804 player->anim_delay_counter =
6805 graphic_info[special_graphic].anim_delay_fixed +
6806 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6807 player->post_delay_counter =
6808 graphic_info[special_graphic].post_delay_fixed +
6809 SimpleRND(graphic_info[special_graphic].post_delay_random);
6811 player->special_action_sleeping = special_action;
6814 if (player->anim_delay_counter > 0)
6816 player->action_waiting = player->special_action_sleeping;
6817 player->anim_delay_counter--;
6819 else if (player->post_delay_counter > 0)
6821 player->post_delay_counter--;
6825 else if (player->is_bored)
6827 if (player->num_special_action_bored > 0)
6829 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6831 int special_action =
6832 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6833 int special_graphic =
6834 el_act_dir2img(player->element_nr, special_action, move_dir);
6836 player->anim_delay_counter =
6837 graphic_info[special_graphic].anim_delay_fixed +
6838 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6839 player->post_delay_counter =
6840 graphic_info[special_graphic].post_delay_fixed +
6841 SimpleRND(graphic_info[special_graphic].post_delay_random);
6843 player->special_action_bored = special_action;
6846 if (player->anim_delay_counter > 0)
6848 player->action_waiting = player->special_action_bored;
6849 player->anim_delay_counter--;
6851 else if (player->post_delay_counter > 0)
6853 player->post_delay_counter--;
6858 else if (last_waiting) /* waiting -> not waiting */
6860 player->is_waiting = FALSE;
6861 player->is_bored = FALSE;
6862 player->is_sleeping = FALSE;
6864 player->frame_counter_bored = -1;
6865 player->frame_counter_sleeping = -1;
6867 player->anim_delay_counter = 0;
6868 player->post_delay_counter = 0;
6870 player->action_waiting = ACTION_DEFAULT;
6872 player->special_action_bored = ACTION_DEFAULT;
6873 player->special_action_sleeping = ACTION_DEFAULT;
6878 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6881 static byte stored_player_action[MAX_PLAYERS];
6882 static int num_stored_actions = 0;
6884 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6885 int left = player_action & JOY_LEFT;
6886 int right = player_action & JOY_RIGHT;
6887 int up = player_action & JOY_UP;
6888 int down = player_action & JOY_DOWN;
6889 int button1 = player_action & JOY_BUTTON_1;
6890 int button2 = player_action & JOY_BUTTON_2;
6891 int dx = (left ? -1 : right ? 1 : 0);
6892 int dy = (up ? -1 : down ? 1 : 0);
6895 stored_player_action[player->index_nr] = 0;
6896 num_stored_actions++;
6900 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6903 if (!player->active || tape.pausing)
6907 printf("::: [%d %d %d %d] [%d %d]\n",
6908 left, right, up, down, button1, button2);
6914 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6918 snapped = SnapField(player, dx, dy);
6922 dropped = DropElement(player);
6924 moved = MovePlayer(player, dx, dy);
6927 if (tape.single_step && tape.recording && !tape.pausing)
6929 if (button1 || (dropped && !moved))
6931 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6932 SnapField(player, 0, 0); /* stop snapping */
6936 SetPlayerWaiting(player, FALSE);
6939 return player_action;
6941 stored_player_action[player->index_nr] = player_action;
6947 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6950 /* no actions for this player (no input at player's configured device) */
6952 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6953 SnapField(player, 0, 0);
6954 CheckGravityMovement(player);
6956 if (player->MovPos == 0)
6957 SetPlayerWaiting(player, TRUE);
6959 if (player->MovPos == 0) /* needed for tape.playing */
6960 player->is_moving = FALSE;
6962 player->is_dropping = FALSE;
6968 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6970 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6972 TapeRecordAction(stored_player_action);
6973 num_stored_actions = 0;
6980 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6982 static byte stored_player_action[MAX_PLAYERS];
6983 static int num_stored_actions = 0;
6984 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6985 int left = player_action & JOY_LEFT;
6986 int right = player_action & JOY_RIGHT;
6987 int up = player_action & JOY_UP;
6988 int down = player_action & JOY_DOWN;
6989 int button1 = player_action & JOY_BUTTON_1;
6990 int button2 = player_action & JOY_BUTTON_2;
6991 int dx = (left ? -1 : right ? 1 : 0);
6992 int dy = (up ? -1 : down ? 1 : 0);
6994 stored_player_action[player->index_nr] = 0;
6995 num_stored_actions++;
6997 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6999 if (!player->active || tape.pausing)
7004 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7007 snapped = SnapField(player, dx, dy);
7011 dropped = DropElement(player);
7013 moved = MovePlayer(player, dx, dy);
7016 if (tape.single_step && tape.recording && !tape.pausing)
7018 if (button1 || (dropped && !moved))
7020 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7021 SnapField(player, 0, 0); /* stop snapping */
7025 stored_player_action[player->index_nr] = player_action;
7029 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7031 /* no actions for this player (no input at player's configured device) */
7033 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
7034 SnapField(player, 0, 0);
7035 CheckGravityMovement(player);
7037 if (player->MovPos == 0)
7038 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7040 if (player->MovPos == 0) /* needed for tape.playing */
7041 player->is_moving = FALSE;
7044 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7046 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7048 TapeRecordAction(stored_player_action);
7049 num_stored_actions = 0;
7056 static unsigned long action_delay = 0;
7057 unsigned long action_delay_value;
7058 int magic_wall_x = 0, magic_wall_y = 0;
7059 int i, x, y, element, graphic;
7060 byte *recorded_player_action;
7061 byte summarized_player_action = 0;
7063 byte tape_action[MAX_PLAYERS];
7066 if (game_status != GAME_MODE_PLAYING)
7069 action_delay_value =
7070 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7072 if (tape.playing && tape.index_search && !tape.pausing)
7073 action_delay_value = 0;
7075 /* ---------- main game synchronization point ---------- */
7077 WaitUntilDelayReached(&action_delay, action_delay_value);
7079 if (network_playing && !network_player_action_received)
7083 printf("DEBUG: try to get network player actions in time\n");
7087 #if defined(PLATFORM_UNIX)
7088 /* last chance to get network player actions without main loop delay */
7092 if (game_status != GAME_MODE_PLAYING)
7095 if (!network_player_action_received)
7099 printf("DEBUG: failed to get network player actions in time\n");
7110 printf("::: getting new tape action [%d]\n", FrameCounter);
7113 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7115 for (i = 0; i < MAX_PLAYERS; i++)
7117 summarized_player_action |= stored_player[i].action;
7119 if (!network_playing)
7120 stored_player[i].effective_action = stored_player[i].action;
7123 #if defined(PLATFORM_UNIX)
7124 if (network_playing)
7125 SendToServer_MovePlayer(summarized_player_action);
7128 if (!options.network && !setup.team_mode)
7129 local_player->effective_action = summarized_player_action;
7131 for (i = 0; i < MAX_PLAYERS; i++)
7133 int actual_player_action = stored_player[i].effective_action;
7135 if (stored_player[i].programmed_action)
7136 actual_player_action = stored_player[i].programmed_action;
7138 if (recorded_player_action)
7139 actual_player_action = recorded_player_action[i];
7141 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7143 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7144 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7146 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7151 TapeRecordAction(tape_action);
7154 network_player_action_received = FALSE;
7156 ScrollScreen(NULL, SCROLL_GO_ON);
7162 for (i = 0; i < MAX_PLAYERS; i++)
7163 stored_player[i].Frame++;
7167 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7169 for (i = 0; i < MAX_PLAYERS; i++)
7171 struct PlayerInfo *player = &stored_player[i];
7175 if (player->active && player->is_pushing && player->is_moving &&
7178 ContinueMoving(x, y);
7180 /* continue moving after pushing (this is actually a bug) */
7181 if (!IS_MOVING(x, y))
7190 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7192 Changed[x][y] = CE_BITMASK_DEFAULT;
7193 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7196 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7198 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7199 printf("GameActions(): This should never happen!\n");
7201 ChangePage[x][y] = -1;
7206 if (WasJustMoving[x][y] > 0)
7207 WasJustMoving[x][y]--;
7208 if (WasJustFalling[x][y] > 0)
7209 WasJustFalling[x][y]--;
7214 /* reset finished pushing action (not done in ContinueMoving() to allow
7215 continous pushing animation for elements with zero push delay) */
7216 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7218 ResetGfxAnimation(x, y);
7219 DrawLevelField(x, y);
7224 if (IS_BLOCKED(x, y))
7228 Blocked2Moving(x, y, &oldx, &oldy);
7229 if (!IS_MOVING(oldx, oldy))
7231 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7232 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7233 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7234 printf("GameActions(): This should never happen!\n");
7240 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7242 element = Feld[x][y];
7244 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7246 graphic = el2img(element);
7252 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7254 element = graphic = 0;
7258 if (graphic_info[graphic].anim_global_sync)
7259 GfxFrame[x][y] = FrameCounter;
7261 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7262 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7263 ResetRandomAnimationValue(x, y);
7265 SetRandomAnimationValue(x, y);
7268 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7271 if (IS_INACTIVE(element))
7273 if (IS_ANIMATED(graphic))
7274 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7280 /* this may take place after moving, so 'element' may have changed */
7282 if (IS_CHANGING(x, y))
7284 if (IS_CHANGING(x, y) &&
7285 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7289 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7290 element_info[element].event_page_nr[CE_DELAY]);
7292 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7295 element = Feld[x][y];
7296 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7300 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7305 element = Feld[x][y];
7306 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7308 if (element == EL_MOLE)
7309 printf("::: %d, %d, %d [%d]\n",
7310 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7314 if (element == EL_YAMYAM)
7315 printf("::: %d, %d, %d\n",
7316 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7320 if (IS_ANIMATED(graphic) &&
7324 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7327 if (element == EL_BUG)
7328 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7332 if (element == EL_MOLE)
7333 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7337 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7338 EdelsteinFunkeln(x, y);
7340 else if ((element == EL_ACID ||
7341 element == EL_EXIT_OPEN ||
7342 element == EL_SP_EXIT_OPEN ||
7343 element == EL_SP_TERMINAL ||
7344 element == EL_SP_TERMINAL_ACTIVE ||
7345 element == EL_EXTRA_TIME ||
7346 element == EL_SHIELD_NORMAL ||
7347 element == EL_SHIELD_DEADLY) &&
7348 IS_ANIMATED(graphic))
7349 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7350 else if (IS_MOVING(x, y))
7351 ContinueMoving(x, y);
7352 else if (IS_ACTIVE_BOMB(element))
7353 CheckDynamite(x, y);
7355 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7356 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7358 else if (element == EL_AMOEBA_GROWING)
7359 AmoebeWaechst(x, y);
7360 else if (element == EL_AMOEBA_SHRINKING)
7361 AmoebaDisappearing(x, y);
7363 #if !USE_NEW_AMOEBA_CODE
7364 else if (IS_AMOEBALIVE(element))
7365 AmoebeAbleger(x, y);
7368 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7370 else if (element == EL_EXIT_CLOSED)
7372 else if (element == EL_SP_EXIT_CLOSED)
7374 else if (element == EL_EXPANDABLE_WALL_GROWING)
7376 else if (element == EL_EXPANDABLE_WALL ||
7377 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7378 element == EL_EXPANDABLE_WALL_VERTICAL ||
7379 element == EL_EXPANDABLE_WALL_ANY)
7381 else if (element == EL_FLAMES)
7382 CheckForDragon(x, y);
7384 else if (IS_AUTO_CHANGING(element))
7385 ChangeElement(x, y);
7387 else if (element == EL_EXPLOSION)
7388 ; /* drawing of correct explosion animation is handled separately */
7389 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7390 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7393 /* this may take place after moving, so 'element' may have changed */
7394 if (IS_AUTO_CHANGING(Feld[x][y]))
7395 ChangeElement(x, y);
7398 if (IS_BELT_ACTIVE(element))
7399 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7401 if (game.magic_wall_active)
7403 int jx = local_player->jx, jy = local_player->jy;
7405 /* play the element sound at the position nearest to the player */
7406 if ((element == EL_MAGIC_WALL_FULL ||
7407 element == EL_MAGIC_WALL_ACTIVE ||
7408 element == EL_MAGIC_WALL_EMPTYING ||
7409 element == EL_BD_MAGIC_WALL_FULL ||
7410 element == EL_BD_MAGIC_WALL_ACTIVE ||
7411 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7412 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7420 #if USE_NEW_AMOEBA_CODE
7421 /* new experimental amoeba growth stuff */
7423 if (!(FrameCounter % 8))
7426 static unsigned long random = 1684108901;
7428 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7431 x = (random >> 10) % lev_fieldx;
7432 y = (random >> 20) % lev_fieldy;
7434 x = RND(lev_fieldx);
7435 y = RND(lev_fieldy);
7437 element = Feld[x][y];
7439 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7440 if (!IS_PLAYER(x,y) &&
7441 (element == EL_EMPTY ||
7442 element == EL_SAND ||
7443 element == EL_QUICKSAND_EMPTY ||
7444 element == EL_ACID_SPLASH_LEFT ||
7445 element == EL_ACID_SPLASH_RIGHT))
7447 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7448 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7449 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7450 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7451 Feld[x][y] = EL_AMOEBA_DROP;
7454 random = random * 129 + 1;
7460 if (game.explosions_delayed)
7463 game.explosions_delayed = FALSE;
7465 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7467 element = Feld[x][y];
7469 if (ExplodeField[x][y])
7470 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7471 else if (element == EL_EXPLOSION)
7472 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7474 ExplodeField[x][y] = EX_NO_EXPLOSION;
7477 game.explosions_delayed = TRUE;
7480 if (game.magic_wall_active)
7482 if (!(game.magic_wall_time_left % 4))
7484 int element = Feld[magic_wall_x][magic_wall_y];
7486 if (element == EL_BD_MAGIC_WALL_FULL ||
7487 element == EL_BD_MAGIC_WALL_ACTIVE ||
7488 element == EL_BD_MAGIC_WALL_EMPTYING)
7489 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7491 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7494 if (game.magic_wall_time_left > 0)
7496 game.magic_wall_time_left--;
7497 if (!game.magic_wall_time_left)
7499 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7501 element = Feld[x][y];
7503 if (element == EL_MAGIC_WALL_ACTIVE ||
7504 element == EL_MAGIC_WALL_FULL)
7506 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7507 DrawLevelField(x, y);
7509 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7510 element == EL_BD_MAGIC_WALL_FULL)
7512 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7513 DrawLevelField(x, y);
7517 game.magic_wall_active = FALSE;
7522 if (game.light_time_left > 0)
7524 game.light_time_left--;
7526 if (game.light_time_left == 0)
7527 RedrawAllLightSwitchesAndInvisibleElements();
7530 if (game.timegate_time_left > 0)
7532 game.timegate_time_left--;
7534 if (game.timegate_time_left == 0)
7535 CloseAllOpenTimegates();
7538 for (i = 0; i < MAX_PLAYERS; i++)
7540 struct PlayerInfo *player = &stored_player[i];
7542 if (SHIELD_ON(player))
7544 if (player->shield_deadly_time_left)
7545 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7546 else if (player->shield_normal_time_left)
7547 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7551 if (TimeFrames >= FRAMES_PER_SECOND)
7556 for (i = 0; i < MAX_PLAYERS; i++)
7558 struct PlayerInfo *player = &stored_player[i];
7560 if (SHIELD_ON(player))
7562 player->shield_normal_time_left--;
7564 if (player->shield_deadly_time_left > 0)
7565 player->shield_deadly_time_left--;
7569 if (tape.recording || tape.playing)
7570 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7576 if (TimeLeft <= 10 && setup.time_limit)
7577 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7579 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7581 if (!TimeLeft && setup.time_limit)
7582 for (i = 0; i < MAX_PLAYERS; i++)
7583 KillHero(&stored_player[i]);
7585 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7586 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7590 PlayAllPlayersSound();
7592 if (options.debug) /* calculate frames per second */
7594 static unsigned long fps_counter = 0;
7595 static int fps_frames = 0;
7596 unsigned long fps_delay_ms = Counter() - fps_counter;
7600 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7602 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7605 fps_counter = Counter();
7608 redraw_mask |= REDRAW_FPS;
7612 if (stored_player[0].jx != stored_player[0].last_jx ||
7613 stored_player[0].jy != stored_player[0].last_jy)
7614 printf("::: %d, %d, %d, %d, %d\n",
7615 stored_player[0].MovDir,
7616 stored_player[0].MovPos,
7617 stored_player[0].GfxPos,
7618 stored_player[0].Frame,
7619 stored_player[0].StepFrame);
7626 for (i = 0; i < MAX_PLAYERS; i++)
7629 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7631 stored_player[i].Frame += move_frames;
7633 if (stored_player[i].MovPos != 0)
7634 stored_player[i].StepFrame += move_frames;
7636 if (stored_player[i].drop_delay > 0)
7637 stored_player[i].drop_delay--;
7642 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7644 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7646 local_player->show_envelope = 0;
7651 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7653 int min_x = x, min_y = y, max_x = x, max_y = y;
7656 for (i = 0; i < MAX_PLAYERS; i++)
7658 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7660 if (!stored_player[i].active || &stored_player[i] == player)
7663 min_x = MIN(min_x, jx);
7664 min_y = MIN(min_y, jy);
7665 max_x = MAX(max_x, jx);
7666 max_y = MAX(max_y, jy);
7669 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7672 static boolean AllPlayersInVisibleScreen()
7676 for (i = 0; i < MAX_PLAYERS; i++)
7678 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7680 if (!stored_player[i].active)
7683 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7690 void ScrollLevel(int dx, int dy)
7692 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7695 BlitBitmap(drawto_field, drawto_field,
7696 FX + TILEX * (dx == -1) - softscroll_offset,
7697 FY + TILEY * (dy == -1) - softscroll_offset,
7698 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7699 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7700 FX + TILEX * (dx == 1) - softscroll_offset,
7701 FY + TILEY * (dy == 1) - softscroll_offset);
7705 x = (dx == 1 ? BX1 : BX2);
7706 for (y = BY1; y <= BY2; y++)
7707 DrawScreenField(x, y);
7712 y = (dy == 1 ? BY1 : BY2);
7713 for (x = BX1; x <= BX2; x++)
7714 DrawScreenField(x, y);
7717 redraw_mask |= REDRAW_FIELD;
7720 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7722 int nextx = x + dx, nexty = y + dy;
7723 int element = Feld[x][y];
7726 element != EL_SP_PORT_LEFT &&
7727 element != EL_SP_GRAVITY_PORT_LEFT &&
7728 element != EL_SP_PORT_HORIZONTAL &&
7729 element != EL_SP_PORT_ANY) ||
7731 element != EL_SP_PORT_RIGHT &&
7732 element != EL_SP_GRAVITY_PORT_RIGHT &&
7733 element != EL_SP_PORT_HORIZONTAL &&
7734 element != EL_SP_PORT_ANY) ||
7736 element != EL_SP_PORT_UP &&
7737 element != EL_SP_GRAVITY_PORT_UP &&
7738 element != EL_SP_PORT_VERTICAL &&
7739 element != EL_SP_PORT_ANY) ||
7741 element != EL_SP_PORT_DOWN &&
7742 element != EL_SP_GRAVITY_PORT_DOWN &&
7743 element != EL_SP_PORT_VERTICAL &&
7744 element != EL_SP_PORT_ANY) ||
7745 !IN_LEV_FIELD(nextx, nexty) ||
7746 !IS_FREE(nextx, nexty))
7752 static void CheckGravityMovement(struct PlayerInfo *player)
7754 if (game.gravity && !player->programmed_action)
7756 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7757 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7759 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7760 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7761 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7762 int jx = player->jx, jy = player->jy;
7763 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7764 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7765 int new_jx = jx + dx, new_jy = jy + dy;
7766 boolean field_under_player_is_free =
7767 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7768 boolean player_is_moving_to_valid_field =
7769 (IN_LEV_FIELD(new_jx, new_jy) &&
7770 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7771 Feld[new_jx][new_jy] == EL_SAND ||
7772 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7773 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7774 /* !!! extend EL_SAND to anything diggable !!! */
7776 if (field_under_player_is_free &&
7777 !player_is_moving_to_valid_field &&
7778 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7779 player->programmed_action = MV_DOWN;
7785 -----------------------------------------------------------------------------
7786 dx, dy: direction (non-diagonal) to try to move the player to
7787 real_dx, real_dy: direction as read from input device (can be diagonal)
7790 boolean MovePlayerOneStep(struct PlayerInfo *player,
7791 int dx, int dy, int real_dx, int real_dy)
7794 static int change_sides[4][2] =
7796 /* enter side leave side */
7797 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7798 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7799 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7800 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7802 int move_direction = (dx == -1 ? MV_LEFT :
7803 dx == +1 ? MV_RIGHT :
7805 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7806 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7807 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7809 int jx = player->jx, jy = player->jy;
7810 int new_jx = jx + dx, new_jy = jy + dy;
7814 if (!player->active || (!dx && !dy))
7815 return MF_NO_ACTION;
7817 player->MovDir = (dx < 0 ? MV_LEFT :
7820 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7822 if (!IN_LEV_FIELD(new_jx, new_jy))
7823 return MF_NO_ACTION;
7825 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7826 return MF_NO_ACTION;
7829 element = MovingOrBlocked2Element(new_jx, new_jy);
7831 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7834 if (DONT_RUN_INTO(element))
7836 if (element == EL_ACID && dx == 0 && dy == 1)
7839 Feld[jx][jy] = EL_PLAYER_1;
7840 InitMovingField(jx, jy, MV_DOWN);
7841 Store[jx][jy] = EL_ACID;
7842 ContinueMoving(jx, jy);
7846 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7851 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7852 if (can_move != MF_MOVING)
7855 /* check if DigField() has caused relocation of the player */
7856 if (player->jx != jx || player->jy != jy)
7857 return MF_NO_ACTION;
7859 StorePlayer[jx][jy] = 0;
7860 player->last_jx = jx;
7861 player->last_jy = jy;
7862 player->jx = new_jx;
7863 player->jy = new_jy;
7864 StorePlayer[new_jx][new_jy] = player->element_nr;
7867 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7869 player->step_counter++;
7871 player->drop_delay = 0;
7873 PlayerVisit[jx][jy] = FrameCounter;
7875 ScrollPlayer(player, SCROLL_INIT);
7878 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7880 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7881 CE_OTHER_GETS_LEFT);
7882 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7883 CE_LEFT_BY_PLAYER, -1);
7886 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7888 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7889 enter_side, CE_OTHER_GETS_ENTERED);
7890 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7891 CE_ENTERED_BY_PLAYER, -1);
7898 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7900 int jx = player->jx, jy = player->jy;
7901 int old_jx = jx, old_jy = jy;
7902 int moved = MF_NO_ACTION;
7905 if (!player->active)
7910 if (player->MovPos == 0)
7912 player->is_moving = FALSE;
7913 player->is_digging = FALSE;
7914 player->is_collecting = FALSE;
7915 player->is_snapping = FALSE;
7916 player->is_pushing = FALSE;
7922 if (!player->active || (!dx && !dy))
7927 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7931 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7932 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7936 /* remove the last programmed player action */
7937 player->programmed_action = 0;
7941 /* should only happen if pre-1.2 tape recordings are played */
7942 /* this is only for backward compatibility */
7944 int original_move_delay_value = player->move_delay_value;
7947 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7951 /* scroll remaining steps with finest movement resolution */
7952 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7954 while (player->MovPos)
7956 ScrollPlayer(player, SCROLL_GO_ON);
7957 ScrollScreen(NULL, SCROLL_GO_ON);
7963 player->move_delay_value = original_move_delay_value;
7966 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7968 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7969 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7973 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7974 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7980 if (moved & MF_MOVING && !ScreenMovPos &&
7981 (player == local_player || !options.network))
7983 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7984 int offset = (setup.scroll_delay ? 3 : 0);
7986 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7988 /* actual player has left the screen -- scroll in that direction */
7989 if (jx != old_jx) /* player has moved horizontally */
7990 scroll_x += (jx - old_jx);
7991 else /* player has moved vertically */
7992 scroll_y += (jy - old_jy);
7996 if (jx != old_jx) /* player has moved horizontally */
7998 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7999 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8000 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8002 /* don't scroll over playfield boundaries */
8003 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8004 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8006 /* don't scroll more than one field at a time */
8007 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8009 /* don't scroll against the player's moving direction */
8010 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8011 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8012 scroll_x = old_scroll_x;
8014 else /* player has moved vertically */
8016 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8017 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8018 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8020 /* don't scroll over playfield boundaries */
8021 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8022 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8024 /* don't scroll more than one field at a time */
8025 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8027 /* don't scroll against the player's moving direction */
8028 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8029 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8030 scroll_y = old_scroll_y;
8034 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8036 if (!options.network && !AllPlayersInVisibleScreen())
8038 scroll_x = old_scroll_x;
8039 scroll_y = old_scroll_y;
8043 ScrollScreen(player, SCROLL_INIT);
8044 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8051 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8053 if (!(moved & MF_MOVING) && !player->is_pushing)
8058 player->StepFrame = 0;
8060 if (moved & MF_MOVING)
8062 if (old_jx != jx && old_jy == jy)
8063 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8064 else if (old_jx == jx && old_jy != jy)
8065 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8067 DrawLevelField(jx, jy); /* for "crumbled sand" */
8069 player->last_move_dir = player->MovDir;
8070 player->is_moving = TRUE;
8072 player->is_snapping = FALSE;
8076 player->is_switching = FALSE;
8079 player->is_dropping = FALSE;
8084 static int change_sides[4][2] =
8086 /* enter side leave side */
8087 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8088 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8089 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8090 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8092 int move_direction = player->MovDir;
8093 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8094 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8097 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8099 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8100 leave_side, CE_OTHER_GETS_LEFT);
8101 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8102 leave_side, CE_LEFT_BY_PLAYER, -1);
8105 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8107 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8108 enter_side, CE_OTHER_GETS_ENTERED);
8109 CheckElementSideChange(jx, jy, Feld[jx][jy],
8110 enter_side, CE_ENTERED_BY_PLAYER, -1);
8121 CheckGravityMovement(player);
8124 player->last_move_dir = MV_NO_MOVING;
8126 player->is_moving = FALSE;
8129 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8131 TestIfHeroTouchesBadThing(jx, jy);
8132 TestIfPlayerTouchesCustomElement(jx, jy);
8135 if (!player->active)
8141 void ScrollPlayer(struct PlayerInfo *player, int mode)
8143 int jx = player->jx, jy = player->jy;
8144 int last_jx = player->last_jx, last_jy = player->last_jy;
8145 int move_stepsize = TILEX / player->move_delay_value;
8147 if (!player->active || !player->MovPos)
8150 if (mode == SCROLL_INIT)
8152 player->actual_frame_counter = FrameCounter;
8153 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8155 if (Feld[last_jx][last_jy] == EL_EMPTY)
8156 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8164 else if (!FrameReached(&player->actual_frame_counter, 1))
8167 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8168 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8170 if (!player->block_last_field &&
8171 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8172 Feld[last_jx][last_jy] = EL_EMPTY;
8174 /* before DrawPlayer() to draw correct player graphic for this case */
8175 if (player->MovPos == 0)
8176 CheckGravityMovement(player);
8179 DrawPlayer(player); /* needed here only to cleanup last field */
8182 if (player->MovPos == 0) /* player reached destination field */
8185 if (player->move_delay_reset_counter > 0)
8187 player->move_delay_reset_counter--;
8189 if (player->move_delay_reset_counter == 0)
8191 /* continue with normal speed after quickly moving through gate */
8192 HALVE_PLAYER_SPEED(player);
8194 /* be able to make the next move without delay */
8195 player->move_delay = 0;
8199 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8201 /* continue with normal speed after quickly moving through gate */
8202 HALVE_PLAYER_SPEED(player);
8204 /* be able to make the next move without delay */
8205 player->move_delay = 0;
8209 if (player->block_last_field &&
8210 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8211 Feld[last_jx][last_jy] = EL_EMPTY;
8213 player->last_jx = jx;
8214 player->last_jy = jy;
8216 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8217 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8218 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8220 DrawPlayer(player); /* needed here only to cleanup last field */
8223 if (local_player->friends_still_needed == 0 ||
8224 IS_SP_ELEMENT(Feld[jx][jy]))
8225 player->LevelSolved = player->GameOver = TRUE;
8228 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8230 TestIfHeroTouchesBadThing(jx, jy);
8231 TestIfPlayerTouchesCustomElement(jx, jy);
8233 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8236 if (!player->active)
8240 if (tape.single_step && tape.recording && !tape.pausing &&
8241 !player->programmed_action)
8242 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8246 void ScrollScreen(struct PlayerInfo *player, int mode)
8248 static unsigned long screen_frame_counter = 0;
8250 if (mode == SCROLL_INIT)
8252 /* set scrolling step size according to actual player's moving speed */
8253 ScrollStepSize = TILEX / player->move_delay_value;
8255 screen_frame_counter = FrameCounter;
8256 ScreenMovDir = player->MovDir;
8257 ScreenMovPos = player->MovPos;
8258 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8261 else if (!FrameReached(&screen_frame_counter, 1))
8266 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8267 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8268 redraw_mask |= REDRAW_FIELD;
8271 ScreenMovDir = MV_NO_MOVING;
8274 void TestIfPlayerTouchesCustomElement(int x, int y)
8276 static int xy[4][2] =
8283 static int change_sides[4][2] =
8285 /* center side border side */
8286 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8287 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8288 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8289 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8291 static int touch_dir[4] =
8298 int center_element = Feld[x][y]; /* should always be non-moving! */
8301 for (i = 0; i < 4; i++)
8303 int xx = x + xy[i][0];
8304 int yy = y + xy[i][1];
8305 int center_side = change_sides[i][0];
8306 int border_side = change_sides[i][1];
8309 if (!IN_LEV_FIELD(xx, yy))
8312 if (IS_PLAYER(x, y))
8314 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8315 border_element = Feld[xx][yy]; /* may be moving! */
8316 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8317 border_element = Feld[xx][yy];
8318 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8319 border_element = MovingOrBlocked2Element(xx, yy);
8321 continue; /* center and border element do not touch */
8323 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8324 CE_OTHER_GETS_TOUCHED);
8325 CheckElementSideChange(xx, yy, border_element, border_side,
8326 CE_TOUCHED_BY_PLAYER, -1);
8328 else if (IS_PLAYER(xx, yy))
8330 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8332 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8334 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8335 continue; /* center and border element do not touch */
8338 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8339 CE_OTHER_GETS_TOUCHED);
8340 CheckElementSideChange(x, y, center_element, center_side,
8341 CE_TOUCHED_BY_PLAYER, -1);
8348 void TestIfElementTouchesCustomElement(int x, int y)
8350 static int xy[4][2] =
8357 static int change_sides[4][2] =
8359 /* center side border side */
8360 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8361 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8362 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8363 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8365 static int touch_dir[4] =
8372 boolean change_center_element = FALSE;
8373 int center_element_change_page = 0;
8374 int center_element = Feld[x][y]; /* should always be non-moving! */
8377 for (i = 0; i < 4; i++)
8379 int xx = x + xy[i][0];
8380 int yy = y + xy[i][1];
8381 int center_side = change_sides[i][0];
8382 int border_side = change_sides[i][1];
8385 if (!IN_LEV_FIELD(xx, yy))
8388 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8389 border_element = Feld[xx][yy]; /* may be moving! */
8390 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8391 border_element = Feld[xx][yy];
8392 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8393 border_element = MovingOrBlocked2Element(xx, yy);
8395 continue; /* center and border element do not touch */
8397 /* check for change of center element (but change it only once) */
8398 if (IS_CUSTOM_ELEMENT(center_element) &&
8399 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8400 !change_center_element)
8402 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8404 struct ElementChangeInfo *change =
8405 &element_info[center_element].change_page[j];
8407 if (change->can_change &&
8408 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8409 change->sides & border_side &&
8411 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8413 change->trigger_element == border_element
8417 change_center_element = TRUE;
8418 center_element_change_page = j;
8425 /* check for change of border element */
8426 if (IS_CUSTOM_ELEMENT(border_element) &&
8427 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8429 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8431 struct ElementChangeInfo *change =
8432 &element_info[border_element].change_page[j];
8434 if (change->can_change &&
8435 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8436 change->sides & center_side &&
8438 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8440 change->trigger_element == center_element
8444 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8445 CE_OTHER_IS_TOUCHING, j);
8452 if (change_center_element)
8453 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8454 CE_OTHER_IS_TOUCHING, center_element_change_page);
8457 void TestIfElementHitsCustomElement(int x, int y, int direction)
8459 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8460 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8461 int hitx = x + dx, hity = y + dy;
8462 int hitting_element = Feld[x][y];
8464 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8465 !IS_FREE(hitx, hity) &&
8466 (!IS_MOVING(hitx, hity) ||
8467 MovDir[hitx][hity] != direction ||
8468 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8471 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8475 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8479 CheckElementSideChange(x, y, hitting_element,
8480 direction, CE_HITTING_SOMETHING, -1);
8482 if (IN_LEV_FIELD(hitx, hity))
8484 int opposite_direction = MV_DIR_OPPOSITE(direction);
8485 int hitting_side = direction;
8486 int touched_side = opposite_direction;
8487 int touched_element = MovingOrBlocked2Element(hitx, hity);
8489 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8490 MovDir[hitx][hity] != direction ||
8491 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8500 CheckElementSideChange(hitx, hity, touched_element,
8501 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8503 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8504 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8506 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8508 struct ElementChangeInfo *change =
8509 &element_info[hitting_element].change_page[i];
8511 if (change->can_change &&
8512 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8513 change->sides & touched_side &&
8516 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8518 change->trigger_element == touched_element
8522 CheckElementSideChange(x, y, hitting_element,
8523 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8529 if (IS_CUSTOM_ELEMENT(touched_element) &&
8530 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8532 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8534 struct ElementChangeInfo *change =
8535 &element_info[touched_element].change_page[i];
8537 if (change->can_change &&
8538 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8539 change->sides & hitting_side &&
8541 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8543 change->trigger_element == hitting_element
8547 CheckElementSideChange(hitx, hity, touched_element,
8548 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8557 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8559 int i, kill_x = -1, kill_y = -1;
8560 static int test_xy[4][2] =
8567 static int test_dir[4] =
8575 for (i = 0; i < 4; i++)
8577 int test_x, test_y, test_move_dir, test_element;
8579 test_x = good_x + test_xy[i][0];
8580 test_y = good_y + test_xy[i][1];
8581 if (!IN_LEV_FIELD(test_x, test_y))
8585 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8588 test_element = Feld[test_x][test_y];
8590 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8593 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8594 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8596 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8597 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8605 if (kill_x != -1 || kill_y != -1)
8607 if (IS_PLAYER(good_x, good_y))
8609 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8611 if (player->shield_deadly_time_left > 0)
8612 Bang(kill_x, kill_y);
8613 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8617 Bang(good_x, good_y);
8621 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8623 int i, kill_x = -1, kill_y = -1;
8624 int bad_element = Feld[bad_x][bad_y];
8625 static int test_xy[4][2] =
8632 static int touch_dir[4] =
8639 static int test_dir[4] =
8647 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8650 for (i = 0; i < 4; i++)
8652 int test_x, test_y, test_move_dir, test_element;
8654 test_x = bad_x + test_xy[i][0];
8655 test_y = bad_y + test_xy[i][1];
8656 if (!IN_LEV_FIELD(test_x, test_y))
8660 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8662 test_element = Feld[test_x][test_y];
8664 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8665 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8667 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8668 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8670 /* good thing is player or penguin that does not move away */
8671 if (IS_PLAYER(test_x, test_y))
8673 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8675 if (bad_element == EL_ROBOT && player->is_moving)
8676 continue; /* robot does not kill player if he is moving */
8678 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8680 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8681 continue; /* center and border element do not touch */
8688 else if (test_element == EL_PENGUIN)
8697 if (kill_x != -1 || kill_y != -1)
8699 if (IS_PLAYER(kill_x, kill_y))
8701 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8703 if (player->shield_deadly_time_left > 0)
8705 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8709 Bang(kill_x, kill_y);
8713 void TestIfHeroTouchesBadThing(int x, int y)
8715 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8718 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8720 TestIfGoodThingHitsBadThing(x, y, move_dir);
8723 void TestIfBadThingTouchesHero(int x, int y)
8725 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8728 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8730 TestIfBadThingHitsGoodThing(x, y, move_dir);
8733 void TestIfFriendTouchesBadThing(int x, int y)
8735 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8738 void TestIfBadThingTouchesFriend(int x, int y)
8740 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8743 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8745 int i, kill_x = bad_x, kill_y = bad_y;
8746 static int xy[4][2] =
8754 for (i = 0; i < 4; i++)
8758 x = bad_x + xy[i][0];
8759 y = bad_y + xy[i][1];
8760 if (!IN_LEV_FIELD(x, y))
8763 element = Feld[x][y];
8764 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8765 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8773 if (kill_x != bad_x || kill_y != bad_y)
8777 void KillHero(struct PlayerInfo *player)
8779 int jx = player->jx, jy = player->jy;
8781 if (!player->active)
8784 /* remove accessible field at the player's position */
8785 Feld[jx][jy] = EL_EMPTY;
8787 /* deactivate shield (else Bang()/Explode() would not work right) */
8788 player->shield_normal_time_left = 0;
8789 player->shield_deadly_time_left = 0;
8795 static void KillHeroUnlessEnemyProtected(int x, int y)
8797 if (!PLAYER_ENEMY_PROTECTED(x, y))
8798 KillHero(PLAYERINFO(x, y));
8801 static void KillHeroUnlessExplosionProtected(int x, int y)
8803 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8804 KillHero(PLAYERINFO(x, y));
8807 void BuryHero(struct PlayerInfo *player)
8809 int jx = player->jx, jy = player->jy;
8811 if (!player->active)
8815 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8817 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8819 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8821 player->GameOver = TRUE;
8825 void RemoveHero(struct PlayerInfo *player)
8827 int jx = player->jx, jy = player->jy;
8828 int i, found = FALSE;
8830 player->present = FALSE;
8831 player->active = FALSE;
8833 if (!ExplodeField[jx][jy])
8834 StorePlayer[jx][jy] = 0;
8836 for (i = 0; i < MAX_PLAYERS; i++)
8837 if (stored_player[i].active)
8841 AllPlayersGone = TRUE;
8848 =============================================================================
8849 checkDiagonalPushing()
8850 -----------------------------------------------------------------------------
8851 check if diagonal input device direction results in pushing of object
8852 (by checking if the alternative direction is walkable, diggable, ...)
8853 =============================================================================
8856 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8857 int x, int y, int real_dx, int real_dy)
8859 int jx, jy, dx, dy, xx, yy;
8861 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8864 /* diagonal direction: check alternative direction */
8869 xx = jx + (dx == 0 ? real_dx : 0);
8870 yy = jy + (dy == 0 ? real_dy : 0);
8872 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8876 =============================================================================
8878 -----------------------------------------------------------------------------
8879 x, y: field next to player (non-diagonal) to try to dig to
8880 real_dx, real_dy: direction as read from input device (can be diagonal)
8881 =============================================================================
8884 int DigField(struct PlayerInfo *player,
8885 int x, int y, int real_dx, int real_dy, int mode)
8887 static int change_sides[4] =
8889 CH_SIDE_RIGHT, /* moving left */
8890 CH_SIDE_LEFT, /* moving right */
8891 CH_SIDE_BOTTOM, /* moving up */
8892 CH_SIDE_TOP, /* moving down */
8894 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8895 int jx = player->jx, jy = player->jy;
8896 int dx = x - jx, dy = y - jy;
8897 int nextx = x + dx, nexty = y + dy;
8898 int move_direction = (dx == -1 ? MV_LEFT :
8899 dx == +1 ? MV_RIGHT :
8901 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8902 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
8903 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8906 if (player->MovPos == 0)
8908 player->is_digging = FALSE;
8909 player->is_collecting = FALSE;
8912 if (player->MovPos == 0) /* last pushing move finished */
8913 player->is_pushing = FALSE;
8915 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8917 player->is_switching = FALSE;
8918 player->push_delay = 0;
8920 return MF_NO_ACTION;
8923 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8924 return MF_NO_ACTION;
8927 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8929 if (IS_TUBE(Feld[jx][jy]) ||
8930 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8934 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8935 int tube_leave_directions[][2] =
8937 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8938 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8939 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8940 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8941 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8942 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8943 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8944 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8945 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8946 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8947 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8948 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8951 while (tube_leave_directions[i][0] != tube_element)
8954 if (tube_leave_directions[i][0] == -1) /* should not happen */
8958 if (!(tube_leave_directions[i][1] & move_direction))
8959 return MF_NO_ACTION; /* tube has no opening in this direction */
8962 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]) && IS_WALKABLE(Feld[jx][jy]) &&
8963 !(element_info[Feld[jx][jy]].access_direction & move_direction))
8964 return MF_NO_ACTION; /* field has no opening in this direction */
8966 element = Feld[x][y];
8968 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8969 game.engine_version >= VERSION_IDENT(2,2,0,0))
8970 return MF_NO_ACTION;
8974 case EL_SP_PORT_LEFT:
8975 case EL_SP_PORT_RIGHT:
8977 case EL_SP_PORT_DOWN:
8978 case EL_SP_PORT_HORIZONTAL:
8979 case EL_SP_PORT_VERTICAL:
8980 case EL_SP_PORT_ANY:
8981 case EL_SP_GRAVITY_PORT_LEFT:
8982 case EL_SP_GRAVITY_PORT_RIGHT:
8983 case EL_SP_GRAVITY_PORT_UP:
8984 case EL_SP_GRAVITY_PORT_DOWN:
8986 if (!canEnterSupaplexPort(x, y, dx, dy))
8987 return MF_NO_ACTION;
8990 element != EL_SP_PORT_LEFT &&
8991 element != EL_SP_GRAVITY_PORT_LEFT &&
8992 element != EL_SP_PORT_HORIZONTAL &&
8993 element != EL_SP_PORT_ANY) ||
8995 element != EL_SP_PORT_RIGHT &&
8996 element != EL_SP_GRAVITY_PORT_RIGHT &&
8997 element != EL_SP_PORT_HORIZONTAL &&
8998 element != EL_SP_PORT_ANY) ||
9000 element != EL_SP_PORT_UP &&
9001 element != EL_SP_GRAVITY_PORT_UP &&
9002 element != EL_SP_PORT_VERTICAL &&
9003 element != EL_SP_PORT_ANY) ||
9005 element != EL_SP_PORT_DOWN &&
9006 element != EL_SP_GRAVITY_PORT_DOWN &&
9007 element != EL_SP_PORT_VERTICAL &&
9008 element != EL_SP_PORT_ANY) ||
9009 !IN_LEV_FIELD(nextx, nexty) ||
9010 !IS_FREE(nextx, nexty))
9011 return MF_NO_ACTION;
9014 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9015 element == EL_SP_GRAVITY_PORT_RIGHT ||
9016 element == EL_SP_GRAVITY_PORT_UP ||
9017 element == EL_SP_GRAVITY_PORT_DOWN)
9018 game.gravity = !game.gravity;
9020 /* automatically move to the next field with double speed */
9021 player->programmed_action = move_direction;
9023 if (player->move_delay_reset_counter == 0)
9025 player->move_delay_reset_counter = 2; /* two double speed steps */
9027 DOUBLE_PLAYER_SPEED(player);
9030 player->move_delay_reset_counter = 2;
9032 DOUBLE_PLAYER_SPEED(player);
9035 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9039 case EL_TUBE_VERTICAL:
9040 case EL_TUBE_HORIZONTAL:
9041 case EL_TUBE_VERTICAL_LEFT:
9042 case EL_TUBE_VERTICAL_RIGHT:
9043 case EL_TUBE_HORIZONTAL_UP:
9044 case EL_TUBE_HORIZONTAL_DOWN:
9045 case EL_TUBE_LEFT_UP:
9046 case EL_TUBE_LEFT_DOWN:
9047 case EL_TUBE_RIGHT_UP:
9048 case EL_TUBE_RIGHT_DOWN:
9051 int tube_enter_directions[][2] =
9053 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9054 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9055 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9056 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9057 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9058 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9059 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9060 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9061 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9062 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9063 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9064 { -1, MV_NO_MOVING }
9067 while (tube_enter_directions[i][0] != element)
9070 if (tube_enter_directions[i][0] == -1) /* should not happen */
9074 if (!(tube_enter_directions[i][1] & move_direction))
9075 return MF_NO_ACTION; /* tube has no opening in this direction */
9077 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9083 if (IS_WALKABLE(element))
9085 int sound_action = ACTION_WALKING;
9087 if (IS_CUSTOM_ELEMENT(element) &&
9088 !(element_info[element].access_direction & opposite_direction))
9089 return MF_NO_ACTION; /* field not accessible from this direction */
9091 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9093 if (!player->key[element - EL_GATE_1])
9094 return MF_NO_ACTION;
9096 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9098 if (!player->key[element - EL_GATE_1_GRAY])
9099 return MF_NO_ACTION;
9101 else if (element == EL_EXIT_OPEN ||
9102 element == EL_SP_EXIT_OPEN ||
9103 element == EL_SP_EXIT_OPENING)
9105 sound_action = ACTION_PASSING; /* player is passing exit */
9107 else if (element == EL_EMPTY)
9109 sound_action = ACTION_MOVING; /* nothing to walk on */
9112 /* play sound from background or player, whatever is available */
9113 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9114 PlayLevelSoundElementAction(x, y, element, sound_action);
9116 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9120 else if (IS_PASSABLE(element))
9122 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9123 return MF_NO_ACTION;
9125 if (IS_CUSTOM_ELEMENT(element) &&
9126 !(element_info[element].access_direction & opposite_direction))
9127 return MF_NO_ACTION; /* field not accessible from this direction */
9130 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9131 return MF_NO_ACTION;
9134 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9136 if (!player->key[element - EL_EM_GATE_1])
9137 return MF_NO_ACTION;
9139 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9141 if (!player->key[element - EL_EM_GATE_1_GRAY])
9142 return MF_NO_ACTION;
9145 /* automatically move to the next field with double speed */
9146 player->programmed_action = move_direction;
9148 if (player->move_delay_reset_counter == 0)
9150 player->move_delay_reset_counter = 2; /* two double speed steps */
9152 DOUBLE_PLAYER_SPEED(player);
9155 player->move_delay_reset_counter = 2;
9157 DOUBLE_PLAYER_SPEED(player);
9160 PlayLevelSoundAction(x, y, ACTION_PASSING);
9164 else if (IS_DIGGABLE(element))
9168 if (mode != DF_SNAP)
9171 GfxElement[x][y] = GFX_ELEMENT(element);
9174 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9176 player->is_digging = TRUE;
9179 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9181 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9184 if (mode == DF_SNAP)
9185 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9190 else if (IS_COLLECTIBLE(element))
9194 if (mode != DF_SNAP)
9196 GfxElement[x][y] = element;
9197 player->is_collecting = TRUE;
9200 if (element == EL_SPEED_PILL)
9201 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9202 else if (element == EL_EXTRA_TIME && level.time > 0)
9205 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9207 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9209 player->shield_normal_time_left += 10;
9210 if (element == EL_SHIELD_DEADLY)
9211 player->shield_deadly_time_left += 10;
9213 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9215 if (player->inventory_size < MAX_INVENTORY_SIZE)
9216 player->inventory_element[player->inventory_size++] = element;
9218 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9219 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9221 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9223 player->dynabomb_count++;
9224 player->dynabombs_left++;
9226 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9228 player->dynabomb_size++;
9230 else if (element == EL_DYNABOMB_INCREASE_POWER)
9232 player->dynabomb_xl = TRUE;
9234 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9235 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9237 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9238 element - EL_KEY_1 : element - EL_EM_KEY_1);
9240 player->key[key_nr] = TRUE;
9242 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9243 el2edimg(EL_KEY_1 + key_nr));
9244 redraw_mask |= REDRAW_DOOR_1;
9246 else if (IS_ENVELOPE(element))
9249 player->show_envelope = element;
9251 ShowEnvelope(element - EL_ENVELOPE_1);
9254 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9258 for (i = 0; i < element_info[element].collect_count; i++)
9259 if (player->inventory_size < MAX_INVENTORY_SIZE)
9260 player->inventory_element[player->inventory_size++] = element;
9262 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9263 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9265 else if (element_info[element].collect_count > 0)
9267 local_player->gems_still_needed -=
9268 element_info[element].collect_count;
9269 if (local_player->gems_still_needed < 0)
9270 local_player->gems_still_needed = 0;
9272 DrawText(DX_EMERALDS, DY_EMERALDS,
9273 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9276 RaiseScoreElement(element);
9277 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9279 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9282 if (mode == DF_SNAP)
9283 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9288 else if (IS_PUSHABLE(element))
9290 if (mode == DF_SNAP && element != EL_BD_ROCK)
9291 return MF_NO_ACTION;
9293 if (CAN_FALL(element) && dy)
9294 return MF_NO_ACTION;
9296 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9297 !(element == EL_SPRING && use_spring_bug))
9298 return MF_NO_ACTION;
9301 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9302 ((move_direction & MV_VERTICAL &&
9303 ((element_info[element].move_pattern & MV_LEFT &&
9304 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9305 (element_info[element].move_pattern & MV_RIGHT &&
9306 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9307 (move_direction & MV_HORIZONTAL &&
9308 ((element_info[element].move_pattern & MV_UP &&
9309 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9310 (element_info[element].move_pattern & MV_DOWN &&
9311 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9312 return MF_NO_ACTION;
9316 /* do not push elements already moving away faster than player */
9317 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9318 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9319 return MF_NO_ACTION;
9321 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9322 return MF_NO_ACTION;
9326 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9328 if (player->push_delay_value == -1)
9329 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9331 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9333 if (!player->is_pushing)
9334 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9338 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9339 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9340 !player_is_pushing))
9341 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9344 if (!player->is_pushing &&
9345 game.engine_version >= VERSION_IDENT(2,2,0,7))
9346 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9350 printf("::: push delay: %ld [%d, %d] [%d]\n",
9351 player->push_delay_value, FrameCounter, game.engine_version,
9352 player->is_pushing);
9355 player->is_pushing = TRUE;
9357 if (!(IN_LEV_FIELD(nextx, nexty) &&
9358 (IS_FREE(nextx, nexty) ||
9359 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9360 IS_SB_ELEMENT(element)))))
9361 return MF_NO_ACTION;
9363 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9364 return MF_NO_ACTION;
9366 if (player->push_delay == 0) /* new pushing; restart delay */
9367 player->push_delay = FrameCounter;
9369 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9370 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9371 element != EL_SPRING && element != EL_BALLOON)
9373 /* make sure that there is no move delay before next try to push */
9374 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9375 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9377 return MF_NO_ACTION;
9381 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9384 if (IS_SB_ELEMENT(element))
9386 if (element == EL_SOKOBAN_FIELD_FULL)
9388 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9389 local_player->sokobanfields_still_needed++;
9392 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9394 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9395 local_player->sokobanfields_still_needed--;
9398 Feld[x][y] = EL_SOKOBAN_OBJECT;
9400 if (Back[x][y] == Back[nextx][nexty])
9401 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9402 else if (Back[x][y] != 0)
9403 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9406 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9409 if (local_player->sokobanfields_still_needed == 0 &&
9410 game.emulation == EMU_SOKOBAN)
9412 player->LevelSolved = player->GameOver = TRUE;
9413 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9417 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9419 InitMovingField(x, y, move_direction);
9420 GfxAction[x][y] = ACTION_PUSHING;
9422 if (mode == DF_SNAP)
9423 ContinueMoving(x, y);
9425 MovPos[x][y] = (dx != 0 ? dx : dy);
9427 Pushed[x][y] = TRUE;
9428 Pushed[nextx][nexty] = TRUE;
9430 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9431 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9433 player->push_delay_value = -1; /* get new value later */
9435 CheckTriggeredElementSideChange(x, y, element, dig_side,
9436 CE_OTHER_GETS_PUSHED);
9437 CheckElementSideChange(x, y, element, dig_side,
9438 CE_PUSHED_BY_PLAYER, -1);
9442 else if (IS_SWITCHABLE(element))
9444 if (PLAYER_SWITCHING(player, x, y))
9447 player->is_switching = TRUE;
9448 player->switch_x = x;
9449 player->switch_y = y;
9451 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9453 if (element == EL_ROBOT_WHEEL)
9455 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9459 DrawLevelField(x, y);
9461 else if (element == EL_SP_TERMINAL)
9465 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9467 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9469 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9470 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9473 else if (IS_BELT_SWITCH(element))
9475 ToggleBeltSwitch(x, y);
9477 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9478 element == EL_SWITCHGATE_SWITCH_DOWN)
9480 ToggleSwitchgateSwitch(x, y);
9482 else if (element == EL_LIGHT_SWITCH ||
9483 element == EL_LIGHT_SWITCH_ACTIVE)
9485 ToggleLightSwitch(x, y);
9488 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9489 SND_LIGHT_SWITCH_ACTIVATING :
9490 SND_LIGHT_SWITCH_DEACTIVATING);
9493 else if (element == EL_TIMEGATE_SWITCH)
9495 ActivateTimegateSwitch(x, y);
9497 else if (element == EL_BALLOON_SWITCH_LEFT ||
9498 element == EL_BALLOON_SWITCH_RIGHT ||
9499 element == EL_BALLOON_SWITCH_UP ||
9500 element == EL_BALLOON_SWITCH_DOWN ||
9501 element == EL_BALLOON_SWITCH_ANY)
9503 if (element == EL_BALLOON_SWITCH_ANY)
9504 game.balloon_dir = move_direction;
9506 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9507 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9508 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9509 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9512 else if (element == EL_LAMP)
9514 Feld[x][y] = EL_LAMP_ACTIVE;
9515 local_player->lights_still_needed--;
9517 DrawLevelField(x, y);
9519 else if (element == EL_TIME_ORB_FULL)
9521 Feld[x][y] = EL_TIME_ORB_EMPTY;
9523 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9525 DrawLevelField(x, y);
9528 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9536 if (!PLAYER_SWITCHING(player, x, y))
9538 player->is_switching = TRUE;
9539 player->switch_x = x;
9540 player->switch_y = y;
9542 CheckTriggeredElementSideChange(x, y, element, dig_side,
9543 CE_OTHER_IS_SWITCHING);
9544 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9547 CheckTriggeredElementSideChange(x, y, element, dig_side,
9548 CE_OTHER_GETS_PRESSED);
9549 CheckElementSideChange(x, y, element, dig_side,
9550 CE_PRESSED_BY_PLAYER, -1);
9553 return MF_NO_ACTION;
9556 player->push_delay = 0;
9558 if (Feld[x][y] != element) /* really digged/collected something */
9559 player->is_collecting = !player->is_digging;
9564 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9566 int jx = player->jx, jy = player->jy;
9567 int x = jx + dx, y = jy + dy;
9568 int snap_direction = (dx == -1 ? MV_LEFT :
9569 dx == +1 ? MV_RIGHT :
9571 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9573 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9576 if (!player->active || !IN_LEV_FIELD(x, y))
9584 if (player->MovPos == 0)
9585 player->is_pushing = FALSE;
9587 player->is_snapping = FALSE;
9589 if (player->MovPos == 0)
9591 player->is_moving = FALSE;
9592 player->is_digging = FALSE;
9593 player->is_collecting = FALSE;
9599 if (player->is_snapping)
9602 player->MovDir = snap_direction;
9605 if (player->MovPos == 0)
9608 player->is_moving = FALSE;
9609 player->is_digging = FALSE;
9610 player->is_collecting = FALSE;
9613 player->is_dropping = FALSE;
9615 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9618 player->is_snapping = TRUE;
9621 if (player->MovPos == 0)
9624 player->is_moving = FALSE;
9625 player->is_digging = FALSE;
9626 player->is_collecting = FALSE;
9629 DrawLevelField(x, y);
9635 boolean DropElement(struct PlayerInfo *player)
9637 int jx = player->jx, jy = player->jy;
9638 int old_element = Feld[jx][jy];
9641 /* check if player is active, not moving and ready to drop */
9642 if (!player->active || player->MovPos || player->drop_delay > 0)
9645 /* check if player has anything that can be dropped */
9646 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9649 /* check if anything can be dropped at the current position */
9650 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9653 /* collected custom elements can only be dropped on empty fields */
9654 if (player->inventory_size > 0 &&
9655 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9656 && old_element != EL_EMPTY)
9659 if (old_element != EL_EMPTY)
9660 Back[jx][jy] = old_element; /* store old element on this field */
9662 ResetGfxAnimation(jx, jy);
9663 ResetRandomAnimationValue(jx, jy);
9665 if (player->inventory_size > 0)
9667 player->inventory_size--;
9668 new_element = player->inventory_element[player->inventory_size];
9670 if (new_element == EL_DYNAMITE)
9671 new_element = EL_DYNAMITE_ACTIVE;
9672 else if (new_element == EL_SP_DISK_RED)
9673 new_element = EL_SP_DISK_RED_ACTIVE;
9675 Feld[jx][jy] = new_element;
9677 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9678 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9680 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9681 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9683 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9686 /* needed if previous element just changed to "empty" in the last frame */
9687 Changed[jx][jy] = 0; /* allow another change */
9690 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9691 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9693 TestIfElementTouchesCustomElement(jx, jy);
9695 else /* player is dropping a dyna bomb */
9697 player->dynabombs_left--;
9698 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9700 Feld[jx][jy] = new_element;
9702 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9703 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9705 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9712 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9714 InitField(jx, jy, FALSE);
9715 if (CAN_MOVE(Feld[jx][jy]))
9719 new_element = Feld[jx][jy];
9721 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9722 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9724 int move_stepsize = element_info[new_element].move_stepsize;
9725 int direction, dx, dy, nextx, nexty;
9727 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9728 MovDir[jx][jy] = player->MovDir;
9730 direction = MovDir[jx][jy];
9731 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9732 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9736 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9739 WasJustMoving[jx][jy] = 3;
9741 InitMovingField(jx, jy, direction);
9742 ContinueMoving(jx, jy);
9747 Changed[jx][jy] = 0; /* allow another change */
9750 TestIfElementHitsCustomElement(jx, jy, direction);
9752 CheckElementSideChange(jx, jy, new_element,
9753 direction, CE_HITTING_SOMETHING, -1);
9757 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9761 player->drop_delay = 8 + 8 + 8;
9766 player->is_dropping = TRUE;
9772 /* ------------------------------------------------------------------------- */
9773 /* game sound playing functions */
9774 /* ------------------------------------------------------------------------- */
9776 static int *loop_sound_frame = NULL;
9777 static int *loop_sound_volume = NULL;
9779 void InitPlayLevelSound()
9781 int num_sounds = getSoundListSize();
9783 checked_free(loop_sound_frame);
9784 checked_free(loop_sound_volume);
9786 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9787 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9790 static void PlayLevelSound(int x, int y, int nr)
9792 int sx = SCREENX(x), sy = SCREENY(y);
9793 int volume, stereo_position;
9794 int max_distance = 8;
9795 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9797 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9798 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9801 if (!IN_LEV_FIELD(x, y) ||
9802 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9803 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9806 volume = SOUND_MAX_VOLUME;
9808 if (!IN_SCR_FIELD(sx, sy))
9810 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9811 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9813 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9816 stereo_position = (SOUND_MAX_LEFT +
9817 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9818 (SCR_FIELDX + 2 * max_distance));
9820 if (IS_LOOP_SOUND(nr))
9822 /* This assures that quieter loop sounds do not overwrite louder ones,
9823 while restarting sound volume comparison with each new game frame. */
9825 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9828 loop_sound_volume[nr] = volume;
9829 loop_sound_frame[nr] = FrameCounter;
9832 PlaySoundExt(nr, volume, stereo_position, type);
9835 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9837 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9838 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9839 y < LEVELY(BY1) ? LEVELY(BY1) :
9840 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9844 static void PlayLevelSoundAction(int x, int y, int action)
9846 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9849 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9851 int sound_effect = element_info[element].sound[action];
9853 if (sound_effect != SND_UNDEFINED)
9854 PlayLevelSound(x, y, sound_effect);
9857 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9860 int sound_effect = element_info[element].sound[action];
9862 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9863 PlayLevelSound(x, y, sound_effect);
9866 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9868 int sound_effect = element_info[Feld[x][y]].sound[action];
9870 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9871 PlayLevelSound(x, y, sound_effect);
9874 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9876 int sound_effect = element_info[Feld[x][y]].sound[action];
9878 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9879 StopSound(sound_effect);
9882 static void PlayLevelMusic()
9884 if (levelset.music[level_nr] != MUS_UNDEFINED)
9885 PlayMusic(levelset.music[level_nr]); /* from config file */
9887 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9890 void RaiseScore(int value)
9892 local_player->score += value;
9893 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9896 void RaiseScoreElement(int element)
9902 case EL_EMERALD_YELLOW:
9903 case EL_EMERALD_RED:
9904 case EL_EMERALD_PURPLE:
9905 case EL_SP_INFOTRON:
9906 RaiseScore(level.score[SC_EMERALD]);
9909 RaiseScore(level.score[SC_DIAMOND]);
9912 RaiseScore(level.score[SC_CRYSTAL]);
9915 RaiseScore(level.score[SC_PEARL]);
9918 case EL_BD_BUTTERFLY:
9919 case EL_SP_ELECTRON:
9920 RaiseScore(level.score[SC_BUG]);
9924 case EL_SP_SNIKSNAK:
9925 RaiseScore(level.score[SC_SPACESHIP]);
9928 case EL_DARK_YAMYAM:
9929 RaiseScore(level.score[SC_YAMYAM]);
9932 RaiseScore(level.score[SC_ROBOT]);
9935 RaiseScore(level.score[SC_PACMAN]);
9938 RaiseScore(level.score[SC_NUT]);
9941 case EL_SP_DISK_RED:
9942 case EL_DYNABOMB_INCREASE_NUMBER:
9943 case EL_DYNABOMB_INCREASE_SIZE:
9944 case EL_DYNABOMB_INCREASE_POWER:
9945 RaiseScore(level.score[SC_DYNAMITE]);
9947 case EL_SHIELD_NORMAL:
9948 case EL_SHIELD_DEADLY:
9949 RaiseScore(level.score[SC_SHIELD]);
9952 RaiseScore(level.score[SC_TIME_BONUS]);
9958 RaiseScore(level.score[SC_KEY]);
9961 RaiseScore(element_info[element].collect_score);
9966 void RequestQuitGame(boolean ask_if_really_quit)
9968 if (AllPlayersGone ||
9969 !ask_if_really_quit ||
9970 level_editor_test_game ||
9971 Request("Do you really want to quit the game ?",
9972 REQ_ASK | REQ_STAY_CLOSED))
9974 #if defined(PLATFORM_UNIX)
9975 if (options.network)
9976 SendToServer_StopPlaying();
9980 game_status = GAME_MODE_MAIN;
9986 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9991 /* ---------- new game button stuff ---------------------------------------- */
9993 /* graphic position values for game buttons */
9994 #define GAME_BUTTON_XSIZE 30
9995 #define GAME_BUTTON_YSIZE 30
9996 #define GAME_BUTTON_XPOS 5
9997 #define GAME_BUTTON_YPOS 215
9998 #define SOUND_BUTTON_XPOS 5
9999 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10001 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10002 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10003 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10004 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10005 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10006 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10013 } gamebutton_info[NUM_GAME_BUTTONS] =
10016 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10021 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10022 GAME_CTRL_ID_PAUSE,
10026 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10031 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10032 SOUND_CTRL_ID_MUSIC,
10033 "background music on/off"
10036 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10037 SOUND_CTRL_ID_LOOPS,
10038 "sound loops on/off"
10041 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10042 SOUND_CTRL_ID_SIMPLE,
10043 "normal sounds on/off"
10047 void CreateGameButtons()
10051 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10053 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10054 struct GadgetInfo *gi;
10057 unsigned long event_mask;
10058 int gd_xoffset, gd_yoffset;
10059 int gd_x1, gd_x2, gd_y1, gd_y2;
10062 gd_xoffset = gamebutton_info[i].x;
10063 gd_yoffset = gamebutton_info[i].y;
10064 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10065 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10067 if (id == GAME_CTRL_ID_STOP ||
10068 id == GAME_CTRL_ID_PAUSE ||
10069 id == GAME_CTRL_ID_PLAY)
10071 button_type = GD_TYPE_NORMAL_BUTTON;
10073 event_mask = GD_EVENT_RELEASED;
10074 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10075 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10079 button_type = GD_TYPE_CHECK_BUTTON;
10081 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10082 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10083 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10084 event_mask = GD_EVENT_PRESSED;
10085 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10086 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10089 gi = CreateGadget(GDI_CUSTOM_ID, id,
10090 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10091 GDI_X, DX + gd_xoffset,
10092 GDI_Y, DY + gd_yoffset,
10093 GDI_WIDTH, GAME_BUTTON_XSIZE,
10094 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10095 GDI_TYPE, button_type,
10096 GDI_STATE, GD_BUTTON_UNPRESSED,
10097 GDI_CHECKED, checked,
10098 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10099 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10100 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10101 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10102 GDI_EVENT_MASK, event_mask,
10103 GDI_CALLBACK_ACTION, HandleGameButtons,
10107 Error(ERR_EXIT, "cannot create gadget");
10109 game_gadget[id] = gi;
10113 void FreeGameButtons()
10117 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10118 FreeGadget(game_gadget[i]);
10121 static void MapGameButtons()
10125 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10126 MapGadget(game_gadget[i]);
10129 void UnmapGameButtons()
10133 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10134 UnmapGadget(game_gadget[i]);
10137 static void HandleGameButtons(struct GadgetInfo *gi)
10139 int id = gi->custom_id;
10141 if (game_status != GAME_MODE_PLAYING)
10146 case GAME_CTRL_ID_STOP:
10147 RequestQuitGame(TRUE);
10150 case GAME_CTRL_ID_PAUSE:
10151 if (options.network)
10153 #if defined(PLATFORM_UNIX)
10155 SendToServer_ContinuePlaying();
10157 SendToServer_PausePlaying();
10161 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10164 case GAME_CTRL_ID_PLAY:
10167 #if defined(PLATFORM_UNIX)
10168 if (options.network)
10169 SendToServer_ContinuePlaying();
10173 tape.pausing = FALSE;
10174 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10179 case SOUND_CTRL_ID_MUSIC:
10180 if (setup.sound_music)
10182 setup.sound_music = FALSE;
10185 else if (audio.music_available)
10187 setup.sound = setup.sound_music = TRUE;
10189 SetAudioMode(setup.sound);
10195 case SOUND_CTRL_ID_LOOPS:
10196 if (setup.sound_loops)
10197 setup.sound_loops = FALSE;
10198 else if (audio.loops_available)
10200 setup.sound = setup.sound_loops = TRUE;
10201 SetAudioMode(setup.sound);
10205 case SOUND_CTRL_ID_SIMPLE:
10206 if (setup.sound_simple)
10207 setup.sound_simple = FALSE;
10208 else if (audio.sound_available)
10210 setup.sound = setup.sound_simple = TRUE;
10211 SetAudioMode(setup.sound);