1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
106 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (DONT_COLLIDE_WITH(e) && \
110 !PLAYER_ENEMY_PROTECTED(x, y))))
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
113 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
115 (DONT_COLLIDE_WITH(e) && \
116 IS_FREE_OR_PLAYER(x, y))))
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
120 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
124 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
127 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
130 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
134 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
135 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
136 Feld[x][y] == EL_DIAMOND))
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
142 #define PACMAN_CAN_ENTER_FIELD(x, y) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
144 IS_AMOEBOID(Feld[x][y])))
146 #define PIG_CAN_ENTER_FIELD(x, y) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 IS_FOOD_PIG(Feld[x][y])))
150 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 IS_FOOD_PENGUIN(Feld[x][y]) || \
153 Feld[x][y] == EL_EXIT_OPEN || \
154 Feld[x][y] == EL_ACID))
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
159 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
167 #define GROUP_NR(e) ((e) - EL_GROUP_START)
168 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
169 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
170 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
172 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
173 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
176 #define CE_ENTER_FIELD_COND(e, x, y) \
177 (!IS_PLAYER(x, y) && \
178 (Feld[x][y] == EL_ACID || \
179 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
181 #define CE_ENTER_FIELD_COND(e, x, y) \
182 (!IS_PLAYER(x, y) && \
183 (Feld[x][y] == EL_ACID || \
184 Feld[x][y] == MOVE_ENTER_EL(e) || \
185 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
186 IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
189 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
192 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
193 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
195 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
196 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
198 /* game button identifiers */
199 #define GAME_CTRL_ID_STOP 0
200 #define GAME_CTRL_ID_PAUSE 1
201 #define GAME_CTRL_ID_PLAY 2
202 #define SOUND_CTRL_ID_MUSIC 3
203 #define SOUND_CTRL_ID_LOOPS 4
204 #define SOUND_CTRL_ID_SIMPLE 5
206 #define NUM_GAME_BUTTONS 6
209 /* forward declaration for internal use */
211 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
212 static boolean MovePlayer(struct PlayerInfo *, int, int);
213 static void ScrollPlayer(struct PlayerInfo *, int);
214 static void ScrollScreen(struct PlayerInfo *, int);
216 static void InitBeltMovement(void);
217 static void CloseAllOpenTimegates(void);
218 static void CheckGravityMovement(struct PlayerInfo *);
219 static void KillHeroUnlessEnemyProtected(int, int);
220 static void KillHeroUnlessExplosionProtected(int, int);
222 static void TestIfPlayerTouchesCustomElement(int, int);
223 static void TestIfElementTouchesCustomElement(int, int);
224 static void TestIfElementHitsCustomElement(int, int, int);
226 static void ChangeElement(int, int, int);
227 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
228 static boolean CheckTriggeredElementChange(int, int, int, int);
229 static boolean CheckElementSideChange(int, int, int, int, int, int);
230 static boolean CheckElementChange(int, int, int, int);
232 static void PlayLevelSound(int, int, int);
233 static void PlayLevelSoundNearest(int, int, int);
234 static void PlayLevelSoundAction(int, int, int);
235 static void PlayLevelSoundElementAction(int, int, int, int);
236 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
237 static void PlayLevelSoundActionIfLoop(int, int, int);
238 static void StopLevelSoundActionIfLoop(int, int, int);
239 static void PlayLevelMusic();
241 static void MapGameButtons();
242 static void HandleGameButtons(struct GadgetInfo *);
244 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
247 /* ------------------------------------------------------------------------- */
248 /* definition of elements that automatically change to other elements after */
249 /* a specified time, eventually calling a function when changing */
250 /* ------------------------------------------------------------------------- */
252 /* forward declaration for changer functions */
253 static void InitBuggyBase(int x, int y);
254 static void WarnBuggyBase(int x, int y);
256 static void InitTrap(int x, int y);
257 static void ActivateTrap(int x, int y);
258 static void ChangeActiveTrap(int x, int y);
260 static void InitRobotWheel(int x, int y);
261 static void RunRobotWheel(int x, int y);
262 static void StopRobotWheel(int x, int y);
264 static void InitTimegateWheel(int x, int y);
265 static void RunTimegateWheel(int x, int y);
267 struct ChangingElementInfo
272 void (*pre_change_function)(int x, int y);
273 void (*change_function)(int x, int y);
274 void (*post_change_function)(int x, int y);
277 static struct ChangingElementInfo change_delay_list[] =
328 EL_SWITCHGATE_OPENING,
336 EL_SWITCHGATE_CLOSING,
337 EL_SWITCHGATE_CLOSED,
369 EL_ACID_SPLASH_RIGHT,
378 EL_SP_BUGGY_BASE_ACTIVATING,
385 EL_SP_BUGGY_BASE_ACTIVATING,
386 EL_SP_BUGGY_BASE_ACTIVE,
393 EL_SP_BUGGY_BASE_ACTIVE,
417 EL_ROBOT_WHEEL_ACTIVE,
425 EL_TIMEGATE_SWITCH_ACTIVE,
446 int push_delay_fixed, push_delay_random;
451 { EL_BALLOON, 0, 0 },
453 { EL_SOKOBAN_OBJECT, 2, 0 },
454 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
455 { EL_SATELLITE, 2, 0 },
456 { EL_SP_DISK_YELLOW, 2, 0 },
458 { EL_UNDEFINED, 0, 0 },
466 move_stepsize_list[] =
468 { EL_AMOEBA_DROP, 2 },
469 { EL_AMOEBA_DROPPING, 2 },
470 { EL_QUICKSAND_FILLING, 1 },
471 { EL_QUICKSAND_EMPTYING, 1 },
472 { EL_MAGIC_WALL_FILLING, 2 },
473 { EL_BD_MAGIC_WALL_FILLING, 2 },
474 { EL_MAGIC_WALL_EMPTYING, 2 },
475 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
485 collect_count_list[] =
488 { EL_BD_DIAMOND, 1 },
489 { EL_EMERALD_YELLOW, 1 },
490 { EL_EMERALD_RED, 1 },
491 { EL_EMERALD_PURPLE, 1 },
493 { EL_SP_INFOTRON, 1 },
500 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
502 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
503 CH_EVENT_BIT(CE_DELAY))
504 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
505 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
506 IS_JUST_CHANGING(x, y))
508 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
511 void GetPlayerConfig()
513 if (!audio.sound_available)
514 setup.sound_simple = FALSE;
516 if (!audio.loops_available)
517 setup.sound_loops = FALSE;
519 if (!audio.music_available)
520 setup.sound_music = FALSE;
522 if (!video.fullscreen_available)
523 setup.fullscreen = FALSE;
525 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
527 SetAudioMode(setup.sound);
531 static int getBeltNrFromBeltElement(int element)
533 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
534 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
535 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
538 static int getBeltNrFromBeltActiveElement(int element)
540 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
541 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
542 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
545 static int getBeltNrFromBeltSwitchElement(int element)
547 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
548 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
549 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
552 static int getBeltDirNrFromBeltSwitchElement(int element)
554 static int belt_base_element[4] =
556 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
557 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
558 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
559 EL_CONVEYOR_BELT_4_SWITCH_LEFT
562 int belt_nr = getBeltNrFromBeltSwitchElement(element);
563 int belt_dir_nr = element - belt_base_element[belt_nr];
565 return (belt_dir_nr % 3);
568 static int getBeltDirFromBeltSwitchElement(int element)
570 static int belt_move_dir[3] =
577 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
579 return belt_move_dir[belt_dir_nr];
582 static void InitPlayerField(int x, int y, int element, boolean init_game)
584 if (element == EL_SP_MURPHY)
588 if (stored_player[0].present)
590 Feld[x][y] = EL_SP_MURPHY_CLONE;
596 stored_player[0].use_murphy_graphic = TRUE;
599 Feld[x][y] = EL_PLAYER_1;
605 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
606 int jx = player->jx, jy = player->jy;
608 player->present = TRUE;
610 player->block_last_field = (element == EL_SP_MURPHY ?
611 level.sp_block_last_field :
612 level.block_last_field);
614 if (!options.network || player->connected)
616 player->active = TRUE;
618 /* remove potentially duplicate players */
619 if (StorePlayer[jx][jy] == Feld[x][y])
620 StorePlayer[jx][jy] = 0;
622 StorePlayer[x][y] = Feld[x][y];
626 printf("Player %d activated.\n", player->element_nr);
627 printf("[Local player is %d and currently %s.]\n",
628 local_player->element_nr,
629 local_player->active ? "active" : "not active");
633 Feld[x][y] = EL_EMPTY;
634 player->jx = player->last_jx = x;
635 player->jy = player->last_jy = y;
639 static void InitField(int x, int y, boolean init_game)
641 int element = Feld[x][y];
650 InitPlayerField(x, y, element, init_game);
653 case EL_SOKOBAN_FIELD_PLAYER:
654 element = Feld[x][y] = EL_PLAYER_1;
655 InitField(x, y, init_game);
657 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
658 InitField(x, y, init_game);
661 case EL_SOKOBAN_FIELD_EMPTY:
662 local_player->sokobanfields_still_needed++;
666 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
667 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
668 else if (x > 0 && Feld[x-1][y] == EL_ACID)
669 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
670 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
671 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
672 else if (y > 0 && Feld[x][y-1] == EL_ACID)
673 Feld[x][y] = EL_ACID_POOL_BOTTOM;
674 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
675 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
683 case EL_SPACESHIP_RIGHT:
684 case EL_SPACESHIP_UP:
685 case EL_SPACESHIP_LEFT:
686 case EL_SPACESHIP_DOWN:
688 case EL_BD_BUTTERFLY_RIGHT:
689 case EL_BD_BUTTERFLY_UP:
690 case EL_BD_BUTTERFLY_LEFT:
691 case EL_BD_BUTTERFLY_DOWN:
692 case EL_BD_BUTTERFLY:
693 case EL_BD_FIREFLY_RIGHT:
694 case EL_BD_FIREFLY_UP:
695 case EL_BD_FIREFLY_LEFT:
696 case EL_BD_FIREFLY_DOWN:
698 case EL_PACMAN_RIGHT:
722 if (y == lev_fieldy - 1)
724 Feld[x][y] = EL_AMOEBA_GROWING;
725 Store[x][y] = EL_AMOEBA_WET;
729 case EL_DYNAMITE_ACTIVE:
730 case EL_SP_DISK_RED_ACTIVE:
731 case EL_DYNABOMB_PLAYER_1_ACTIVE:
732 case EL_DYNABOMB_PLAYER_2_ACTIVE:
733 case EL_DYNABOMB_PLAYER_3_ACTIVE:
734 case EL_DYNABOMB_PLAYER_4_ACTIVE:
739 local_player->lights_still_needed++;
743 local_player->friends_still_needed++;
748 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
753 Feld[x][y] = EL_EMPTY;
758 case EL_EM_KEY_1_FILE:
759 Feld[x][y] = EL_EM_KEY_1;
761 case EL_EM_KEY_2_FILE:
762 Feld[x][y] = EL_EM_KEY_2;
764 case EL_EM_KEY_3_FILE:
765 Feld[x][y] = EL_EM_KEY_3;
767 case EL_EM_KEY_4_FILE:
768 Feld[x][y] = EL_EM_KEY_4;
772 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
773 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
774 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
775 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
776 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
777 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
778 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
779 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
780 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
781 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
782 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
783 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
786 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
787 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
788 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
790 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
792 game.belt_dir[belt_nr] = belt_dir;
793 game.belt_dir_nr[belt_nr] = belt_dir_nr;
795 else /* more than one switch -- set it like the first switch */
797 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
802 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
804 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
807 case EL_LIGHT_SWITCH_ACTIVE:
809 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
813 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
815 else if (IS_GROUP_ELEMENT(element))
817 struct ElementGroupInfo *group = element_info[element].group;
818 int last_anim_random_frame = gfx.anim_random_frame;
821 if (group->choice_mode == ANIM_RANDOM)
822 gfx.anim_random_frame = RND(group->num_elements_resolved);
824 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
825 group->choice_mode, 0,
828 if (group->choice_mode == ANIM_RANDOM)
829 gfx.anim_random_frame = last_anim_random_frame;
833 Feld[x][y] = group->element_resolved[element_pos];
835 InitField(x, y, init_game);
841 void DrawGameDoorValues()
845 for (i = 0; i < MAX_PLAYERS; i++)
846 for (j = 0; j < 4; j++)
847 if (stored_player[i].key[j])
848 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
849 el2edimg(EL_KEY_1 + j));
851 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
852 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
853 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
854 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
855 DrawText(DX + XX_SCORE, DY + YY_SCORE,
856 int2str(local_player->score, 5), FONT_TEXT_2);
857 DrawText(DX + XX_TIME, DY + YY_TIME,
858 int2str(TimeLeft, 3), FONT_TEXT_2);
861 static void resolve_group_element(int group_element, int recursion_depth)
864 static struct ElementGroupInfo *group;
865 struct ElementGroupInfo *actual_group = element_info[group_element].group;
868 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
870 Error(ERR_WARN, "recursion too deep when resolving group element %d",
871 group_element - EL_GROUP_START + 1);
873 /* replace element which caused too deep recursion by question mark */
874 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
879 if (recursion_depth == 0) /* initialization */
881 group = element_info[group_element].group;
882 group_nr = group_element - EL_GROUP_START;
884 group->num_elements_resolved = 0;
885 group->choice_pos = 0;
888 for (i = 0; i < actual_group->num_elements; i++)
890 int element = actual_group->element[i];
892 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
895 if (IS_GROUP_ELEMENT(element))
896 resolve_group_element(element, recursion_depth + 1);
899 group->element_resolved[group->num_elements_resolved++] = element;
900 element_info[element].in_group[group_nr] = TRUE;
905 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
907 printf("::: group %d: %d resolved elements\n",
908 group_element - EL_GROUP_START, group->num_elements_resolved);
909 for (i = 0; i < group->num_elements_resolved; i++)
910 printf("::: - %d ['%s']\n", group->element_resolved[i],
911 element_info[group->element_resolved[i]].token_name);
918 =============================================================================
920 -----------------------------------------------------------------------------
921 initialize game engine due to level / tape version number
922 =============================================================================
925 static void InitGameEngine()
929 /* set game engine from tape file when re-playing, else from level file */
930 game.engine_version = (tape.playing ? tape.engine_version :
933 /* dynamically adjust element properties according to game engine version */
934 InitElementPropertiesEngine(game.engine_version);
937 printf("level %d: level version == %06d\n", level_nr, level.game_version);
938 printf(" tape version == %06d [%s] [file: %06d]\n",
939 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
941 printf(" => game.engine_version == %06d\n", game.engine_version);
944 /* ---------- recursively resolve group elements ------------------------- */
946 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
947 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
948 element_info[i].in_group[j] = FALSE;
950 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
951 resolve_group_element(EL_GROUP_START + i, 0);
953 /* ---------- initialize player's initial move delay --------------------- */
955 /* dynamically adjust player properties according to game engine version */
956 game.initial_move_delay =
957 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
958 INITIAL_MOVE_DELAY_OFF);
960 /* dynamically adjust player properties according to level information */
961 game.initial_move_delay_value =
962 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
964 /* ---------- initialize player's initial push delay --------------------- */
966 /* dynamically adjust player properties according to game engine version */
967 game.initial_push_delay_value =
968 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
970 /* ---------- initialize changing elements ------------------------------- */
972 /* initialize changing elements information */
973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
975 struct ElementInfo *ei = &element_info[i];
977 /* this pointer might have been changed in the level editor */
978 ei->change = &ei->change_page[0];
980 if (!IS_CUSTOM_ELEMENT(i))
982 ei->change->target_element = EL_EMPTY_SPACE;
983 ei->change->delay_fixed = 0;
984 ei->change->delay_random = 0;
985 ei->change->delay_frames = 1;
988 ei->change_events = CE_BITMASK_DEFAULT;
989 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
991 ei->event_page_nr[j] = 0;
992 ei->event_page[j] = &ei->change_page[0];
996 /* add changing elements from pre-defined list */
997 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
999 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1000 struct ElementInfo *ei = &element_info[ch_delay->element];
1002 ei->change->target_element = ch_delay->target_element;
1003 ei->change->delay_fixed = ch_delay->change_delay;
1005 ei->change->pre_change_function = ch_delay->pre_change_function;
1006 ei->change->change_function = ch_delay->change_function;
1007 ei->change->post_change_function = ch_delay->post_change_function;
1009 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1013 /* add change events from custom element configuration */
1014 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1016 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1018 for (j = 0; j < ei->num_change_pages; j++)
1020 if (!ei->change_page[j].can_change)
1023 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1025 /* only add event page for the first page found with this event */
1026 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1027 !(ei->change_events & CH_EVENT_BIT(k)))
1029 ei->change_events |= CH_EVENT_BIT(k);
1030 ei->event_page_nr[k] = j;
1031 ei->event_page[k] = &ei->change_page[j];
1039 /* add change events from custom element configuration */
1040 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1042 int element = EL_CUSTOM_START + i;
1044 /* only add custom elements that change after fixed/random frame delay */
1045 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1046 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1050 /* ---------- initialize trigger events ---------------------------------- */
1052 /* initialize trigger events information */
1053 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1054 trigger_events[i] = EP_BITMASK_DEFAULT;
1057 /* add trigger events from element change event properties */
1058 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1060 struct ElementInfo *ei = &element_info[i];
1062 for (j = 0; j < ei->num_change_pages; j++)
1064 if (!ei->change_page[j].can_change)
1067 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1069 int trigger_element = ei->change_page[j].trigger_element;
1071 if (IS_GROUP_ELEMENT(trigger_element))
1073 struct ElementGroupInfo *group = element_info[trigger_element].group;
1075 for (k = 0; k < group->num_elements_resolved; k++)
1076 trigger_events[group->element_resolved[k]]
1077 |= ei->change_page[j].events;
1080 trigger_events[trigger_element] |= ei->change_page[j].events;
1085 /* add trigger events from element change event properties */
1086 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1087 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1088 trigger_events[element_info[i].change->trigger_element] |=
1089 element_info[i].change->events;
1092 /* ---------- initialize push delay -------------------------------------- */
1094 /* initialize push delay values to default */
1095 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1097 if (!IS_CUSTOM_ELEMENT(i))
1099 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1100 element_info[i].push_delay_random = game.default_push_delay_random;
1104 /* set push delay value for certain elements from pre-defined list */
1105 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1107 int e = push_delay_list[i].element;
1109 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1110 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1113 /* set push delay value for Supaplex elements for newer engine versions */
1114 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1118 if (IS_SP_ELEMENT(i))
1120 element_info[i].push_delay_fixed = 6;
1121 element_info[i].push_delay_random = 0;
1126 /* ---------- initialize move stepsize ----------------------------------- */
1128 /* initialize move stepsize values to default */
1129 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1130 if (!IS_CUSTOM_ELEMENT(i))
1131 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1133 /* set move stepsize value for certain elements from pre-defined list */
1134 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1136 int e = move_stepsize_list[i].element;
1138 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1141 /* ---------- initialize move dig/leave ---------------------------------- */
1143 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1145 element_info[i].can_leave_element = FALSE;
1146 element_info[i].can_leave_element_last = FALSE;
1149 /* ---------- initialize gem count --------------------------------------- */
1151 /* initialize gem count values for each element */
1152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1153 if (!IS_CUSTOM_ELEMENT(i))
1154 element_info[i].collect_count = 0;
1156 /* add gem count values for all elements from pre-defined list */
1157 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1158 element_info[collect_count_list[i].element].collect_count =
1159 collect_count_list[i].count;
1164 =============================================================================
1166 -----------------------------------------------------------------------------
1167 initialize and start new game
1168 =============================================================================
1173 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1174 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1175 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1182 #if USE_NEW_AMOEBA_CODE
1183 printf("Using new amoeba code.\n");
1185 printf("Using old amoeba code.\n");
1190 /* don't play tapes over network */
1191 network_playing = (options.network && !tape.playing);
1193 for (i = 0; i < MAX_PLAYERS; i++)
1195 struct PlayerInfo *player = &stored_player[i];
1197 player->index_nr = i;
1198 player->element_nr = EL_PLAYER_1 + i;
1200 player->present = FALSE;
1201 player->active = FALSE;
1204 player->effective_action = 0;
1205 player->programmed_action = 0;
1208 player->gems_still_needed = level.gems_needed;
1209 player->sokobanfields_still_needed = 0;
1210 player->lights_still_needed = 0;
1211 player->friends_still_needed = 0;
1213 for (j = 0; j < 4; j++)
1214 player->key[j] = FALSE;
1216 player->dynabomb_count = 0;
1217 player->dynabomb_size = 1;
1218 player->dynabombs_left = 0;
1219 player->dynabomb_xl = FALSE;
1221 player->MovDir = MV_NO_MOVING;
1224 player->GfxDir = MV_NO_MOVING;
1225 player->GfxAction = ACTION_DEFAULT;
1227 player->StepFrame = 0;
1229 player->use_murphy_graphic = FALSE;
1231 player->block_last_field = FALSE;
1233 player->actual_frame_counter = 0;
1235 player->step_counter = 0;
1237 player->last_move_dir = MV_NO_MOVING;
1239 player->is_waiting = FALSE;
1240 player->is_moving = FALSE;
1241 player->is_digging = FALSE;
1242 player->is_snapping = FALSE;
1243 player->is_collecting = FALSE;
1244 player->is_pushing = FALSE;
1245 player->is_switching = FALSE;
1246 player->is_dropping = FALSE;
1248 player->is_bored = FALSE;
1249 player->is_sleeping = FALSE;
1251 player->frame_counter_bored = -1;
1252 player->frame_counter_sleeping = -1;
1254 player->anim_delay_counter = 0;
1255 player->post_delay_counter = 0;
1257 player->action_waiting = ACTION_DEFAULT;
1258 player->last_action_waiting = ACTION_DEFAULT;
1259 player->special_action_bored = ACTION_DEFAULT;
1260 player->special_action_sleeping = ACTION_DEFAULT;
1262 player->num_special_action_bored = 0;
1263 player->num_special_action_sleeping = 0;
1265 /* determine number of special actions for bored and sleeping animation */
1266 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1268 boolean found = FALSE;
1270 for (k = 0; k < NUM_DIRECTIONS; k++)
1271 if (el_act_dir2img(player->element_nr, j, k) !=
1272 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1276 player->num_special_action_bored++;
1280 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1282 boolean found = FALSE;
1284 for (k = 0; k < NUM_DIRECTIONS; k++)
1285 if (el_act_dir2img(player->element_nr, j, k) !=
1286 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1290 player->num_special_action_sleeping++;
1295 player->switch_x = -1;
1296 player->switch_y = -1;
1298 player->show_envelope = 0;
1300 player->move_delay = game.initial_move_delay;
1301 player->move_delay_value = game.initial_move_delay_value;
1303 player->move_delay_reset_counter = 0;
1305 player->push_delay = 0;
1306 player->push_delay_value = game.initial_push_delay_value;
1308 player->drop_delay = 0;
1310 player->last_jx = player->last_jy = 0;
1311 player->jx = player->jy = 0;
1313 player->shield_normal_time_left = 0;
1314 player->shield_deadly_time_left = 0;
1316 player->inventory_size = 0;
1318 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1319 SnapField(player, 0, 0);
1321 player->LevelSolved = FALSE;
1322 player->GameOver = FALSE;
1325 network_player_action_received = FALSE;
1327 #if defined(PLATFORM_UNIX)
1328 /* initial null action */
1329 if (network_playing)
1330 SendToServer_MovePlayer(MV_NO_MOVING);
1338 TimeLeft = level.time;
1340 ScreenMovDir = MV_NO_MOVING;
1344 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1346 AllPlayersGone = FALSE;
1348 game.yamyam_content_nr = 0;
1349 game.magic_wall_active = FALSE;
1350 game.magic_wall_time_left = 0;
1351 game.light_time_left = 0;
1352 game.timegate_time_left = 0;
1353 game.switchgate_pos = 0;
1354 game.balloon_dir = MV_NO_MOVING;
1355 game.gravity = level.initial_gravity;
1356 game.explosions_delayed = TRUE;
1358 game.envelope_active = FALSE;
1360 for (i = 0; i < 4; i++)
1362 game.belt_dir[i] = MV_NO_MOVING;
1363 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1366 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1367 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1369 for (x = 0; x < lev_fieldx; x++)
1371 for (y = 0; y < lev_fieldy; y++)
1373 Feld[x][y] = level.field[x][y];
1374 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1375 ChangeDelay[x][y] = 0;
1376 ChangePage[x][y] = -1;
1377 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1379 WasJustMoving[x][y] = 0;
1380 WasJustFalling[x][y] = 0;
1382 Pushed[x][y] = FALSE;
1384 Changed[x][y] = CE_BITMASK_DEFAULT;
1385 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1387 ExplodePhase[x][y] = 0;
1388 ExplodeDelay[x][y] = 0;
1389 ExplodeField[x][y] = EX_NO_EXPLOSION;
1391 RunnerVisit[x][y] = 0;
1392 PlayerVisit[x][y] = 0;
1395 GfxRandom[x][y] = INIT_GFX_RANDOM();
1396 GfxElement[x][y] = EL_UNDEFINED;
1397 GfxAction[x][y] = ACTION_DEFAULT;
1398 GfxDir[x][y] = MV_NO_MOVING;
1402 for (y = 0; y < lev_fieldy; y++)
1404 for (x = 0; x < lev_fieldx; x++)
1406 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1408 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1410 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1413 InitField(x, y, TRUE);
1419 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1420 emulate_sb ? EMU_SOKOBAN :
1421 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1423 /* initialize explosion and ignition delay */
1424 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1426 if (!IS_CUSTOM_ELEMENT(i))
1429 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1430 int last_phase = num_phase * delay;
1431 int half_phase = (num_phase / 2) * delay;
1433 element_info[i].explosion_delay = last_phase;
1434 element_info[i].ignition_delay = half_phase;
1436 if (i == EL_BLACK_ORB)
1437 element_info[i].ignition_delay = 1;
1440 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1441 element_info[i].explosion_delay = 2;
1443 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1444 element_info[i].ignition_delay = 1;
1447 /* correct non-moving belts to start moving left */
1448 for (i = 0; i < 4; i++)
1449 if (game.belt_dir[i] == MV_NO_MOVING)
1450 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1452 /* check if any connected player was not found in playfield */
1453 for (i = 0; i < MAX_PLAYERS; i++)
1455 struct PlayerInfo *player = &stored_player[i];
1457 if (player->connected && !player->present)
1459 for (j = 0; j < MAX_PLAYERS; j++)
1461 struct PlayerInfo *some_player = &stored_player[j];
1462 int jx = some_player->jx, jy = some_player->jy;
1464 /* assign first free player found that is present in the playfield */
1465 if (some_player->present && !some_player->connected)
1467 player->present = TRUE;
1468 player->active = TRUE;
1470 some_player->present = FALSE;
1471 some_player->active = FALSE;
1473 StorePlayer[jx][jy] = player->element_nr;
1474 player->jx = player->last_jx = jx;
1475 player->jy = player->last_jy = jy;
1485 /* when playing a tape, eliminate all players which do not participate */
1487 for (i = 0; i < MAX_PLAYERS; i++)
1489 if (stored_player[i].active && !tape.player_participates[i])
1491 struct PlayerInfo *player = &stored_player[i];
1492 int jx = player->jx, jy = player->jy;
1494 player->active = FALSE;
1495 StorePlayer[jx][jy] = 0;
1496 Feld[jx][jy] = EL_EMPTY;
1500 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1502 /* when in single player mode, eliminate all but the first active player */
1504 for (i = 0; i < MAX_PLAYERS; i++)
1506 if (stored_player[i].active)
1508 for (j = i + 1; j < MAX_PLAYERS; j++)
1510 if (stored_player[j].active)
1512 struct PlayerInfo *player = &stored_player[j];
1513 int jx = player->jx, jy = player->jy;
1515 player->active = FALSE;
1516 player->present = FALSE;
1518 StorePlayer[jx][jy] = 0;
1519 Feld[jx][jy] = EL_EMPTY;
1526 /* when recording the game, store which players take part in the game */
1529 for (i = 0; i < MAX_PLAYERS; i++)
1530 if (stored_player[i].active)
1531 tape.player_participates[i] = TRUE;
1536 for (i = 0; i < MAX_PLAYERS; i++)
1538 struct PlayerInfo *player = &stored_player[i];
1540 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1545 if (local_player == player)
1546 printf("Player %d is local player.\n", i+1);
1550 if (BorderElement == EL_EMPTY)
1553 SBX_Right = lev_fieldx - SCR_FIELDX;
1555 SBY_Lower = lev_fieldy - SCR_FIELDY;
1560 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1562 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1565 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1566 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1568 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1569 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1571 /* if local player not found, look for custom element that might create
1572 the player (make some assumptions about the right custom element) */
1573 if (!local_player->present)
1575 int start_x = 0, start_y = 0;
1576 int found_rating = 0;
1577 int found_element = EL_UNDEFINED;
1579 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1581 int element = Feld[x][y];
1586 if (!IS_CUSTOM_ELEMENT(element))
1589 if (CAN_CHANGE(element))
1591 for (i = 0; i < element_info[element].num_change_pages; i++)
1593 content = element_info[element].change_page[i].target_element;
1594 is_player = ELEM_IS_PLAYER(content);
1596 if (is_player && (found_rating < 3 || element < found_element))
1602 found_element = element;
1607 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1609 content = element_info[element].content[xx][yy];
1610 is_player = ELEM_IS_PLAYER(content);
1612 if (is_player && (found_rating < 2 || element < found_element))
1614 start_x = x + xx - 1;
1615 start_y = y + yy - 1;
1618 found_element = element;
1621 if (!CAN_CHANGE(element))
1624 for (i = 0; i < element_info[element].num_change_pages; i++)
1626 content = element_info[element].change_page[i].content[xx][yy];
1627 is_player = ELEM_IS_PLAYER(content);
1629 if (is_player && (found_rating < 1 || element < found_element))
1631 start_x = x + xx - 1;
1632 start_y = y + yy - 1;
1635 found_element = element;
1641 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1642 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1645 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1646 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1652 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1653 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1654 local_player->jx - MIDPOSX);
1656 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1657 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1658 local_player->jy - MIDPOSY);
1660 scroll_x = SBX_Left;
1661 scroll_y = SBY_Upper;
1662 if (local_player->jx >= SBX_Left + MIDPOSX)
1663 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1664 local_player->jx - MIDPOSX :
1666 if (local_player->jy >= SBY_Upper + MIDPOSY)
1667 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1668 local_player->jy - MIDPOSY :
1673 CloseDoor(DOOR_CLOSE_1);
1678 /* after drawing the level, correct some elements */
1679 if (game.timegate_time_left == 0)
1680 CloseAllOpenTimegates();
1682 if (setup.soft_scrolling)
1683 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1685 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1688 /* copy default game door content to main double buffer */
1689 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1690 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1693 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1696 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1697 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1698 BlitBitmap(drawto, drawto,
1699 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1700 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1701 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1702 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1705 DrawGameDoorValues();
1709 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1710 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1711 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1715 /* copy actual game door content to door double buffer for OpenDoor() */
1716 BlitBitmap(drawto, bitmap_db_door,
1717 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1719 OpenDoor(DOOR_OPEN_ALL);
1721 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1723 if (setup.sound_music)
1726 KeyboardAutoRepeatOffUnlessAutoplay();
1730 for (i = 0; i < 4; i++)
1731 printf("Player %d %sactive.\n",
1732 i + 1, (stored_player[i].active ? "" : "not "));
1736 printf("::: starting game [%d]\n", FrameCounter);
1740 void InitMovDir(int x, int y)
1742 int i, element = Feld[x][y];
1743 static int xy[4][2] =
1750 static int direction[3][4] =
1752 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1753 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1754 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1763 Feld[x][y] = EL_BUG;
1764 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1767 case EL_SPACESHIP_RIGHT:
1768 case EL_SPACESHIP_UP:
1769 case EL_SPACESHIP_LEFT:
1770 case EL_SPACESHIP_DOWN:
1771 Feld[x][y] = EL_SPACESHIP;
1772 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1775 case EL_BD_BUTTERFLY_RIGHT:
1776 case EL_BD_BUTTERFLY_UP:
1777 case EL_BD_BUTTERFLY_LEFT:
1778 case EL_BD_BUTTERFLY_DOWN:
1779 Feld[x][y] = EL_BD_BUTTERFLY;
1780 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1783 case EL_BD_FIREFLY_RIGHT:
1784 case EL_BD_FIREFLY_UP:
1785 case EL_BD_FIREFLY_LEFT:
1786 case EL_BD_FIREFLY_DOWN:
1787 Feld[x][y] = EL_BD_FIREFLY;
1788 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1791 case EL_PACMAN_RIGHT:
1793 case EL_PACMAN_LEFT:
1794 case EL_PACMAN_DOWN:
1795 Feld[x][y] = EL_PACMAN;
1796 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1799 case EL_SP_SNIKSNAK:
1800 MovDir[x][y] = MV_UP;
1803 case EL_SP_ELECTRON:
1804 MovDir[x][y] = MV_LEFT;
1811 Feld[x][y] = EL_MOLE;
1812 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1816 if (IS_CUSTOM_ELEMENT(element))
1818 struct ElementInfo *ei = &element_info[element];
1819 int move_direction_initial = ei->move_direction_initial;
1820 int move_pattern = ei->move_pattern;
1822 if (move_direction_initial == MV_START_PREVIOUS)
1824 if (MovDir[x][y] != MV_NO_MOVING)
1827 move_direction_initial = MV_START_AUTOMATIC;
1830 if (move_direction_initial == MV_START_RANDOM)
1831 MovDir[x][y] = 1 << RND(4);
1832 else if (move_direction_initial & MV_ANY_DIRECTION)
1833 MovDir[x][y] = move_direction_initial;
1834 else if (move_pattern == MV_ALL_DIRECTIONS ||
1835 move_pattern == MV_TURNING_LEFT ||
1836 move_pattern == MV_TURNING_RIGHT ||
1837 move_pattern == MV_TURNING_LEFT_RIGHT ||
1838 move_pattern == MV_TURNING_RIGHT_LEFT ||
1839 move_pattern == MV_TURNING_RANDOM)
1840 MovDir[x][y] = 1 << RND(4);
1841 else if (move_pattern == MV_HORIZONTAL)
1842 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1843 else if (move_pattern == MV_VERTICAL)
1844 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1845 else if (move_pattern & MV_ANY_DIRECTION)
1846 MovDir[x][y] = element_info[element].move_pattern;
1847 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1848 move_pattern == MV_ALONG_RIGHT_SIDE)
1850 for (i = 0; i < 4; i++)
1852 int x1 = x + xy[i][0];
1853 int y1 = y + xy[i][1];
1855 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1857 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1858 MovDir[x][y] = direction[0][i];
1860 MovDir[x][y] = direction[1][i];
1869 MovDir[x][y] = 1 << RND(4);
1871 if (element != EL_BUG &&
1872 element != EL_SPACESHIP &&
1873 element != EL_BD_BUTTERFLY &&
1874 element != EL_BD_FIREFLY)
1877 for (i = 0; i < 4; i++)
1879 int x1 = x + xy[i][0];
1880 int y1 = y + xy[i][1];
1882 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1884 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1886 MovDir[x][y] = direction[0][i];
1889 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1890 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1892 MovDir[x][y] = direction[1][i];
1901 GfxDir[x][y] = MovDir[x][y];
1904 void InitAmoebaNr(int x, int y)
1907 int group_nr = AmoebeNachbarNr(x, y);
1911 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1913 if (AmoebaCnt[i] == 0)
1921 AmoebaNr[x][y] = group_nr;
1922 AmoebaCnt[group_nr]++;
1923 AmoebaCnt2[group_nr]++;
1929 boolean raise_level = FALSE;
1931 if (local_player->MovPos)
1935 if (tape.auto_play) /* tape might already be stopped here */
1936 tape.auto_play_level_solved = TRUE;
1938 if (tape.playing && tape.auto_play)
1939 tape.auto_play_level_solved = TRUE;
1942 local_player->LevelSolved = FALSE;
1944 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1948 if (!tape.playing && setup.sound_loops)
1949 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1950 SND_CTRL_PLAY_LOOP);
1952 while (TimeLeft > 0)
1954 if (!tape.playing && !setup.sound_loops)
1955 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1956 if (TimeLeft > 0 && !(TimeLeft % 10))
1957 RaiseScore(level.score[SC_TIME_BONUS]);
1958 if (TimeLeft > 100 && !(TimeLeft % 10))
1962 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1969 if (!tape.playing && setup.sound_loops)
1970 StopSound(SND_GAME_LEVELTIME_BONUS);
1972 else if (level.time == 0) /* level without time limit */
1974 if (!tape.playing && setup.sound_loops)
1975 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1976 SND_CTRL_PLAY_LOOP);
1978 while (TimePlayed < 999)
1980 if (!tape.playing && !setup.sound_loops)
1981 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1982 if (TimePlayed < 999 && !(TimePlayed % 10))
1983 RaiseScore(level.score[SC_TIME_BONUS]);
1984 if (TimePlayed < 900 && !(TimePlayed % 10))
1988 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1995 if (!tape.playing && setup.sound_loops)
1996 StopSound(SND_GAME_LEVELTIME_BONUS);
1999 /* close exit door after last player */
2000 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2001 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2003 int element = Feld[ExitX][ExitY];
2005 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2006 EL_SP_EXIT_CLOSING);
2008 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2011 /* Hero disappears */
2012 DrawLevelField(ExitX, ExitY);
2018 CloseDoor(DOOR_CLOSE_1);
2023 SaveTape(tape.level_nr); /* Ask to save tape */
2026 if (level_nr == leveldir_current->handicap_level)
2028 leveldir_current->handicap_level++;
2029 SaveLevelSetup_SeriesInfo();
2032 if (level_editor_test_game)
2033 local_player->score = -1; /* no highscore when playing from editor */
2034 else if (level_nr < leveldir_current->last_level)
2035 raise_level = TRUE; /* advance to next level */
2037 if ((hi_pos = NewHiScore()) >= 0)
2039 game_status = GAME_MODE_SCORES;
2040 DrawHallOfFame(hi_pos);
2049 game_status = GAME_MODE_MAIN;
2066 LoadScore(level_nr);
2068 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2069 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2072 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2074 if (local_player->score > highscore[k].Score)
2076 /* player has made it to the hall of fame */
2078 if (k < MAX_SCORE_ENTRIES - 1)
2080 int m = MAX_SCORE_ENTRIES - 1;
2083 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2084 if (!strcmp(setup.player_name, highscore[l].Name))
2086 if (m == k) /* player's new highscore overwrites his old one */
2090 for (l = m; l > k; l--)
2092 strcpy(highscore[l].Name, highscore[l - 1].Name);
2093 highscore[l].Score = highscore[l - 1].Score;
2100 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2101 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2102 highscore[k].Score = local_player->score;
2108 else if (!strncmp(setup.player_name, highscore[k].Name,
2109 MAX_PLAYER_NAME_LEN))
2110 break; /* player already there with a higher score */
2116 SaveScore(level_nr);
2121 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2123 if (player->GfxAction != action || player->GfxDir != dir)
2126 printf("Player frame reset! (%d => %d, %d => %d)\n",
2127 player->GfxAction, action, player->GfxDir, dir);
2130 player->GfxAction = action;
2131 player->GfxDir = dir;
2133 player->StepFrame = 0;
2137 static void ResetRandomAnimationValue(int x, int y)
2139 GfxRandom[x][y] = INIT_GFX_RANDOM();
2142 static void ResetGfxAnimation(int x, int y)
2145 GfxAction[x][y] = ACTION_DEFAULT;
2146 GfxDir[x][y] = MovDir[x][y];
2149 void InitMovingField(int x, int y, int direction)
2151 int element = Feld[x][y];
2152 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2153 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2157 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2158 ResetGfxAnimation(x, y);
2160 MovDir[newx][newy] = MovDir[x][y] = direction;
2161 GfxDir[x][y] = direction;
2163 if (Feld[newx][newy] == EL_EMPTY)
2164 Feld[newx][newy] = EL_BLOCKED;
2166 if (direction == MV_DOWN && CAN_FALL(element))
2167 GfxAction[x][y] = ACTION_FALLING;
2169 GfxAction[x][y] = ACTION_MOVING;
2171 GfxFrame[newx][newy] = GfxFrame[x][y];
2172 GfxRandom[newx][newy] = GfxRandom[x][y];
2173 GfxAction[newx][newy] = GfxAction[x][y];
2174 GfxDir[newx][newy] = GfxDir[x][y];
2177 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2179 int direction = MovDir[x][y];
2180 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2181 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2187 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2189 int oldx = x, oldy = y;
2190 int direction = MovDir[x][y];
2192 if (direction == MV_LEFT)
2194 else if (direction == MV_RIGHT)
2196 else if (direction == MV_UP)
2198 else if (direction == MV_DOWN)
2201 *comes_from_x = oldx;
2202 *comes_from_y = oldy;
2205 int MovingOrBlocked2Element(int x, int y)
2207 int element = Feld[x][y];
2209 if (element == EL_BLOCKED)
2213 Blocked2Moving(x, y, &oldx, &oldy);
2214 return Feld[oldx][oldy];
2220 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2222 /* like MovingOrBlocked2Element(), but if element is moving
2223 and (x,y) is the field the moving element is just leaving,
2224 return EL_BLOCKED instead of the element value */
2225 int element = Feld[x][y];
2227 if (IS_MOVING(x, y))
2229 if (element == EL_BLOCKED)
2233 Blocked2Moving(x, y, &oldx, &oldy);
2234 return Feld[oldx][oldy];
2243 static void RemoveField(int x, int y)
2245 Feld[x][y] = EL_EMPTY;
2252 ChangeDelay[x][y] = 0;
2253 ChangePage[x][y] = -1;
2254 Pushed[x][y] = FALSE;
2256 GfxElement[x][y] = EL_UNDEFINED;
2257 GfxAction[x][y] = ACTION_DEFAULT;
2258 GfxDir[x][y] = MV_NO_MOVING;
2261 void RemoveMovingField(int x, int y)
2263 int oldx = x, oldy = y, newx = x, newy = y;
2264 int element = Feld[x][y];
2265 int next_element = EL_UNDEFINED;
2267 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2270 if (IS_MOVING(x, y))
2272 Moving2Blocked(x, y, &newx, &newy);
2273 if (Feld[newx][newy] != EL_BLOCKED)
2276 else if (element == EL_BLOCKED)
2278 Blocked2Moving(x, y, &oldx, &oldy);
2279 if (!IS_MOVING(oldx, oldy))
2283 if (element == EL_BLOCKED &&
2284 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2285 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2286 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2287 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2288 next_element = get_next_element(Feld[oldx][oldy]);
2290 RemoveField(oldx, oldy);
2291 RemoveField(newx, newy);
2293 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2295 if (next_element != EL_UNDEFINED)
2296 Feld[oldx][oldy] = next_element;
2298 DrawLevelField(oldx, oldy);
2299 DrawLevelField(newx, newy);
2302 void DrawDynamite(int x, int y)
2304 int sx = SCREENX(x), sy = SCREENY(y);
2305 int graphic = el2img(Feld[x][y]);
2308 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2311 if (IS_WALKABLE_INSIDE(Back[x][y]))
2315 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2316 else if (Store[x][y])
2317 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2319 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2322 if (Back[x][y] || Store[x][y])
2323 DrawGraphicThruMask(sx, sy, graphic, frame);
2325 DrawGraphic(sx, sy, graphic, frame);
2327 if (game.emulation == EMU_SUPAPLEX)
2328 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2329 else if (Store[x][y])
2330 DrawGraphicThruMask(sx, sy, graphic, frame);
2332 DrawGraphic(sx, sy, graphic, frame);
2336 void CheckDynamite(int x, int y)
2338 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2342 if (MovDelay[x][y] != 0)
2345 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2352 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2354 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2355 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2356 StopSound(SND_DYNAMITE_ACTIVE);
2358 StopSound(SND_DYNABOMB_ACTIVE);
2364 void RelocatePlayer(int x, int y, int element)
2366 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2367 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2368 boolean no_delay = (tape.index_search);
2369 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2370 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2372 if (player->GameOver) /* do not reanimate dead player */
2376 RemoveField(x, y); /* temporarily remove newly placed player */
2377 DrawLevelField(x, y);
2380 if (player->present)
2382 while (player->MovPos)
2384 ScrollPlayer(player, SCROLL_GO_ON);
2385 ScrollScreen(NULL, SCROLL_GO_ON);
2391 Delay(wait_delay_value);
2394 DrawPlayer(player); /* needed here only to cleanup last field */
2395 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2397 player->is_moving = FALSE;
2400 Feld[x][y] = element;
2401 InitPlayerField(x, y, element, TRUE);
2403 if (player == local_player)
2405 int scroll_xx = -999, scroll_yy = -999;
2407 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2410 int fx = FX, fy = FY;
2412 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2413 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2414 local_player->jx - MIDPOSX);
2416 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2417 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2418 local_player->jy - MIDPOSY);
2420 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2421 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2426 fx += dx * TILEX / 2;
2427 fy += dy * TILEY / 2;
2429 ScrollLevel(dx, dy);
2432 /* scroll in two steps of half tile size to make things smoother */
2433 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2435 Delay(wait_delay_value);
2437 /* scroll second step to align at full tile size */
2439 Delay(wait_delay_value);
2444 void Explode(int ex, int ey, int phase, int mode)
2448 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2449 int last_phase = num_phase * delay;
2450 int half_phase = (num_phase / 2) * delay;
2451 int first_phase_after_start = EX_PHASE_START + 1;
2454 int last_phase_TEST = last_phase;
2456 if (game.explosions_delayed)
2458 ExplodeField[ex][ey] = mode;
2462 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2464 int center_element = Feld[ex][ey];
2467 /* --- This is only really needed (and now handled) in "Impact()". --- */
2468 /* do not explode moving elements that left the explode field in time */
2469 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2470 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2474 if (mode == EX_NORMAL || mode == EX_CENTER)
2475 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2477 /* remove things displayed in background while burning dynamite */
2478 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2481 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2483 /* put moving element to center field (and let it explode there) */
2484 center_element = MovingOrBlocked2Element(ex, ey);
2485 RemoveMovingField(ex, ey);
2486 Feld[ex][ey] = center_element;
2490 last_phase = element_info[center_element].explosion_delay;
2493 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2495 int xx = x - ex + 1;
2496 int yy = y - ey + 1;
2499 if (!IN_LEV_FIELD(x, y) ||
2500 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2501 (x != ex || y != ey)))
2504 element = Feld[x][y];
2506 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2508 element = MovingOrBlocked2Element(x, y);
2510 if (!IS_EXPLOSION_PROOF(element))
2511 RemoveMovingField(x, y);
2517 if (IS_EXPLOSION_PROOF(element))
2520 /* indestructible elements can only explode in center (but not flames) */
2521 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2522 element == EL_FLAMES)
2527 if ((IS_INDESTRUCTIBLE(element) &&
2528 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2529 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2530 element == EL_FLAMES)
2534 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2536 if (IS_ACTIVE_BOMB(element))
2538 /* re-activate things under the bomb like gate or penguin */
2539 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2546 /* save walkable background elements while explosion on same tile */
2548 if (IS_INDESTRUCTIBLE(element))
2549 Back[x][y] = element;
2551 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2552 Back[x][y] = element;
2555 /* ignite explodable elements reached by other explosion */
2556 if (element == EL_EXPLOSION)
2557 element = Store2[x][y];
2560 if (AmoebaNr[x][y] &&
2561 (element == EL_AMOEBA_FULL ||
2562 element == EL_BD_AMOEBA ||
2563 element == EL_AMOEBA_GROWING))
2565 AmoebaCnt[AmoebaNr[x][y]]--;
2566 AmoebaCnt2[AmoebaNr[x][y]]--;
2572 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2574 switch(StorePlayer[ex][ey])
2577 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2580 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2583 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2587 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2591 if (game.emulation == EMU_SUPAPLEX)
2592 Store[x][y] = EL_EMPTY;
2594 else if (center_element == EL_MOLE)
2595 Store[x][y] = EL_EMERALD_RED;
2596 else if (center_element == EL_PENGUIN)
2597 Store[x][y] = EL_EMERALD_PURPLE;
2598 else if (center_element == EL_BUG)
2599 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2600 else if (center_element == EL_BD_BUTTERFLY)
2601 Store[x][y] = EL_BD_DIAMOND;
2602 else if (center_element == EL_SP_ELECTRON)
2603 Store[x][y] = EL_SP_INFOTRON;
2604 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2605 Store[x][y] = level.amoeba_content;
2606 else if (center_element == EL_YAMYAM)
2607 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2608 else if (IS_CUSTOM_ELEMENT(center_element) &&
2609 element_info[center_element].content[xx][yy] != EL_EMPTY)
2610 Store[x][y] = element_info[center_element].content[xx][yy];
2611 else if (element == EL_WALL_EMERALD)
2612 Store[x][y] = EL_EMERALD;
2613 else if (element == EL_WALL_DIAMOND)
2614 Store[x][y] = EL_DIAMOND;
2615 else if (element == EL_WALL_BD_DIAMOND)
2616 Store[x][y] = EL_BD_DIAMOND;
2617 else if (element == EL_WALL_EMERALD_YELLOW)
2618 Store[x][y] = EL_EMERALD_YELLOW;
2619 else if (element == EL_WALL_EMERALD_RED)
2620 Store[x][y] = EL_EMERALD_RED;
2621 else if (element == EL_WALL_EMERALD_PURPLE)
2622 Store[x][y] = EL_EMERALD_PURPLE;
2623 else if (element == EL_WALL_PEARL)
2624 Store[x][y] = EL_PEARL;
2625 else if (element == EL_WALL_CRYSTAL)
2626 Store[x][y] = EL_CRYSTAL;
2627 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2628 Store[x][y] = element_info[element].content[1][1];
2630 Store[x][y] = EL_EMPTY;
2632 if (x != ex || y != ey ||
2633 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2634 Store2[x][y] = element;
2637 if (AmoebaNr[x][y] &&
2638 (element == EL_AMOEBA_FULL ||
2639 element == EL_BD_AMOEBA ||
2640 element == EL_AMOEBA_GROWING))
2642 AmoebaCnt[AmoebaNr[x][y]]--;
2643 AmoebaCnt2[AmoebaNr[x][y]]--;
2649 MovDir[x][y] = MovPos[x][y] = 0;
2650 GfxDir[x][y] = MovDir[x][y];
2655 Feld[x][y] = EL_EXPLOSION;
2657 GfxElement[x][y] = center_element;
2659 GfxElement[x][y] = EL_UNDEFINED;
2662 ExplodePhase[x][y] = 1;
2664 ExplodeDelay[x][y] = last_phase;
2669 if (center_element == EL_YAMYAM)
2670 game.yamyam_content_nr =
2671 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2683 last_phase = ExplodeDelay[x][y];
2686 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2690 /* activate this even in non-DEBUG version until cause for crash in
2691 getGraphicAnimationFrame() (see below) is found and eliminated */
2695 if (GfxElement[x][y] == EL_UNDEFINED)
2698 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2699 printf("Explode(): This should never happen!\n");
2702 GfxElement[x][y] = EL_EMPTY;
2708 border_element = Store2[x][y];
2709 if (IS_PLAYER(x, y))
2710 border_element = StorePlayer[x][y];
2712 if (phase == element_info[border_element].ignition_delay ||
2713 phase == last_phase)
2715 if (IS_PLAYER(x, y))
2718 printf("::: IS_PLAYER\n");
2720 KillHeroUnlessExplosionProtected(x, y);
2723 else if (CAN_EXPLODE_BY_FIRE(border_element))
2726 printf("::: CAN_EXPLODE_BY_FIRE\n");
2728 Feld[x][y] = Store2[x][y];
2733 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2736 printf("::: EL_AMOEBA_TO_DIAMOND\n");
2738 AmoebeUmwandeln(x, y);
2743 if (phase == last_phase)
2747 element = Feld[x][y] = Store[x][y];
2748 Store[x][y] = Store2[x][y] = 0;
2749 GfxElement[x][y] = EL_UNDEFINED;
2751 /* player can escape from explosions and might therefore be still alive */
2752 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2753 element <= EL_PLAYER_IS_EXPLODING_4)
2754 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2756 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2757 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2758 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2761 /* restore probably existing indestructible background element */
2762 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2763 element = Feld[x][y] = Back[x][y];
2766 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2767 GfxDir[x][y] = MV_NO_MOVING;
2768 ChangeDelay[x][y] = 0;
2769 ChangePage[x][y] = -1;
2771 InitField(x, y, FALSE);
2773 /* !!! not needed !!! */
2774 if (CAN_MOVE(element))
2777 DrawLevelField(x, y);
2779 TestIfElementTouchesCustomElement(x, y);
2781 if (GFX_CRUMBLED(element))
2782 DrawLevelFieldCrumbledSandNeighbours(x, y);
2784 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2785 StorePlayer[x][y] = 0;
2787 if (ELEM_IS_PLAYER(element))
2788 RelocatePlayer(x, y, element);
2790 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2793 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2795 int stored = Store[x][y];
2796 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2797 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2800 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2803 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2804 element_info[GfxElement[x][y]].token_name,
2809 DrawLevelFieldCrumbledSand(x, y);
2811 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2813 DrawLevelElement(x, y, Back[x][y]);
2814 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2816 else if (IS_WALKABLE_UNDER(Back[x][y]))
2818 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2819 DrawLevelElementThruMask(x, y, Back[x][y]);
2821 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2822 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2827 if (phase == first_phase_after_start)
2829 int element = Store2[x][y];
2831 if (element == EL_BLACK_ORB)
2833 Feld[x][y] = Store2[x][y];
2838 else if (phase == half_phase)
2840 int element = Store2[x][y];
2842 if (IS_PLAYER(x, y))
2843 KillHeroUnlessExplosionProtected(x, y);
2844 else if (CAN_EXPLODE_BY_FIRE(element))
2846 Feld[x][y] = Store2[x][y];
2850 else if (element == EL_AMOEBA_TO_DIAMOND)
2851 AmoebeUmwandeln(x, y);
2854 if (phase == last_phase)
2858 element = Feld[x][y] = Store[x][y];
2859 Store[x][y] = Store2[x][y] = 0;
2860 GfxElement[x][y] = EL_UNDEFINED;
2862 /* player can escape from explosions and might therefore be still alive */
2863 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2864 element <= EL_PLAYER_IS_EXPLODING_4)
2865 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2867 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2868 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2869 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2872 /* restore probably existing indestructible background element */
2873 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2874 element = Feld[x][y] = Back[x][y];
2877 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2878 GfxDir[x][y] = MV_NO_MOVING;
2879 ChangeDelay[x][y] = 0;
2880 ChangePage[x][y] = -1;
2882 InitField(x, y, FALSE);
2884 /* !!! not needed !!! */
2885 if (CAN_MOVE(element))
2888 DrawLevelField(x, y);
2890 TestIfElementTouchesCustomElement(x, y);
2892 if (GFX_CRUMBLED(element))
2893 DrawLevelFieldCrumbledSandNeighbours(x, y);
2895 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2896 StorePlayer[x][y] = 0;
2898 if (ELEM_IS_PLAYER(element))
2899 RelocatePlayer(x, y, element);
2901 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2904 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2906 int stored = Store[x][y];
2907 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2908 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2911 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2914 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2915 element_info[GfxElement[x][y]].token_name,
2920 DrawLevelFieldCrumbledSand(x, y);
2922 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2924 DrawLevelElement(x, y, Back[x][y]);
2925 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2927 else if (IS_WALKABLE_UNDER(Back[x][y]))
2929 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2930 DrawLevelElementThruMask(x, y, Back[x][y]);
2932 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2933 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2938 void DynaExplode(int ex, int ey)
2941 int dynabomb_element = Feld[ex][ey];
2942 int dynabomb_size = 1;
2943 boolean dynabomb_xl = FALSE;
2944 struct PlayerInfo *player;
2945 static int xy[4][2] =
2953 if (IS_ACTIVE_BOMB(dynabomb_element))
2955 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2956 dynabomb_size = player->dynabomb_size;
2957 dynabomb_xl = player->dynabomb_xl;
2958 player->dynabombs_left++;
2961 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2963 for (i = 0; i < 4; i++)
2965 for (j = 1; j <= dynabomb_size; j++)
2967 int x = ex + j * xy[i % 4][0];
2968 int y = ey + j * xy[i % 4][1];
2971 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2974 element = Feld[x][y];
2976 /* do not restart explosions of fields with active bombs */
2977 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2980 Explode(x, y, EX_PHASE_START, EX_BORDER);
2982 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2983 if (element != EL_EMPTY &&
2984 element != EL_SAND &&
2985 element != EL_EXPLOSION &&
2992 void Bang(int x, int y)
2995 int element = MovingOrBlocked2Element(x, y);
2997 int element = Feld[x][y];
3001 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3003 if (IS_PLAYER(x, y))
3006 struct PlayerInfo *player = PLAYERINFO(x, y);
3008 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3009 player->element_nr);
3014 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3016 if (game.emulation == EMU_SUPAPLEX)
3017 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3019 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3024 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3032 case EL_BD_BUTTERFLY:
3035 case EL_DARK_YAMYAM:
3039 RaiseScoreElement(element);
3040 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3042 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3043 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3044 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3045 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3046 case EL_DYNABOMB_INCREASE_NUMBER:
3047 case EL_DYNABOMB_INCREASE_SIZE:
3048 case EL_DYNABOMB_INCREASE_POWER:
3053 case EL_LAMP_ACTIVE:
3054 if (IS_PLAYER(x, y))
3055 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3057 Explode(x, y, EX_PHASE_START, EX_CENTER);
3060 if (CAN_EXPLODE_DYNA(element))
3062 else if (CAN_EXPLODE_1X1(element))
3063 Explode(x, y, EX_PHASE_START, EX_CENTER);
3065 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3069 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3072 void SplashAcid(int x, int y)
3074 int element = Feld[x][y];
3076 if (element != EL_ACID_SPLASH_LEFT &&
3077 element != EL_ACID_SPLASH_RIGHT)
3079 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3081 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3082 (!IN_LEV_FIELD(x-1, y-1) ||
3083 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3084 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3086 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3087 (!IN_LEV_FIELD(x+1, y-1) ||
3088 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3089 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3093 static void InitBeltMovement()
3095 static int belt_base_element[4] =
3097 EL_CONVEYOR_BELT_1_LEFT,
3098 EL_CONVEYOR_BELT_2_LEFT,
3099 EL_CONVEYOR_BELT_3_LEFT,
3100 EL_CONVEYOR_BELT_4_LEFT
3102 static int belt_base_active_element[4] =
3104 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3105 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3106 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3107 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3112 /* set frame order for belt animation graphic according to belt direction */
3113 for (i = 0; i < 4; i++)
3117 for (j = 0; j < 3; j++)
3119 int element = belt_base_active_element[belt_nr] + j;
3120 int graphic = el2img(element);
3122 if (game.belt_dir[i] == MV_LEFT)
3123 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3125 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3129 for (y = 0; y < lev_fieldy; y++)
3131 for (x = 0; x < lev_fieldx; x++)
3133 int element = Feld[x][y];
3135 for (i = 0; i < 4; i++)
3137 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3139 int e_belt_nr = getBeltNrFromBeltElement(element);
3142 if (e_belt_nr == belt_nr)
3144 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3146 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3154 static void ToggleBeltSwitch(int x, int y)
3156 static int belt_base_element[4] =
3158 EL_CONVEYOR_BELT_1_LEFT,
3159 EL_CONVEYOR_BELT_2_LEFT,
3160 EL_CONVEYOR_BELT_3_LEFT,
3161 EL_CONVEYOR_BELT_4_LEFT
3163 static int belt_base_active_element[4] =
3165 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3166 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3167 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3168 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3170 static int belt_base_switch_element[4] =
3172 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3173 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3174 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3175 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3177 static int belt_move_dir[4] =
3185 int element = Feld[x][y];
3186 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3187 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3188 int belt_dir = belt_move_dir[belt_dir_nr];
3191 if (!IS_BELT_SWITCH(element))
3194 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3195 game.belt_dir[belt_nr] = belt_dir;
3197 if (belt_dir_nr == 3)
3200 /* set frame order for belt animation graphic according to belt direction */
3201 for (i = 0; i < 3; i++)
3203 int element = belt_base_active_element[belt_nr] + i;
3204 int graphic = el2img(element);
3206 if (belt_dir == MV_LEFT)
3207 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3209 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3212 for (yy = 0; yy < lev_fieldy; yy++)
3214 for (xx = 0; xx < lev_fieldx; xx++)
3216 int element = Feld[xx][yy];
3218 if (IS_BELT_SWITCH(element))
3220 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3222 if (e_belt_nr == belt_nr)
3224 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3225 DrawLevelField(xx, yy);
3228 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3230 int e_belt_nr = getBeltNrFromBeltElement(element);
3232 if (e_belt_nr == belt_nr)
3234 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3236 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3237 DrawLevelField(xx, yy);
3240 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3242 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3244 if (e_belt_nr == belt_nr)
3246 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3248 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3249 DrawLevelField(xx, yy);
3256 static void ToggleSwitchgateSwitch(int x, int y)
3260 game.switchgate_pos = !game.switchgate_pos;
3262 for (yy = 0; yy < lev_fieldy; yy++)
3264 for (xx = 0; xx < lev_fieldx; xx++)
3266 int element = Feld[xx][yy];
3268 if (element == EL_SWITCHGATE_SWITCH_UP ||
3269 element == EL_SWITCHGATE_SWITCH_DOWN)
3271 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3272 DrawLevelField(xx, yy);
3274 else if (element == EL_SWITCHGATE_OPEN ||
3275 element == EL_SWITCHGATE_OPENING)
3277 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3279 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3281 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3284 else if (element == EL_SWITCHGATE_CLOSED ||
3285 element == EL_SWITCHGATE_CLOSING)
3287 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3289 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3291 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3298 static int getInvisibleActiveFromInvisibleElement(int element)
3300 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3301 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3302 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3306 static int getInvisibleFromInvisibleActiveElement(int element)
3308 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3309 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3310 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3314 static void RedrawAllLightSwitchesAndInvisibleElements()
3318 for (y = 0; y < lev_fieldy; y++)
3320 for (x = 0; x < lev_fieldx; x++)
3322 int element = Feld[x][y];
3324 if (element == EL_LIGHT_SWITCH &&
3325 game.light_time_left > 0)
3327 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3328 DrawLevelField(x, y);
3330 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3331 game.light_time_left == 0)
3333 Feld[x][y] = EL_LIGHT_SWITCH;
3334 DrawLevelField(x, y);
3336 else if (element == EL_INVISIBLE_STEELWALL ||
3337 element == EL_INVISIBLE_WALL ||
3338 element == EL_INVISIBLE_SAND)
3340 if (game.light_time_left > 0)
3341 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3343 DrawLevelField(x, y);
3345 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3346 element == EL_INVISIBLE_WALL_ACTIVE ||
3347 element == EL_INVISIBLE_SAND_ACTIVE)
3349 if (game.light_time_left == 0)
3350 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3352 DrawLevelField(x, y);
3358 static void ToggleLightSwitch(int x, int y)
3360 int element = Feld[x][y];
3362 game.light_time_left =
3363 (element == EL_LIGHT_SWITCH ?
3364 level.time_light * FRAMES_PER_SECOND : 0);
3366 RedrawAllLightSwitchesAndInvisibleElements();
3369 static void ActivateTimegateSwitch(int x, int y)
3373 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3375 for (yy = 0; yy < lev_fieldy; yy++)
3377 for (xx = 0; xx < lev_fieldx; xx++)
3379 int element = Feld[xx][yy];
3381 if (element == EL_TIMEGATE_CLOSED ||
3382 element == EL_TIMEGATE_CLOSING)
3384 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3385 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3389 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3391 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3392 DrawLevelField(xx, yy);
3399 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3402 inline static int getElementMoveStepsize(int x, int y)
3404 int element = Feld[x][y];
3405 int direction = MovDir[x][y];
3406 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3407 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3408 int horiz_move = (dx != 0);
3409 int sign = (horiz_move ? dx : dy);
3410 int step = sign * element_info[element].move_stepsize;
3412 /* special values for move stepsize for spring and things on conveyor belt */
3415 if (CAN_FALL(element) &&
3416 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3417 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3418 else if (element == EL_SPRING)
3419 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3425 void Impact(int x, int y)
3427 boolean lastline = (y == lev_fieldy-1);
3428 boolean object_hit = FALSE;
3429 boolean impact = (lastline || object_hit);
3430 int element = Feld[x][y];
3431 int smashed = EL_UNDEFINED;
3433 if (!lastline) /* check if element below was hit */
3435 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3438 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3439 MovDir[x][y + 1] != MV_DOWN ||
3440 MovPos[x][y + 1] <= TILEY / 2));
3443 object_hit = !IS_FREE(x, y + 1);
3446 /* do not smash moving elements that left the smashed field in time */
3447 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3448 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3452 smashed = MovingOrBlocked2Element(x, y + 1);
3454 impact = (lastline || object_hit);
3457 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3463 /* only reset graphic animation if graphic really changes after impact */
3465 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3467 ResetGfxAnimation(x, y);
3468 DrawLevelField(x, y);
3471 if (impact && CAN_EXPLODE_IMPACT(element))
3476 else if (impact && element == EL_PEARL)
3478 Feld[x][y] = EL_PEARL_BREAKING;
3479 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3482 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3484 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3489 if (impact && element == EL_AMOEBA_DROP)
3491 if (object_hit && IS_PLAYER(x, y + 1))
3492 KillHeroUnlessEnemyProtected(x, y + 1);
3493 else if (object_hit && smashed == EL_PENGUIN)
3497 Feld[x][y] = EL_AMOEBA_GROWING;
3498 Store[x][y] = EL_AMOEBA_WET;
3500 ResetRandomAnimationValue(x, y);
3505 if (object_hit) /* check which object was hit */
3507 if (CAN_PASS_MAGIC_WALL(element) &&
3508 (smashed == EL_MAGIC_WALL ||
3509 smashed == EL_BD_MAGIC_WALL))
3512 int activated_magic_wall =
3513 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3514 EL_BD_MAGIC_WALL_ACTIVE);
3516 /* activate magic wall / mill */
3517 for (yy = 0; yy < lev_fieldy; yy++)
3518 for (xx = 0; xx < lev_fieldx; xx++)
3519 if (Feld[xx][yy] == smashed)
3520 Feld[xx][yy] = activated_magic_wall;
3522 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3523 game.magic_wall_active = TRUE;
3525 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3526 SND_MAGIC_WALL_ACTIVATING :
3527 SND_BD_MAGIC_WALL_ACTIVATING));
3530 if (IS_PLAYER(x, y + 1))
3532 if (CAN_SMASH_PLAYER(element))
3534 KillHeroUnlessEnemyProtected(x, y + 1);
3538 else if (smashed == EL_PENGUIN)
3540 if (CAN_SMASH_PLAYER(element))
3546 else if (element == EL_BD_DIAMOND)
3548 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3554 else if (((element == EL_SP_INFOTRON ||
3555 element == EL_SP_ZONK) &&
3556 (smashed == EL_SP_SNIKSNAK ||
3557 smashed == EL_SP_ELECTRON ||
3558 smashed == EL_SP_DISK_ORANGE)) ||
3559 (element == EL_SP_INFOTRON &&
3560 smashed == EL_SP_DISK_YELLOW))
3566 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3572 else if (CAN_SMASH_EVERYTHING(element))
3574 if (IS_CLASSIC_ENEMY(smashed) ||
3575 CAN_EXPLODE_SMASHED(smashed))
3580 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3582 if (smashed == EL_LAMP ||
3583 smashed == EL_LAMP_ACTIVE)
3588 else if (smashed == EL_NUT)
3590 Feld[x][y + 1] = EL_NUT_BREAKING;
3591 PlayLevelSound(x, y, SND_NUT_BREAKING);
3592 RaiseScoreElement(EL_NUT);
3595 else if (smashed == EL_PEARL)
3597 Feld[x][y + 1] = EL_PEARL_BREAKING;
3598 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3601 else if (smashed == EL_DIAMOND)
3603 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3604 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3607 else if (IS_BELT_SWITCH(smashed))
3609 ToggleBeltSwitch(x, y + 1);
3611 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3612 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3614 ToggleSwitchgateSwitch(x, y + 1);
3616 else if (smashed == EL_LIGHT_SWITCH ||
3617 smashed == EL_LIGHT_SWITCH_ACTIVE)
3619 ToggleLightSwitch(x, y + 1);
3623 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3625 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3626 CE_OTHER_IS_SWITCHING);
3627 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3633 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3638 /* play sound of magic wall / mill */
3640 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3641 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3643 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3644 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3645 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3646 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3651 /* play sound of object that hits the ground */
3652 if (lastline || object_hit)
3653 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3656 inline static void TurnRoundExt(int x, int y)
3668 { 0, 0 }, { 0, 0 }, { 0, 0 },
3673 int left, right, back;
3677 { MV_DOWN, MV_UP, MV_RIGHT },
3678 { MV_UP, MV_DOWN, MV_LEFT },
3680 { MV_LEFT, MV_RIGHT, MV_DOWN },
3684 { MV_RIGHT, MV_LEFT, MV_UP }
3687 int element = Feld[x][y];
3688 int move_pattern = element_info[element].move_pattern;
3690 int old_move_dir = MovDir[x][y];
3691 int left_dir = turn[old_move_dir].left;
3692 int right_dir = turn[old_move_dir].right;
3693 int back_dir = turn[old_move_dir].back;
3695 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3696 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3697 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3698 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3700 int left_x = x + left_dx, left_y = y + left_dy;
3701 int right_x = x + right_dx, right_y = y + right_dy;
3702 int move_x = x + move_dx, move_y = y + move_dy;
3706 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3708 TestIfBadThingTouchesOtherBadThing(x, y);
3710 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3711 MovDir[x][y] = right_dir;
3712 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3713 MovDir[x][y] = left_dir;
3715 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3717 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3720 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3721 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3723 TestIfBadThingTouchesOtherBadThing(x, y);
3725 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3726 MovDir[x][y] = left_dir;
3727 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3728 MovDir[x][y] = right_dir;
3730 if ((element == EL_SPACESHIP ||
3731 element == EL_SP_SNIKSNAK ||
3732 element == EL_SP_ELECTRON)
3733 && MovDir[x][y] != old_move_dir)
3735 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3738 else if (element == EL_YAMYAM)
3740 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3741 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3743 if (can_turn_left && can_turn_right)
3744 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3745 else if (can_turn_left)
3746 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3747 else if (can_turn_right)
3748 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3750 MovDir[x][y] = back_dir;
3752 MovDelay[x][y] = 16 + 16 * RND(3);
3754 else if (element == EL_DARK_YAMYAM)
3756 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3757 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3759 if (can_turn_left && can_turn_right)
3760 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3761 else if (can_turn_left)
3762 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3763 else if (can_turn_right)
3764 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3766 MovDir[x][y] = back_dir;
3768 MovDelay[x][y] = 16 + 16 * RND(3);
3770 else if (element == EL_PACMAN)
3772 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3773 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3775 if (can_turn_left && can_turn_right)
3776 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3777 else if (can_turn_left)
3778 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3779 else if (can_turn_right)
3780 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3782 MovDir[x][y] = back_dir;
3784 MovDelay[x][y] = 6 + RND(40);
3786 else if (element == EL_PIG)
3788 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3789 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3790 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3791 boolean should_turn_left, should_turn_right, should_move_on;
3793 int rnd = RND(rnd_value);
3795 should_turn_left = (can_turn_left &&
3797 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3798 y + back_dy + left_dy)));
3799 should_turn_right = (can_turn_right &&
3801 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3802 y + back_dy + right_dy)));
3803 should_move_on = (can_move_on &&
3806 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3807 y + move_dy + left_dy) ||
3808 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3809 y + move_dy + right_dy)));
3811 if (should_turn_left || should_turn_right || should_move_on)
3813 if (should_turn_left && should_turn_right && should_move_on)
3814 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3815 rnd < 2 * rnd_value / 3 ? right_dir :
3817 else if (should_turn_left && should_turn_right)
3818 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3819 else if (should_turn_left && should_move_on)
3820 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3821 else if (should_turn_right && should_move_on)
3822 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3823 else if (should_turn_left)
3824 MovDir[x][y] = left_dir;
3825 else if (should_turn_right)
3826 MovDir[x][y] = right_dir;
3827 else if (should_move_on)
3828 MovDir[x][y] = old_move_dir;
3830 else if (can_move_on && rnd > rnd_value / 8)
3831 MovDir[x][y] = old_move_dir;
3832 else if (can_turn_left && can_turn_right)
3833 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3834 else if (can_turn_left && rnd > rnd_value / 8)
3835 MovDir[x][y] = left_dir;
3836 else if (can_turn_right && rnd > rnd_value/8)
3837 MovDir[x][y] = right_dir;
3839 MovDir[x][y] = back_dir;
3841 xx = x + move_xy[MovDir[x][y]].x;
3842 yy = y + move_xy[MovDir[x][y]].y;
3844 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3845 MovDir[x][y] = old_move_dir;
3849 else if (element == EL_DRAGON)
3851 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3852 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3853 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3855 int rnd = RND(rnd_value);
3858 if (FrameCounter < 1 && x == 0 && y == 29)
3859 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3862 if (can_move_on && rnd > rnd_value / 8)
3863 MovDir[x][y] = old_move_dir;
3864 else if (can_turn_left && can_turn_right)
3865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3866 else if (can_turn_left && rnd > rnd_value / 8)
3867 MovDir[x][y] = left_dir;
3868 else if (can_turn_right && rnd > rnd_value / 8)
3869 MovDir[x][y] = right_dir;
3871 MovDir[x][y] = back_dir;
3873 xx = x + move_xy[MovDir[x][y]].x;
3874 yy = y + move_xy[MovDir[x][y]].y;
3877 if (FrameCounter < 1 && x == 0 && y == 29)
3878 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3879 xx, yy, Feld[xx][yy],
3884 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3885 MovDir[x][y] = old_move_dir;
3887 if (!IS_FREE(xx, yy))
3888 MovDir[x][y] = old_move_dir;
3892 if (FrameCounter < 1 && x == 0 && y == 29)
3893 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3898 else if (element == EL_MOLE)
3900 boolean can_move_on =
3901 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3902 IS_AMOEBOID(Feld[move_x][move_y]) ||
3903 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3906 boolean can_turn_left =
3907 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3908 IS_AMOEBOID(Feld[left_x][left_y])));
3910 boolean can_turn_right =
3911 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3912 IS_AMOEBOID(Feld[right_x][right_y])));
3914 if (can_turn_left && can_turn_right)
3915 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3916 else if (can_turn_left)
3917 MovDir[x][y] = left_dir;
3919 MovDir[x][y] = right_dir;
3922 if (MovDir[x][y] != old_move_dir)
3925 else if (element == EL_BALLOON)
3927 MovDir[x][y] = game.balloon_dir;
3930 else if (element == EL_SPRING)
3932 if (MovDir[x][y] & MV_HORIZONTAL &&
3933 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3934 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3935 MovDir[x][y] = MV_NO_MOVING;
3939 else if (element == EL_ROBOT ||
3940 element == EL_SATELLITE ||
3941 element == EL_PENGUIN)
3943 int attr_x = -1, attr_y = -1;
3954 for (i = 0; i < MAX_PLAYERS; i++)
3956 struct PlayerInfo *player = &stored_player[i];
3957 int jx = player->jx, jy = player->jy;
3959 if (!player->active)
3963 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3971 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3977 if (element == EL_PENGUIN)
3980 static int xy[4][2] =
3988 for (i = 0; i < 4; i++)
3990 int ex = x + xy[i % 4][0];
3991 int ey = y + xy[i % 4][1];
3993 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4002 MovDir[x][y] = MV_NO_MOVING;
4004 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4005 else if (attr_x > x)
4006 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4008 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4009 else if (attr_y > y)
4010 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4012 if (element == EL_ROBOT)
4016 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4017 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4018 Moving2Blocked(x, y, &newx, &newy);
4020 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4021 MovDelay[x][y] = 8 + 8 * !RND(3);
4023 MovDelay[x][y] = 16;
4025 else if (element == EL_PENGUIN)
4031 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4033 boolean first_horiz = RND(2);
4034 int new_move_dir = MovDir[x][y];
4037 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4038 Moving2Blocked(x, y, &newx, &newy);
4040 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4044 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4045 Moving2Blocked(x, y, &newx, &newy);
4047 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4050 MovDir[x][y] = old_move_dir;
4054 else /* (element == EL_SATELLITE) */
4060 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4062 boolean first_horiz = RND(2);
4063 int new_move_dir = MovDir[x][y];
4066 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4067 Moving2Blocked(x, y, &newx, &newy);
4069 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4073 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4074 Moving2Blocked(x, y, &newx, &newy);
4076 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4079 MovDir[x][y] = old_move_dir;
4084 else if (move_pattern == MV_TURNING_LEFT ||
4085 move_pattern == MV_TURNING_RIGHT ||
4086 move_pattern == MV_TURNING_LEFT_RIGHT ||
4087 move_pattern == MV_TURNING_RIGHT_LEFT ||
4088 move_pattern == MV_TURNING_RANDOM ||
4089 move_pattern == MV_ALL_DIRECTIONS)
4091 boolean can_turn_left =
4092 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4093 boolean can_turn_right =
4094 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4096 if (move_pattern == MV_TURNING_LEFT)
4097 MovDir[x][y] = left_dir;
4098 else if (move_pattern == MV_TURNING_RIGHT)
4099 MovDir[x][y] = right_dir;
4100 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4101 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4102 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4103 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4104 else if (move_pattern == MV_TURNING_RANDOM)
4105 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4106 can_turn_right && !can_turn_left ? right_dir :
4107 RND(2) ? left_dir : right_dir);
4108 else if (can_turn_left && can_turn_right)
4109 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4110 else if (can_turn_left)
4111 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4112 else if (can_turn_right)
4113 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4115 MovDir[x][y] = back_dir;
4117 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4119 else if (move_pattern == MV_HORIZONTAL ||
4120 move_pattern == MV_VERTICAL)
4122 if (move_pattern & old_move_dir)
4123 MovDir[x][y] = back_dir;
4124 else if (move_pattern == MV_HORIZONTAL)
4125 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4126 else if (move_pattern == MV_VERTICAL)
4127 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4129 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4131 else if (move_pattern & MV_ANY_DIRECTION)
4133 MovDir[x][y] = move_pattern;
4134 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4136 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4138 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4139 MovDir[x][y] = left_dir;
4140 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4141 MovDir[x][y] = right_dir;
4143 if (MovDir[x][y] != old_move_dir)
4144 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4146 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4148 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4149 MovDir[x][y] = right_dir;
4150 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4151 MovDir[x][y] = left_dir;
4153 if (MovDir[x][y] != old_move_dir)
4154 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4156 else if (move_pattern == MV_TOWARDS_PLAYER ||
4157 move_pattern == MV_AWAY_FROM_PLAYER)
4159 int attr_x = -1, attr_y = -1;
4161 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4172 for (i = 0; i < MAX_PLAYERS; i++)
4174 struct PlayerInfo *player = &stored_player[i];
4175 int jx = player->jx, jy = player->jy;
4177 if (!player->active)
4181 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4189 MovDir[x][y] = MV_NO_MOVING;
4191 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4192 else if (attr_x > x)
4193 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4195 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4196 else if (attr_y > y)
4197 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4199 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4201 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4203 boolean first_horiz = RND(2);
4204 int new_move_dir = MovDir[x][y];
4207 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4208 Moving2Blocked(x, y, &newx, &newy);
4210 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4214 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4215 Moving2Blocked(x, y, &newx, &newy);
4217 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4220 MovDir[x][y] = old_move_dir;
4223 else if (move_pattern == MV_WHEN_PUSHED ||
4224 move_pattern == MV_WHEN_DROPPED)
4226 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4227 MovDir[x][y] = MV_NO_MOVING;
4231 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4233 static int test_xy[7][2] =
4243 static int test_dir[7] =
4253 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4254 int move_preference = -1000000; /* start with very low preference */
4255 int new_move_dir = MV_NO_MOVING;
4256 int start_test = RND(4);
4259 for (i = 0; i < 4; i++)
4261 int move_dir = test_dir[start_test + i];
4262 int move_dir_preference;
4264 xx = x + test_xy[start_test + i][0];
4265 yy = y + test_xy[start_test + i][1];
4267 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4268 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4270 new_move_dir = move_dir;
4275 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4278 move_dir_preference = -1 * RunnerVisit[xx][yy];
4279 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4280 move_dir_preference = PlayerVisit[xx][yy];
4282 if (move_dir_preference > move_preference)
4284 /* prefer field that has not been visited for the longest time */
4285 move_preference = move_dir_preference;
4286 new_move_dir = move_dir;
4288 else if (move_dir_preference == move_preference &&
4289 move_dir == old_move_dir)
4291 /* prefer last direction when all directions are preferred equally */
4292 move_preference = move_dir_preference;
4293 new_move_dir = move_dir;
4297 MovDir[x][y] = new_move_dir;
4298 if (old_move_dir != new_move_dir)
4303 static void TurnRound(int x, int y)
4305 int direction = MovDir[x][y];
4308 GfxDir[x][y] = MovDir[x][y];
4314 GfxDir[x][y] = MovDir[x][y];
4317 if (direction != MovDir[x][y])
4322 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4325 GfxAction[x][y] = ACTION_WAITING;
4329 static boolean JustBeingPushed(int x, int y)
4333 for (i = 0; i < MAX_PLAYERS; i++)
4335 struct PlayerInfo *player = &stored_player[i];
4337 if (player->active && player->is_pushing && player->MovPos)
4339 int next_jx = player->jx + (player->jx - player->last_jx);
4340 int next_jy = player->jy + (player->jy - player->last_jy);
4342 if (x == next_jx && y == next_jy)
4350 void StartMoving(int x, int y)
4352 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4353 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4354 int element = Feld[x][y];
4360 if (MovDelay[x][y] == 0)
4361 GfxAction[x][y] = ACTION_DEFAULT;
4363 /* !!! this should be handled more generic (not only for mole) !!! */
4364 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4365 GfxAction[x][y] = ACTION_DEFAULT;
4368 if (CAN_FALL(element) && y < lev_fieldy - 1)
4370 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4371 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4372 if (JustBeingPushed(x, y))
4375 if (element == EL_QUICKSAND_FULL)
4377 if (IS_FREE(x, y + 1))
4379 InitMovingField(x, y, MV_DOWN);
4380 started_moving = TRUE;
4382 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4383 Store[x][y] = EL_ROCK;
4385 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4387 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4390 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4392 if (!MovDelay[x][y])
4393 MovDelay[x][y] = TILEY + 1;
4402 Feld[x][y] = EL_QUICKSAND_EMPTY;
4403 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4404 Store[x][y + 1] = Store[x][y];
4407 PlayLevelSoundAction(x, y, ACTION_FILLING);
4409 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4413 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4414 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4416 InitMovingField(x, y, MV_DOWN);
4417 started_moving = TRUE;
4419 Feld[x][y] = EL_QUICKSAND_FILLING;
4420 Store[x][y] = element;
4422 PlayLevelSoundAction(x, y, ACTION_FILLING);
4424 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4427 else if (element == EL_MAGIC_WALL_FULL)
4429 if (IS_FREE(x, y + 1))
4431 InitMovingField(x, y, MV_DOWN);
4432 started_moving = TRUE;
4434 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4435 Store[x][y] = EL_CHANGED(Store[x][y]);
4437 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4439 if (!MovDelay[x][y])
4440 MovDelay[x][y] = TILEY/4 + 1;
4449 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4450 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4451 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4455 else if (element == EL_BD_MAGIC_WALL_FULL)
4457 if (IS_FREE(x, y + 1))
4459 InitMovingField(x, y, MV_DOWN);
4460 started_moving = TRUE;
4462 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4463 Store[x][y] = EL_CHANGED2(Store[x][y]);
4465 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4467 if (!MovDelay[x][y])
4468 MovDelay[x][y] = TILEY/4 + 1;
4477 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4478 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4479 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4483 else if (CAN_PASS_MAGIC_WALL(element) &&
4484 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4485 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4487 InitMovingField(x, y, MV_DOWN);
4488 started_moving = TRUE;
4491 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4492 EL_BD_MAGIC_WALL_FILLING);
4493 Store[x][y] = element;
4496 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4498 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4503 InitMovingField(x, y, MV_DOWN);
4504 started_moving = TRUE;
4506 Store[x][y] = EL_ACID;
4508 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4509 GfxAction[x][y + 1] = ACTION_ACTIVE;
4513 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4514 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4515 (Feld[x][y + 1] == EL_BLOCKED)) ||
4516 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4517 CAN_SMASH(element) && WasJustFalling[x][y] &&
4518 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4522 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4523 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4524 WasJustMoving[x][y] && !Pushed[x][y + 1])
4526 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4527 WasJustMoving[x][y])
4532 /* this is needed for a special case not covered by calling "Impact()"
4533 from "ContinueMoving()": if an element moves to a tile directly below
4534 another element which was just falling on that tile (which was empty
4535 in the previous frame), the falling element above would just stop
4536 instead of smashing the element below (in previous version, the above
4537 element was just checked for "moving" instead of "falling", resulting
4538 in incorrect smashes caused by horizontal movement of the above
4539 element; also, the case of the player being the element to smash was
4540 simply not covered here... :-/ ) */
4543 WasJustMoving[x][y] = 0;
4544 WasJustFalling[x][y] = 0;
4549 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4551 if (MovDir[x][y] == MV_NO_MOVING)
4553 InitMovingField(x, y, MV_DOWN);
4554 started_moving = TRUE;
4557 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4559 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4560 MovDir[x][y] = MV_DOWN;
4562 InitMovingField(x, y, MV_DOWN);
4563 started_moving = TRUE;
4565 else if (element == EL_AMOEBA_DROP)
4567 Feld[x][y] = EL_AMOEBA_GROWING;
4568 Store[x][y] = EL_AMOEBA_WET;
4570 /* Store[x][y + 1] must be zero, because:
4571 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4574 #if OLD_GAME_BEHAVIOUR
4575 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4577 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4578 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4579 element != EL_DX_SUPABOMB)
4582 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4583 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4584 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4585 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4588 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4589 (IS_FREE(x - 1, y + 1) ||
4590 Feld[x - 1][y + 1] == EL_ACID));
4591 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4592 (IS_FREE(x + 1, y + 1) ||
4593 Feld[x + 1][y + 1] == EL_ACID));
4594 boolean can_fall_any = (can_fall_left || can_fall_right);
4595 boolean can_fall_both = (can_fall_left && can_fall_right);
4597 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4599 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4601 if (slippery_type == SLIPPERY_ONLY_LEFT)
4602 can_fall_right = FALSE;
4603 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4604 can_fall_left = FALSE;
4605 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4606 can_fall_right = FALSE;
4607 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4608 can_fall_left = FALSE;
4610 can_fall_any = (can_fall_left || can_fall_right);
4611 can_fall_both = (can_fall_left && can_fall_right);
4616 if (can_fall_both &&
4617 (game.emulation != EMU_BOULDERDASH &&
4618 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4619 can_fall_left = !(can_fall_right = RND(2));
4621 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4622 started_moving = TRUE;
4625 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4627 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4628 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4629 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4630 int belt_dir = game.belt_dir[belt_nr];
4632 if ((belt_dir == MV_LEFT && left_is_free) ||
4633 (belt_dir == MV_RIGHT && right_is_free))
4635 InitMovingField(x, y, belt_dir);
4636 started_moving = TRUE;
4638 GfxAction[x][y] = ACTION_DEFAULT;
4643 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4644 if (CAN_MOVE(element) && !started_moving)
4646 int move_pattern = element_info[element].move_pattern;
4649 Moving2Blocked(x, y, &newx, &newy);
4652 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4655 if ((element == EL_SATELLITE ||
4656 element == EL_BALLOON ||
4657 element == EL_SPRING)
4658 && JustBeingPushed(x, y))
4663 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4664 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4665 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4668 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4669 element, element_info[element].token_name,
4670 WasJustMoving[x][y],
4671 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4672 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4673 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4674 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4678 WasJustMoving[x][y] = 0;
4681 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4684 if (Feld[x][y] != element) /* element has changed */
4686 element = Feld[x][y];
4687 move_pattern = element_info[element].move_pattern;
4689 if (!CAN_MOVE(element))
4693 if (Feld[x][y] != element) /* element has changed */
4701 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4702 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4704 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4706 Moving2Blocked(x, y, &newx, &newy);
4707 if (Feld[newx][newy] == EL_BLOCKED)
4708 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4714 if (FrameCounter < 1 && x == 0 && y == 29)
4715 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4718 if (!MovDelay[x][y]) /* start new movement phase */
4720 /* all objects that can change their move direction after each step
4721 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4723 if (element != EL_YAMYAM &&
4724 element != EL_DARK_YAMYAM &&
4725 element != EL_PACMAN &&
4726 !(move_pattern & MV_ANY_DIRECTION) &&
4727 move_pattern != MV_TURNING_LEFT &&
4728 move_pattern != MV_TURNING_RIGHT &&
4729 move_pattern != MV_TURNING_LEFT_RIGHT &&
4730 move_pattern != MV_TURNING_RIGHT_LEFT &&
4731 move_pattern != MV_TURNING_RANDOM)
4736 if (FrameCounter < 1 && x == 0 && y == 29)
4737 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4740 if (MovDelay[x][y] && (element == EL_BUG ||
4741 element == EL_SPACESHIP ||
4742 element == EL_SP_SNIKSNAK ||
4743 element == EL_SP_ELECTRON ||
4744 element == EL_MOLE))
4745 DrawLevelField(x, y);
4749 if (MovDelay[x][y]) /* wait some time before next movement */
4754 if (element == EL_YAMYAM)
4757 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4758 DrawLevelElementAnimation(x, y, element);
4762 if (MovDelay[x][y]) /* element still has to wait some time */
4765 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4766 ResetGfxAnimation(x, y);
4770 if (GfxAction[x][y] != ACTION_WAITING)
4771 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4773 GfxAction[x][y] = ACTION_WAITING;
4777 if (element == EL_ROBOT ||
4779 element == EL_PACMAN ||
4781 element == EL_YAMYAM ||
4782 element == EL_DARK_YAMYAM)
4785 DrawLevelElementAnimation(x, y, element);
4787 DrawLevelElementAnimationIfNeeded(x, y, element);
4789 PlayLevelSoundAction(x, y, ACTION_WAITING);
4791 else if (element == EL_SP_ELECTRON)
4792 DrawLevelElementAnimationIfNeeded(x, y, element);
4793 else if (element == EL_DRAGON)
4796 int dir = MovDir[x][y];
4797 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4798 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4799 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4800 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4801 dir == MV_UP ? IMG_FLAMES_1_UP :
4802 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4803 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4806 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4809 GfxAction[x][y] = ACTION_ATTACKING;
4811 if (IS_PLAYER(x, y))
4812 DrawPlayerField(x, y);
4814 DrawLevelField(x, y);
4816 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4818 for (i = 1; i <= 3; i++)
4820 int xx = x + i * dx;
4821 int yy = y + i * dy;
4822 int sx = SCREENX(xx);
4823 int sy = SCREENY(yy);
4824 int flame_graphic = graphic + (i - 1);
4826 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4831 int flamed = MovingOrBlocked2Element(xx, yy);
4833 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4836 RemoveMovingField(xx, yy);
4838 Feld[xx][yy] = EL_FLAMES;
4839 if (IN_SCR_FIELD(sx, sy))
4841 DrawLevelFieldCrumbledSand(xx, yy);
4842 DrawGraphic(sx, sy, flame_graphic, frame);
4847 if (Feld[xx][yy] == EL_FLAMES)
4848 Feld[xx][yy] = EL_EMPTY;
4849 DrawLevelField(xx, yy);
4854 if (MovDelay[x][y]) /* element still has to wait some time */
4856 PlayLevelSoundAction(x, y, ACTION_WAITING);
4862 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4863 for all other elements GfxAction will be set by InitMovingField() */
4864 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4865 GfxAction[x][y] = ACTION_MOVING;
4869 /* now make next step */
4871 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4873 if (DONT_COLLIDE_WITH(element) &&
4874 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4875 !PLAYER_ENEMY_PROTECTED(newx, newy))
4878 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4882 /* player killed by element which is deadly when colliding with */
4884 KillHero(PLAYERINFO(newx, newy));
4889 else if ((element == EL_PENGUIN ||
4890 element == EL_ROBOT ||
4891 element == EL_SATELLITE ||
4892 element == EL_BALLOON ||
4893 IS_CUSTOM_ELEMENT(element)) &&
4894 IN_LEV_FIELD(newx, newy) &&
4895 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4898 Store[x][y] = EL_ACID;
4900 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4902 if (Feld[newx][newy] == EL_EXIT_OPEN)
4906 DrawLevelField(x, y);
4908 Feld[x][y] = EL_EMPTY;
4909 DrawLevelField(x, y);
4912 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4913 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4914 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4916 local_player->friends_still_needed--;
4917 if (!local_player->friends_still_needed &&
4918 !local_player->GameOver && AllPlayersGone)
4919 local_player->LevelSolved = local_player->GameOver = TRUE;
4923 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4925 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4926 DrawLevelField(newx, newy);
4928 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4930 else if (!IS_FREE(newx, newy))
4932 GfxAction[x][y] = ACTION_WAITING;
4934 if (IS_PLAYER(x, y))
4935 DrawPlayerField(x, y);
4937 DrawLevelField(x, y);
4942 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4944 if (IS_FOOD_PIG(Feld[newx][newy]))
4946 if (IS_MOVING(newx, newy))
4947 RemoveMovingField(newx, newy);
4950 Feld[newx][newy] = EL_EMPTY;
4951 DrawLevelField(newx, newy);
4954 PlayLevelSound(x, y, SND_PIG_DIGGING);
4956 else if (!IS_FREE(newx, newy))
4958 if (IS_PLAYER(x, y))
4959 DrawPlayerField(x, y);
4961 DrawLevelField(x, y);
4970 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4973 else if (IS_CUSTOM_ELEMENT(element) &&
4974 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4978 !IS_FREE(newx, newy)
4983 int new_element = Feld[newx][newy];
4986 printf("::: '%s' digs '%s' [%d]\n",
4987 element_info[element].token_name,
4988 element_info[Feld[newx][newy]].token_name,
4989 StorePlayer[newx][newy]);
4992 if (!IS_FREE(newx, newy))
4994 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4995 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4998 /* no element can dig solid indestructible elements */
4999 if (IS_INDESTRUCTIBLE(new_element) &&
5000 !IS_DIGGABLE(new_element) &&
5001 !IS_COLLECTIBLE(new_element))
5004 if (AmoebaNr[newx][newy] &&
5005 (new_element == EL_AMOEBA_FULL ||
5006 new_element == EL_BD_AMOEBA ||
5007 new_element == EL_AMOEBA_GROWING))
5009 AmoebaCnt[AmoebaNr[newx][newy]]--;
5010 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5013 if (IS_MOVING(newx, newy))
5014 RemoveMovingField(newx, newy);
5017 RemoveField(newx, newy);
5018 DrawLevelField(newx, newy);
5021 PlayLevelSoundAction(x, y, action);
5024 if (new_element == element_info[element].move_enter_element)
5025 element_info[element].can_leave_element = TRUE;
5027 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5029 RunnerVisit[x][y] = FrameCounter;
5030 PlayerVisit[x][y] /= 8; /* expire player visit path */
5036 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5038 if (!IS_FREE(newx, newy))
5040 if (IS_PLAYER(x, y))
5041 DrawPlayerField(x, y);
5043 DrawLevelField(x, y);
5049 boolean wanna_flame = !RND(10);
5050 int dx = newx - x, dy = newy - y;
5051 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5052 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5053 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5054 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5055 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5056 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5059 IS_CLASSIC_ENEMY(element1) ||
5060 IS_CLASSIC_ENEMY(element2)) &&
5061 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5062 element1 != EL_FLAMES && element2 != EL_FLAMES)
5065 ResetGfxAnimation(x, y);
5066 GfxAction[x][y] = ACTION_ATTACKING;
5069 if (IS_PLAYER(x, y))
5070 DrawPlayerField(x, y);
5072 DrawLevelField(x, y);
5074 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5076 MovDelay[x][y] = 50;
5078 Feld[newx][newy] = EL_FLAMES;
5079 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5080 Feld[newx1][newy1] = EL_FLAMES;
5081 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5082 Feld[newx2][newy2] = EL_FLAMES;
5088 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5089 Feld[newx][newy] == EL_DIAMOND)
5091 if (IS_MOVING(newx, newy))
5092 RemoveMovingField(newx, newy);
5095 Feld[newx][newy] = EL_EMPTY;
5096 DrawLevelField(newx, newy);
5099 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5101 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5102 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5104 if (AmoebaNr[newx][newy])
5106 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5107 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5108 Feld[newx][newy] == EL_BD_AMOEBA)
5109 AmoebaCnt[AmoebaNr[newx][newy]]--;
5112 if (IS_MOVING(newx, newy))
5113 RemoveMovingField(newx, newy);
5116 Feld[newx][newy] = EL_EMPTY;
5117 DrawLevelField(newx, newy);
5120 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5122 else if ((element == EL_PACMAN || element == EL_MOLE)
5123 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5125 if (AmoebaNr[newx][newy])
5127 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5128 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5129 Feld[newx][newy] == EL_BD_AMOEBA)
5130 AmoebaCnt[AmoebaNr[newx][newy]]--;
5133 if (element == EL_MOLE)
5135 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5136 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5138 ResetGfxAnimation(x, y);
5139 GfxAction[x][y] = ACTION_DIGGING;
5140 DrawLevelField(x, y);
5142 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5144 return; /* wait for shrinking amoeba */
5146 else /* element == EL_PACMAN */
5148 Feld[newx][newy] = EL_EMPTY;
5149 DrawLevelField(newx, newy);
5150 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5153 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5154 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5155 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5157 /* wait for shrinking amoeba to completely disappear */
5160 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5162 /* object was running against a wall */
5167 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5168 DrawLevelElementAnimation(x, y, element);
5170 if (element == EL_BUG ||
5171 element == EL_SPACESHIP ||
5172 element == EL_SP_SNIKSNAK)
5173 DrawLevelField(x, y);
5174 else if (element == EL_MOLE)
5175 DrawLevelField(x, y);
5176 else if (element == EL_BD_BUTTERFLY ||
5177 element == EL_BD_FIREFLY)
5178 DrawLevelElementAnimationIfNeeded(x, y, element);
5179 else if (element == EL_SATELLITE)
5180 DrawLevelElementAnimationIfNeeded(x, y, element);
5181 else if (element == EL_SP_ELECTRON)
5182 DrawLevelElementAnimationIfNeeded(x, y, element);
5185 if (DONT_TOUCH(element))
5186 TestIfBadThingTouchesHero(x, y);
5189 PlayLevelSoundAction(x, y, ACTION_WAITING);
5195 InitMovingField(x, y, MovDir[x][y]);
5197 PlayLevelSoundAction(x, y, ACTION_MOVING);
5201 ContinueMoving(x, y);
5204 void ContinueMoving(int x, int y)
5206 int element = Feld[x][y];
5207 struct ElementInfo *ei = &element_info[element];
5208 int direction = MovDir[x][y];
5209 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5210 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5211 int newx = x + dx, newy = y + dy;
5213 int nextx = newx + dx, nexty = newy + dy;
5215 boolean pushed = Pushed[x][y];
5217 MovPos[x][y] += getElementMoveStepsize(x, y);
5219 if (pushed) /* special case: moving object pushed by player */
5220 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5222 if (ABS(MovPos[x][y]) < TILEX)
5224 DrawLevelField(x, y);
5226 return; /* element is still moving */
5229 /* element reached destination field */
5231 Feld[x][y] = EL_EMPTY;
5232 Feld[newx][newy] = element;
5233 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5235 if (element == EL_MOLE)
5237 Feld[x][y] = EL_SAND;
5239 DrawLevelFieldCrumbledSandNeighbours(x, y);
5241 else if (element == EL_QUICKSAND_FILLING)
5243 element = Feld[newx][newy] = get_next_element(element);
5244 Store[newx][newy] = Store[x][y];
5246 else if (element == EL_QUICKSAND_EMPTYING)
5248 Feld[x][y] = get_next_element(element);
5249 element = Feld[newx][newy] = Store[x][y];
5251 else if (element == EL_MAGIC_WALL_FILLING)
5253 element = Feld[newx][newy] = get_next_element(element);
5254 if (!game.magic_wall_active)
5255 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5256 Store[newx][newy] = Store[x][y];
5258 else if (element == EL_MAGIC_WALL_EMPTYING)
5260 Feld[x][y] = get_next_element(element);
5261 if (!game.magic_wall_active)
5262 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5263 element = Feld[newx][newy] = Store[x][y];
5265 else if (element == EL_BD_MAGIC_WALL_FILLING)
5267 element = Feld[newx][newy] = get_next_element(element);
5268 if (!game.magic_wall_active)
5269 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5270 Store[newx][newy] = Store[x][y];
5272 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5274 Feld[x][y] = get_next_element(element);
5275 if (!game.magic_wall_active)
5276 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5277 element = Feld[newx][newy] = Store[x][y];
5279 else if (element == EL_AMOEBA_DROPPING)
5281 Feld[x][y] = get_next_element(element);
5282 element = Feld[newx][newy] = Store[x][y];
5284 else if (element == EL_SOKOBAN_OBJECT)
5287 Feld[x][y] = Back[x][y];
5289 if (Back[newx][newy])
5290 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5292 Back[x][y] = Back[newx][newy] = 0;
5294 else if (Store[x][y] == EL_ACID)
5296 element = Feld[newx][newy] = EL_ACID;
5300 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5301 MovDelay[newx][newy] = 0;
5303 /* copy element change control values to new field */
5304 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5305 ChangePage[newx][newy] = ChangePage[x][y];
5306 Changed[newx][newy] = Changed[x][y];
5307 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5309 ChangeDelay[x][y] = 0;
5310 ChangePage[x][y] = -1;
5311 Changed[x][y] = CE_BITMASK_DEFAULT;
5312 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5314 /* copy animation control values to new field */
5315 GfxFrame[newx][newy] = GfxFrame[x][y];
5316 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5317 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5318 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5320 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5322 ResetGfxAnimation(x, y); /* reset animation values for old field */
5325 /* some elements can leave other elements behind after moving */
5326 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5327 ei->move_leave_element != EL_EMPTY &&
5328 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5329 ei->can_leave_element_last))
5331 Feld[x][y] = ei->move_leave_element;
5332 InitField(x, y, FALSE);
5334 if (GFX_CRUMBLED(Feld[x][y]))
5335 DrawLevelFieldCrumbledSandNeighbours(x, y);
5338 ei->can_leave_element_last = ei->can_leave_element;
5339 ei->can_leave_element = FALSE;
5343 /* 2.1.1 (does not work correctly for spring) */
5344 if (!CAN_MOVE(element))
5345 MovDir[newx][newy] = 0;
5349 /* (does not work for falling objects that slide horizontally) */
5350 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5351 MovDir[newx][newy] = 0;
5354 if (!CAN_MOVE(element) ||
5355 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5356 MovDir[newx][newy] = 0;
5359 if (!CAN_MOVE(element) ||
5360 (CAN_FALL(element) && direction == MV_DOWN))
5361 GfxDir[x][y] = MovDir[newx][newy] = 0;
5366 DrawLevelField(x, y);
5367 DrawLevelField(newx, newy);
5369 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5371 /* prevent pushed element from moving on in pushed direction */
5372 if (pushed && CAN_MOVE(element) &&
5373 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5374 !(element_info[element].move_pattern & direction))
5375 TurnRound(newx, newy);
5377 if (!pushed) /* special case: moving object pushed by player */
5379 WasJustMoving[newx][newy] = 3;
5381 if (CAN_FALL(element) && direction == MV_DOWN)
5382 WasJustFalling[newx][newy] = 3;
5385 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5387 TestIfBadThingTouchesHero(newx, newy);
5388 TestIfBadThingTouchesFriend(newx, newy);
5390 if (!IS_CUSTOM_ELEMENT(element))
5391 TestIfBadThingTouchesOtherBadThing(newx, newy);
5393 else if (element == EL_PENGUIN)
5394 TestIfFriendTouchesBadThing(newx, newy);
5396 if (CAN_FALL(element) && direction == MV_DOWN &&
5397 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5401 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5405 if (ChangePage[newx][newy] != -1) /* delayed change */
5406 ChangeElement(newx, newy, ChangePage[newx][newy]);
5411 TestIfElementHitsCustomElement(newx, newy, direction);
5415 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5417 int hitting_element = Feld[newx][newy];
5419 /* !!! fix side (direction) orientation here and elsewhere !!! */
5420 CheckElementSideChange(newx, newy, hitting_element,
5421 direction, CE_HITTING_SOMETHING, -1);
5424 if (IN_LEV_FIELD(nextx, nexty))
5426 int opposite_direction = MV_DIR_OPPOSITE(direction);
5427 int hitting_side = direction;
5428 int touched_side = opposite_direction;
5429 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5430 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5431 MovDir[nextx][nexty] != direction ||
5432 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5438 CheckElementSideChange(nextx, nexty, touched_element,
5439 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5441 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5442 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5444 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5446 struct ElementChangeInfo *change =
5447 &element_info[hitting_element].change_page[i];
5449 if (change->can_change &&
5450 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5451 change->sides & touched_side &&
5452 change->trigger_element == touched_element)
5454 CheckElementSideChange(newx, newy, hitting_element,
5455 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5461 if (IS_CUSTOM_ELEMENT(touched_element) &&
5462 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5464 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5466 struct ElementChangeInfo *change =
5467 &element_info[touched_element].change_page[i];
5469 if (change->can_change &&
5470 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5471 change->sides & hitting_side &&
5472 change->trigger_element == hitting_element)
5474 CheckElementSideChange(nextx, nexty, touched_element,
5475 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5486 TestIfPlayerTouchesCustomElement(newx, newy);
5487 TestIfElementTouchesCustomElement(newx, newy);
5490 int AmoebeNachbarNr(int ax, int ay)
5493 int element = Feld[ax][ay];
5495 static int xy[4][2] =
5503 for (i = 0; i < 4; i++)
5505 int x = ax + xy[i][0];
5506 int y = ay + xy[i][1];
5508 if (!IN_LEV_FIELD(x, y))
5511 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5512 group_nr = AmoebaNr[x][y];
5518 void AmoebenVereinigen(int ax, int ay)
5520 int i, x, y, xx, yy;
5521 int new_group_nr = AmoebaNr[ax][ay];
5522 static int xy[4][2] =
5530 if (new_group_nr == 0)
5533 for (i = 0; i < 4; i++)
5538 if (!IN_LEV_FIELD(x, y))
5541 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5542 Feld[x][y] == EL_BD_AMOEBA ||
5543 Feld[x][y] == EL_AMOEBA_DEAD) &&
5544 AmoebaNr[x][y] != new_group_nr)
5546 int old_group_nr = AmoebaNr[x][y];
5548 if (old_group_nr == 0)
5551 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5552 AmoebaCnt[old_group_nr] = 0;
5553 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5554 AmoebaCnt2[old_group_nr] = 0;
5556 for (yy = 0; yy < lev_fieldy; yy++)
5558 for (xx = 0; xx < lev_fieldx; xx++)
5560 if (AmoebaNr[xx][yy] == old_group_nr)
5561 AmoebaNr[xx][yy] = new_group_nr;
5568 void AmoebeUmwandeln(int ax, int ay)
5572 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5574 int group_nr = AmoebaNr[ax][ay];
5579 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5580 printf("AmoebeUmwandeln(): This should never happen!\n");
5585 for (y = 0; y < lev_fieldy; y++)
5587 for (x = 0; x < lev_fieldx; x++)
5589 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5592 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5596 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5597 SND_AMOEBA_TURNING_TO_GEM :
5598 SND_AMOEBA_TURNING_TO_ROCK));
5603 static int xy[4][2] =
5611 for (i = 0; i < 4; i++)
5616 if (!IN_LEV_FIELD(x, y))
5619 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5621 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5622 SND_AMOEBA_TURNING_TO_GEM :
5623 SND_AMOEBA_TURNING_TO_ROCK));
5630 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5633 int group_nr = AmoebaNr[ax][ay];
5634 boolean done = FALSE;
5639 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5640 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5645 for (y = 0; y < lev_fieldy; y++)
5647 for (x = 0; x < lev_fieldx; x++)
5649 if (AmoebaNr[x][y] == group_nr &&
5650 (Feld[x][y] == EL_AMOEBA_DEAD ||
5651 Feld[x][y] == EL_BD_AMOEBA ||
5652 Feld[x][y] == EL_AMOEBA_GROWING))
5655 Feld[x][y] = new_element;
5656 InitField(x, y, FALSE);
5657 DrawLevelField(x, y);
5664 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5665 SND_BD_AMOEBA_TURNING_TO_ROCK :
5666 SND_BD_AMOEBA_TURNING_TO_GEM));
5669 void AmoebeWaechst(int x, int y)
5671 static unsigned long sound_delay = 0;
5672 static unsigned long sound_delay_value = 0;
5674 if (!MovDelay[x][y]) /* start new growing cycle */
5678 if (DelayReached(&sound_delay, sound_delay_value))
5681 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5683 if (Store[x][y] == EL_BD_AMOEBA)
5684 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5686 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5688 sound_delay_value = 30;
5692 if (MovDelay[x][y]) /* wait some time before growing bigger */
5695 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5697 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5698 6 - MovDelay[x][y]);
5700 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5703 if (!MovDelay[x][y])
5705 Feld[x][y] = Store[x][y];
5707 DrawLevelField(x, y);
5712 void AmoebaDisappearing(int x, int y)
5714 static unsigned long sound_delay = 0;
5715 static unsigned long sound_delay_value = 0;
5717 if (!MovDelay[x][y]) /* start new shrinking cycle */
5721 if (DelayReached(&sound_delay, sound_delay_value))
5722 sound_delay_value = 30;
5725 if (MovDelay[x][y]) /* wait some time before shrinking */
5728 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5730 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5731 6 - MovDelay[x][y]);
5733 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5736 if (!MovDelay[x][y])
5738 Feld[x][y] = EL_EMPTY;
5739 DrawLevelField(x, y);
5741 /* don't let mole enter this field in this cycle;
5742 (give priority to objects falling to this field from above) */
5748 void AmoebeAbleger(int ax, int ay)
5751 int element = Feld[ax][ay];
5752 int graphic = el2img(element);
5753 int newax = ax, neway = ay;
5754 static int xy[4][2] =
5762 if (!level.amoeba_speed)
5764 Feld[ax][ay] = EL_AMOEBA_DEAD;
5765 DrawLevelField(ax, ay);
5769 if (IS_ANIMATED(graphic))
5770 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5772 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5773 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5775 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5778 if (MovDelay[ax][ay])
5782 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5785 int x = ax + xy[start][0];
5786 int y = ay + xy[start][1];
5788 if (!IN_LEV_FIELD(x, y))
5791 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5792 if (IS_FREE(x, y) ||
5793 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5799 if (newax == ax && neway == ay)
5802 else /* normal or "filled" (BD style) amoeba */
5805 boolean waiting_for_player = FALSE;
5807 for (i = 0; i < 4; i++)
5809 int j = (start + i) % 4;
5810 int x = ax + xy[j][0];
5811 int y = ay + xy[j][1];
5813 if (!IN_LEV_FIELD(x, y))
5816 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5817 if (IS_FREE(x, y) ||
5818 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5824 else if (IS_PLAYER(x, y))
5825 waiting_for_player = TRUE;
5828 if (newax == ax && neway == ay) /* amoeba cannot grow */
5830 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5832 Feld[ax][ay] = EL_AMOEBA_DEAD;
5833 DrawLevelField(ax, ay);
5834 AmoebaCnt[AmoebaNr[ax][ay]]--;
5836 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5838 if (element == EL_AMOEBA_FULL)
5839 AmoebeUmwandeln(ax, ay);
5840 else if (element == EL_BD_AMOEBA)
5841 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5846 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5848 /* amoeba gets larger by growing in some direction */
5850 int new_group_nr = AmoebaNr[ax][ay];
5853 if (new_group_nr == 0)
5855 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5856 printf("AmoebeAbleger(): This should never happen!\n");
5861 AmoebaNr[newax][neway] = new_group_nr;
5862 AmoebaCnt[new_group_nr]++;
5863 AmoebaCnt2[new_group_nr]++;
5865 /* if amoeba touches other amoeba(s) after growing, unify them */
5866 AmoebenVereinigen(newax, neway);
5868 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5870 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5876 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5877 (neway == lev_fieldy - 1 && newax != ax))
5879 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5880 Store[newax][neway] = element;
5882 else if (neway == ay)
5884 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5886 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5888 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5893 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5894 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5895 Store[ax][ay] = EL_AMOEBA_DROP;
5896 ContinueMoving(ax, ay);
5900 DrawLevelField(newax, neway);
5903 void Life(int ax, int ay)
5906 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5908 int element = Feld[ax][ay];
5909 int graphic = el2img(element);
5910 boolean changed = FALSE;
5912 if (IS_ANIMATED(graphic))
5913 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5918 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5919 MovDelay[ax][ay] = life_time;
5921 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5924 if (MovDelay[ax][ay])
5928 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5930 int xx = ax+x1, yy = ay+y1;
5933 if (!IN_LEV_FIELD(xx, yy))
5936 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5938 int x = xx+x2, y = yy+y2;
5940 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5943 if (((Feld[x][y] == element ||
5944 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5946 (IS_FREE(x, y) && Stop[x][y]))
5950 if (xx == ax && yy == ay) /* field in the middle */
5952 if (nachbarn < life[0] || nachbarn > life[1])
5954 Feld[xx][yy] = EL_EMPTY;
5956 DrawLevelField(xx, yy);
5957 Stop[xx][yy] = TRUE;
5961 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5962 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5963 { /* free border field */
5964 if (nachbarn >= life[2] && nachbarn <= life[3])
5966 Feld[xx][yy] = element;
5967 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5969 DrawLevelField(xx, yy);
5970 Stop[xx][yy] = TRUE;
5977 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5978 SND_GAME_OF_LIFE_GROWING);
5981 static void InitRobotWheel(int x, int y)
5983 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5986 static void RunRobotWheel(int x, int y)
5988 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5991 static void StopRobotWheel(int x, int y)
5993 if (ZX == x && ZY == y)
5997 static void InitTimegateWheel(int x, int y)
5999 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6002 static void RunTimegateWheel(int x, int y)
6004 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6007 void CheckExit(int x, int y)
6009 if (local_player->gems_still_needed > 0 ||
6010 local_player->sokobanfields_still_needed > 0 ||
6011 local_player->lights_still_needed > 0)
6013 int element = Feld[x][y];
6014 int graphic = el2img(element);
6016 if (IS_ANIMATED(graphic))
6017 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6022 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6025 Feld[x][y] = EL_EXIT_OPENING;
6027 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6030 void CheckExitSP(int x, int y)
6032 if (local_player->gems_still_needed > 0)
6034 int element = Feld[x][y];
6035 int graphic = el2img(element);
6037 if (IS_ANIMATED(graphic))
6038 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6043 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6046 Feld[x][y] = EL_SP_EXIT_OPENING;
6048 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6051 static void CloseAllOpenTimegates()
6055 for (y = 0; y < lev_fieldy; y++)
6057 for (x = 0; x < lev_fieldx; x++)
6059 int element = Feld[x][y];
6061 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6063 Feld[x][y] = EL_TIMEGATE_CLOSING;
6065 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6067 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6074 void EdelsteinFunkeln(int x, int y)
6076 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6079 if (Feld[x][y] == EL_BD_DIAMOND)
6082 if (MovDelay[x][y] == 0) /* next animation frame */
6083 MovDelay[x][y] = 11 * !SimpleRND(500);
6085 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6089 if (setup.direct_draw && MovDelay[x][y])
6090 SetDrawtoField(DRAW_BUFFERED);
6092 DrawLevelElementAnimation(x, y, Feld[x][y]);
6094 if (MovDelay[x][y] != 0)
6096 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6097 10 - MovDelay[x][y]);
6099 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6101 if (setup.direct_draw)
6105 dest_x = FX + SCREENX(x) * TILEX;
6106 dest_y = FY + SCREENY(y) * TILEY;
6108 BlitBitmap(drawto_field, window,
6109 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6110 SetDrawtoField(DRAW_DIRECT);
6116 void MauerWaechst(int x, int y)
6120 if (!MovDelay[x][y]) /* next animation frame */
6121 MovDelay[x][y] = 3 * delay;
6123 if (MovDelay[x][y]) /* wait some time before next frame */
6127 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6129 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6130 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6132 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6135 if (!MovDelay[x][y])
6137 if (MovDir[x][y] == MV_LEFT)
6139 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6140 DrawLevelField(x - 1, y);
6142 else if (MovDir[x][y] == MV_RIGHT)
6144 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6145 DrawLevelField(x + 1, y);
6147 else if (MovDir[x][y] == MV_UP)
6149 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6150 DrawLevelField(x, y - 1);
6154 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6155 DrawLevelField(x, y + 1);
6158 Feld[x][y] = Store[x][y];
6160 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6161 DrawLevelField(x, y);
6166 void MauerAbleger(int ax, int ay)
6168 int element = Feld[ax][ay];
6169 int graphic = el2img(element);
6170 boolean oben_frei = FALSE, unten_frei = FALSE;
6171 boolean links_frei = FALSE, rechts_frei = FALSE;
6172 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6173 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6174 boolean new_wall = FALSE;
6176 if (IS_ANIMATED(graphic))
6177 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6179 if (!MovDelay[ax][ay]) /* start building new wall */
6180 MovDelay[ax][ay] = 6;
6182 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6185 if (MovDelay[ax][ay])
6189 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6191 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6193 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6195 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6198 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6199 element == EL_EXPANDABLE_WALL_ANY)
6203 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6204 Store[ax][ay-1] = element;
6205 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6206 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6207 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6208 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6213 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6214 Store[ax][ay+1] = element;
6215 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6216 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6217 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6218 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6223 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6224 element == EL_EXPANDABLE_WALL_ANY ||
6225 element == EL_EXPANDABLE_WALL)
6229 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6230 Store[ax-1][ay] = element;
6231 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6232 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6233 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6234 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6240 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6241 Store[ax+1][ay] = element;
6242 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6243 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6244 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6245 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6250 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6251 DrawLevelField(ax, ay);
6253 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6255 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6256 unten_massiv = TRUE;
6257 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6258 links_massiv = TRUE;
6259 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6260 rechts_massiv = TRUE;
6262 if (((oben_massiv && unten_massiv) ||
6263 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6264 element == EL_EXPANDABLE_WALL) &&
6265 ((links_massiv && rechts_massiv) ||
6266 element == EL_EXPANDABLE_WALL_VERTICAL))
6267 Feld[ax][ay] = EL_WALL;
6271 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6273 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6277 void CheckForDragon(int x, int y)
6280 boolean dragon_found = FALSE;
6281 static int xy[4][2] =
6289 for (i = 0; i < 4; i++)
6291 for (j = 0; j < 4; j++)
6293 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6295 if (IN_LEV_FIELD(xx, yy) &&
6296 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6298 if (Feld[xx][yy] == EL_DRAGON)
6299 dragon_found = TRUE;
6308 for (i = 0; i < 4; i++)
6310 for (j = 0; j < 3; j++)
6312 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6314 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6316 Feld[xx][yy] = EL_EMPTY;
6317 DrawLevelField(xx, yy);
6326 static void InitBuggyBase(int x, int y)
6328 int element = Feld[x][y];
6329 int activating_delay = FRAMES_PER_SECOND / 4;
6332 (element == EL_SP_BUGGY_BASE ?
6333 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6334 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6336 element == EL_SP_BUGGY_BASE_ACTIVE ?
6337 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6340 static void WarnBuggyBase(int x, int y)
6343 static int xy[4][2] =
6351 for (i = 0; i < 4; i++)
6353 int xx = x + xy[i][0], yy = y + xy[i][1];
6355 if (IS_PLAYER(xx, yy))
6357 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6364 static void InitTrap(int x, int y)
6366 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6369 static void ActivateTrap(int x, int y)
6371 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6374 static void ChangeActiveTrap(int x, int y)
6376 int graphic = IMG_TRAP_ACTIVE;
6378 /* if new animation frame was drawn, correct crumbled sand border */
6379 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6380 DrawLevelFieldCrumbledSand(x, y);
6383 static void ChangeElementNowExt(int x, int y, int target_element)
6385 int previous_move_direction = MovDir[x][y];
6387 /* check if element under player changes from accessible to unaccessible
6388 (needed for special case of dropping element which then changes) */
6389 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6390 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6397 Feld[x][y] = target_element;
6399 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6401 ResetGfxAnimation(x, y);
6402 ResetRandomAnimationValue(x, y);
6404 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6405 MovDir[x][y] = previous_move_direction;
6407 InitField(x, y, FALSE);
6408 if (CAN_MOVE(Feld[x][y]))
6411 DrawLevelField(x, y);
6413 if (GFX_CRUMBLED(Feld[x][y]))
6414 DrawLevelFieldCrumbledSandNeighbours(x, y);
6416 TestIfBadThingTouchesHero(x, y);
6417 TestIfPlayerTouchesCustomElement(x, y);
6418 TestIfElementTouchesCustomElement(x, y);
6420 if (ELEM_IS_PLAYER(target_element))
6421 RelocatePlayer(x, y, target_element);
6424 static boolean ChangeElementNow(int x, int y, int element, int page)
6426 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6428 /* always use default change event to prevent running into a loop */
6429 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6430 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6432 /* do not change already changed elements with same change event */
6434 if (Changed[x][y] & ChangeEvent[x][y])
6441 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6443 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6445 if (change->explode)
6452 if (change->use_content)
6454 boolean complete_change = TRUE;
6455 boolean can_change[3][3];
6458 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6460 boolean half_destructible;
6461 int ex = x + xx - 1;
6462 int ey = y + yy - 1;
6465 can_change[xx][yy] = TRUE;
6467 if (ex == x && ey == y) /* do not check changing element itself */
6470 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6472 can_change[xx][yy] = FALSE; /* do not change empty borders */
6477 if (!IN_LEV_FIELD(ex, ey))
6479 can_change[xx][yy] = FALSE;
6480 complete_change = FALSE;
6487 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6488 e = MovingOrBlocked2Element(ex, ey);
6490 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6492 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6493 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6494 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6496 can_change[xx][yy] = FALSE;
6497 complete_change = FALSE;
6501 if (!change->only_complete || complete_change)
6503 boolean something_has_changed = FALSE;
6505 if (change->only_complete && change->use_random_change &&
6506 RND(100) < change->random)
6509 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6511 int ex = x + xx - 1;
6512 int ey = y + yy - 1;
6514 if (can_change[xx][yy] && (!change->use_random_change ||
6515 RND(100) < change->random))
6517 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6518 RemoveMovingField(ex, ey);
6520 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6522 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6524 something_has_changed = TRUE;
6526 /* for symmetry reasons, freeze newly created border elements */
6527 if (ex != x || ey != y)
6528 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6532 if (something_has_changed)
6533 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6538 ChangeElementNowExt(x, y, change->target_element);
6540 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6546 static void ChangeElement(int x, int y, int page)
6548 int element = MovingOrBlocked2Element(x, y);
6549 struct ElementInfo *ei = &element_info[element];
6550 struct ElementChangeInfo *change = &ei->change_page[page];
6554 if (!CAN_CHANGE(element))
6557 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6558 x, y, element, element_info[element].token_name);
6559 printf("ChangeElement(): This should never happen!\n");
6565 if (ChangeDelay[x][y] == 0) /* initialize element change */
6567 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6568 RND(change->delay_random * change->delay_frames)) + 1;
6570 ResetGfxAnimation(x, y);
6571 ResetRandomAnimationValue(x, y);
6573 if (change->pre_change_function)
6574 change->pre_change_function(x, y);
6577 ChangeDelay[x][y]--;
6579 if (ChangeDelay[x][y] != 0) /* continue element change */
6581 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6583 if (IS_ANIMATED(graphic))
6584 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6586 if (change->change_function)
6587 change->change_function(x, y);
6589 else /* finish element change */
6591 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6593 page = ChangePage[x][y];
6594 ChangePage[x][y] = -1;
6598 if (IS_MOVING(x, y) && !change->explode)
6600 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6603 ChangeDelay[x][y] = 1; /* try change after next move step */
6604 ChangePage[x][y] = page; /* remember page to use for change */
6609 if (ChangeElementNow(x, y, element, page))
6611 if (change->post_change_function)
6612 change->post_change_function(x, y);
6617 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6618 int trigger_element,
6624 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6627 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6629 int element = EL_CUSTOM_START + i;
6631 boolean change_element = FALSE;
6634 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6637 for (j = 0; j < element_info[element].num_change_pages; j++)
6639 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6641 if (change->can_change &&
6643 change->events & CH_EVENT_BIT(trigger_event) &&
6645 change->sides & trigger_side &&
6647 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6649 change->trigger_element == trigger_element
6654 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6655 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6656 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6659 change_element = TRUE;
6666 if (!change_element)
6669 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6672 if (x == lx && y == ly) /* do not change trigger element itself */
6676 if (Feld[x][y] == element)
6678 ChangeDelay[x][y] = 1;
6679 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6680 ChangeElement(x, y, page);
6688 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6691 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6695 static boolean CheckElementSideChange(int x, int y, int element, int side,
6696 int trigger_event, int page)
6698 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6701 if (Feld[x][y] == EL_BLOCKED)
6703 Blocked2Moving(x, y, &x, &y);
6704 element = Feld[x][y];
6708 page = element_info[element].event_page_nr[trigger_event];
6710 if (!(element_info[element].change_page[page].sides & side))
6713 ChangeDelay[x][y] = 1;
6714 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6715 ChangeElement(x, y, page);
6720 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6722 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6725 static void PlayPlayerSound(struct PlayerInfo *player)
6727 int jx = player->jx, jy = player->jy;
6728 int element = player->element_nr;
6729 int last_action = player->last_action_waiting;
6730 int action = player->action_waiting;
6732 if (player->is_waiting)
6734 if (action != last_action)
6735 PlayLevelSoundElementAction(jx, jy, element, action);
6737 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6741 if (action != last_action)
6742 StopSound(element_info[element].sound[last_action]);
6744 if (last_action == ACTION_SLEEPING)
6745 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6749 static void PlayAllPlayersSound()
6753 for (i = 0; i < MAX_PLAYERS; i++)
6754 if (stored_player[i].active)
6755 PlayPlayerSound(&stored_player[i]);
6758 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6760 boolean last_waiting = player->is_waiting;
6761 int move_dir = player->MovDir;
6763 player->last_action_waiting = player->action_waiting;
6767 if (!last_waiting) /* not waiting -> waiting */
6769 player->is_waiting = TRUE;
6771 player->frame_counter_bored =
6773 game.player_boring_delay_fixed +
6774 SimpleRND(game.player_boring_delay_random);
6775 player->frame_counter_sleeping =
6777 game.player_sleeping_delay_fixed +
6778 SimpleRND(game.player_sleeping_delay_random);
6780 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6783 if (game.player_sleeping_delay_fixed +
6784 game.player_sleeping_delay_random > 0 &&
6785 player->anim_delay_counter == 0 &&
6786 player->post_delay_counter == 0 &&
6787 FrameCounter >= player->frame_counter_sleeping)
6788 player->is_sleeping = TRUE;
6789 else if (game.player_boring_delay_fixed +
6790 game.player_boring_delay_random > 0 &&
6791 FrameCounter >= player->frame_counter_bored)
6792 player->is_bored = TRUE;
6794 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6795 player->is_bored ? ACTION_BORING :
6798 if (player->is_sleeping)
6800 if (player->num_special_action_sleeping > 0)
6802 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6804 int last_special_action = player->special_action_sleeping;
6805 int num_special_action = player->num_special_action_sleeping;
6806 int special_action =
6807 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6808 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6809 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6810 last_special_action + 1 : ACTION_SLEEPING);
6811 int special_graphic =
6812 el_act_dir2img(player->element_nr, special_action, move_dir);
6814 player->anim_delay_counter =
6815 graphic_info[special_graphic].anim_delay_fixed +
6816 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6817 player->post_delay_counter =
6818 graphic_info[special_graphic].post_delay_fixed +
6819 SimpleRND(graphic_info[special_graphic].post_delay_random);
6821 player->special_action_sleeping = special_action;
6824 if (player->anim_delay_counter > 0)
6826 player->action_waiting = player->special_action_sleeping;
6827 player->anim_delay_counter--;
6829 else if (player->post_delay_counter > 0)
6831 player->post_delay_counter--;
6835 else if (player->is_bored)
6837 if (player->num_special_action_bored > 0)
6839 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6841 int special_action =
6842 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6843 int special_graphic =
6844 el_act_dir2img(player->element_nr, special_action, move_dir);
6846 player->anim_delay_counter =
6847 graphic_info[special_graphic].anim_delay_fixed +
6848 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6849 player->post_delay_counter =
6850 graphic_info[special_graphic].post_delay_fixed +
6851 SimpleRND(graphic_info[special_graphic].post_delay_random);
6853 player->special_action_bored = special_action;
6856 if (player->anim_delay_counter > 0)
6858 player->action_waiting = player->special_action_bored;
6859 player->anim_delay_counter--;
6861 else if (player->post_delay_counter > 0)
6863 player->post_delay_counter--;
6868 else if (last_waiting) /* waiting -> not waiting */
6870 player->is_waiting = FALSE;
6871 player->is_bored = FALSE;
6872 player->is_sleeping = FALSE;
6874 player->frame_counter_bored = -1;
6875 player->frame_counter_sleeping = -1;
6877 player->anim_delay_counter = 0;
6878 player->post_delay_counter = 0;
6880 player->action_waiting = ACTION_DEFAULT;
6882 player->special_action_bored = ACTION_DEFAULT;
6883 player->special_action_sleeping = ACTION_DEFAULT;
6888 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6891 static byte stored_player_action[MAX_PLAYERS];
6892 static int num_stored_actions = 0;
6894 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6895 int left = player_action & JOY_LEFT;
6896 int right = player_action & JOY_RIGHT;
6897 int up = player_action & JOY_UP;
6898 int down = player_action & JOY_DOWN;
6899 int button1 = player_action & JOY_BUTTON_1;
6900 int button2 = player_action & JOY_BUTTON_2;
6901 int dx = (left ? -1 : right ? 1 : 0);
6902 int dy = (up ? -1 : down ? 1 : 0);
6905 stored_player_action[player->index_nr] = 0;
6906 num_stored_actions++;
6910 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6913 if (!player->active || tape.pausing)
6917 printf("::: [%d %d %d %d] [%d %d]\n",
6918 left, right, up, down, button1, button2);
6924 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6928 snapped = SnapField(player, dx, dy);
6932 dropped = DropElement(player);
6934 moved = MovePlayer(player, dx, dy);
6937 if (tape.single_step && tape.recording && !tape.pausing)
6939 if (button1 || (dropped && !moved))
6941 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6942 SnapField(player, 0, 0); /* stop snapping */
6946 SetPlayerWaiting(player, FALSE);
6949 return player_action;
6951 stored_player_action[player->index_nr] = player_action;
6957 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6960 /* no actions for this player (no input at player's configured device) */
6962 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6963 SnapField(player, 0, 0);
6964 CheckGravityMovement(player);
6966 if (player->MovPos == 0)
6967 SetPlayerWaiting(player, TRUE);
6969 if (player->MovPos == 0) /* needed for tape.playing */
6970 player->is_moving = FALSE;
6972 player->is_dropping = FALSE;
6978 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6980 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6982 TapeRecordAction(stored_player_action);
6983 num_stored_actions = 0;
6990 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6992 static byte stored_player_action[MAX_PLAYERS];
6993 static int num_stored_actions = 0;
6994 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6995 int left = player_action & JOY_LEFT;
6996 int right = player_action & JOY_RIGHT;
6997 int up = player_action & JOY_UP;
6998 int down = player_action & JOY_DOWN;
6999 int button1 = player_action & JOY_BUTTON_1;
7000 int button2 = player_action & JOY_BUTTON_2;
7001 int dx = (left ? -1 : right ? 1 : 0);
7002 int dy = (up ? -1 : down ? 1 : 0);
7004 stored_player_action[player->index_nr] = 0;
7005 num_stored_actions++;
7007 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7009 if (!player->active || tape.pausing)
7014 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7017 snapped = SnapField(player, dx, dy);
7021 dropped = DropElement(player);
7023 moved = MovePlayer(player, dx, dy);
7026 if (tape.single_step && tape.recording && !tape.pausing)
7028 if (button1 || (dropped && !moved))
7030 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7031 SnapField(player, 0, 0); /* stop snapping */
7035 stored_player_action[player->index_nr] = player_action;
7039 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7041 /* no actions for this player (no input at player's configured device) */
7043 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
7044 SnapField(player, 0, 0);
7045 CheckGravityMovement(player);
7047 if (player->MovPos == 0)
7048 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7050 if (player->MovPos == 0) /* needed for tape.playing */
7051 player->is_moving = FALSE;
7054 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7056 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7058 TapeRecordAction(stored_player_action);
7059 num_stored_actions = 0;
7066 static unsigned long action_delay = 0;
7067 unsigned long action_delay_value;
7068 int magic_wall_x = 0, magic_wall_y = 0;
7069 int i, x, y, element, graphic;
7070 byte *recorded_player_action;
7071 byte summarized_player_action = 0;
7073 byte tape_action[MAX_PLAYERS];
7076 if (game_status != GAME_MODE_PLAYING)
7079 action_delay_value =
7080 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7082 if (tape.playing && tape.index_search && !tape.pausing)
7083 action_delay_value = 0;
7085 /* ---------- main game synchronization point ---------- */
7087 WaitUntilDelayReached(&action_delay, action_delay_value);
7089 if (network_playing && !network_player_action_received)
7093 printf("DEBUG: try to get network player actions in time\n");
7097 #if defined(PLATFORM_UNIX)
7098 /* last chance to get network player actions without main loop delay */
7102 if (game_status != GAME_MODE_PLAYING)
7105 if (!network_player_action_received)
7109 printf("DEBUG: failed to get network player actions in time\n");
7120 printf("::: getting new tape action [%d]\n", FrameCounter);
7123 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7125 for (i = 0; i < MAX_PLAYERS; i++)
7127 summarized_player_action |= stored_player[i].action;
7129 if (!network_playing)
7130 stored_player[i].effective_action = stored_player[i].action;
7133 #if defined(PLATFORM_UNIX)
7134 if (network_playing)
7135 SendToServer_MovePlayer(summarized_player_action);
7138 if (!options.network && !setup.team_mode)
7139 local_player->effective_action = summarized_player_action;
7141 for (i = 0; i < MAX_PLAYERS; i++)
7143 int actual_player_action = stored_player[i].effective_action;
7145 if (stored_player[i].programmed_action)
7146 actual_player_action = stored_player[i].programmed_action;
7148 if (recorded_player_action)
7149 actual_player_action = recorded_player_action[i];
7151 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7153 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7154 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7156 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7161 TapeRecordAction(tape_action);
7164 network_player_action_received = FALSE;
7166 ScrollScreen(NULL, SCROLL_GO_ON);
7172 for (i = 0; i < MAX_PLAYERS; i++)
7173 stored_player[i].Frame++;
7177 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7179 for (i = 0; i < MAX_PLAYERS; i++)
7181 struct PlayerInfo *player = &stored_player[i];
7185 if (player->active && player->is_pushing && player->is_moving &&
7188 ContinueMoving(x, y);
7190 /* continue moving after pushing (this is actually a bug) */
7191 if (!IS_MOVING(x, y))
7200 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7202 Changed[x][y] = CE_BITMASK_DEFAULT;
7203 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7206 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7208 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7209 printf("GameActions(): This should never happen!\n");
7211 ChangePage[x][y] = -1;
7216 if (WasJustMoving[x][y] > 0)
7217 WasJustMoving[x][y]--;
7218 if (WasJustFalling[x][y] > 0)
7219 WasJustFalling[x][y]--;
7224 /* reset finished pushing action (not done in ContinueMoving() to allow
7225 continous pushing animation for elements with zero push delay) */
7226 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7228 ResetGfxAnimation(x, y);
7229 DrawLevelField(x, y);
7234 if (IS_BLOCKED(x, y))
7238 Blocked2Moving(x, y, &oldx, &oldy);
7239 if (!IS_MOVING(oldx, oldy))
7241 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7242 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7243 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7244 printf("GameActions(): This should never happen!\n");
7250 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7252 element = Feld[x][y];
7254 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7256 graphic = el2img(element);
7262 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7264 element = graphic = 0;
7268 if (graphic_info[graphic].anim_global_sync)
7269 GfxFrame[x][y] = FrameCounter;
7271 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7272 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7273 ResetRandomAnimationValue(x, y);
7275 SetRandomAnimationValue(x, y);
7278 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7281 if (IS_INACTIVE(element))
7283 if (IS_ANIMATED(graphic))
7284 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7290 /* this may take place after moving, so 'element' may have changed */
7292 if (IS_CHANGING(x, y))
7294 if (IS_CHANGING(x, y) &&
7295 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7299 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7300 element_info[element].event_page_nr[CE_DELAY]);
7302 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7305 element = Feld[x][y];
7306 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7310 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7315 element = Feld[x][y];
7316 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7318 if (element == EL_MOLE)
7319 printf("::: %d, %d, %d [%d]\n",
7320 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7324 if (element == EL_YAMYAM)
7325 printf("::: %d, %d, %d\n",
7326 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7330 if (IS_ANIMATED(graphic) &&
7334 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7337 if (element == EL_BUG)
7338 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7342 if (element == EL_MOLE)
7343 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7347 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7348 EdelsteinFunkeln(x, y);
7350 else if ((element == EL_ACID ||
7351 element == EL_EXIT_OPEN ||
7352 element == EL_SP_EXIT_OPEN ||
7353 element == EL_SP_TERMINAL ||
7354 element == EL_SP_TERMINAL_ACTIVE ||
7355 element == EL_EXTRA_TIME ||
7356 element == EL_SHIELD_NORMAL ||
7357 element == EL_SHIELD_DEADLY) &&
7358 IS_ANIMATED(graphic))
7359 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7360 else if (IS_MOVING(x, y))
7361 ContinueMoving(x, y);
7362 else if (IS_ACTIVE_BOMB(element))
7363 CheckDynamite(x, y);
7365 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7366 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7368 else if (element == EL_AMOEBA_GROWING)
7369 AmoebeWaechst(x, y);
7370 else if (element == EL_AMOEBA_SHRINKING)
7371 AmoebaDisappearing(x, y);
7373 #if !USE_NEW_AMOEBA_CODE
7374 else if (IS_AMOEBALIVE(element))
7375 AmoebeAbleger(x, y);
7378 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7380 else if (element == EL_EXIT_CLOSED)
7382 else if (element == EL_SP_EXIT_CLOSED)
7384 else if (element == EL_EXPANDABLE_WALL_GROWING)
7386 else if (element == EL_EXPANDABLE_WALL ||
7387 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7388 element == EL_EXPANDABLE_WALL_VERTICAL ||
7389 element == EL_EXPANDABLE_WALL_ANY)
7391 else if (element == EL_FLAMES)
7392 CheckForDragon(x, y);
7394 else if (IS_AUTO_CHANGING(element))
7395 ChangeElement(x, y);
7397 else if (element == EL_EXPLOSION)
7398 ; /* drawing of correct explosion animation is handled separately */
7399 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7400 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7403 /* this may take place after moving, so 'element' may have changed */
7404 if (IS_AUTO_CHANGING(Feld[x][y]))
7405 ChangeElement(x, y);
7408 if (IS_BELT_ACTIVE(element))
7409 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7411 if (game.magic_wall_active)
7413 int jx = local_player->jx, jy = local_player->jy;
7415 /* play the element sound at the position nearest to the player */
7416 if ((element == EL_MAGIC_WALL_FULL ||
7417 element == EL_MAGIC_WALL_ACTIVE ||
7418 element == EL_MAGIC_WALL_EMPTYING ||
7419 element == EL_BD_MAGIC_WALL_FULL ||
7420 element == EL_BD_MAGIC_WALL_ACTIVE ||
7421 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7422 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7430 #if USE_NEW_AMOEBA_CODE
7431 /* new experimental amoeba growth stuff */
7433 if (!(FrameCounter % 8))
7436 static unsigned long random = 1684108901;
7438 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7441 x = (random >> 10) % lev_fieldx;
7442 y = (random >> 20) % lev_fieldy;
7444 x = RND(lev_fieldx);
7445 y = RND(lev_fieldy);
7447 element = Feld[x][y];
7449 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7450 if (!IS_PLAYER(x,y) &&
7451 (element == EL_EMPTY ||
7452 element == EL_SAND ||
7453 element == EL_QUICKSAND_EMPTY ||
7454 element == EL_ACID_SPLASH_LEFT ||
7455 element == EL_ACID_SPLASH_RIGHT))
7457 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7458 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7459 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7460 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7461 Feld[x][y] = EL_AMOEBA_DROP;
7464 random = random * 129 + 1;
7470 if (game.explosions_delayed)
7473 game.explosions_delayed = FALSE;
7475 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7477 element = Feld[x][y];
7479 if (ExplodeField[x][y])
7480 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7481 else if (element == EL_EXPLOSION)
7482 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7484 ExplodeField[x][y] = EX_NO_EXPLOSION;
7487 game.explosions_delayed = TRUE;
7490 if (game.magic_wall_active)
7492 if (!(game.magic_wall_time_left % 4))
7494 int element = Feld[magic_wall_x][magic_wall_y];
7496 if (element == EL_BD_MAGIC_WALL_FULL ||
7497 element == EL_BD_MAGIC_WALL_ACTIVE ||
7498 element == EL_BD_MAGIC_WALL_EMPTYING)
7499 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7501 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7504 if (game.magic_wall_time_left > 0)
7506 game.magic_wall_time_left--;
7507 if (!game.magic_wall_time_left)
7509 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7511 element = Feld[x][y];
7513 if (element == EL_MAGIC_WALL_ACTIVE ||
7514 element == EL_MAGIC_WALL_FULL)
7516 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7517 DrawLevelField(x, y);
7519 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7520 element == EL_BD_MAGIC_WALL_FULL)
7522 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7523 DrawLevelField(x, y);
7527 game.magic_wall_active = FALSE;
7532 if (game.light_time_left > 0)
7534 game.light_time_left--;
7536 if (game.light_time_left == 0)
7537 RedrawAllLightSwitchesAndInvisibleElements();
7540 if (game.timegate_time_left > 0)
7542 game.timegate_time_left--;
7544 if (game.timegate_time_left == 0)
7545 CloseAllOpenTimegates();
7548 for (i = 0; i < MAX_PLAYERS; i++)
7550 struct PlayerInfo *player = &stored_player[i];
7552 if (SHIELD_ON(player))
7554 if (player->shield_deadly_time_left)
7555 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7556 else if (player->shield_normal_time_left)
7557 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7561 if (TimeFrames >= FRAMES_PER_SECOND)
7566 for (i = 0; i < MAX_PLAYERS; i++)
7568 struct PlayerInfo *player = &stored_player[i];
7570 if (SHIELD_ON(player))
7572 player->shield_normal_time_left--;
7574 if (player->shield_deadly_time_left > 0)
7575 player->shield_deadly_time_left--;
7579 if (tape.recording || tape.playing)
7580 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7586 if (TimeLeft <= 10 && setup.time_limit)
7587 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7589 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7591 if (!TimeLeft && setup.time_limit)
7592 for (i = 0; i < MAX_PLAYERS; i++)
7593 KillHero(&stored_player[i]);
7595 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7596 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7600 PlayAllPlayersSound();
7602 if (options.debug) /* calculate frames per second */
7604 static unsigned long fps_counter = 0;
7605 static int fps_frames = 0;
7606 unsigned long fps_delay_ms = Counter() - fps_counter;
7610 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7612 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7615 fps_counter = Counter();
7618 redraw_mask |= REDRAW_FPS;
7622 if (stored_player[0].jx != stored_player[0].last_jx ||
7623 stored_player[0].jy != stored_player[0].last_jy)
7624 printf("::: %d, %d, %d, %d, %d\n",
7625 stored_player[0].MovDir,
7626 stored_player[0].MovPos,
7627 stored_player[0].GfxPos,
7628 stored_player[0].Frame,
7629 stored_player[0].StepFrame);
7636 for (i = 0; i < MAX_PLAYERS; i++)
7639 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7641 stored_player[i].Frame += move_frames;
7643 if (stored_player[i].MovPos != 0)
7644 stored_player[i].StepFrame += move_frames;
7646 if (stored_player[i].drop_delay > 0)
7647 stored_player[i].drop_delay--;
7652 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7654 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7656 local_player->show_envelope = 0;
7661 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7663 int min_x = x, min_y = y, max_x = x, max_y = y;
7666 for (i = 0; i < MAX_PLAYERS; i++)
7668 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7670 if (!stored_player[i].active || &stored_player[i] == player)
7673 min_x = MIN(min_x, jx);
7674 min_y = MIN(min_y, jy);
7675 max_x = MAX(max_x, jx);
7676 max_y = MAX(max_y, jy);
7679 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7682 static boolean AllPlayersInVisibleScreen()
7686 for (i = 0; i < MAX_PLAYERS; i++)
7688 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7690 if (!stored_player[i].active)
7693 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7700 void ScrollLevel(int dx, int dy)
7702 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7705 BlitBitmap(drawto_field, drawto_field,
7706 FX + TILEX * (dx == -1) - softscroll_offset,
7707 FY + TILEY * (dy == -1) - softscroll_offset,
7708 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7709 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7710 FX + TILEX * (dx == 1) - softscroll_offset,
7711 FY + TILEY * (dy == 1) - softscroll_offset);
7715 x = (dx == 1 ? BX1 : BX2);
7716 for (y = BY1; y <= BY2; y++)
7717 DrawScreenField(x, y);
7722 y = (dy == 1 ? BY1 : BY2);
7723 for (x = BX1; x <= BX2; x++)
7724 DrawScreenField(x, y);
7727 redraw_mask |= REDRAW_FIELD;
7730 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7732 int nextx = x + dx, nexty = y + dy;
7733 int element = Feld[x][y];
7736 element != EL_SP_PORT_LEFT &&
7737 element != EL_SP_GRAVITY_PORT_LEFT &&
7738 element != EL_SP_PORT_HORIZONTAL &&
7739 element != EL_SP_PORT_ANY) ||
7741 element != EL_SP_PORT_RIGHT &&
7742 element != EL_SP_GRAVITY_PORT_RIGHT &&
7743 element != EL_SP_PORT_HORIZONTAL &&
7744 element != EL_SP_PORT_ANY) ||
7746 element != EL_SP_PORT_UP &&
7747 element != EL_SP_GRAVITY_PORT_UP &&
7748 element != EL_SP_PORT_VERTICAL &&
7749 element != EL_SP_PORT_ANY) ||
7751 element != EL_SP_PORT_DOWN &&
7752 element != EL_SP_GRAVITY_PORT_DOWN &&
7753 element != EL_SP_PORT_VERTICAL &&
7754 element != EL_SP_PORT_ANY) ||
7755 !IN_LEV_FIELD(nextx, nexty) ||
7756 !IS_FREE(nextx, nexty))
7762 static void CheckGravityMovement(struct PlayerInfo *player)
7764 if (game.gravity && !player->programmed_action)
7766 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7767 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7769 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7770 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7771 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7772 int jx = player->jx, jy = player->jy;
7773 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7774 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7775 int new_jx = jx + dx, new_jy = jy + dy;
7776 boolean field_under_player_is_free =
7777 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7778 boolean player_is_moving_to_valid_field =
7779 (IN_LEV_FIELD(new_jx, new_jy) &&
7780 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7781 Feld[new_jx][new_jy] == EL_SAND ||
7782 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7783 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7784 /* !!! extend EL_SAND to anything diggable !!! */
7786 if (field_under_player_is_free &&
7787 !player_is_moving_to_valid_field &&
7788 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7789 player->programmed_action = MV_DOWN;
7795 -----------------------------------------------------------------------------
7796 dx, dy: direction (non-diagonal) to try to move the player to
7797 real_dx, real_dy: direction as read from input device (can be diagonal)
7800 boolean MovePlayerOneStep(struct PlayerInfo *player,
7801 int dx, int dy, int real_dx, int real_dy)
7804 static int change_sides[4][2] =
7806 /* enter side leave side */
7807 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7808 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7809 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7810 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7812 int move_direction = (dx == -1 ? MV_LEFT :
7813 dx == +1 ? MV_RIGHT :
7815 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7816 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7817 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7819 int jx = player->jx, jy = player->jy;
7820 int new_jx = jx + dx, new_jy = jy + dy;
7824 if (!player->active || (!dx && !dy))
7825 return MF_NO_ACTION;
7827 player->MovDir = (dx < 0 ? MV_LEFT :
7830 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7832 if (!IN_LEV_FIELD(new_jx, new_jy))
7833 return MF_NO_ACTION;
7835 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7836 return MF_NO_ACTION;
7839 element = MovingOrBlocked2Element(new_jx, new_jy);
7841 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7844 if (DONT_RUN_INTO(element))
7846 if (element == EL_ACID && dx == 0 && dy == 1)
7849 Feld[jx][jy] = EL_PLAYER_1;
7850 InitMovingField(jx, jy, MV_DOWN);
7851 Store[jx][jy] = EL_ACID;
7852 ContinueMoving(jx, jy);
7856 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7861 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7862 if (can_move != MF_MOVING)
7865 /* check if DigField() has caused relocation of the player */
7866 if (player->jx != jx || player->jy != jy)
7867 return MF_NO_ACTION;
7869 StorePlayer[jx][jy] = 0;
7870 player->last_jx = jx;
7871 player->last_jy = jy;
7872 player->jx = new_jx;
7873 player->jy = new_jy;
7874 StorePlayer[new_jx][new_jy] = player->element_nr;
7877 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7879 player->step_counter++;
7881 player->drop_delay = 0;
7883 PlayerVisit[jx][jy] = FrameCounter;
7885 ScrollPlayer(player, SCROLL_INIT);
7888 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7890 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7891 CE_OTHER_GETS_LEFT);
7892 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7893 CE_LEFT_BY_PLAYER, -1);
7896 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7898 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7899 enter_side, CE_OTHER_GETS_ENTERED);
7900 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7901 CE_ENTERED_BY_PLAYER, -1);
7908 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7910 int jx = player->jx, jy = player->jy;
7911 int old_jx = jx, old_jy = jy;
7912 int moved = MF_NO_ACTION;
7915 if (!player->active)
7920 if (player->MovPos == 0)
7922 player->is_moving = FALSE;
7923 player->is_digging = FALSE;
7924 player->is_collecting = FALSE;
7925 player->is_snapping = FALSE;
7926 player->is_pushing = FALSE;
7932 if (!player->active || (!dx && !dy))
7937 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7941 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7942 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7946 /* remove the last programmed player action */
7947 player->programmed_action = 0;
7951 /* should only happen if pre-1.2 tape recordings are played */
7952 /* this is only for backward compatibility */
7954 int original_move_delay_value = player->move_delay_value;
7957 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7961 /* scroll remaining steps with finest movement resolution */
7962 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7964 while (player->MovPos)
7966 ScrollPlayer(player, SCROLL_GO_ON);
7967 ScrollScreen(NULL, SCROLL_GO_ON);
7973 player->move_delay_value = original_move_delay_value;
7976 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7978 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7979 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7983 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7984 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7990 if (moved & MF_MOVING && !ScreenMovPos &&
7991 (player == local_player || !options.network))
7993 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7994 int offset = (setup.scroll_delay ? 3 : 0);
7996 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7998 /* actual player has left the screen -- scroll in that direction */
7999 if (jx != old_jx) /* player has moved horizontally */
8000 scroll_x += (jx - old_jx);
8001 else /* player has moved vertically */
8002 scroll_y += (jy - old_jy);
8006 if (jx != old_jx) /* player has moved horizontally */
8008 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8009 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8010 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8012 /* don't scroll over playfield boundaries */
8013 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8014 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8016 /* don't scroll more than one field at a time */
8017 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8019 /* don't scroll against the player's moving direction */
8020 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8021 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8022 scroll_x = old_scroll_x;
8024 else /* player has moved vertically */
8026 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8027 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8028 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8030 /* don't scroll over playfield boundaries */
8031 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8032 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8034 /* don't scroll more than one field at a time */
8035 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8037 /* don't scroll against the player's moving direction */
8038 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8039 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8040 scroll_y = old_scroll_y;
8044 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8046 if (!options.network && !AllPlayersInVisibleScreen())
8048 scroll_x = old_scroll_x;
8049 scroll_y = old_scroll_y;
8053 ScrollScreen(player, SCROLL_INIT);
8054 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8061 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8063 if (!(moved & MF_MOVING) && !player->is_pushing)
8068 player->StepFrame = 0;
8070 if (moved & MF_MOVING)
8072 if (old_jx != jx && old_jy == jy)
8073 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8074 else if (old_jx == jx && old_jy != jy)
8075 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8077 DrawLevelField(jx, jy); /* for "crumbled sand" */
8079 player->last_move_dir = player->MovDir;
8080 player->is_moving = TRUE;
8082 player->is_snapping = FALSE;
8086 player->is_switching = FALSE;
8089 player->is_dropping = FALSE;
8094 static int change_sides[4][2] =
8096 /* enter side leave side */
8097 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8098 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8099 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8100 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8102 int move_direction = player->MovDir;
8103 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8104 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8107 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8109 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8110 leave_side, CE_OTHER_GETS_LEFT);
8111 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8112 leave_side, CE_LEFT_BY_PLAYER, -1);
8115 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8117 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8118 enter_side, CE_OTHER_GETS_ENTERED);
8119 CheckElementSideChange(jx, jy, Feld[jx][jy],
8120 enter_side, CE_ENTERED_BY_PLAYER, -1);
8131 CheckGravityMovement(player);
8134 player->last_move_dir = MV_NO_MOVING;
8136 player->is_moving = FALSE;
8139 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8141 TestIfHeroTouchesBadThing(jx, jy);
8142 TestIfPlayerTouchesCustomElement(jx, jy);
8145 if (!player->active)
8151 void ScrollPlayer(struct PlayerInfo *player, int mode)
8153 int jx = player->jx, jy = player->jy;
8154 int last_jx = player->last_jx, last_jy = player->last_jy;
8155 int move_stepsize = TILEX / player->move_delay_value;
8157 if (!player->active || !player->MovPos)
8160 if (mode == SCROLL_INIT)
8162 player->actual_frame_counter = FrameCounter;
8163 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8165 if (Feld[last_jx][last_jy] == EL_EMPTY)
8166 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8174 else if (!FrameReached(&player->actual_frame_counter, 1))
8177 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8178 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8180 if (!player->block_last_field &&
8181 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8182 Feld[last_jx][last_jy] = EL_EMPTY;
8184 /* before DrawPlayer() to draw correct player graphic for this case */
8185 if (player->MovPos == 0)
8186 CheckGravityMovement(player);
8189 DrawPlayer(player); /* needed here only to cleanup last field */
8192 if (player->MovPos == 0) /* player reached destination field */
8195 if (player->move_delay_reset_counter > 0)
8197 player->move_delay_reset_counter--;
8199 if (player->move_delay_reset_counter == 0)
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 (IS_PASSABLE(Feld[last_jx][last_jy]))
8211 /* continue with normal speed after quickly moving through gate */
8212 HALVE_PLAYER_SPEED(player);
8214 /* be able to make the next move without delay */
8215 player->move_delay = 0;
8219 if (player->block_last_field &&
8220 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8221 Feld[last_jx][last_jy] = EL_EMPTY;
8223 player->last_jx = jx;
8224 player->last_jy = jy;
8226 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8227 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8228 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8230 DrawPlayer(player); /* needed here only to cleanup last field */
8233 if (local_player->friends_still_needed == 0 ||
8234 IS_SP_ELEMENT(Feld[jx][jy]))
8235 player->LevelSolved = player->GameOver = TRUE;
8238 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8240 TestIfHeroTouchesBadThing(jx, jy);
8241 TestIfPlayerTouchesCustomElement(jx, jy);
8243 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8246 if (!player->active)
8250 if (tape.single_step && tape.recording && !tape.pausing &&
8251 !player->programmed_action)
8252 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8256 void ScrollScreen(struct PlayerInfo *player, int mode)
8258 static unsigned long screen_frame_counter = 0;
8260 if (mode == SCROLL_INIT)
8262 /* set scrolling step size according to actual player's moving speed */
8263 ScrollStepSize = TILEX / player->move_delay_value;
8265 screen_frame_counter = FrameCounter;
8266 ScreenMovDir = player->MovDir;
8267 ScreenMovPos = player->MovPos;
8268 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8271 else if (!FrameReached(&screen_frame_counter, 1))
8276 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8277 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8278 redraw_mask |= REDRAW_FIELD;
8281 ScreenMovDir = MV_NO_MOVING;
8284 void TestIfPlayerTouchesCustomElement(int x, int y)
8286 static int xy[4][2] =
8293 static int change_sides[4][2] =
8295 /* center side border side */
8296 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8297 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8298 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8299 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8301 static int touch_dir[4] =
8308 int center_element = Feld[x][y]; /* should always be non-moving! */
8311 for (i = 0; i < 4; i++)
8313 int xx = x + xy[i][0];
8314 int yy = y + xy[i][1];
8315 int center_side = change_sides[i][0];
8316 int border_side = change_sides[i][1];
8319 if (!IN_LEV_FIELD(xx, yy))
8322 if (IS_PLAYER(x, y))
8324 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8325 border_element = Feld[xx][yy]; /* may be moving! */
8326 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8327 border_element = Feld[xx][yy];
8328 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8329 border_element = MovingOrBlocked2Element(xx, yy);
8331 continue; /* center and border element do not touch */
8333 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8334 CE_OTHER_GETS_TOUCHED);
8335 CheckElementSideChange(xx, yy, border_element, border_side,
8336 CE_TOUCHED_BY_PLAYER, -1);
8338 else if (IS_PLAYER(xx, yy))
8340 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8342 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8344 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8345 continue; /* center and border element do not touch */
8348 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8349 CE_OTHER_GETS_TOUCHED);
8350 CheckElementSideChange(x, y, center_element, center_side,
8351 CE_TOUCHED_BY_PLAYER, -1);
8358 void TestIfElementTouchesCustomElement(int x, int y)
8360 static int xy[4][2] =
8367 static int change_sides[4][2] =
8369 /* center side border side */
8370 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8371 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8372 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8373 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8375 static int touch_dir[4] =
8382 boolean change_center_element = FALSE;
8383 int center_element_change_page = 0;
8384 int center_element = Feld[x][y]; /* should always be non-moving! */
8387 for (i = 0; i < 4; i++)
8389 int xx = x + xy[i][0];
8390 int yy = y + xy[i][1];
8391 int center_side = change_sides[i][0];
8392 int border_side = change_sides[i][1];
8395 if (!IN_LEV_FIELD(xx, yy))
8398 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8399 border_element = Feld[xx][yy]; /* may be moving! */
8400 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8401 border_element = Feld[xx][yy];
8402 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8403 border_element = MovingOrBlocked2Element(xx, yy);
8405 continue; /* center and border element do not touch */
8407 /* check for change of center element (but change it only once) */
8408 if (IS_CUSTOM_ELEMENT(center_element) &&
8409 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8410 !change_center_element)
8412 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8414 struct ElementChangeInfo *change =
8415 &element_info[center_element].change_page[j];
8417 if (change->can_change &&
8418 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8419 change->sides & border_side &&
8421 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8423 change->trigger_element == border_element
8427 change_center_element = TRUE;
8428 center_element_change_page = j;
8435 /* check for change of border element */
8436 if (IS_CUSTOM_ELEMENT(border_element) &&
8437 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8439 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8441 struct ElementChangeInfo *change =
8442 &element_info[border_element].change_page[j];
8444 if (change->can_change &&
8445 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8446 change->sides & center_side &&
8448 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8450 change->trigger_element == center_element
8454 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8455 CE_OTHER_IS_TOUCHING, j);
8462 if (change_center_element)
8463 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8464 CE_OTHER_IS_TOUCHING, center_element_change_page);
8467 void TestIfElementHitsCustomElement(int x, int y, int direction)
8469 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8470 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8471 int hitx = x + dx, hity = y + dy;
8472 int hitting_element = Feld[x][y];
8474 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8475 !IS_FREE(hitx, hity) &&
8476 (!IS_MOVING(hitx, hity) ||
8477 MovDir[hitx][hity] != direction ||
8478 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8481 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8485 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8489 CheckElementSideChange(x, y, hitting_element,
8490 direction, CE_HITTING_SOMETHING, -1);
8492 if (IN_LEV_FIELD(hitx, hity))
8494 int opposite_direction = MV_DIR_OPPOSITE(direction);
8495 int hitting_side = direction;
8496 int touched_side = opposite_direction;
8497 int touched_element = MovingOrBlocked2Element(hitx, hity);
8499 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8500 MovDir[hitx][hity] != direction ||
8501 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8510 CheckElementSideChange(hitx, hity, touched_element,
8511 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8513 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8514 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8516 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8518 struct ElementChangeInfo *change =
8519 &element_info[hitting_element].change_page[i];
8521 if (change->can_change &&
8522 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8523 change->sides & touched_side &&
8526 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8528 change->trigger_element == touched_element
8532 CheckElementSideChange(x, y, hitting_element,
8533 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8539 if (IS_CUSTOM_ELEMENT(touched_element) &&
8540 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8542 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8544 struct ElementChangeInfo *change =
8545 &element_info[touched_element].change_page[i];
8547 if (change->can_change &&
8548 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8549 change->sides & hitting_side &&
8551 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8553 change->trigger_element == hitting_element
8557 CheckElementSideChange(hitx, hity, touched_element,
8558 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8567 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8569 int i, kill_x = -1, kill_y = -1;
8570 static int test_xy[4][2] =
8577 static int test_dir[4] =
8585 for (i = 0; i < 4; i++)
8587 int test_x, test_y, test_move_dir, test_element;
8589 test_x = good_x + test_xy[i][0];
8590 test_y = good_y + test_xy[i][1];
8591 if (!IN_LEV_FIELD(test_x, test_y))
8595 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8598 test_element = Feld[test_x][test_y];
8600 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8603 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8604 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8606 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8607 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8615 if (kill_x != -1 || kill_y != -1)
8617 if (IS_PLAYER(good_x, good_y))
8619 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8621 if (player->shield_deadly_time_left > 0)
8622 Bang(kill_x, kill_y);
8623 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8627 Bang(good_x, good_y);
8631 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8633 int i, kill_x = -1, kill_y = -1;
8634 int bad_element = Feld[bad_x][bad_y];
8635 static int test_xy[4][2] =
8642 static int touch_dir[4] =
8649 static int test_dir[4] =
8657 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8660 for (i = 0; i < 4; i++)
8662 int test_x, test_y, test_move_dir, test_element;
8664 test_x = bad_x + test_xy[i][0];
8665 test_y = bad_y + test_xy[i][1];
8666 if (!IN_LEV_FIELD(test_x, test_y))
8670 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8672 test_element = Feld[test_x][test_y];
8674 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8675 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8677 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8678 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8680 /* good thing is player or penguin that does not move away */
8681 if (IS_PLAYER(test_x, test_y))
8683 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8685 if (bad_element == EL_ROBOT && player->is_moving)
8686 continue; /* robot does not kill player if he is moving */
8688 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8690 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8691 continue; /* center and border element do not touch */
8698 else if (test_element == EL_PENGUIN)
8707 if (kill_x != -1 || kill_y != -1)
8709 if (IS_PLAYER(kill_x, kill_y))
8711 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8713 if (player->shield_deadly_time_left > 0)
8715 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8719 Bang(kill_x, kill_y);
8723 void TestIfHeroTouchesBadThing(int x, int y)
8725 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8728 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8730 TestIfGoodThingHitsBadThing(x, y, move_dir);
8733 void TestIfBadThingTouchesHero(int x, int y)
8735 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8738 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8740 TestIfBadThingHitsGoodThing(x, y, move_dir);
8743 void TestIfFriendTouchesBadThing(int x, int y)
8745 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8748 void TestIfBadThingTouchesFriend(int x, int y)
8750 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8753 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8755 int i, kill_x = bad_x, kill_y = bad_y;
8756 static int xy[4][2] =
8764 for (i = 0; i < 4; i++)
8768 x = bad_x + xy[i][0];
8769 y = bad_y + xy[i][1];
8770 if (!IN_LEV_FIELD(x, y))
8773 element = Feld[x][y];
8774 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8775 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8783 if (kill_x != bad_x || kill_y != bad_y)
8787 void KillHero(struct PlayerInfo *player)
8789 int jx = player->jx, jy = player->jy;
8791 if (!player->active)
8794 /* remove accessible field at the player's position */
8795 Feld[jx][jy] = EL_EMPTY;
8797 /* deactivate shield (else Bang()/Explode() would not work right) */
8798 player->shield_normal_time_left = 0;
8799 player->shield_deadly_time_left = 0;
8805 static void KillHeroUnlessEnemyProtected(int x, int y)
8807 if (!PLAYER_ENEMY_PROTECTED(x, y))
8808 KillHero(PLAYERINFO(x, y));
8811 static void KillHeroUnlessExplosionProtected(int x, int y)
8813 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8814 KillHero(PLAYERINFO(x, y));
8817 void BuryHero(struct PlayerInfo *player)
8819 int jx = player->jx, jy = player->jy;
8821 if (!player->active)
8825 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8827 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8829 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8831 player->GameOver = TRUE;
8835 void RemoveHero(struct PlayerInfo *player)
8837 int jx = player->jx, jy = player->jy;
8838 int i, found = FALSE;
8840 player->present = FALSE;
8841 player->active = FALSE;
8843 if (!ExplodeField[jx][jy])
8844 StorePlayer[jx][jy] = 0;
8846 for (i = 0; i < MAX_PLAYERS; i++)
8847 if (stored_player[i].active)
8851 AllPlayersGone = TRUE;
8858 =============================================================================
8859 checkDiagonalPushing()
8860 -----------------------------------------------------------------------------
8861 check if diagonal input device direction results in pushing of object
8862 (by checking if the alternative direction is walkable, diggable, ...)
8863 =============================================================================
8866 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8867 int x, int y, int real_dx, int real_dy)
8869 int jx, jy, dx, dy, xx, yy;
8871 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8874 /* diagonal direction: check alternative direction */
8879 xx = jx + (dx == 0 ? real_dx : 0);
8880 yy = jy + (dy == 0 ? real_dy : 0);
8882 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8886 =============================================================================
8888 -----------------------------------------------------------------------------
8889 x, y: field next to player (non-diagonal) to try to dig to
8890 real_dx, real_dy: direction as read from input device (can be diagonal)
8891 =============================================================================
8894 int DigField(struct PlayerInfo *player,
8895 int x, int y, int real_dx, int real_dy, int mode)
8897 static int change_sides[4] =
8899 CH_SIDE_RIGHT, /* moving left */
8900 CH_SIDE_LEFT, /* moving right */
8901 CH_SIDE_BOTTOM, /* moving up */
8902 CH_SIDE_TOP, /* moving down */
8904 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8905 int jx = player->jx, jy = player->jy;
8906 int dx = x - jx, dy = y - jy;
8907 int nextx = x + dx, nexty = y + dy;
8908 int move_direction = (dx == -1 ? MV_LEFT :
8909 dx == +1 ? MV_RIGHT :
8911 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8912 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8915 if (player->MovPos == 0)
8917 player->is_digging = FALSE;
8918 player->is_collecting = FALSE;
8921 if (player->MovPos == 0) /* last pushing move finished */
8922 player->is_pushing = FALSE;
8924 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8926 player->is_switching = FALSE;
8927 player->push_delay = 0;
8929 return MF_NO_ACTION;
8932 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8933 return MF_NO_ACTION;
8936 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8938 if (IS_TUBE(Feld[jx][jy]) ||
8939 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8943 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8944 int tube_leave_directions[][2] =
8946 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8947 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8948 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8949 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8950 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8951 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8952 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8953 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8954 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8955 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8956 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8957 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8960 while (tube_leave_directions[i][0] != tube_element)
8963 if (tube_leave_directions[i][0] == -1) /* should not happen */
8967 if (!(tube_leave_directions[i][1] & move_direction))
8968 return MF_NO_ACTION; /* tube has no opening in this direction */
8971 element = Feld[x][y];
8973 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8974 game.engine_version >= VERSION_IDENT(2,2,0,0))
8975 return MF_NO_ACTION;
8979 case EL_SP_PORT_LEFT:
8980 case EL_SP_PORT_RIGHT:
8982 case EL_SP_PORT_DOWN:
8983 case EL_SP_PORT_HORIZONTAL:
8984 case EL_SP_PORT_VERTICAL:
8985 case EL_SP_PORT_ANY:
8986 case EL_SP_GRAVITY_PORT_LEFT:
8987 case EL_SP_GRAVITY_PORT_RIGHT:
8988 case EL_SP_GRAVITY_PORT_UP:
8989 case EL_SP_GRAVITY_PORT_DOWN:
8991 if (!canEnterSupaplexPort(x, y, dx, dy))
8992 return MF_NO_ACTION;
8995 element != EL_SP_PORT_LEFT &&
8996 element != EL_SP_GRAVITY_PORT_LEFT &&
8997 element != EL_SP_PORT_HORIZONTAL &&
8998 element != EL_SP_PORT_ANY) ||
9000 element != EL_SP_PORT_RIGHT &&
9001 element != EL_SP_GRAVITY_PORT_RIGHT &&
9002 element != EL_SP_PORT_HORIZONTAL &&
9003 element != EL_SP_PORT_ANY) ||
9005 element != EL_SP_PORT_UP &&
9006 element != EL_SP_GRAVITY_PORT_UP &&
9007 element != EL_SP_PORT_VERTICAL &&
9008 element != EL_SP_PORT_ANY) ||
9010 element != EL_SP_PORT_DOWN &&
9011 element != EL_SP_GRAVITY_PORT_DOWN &&
9012 element != EL_SP_PORT_VERTICAL &&
9013 element != EL_SP_PORT_ANY) ||
9014 !IN_LEV_FIELD(nextx, nexty) ||
9015 !IS_FREE(nextx, nexty))
9016 return MF_NO_ACTION;
9019 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9020 element == EL_SP_GRAVITY_PORT_RIGHT ||
9021 element == EL_SP_GRAVITY_PORT_UP ||
9022 element == EL_SP_GRAVITY_PORT_DOWN)
9023 game.gravity = !game.gravity;
9025 /* automatically move to the next field with double speed */
9026 player->programmed_action = move_direction;
9028 if (player->move_delay_reset_counter == 0)
9030 player->move_delay_reset_counter = 2; /* two double speed steps */
9032 DOUBLE_PLAYER_SPEED(player);
9035 player->move_delay_reset_counter = 2;
9037 DOUBLE_PLAYER_SPEED(player);
9040 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9044 case EL_TUBE_VERTICAL:
9045 case EL_TUBE_HORIZONTAL:
9046 case EL_TUBE_VERTICAL_LEFT:
9047 case EL_TUBE_VERTICAL_RIGHT:
9048 case EL_TUBE_HORIZONTAL_UP:
9049 case EL_TUBE_HORIZONTAL_DOWN:
9050 case EL_TUBE_LEFT_UP:
9051 case EL_TUBE_LEFT_DOWN:
9052 case EL_TUBE_RIGHT_UP:
9053 case EL_TUBE_RIGHT_DOWN:
9056 int tube_enter_directions[][2] =
9058 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9059 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9060 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9061 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9062 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9063 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9064 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9065 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9066 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9067 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9068 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9069 { -1, MV_NO_MOVING }
9072 while (tube_enter_directions[i][0] != element)
9075 if (tube_enter_directions[i][0] == -1) /* should not happen */
9079 if (!(tube_enter_directions[i][1] & move_direction))
9080 return MF_NO_ACTION; /* tube has no opening in this direction */
9082 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9088 if (IS_WALKABLE(element))
9090 int sound_action = ACTION_WALKING;
9092 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9094 if (!player->key[element - EL_GATE_1])
9095 return MF_NO_ACTION;
9097 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9099 if (!player->key[element - EL_GATE_1_GRAY])
9100 return MF_NO_ACTION;
9102 else if (element == EL_EXIT_OPEN ||
9103 element == EL_SP_EXIT_OPEN ||
9104 element == EL_SP_EXIT_OPENING)
9106 sound_action = ACTION_PASSING; /* player is passing exit */
9108 else if (element == EL_EMPTY)
9110 sound_action = ACTION_MOVING; /* nothing to walk on */
9113 /* play sound from background or player, whatever is available */
9114 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9115 PlayLevelSoundElementAction(x, y, element, sound_action);
9117 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9121 else if (IS_PASSABLE(element))
9123 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9124 return MF_NO_ACTION;
9127 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9128 return MF_NO_ACTION;
9131 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9133 if (!player->key[element - EL_EM_GATE_1])
9134 return MF_NO_ACTION;
9136 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9138 if (!player->key[element - EL_EM_GATE_1_GRAY])
9139 return MF_NO_ACTION;
9142 /* automatically move to the next field with double speed */
9143 player->programmed_action = move_direction;
9145 if (player->move_delay_reset_counter == 0)
9147 player->move_delay_reset_counter = 2; /* two double speed steps */
9149 DOUBLE_PLAYER_SPEED(player);
9152 player->move_delay_reset_counter = 2;
9154 DOUBLE_PLAYER_SPEED(player);
9157 PlayLevelSoundAction(x, y, ACTION_PASSING);
9161 else if (IS_DIGGABLE(element))
9165 if (mode != DF_SNAP)
9168 GfxElement[x][y] = GFX_ELEMENT(element);
9171 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9173 player->is_digging = TRUE;
9176 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9178 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9181 if (mode == DF_SNAP)
9182 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9187 else if (IS_COLLECTIBLE(element))
9191 if (mode != DF_SNAP)
9193 GfxElement[x][y] = element;
9194 player->is_collecting = TRUE;
9197 if (element == EL_SPEED_PILL)
9198 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9199 else if (element == EL_EXTRA_TIME && level.time > 0)
9202 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9204 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9206 player->shield_normal_time_left += 10;
9207 if (element == EL_SHIELD_DEADLY)
9208 player->shield_deadly_time_left += 10;
9210 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9212 if (player->inventory_size < MAX_INVENTORY_SIZE)
9213 player->inventory_element[player->inventory_size++] = element;
9215 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9216 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9218 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9220 player->dynabomb_count++;
9221 player->dynabombs_left++;
9223 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9225 player->dynabomb_size++;
9227 else if (element == EL_DYNABOMB_INCREASE_POWER)
9229 player->dynabomb_xl = TRUE;
9231 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9232 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9234 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9235 element - EL_KEY_1 : element - EL_EM_KEY_1);
9237 player->key[key_nr] = TRUE;
9239 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9240 el2edimg(EL_KEY_1 + key_nr));
9241 redraw_mask |= REDRAW_DOOR_1;
9243 else if (IS_ENVELOPE(element))
9246 player->show_envelope = element;
9248 ShowEnvelope(element - EL_ENVELOPE_1);
9251 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9255 for (i = 0; i < element_info[element].collect_count; i++)
9256 if (player->inventory_size < MAX_INVENTORY_SIZE)
9257 player->inventory_element[player->inventory_size++] = element;
9259 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9260 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9262 else if (element_info[element].collect_count > 0)
9264 local_player->gems_still_needed -=
9265 element_info[element].collect_count;
9266 if (local_player->gems_still_needed < 0)
9267 local_player->gems_still_needed = 0;
9269 DrawText(DX_EMERALDS, DY_EMERALDS,
9270 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9273 RaiseScoreElement(element);
9274 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9276 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9279 if (mode == DF_SNAP)
9280 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9285 else if (IS_PUSHABLE(element))
9287 if (mode == DF_SNAP && element != EL_BD_ROCK)
9288 return MF_NO_ACTION;
9290 if (CAN_FALL(element) && dy)
9291 return MF_NO_ACTION;
9293 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9294 !(element == EL_SPRING && use_spring_bug))
9295 return MF_NO_ACTION;
9298 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9299 ((move_direction & MV_VERTICAL &&
9300 ((element_info[element].move_pattern & MV_LEFT &&
9301 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9302 (element_info[element].move_pattern & MV_RIGHT &&
9303 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9304 (move_direction & MV_HORIZONTAL &&
9305 ((element_info[element].move_pattern & MV_UP &&
9306 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9307 (element_info[element].move_pattern & MV_DOWN &&
9308 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9309 return MF_NO_ACTION;
9313 /* do not push elements already moving away faster than player */
9314 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9315 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9316 return MF_NO_ACTION;
9318 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9319 return MF_NO_ACTION;
9323 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9325 if (player->push_delay_value == -1)
9326 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9328 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9330 if (!player->is_pushing)
9331 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9335 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9336 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9337 !player_is_pushing))
9338 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9341 if (!player->is_pushing &&
9342 game.engine_version >= VERSION_IDENT(2,2,0,7))
9343 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9347 printf("::: push delay: %ld [%d, %d] [%d]\n",
9348 player->push_delay_value, FrameCounter, game.engine_version,
9349 player->is_pushing);
9352 player->is_pushing = TRUE;
9354 if (!(IN_LEV_FIELD(nextx, nexty) &&
9355 (IS_FREE(nextx, nexty) ||
9356 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9357 IS_SB_ELEMENT(element)))))
9358 return MF_NO_ACTION;
9360 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9361 return MF_NO_ACTION;
9363 if (player->push_delay == 0) /* new pushing; restart delay */
9364 player->push_delay = FrameCounter;
9366 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9367 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9368 element != EL_SPRING && element != EL_BALLOON)
9370 /* make sure that there is no move delay before next try to push */
9371 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9372 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9374 return MF_NO_ACTION;
9378 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9381 if (IS_SB_ELEMENT(element))
9383 if (element == EL_SOKOBAN_FIELD_FULL)
9385 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9386 local_player->sokobanfields_still_needed++;
9389 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9391 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9392 local_player->sokobanfields_still_needed--;
9395 Feld[x][y] = EL_SOKOBAN_OBJECT;
9397 if (Back[x][y] == Back[nextx][nexty])
9398 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9399 else if (Back[x][y] != 0)
9400 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9403 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9406 if (local_player->sokobanfields_still_needed == 0 &&
9407 game.emulation == EMU_SOKOBAN)
9409 player->LevelSolved = player->GameOver = TRUE;
9410 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9414 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9416 InitMovingField(x, y, move_direction);
9417 GfxAction[x][y] = ACTION_PUSHING;
9419 if (mode == DF_SNAP)
9420 ContinueMoving(x, y);
9422 MovPos[x][y] = (dx != 0 ? dx : dy);
9424 Pushed[x][y] = TRUE;
9425 Pushed[nextx][nexty] = TRUE;
9427 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9428 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9430 player->push_delay_value = -1; /* get new value later */
9432 CheckTriggeredElementSideChange(x, y, element, dig_side,
9433 CE_OTHER_GETS_PUSHED);
9434 CheckElementSideChange(x, y, element, dig_side,
9435 CE_PUSHED_BY_PLAYER, -1);
9439 else if (IS_SWITCHABLE(element))
9441 if (PLAYER_SWITCHING(player, x, y))
9444 player->is_switching = TRUE;
9445 player->switch_x = x;
9446 player->switch_y = y;
9448 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9450 if (element == EL_ROBOT_WHEEL)
9452 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9456 DrawLevelField(x, y);
9458 else if (element == EL_SP_TERMINAL)
9462 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9464 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9466 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9467 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9470 else if (IS_BELT_SWITCH(element))
9472 ToggleBeltSwitch(x, y);
9474 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9475 element == EL_SWITCHGATE_SWITCH_DOWN)
9477 ToggleSwitchgateSwitch(x, y);
9479 else if (element == EL_LIGHT_SWITCH ||
9480 element == EL_LIGHT_SWITCH_ACTIVE)
9482 ToggleLightSwitch(x, y);
9485 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9486 SND_LIGHT_SWITCH_ACTIVATING :
9487 SND_LIGHT_SWITCH_DEACTIVATING);
9490 else if (element == EL_TIMEGATE_SWITCH)
9492 ActivateTimegateSwitch(x, y);
9494 else if (element == EL_BALLOON_SWITCH_LEFT ||
9495 element == EL_BALLOON_SWITCH_RIGHT ||
9496 element == EL_BALLOON_SWITCH_UP ||
9497 element == EL_BALLOON_SWITCH_DOWN ||
9498 element == EL_BALLOON_SWITCH_ANY)
9500 if (element == EL_BALLOON_SWITCH_ANY)
9501 game.balloon_dir = move_direction;
9503 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9504 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9505 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9506 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9509 else if (element == EL_LAMP)
9511 Feld[x][y] = EL_LAMP_ACTIVE;
9512 local_player->lights_still_needed--;
9514 DrawLevelField(x, y);
9516 else if (element == EL_TIME_ORB_FULL)
9518 Feld[x][y] = EL_TIME_ORB_EMPTY;
9520 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9522 DrawLevelField(x, y);
9525 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9533 if (!PLAYER_SWITCHING(player, x, y))
9535 player->is_switching = TRUE;
9536 player->switch_x = x;
9537 player->switch_y = y;
9539 CheckTriggeredElementSideChange(x, y, element, dig_side,
9540 CE_OTHER_IS_SWITCHING);
9541 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9544 CheckTriggeredElementSideChange(x, y, element, dig_side,
9545 CE_OTHER_GETS_PRESSED);
9546 CheckElementSideChange(x, y, element, dig_side,
9547 CE_PRESSED_BY_PLAYER, -1);
9550 return MF_NO_ACTION;
9553 player->push_delay = 0;
9555 if (Feld[x][y] != element) /* really digged/collected something */
9556 player->is_collecting = !player->is_digging;
9561 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9563 int jx = player->jx, jy = player->jy;
9564 int x = jx + dx, y = jy + dy;
9565 int snap_direction = (dx == -1 ? MV_LEFT :
9566 dx == +1 ? MV_RIGHT :
9568 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9570 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9573 if (!player->active || !IN_LEV_FIELD(x, y))
9581 if (player->MovPos == 0)
9582 player->is_pushing = FALSE;
9584 player->is_snapping = FALSE;
9586 if (player->MovPos == 0)
9588 player->is_moving = FALSE;
9589 player->is_digging = FALSE;
9590 player->is_collecting = FALSE;
9596 if (player->is_snapping)
9599 player->MovDir = snap_direction;
9602 if (player->MovPos == 0)
9605 player->is_moving = FALSE;
9606 player->is_digging = FALSE;
9607 player->is_collecting = FALSE;
9610 player->is_dropping = FALSE;
9612 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9615 player->is_snapping = TRUE;
9618 if (player->MovPos == 0)
9621 player->is_moving = FALSE;
9622 player->is_digging = FALSE;
9623 player->is_collecting = FALSE;
9626 DrawLevelField(x, y);
9632 boolean DropElement(struct PlayerInfo *player)
9634 int jx = player->jx, jy = player->jy;
9635 int old_element = Feld[jx][jy];
9638 /* check if player is active, not moving and ready to drop */
9639 if (!player->active || player->MovPos || player->drop_delay > 0)
9642 /* check if player has anything that can be dropped */
9643 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9646 /* check if anything can be dropped at the current position */
9647 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9650 /* collected custom elements can only be dropped on empty fields */
9651 if (player->inventory_size > 0 &&
9652 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9653 && old_element != EL_EMPTY)
9656 if (old_element != EL_EMPTY)
9657 Back[jx][jy] = old_element; /* store old element on this field */
9659 ResetGfxAnimation(jx, jy);
9660 ResetRandomAnimationValue(jx, jy);
9662 if (player->inventory_size > 0)
9664 player->inventory_size--;
9665 new_element = player->inventory_element[player->inventory_size];
9667 if (new_element == EL_DYNAMITE)
9668 new_element = EL_DYNAMITE_ACTIVE;
9669 else if (new_element == EL_SP_DISK_RED)
9670 new_element = EL_SP_DISK_RED_ACTIVE;
9672 Feld[jx][jy] = new_element;
9674 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9675 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9677 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9678 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9680 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9683 /* needed if previous element just changed to "empty" in the last frame */
9684 Changed[jx][jy] = 0; /* allow another change */
9687 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9688 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9690 TestIfElementTouchesCustomElement(jx, jy);
9692 else /* player is dropping a dyna bomb */
9694 player->dynabombs_left--;
9695 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9697 Feld[jx][jy] = new_element;
9699 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9700 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9702 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9709 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9711 InitField(jx, jy, FALSE);
9712 if (CAN_MOVE(Feld[jx][jy]))
9716 new_element = Feld[jx][jy];
9718 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9719 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9721 int move_stepsize = element_info[new_element].move_stepsize;
9722 int direction, dx, dy, nextx, nexty;
9724 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9725 MovDir[jx][jy] = player->MovDir;
9727 direction = MovDir[jx][jy];
9728 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9729 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9733 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9736 WasJustMoving[jx][jy] = 3;
9738 InitMovingField(jx, jy, direction);
9739 ContinueMoving(jx, jy);
9744 Changed[jx][jy] = 0; /* allow another change */
9747 TestIfElementHitsCustomElement(jx, jy, direction);
9749 CheckElementSideChange(jx, jy, new_element,
9750 direction, CE_HITTING_SOMETHING, -1);
9754 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9758 player->drop_delay = 8 + 8 + 8;
9763 player->is_dropping = TRUE;
9769 /* ------------------------------------------------------------------------- */
9770 /* game sound playing functions */
9771 /* ------------------------------------------------------------------------- */
9773 static int *loop_sound_frame = NULL;
9774 static int *loop_sound_volume = NULL;
9776 void InitPlayLevelSound()
9778 int num_sounds = getSoundListSize();
9780 checked_free(loop_sound_frame);
9781 checked_free(loop_sound_volume);
9783 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9784 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9787 static void PlayLevelSound(int x, int y, int nr)
9789 int sx = SCREENX(x), sy = SCREENY(y);
9790 int volume, stereo_position;
9791 int max_distance = 8;
9792 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9794 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9795 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9798 if (!IN_LEV_FIELD(x, y) ||
9799 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9800 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9803 volume = SOUND_MAX_VOLUME;
9805 if (!IN_SCR_FIELD(sx, sy))
9807 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9808 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9810 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9813 stereo_position = (SOUND_MAX_LEFT +
9814 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9815 (SCR_FIELDX + 2 * max_distance));
9817 if (IS_LOOP_SOUND(nr))
9819 /* This assures that quieter loop sounds do not overwrite louder ones,
9820 while restarting sound volume comparison with each new game frame. */
9822 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9825 loop_sound_volume[nr] = volume;
9826 loop_sound_frame[nr] = FrameCounter;
9829 PlaySoundExt(nr, volume, stereo_position, type);
9832 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9834 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9835 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9836 y < LEVELY(BY1) ? LEVELY(BY1) :
9837 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9841 static void PlayLevelSoundAction(int x, int y, int action)
9843 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9846 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9848 int sound_effect = element_info[element].sound[action];
9850 if (sound_effect != SND_UNDEFINED)
9851 PlayLevelSound(x, y, sound_effect);
9854 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9857 int sound_effect = element_info[element].sound[action];
9859 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9860 PlayLevelSound(x, y, sound_effect);
9863 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9865 int sound_effect = element_info[Feld[x][y]].sound[action];
9867 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9868 PlayLevelSound(x, y, sound_effect);
9871 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9873 int sound_effect = element_info[Feld[x][y]].sound[action];
9875 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9876 StopSound(sound_effect);
9879 static void PlayLevelMusic()
9881 if (levelset.music[level_nr] != MUS_UNDEFINED)
9882 PlayMusic(levelset.music[level_nr]); /* from config file */
9884 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9887 void RaiseScore(int value)
9889 local_player->score += value;
9890 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9893 void RaiseScoreElement(int element)
9899 case EL_EMERALD_YELLOW:
9900 case EL_EMERALD_RED:
9901 case EL_EMERALD_PURPLE:
9902 case EL_SP_INFOTRON:
9903 RaiseScore(level.score[SC_EMERALD]);
9906 RaiseScore(level.score[SC_DIAMOND]);
9909 RaiseScore(level.score[SC_CRYSTAL]);
9912 RaiseScore(level.score[SC_PEARL]);
9915 case EL_BD_BUTTERFLY:
9916 case EL_SP_ELECTRON:
9917 RaiseScore(level.score[SC_BUG]);
9921 case EL_SP_SNIKSNAK:
9922 RaiseScore(level.score[SC_SPACESHIP]);
9925 case EL_DARK_YAMYAM:
9926 RaiseScore(level.score[SC_YAMYAM]);
9929 RaiseScore(level.score[SC_ROBOT]);
9932 RaiseScore(level.score[SC_PACMAN]);
9935 RaiseScore(level.score[SC_NUT]);
9938 case EL_SP_DISK_RED:
9939 case EL_DYNABOMB_INCREASE_NUMBER:
9940 case EL_DYNABOMB_INCREASE_SIZE:
9941 case EL_DYNABOMB_INCREASE_POWER:
9942 RaiseScore(level.score[SC_DYNAMITE]);
9944 case EL_SHIELD_NORMAL:
9945 case EL_SHIELD_DEADLY:
9946 RaiseScore(level.score[SC_SHIELD]);
9949 RaiseScore(level.score[SC_TIME_BONUS]);
9955 RaiseScore(level.score[SC_KEY]);
9958 RaiseScore(element_info[element].collect_score);
9963 void RequestQuitGame(boolean ask_if_really_quit)
9965 if (AllPlayersGone ||
9966 !ask_if_really_quit ||
9967 level_editor_test_game ||
9968 Request("Do you really want to quit the game ?",
9969 REQ_ASK | REQ_STAY_CLOSED))
9971 #if defined(PLATFORM_UNIX)
9972 if (options.network)
9973 SendToServer_StopPlaying();
9977 game_status = GAME_MODE_MAIN;
9983 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9988 /* ---------- new game button stuff ---------------------------------------- */
9990 /* graphic position values for game buttons */
9991 #define GAME_BUTTON_XSIZE 30
9992 #define GAME_BUTTON_YSIZE 30
9993 #define GAME_BUTTON_XPOS 5
9994 #define GAME_BUTTON_YPOS 215
9995 #define SOUND_BUTTON_XPOS 5
9996 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9998 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9999 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10000 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10001 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10002 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10003 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10010 } gamebutton_info[NUM_GAME_BUTTONS] =
10013 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10018 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10019 GAME_CTRL_ID_PAUSE,
10023 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10028 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10029 SOUND_CTRL_ID_MUSIC,
10030 "background music on/off"
10033 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10034 SOUND_CTRL_ID_LOOPS,
10035 "sound loops on/off"
10038 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10039 SOUND_CTRL_ID_SIMPLE,
10040 "normal sounds on/off"
10044 void CreateGameButtons()
10048 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10050 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10051 struct GadgetInfo *gi;
10054 unsigned long event_mask;
10055 int gd_xoffset, gd_yoffset;
10056 int gd_x1, gd_x2, gd_y1, gd_y2;
10059 gd_xoffset = gamebutton_info[i].x;
10060 gd_yoffset = gamebutton_info[i].y;
10061 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10062 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10064 if (id == GAME_CTRL_ID_STOP ||
10065 id == GAME_CTRL_ID_PAUSE ||
10066 id == GAME_CTRL_ID_PLAY)
10068 button_type = GD_TYPE_NORMAL_BUTTON;
10070 event_mask = GD_EVENT_RELEASED;
10071 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10072 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10076 button_type = GD_TYPE_CHECK_BUTTON;
10078 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10079 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10080 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10081 event_mask = GD_EVENT_PRESSED;
10082 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10083 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10086 gi = CreateGadget(GDI_CUSTOM_ID, id,
10087 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10088 GDI_X, DX + gd_xoffset,
10089 GDI_Y, DY + gd_yoffset,
10090 GDI_WIDTH, GAME_BUTTON_XSIZE,
10091 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10092 GDI_TYPE, button_type,
10093 GDI_STATE, GD_BUTTON_UNPRESSED,
10094 GDI_CHECKED, checked,
10095 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10096 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10097 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10098 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10099 GDI_EVENT_MASK, event_mask,
10100 GDI_CALLBACK_ACTION, HandleGameButtons,
10104 Error(ERR_EXIT, "cannot create gadget");
10106 game_gadget[id] = gi;
10110 void FreeGameButtons()
10114 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10115 FreeGadget(game_gadget[i]);
10118 static void MapGameButtons()
10122 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10123 MapGadget(game_gadget[i]);
10126 void UnmapGameButtons()
10130 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10131 UnmapGadget(game_gadget[i]);
10134 static void HandleGameButtons(struct GadgetInfo *gi)
10136 int id = gi->custom_id;
10138 if (game_status != GAME_MODE_PLAYING)
10143 case GAME_CTRL_ID_STOP:
10144 RequestQuitGame(TRUE);
10147 case GAME_CTRL_ID_PAUSE:
10148 if (options.network)
10150 #if defined(PLATFORM_UNIX)
10152 SendToServer_ContinuePlaying();
10154 SendToServer_PausePlaying();
10158 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10161 case GAME_CTRL_ID_PLAY:
10164 #if defined(PLATFORM_UNIX)
10165 if (options.network)
10166 SendToServer_ContinuePlaying();
10170 tape.pausing = FALSE;
10171 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10176 case SOUND_CTRL_ID_MUSIC:
10177 if (setup.sound_music)
10179 setup.sound_music = FALSE;
10182 else if (audio.music_available)
10184 setup.sound = setup.sound_music = TRUE;
10186 SetAudioMode(setup.sound);
10192 case SOUND_CTRL_ID_LOOPS:
10193 if (setup.sound_loops)
10194 setup.sound_loops = FALSE;
10195 else if (audio.loops_available)
10197 setup.sound = setup.sound_loops = TRUE;
10198 SetAudioMode(setup.sound);
10202 case SOUND_CTRL_ID_SIMPLE:
10203 if (setup.sound_simple)
10204 setup.sound_simple = FALSE;
10205 else if (audio.sound_available)
10207 setup.sound = setup.sound_simple = TRUE;
10208 SetAudioMode(setup.sound);