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))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
109 !PLAYER_ENEMY_PROTECTED(x, y))))
111 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_X(e, x, y, condition) \
112 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 (CAN_MOVE_INTO_ACID(e) && \
115 Feld[x][y] == EL_ACID) || \
116 (DONT_COLLIDE_WITH(e) && \
118 !PLAYER_ENEMY_PROTECTED(x, y))))
120 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
124 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
125 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
127 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
128 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
130 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
131 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
133 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
135 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
137 Feld[x][y] == EL_DIAMOND))
139 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
141 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
143 #define PACMAN_CAN_ENTER_FIELD(x, y) \
144 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
145 IS_AMOEBOID(Feld[x][y])))
147 #define PIG_CAN_ENTER_FIELD(x, y) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 IS_FOOD_PIG(Feld[x][y])))
151 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
153 IS_FOOD_PENGUIN(Feld[x][y]) || \
154 Feld[x][y] == EL_EXIT_OPEN || \
155 Feld[x][y] == EL_ACID))
157 #define GROUP_NR(e) ((e) - EL_GROUP_START)
158 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
159 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
160 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
162 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
163 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
165 #define CE_ENTER_FIELD_COND(e, x, y) \
166 (!IS_PLAYER(x, y) && \
167 (Feld[x][y] == EL_ACID || \
168 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
170 #define CE_ENTER_FIELD_COND_X(e, x, y) \
171 (!IS_PLAYER(x, y) && \
172 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
174 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_GENERIC_X(e, x, y, CE_ENTER_FIELD_COND_X(e, x, y))
177 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
178 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
180 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
181 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
183 /* game button identifiers */
184 #define GAME_CTRL_ID_STOP 0
185 #define GAME_CTRL_ID_PAUSE 1
186 #define GAME_CTRL_ID_PLAY 2
187 #define SOUND_CTRL_ID_MUSIC 3
188 #define SOUND_CTRL_ID_LOOPS 4
189 #define SOUND_CTRL_ID_SIMPLE 5
191 #define NUM_GAME_BUTTONS 6
194 /* forward declaration for internal use */
196 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
197 static boolean MovePlayer(struct PlayerInfo *, int, int);
198 static void ScrollPlayer(struct PlayerInfo *, int);
199 static void ScrollScreen(struct PlayerInfo *, int);
201 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
203 static void InitBeltMovement(void);
204 static void CloseAllOpenTimegates(void);
205 static void CheckGravityMovement(struct PlayerInfo *);
206 static void KillHeroUnlessEnemyProtected(int, int);
207 static void KillHeroUnlessExplosionProtected(int, int);
209 static void TestIfPlayerTouchesCustomElement(int, int);
210 static void TestIfElementTouchesCustomElement(int, int);
211 static void TestIfElementHitsCustomElement(int, int, int);
213 static void ChangeElement(int, int, int);
214 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
215 static boolean CheckTriggeredElementChange(int, int, int, int);
216 static boolean CheckElementSideChange(int, int, int, int, int, int);
217 static boolean CheckElementChange(int, int, int, int);
219 static void PlayLevelSound(int, int, int);
220 static void PlayLevelSoundNearest(int, int, int);
221 static void PlayLevelSoundAction(int, int, int);
222 static void PlayLevelSoundElementAction(int, int, int, int);
223 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
224 static void PlayLevelSoundActionIfLoop(int, int, int);
225 static void StopLevelSoundActionIfLoop(int, int, int);
226 static void PlayLevelMusic();
228 static void MapGameButtons();
229 static void HandleGameButtons(struct GadgetInfo *);
231 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
234 /* ------------------------------------------------------------------------- */
235 /* definition of elements that automatically change to other elements after */
236 /* a specified time, eventually calling a function when changing */
237 /* ------------------------------------------------------------------------- */
239 /* forward declaration for changer functions */
240 static void InitBuggyBase(int x, int y);
241 static void WarnBuggyBase(int x, int y);
243 static void InitTrap(int x, int y);
244 static void ActivateTrap(int x, int y);
245 static void ChangeActiveTrap(int x, int y);
247 static void InitRobotWheel(int x, int y);
248 static void RunRobotWheel(int x, int y);
249 static void StopRobotWheel(int x, int y);
251 static void InitTimegateWheel(int x, int y);
252 static void RunTimegateWheel(int x, int y);
254 struct ChangingElementInfo
259 void (*pre_change_function)(int x, int y);
260 void (*change_function)(int x, int y);
261 void (*post_change_function)(int x, int y);
264 static struct ChangingElementInfo change_delay_list[] =
315 EL_SWITCHGATE_OPENING,
323 EL_SWITCHGATE_CLOSING,
324 EL_SWITCHGATE_CLOSED,
356 EL_ACID_SPLASH_RIGHT,
365 EL_SP_BUGGY_BASE_ACTIVATING,
372 EL_SP_BUGGY_BASE_ACTIVATING,
373 EL_SP_BUGGY_BASE_ACTIVE,
380 EL_SP_BUGGY_BASE_ACTIVE,
404 EL_ROBOT_WHEEL_ACTIVE,
412 EL_TIMEGATE_SWITCH_ACTIVE,
433 int push_delay_fixed, push_delay_random;
438 { EL_BALLOON, 0, 0 },
440 { EL_SOKOBAN_OBJECT, 2, 0 },
441 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
442 { EL_SATELLITE, 2, 0 },
443 { EL_SP_DISK_YELLOW, 2, 0 },
445 { EL_UNDEFINED, 0, 0 },
453 move_stepsize_list[] =
455 { EL_AMOEBA_DROP, 2 },
456 { EL_AMOEBA_DROPPING, 2 },
457 { EL_QUICKSAND_FILLING, 1 },
458 { EL_QUICKSAND_EMPTYING, 1 },
459 { EL_MAGIC_WALL_FILLING, 2 },
460 { EL_BD_MAGIC_WALL_FILLING, 2 },
461 { EL_MAGIC_WALL_EMPTYING, 2 },
462 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
472 collect_count_list[] =
475 { EL_BD_DIAMOND, 1 },
476 { EL_EMERALD_YELLOW, 1 },
477 { EL_EMERALD_RED, 1 },
478 { EL_EMERALD_PURPLE, 1 },
480 { EL_SP_INFOTRON, 1 },
494 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
495 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
496 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
497 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
498 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
499 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
500 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
501 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
502 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
503 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
504 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
509 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
511 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
512 CH_EVENT_BIT(CE_DELAY))
513 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
514 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
515 IS_JUST_CHANGING(x, y))
517 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
520 void GetPlayerConfig()
522 if (!audio.sound_available)
523 setup.sound_simple = FALSE;
525 if (!audio.loops_available)
526 setup.sound_loops = FALSE;
528 if (!audio.music_available)
529 setup.sound_music = FALSE;
531 if (!video.fullscreen_available)
532 setup.fullscreen = FALSE;
534 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
536 SetAudioMode(setup.sound);
540 static int getBeltNrFromBeltElement(int element)
542 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
543 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
544 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
547 static int getBeltNrFromBeltActiveElement(int element)
549 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
550 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
551 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
554 static int getBeltNrFromBeltSwitchElement(int element)
556 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
557 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
558 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
561 static int getBeltDirNrFromBeltSwitchElement(int element)
563 static int belt_base_element[4] =
565 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
566 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
567 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
568 EL_CONVEYOR_BELT_4_SWITCH_LEFT
571 int belt_nr = getBeltNrFromBeltSwitchElement(element);
572 int belt_dir_nr = element - belt_base_element[belt_nr];
574 return (belt_dir_nr % 3);
577 static int getBeltDirFromBeltSwitchElement(int element)
579 static int belt_move_dir[3] =
586 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
588 return belt_move_dir[belt_dir_nr];
591 static void InitPlayerField(int x, int y, int element, boolean init_game)
593 if (element == EL_SP_MURPHY)
597 if (stored_player[0].present)
599 Feld[x][y] = EL_SP_MURPHY_CLONE;
605 stored_player[0].use_murphy_graphic = TRUE;
608 Feld[x][y] = EL_PLAYER_1;
614 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
615 int jx = player->jx, jy = player->jy;
617 player->present = TRUE;
619 player->block_last_field = (element == EL_SP_MURPHY ?
620 level.sp_block_last_field :
621 level.block_last_field);
623 if (!options.network || player->connected)
625 player->active = TRUE;
627 /* remove potentially duplicate players */
628 if (StorePlayer[jx][jy] == Feld[x][y])
629 StorePlayer[jx][jy] = 0;
631 StorePlayer[x][y] = Feld[x][y];
635 printf("Player %d activated.\n", player->element_nr);
636 printf("[Local player is %d and currently %s.]\n",
637 local_player->element_nr,
638 local_player->active ? "active" : "not active");
642 Feld[x][y] = EL_EMPTY;
643 player->jx = player->last_jx = x;
644 player->jy = player->last_jy = y;
648 static void InitField(int x, int y, boolean init_game)
650 int element = Feld[x][y];
659 InitPlayerField(x, y, element, init_game);
662 case EL_SOKOBAN_FIELD_PLAYER:
663 element = Feld[x][y] = EL_PLAYER_1;
664 InitField(x, y, init_game);
666 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
667 InitField(x, y, init_game);
670 case EL_SOKOBAN_FIELD_EMPTY:
671 local_player->sokobanfields_still_needed++;
675 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
676 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
677 else if (x > 0 && Feld[x-1][y] == EL_ACID)
678 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
679 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
680 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
681 else if (y > 0 && Feld[x][y-1] == EL_ACID)
682 Feld[x][y] = EL_ACID_POOL_BOTTOM;
683 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
684 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
692 case EL_SPACESHIP_RIGHT:
693 case EL_SPACESHIP_UP:
694 case EL_SPACESHIP_LEFT:
695 case EL_SPACESHIP_DOWN:
697 case EL_BD_BUTTERFLY_RIGHT:
698 case EL_BD_BUTTERFLY_UP:
699 case EL_BD_BUTTERFLY_LEFT:
700 case EL_BD_BUTTERFLY_DOWN:
701 case EL_BD_BUTTERFLY:
702 case EL_BD_FIREFLY_RIGHT:
703 case EL_BD_FIREFLY_UP:
704 case EL_BD_FIREFLY_LEFT:
705 case EL_BD_FIREFLY_DOWN:
707 case EL_PACMAN_RIGHT:
731 if (y == lev_fieldy - 1)
733 Feld[x][y] = EL_AMOEBA_GROWING;
734 Store[x][y] = EL_AMOEBA_WET;
738 case EL_DYNAMITE_ACTIVE:
739 case EL_SP_DISK_RED_ACTIVE:
740 case EL_DYNABOMB_PLAYER_1_ACTIVE:
741 case EL_DYNABOMB_PLAYER_2_ACTIVE:
742 case EL_DYNABOMB_PLAYER_3_ACTIVE:
743 case EL_DYNABOMB_PLAYER_4_ACTIVE:
748 local_player->lights_still_needed++;
752 local_player->friends_still_needed++;
757 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
762 Feld[x][y] = EL_EMPTY;
767 case EL_EM_KEY_1_FILE:
768 Feld[x][y] = EL_EM_KEY_1;
770 case EL_EM_KEY_2_FILE:
771 Feld[x][y] = EL_EM_KEY_2;
773 case EL_EM_KEY_3_FILE:
774 Feld[x][y] = EL_EM_KEY_3;
776 case EL_EM_KEY_4_FILE:
777 Feld[x][y] = EL_EM_KEY_4;
781 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
782 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
783 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
784 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
785 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
786 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
787 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
788 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
789 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
790 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
791 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
792 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
795 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
796 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
797 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
799 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
801 game.belt_dir[belt_nr] = belt_dir;
802 game.belt_dir_nr[belt_nr] = belt_dir_nr;
804 else /* more than one switch -- set it like the first switch */
806 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
811 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
813 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
816 case EL_LIGHT_SWITCH_ACTIVE:
818 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
822 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
824 else if (IS_GROUP_ELEMENT(element))
826 struct ElementGroupInfo *group = element_info[element].group;
827 int last_anim_random_frame = gfx.anim_random_frame;
830 if (group->choice_mode == ANIM_RANDOM)
831 gfx.anim_random_frame = RND(group->num_elements_resolved);
833 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
834 group->choice_mode, 0,
837 if (group->choice_mode == ANIM_RANDOM)
838 gfx.anim_random_frame = last_anim_random_frame;
842 Feld[x][y] = group->element_resolved[element_pos];
844 InitField(x, y, init_game);
850 static inline void InitField_WithBug1(int x, int y, boolean init_game)
852 InitField(x, y, init_game);
854 /* not needed to call InitMovDir() -- already done by InitField()! */
855 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
856 CAN_MOVE(Feld[x][y]))
860 static inline void InitField_WithBug2(int x, int y, boolean init_game)
862 int old_element = Feld[x][y];
864 InitField(x, y, init_game);
866 /* not needed to call InitMovDir() -- already done by InitField()! */
867 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
868 CAN_MOVE(old_element) &&
869 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
872 /* this case is in fact a combination of not less than three bugs:
873 first, it calls InitMovDir() for elements that can move, although this is
874 already done by InitField(); then, it checks the element that was at this
875 field _before_ the call to InitField() (which can change it)
880 void DrawGameDoorValues()
884 for (i = 0; i < MAX_PLAYERS; i++)
885 for (j = 0; j < 4; j++)
886 if (stored_player[i].key[j])
887 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
888 el2edimg(EL_KEY_1 + j));
890 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
891 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
892 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
893 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
894 DrawText(DX + XX_SCORE, DY + YY_SCORE,
895 int2str(local_player->score, 5), FONT_TEXT_2);
896 DrawText(DX + XX_TIME, DY + YY_TIME,
897 int2str(TimeLeft, 3), FONT_TEXT_2);
900 static void resolve_group_element(int group_element, int recursion_depth)
903 static struct ElementGroupInfo *group;
904 struct ElementGroupInfo *actual_group = element_info[group_element].group;
907 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
909 Error(ERR_WARN, "recursion too deep when resolving group element %d",
910 group_element - EL_GROUP_START + 1);
912 /* replace element which caused too deep recursion by question mark */
913 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
918 if (recursion_depth == 0) /* initialization */
920 group = element_info[group_element].group;
921 group_nr = group_element - EL_GROUP_START;
923 group->num_elements_resolved = 0;
924 group->choice_pos = 0;
927 for (i = 0; i < actual_group->num_elements; i++)
929 int element = actual_group->element[i];
931 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
934 if (IS_GROUP_ELEMENT(element))
935 resolve_group_element(element, recursion_depth + 1);
938 group->element_resolved[group->num_elements_resolved++] = element;
939 element_info[element].in_group[group_nr] = TRUE;
944 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
946 printf("::: group %d: %d resolved elements\n",
947 group_element - EL_GROUP_START, group->num_elements_resolved);
948 for (i = 0; i < group->num_elements_resolved; i++)
949 printf("::: - %d ['%s']\n", group->element_resolved[i],
950 element_info[group->element_resolved[i]].token_name);
957 =============================================================================
959 -----------------------------------------------------------------------------
960 initialize game engine due to level / tape version number
961 =============================================================================
964 static void InitGameEngine()
968 /* set game engine from tape file when re-playing, else from level file */
969 game.engine_version = (tape.playing ? tape.engine_version :
972 /* dynamically adjust element properties according to game engine version */
973 InitElementPropertiesEngine(game.engine_version);
976 printf("level %d: level version == %06d\n", level_nr, level.game_version);
977 printf(" tape version == %06d [%s] [file: %06d]\n",
978 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
980 printf(" => game.engine_version == %06d\n", game.engine_version);
983 /* ---------- recursively resolve group elements ------------------------- */
985 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
986 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
987 element_info[i].in_group[j] = FALSE;
989 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
990 resolve_group_element(EL_GROUP_START + i, 0);
992 /* ---------- initialize player's initial move delay --------------------- */
994 /* dynamically adjust player properties according to game engine version */
995 game.initial_move_delay =
996 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
997 INITIAL_MOVE_DELAY_OFF);
999 /* dynamically adjust player properties according to level information */
1000 game.initial_move_delay_value =
1001 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1003 /* ---------- initialize player's initial push delay --------------------- */
1005 /* dynamically adjust player properties according to game engine version */
1006 game.initial_push_delay_value =
1007 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1009 /* ---------- initialize changing elements ------------------------------- */
1011 /* initialize changing elements information */
1012 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1014 struct ElementInfo *ei = &element_info[i];
1016 /* this pointer might have been changed in the level editor */
1017 ei->change = &ei->change_page[0];
1019 if (!IS_CUSTOM_ELEMENT(i))
1021 ei->change->target_element = EL_EMPTY_SPACE;
1022 ei->change->delay_fixed = 0;
1023 ei->change->delay_random = 0;
1024 ei->change->delay_frames = 1;
1027 ei->change_events = CE_BITMASK_DEFAULT;
1028 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1030 ei->event_page_nr[j] = 0;
1031 ei->event_page[j] = &ei->change_page[0];
1035 /* add changing elements from pre-defined list */
1036 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1038 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1039 struct ElementInfo *ei = &element_info[ch_delay->element];
1041 ei->change->target_element = ch_delay->target_element;
1042 ei->change->delay_fixed = ch_delay->change_delay;
1044 ei->change->pre_change_function = ch_delay->pre_change_function;
1045 ei->change->change_function = ch_delay->change_function;
1046 ei->change->post_change_function = ch_delay->post_change_function;
1048 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1052 /* add change events from custom element configuration */
1053 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1055 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1057 for (j = 0; j < ei->num_change_pages; j++)
1059 if (!ei->change_page[j].can_change)
1062 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1064 /* only add event page for the first page found with this event */
1065 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1066 !(ei->change_events & CH_EVENT_BIT(k)))
1068 ei->change_events |= CH_EVENT_BIT(k);
1069 ei->event_page_nr[k] = j;
1070 ei->event_page[k] = &ei->change_page[j];
1078 /* add change events from custom element configuration */
1079 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1081 int element = EL_CUSTOM_START + i;
1083 /* only add custom elements that change after fixed/random frame delay */
1084 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1085 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1089 /* ---------- initialize trigger events ---------------------------------- */
1091 /* initialize trigger events information */
1092 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1093 trigger_events[i] = EP_BITMASK_DEFAULT;
1096 /* add trigger events from element change event properties */
1097 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1099 struct ElementInfo *ei = &element_info[i];
1101 for (j = 0; j < ei->num_change_pages; j++)
1103 if (!ei->change_page[j].can_change)
1106 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1108 int trigger_element = ei->change_page[j].trigger_element;
1110 if (IS_GROUP_ELEMENT(trigger_element))
1112 struct ElementGroupInfo *group = element_info[trigger_element].group;
1114 for (k = 0; k < group->num_elements_resolved; k++)
1115 trigger_events[group->element_resolved[k]]
1116 |= ei->change_page[j].events;
1119 trigger_events[trigger_element] |= ei->change_page[j].events;
1124 /* add trigger events from element change event properties */
1125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1126 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1127 trigger_events[element_info[i].change->trigger_element] |=
1128 element_info[i].change->events;
1131 /* ---------- initialize push delay -------------------------------------- */
1133 /* initialize push delay values to default */
1134 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1136 if (!IS_CUSTOM_ELEMENT(i))
1138 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1139 element_info[i].push_delay_random = game.default_push_delay_random;
1143 /* set push delay value for certain elements from pre-defined list */
1144 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1146 int e = push_delay_list[i].element;
1148 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1149 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1152 /* set push delay value for Supaplex elements for newer engine versions */
1153 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1155 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1157 if (IS_SP_ELEMENT(i))
1159 element_info[i].push_delay_fixed = 6;
1160 element_info[i].push_delay_random = 0;
1165 /* ---------- initialize move stepsize ----------------------------------- */
1167 /* initialize move stepsize values to default */
1168 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1169 if (!IS_CUSTOM_ELEMENT(i))
1170 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1172 /* set move stepsize value for certain elements from pre-defined list */
1173 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1175 int e = move_stepsize_list[i].element;
1177 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1180 /* ---------- initialize move dig/leave ---------------------------------- */
1182 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1184 element_info[i].can_leave_element = FALSE;
1185 element_info[i].can_leave_element_last = FALSE;
1188 /* ---------- initialize gem count --------------------------------------- */
1190 /* initialize gem count values for each element */
1191 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1192 if (!IS_CUSTOM_ELEMENT(i))
1193 element_info[i].collect_count = 0;
1195 /* add gem count values for all elements from pre-defined list */
1196 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1197 element_info[collect_count_list[i].element].collect_count =
1198 collect_count_list[i].count;
1200 /* ---------- initialize access direction -------------------------------- */
1202 /* initialize access direction values to default */
1203 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1204 if (!IS_CUSTOM_ELEMENT(i))
1205 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1207 /* set access direction value for certain elements from pre-defined list */
1208 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1209 element_info[tube_access[i].element].access_direction =
1210 tube_access[i].direction;
1215 =============================================================================
1217 -----------------------------------------------------------------------------
1218 initialize and start new game
1219 =============================================================================
1224 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1225 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1226 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1233 #if USE_NEW_AMOEBA_CODE
1234 printf("Using new amoeba code.\n");
1236 printf("Using old amoeba code.\n");
1241 /* don't play tapes over network */
1242 network_playing = (options.network && !tape.playing);
1244 for (i = 0; i < MAX_PLAYERS; i++)
1246 struct PlayerInfo *player = &stored_player[i];
1248 player->index_nr = i;
1249 player->element_nr = EL_PLAYER_1 + i;
1251 player->present = FALSE;
1252 player->active = FALSE;
1255 player->effective_action = 0;
1256 player->programmed_action = 0;
1259 player->gems_still_needed = level.gems_needed;
1260 player->sokobanfields_still_needed = 0;
1261 player->lights_still_needed = 0;
1262 player->friends_still_needed = 0;
1264 for (j = 0; j < 4; j++)
1265 player->key[j] = FALSE;
1267 player->dynabomb_count = 0;
1268 player->dynabomb_size = 1;
1269 player->dynabombs_left = 0;
1270 player->dynabomb_xl = FALSE;
1272 player->MovDir = MV_NO_MOVING;
1275 player->GfxDir = MV_NO_MOVING;
1276 player->GfxAction = ACTION_DEFAULT;
1278 player->StepFrame = 0;
1280 player->use_murphy_graphic = FALSE;
1282 player->block_last_field = FALSE;
1284 player->actual_frame_counter = 0;
1286 player->step_counter = 0;
1288 player->last_move_dir = MV_NO_MOVING;
1290 player->is_waiting = FALSE;
1291 player->is_moving = FALSE;
1292 player->is_digging = FALSE;
1293 player->is_snapping = FALSE;
1294 player->is_collecting = FALSE;
1295 player->is_pushing = FALSE;
1296 player->is_switching = FALSE;
1297 player->is_dropping = FALSE;
1299 player->is_bored = FALSE;
1300 player->is_sleeping = FALSE;
1302 player->frame_counter_bored = -1;
1303 player->frame_counter_sleeping = -1;
1305 player->anim_delay_counter = 0;
1306 player->post_delay_counter = 0;
1308 player->action_waiting = ACTION_DEFAULT;
1309 player->last_action_waiting = ACTION_DEFAULT;
1310 player->special_action_bored = ACTION_DEFAULT;
1311 player->special_action_sleeping = ACTION_DEFAULT;
1313 player->num_special_action_bored = 0;
1314 player->num_special_action_sleeping = 0;
1316 /* determine number of special actions for bored and sleeping animation */
1317 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1319 boolean found = FALSE;
1321 for (k = 0; k < NUM_DIRECTIONS; k++)
1322 if (el_act_dir2img(player->element_nr, j, k) !=
1323 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1327 player->num_special_action_bored++;
1331 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1333 boolean found = FALSE;
1335 for (k = 0; k < NUM_DIRECTIONS; k++)
1336 if (el_act_dir2img(player->element_nr, j, k) !=
1337 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1341 player->num_special_action_sleeping++;
1346 player->switch_x = -1;
1347 player->switch_y = -1;
1349 player->show_envelope = 0;
1351 player->move_delay = game.initial_move_delay;
1352 player->move_delay_value = game.initial_move_delay_value;
1354 player->move_delay_reset_counter = 0;
1356 player->push_delay = 0;
1357 player->push_delay_value = game.initial_push_delay_value;
1359 player->drop_delay = 0;
1361 player->last_jx = player->last_jy = 0;
1362 player->jx = player->jy = 0;
1364 player->shield_normal_time_left = 0;
1365 player->shield_deadly_time_left = 0;
1367 player->inventory_size = 0;
1369 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1370 SnapField(player, 0, 0);
1372 player->LevelSolved = FALSE;
1373 player->GameOver = FALSE;
1376 network_player_action_received = FALSE;
1378 #if defined(PLATFORM_UNIX)
1379 /* initial null action */
1380 if (network_playing)
1381 SendToServer_MovePlayer(MV_NO_MOVING);
1389 TimeLeft = level.time;
1391 ScreenMovDir = MV_NO_MOVING;
1395 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1397 AllPlayersGone = FALSE;
1399 game.yamyam_content_nr = 0;
1400 game.magic_wall_active = FALSE;
1401 game.magic_wall_time_left = 0;
1402 game.light_time_left = 0;
1403 game.timegate_time_left = 0;
1404 game.switchgate_pos = 0;
1405 game.balloon_dir = MV_NO_MOVING;
1406 game.gravity = level.initial_gravity;
1407 game.explosions_delayed = TRUE;
1409 game.envelope_active = FALSE;
1411 for (i = 0; i < 4; i++)
1413 game.belt_dir[i] = MV_NO_MOVING;
1414 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1417 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1418 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1420 for (x = 0; x < lev_fieldx; x++)
1422 for (y = 0; y < lev_fieldy; y++)
1424 Feld[x][y] = level.field[x][y];
1425 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1426 ChangeDelay[x][y] = 0;
1427 ChangePage[x][y] = -1;
1428 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1430 WasJustMoving[x][y] = 0;
1431 WasJustFalling[x][y] = 0;
1433 Pushed[x][y] = FALSE;
1435 Changed[x][y] = CE_BITMASK_DEFAULT;
1436 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1438 ExplodePhase[x][y] = 0;
1439 ExplodeDelay[x][y] = 0;
1440 ExplodeField[x][y] = EX_NO_EXPLOSION;
1442 RunnerVisit[x][y] = 0;
1443 PlayerVisit[x][y] = 0;
1446 GfxRandom[x][y] = INIT_GFX_RANDOM();
1447 GfxElement[x][y] = EL_UNDEFINED;
1448 GfxAction[x][y] = ACTION_DEFAULT;
1449 GfxDir[x][y] = MV_NO_MOVING;
1453 for (y = 0; y < lev_fieldy; y++)
1455 for (x = 0; x < lev_fieldx; x++)
1457 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1459 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1461 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1464 InitField(x, y, TRUE);
1470 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1471 emulate_sb ? EMU_SOKOBAN :
1472 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1474 /* initialize explosion and ignition delay */
1475 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1477 if (!IS_CUSTOM_ELEMENT(i))
1480 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1481 int last_phase = num_phase * delay;
1482 int half_phase = (num_phase / 2) * delay;
1484 element_info[i].explosion_delay = last_phase;
1485 element_info[i].ignition_delay = half_phase;
1487 if (i == EL_BLACK_ORB)
1488 element_info[i].ignition_delay = 1;
1491 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1492 element_info[i].explosion_delay = 2;
1494 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1495 element_info[i].ignition_delay = 1;
1498 /* correct non-moving belts to start moving left */
1499 for (i = 0; i < 4; i++)
1500 if (game.belt_dir[i] == MV_NO_MOVING)
1501 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1503 /* check if any connected player was not found in playfield */
1504 for (i = 0; i < MAX_PLAYERS; i++)
1506 struct PlayerInfo *player = &stored_player[i];
1508 if (player->connected && !player->present)
1510 for (j = 0; j < MAX_PLAYERS; j++)
1512 struct PlayerInfo *some_player = &stored_player[j];
1513 int jx = some_player->jx, jy = some_player->jy;
1515 /* assign first free player found that is present in the playfield */
1516 if (some_player->present && !some_player->connected)
1518 player->present = TRUE;
1519 player->active = TRUE;
1521 some_player->present = FALSE;
1522 some_player->active = FALSE;
1524 StorePlayer[jx][jy] = player->element_nr;
1525 player->jx = player->last_jx = jx;
1526 player->jy = player->last_jy = jy;
1536 /* when playing a tape, eliminate all players which do not participate */
1538 for (i = 0; i < MAX_PLAYERS; i++)
1540 if (stored_player[i].active && !tape.player_participates[i])
1542 struct PlayerInfo *player = &stored_player[i];
1543 int jx = player->jx, jy = player->jy;
1545 player->active = FALSE;
1546 StorePlayer[jx][jy] = 0;
1547 Feld[jx][jy] = EL_EMPTY;
1551 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1553 /* when in single player mode, eliminate all but the first active player */
1555 for (i = 0; i < MAX_PLAYERS; i++)
1557 if (stored_player[i].active)
1559 for (j = i + 1; j < MAX_PLAYERS; j++)
1561 if (stored_player[j].active)
1563 struct PlayerInfo *player = &stored_player[j];
1564 int jx = player->jx, jy = player->jy;
1566 player->active = FALSE;
1567 player->present = FALSE;
1569 StorePlayer[jx][jy] = 0;
1570 Feld[jx][jy] = EL_EMPTY;
1577 /* when recording the game, store which players take part in the game */
1580 for (i = 0; i < MAX_PLAYERS; i++)
1581 if (stored_player[i].active)
1582 tape.player_participates[i] = TRUE;
1587 for (i = 0; i < MAX_PLAYERS; i++)
1589 struct PlayerInfo *player = &stored_player[i];
1591 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1596 if (local_player == player)
1597 printf("Player %d is local player.\n", i+1);
1601 if (BorderElement == EL_EMPTY)
1604 SBX_Right = lev_fieldx - SCR_FIELDX;
1606 SBY_Lower = lev_fieldy - SCR_FIELDY;
1611 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1613 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1616 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1617 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1619 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1620 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1622 /* if local player not found, look for custom element that might create
1623 the player (make some assumptions about the right custom element) */
1624 if (!local_player->present)
1626 int start_x = 0, start_y = 0;
1627 int found_rating = 0;
1628 int found_element = EL_UNDEFINED;
1630 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1632 int element = Feld[x][y];
1637 if (!IS_CUSTOM_ELEMENT(element))
1640 if (CAN_CHANGE(element))
1642 for (i = 0; i < element_info[element].num_change_pages; i++)
1644 content = element_info[element].change_page[i].target_element;
1645 is_player = ELEM_IS_PLAYER(content);
1647 if (is_player && (found_rating < 3 || element < found_element))
1653 found_element = element;
1658 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1660 content = element_info[element].content[xx][yy];
1661 is_player = ELEM_IS_PLAYER(content);
1663 if (is_player && (found_rating < 2 || element < found_element))
1665 start_x = x + xx - 1;
1666 start_y = y + yy - 1;
1669 found_element = element;
1672 if (!CAN_CHANGE(element))
1675 for (i = 0; i < element_info[element].num_change_pages; i++)
1677 content = element_info[element].change_page[i].content[xx][yy];
1678 is_player = ELEM_IS_PLAYER(content);
1680 if (is_player && (found_rating < 1 || element < found_element))
1682 start_x = x + xx - 1;
1683 start_y = y + yy - 1;
1686 found_element = element;
1692 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1693 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1696 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1697 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1703 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1704 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1705 local_player->jx - MIDPOSX);
1707 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1708 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1709 local_player->jy - MIDPOSY);
1711 scroll_x = SBX_Left;
1712 scroll_y = SBY_Upper;
1713 if (local_player->jx >= SBX_Left + MIDPOSX)
1714 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1715 local_player->jx - MIDPOSX :
1717 if (local_player->jy >= SBY_Upper + MIDPOSY)
1718 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1719 local_player->jy - MIDPOSY :
1724 CloseDoor(DOOR_CLOSE_1);
1729 /* after drawing the level, correct some elements */
1730 if (game.timegate_time_left == 0)
1731 CloseAllOpenTimegates();
1733 if (setup.soft_scrolling)
1734 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1736 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1739 /* copy default game door content to main double buffer */
1740 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1741 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1744 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1747 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1748 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1749 BlitBitmap(drawto, drawto,
1750 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1751 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1752 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1753 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1756 DrawGameDoorValues();
1760 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1761 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1762 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1766 /* copy actual game door content to door double buffer for OpenDoor() */
1767 BlitBitmap(drawto, bitmap_db_door,
1768 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1770 OpenDoor(DOOR_OPEN_ALL);
1772 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1774 if (setup.sound_music)
1777 KeyboardAutoRepeatOffUnlessAutoplay();
1781 for (i = 0; i < 4; i++)
1782 printf("Player %d %sactive.\n",
1783 i + 1, (stored_player[i].active ? "" : "not "));
1787 printf("::: starting game [%d]\n", FrameCounter);
1791 void InitMovDir(int x, int y)
1793 int i, element = Feld[x][y];
1794 static int xy[4][2] =
1801 static int direction[3][4] =
1803 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1804 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1805 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1814 Feld[x][y] = EL_BUG;
1815 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1818 case EL_SPACESHIP_RIGHT:
1819 case EL_SPACESHIP_UP:
1820 case EL_SPACESHIP_LEFT:
1821 case EL_SPACESHIP_DOWN:
1822 Feld[x][y] = EL_SPACESHIP;
1823 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1826 case EL_BD_BUTTERFLY_RIGHT:
1827 case EL_BD_BUTTERFLY_UP:
1828 case EL_BD_BUTTERFLY_LEFT:
1829 case EL_BD_BUTTERFLY_DOWN:
1830 Feld[x][y] = EL_BD_BUTTERFLY;
1831 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1834 case EL_BD_FIREFLY_RIGHT:
1835 case EL_BD_FIREFLY_UP:
1836 case EL_BD_FIREFLY_LEFT:
1837 case EL_BD_FIREFLY_DOWN:
1838 Feld[x][y] = EL_BD_FIREFLY;
1839 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1842 case EL_PACMAN_RIGHT:
1844 case EL_PACMAN_LEFT:
1845 case EL_PACMAN_DOWN:
1846 Feld[x][y] = EL_PACMAN;
1847 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1850 case EL_SP_SNIKSNAK:
1851 MovDir[x][y] = MV_UP;
1854 case EL_SP_ELECTRON:
1855 MovDir[x][y] = MV_LEFT;
1862 Feld[x][y] = EL_MOLE;
1863 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1867 if (IS_CUSTOM_ELEMENT(element))
1869 struct ElementInfo *ei = &element_info[element];
1870 int move_direction_initial = ei->move_direction_initial;
1871 int move_pattern = ei->move_pattern;
1873 if (move_direction_initial == MV_START_PREVIOUS)
1875 if (MovDir[x][y] != MV_NO_MOVING)
1878 move_direction_initial = MV_START_AUTOMATIC;
1881 if (move_direction_initial == MV_START_RANDOM)
1882 MovDir[x][y] = 1 << RND(4);
1883 else if (move_direction_initial & MV_ANY_DIRECTION)
1884 MovDir[x][y] = move_direction_initial;
1885 else if (move_pattern == MV_ALL_DIRECTIONS ||
1886 move_pattern == MV_TURNING_LEFT ||
1887 move_pattern == MV_TURNING_RIGHT ||
1888 move_pattern == MV_TURNING_LEFT_RIGHT ||
1889 move_pattern == MV_TURNING_RIGHT_LEFT ||
1890 move_pattern == MV_TURNING_RANDOM)
1891 MovDir[x][y] = 1 << RND(4);
1892 else if (move_pattern == MV_HORIZONTAL)
1893 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1894 else if (move_pattern == MV_VERTICAL)
1895 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1896 else if (move_pattern & MV_ANY_DIRECTION)
1897 MovDir[x][y] = element_info[element].move_pattern;
1898 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1899 move_pattern == MV_ALONG_RIGHT_SIDE)
1901 for (i = 0; i < 4; i++)
1903 int x1 = x + xy[i][0];
1904 int y1 = y + xy[i][1];
1906 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1908 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1909 MovDir[x][y] = direction[0][i];
1911 MovDir[x][y] = direction[1][i];
1920 MovDir[x][y] = 1 << RND(4);
1922 if (element != EL_BUG &&
1923 element != EL_SPACESHIP &&
1924 element != EL_BD_BUTTERFLY &&
1925 element != EL_BD_FIREFLY)
1928 for (i = 0; i < 4; i++)
1930 int x1 = x + xy[i][0];
1931 int y1 = y + xy[i][1];
1933 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1935 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1937 MovDir[x][y] = direction[0][i];
1940 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1941 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1943 MovDir[x][y] = direction[1][i];
1952 GfxDir[x][y] = MovDir[x][y];
1955 void InitAmoebaNr(int x, int y)
1958 int group_nr = AmoebeNachbarNr(x, y);
1962 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1964 if (AmoebaCnt[i] == 0)
1972 AmoebaNr[x][y] = group_nr;
1973 AmoebaCnt[group_nr]++;
1974 AmoebaCnt2[group_nr]++;
1980 boolean raise_level = FALSE;
1982 if (local_player->MovPos)
1986 if (tape.auto_play) /* tape might already be stopped here */
1987 tape.auto_play_level_solved = TRUE;
1989 if (tape.playing && tape.auto_play)
1990 tape.auto_play_level_solved = TRUE;
1993 local_player->LevelSolved = FALSE;
1995 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1999 if (!tape.playing && setup.sound_loops)
2000 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2001 SND_CTRL_PLAY_LOOP);
2003 while (TimeLeft > 0)
2005 if (!tape.playing && !setup.sound_loops)
2006 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2007 if (TimeLeft > 0 && !(TimeLeft % 10))
2008 RaiseScore(level.score[SC_TIME_BONUS]);
2009 if (TimeLeft > 100 && !(TimeLeft % 10))
2013 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
2020 if (!tape.playing && setup.sound_loops)
2021 StopSound(SND_GAME_LEVELTIME_BONUS);
2023 else if (level.time == 0) /* level without time limit */
2025 if (!tape.playing && setup.sound_loops)
2026 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2027 SND_CTRL_PLAY_LOOP);
2029 while (TimePlayed < 999)
2031 if (!tape.playing && !setup.sound_loops)
2032 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2033 if (TimePlayed < 999 && !(TimePlayed % 10))
2034 RaiseScore(level.score[SC_TIME_BONUS]);
2035 if (TimePlayed < 900 && !(TimePlayed % 10))
2039 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
2046 if (!tape.playing && setup.sound_loops)
2047 StopSound(SND_GAME_LEVELTIME_BONUS);
2050 /* close exit door after last player */
2051 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2052 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2054 int element = Feld[ExitX][ExitY];
2056 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2057 EL_SP_EXIT_CLOSING);
2059 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2062 /* Hero disappears */
2063 DrawLevelField(ExitX, ExitY);
2069 CloseDoor(DOOR_CLOSE_1);
2074 SaveTape(tape.level_nr); /* Ask to save tape */
2077 if (level_nr == leveldir_current->handicap_level)
2079 leveldir_current->handicap_level++;
2080 SaveLevelSetup_SeriesInfo();
2083 if (level_editor_test_game)
2084 local_player->score = -1; /* no highscore when playing from editor */
2085 else if (level_nr < leveldir_current->last_level)
2086 raise_level = TRUE; /* advance to next level */
2088 if ((hi_pos = NewHiScore()) >= 0)
2090 game_status = GAME_MODE_SCORES;
2091 DrawHallOfFame(hi_pos);
2100 game_status = GAME_MODE_MAIN;
2117 LoadScore(level_nr);
2119 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2120 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2123 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2125 if (local_player->score > highscore[k].Score)
2127 /* player has made it to the hall of fame */
2129 if (k < MAX_SCORE_ENTRIES - 1)
2131 int m = MAX_SCORE_ENTRIES - 1;
2134 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2135 if (!strcmp(setup.player_name, highscore[l].Name))
2137 if (m == k) /* player's new highscore overwrites his old one */
2141 for (l = m; l > k; l--)
2143 strcpy(highscore[l].Name, highscore[l - 1].Name);
2144 highscore[l].Score = highscore[l - 1].Score;
2151 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2152 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2153 highscore[k].Score = local_player->score;
2159 else if (!strncmp(setup.player_name, highscore[k].Name,
2160 MAX_PLAYER_NAME_LEN))
2161 break; /* player already there with a higher score */
2167 SaveScore(level_nr);
2172 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2174 if (player->GfxAction != action || player->GfxDir != dir)
2177 printf("Player frame reset! (%d => %d, %d => %d)\n",
2178 player->GfxAction, action, player->GfxDir, dir);
2181 player->GfxAction = action;
2182 player->GfxDir = dir;
2184 player->StepFrame = 0;
2188 static void ResetRandomAnimationValue(int x, int y)
2190 GfxRandom[x][y] = INIT_GFX_RANDOM();
2193 static void ResetGfxAnimation(int x, int y)
2196 GfxAction[x][y] = ACTION_DEFAULT;
2197 GfxDir[x][y] = MovDir[x][y];
2200 void InitMovingField(int x, int y, int direction)
2202 int element = Feld[x][y];
2203 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2204 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2208 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2209 ResetGfxAnimation(x, y);
2211 MovDir[newx][newy] = MovDir[x][y] = direction;
2212 GfxDir[x][y] = direction;
2214 if (Feld[newx][newy] == EL_EMPTY)
2215 Feld[newx][newy] = EL_BLOCKED;
2217 if (direction == MV_DOWN && CAN_FALL(element))
2218 GfxAction[x][y] = ACTION_FALLING;
2220 GfxAction[x][y] = ACTION_MOVING;
2222 GfxFrame[newx][newy] = GfxFrame[x][y];
2223 GfxRandom[newx][newy] = GfxRandom[x][y];
2224 GfxAction[newx][newy] = GfxAction[x][y];
2225 GfxDir[newx][newy] = GfxDir[x][y];
2228 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2230 int direction = MovDir[x][y];
2231 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2232 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2238 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2240 int oldx = x, oldy = y;
2241 int direction = MovDir[x][y];
2243 if (direction == MV_LEFT)
2245 else if (direction == MV_RIGHT)
2247 else if (direction == MV_UP)
2249 else if (direction == MV_DOWN)
2252 *comes_from_x = oldx;
2253 *comes_from_y = oldy;
2256 int MovingOrBlocked2Element(int x, int y)
2258 int element = Feld[x][y];
2260 if (element == EL_BLOCKED)
2264 Blocked2Moving(x, y, &oldx, &oldy);
2265 return Feld[oldx][oldy];
2271 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2273 /* like MovingOrBlocked2Element(), but if element is moving
2274 and (x,y) is the field the moving element is just leaving,
2275 return EL_BLOCKED instead of the element value */
2276 int element = Feld[x][y];
2278 if (IS_MOVING(x, y))
2280 if (element == EL_BLOCKED)
2284 Blocked2Moving(x, y, &oldx, &oldy);
2285 return Feld[oldx][oldy];
2294 static void RemoveField(int x, int y)
2296 Feld[x][y] = EL_EMPTY;
2303 ChangeDelay[x][y] = 0;
2304 ChangePage[x][y] = -1;
2305 Pushed[x][y] = FALSE;
2307 GfxElement[x][y] = EL_UNDEFINED;
2308 GfxAction[x][y] = ACTION_DEFAULT;
2309 GfxDir[x][y] = MV_NO_MOVING;
2312 void RemoveMovingField(int x, int y)
2314 int oldx = x, oldy = y, newx = x, newy = y;
2315 int element = Feld[x][y];
2316 int next_element = EL_UNDEFINED;
2318 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2321 if (IS_MOVING(x, y))
2323 Moving2Blocked(x, y, &newx, &newy);
2324 if (Feld[newx][newy] != EL_BLOCKED)
2327 else if (element == EL_BLOCKED)
2329 Blocked2Moving(x, y, &oldx, &oldy);
2330 if (!IS_MOVING(oldx, oldy))
2334 if (element == EL_BLOCKED &&
2335 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2336 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2337 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2338 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2339 next_element = get_next_element(Feld[oldx][oldy]);
2341 RemoveField(oldx, oldy);
2342 RemoveField(newx, newy);
2344 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2346 if (next_element != EL_UNDEFINED)
2347 Feld[oldx][oldy] = next_element;
2349 DrawLevelField(oldx, oldy);
2350 DrawLevelField(newx, newy);
2353 void DrawDynamite(int x, int y)
2355 int sx = SCREENX(x), sy = SCREENY(y);
2356 int graphic = el2img(Feld[x][y]);
2359 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2362 if (IS_WALKABLE_INSIDE(Back[x][y]))
2366 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2367 else if (Store[x][y])
2368 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2370 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2373 if (Back[x][y] || Store[x][y])
2374 DrawGraphicThruMask(sx, sy, graphic, frame);
2376 DrawGraphic(sx, sy, graphic, frame);
2378 if (game.emulation == EMU_SUPAPLEX)
2379 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2380 else if (Store[x][y])
2381 DrawGraphicThruMask(sx, sy, graphic, frame);
2383 DrawGraphic(sx, sy, graphic, frame);
2387 void CheckDynamite(int x, int y)
2389 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2393 if (MovDelay[x][y] != 0)
2396 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2403 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2405 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2406 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2407 StopSound(SND_DYNAMITE_ACTIVE);
2409 StopSound(SND_DYNABOMB_ACTIVE);
2415 void RelocatePlayer(int x, int y, int element_raw)
2417 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2418 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2419 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2420 boolean no_delay = (tape.index_search);
2421 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2422 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2424 if (player->GameOver) /* do not reanimate dead player */
2428 RemoveField(x, y); /* temporarily remove newly placed player */
2429 DrawLevelField(x, y);
2432 if (player->present)
2434 while (player->MovPos)
2436 ScrollPlayer(player, SCROLL_GO_ON);
2437 ScrollScreen(NULL, SCROLL_GO_ON);
2443 Delay(wait_delay_value);
2446 DrawPlayer(player); /* needed here only to cleanup last field */
2447 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2449 player->is_moving = FALSE;
2452 Feld[x][y] = element;
2453 InitPlayerField(x, y, element, TRUE);
2455 if (player == local_player)
2457 int scroll_xx = -999, scroll_yy = -999;
2459 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2462 int fx = FX, fy = FY;
2464 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2465 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2466 local_player->jx - MIDPOSX);
2468 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2469 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2470 local_player->jy - MIDPOSY);
2472 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2473 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2478 fx += dx * TILEX / 2;
2479 fy += dy * TILEY / 2;
2481 ScrollLevel(dx, dy);
2484 /* scroll in two steps of half tile size to make things smoother */
2485 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2487 Delay(wait_delay_value);
2489 /* scroll second step to align at full tile size */
2491 Delay(wait_delay_value);
2496 void Explode(int ex, int ey, int phase, int mode)
2503 /* !!! eliminate this variable !!! */
2504 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2509 int last_phase = num_phase * delay;
2510 int half_phase = (num_phase / 2) * delay;
2511 int first_phase_after_start = EX_PHASE_START + 1;
2515 if (game.explosions_delayed)
2517 ExplodeField[ex][ey] = mode;
2521 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2523 int center_element = Feld[ex][ey];
2526 /* --- This is only really needed (and now handled) in "Impact()". --- */
2527 /* do not explode moving elements that left the explode field in time */
2528 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2529 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2533 if (mode == EX_NORMAL || mode == EX_CENTER)
2534 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2536 /* remove things displayed in background while burning dynamite */
2537 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2540 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2542 /* put moving element to center field (and let it explode there) */
2543 center_element = MovingOrBlocked2Element(ex, ey);
2544 RemoveMovingField(ex, ey);
2545 Feld[ex][ey] = center_element;
2549 last_phase = element_info[center_element].explosion_delay;
2552 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2554 int xx = x - ex + 1;
2555 int yy = y - ey + 1;
2558 if (!IN_LEV_FIELD(x, y) ||
2559 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2560 (x != ex || y != ey)))
2563 element = Feld[x][y];
2565 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2567 element = MovingOrBlocked2Element(x, y);
2569 if (!IS_EXPLOSION_PROOF(element))
2570 RemoveMovingField(x, y);
2576 if (IS_EXPLOSION_PROOF(element))
2579 /* indestructible elements can only explode in center (but not flames) */
2580 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2581 element == EL_FLAMES)
2586 if ((IS_INDESTRUCTIBLE(element) &&
2587 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2588 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2589 element == EL_FLAMES)
2593 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2595 if (IS_ACTIVE_BOMB(element))
2597 /* re-activate things under the bomb like gate or penguin */
2598 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2605 /* save walkable background elements while explosion on same tile */
2607 if (IS_INDESTRUCTIBLE(element))
2608 Back[x][y] = element;
2610 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2611 Back[x][y] = element;
2614 /* ignite explodable elements reached by other explosion */
2615 if (element == EL_EXPLOSION)
2616 element = Store2[x][y];
2619 if (AmoebaNr[x][y] &&
2620 (element == EL_AMOEBA_FULL ||
2621 element == EL_BD_AMOEBA ||
2622 element == EL_AMOEBA_GROWING))
2624 AmoebaCnt[AmoebaNr[x][y]]--;
2625 AmoebaCnt2[AmoebaNr[x][y]]--;
2631 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2633 switch(StorePlayer[ex][ey])
2636 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2639 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2642 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2646 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2650 if (game.emulation == EMU_SUPAPLEX)
2651 Store[x][y] = EL_EMPTY;
2653 else if (center_element == EL_MOLE)
2654 Store[x][y] = EL_EMERALD_RED;
2655 else if (center_element == EL_PENGUIN)
2656 Store[x][y] = EL_EMERALD_PURPLE;
2657 else if (center_element == EL_BUG)
2658 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2659 else if (center_element == EL_BD_BUTTERFLY)
2660 Store[x][y] = EL_BD_DIAMOND;
2661 else if (center_element == EL_SP_ELECTRON)
2662 Store[x][y] = EL_SP_INFOTRON;
2663 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2664 Store[x][y] = level.amoeba_content;
2665 else if (center_element == EL_YAMYAM)
2666 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2667 else if (IS_CUSTOM_ELEMENT(center_element) &&
2668 element_info[center_element].content[xx][yy] != EL_EMPTY)
2669 Store[x][y] = element_info[center_element].content[xx][yy];
2670 else if (element == EL_WALL_EMERALD)
2671 Store[x][y] = EL_EMERALD;
2672 else if (element == EL_WALL_DIAMOND)
2673 Store[x][y] = EL_DIAMOND;
2674 else if (element == EL_WALL_BD_DIAMOND)
2675 Store[x][y] = EL_BD_DIAMOND;
2676 else if (element == EL_WALL_EMERALD_YELLOW)
2677 Store[x][y] = EL_EMERALD_YELLOW;
2678 else if (element == EL_WALL_EMERALD_RED)
2679 Store[x][y] = EL_EMERALD_RED;
2680 else if (element == EL_WALL_EMERALD_PURPLE)
2681 Store[x][y] = EL_EMERALD_PURPLE;
2682 else if (element == EL_WALL_PEARL)
2683 Store[x][y] = EL_PEARL;
2684 else if (element == EL_WALL_CRYSTAL)
2685 Store[x][y] = EL_CRYSTAL;
2686 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2687 Store[x][y] = element_info[element].content[1][1];
2689 Store[x][y] = EL_EMPTY;
2691 if (x != ex || y != ey ||
2692 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2693 Store2[x][y] = element;
2696 if (AmoebaNr[x][y] &&
2697 (element == EL_AMOEBA_FULL ||
2698 element == EL_BD_AMOEBA ||
2699 element == EL_AMOEBA_GROWING))
2701 AmoebaCnt[AmoebaNr[x][y]]--;
2702 AmoebaCnt2[AmoebaNr[x][y]]--;
2708 MovDir[x][y] = MovPos[x][y] = 0;
2709 GfxDir[x][y] = MovDir[x][y];
2714 Feld[x][y] = EL_EXPLOSION;
2716 GfxElement[x][y] = center_element;
2718 GfxElement[x][y] = EL_UNDEFINED;
2721 ExplodePhase[x][y] = 1;
2723 ExplodeDelay[x][y] = last_phase;
2728 if (center_element == EL_YAMYAM)
2729 game.yamyam_content_nr =
2730 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2742 last_phase = ExplodeDelay[x][y];
2745 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2749 /* activate this even in non-DEBUG version until cause for crash in
2750 getGraphicAnimationFrame() (see below) is found and eliminated */
2754 if (GfxElement[x][y] == EL_UNDEFINED)
2757 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2758 printf("Explode(): This should never happen!\n");
2761 GfxElement[x][y] = EL_EMPTY;
2767 border_element = Store2[x][y];
2768 if (IS_PLAYER(x, y))
2769 border_element = StorePlayer[x][y];
2771 if (phase == element_info[border_element].ignition_delay ||
2772 phase == last_phase)
2774 boolean border_explosion = FALSE;
2777 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2779 if (IS_PLAYER(x, y))
2782 KillHeroUnlessExplosionProtected(x, y);
2783 border_explosion = TRUE;
2786 if (phase == last_phase)
2787 printf("::: IS_PLAYER\n");
2790 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2792 Feld[x][y] = Store2[x][y];
2795 border_explosion = TRUE;
2798 if (phase == last_phase)
2799 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2802 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2804 AmoebeUmwandeln(x, y);
2806 border_explosion = TRUE;
2809 if (phase == last_phase)
2810 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2811 element_info[border_element].explosion_delay,
2812 element_info[border_element].ignition_delay,
2818 /* if an element just explodes due to another explosion (chain-reaction),
2819 do not immediately end the new explosion when it was the last frame of
2820 the explosion (as it would be done in the following "if"-statement!) */
2821 if (border_explosion && phase == last_phase)
2828 if (phase == first_phase_after_start)
2830 int element = Store2[x][y];
2832 if (element == EL_BLACK_ORB)
2834 Feld[x][y] = Store2[x][y];
2839 else if (phase == half_phase)
2841 int element = Store2[x][y];
2843 if (IS_PLAYER(x, y))
2844 KillHeroUnlessExplosionProtected(x, y);
2845 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2847 Feld[x][y] = Store2[x][y];
2851 else if (element == EL_AMOEBA_TO_DIAMOND)
2852 AmoebeUmwandeln(x, y);
2856 if (phase == last_phase)
2860 element = Feld[x][y] = Store[x][y];
2861 Store[x][y] = Store2[x][y] = 0;
2862 GfxElement[x][y] = EL_UNDEFINED;
2864 /* player can escape from explosions and might therefore be still alive */
2865 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2866 element <= EL_PLAYER_IS_EXPLODING_4)
2867 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2869 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2870 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2871 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2874 /* restore probably existing indestructible background element */
2875 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2876 element = Feld[x][y] = Back[x][y];
2879 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2880 GfxDir[x][y] = MV_NO_MOVING;
2881 ChangeDelay[x][y] = 0;
2882 ChangePage[x][y] = -1;
2885 InitField_WithBug2(x, y, FALSE);
2887 InitField(x, y, FALSE);
2889 /* !!! not needed !!! */
2891 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
2892 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
2895 if (CAN_MOVE(element))
2900 DrawLevelField(x, y);
2902 TestIfElementTouchesCustomElement(x, y);
2904 if (GFX_CRUMBLED(element))
2905 DrawLevelFieldCrumbledSandNeighbours(x, y);
2907 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
2908 StorePlayer[x][y] = 0;
2910 if (ELEM_IS_PLAYER(element))
2911 RelocatePlayer(x, y, element);
2914 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2916 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2920 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2922 int stored = Store[x][y];
2923 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2924 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2927 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2930 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2931 element_info[GfxElement[x][y]].token_name,
2936 DrawLevelFieldCrumbledSand(x, y);
2938 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2940 DrawLevelElement(x, y, Back[x][y]);
2941 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2943 else if (IS_WALKABLE_UNDER(Back[x][y]))
2945 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2946 DrawLevelElementThruMask(x, y, Back[x][y]);
2948 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2949 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2953 void DynaExplode(int ex, int ey)
2956 int dynabomb_element = Feld[ex][ey];
2957 int dynabomb_size = 1;
2958 boolean dynabomb_xl = FALSE;
2959 struct PlayerInfo *player;
2960 static int xy[4][2] =
2968 if (IS_ACTIVE_BOMB(dynabomb_element))
2970 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2971 dynabomb_size = player->dynabomb_size;
2972 dynabomb_xl = player->dynabomb_xl;
2973 player->dynabombs_left++;
2976 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2978 for (i = 0; i < 4; i++)
2980 for (j = 1; j <= dynabomb_size; j++)
2982 int x = ex + j * xy[i % 4][0];
2983 int y = ey + j * xy[i % 4][1];
2986 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2989 element = Feld[x][y];
2991 /* do not restart explosions of fields with active bombs */
2992 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2995 Explode(x, y, EX_PHASE_START, EX_BORDER);
2997 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2998 if (element != EL_EMPTY &&
2999 element != EL_SAND &&
3000 element != EL_EXPLOSION &&
3007 void Bang(int x, int y)
3010 int element = MovingOrBlocked2Element(x, y);
3012 int element = Feld[x][y];
3016 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3018 if (IS_PLAYER(x, y))
3021 struct PlayerInfo *player = PLAYERINFO(x, y);
3023 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3024 player->element_nr);
3029 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3031 if (game.emulation == EMU_SUPAPLEX)
3032 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3034 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3039 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3047 case EL_BD_BUTTERFLY:
3050 case EL_DARK_YAMYAM:
3054 RaiseScoreElement(element);
3055 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3057 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3058 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3059 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3060 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3061 case EL_DYNABOMB_INCREASE_NUMBER:
3062 case EL_DYNABOMB_INCREASE_SIZE:
3063 case EL_DYNABOMB_INCREASE_POWER:
3068 case EL_LAMP_ACTIVE:
3069 if (IS_PLAYER(x, y))
3070 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3072 Explode(x, y, EX_PHASE_START, EX_CENTER);
3075 if (CAN_EXPLODE_DYNA(element))
3077 else if (CAN_EXPLODE_1X1(element))
3078 Explode(x, y, EX_PHASE_START, EX_CENTER);
3080 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3084 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3087 void SplashAcid(int x, int y)
3089 int element = Feld[x][y];
3091 if (element != EL_ACID_SPLASH_LEFT &&
3092 element != EL_ACID_SPLASH_RIGHT)
3094 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3096 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3097 (!IN_LEV_FIELD(x-1, y-1) ||
3098 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3099 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3101 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3102 (!IN_LEV_FIELD(x+1, y-1) ||
3103 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3104 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3108 static void InitBeltMovement()
3110 static int belt_base_element[4] =
3112 EL_CONVEYOR_BELT_1_LEFT,
3113 EL_CONVEYOR_BELT_2_LEFT,
3114 EL_CONVEYOR_BELT_3_LEFT,
3115 EL_CONVEYOR_BELT_4_LEFT
3117 static int belt_base_active_element[4] =
3119 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3120 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3121 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3122 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3127 /* set frame order for belt animation graphic according to belt direction */
3128 for (i = 0; i < 4; i++)
3132 for (j = 0; j < 3; j++)
3134 int element = belt_base_active_element[belt_nr] + j;
3135 int graphic = el2img(element);
3137 if (game.belt_dir[i] == MV_LEFT)
3138 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3140 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3144 for (y = 0; y < lev_fieldy; y++)
3146 for (x = 0; x < lev_fieldx; x++)
3148 int element = Feld[x][y];
3150 for (i = 0; i < 4; i++)
3152 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3154 int e_belt_nr = getBeltNrFromBeltElement(element);
3157 if (e_belt_nr == belt_nr)
3159 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3161 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3169 static void ToggleBeltSwitch(int x, int y)
3171 static int belt_base_element[4] =
3173 EL_CONVEYOR_BELT_1_LEFT,
3174 EL_CONVEYOR_BELT_2_LEFT,
3175 EL_CONVEYOR_BELT_3_LEFT,
3176 EL_CONVEYOR_BELT_4_LEFT
3178 static int belt_base_active_element[4] =
3180 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3181 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3182 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3183 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3185 static int belt_base_switch_element[4] =
3187 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3188 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3189 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3190 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3192 static int belt_move_dir[4] =
3200 int element = Feld[x][y];
3201 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3202 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3203 int belt_dir = belt_move_dir[belt_dir_nr];
3206 if (!IS_BELT_SWITCH(element))
3209 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3210 game.belt_dir[belt_nr] = belt_dir;
3212 if (belt_dir_nr == 3)
3215 /* set frame order for belt animation graphic according to belt direction */
3216 for (i = 0; i < 3; i++)
3218 int element = belt_base_active_element[belt_nr] + i;
3219 int graphic = el2img(element);
3221 if (belt_dir == MV_LEFT)
3222 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3224 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3227 for (yy = 0; yy < lev_fieldy; yy++)
3229 for (xx = 0; xx < lev_fieldx; xx++)
3231 int element = Feld[xx][yy];
3233 if (IS_BELT_SWITCH(element))
3235 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3237 if (e_belt_nr == belt_nr)
3239 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3240 DrawLevelField(xx, yy);
3243 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3245 int e_belt_nr = getBeltNrFromBeltElement(element);
3247 if (e_belt_nr == belt_nr)
3249 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3251 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3252 DrawLevelField(xx, yy);
3255 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3257 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3259 if (e_belt_nr == belt_nr)
3261 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3263 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3264 DrawLevelField(xx, yy);
3271 static void ToggleSwitchgateSwitch(int x, int y)
3275 game.switchgate_pos = !game.switchgate_pos;
3277 for (yy = 0; yy < lev_fieldy; yy++)
3279 for (xx = 0; xx < lev_fieldx; xx++)
3281 int element = Feld[xx][yy];
3283 if (element == EL_SWITCHGATE_SWITCH_UP ||
3284 element == EL_SWITCHGATE_SWITCH_DOWN)
3286 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3287 DrawLevelField(xx, yy);
3289 else if (element == EL_SWITCHGATE_OPEN ||
3290 element == EL_SWITCHGATE_OPENING)
3292 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3294 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3296 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3299 else if (element == EL_SWITCHGATE_CLOSED ||
3300 element == EL_SWITCHGATE_CLOSING)
3302 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3304 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3306 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3313 static int getInvisibleActiveFromInvisibleElement(int element)
3315 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3316 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3317 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3321 static int getInvisibleFromInvisibleActiveElement(int element)
3323 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3324 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3325 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3329 static void RedrawAllLightSwitchesAndInvisibleElements()
3333 for (y = 0; y < lev_fieldy; y++)
3335 for (x = 0; x < lev_fieldx; x++)
3337 int element = Feld[x][y];
3339 if (element == EL_LIGHT_SWITCH &&
3340 game.light_time_left > 0)
3342 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3343 DrawLevelField(x, y);
3345 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3346 game.light_time_left == 0)
3348 Feld[x][y] = EL_LIGHT_SWITCH;
3349 DrawLevelField(x, y);
3351 else if (element == EL_INVISIBLE_STEELWALL ||
3352 element == EL_INVISIBLE_WALL ||
3353 element == EL_INVISIBLE_SAND)
3355 if (game.light_time_left > 0)
3356 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3358 DrawLevelField(x, y);
3360 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3361 element == EL_INVISIBLE_WALL_ACTIVE ||
3362 element == EL_INVISIBLE_SAND_ACTIVE)
3364 if (game.light_time_left == 0)
3365 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3367 DrawLevelField(x, y);
3373 static void ToggleLightSwitch(int x, int y)
3375 int element = Feld[x][y];
3377 game.light_time_left =
3378 (element == EL_LIGHT_SWITCH ?
3379 level.time_light * FRAMES_PER_SECOND : 0);
3381 RedrawAllLightSwitchesAndInvisibleElements();
3384 static void ActivateTimegateSwitch(int x, int y)
3388 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3390 for (yy = 0; yy < lev_fieldy; yy++)
3392 for (xx = 0; xx < lev_fieldx; xx++)
3394 int element = Feld[xx][yy];
3396 if (element == EL_TIMEGATE_CLOSED ||
3397 element == EL_TIMEGATE_CLOSING)
3399 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3400 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3404 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3406 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3407 DrawLevelField(xx, yy);
3414 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3417 inline static int getElementMoveStepsize(int x, int y)
3419 int element = Feld[x][y];
3420 int direction = MovDir[x][y];
3421 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3422 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3423 int horiz_move = (dx != 0);
3424 int sign = (horiz_move ? dx : dy);
3425 int step = sign * element_info[element].move_stepsize;
3427 /* special values for move stepsize for spring and things on conveyor belt */
3431 if (element == EL_SPRING)
3432 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3433 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3434 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3435 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3437 if (CAN_FALL(element) &&
3438 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3439 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3440 else if (element == EL_SPRING)
3441 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3448 void Impact(int x, int y)
3450 boolean lastline = (y == lev_fieldy-1);
3451 boolean object_hit = FALSE;
3452 boolean impact = (lastline || object_hit);
3453 int element = Feld[x][y];
3454 int smashed = EL_UNDEFINED;
3456 if (!lastline) /* check if element below was hit */
3458 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3461 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3462 MovDir[x][y + 1] != MV_DOWN ||
3463 MovPos[x][y + 1] <= TILEY / 2));
3466 object_hit = !IS_FREE(x, y + 1);
3469 /* do not smash moving elements that left the smashed field in time */
3470 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3471 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3475 smashed = MovingOrBlocked2Element(x, y + 1);
3477 impact = (lastline || object_hit);
3480 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3486 /* only reset graphic animation if graphic really changes after impact */
3488 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3490 ResetGfxAnimation(x, y);
3491 DrawLevelField(x, y);
3494 if (impact && CAN_EXPLODE_IMPACT(element))
3499 else if (impact && element == EL_PEARL)
3501 Feld[x][y] = EL_PEARL_BREAKING;
3502 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3505 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3507 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3512 if (impact && element == EL_AMOEBA_DROP)
3514 if (object_hit && IS_PLAYER(x, y + 1))
3515 KillHeroUnlessEnemyProtected(x, y + 1);
3516 else if (object_hit && smashed == EL_PENGUIN)
3520 Feld[x][y] = EL_AMOEBA_GROWING;
3521 Store[x][y] = EL_AMOEBA_WET;
3523 ResetRandomAnimationValue(x, y);
3528 if (object_hit) /* check which object was hit */
3530 if (CAN_PASS_MAGIC_WALL(element) &&
3531 (smashed == EL_MAGIC_WALL ||
3532 smashed == EL_BD_MAGIC_WALL))
3535 int activated_magic_wall =
3536 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3537 EL_BD_MAGIC_WALL_ACTIVE);
3539 /* activate magic wall / mill */
3540 for (yy = 0; yy < lev_fieldy; yy++)
3541 for (xx = 0; xx < lev_fieldx; xx++)
3542 if (Feld[xx][yy] == smashed)
3543 Feld[xx][yy] = activated_magic_wall;
3545 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3546 game.magic_wall_active = TRUE;
3548 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3549 SND_MAGIC_WALL_ACTIVATING :
3550 SND_BD_MAGIC_WALL_ACTIVATING));
3553 if (IS_PLAYER(x, y + 1))
3555 if (CAN_SMASH_PLAYER(element))
3557 KillHeroUnlessEnemyProtected(x, y + 1);
3561 else if (smashed == EL_PENGUIN)
3563 if (CAN_SMASH_PLAYER(element))
3569 else if (element == EL_BD_DIAMOND)
3571 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3577 else if (((element == EL_SP_INFOTRON ||
3578 element == EL_SP_ZONK) &&
3579 (smashed == EL_SP_SNIKSNAK ||
3580 smashed == EL_SP_ELECTRON ||
3581 smashed == EL_SP_DISK_ORANGE)) ||
3582 (element == EL_SP_INFOTRON &&
3583 smashed == EL_SP_DISK_YELLOW))
3589 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3595 else if (CAN_SMASH_EVERYTHING(element))
3597 if (IS_CLASSIC_ENEMY(smashed) ||
3598 CAN_EXPLODE_SMASHED(smashed))
3603 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3605 if (smashed == EL_LAMP ||
3606 smashed == EL_LAMP_ACTIVE)
3611 else if (smashed == EL_NUT)
3613 Feld[x][y + 1] = EL_NUT_BREAKING;
3614 PlayLevelSound(x, y, SND_NUT_BREAKING);
3615 RaiseScoreElement(EL_NUT);
3618 else if (smashed == EL_PEARL)
3620 Feld[x][y + 1] = EL_PEARL_BREAKING;
3621 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3624 else if (smashed == EL_DIAMOND)
3626 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3627 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3630 else if (IS_BELT_SWITCH(smashed))
3632 ToggleBeltSwitch(x, y + 1);
3634 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3635 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3637 ToggleSwitchgateSwitch(x, y + 1);
3639 else if (smashed == EL_LIGHT_SWITCH ||
3640 smashed == EL_LIGHT_SWITCH_ACTIVE)
3642 ToggleLightSwitch(x, y + 1);
3646 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3648 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3649 CE_OTHER_IS_SWITCHING);
3650 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3656 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3661 /* play sound of magic wall / mill */
3663 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3664 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3666 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3667 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3668 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3669 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3674 /* play sound of object that hits the ground */
3675 if (lastline || object_hit)
3676 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3679 inline static void TurnRoundExt(int x, int y)
3691 { 0, 0 }, { 0, 0 }, { 0, 0 },
3696 int left, right, back;
3700 { MV_DOWN, MV_UP, MV_RIGHT },
3701 { MV_UP, MV_DOWN, MV_LEFT },
3703 { MV_LEFT, MV_RIGHT, MV_DOWN },
3707 { MV_RIGHT, MV_LEFT, MV_UP }
3710 int element = Feld[x][y];
3711 int move_pattern = element_info[element].move_pattern;
3713 int old_move_dir = MovDir[x][y];
3714 int left_dir = turn[old_move_dir].left;
3715 int right_dir = turn[old_move_dir].right;
3716 int back_dir = turn[old_move_dir].back;
3718 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3719 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3720 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3721 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3723 int left_x = x + left_dx, left_y = y + left_dy;
3724 int right_x = x + right_dx, right_y = y + right_dy;
3725 int move_x = x + move_dx, move_y = y + move_dy;
3729 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3731 TestIfBadThingTouchesOtherBadThing(x, y);
3733 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3734 MovDir[x][y] = right_dir;
3735 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3736 MovDir[x][y] = left_dir;
3738 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3740 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3743 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3744 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3746 TestIfBadThingTouchesOtherBadThing(x, y);
3748 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3749 MovDir[x][y] = left_dir;
3750 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3751 MovDir[x][y] = right_dir;
3753 if ((element == EL_SPACESHIP ||
3754 element == EL_SP_SNIKSNAK ||
3755 element == EL_SP_ELECTRON)
3756 && MovDir[x][y] != old_move_dir)
3758 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3761 else if (element == EL_YAMYAM)
3763 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3764 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3766 if (can_turn_left && can_turn_right)
3767 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3768 else if (can_turn_left)
3769 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3770 else if (can_turn_right)
3771 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3773 MovDir[x][y] = back_dir;
3775 MovDelay[x][y] = 16 + 16 * RND(3);
3777 else if (element == EL_DARK_YAMYAM)
3779 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3780 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3782 if (can_turn_left && can_turn_right)
3783 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3784 else if (can_turn_left)
3785 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3786 else if (can_turn_right)
3787 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3789 MovDir[x][y] = back_dir;
3791 MovDelay[x][y] = 16 + 16 * RND(3);
3793 else if (element == EL_PACMAN)
3795 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3796 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3798 if (can_turn_left && can_turn_right)
3799 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3800 else if (can_turn_left)
3801 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3802 else if (can_turn_right)
3803 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3805 MovDir[x][y] = back_dir;
3807 MovDelay[x][y] = 6 + RND(40);
3809 else if (element == EL_PIG)
3811 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3812 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3813 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3814 boolean should_turn_left, should_turn_right, should_move_on;
3816 int rnd = RND(rnd_value);
3818 should_turn_left = (can_turn_left &&
3820 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3821 y + back_dy + left_dy)));
3822 should_turn_right = (can_turn_right &&
3824 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3825 y + back_dy + right_dy)));
3826 should_move_on = (can_move_on &&
3829 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3830 y + move_dy + left_dy) ||
3831 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3832 y + move_dy + right_dy)));
3834 if (should_turn_left || should_turn_right || should_move_on)
3836 if (should_turn_left && should_turn_right && should_move_on)
3837 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3838 rnd < 2 * rnd_value / 3 ? right_dir :
3840 else if (should_turn_left && should_turn_right)
3841 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3842 else if (should_turn_left && should_move_on)
3843 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3844 else if (should_turn_right && should_move_on)
3845 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3846 else if (should_turn_left)
3847 MovDir[x][y] = left_dir;
3848 else if (should_turn_right)
3849 MovDir[x][y] = right_dir;
3850 else if (should_move_on)
3851 MovDir[x][y] = old_move_dir;
3853 else if (can_move_on && rnd > rnd_value / 8)
3854 MovDir[x][y] = old_move_dir;
3855 else if (can_turn_left && can_turn_right)
3856 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3857 else if (can_turn_left && rnd > rnd_value / 8)
3858 MovDir[x][y] = left_dir;
3859 else if (can_turn_right && rnd > rnd_value/8)
3860 MovDir[x][y] = right_dir;
3862 MovDir[x][y] = back_dir;
3864 xx = x + move_xy[MovDir[x][y]].x;
3865 yy = y + move_xy[MovDir[x][y]].y;
3867 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3868 MovDir[x][y] = old_move_dir;
3872 else if (element == EL_DRAGON)
3874 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3875 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3876 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3878 int rnd = RND(rnd_value);
3881 if (FrameCounter < 1 && x == 0 && y == 29)
3882 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3885 if (can_move_on && rnd > rnd_value / 8)
3886 MovDir[x][y] = old_move_dir;
3887 else if (can_turn_left && can_turn_right)
3888 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3889 else if (can_turn_left && rnd > rnd_value / 8)
3890 MovDir[x][y] = left_dir;
3891 else if (can_turn_right && rnd > rnd_value / 8)
3892 MovDir[x][y] = right_dir;
3894 MovDir[x][y] = back_dir;
3896 xx = x + move_xy[MovDir[x][y]].x;
3897 yy = y + move_xy[MovDir[x][y]].y;
3900 if (FrameCounter < 1 && x == 0 && y == 29)
3901 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3902 xx, yy, Feld[xx][yy],
3907 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3908 MovDir[x][y] = old_move_dir;
3910 if (!IS_FREE(xx, yy))
3911 MovDir[x][y] = old_move_dir;
3915 if (FrameCounter < 1 && x == 0 && y == 29)
3916 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3921 else if (element == EL_MOLE)
3923 boolean can_move_on =
3924 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3925 IS_AMOEBOID(Feld[move_x][move_y]) ||
3926 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3929 boolean can_turn_left =
3930 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3931 IS_AMOEBOID(Feld[left_x][left_y])));
3933 boolean can_turn_right =
3934 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3935 IS_AMOEBOID(Feld[right_x][right_y])));
3937 if (can_turn_left && can_turn_right)
3938 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3939 else if (can_turn_left)
3940 MovDir[x][y] = left_dir;
3942 MovDir[x][y] = right_dir;
3945 if (MovDir[x][y] != old_move_dir)
3948 else if (element == EL_BALLOON)
3950 MovDir[x][y] = game.balloon_dir;
3953 else if (element == EL_SPRING)
3955 if (MovDir[x][y] & MV_HORIZONTAL &&
3956 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3957 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3958 MovDir[x][y] = MV_NO_MOVING;
3962 else if (element == EL_ROBOT ||
3963 element == EL_SATELLITE ||
3964 element == EL_PENGUIN)
3966 int attr_x = -1, attr_y = -1;
3977 for (i = 0; i < MAX_PLAYERS; i++)
3979 struct PlayerInfo *player = &stored_player[i];
3980 int jx = player->jx, jy = player->jy;
3982 if (!player->active)
3986 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3994 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4000 if (element == EL_PENGUIN)
4003 static int xy[4][2] =
4011 for (i = 0; i < 4; i++)
4013 int ex = x + xy[i % 4][0];
4014 int ey = y + xy[i % 4][1];
4016 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4025 MovDir[x][y] = MV_NO_MOVING;
4027 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4028 else if (attr_x > x)
4029 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4031 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4032 else if (attr_y > y)
4033 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4035 if (element == EL_ROBOT)
4039 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4040 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4041 Moving2Blocked(x, y, &newx, &newy);
4043 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4044 MovDelay[x][y] = 8 + 8 * !RND(3);
4046 MovDelay[x][y] = 16;
4048 else if (element == EL_PENGUIN)
4054 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4056 boolean first_horiz = RND(2);
4057 int new_move_dir = MovDir[x][y];
4060 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4061 Moving2Blocked(x, y, &newx, &newy);
4063 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4067 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4068 Moving2Blocked(x, y, &newx, &newy);
4070 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4073 MovDir[x][y] = old_move_dir;
4077 else /* (element == EL_SATELLITE) */
4083 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4085 boolean first_horiz = RND(2);
4086 int new_move_dir = MovDir[x][y];
4089 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4090 Moving2Blocked(x, y, &newx, &newy);
4092 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4096 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4097 Moving2Blocked(x, y, &newx, &newy);
4099 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4102 MovDir[x][y] = old_move_dir;
4107 else if (move_pattern == MV_TURNING_LEFT ||
4108 move_pattern == MV_TURNING_RIGHT ||
4109 move_pattern == MV_TURNING_LEFT_RIGHT ||
4110 move_pattern == MV_TURNING_RIGHT_LEFT ||
4111 move_pattern == MV_TURNING_RANDOM ||
4112 move_pattern == MV_ALL_DIRECTIONS)
4114 boolean can_turn_left =
4115 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4116 boolean can_turn_right =
4117 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4119 if (move_pattern == MV_TURNING_LEFT)
4120 MovDir[x][y] = left_dir;
4121 else if (move_pattern == MV_TURNING_RIGHT)
4122 MovDir[x][y] = right_dir;
4123 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4124 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4125 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4126 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4127 else if (move_pattern == MV_TURNING_RANDOM)
4128 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4129 can_turn_right && !can_turn_left ? right_dir :
4130 RND(2) ? left_dir : right_dir);
4131 else if (can_turn_left && can_turn_right)
4132 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4133 else if (can_turn_left)
4134 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4135 else if (can_turn_right)
4136 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4138 MovDir[x][y] = back_dir;
4140 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4142 else if (move_pattern == MV_HORIZONTAL ||
4143 move_pattern == MV_VERTICAL)
4145 if (move_pattern & old_move_dir)
4146 MovDir[x][y] = back_dir;
4147 else if (move_pattern == MV_HORIZONTAL)
4148 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4149 else if (move_pattern == MV_VERTICAL)
4150 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4152 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4154 else if (move_pattern & MV_ANY_DIRECTION)
4156 MovDir[x][y] = move_pattern;
4157 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4159 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4161 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4162 MovDir[x][y] = left_dir;
4163 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4164 MovDir[x][y] = right_dir;
4166 if (MovDir[x][y] != old_move_dir)
4167 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4169 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4171 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4172 MovDir[x][y] = right_dir;
4173 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4174 MovDir[x][y] = left_dir;
4176 if (MovDir[x][y] != old_move_dir)
4177 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4179 else if (move_pattern == MV_TOWARDS_PLAYER ||
4180 move_pattern == MV_AWAY_FROM_PLAYER)
4182 int attr_x = -1, attr_y = -1;
4184 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4195 for (i = 0; i < MAX_PLAYERS; i++)
4197 struct PlayerInfo *player = &stored_player[i];
4198 int jx = player->jx, jy = player->jy;
4200 if (!player->active)
4204 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4212 MovDir[x][y] = MV_NO_MOVING;
4214 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4215 else if (attr_x > x)
4216 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4218 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4219 else if (attr_y > y)
4220 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4222 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4224 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4226 boolean first_horiz = RND(2);
4227 int new_move_dir = MovDir[x][y];
4230 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4231 Moving2Blocked(x, y, &newx, &newy);
4233 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4237 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4238 Moving2Blocked(x, y, &newx, &newy);
4240 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4243 MovDir[x][y] = old_move_dir;
4246 else if (move_pattern == MV_WHEN_PUSHED ||
4247 move_pattern == MV_WHEN_DROPPED)
4249 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4250 MovDir[x][y] = MV_NO_MOVING;
4254 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4256 static int test_xy[7][2] =
4266 static int test_dir[7] =
4276 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4277 int move_preference = -1000000; /* start with very low preference */
4278 int new_move_dir = MV_NO_MOVING;
4279 int start_test = RND(4);
4282 for (i = 0; i < 4; i++)
4284 int move_dir = test_dir[start_test + i];
4285 int move_dir_preference;
4287 xx = x + test_xy[start_test + i][0];
4288 yy = y + test_xy[start_test + i][1];
4290 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4291 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4293 new_move_dir = move_dir;
4298 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4301 move_dir_preference = -1 * RunnerVisit[xx][yy];
4302 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4303 move_dir_preference = PlayerVisit[xx][yy];
4305 if (move_dir_preference > move_preference)
4307 /* prefer field that has not been visited for the longest time */
4308 move_preference = move_dir_preference;
4309 new_move_dir = move_dir;
4311 else if (move_dir_preference == move_preference &&
4312 move_dir == old_move_dir)
4314 /* prefer last direction when all directions are preferred equally */
4315 move_preference = move_dir_preference;
4316 new_move_dir = move_dir;
4320 MovDir[x][y] = new_move_dir;
4321 if (old_move_dir != new_move_dir)
4326 static void TurnRound(int x, int y)
4328 int direction = MovDir[x][y];
4331 GfxDir[x][y] = MovDir[x][y];
4337 GfxDir[x][y] = MovDir[x][y];
4340 if (direction != MovDir[x][y])
4345 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4348 GfxAction[x][y] = ACTION_WAITING;
4352 static boolean JustBeingPushed(int x, int y)
4356 for (i = 0; i < MAX_PLAYERS; i++)
4358 struct PlayerInfo *player = &stored_player[i];
4360 if (player->active && player->is_pushing && player->MovPos)
4362 int next_jx = player->jx + (player->jx - player->last_jx);
4363 int next_jy = player->jy + (player->jy - player->last_jy);
4365 if (x == next_jx && y == next_jy)
4373 void StartMoving(int x, int y)
4376 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4378 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4379 int element = Feld[x][y];
4385 if (MovDelay[x][y] == 0)
4386 GfxAction[x][y] = ACTION_DEFAULT;
4388 /* !!! this should be handled more generic (not only for mole) !!! */
4389 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4390 GfxAction[x][y] = ACTION_DEFAULT;
4393 if (CAN_FALL(element) && y < lev_fieldy - 1)
4395 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4396 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4397 if (JustBeingPushed(x, y))
4400 if (element == EL_QUICKSAND_FULL)
4402 if (IS_FREE(x, y + 1))
4404 InitMovingField(x, y, MV_DOWN);
4405 started_moving = TRUE;
4407 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4408 Store[x][y] = EL_ROCK;
4410 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4412 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4415 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4417 if (!MovDelay[x][y])
4418 MovDelay[x][y] = TILEY + 1;
4427 Feld[x][y] = EL_QUICKSAND_EMPTY;
4428 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4429 Store[x][y + 1] = Store[x][y];
4432 PlayLevelSoundAction(x, y, ACTION_FILLING);
4434 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4438 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4439 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4441 InitMovingField(x, y, MV_DOWN);
4442 started_moving = TRUE;
4444 Feld[x][y] = EL_QUICKSAND_FILLING;
4445 Store[x][y] = element;
4447 PlayLevelSoundAction(x, y, ACTION_FILLING);
4449 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4452 else if (element == EL_MAGIC_WALL_FULL)
4454 if (IS_FREE(x, y + 1))
4456 InitMovingField(x, y, MV_DOWN);
4457 started_moving = TRUE;
4459 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4460 Store[x][y] = EL_CHANGED(Store[x][y]);
4462 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4464 if (!MovDelay[x][y])
4465 MovDelay[x][y] = TILEY/4 + 1;
4474 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4475 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4476 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4480 else if (element == EL_BD_MAGIC_WALL_FULL)
4482 if (IS_FREE(x, y + 1))
4484 InitMovingField(x, y, MV_DOWN);
4485 started_moving = TRUE;
4487 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4488 Store[x][y] = EL_CHANGED2(Store[x][y]);
4490 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4492 if (!MovDelay[x][y])
4493 MovDelay[x][y] = TILEY/4 + 1;
4502 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4503 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4504 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4508 else if (CAN_PASS_MAGIC_WALL(element) &&
4509 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4510 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4512 InitMovingField(x, y, MV_DOWN);
4513 started_moving = TRUE;
4516 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4517 EL_BD_MAGIC_WALL_FILLING);
4518 Store[x][y] = element;
4521 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4523 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4528 InitMovingField(x, y, MV_DOWN);
4529 started_moving = TRUE;
4531 Store[x][y] = EL_ACID;
4533 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4534 GfxAction[x][y + 1] = ACTION_ACTIVE;
4538 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4539 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4540 (Feld[x][y + 1] == EL_BLOCKED)) ||
4541 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4542 CAN_SMASH(element) && WasJustFalling[x][y] &&
4543 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4547 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4548 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4549 WasJustMoving[x][y] && !Pushed[x][y + 1])
4551 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4552 WasJustMoving[x][y])
4557 /* this is needed for a special case not covered by calling "Impact()"
4558 from "ContinueMoving()": if an element moves to a tile directly below
4559 another element which was just falling on that tile (which was empty
4560 in the previous frame), the falling element above would just stop
4561 instead of smashing the element below (in previous version, the above
4562 element was just checked for "moving" instead of "falling", resulting
4563 in incorrect smashes caused by horizontal movement of the above
4564 element; also, the case of the player being the element to smash was
4565 simply not covered here... :-/ ) */
4568 WasJustMoving[x][y] = 0;
4569 WasJustFalling[x][y] = 0;
4574 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4576 if (MovDir[x][y] == MV_NO_MOVING)
4578 InitMovingField(x, y, MV_DOWN);
4579 started_moving = TRUE;
4582 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4584 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4585 MovDir[x][y] = MV_DOWN;
4587 InitMovingField(x, y, MV_DOWN);
4588 started_moving = TRUE;
4590 else if (element == EL_AMOEBA_DROP)
4592 Feld[x][y] = EL_AMOEBA_GROWING;
4593 Store[x][y] = EL_AMOEBA_WET;
4595 /* Store[x][y + 1] must be zero, because:
4596 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4599 #if OLD_GAME_BEHAVIOUR
4600 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4602 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4603 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4604 element != EL_DX_SUPABOMB)
4607 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4608 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4609 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4610 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4613 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4614 (IS_FREE(x - 1, y + 1) ||
4615 Feld[x - 1][y + 1] == EL_ACID));
4616 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4617 (IS_FREE(x + 1, y + 1) ||
4618 Feld[x + 1][y + 1] == EL_ACID));
4619 boolean can_fall_any = (can_fall_left || can_fall_right);
4620 boolean can_fall_both = (can_fall_left && can_fall_right);
4622 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4624 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4626 if (slippery_type == SLIPPERY_ONLY_LEFT)
4627 can_fall_right = FALSE;
4628 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4629 can_fall_left = FALSE;
4630 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4631 can_fall_right = FALSE;
4632 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4633 can_fall_left = FALSE;
4635 can_fall_any = (can_fall_left || can_fall_right);
4636 can_fall_both = (can_fall_left && can_fall_right);
4641 if (can_fall_both &&
4642 (game.emulation != EMU_BOULDERDASH &&
4643 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4644 can_fall_left = !(can_fall_right = RND(2));
4646 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4647 started_moving = TRUE;
4651 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4653 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4656 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4657 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4658 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4659 int belt_dir = game.belt_dir[belt_nr];
4661 if ((belt_dir == MV_LEFT && left_is_free) ||
4662 (belt_dir == MV_RIGHT && right_is_free))
4665 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4668 InitMovingField(x, y, belt_dir);
4669 started_moving = TRUE;
4672 Pushed[x][y] = TRUE;
4673 Pushed[nextx][y] = TRUE;
4676 GfxAction[x][y] = ACTION_DEFAULT;
4680 MovDir[x][y] = 0; /* if element was moving, stop it */
4685 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4686 if (CAN_MOVE(element) && !started_moving)
4688 int move_pattern = element_info[element].move_pattern;
4691 Moving2Blocked(x, y, &newx, &newy);
4694 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4697 if ((element == EL_SATELLITE ||
4698 element == EL_BALLOON ||
4699 element == EL_SPRING)
4700 && JustBeingPushed(x, y))
4705 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4706 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4707 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4710 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4711 element, element_info[element].token_name,
4712 WasJustMoving[x][y],
4713 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4714 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4715 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4716 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4720 WasJustMoving[x][y] = 0;
4723 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4726 if (Feld[x][y] != element) /* element has changed */
4728 element = Feld[x][y];
4729 move_pattern = element_info[element].move_pattern;
4731 if (!CAN_MOVE(element))
4735 if (Feld[x][y] != element) /* element has changed */
4743 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4744 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4746 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4748 Moving2Blocked(x, y, &newx, &newy);
4749 if (Feld[newx][newy] == EL_BLOCKED)
4750 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4756 if (FrameCounter < 1 && x == 0 && y == 29)
4757 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4760 if (!MovDelay[x][y]) /* start new movement phase */
4762 /* all objects that can change their move direction after each step
4763 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4765 if (element != EL_YAMYAM &&
4766 element != EL_DARK_YAMYAM &&
4767 element != EL_PACMAN &&
4768 !(move_pattern & MV_ANY_DIRECTION) &&
4769 move_pattern != MV_TURNING_LEFT &&
4770 move_pattern != MV_TURNING_RIGHT &&
4771 move_pattern != MV_TURNING_LEFT_RIGHT &&
4772 move_pattern != MV_TURNING_RIGHT_LEFT &&
4773 move_pattern != MV_TURNING_RANDOM)
4778 if (FrameCounter < 1 && x == 0 && y == 29)
4779 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4782 if (MovDelay[x][y] && (element == EL_BUG ||
4783 element == EL_SPACESHIP ||
4784 element == EL_SP_SNIKSNAK ||
4785 element == EL_SP_ELECTRON ||
4786 element == EL_MOLE))
4787 DrawLevelField(x, y);
4791 if (MovDelay[x][y]) /* wait some time before next movement */
4796 if (element == EL_YAMYAM)
4799 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4800 DrawLevelElementAnimation(x, y, element);
4804 if (MovDelay[x][y]) /* element still has to wait some time */
4807 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4808 ResetGfxAnimation(x, y);
4812 if (GfxAction[x][y] != ACTION_WAITING)
4813 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4815 GfxAction[x][y] = ACTION_WAITING;
4819 if (element == EL_ROBOT ||
4821 element == EL_PACMAN ||
4823 element == EL_YAMYAM ||
4824 element == EL_DARK_YAMYAM)
4827 DrawLevelElementAnimation(x, y, element);
4829 DrawLevelElementAnimationIfNeeded(x, y, element);
4831 PlayLevelSoundAction(x, y, ACTION_WAITING);
4833 else if (element == EL_SP_ELECTRON)
4834 DrawLevelElementAnimationIfNeeded(x, y, element);
4835 else if (element == EL_DRAGON)
4838 int dir = MovDir[x][y];
4839 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4840 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4841 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4842 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4843 dir == MV_UP ? IMG_FLAMES_1_UP :
4844 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4845 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4848 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4851 GfxAction[x][y] = ACTION_ATTACKING;
4853 if (IS_PLAYER(x, y))
4854 DrawPlayerField(x, y);
4856 DrawLevelField(x, y);
4858 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4860 for (i = 1; i <= 3; i++)
4862 int xx = x + i * dx;
4863 int yy = y + i * dy;
4864 int sx = SCREENX(xx);
4865 int sy = SCREENY(yy);
4866 int flame_graphic = graphic + (i - 1);
4868 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4873 int flamed = MovingOrBlocked2Element(xx, yy);
4875 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4878 RemoveMovingField(xx, yy);
4880 Feld[xx][yy] = EL_FLAMES;
4881 if (IN_SCR_FIELD(sx, sy))
4883 DrawLevelFieldCrumbledSand(xx, yy);
4884 DrawGraphic(sx, sy, flame_graphic, frame);
4889 if (Feld[xx][yy] == EL_FLAMES)
4890 Feld[xx][yy] = EL_EMPTY;
4891 DrawLevelField(xx, yy);
4896 if (MovDelay[x][y]) /* element still has to wait some time */
4898 PlayLevelSoundAction(x, y, ACTION_WAITING);
4904 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4905 for all other elements GfxAction will be set by InitMovingField() */
4906 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4907 GfxAction[x][y] = ACTION_MOVING;
4911 /* now make next step */
4913 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4915 if (DONT_COLLIDE_WITH(element) &&
4916 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4917 !PLAYER_ENEMY_PROTECTED(newx, newy))
4920 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4924 /* player killed by element which is deadly when colliding with */
4926 KillHero(PLAYERINFO(newx, newy));
4932 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
4933 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
4937 else if ((element == EL_PENGUIN ||
4938 element == EL_ROBOT ||
4939 element == EL_SATELLITE ||
4940 element == EL_BALLOON ||
4941 IS_CUSTOM_ELEMENT(element)) &&
4942 IN_LEV_FIELD(newx, newy) &&
4943 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4947 Store[x][y] = EL_ACID;
4949 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4951 if (Feld[newx][newy] == EL_EXIT_OPEN)
4955 DrawLevelField(x, y);
4957 Feld[x][y] = EL_EMPTY;
4958 DrawLevelField(x, y);
4961 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4962 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4963 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4965 local_player->friends_still_needed--;
4966 if (!local_player->friends_still_needed &&
4967 !local_player->GameOver && AllPlayersGone)
4968 local_player->LevelSolved = local_player->GameOver = TRUE;
4972 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4974 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
4975 DrawLevelField(newx, newy);
4977 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4979 else if (!IS_FREE(newx, newy))
4981 GfxAction[x][y] = ACTION_WAITING;
4983 if (IS_PLAYER(x, y))
4984 DrawPlayerField(x, y);
4986 DrawLevelField(x, y);
4991 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4993 if (IS_FOOD_PIG(Feld[newx][newy]))
4995 if (IS_MOVING(newx, newy))
4996 RemoveMovingField(newx, newy);
4999 Feld[newx][newy] = EL_EMPTY;
5000 DrawLevelField(newx, newy);
5003 PlayLevelSound(x, y, SND_PIG_DIGGING);
5005 else if (!IS_FREE(newx, newy))
5007 if (IS_PLAYER(x, y))
5008 DrawPlayerField(x, y);
5010 DrawLevelField(x, y);
5019 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5022 else if (IS_CUSTOM_ELEMENT(element) &&
5023 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5027 !IS_FREE(newx, newy)
5032 int new_element = Feld[newx][newy];
5035 printf("::: '%s' digs '%s' [%d]\n",
5036 element_info[element].token_name,
5037 element_info[Feld[newx][newy]].token_name,
5038 StorePlayer[newx][newy]);
5041 if (!IS_FREE(newx, newy))
5043 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5044 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5047 /* no element can dig solid indestructible elements */
5048 if (IS_INDESTRUCTIBLE(new_element) &&
5049 !IS_DIGGABLE(new_element) &&
5050 !IS_COLLECTIBLE(new_element))
5053 if (AmoebaNr[newx][newy] &&
5054 (new_element == EL_AMOEBA_FULL ||
5055 new_element == EL_BD_AMOEBA ||
5056 new_element == EL_AMOEBA_GROWING))
5058 AmoebaCnt[AmoebaNr[newx][newy]]--;
5059 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5062 if (IS_MOVING(newx, newy))
5063 RemoveMovingField(newx, newy);
5066 RemoveField(newx, newy);
5067 DrawLevelField(newx, newy);
5070 PlayLevelSoundAction(x, y, action);
5073 if (new_element == element_info[element].move_enter_element)
5074 element_info[element].can_leave_element = TRUE;
5076 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5078 RunnerVisit[x][y] = FrameCounter;
5079 PlayerVisit[x][y] /= 8; /* expire player visit path */
5085 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5087 if (!IS_FREE(newx, newy))
5089 if (IS_PLAYER(x, y))
5090 DrawPlayerField(x, y);
5092 DrawLevelField(x, y);
5098 boolean wanna_flame = !RND(10);
5099 int dx = newx - x, dy = newy - y;
5100 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5101 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5102 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5103 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5104 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5105 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5108 IS_CLASSIC_ENEMY(element1) ||
5109 IS_CLASSIC_ENEMY(element2)) &&
5110 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5111 element1 != EL_FLAMES && element2 != EL_FLAMES)
5114 ResetGfxAnimation(x, y);
5115 GfxAction[x][y] = ACTION_ATTACKING;
5118 if (IS_PLAYER(x, y))
5119 DrawPlayerField(x, y);
5121 DrawLevelField(x, y);
5123 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5125 MovDelay[x][y] = 50;
5127 Feld[newx][newy] = EL_FLAMES;
5128 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5129 Feld[newx1][newy1] = EL_FLAMES;
5130 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5131 Feld[newx2][newy2] = EL_FLAMES;
5137 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5138 Feld[newx][newy] == EL_DIAMOND)
5140 if (IS_MOVING(newx, newy))
5141 RemoveMovingField(newx, newy);
5144 Feld[newx][newy] = EL_EMPTY;
5145 DrawLevelField(newx, newy);
5148 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5150 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5151 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5153 if (AmoebaNr[newx][newy])
5155 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5156 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5157 Feld[newx][newy] == EL_BD_AMOEBA)
5158 AmoebaCnt[AmoebaNr[newx][newy]]--;
5161 if (IS_MOVING(newx, newy))
5162 RemoveMovingField(newx, newy);
5165 Feld[newx][newy] = EL_EMPTY;
5166 DrawLevelField(newx, newy);
5169 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5171 else if ((element == EL_PACMAN || element == EL_MOLE)
5172 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5174 if (AmoebaNr[newx][newy])
5176 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5177 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5178 Feld[newx][newy] == EL_BD_AMOEBA)
5179 AmoebaCnt[AmoebaNr[newx][newy]]--;
5182 if (element == EL_MOLE)
5184 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5185 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5187 ResetGfxAnimation(x, y);
5188 GfxAction[x][y] = ACTION_DIGGING;
5189 DrawLevelField(x, y);
5191 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5193 return; /* wait for shrinking amoeba */
5195 else /* element == EL_PACMAN */
5197 Feld[newx][newy] = EL_EMPTY;
5198 DrawLevelField(newx, newy);
5199 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5202 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5203 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5204 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5206 /* wait for shrinking amoeba to completely disappear */
5209 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5211 /* object was running against a wall */
5216 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5217 DrawLevelElementAnimation(x, y, element);
5219 if (element == EL_BUG ||
5220 element == EL_SPACESHIP ||
5221 element == EL_SP_SNIKSNAK)
5222 DrawLevelField(x, y);
5223 else if (element == EL_MOLE)
5224 DrawLevelField(x, y);
5225 else if (element == EL_BD_BUTTERFLY ||
5226 element == EL_BD_FIREFLY)
5227 DrawLevelElementAnimationIfNeeded(x, y, element);
5228 else if (element == EL_SATELLITE)
5229 DrawLevelElementAnimationIfNeeded(x, y, element);
5230 else if (element == EL_SP_ELECTRON)
5231 DrawLevelElementAnimationIfNeeded(x, y, element);
5234 if (DONT_TOUCH(element))
5235 TestIfBadThingTouchesHero(x, y);
5238 PlayLevelSoundAction(x, y, ACTION_WAITING);
5244 InitMovingField(x, y, MovDir[x][y]);
5246 PlayLevelSoundAction(x, y, ACTION_MOVING);
5250 ContinueMoving(x, y);
5253 void ContinueMoving(int x, int y)
5255 int element = Feld[x][y];
5256 struct ElementInfo *ei = &element_info[element];
5257 int direction = MovDir[x][y];
5258 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5259 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5260 int newx = x + dx, newy = y + dy;
5262 int nextx = newx + dx, nexty = newy + dy;
5265 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5266 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5268 boolean pushed_by_player = Pushed[x][y];
5271 MovPos[x][y] += getElementMoveStepsize(x, y);
5274 if (pushed_by_player && IS_PLAYER(x, y))
5276 /* special case: moving object pushed by player */
5277 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5280 if (pushed_by_player) /* special case: moving object pushed by player */
5281 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5284 if (ABS(MovPos[x][y]) < TILEX)
5286 DrawLevelField(x, y);
5288 return; /* element is still moving */
5291 /* element reached destination field */
5293 Feld[x][y] = EL_EMPTY;
5294 Feld[newx][newy] = element;
5295 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5297 if (element == EL_MOLE)
5299 Feld[x][y] = EL_SAND;
5301 DrawLevelFieldCrumbledSandNeighbours(x, y);
5303 else if (element == EL_QUICKSAND_FILLING)
5305 element = Feld[newx][newy] = get_next_element(element);
5306 Store[newx][newy] = Store[x][y];
5308 else if (element == EL_QUICKSAND_EMPTYING)
5310 Feld[x][y] = get_next_element(element);
5311 element = Feld[newx][newy] = Store[x][y];
5313 else if (element == EL_MAGIC_WALL_FILLING)
5315 element = Feld[newx][newy] = get_next_element(element);
5316 if (!game.magic_wall_active)
5317 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5318 Store[newx][newy] = Store[x][y];
5320 else if (element == EL_MAGIC_WALL_EMPTYING)
5322 Feld[x][y] = get_next_element(element);
5323 if (!game.magic_wall_active)
5324 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5325 element = Feld[newx][newy] = Store[x][y];
5327 else if (element == EL_BD_MAGIC_WALL_FILLING)
5329 element = Feld[newx][newy] = get_next_element(element);
5330 if (!game.magic_wall_active)
5331 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5332 Store[newx][newy] = Store[x][y];
5334 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5336 Feld[x][y] = get_next_element(element);
5337 if (!game.magic_wall_active)
5338 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5339 element = Feld[newx][newy] = Store[x][y];
5341 else if (element == EL_AMOEBA_DROPPING)
5343 Feld[x][y] = get_next_element(element);
5344 element = Feld[newx][newy] = Store[x][y];
5346 else if (element == EL_SOKOBAN_OBJECT)
5349 Feld[x][y] = Back[x][y];
5351 if (Back[newx][newy])
5352 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5354 Back[x][y] = Back[newx][newy] = 0;
5356 else if (Store[x][y] == EL_ACID)
5358 element = Feld[newx][newy] = EL_ACID;
5362 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5363 MovDelay[newx][newy] = 0;
5365 /* copy element change control values to new field */
5366 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5367 ChangePage[newx][newy] = ChangePage[x][y];
5368 Changed[newx][newy] = Changed[x][y];
5369 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5371 ChangeDelay[x][y] = 0;
5372 ChangePage[x][y] = -1;
5373 Changed[x][y] = CE_BITMASK_DEFAULT;
5374 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5376 /* copy animation control values to new field */
5377 GfxFrame[newx][newy] = GfxFrame[x][y];
5378 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5379 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5380 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5382 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5384 ResetGfxAnimation(x, y); /* reset animation values for old field */
5387 /* some elements can leave other elements behind after moving */
5388 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5389 ei->move_leave_element != EL_EMPTY &&
5390 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5391 ei->can_leave_element_last))
5393 Feld[x][y] = ei->move_leave_element;
5394 InitField(x, y, FALSE);
5396 if (GFX_CRUMBLED(Feld[x][y]))
5397 DrawLevelFieldCrumbledSandNeighbours(x, y);
5400 ei->can_leave_element_last = ei->can_leave_element;
5401 ei->can_leave_element = FALSE;
5405 /* 2.1.1 (does not work correctly for spring) */
5406 if (!CAN_MOVE(element))
5407 MovDir[newx][newy] = 0;
5411 /* (does not work for falling objects that slide horizontally) */
5412 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5413 MovDir[newx][newy] = 0;
5416 if (!CAN_MOVE(element) ||
5417 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5418 MovDir[newx][newy] = 0;
5421 if (!CAN_MOVE(element) ||
5422 (CAN_FALL(element) && direction == MV_DOWN))
5423 GfxDir[x][y] = MovDir[newx][newy] = 0;
5428 DrawLevelField(x, y);
5429 DrawLevelField(newx, newy);
5431 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5433 /* prevent pushed element from moving on in pushed direction */
5434 if (pushed_by_player && CAN_MOVE(element) &&
5435 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5436 !(element_info[element].move_pattern & direction))
5437 TurnRound(newx, newy);
5440 /* prevent elements on conveyor belt from moving on in last direction */
5441 if (pushed_by_conveyor && CAN_FALL(element) &&
5442 direction & MV_HORIZONTAL)
5443 MovDir[newx][newy] = 0;
5446 if (!pushed_by_player)
5448 WasJustMoving[newx][newy] = 3;
5450 if (CAN_FALL(element) && direction == MV_DOWN)
5451 WasJustFalling[newx][newy] = 3;
5454 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5456 TestIfBadThingTouchesHero(newx, newy);
5457 TestIfBadThingTouchesFriend(newx, newy);
5459 if (!IS_CUSTOM_ELEMENT(element))
5460 TestIfBadThingTouchesOtherBadThing(newx, newy);
5462 else if (element == EL_PENGUIN)
5463 TestIfFriendTouchesBadThing(newx, newy);
5465 if (CAN_FALL(element) && direction == MV_DOWN &&
5466 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5470 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5474 if (ChangePage[newx][newy] != -1) /* delayed change */
5475 ChangeElement(newx, newy, ChangePage[newx][newy]);
5480 TestIfElementHitsCustomElement(newx, newy, direction);
5484 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5486 int hitting_element = Feld[newx][newy];
5488 /* !!! fix side (direction) orientation here and elsewhere !!! */
5489 CheckElementSideChange(newx, newy, hitting_element,
5490 direction, CE_HITTING_SOMETHING, -1);
5493 if (IN_LEV_FIELD(nextx, nexty))
5495 int opposite_direction = MV_DIR_OPPOSITE(direction);
5496 int hitting_side = direction;
5497 int touched_side = opposite_direction;
5498 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5499 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5500 MovDir[nextx][nexty] != direction ||
5501 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5507 CheckElementSideChange(nextx, nexty, touched_element,
5508 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5510 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5511 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5513 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5515 struct ElementChangeInfo *change =
5516 &element_info[hitting_element].change_page[i];
5518 if (change->can_change &&
5519 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5520 change->sides & touched_side &&
5521 change->trigger_element == touched_element)
5523 CheckElementSideChange(newx, newy, hitting_element,
5524 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5530 if (IS_CUSTOM_ELEMENT(touched_element) &&
5531 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5533 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5535 struct ElementChangeInfo *change =
5536 &element_info[touched_element].change_page[i];
5538 if (change->can_change &&
5539 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5540 change->sides & hitting_side &&
5541 change->trigger_element == hitting_element)
5543 CheckElementSideChange(nextx, nexty, touched_element,
5544 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5555 TestIfPlayerTouchesCustomElement(newx, newy);
5556 TestIfElementTouchesCustomElement(newx, newy);
5559 int AmoebeNachbarNr(int ax, int ay)
5562 int element = Feld[ax][ay];
5564 static int xy[4][2] =
5572 for (i = 0; i < 4; i++)
5574 int x = ax + xy[i][0];
5575 int y = ay + xy[i][1];
5577 if (!IN_LEV_FIELD(x, y))
5580 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5581 group_nr = AmoebaNr[x][y];
5587 void AmoebenVereinigen(int ax, int ay)
5589 int i, x, y, xx, yy;
5590 int new_group_nr = AmoebaNr[ax][ay];
5591 static int xy[4][2] =
5599 if (new_group_nr == 0)
5602 for (i = 0; i < 4; i++)
5607 if (!IN_LEV_FIELD(x, y))
5610 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5611 Feld[x][y] == EL_BD_AMOEBA ||
5612 Feld[x][y] == EL_AMOEBA_DEAD) &&
5613 AmoebaNr[x][y] != new_group_nr)
5615 int old_group_nr = AmoebaNr[x][y];
5617 if (old_group_nr == 0)
5620 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5621 AmoebaCnt[old_group_nr] = 0;
5622 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5623 AmoebaCnt2[old_group_nr] = 0;
5625 for (yy = 0; yy < lev_fieldy; yy++)
5627 for (xx = 0; xx < lev_fieldx; xx++)
5629 if (AmoebaNr[xx][yy] == old_group_nr)
5630 AmoebaNr[xx][yy] = new_group_nr;
5637 void AmoebeUmwandeln(int ax, int ay)
5641 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5643 int group_nr = AmoebaNr[ax][ay];
5648 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5649 printf("AmoebeUmwandeln(): This should never happen!\n");
5654 for (y = 0; y < lev_fieldy; y++)
5656 for (x = 0; x < lev_fieldx; x++)
5658 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5661 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5665 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5666 SND_AMOEBA_TURNING_TO_GEM :
5667 SND_AMOEBA_TURNING_TO_ROCK));
5672 static int xy[4][2] =
5680 for (i = 0; i < 4; i++)
5685 if (!IN_LEV_FIELD(x, y))
5688 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5690 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5691 SND_AMOEBA_TURNING_TO_GEM :
5692 SND_AMOEBA_TURNING_TO_ROCK));
5699 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5702 int group_nr = AmoebaNr[ax][ay];
5703 boolean done = FALSE;
5708 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5709 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5714 for (y = 0; y < lev_fieldy; y++)
5716 for (x = 0; x < lev_fieldx; x++)
5718 if (AmoebaNr[x][y] == group_nr &&
5719 (Feld[x][y] == EL_AMOEBA_DEAD ||
5720 Feld[x][y] == EL_BD_AMOEBA ||
5721 Feld[x][y] == EL_AMOEBA_GROWING))
5724 Feld[x][y] = new_element;
5725 InitField(x, y, FALSE);
5726 DrawLevelField(x, y);
5733 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5734 SND_BD_AMOEBA_TURNING_TO_ROCK :
5735 SND_BD_AMOEBA_TURNING_TO_GEM));
5738 void AmoebeWaechst(int x, int y)
5740 static unsigned long sound_delay = 0;
5741 static unsigned long sound_delay_value = 0;
5743 if (!MovDelay[x][y]) /* start new growing cycle */
5747 if (DelayReached(&sound_delay, sound_delay_value))
5750 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5752 if (Store[x][y] == EL_BD_AMOEBA)
5753 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5755 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5757 sound_delay_value = 30;
5761 if (MovDelay[x][y]) /* wait some time before growing bigger */
5764 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5766 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5767 6 - MovDelay[x][y]);
5769 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5772 if (!MovDelay[x][y])
5774 Feld[x][y] = Store[x][y];
5776 DrawLevelField(x, y);
5781 void AmoebaDisappearing(int x, int y)
5783 static unsigned long sound_delay = 0;
5784 static unsigned long sound_delay_value = 0;
5786 if (!MovDelay[x][y]) /* start new shrinking cycle */
5790 if (DelayReached(&sound_delay, sound_delay_value))
5791 sound_delay_value = 30;
5794 if (MovDelay[x][y]) /* wait some time before shrinking */
5797 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5799 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5800 6 - MovDelay[x][y]);
5802 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5805 if (!MovDelay[x][y])
5807 Feld[x][y] = EL_EMPTY;
5808 DrawLevelField(x, y);
5810 /* don't let mole enter this field in this cycle;
5811 (give priority to objects falling to this field from above) */
5817 void AmoebeAbleger(int ax, int ay)
5820 int element = Feld[ax][ay];
5821 int graphic = el2img(element);
5822 int newax = ax, neway = ay;
5823 static int xy[4][2] =
5831 if (!level.amoeba_speed)
5833 Feld[ax][ay] = EL_AMOEBA_DEAD;
5834 DrawLevelField(ax, ay);
5838 if (IS_ANIMATED(graphic))
5839 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5841 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5842 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5844 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5847 if (MovDelay[ax][ay])
5851 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5854 int x = ax + xy[start][0];
5855 int y = ay + xy[start][1];
5857 if (!IN_LEV_FIELD(x, y))
5860 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5861 if (IS_FREE(x, y) ||
5862 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5868 if (newax == ax && neway == ay)
5871 else /* normal or "filled" (BD style) amoeba */
5874 boolean waiting_for_player = FALSE;
5876 for (i = 0; i < 4; i++)
5878 int j = (start + i) % 4;
5879 int x = ax + xy[j][0];
5880 int y = ay + xy[j][1];
5882 if (!IN_LEV_FIELD(x, y))
5885 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5886 if (IS_FREE(x, y) ||
5887 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5893 else if (IS_PLAYER(x, y))
5894 waiting_for_player = TRUE;
5897 if (newax == ax && neway == ay) /* amoeba cannot grow */
5899 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5901 Feld[ax][ay] = EL_AMOEBA_DEAD;
5902 DrawLevelField(ax, ay);
5903 AmoebaCnt[AmoebaNr[ax][ay]]--;
5905 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5907 if (element == EL_AMOEBA_FULL)
5908 AmoebeUmwandeln(ax, ay);
5909 else if (element == EL_BD_AMOEBA)
5910 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5915 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5917 /* amoeba gets larger by growing in some direction */
5919 int new_group_nr = AmoebaNr[ax][ay];
5922 if (new_group_nr == 0)
5924 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5925 printf("AmoebeAbleger(): This should never happen!\n");
5930 AmoebaNr[newax][neway] = new_group_nr;
5931 AmoebaCnt[new_group_nr]++;
5932 AmoebaCnt2[new_group_nr]++;
5934 /* if amoeba touches other amoeba(s) after growing, unify them */
5935 AmoebenVereinigen(newax, neway);
5937 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5939 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5945 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5946 (neway == lev_fieldy - 1 && newax != ax))
5948 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5949 Store[newax][neway] = element;
5951 else if (neway == ay)
5953 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5955 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5957 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5962 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5963 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5964 Store[ax][ay] = EL_AMOEBA_DROP;
5965 ContinueMoving(ax, ay);
5969 DrawLevelField(newax, neway);
5972 void Life(int ax, int ay)
5975 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5977 int element = Feld[ax][ay];
5978 int graphic = el2img(element);
5979 boolean changed = FALSE;
5981 if (IS_ANIMATED(graphic))
5982 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5987 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5988 MovDelay[ax][ay] = life_time;
5990 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5993 if (MovDelay[ax][ay])
5997 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5999 int xx = ax+x1, yy = ay+y1;
6002 if (!IN_LEV_FIELD(xx, yy))
6005 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6007 int x = xx+x2, y = yy+y2;
6009 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6012 if (((Feld[x][y] == element ||
6013 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6015 (IS_FREE(x, y) && Stop[x][y]))
6019 if (xx == ax && yy == ay) /* field in the middle */
6021 if (nachbarn < life[0] || nachbarn > life[1])
6023 Feld[xx][yy] = EL_EMPTY;
6025 DrawLevelField(xx, yy);
6026 Stop[xx][yy] = TRUE;
6030 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6031 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6032 { /* free border field */
6033 if (nachbarn >= life[2] && nachbarn <= life[3])
6035 Feld[xx][yy] = element;
6036 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6038 DrawLevelField(xx, yy);
6039 Stop[xx][yy] = TRUE;
6046 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6047 SND_GAME_OF_LIFE_GROWING);
6050 static void InitRobotWheel(int x, int y)
6052 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6055 static void RunRobotWheel(int x, int y)
6057 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6060 static void StopRobotWheel(int x, int y)
6062 if (ZX == x && ZY == y)
6066 static void InitTimegateWheel(int x, int y)
6068 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6071 static void RunTimegateWheel(int x, int y)
6073 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6076 void CheckExit(int x, int y)
6078 if (local_player->gems_still_needed > 0 ||
6079 local_player->sokobanfields_still_needed > 0 ||
6080 local_player->lights_still_needed > 0)
6082 int element = Feld[x][y];
6083 int graphic = el2img(element);
6085 if (IS_ANIMATED(graphic))
6086 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6091 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6094 Feld[x][y] = EL_EXIT_OPENING;
6096 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6099 void CheckExitSP(int x, int y)
6101 if (local_player->gems_still_needed > 0)
6103 int element = Feld[x][y];
6104 int graphic = el2img(element);
6106 if (IS_ANIMATED(graphic))
6107 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6112 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6115 Feld[x][y] = EL_SP_EXIT_OPENING;
6117 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6120 static void CloseAllOpenTimegates()
6124 for (y = 0; y < lev_fieldy; y++)
6126 for (x = 0; x < lev_fieldx; x++)
6128 int element = Feld[x][y];
6130 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6132 Feld[x][y] = EL_TIMEGATE_CLOSING;
6134 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6136 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6143 void EdelsteinFunkeln(int x, int y)
6145 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6148 if (Feld[x][y] == EL_BD_DIAMOND)
6151 if (MovDelay[x][y] == 0) /* next animation frame */
6152 MovDelay[x][y] = 11 * !SimpleRND(500);
6154 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6158 if (setup.direct_draw && MovDelay[x][y])
6159 SetDrawtoField(DRAW_BUFFERED);
6161 DrawLevelElementAnimation(x, y, Feld[x][y]);
6163 if (MovDelay[x][y] != 0)
6165 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6166 10 - MovDelay[x][y]);
6168 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6170 if (setup.direct_draw)
6174 dest_x = FX + SCREENX(x) * TILEX;
6175 dest_y = FY + SCREENY(y) * TILEY;
6177 BlitBitmap(drawto_field, window,
6178 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6179 SetDrawtoField(DRAW_DIRECT);
6185 void MauerWaechst(int x, int y)
6189 if (!MovDelay[x][y]) /* next animation frame */
6190 MovDelay[x][y] = 3 * delay;
6192 if (MovDelay[x][y]) /* wait some time before next frame */
6196 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6198 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6199 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6201 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6204 if (!MovDelay[x][y])
6206 if (MovDir[x][y] == MV_LEFT)
6208 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6209 DrawLevelField(x - 1, y);
6211 else if (MovDir[x][y] == MV_RIGHT)
6213 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6214 DrawLevelField(x + 1, y);
6216 else if (MovDir[x][y] == MV_UP)
6218 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6219 DrawLevelField(x, y - 1);
6223 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6224 DrawLevelField(x, y + 1);
6227 Feld[x][y] = Store[x][y];
6229 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6230 DrawLevelField(x, y);
6235 void MauerAbleger(int ax, int ay)
6237 int element = Feld[ax][ay];
6238 int graphic = el2img(element);
6239 boolean oben_frei = FALSE, unten_frei = FALSE;
6240 boolean links_frei = FALSE, rechts_frei = FALSE;
6241 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6242 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6243 boolean new_wall = FALSE;
6245 if (IS_ANIMATED(graphic))
6246 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6248 if (!MovDelay[ax][ay]) /* start building new wall */
6249 MovDelay[ax][ay] = 6;
6251 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6254 if (MovDelay[ax][ay])
6258 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6260 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6262 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6264 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6267 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6268 element == EL_EXPANDABLE_WALL_ANY)
6272 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6273 Store[ax][ay-1] = element;
6274 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6275 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6276 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6277 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6282 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6283 Store[ax][ay+1] = element;
6284 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6285 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6286 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6287 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6292 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6293 element == EL_EXPANDABLE_WALL_ANY ||
6294 element == EL_EXPANDABLE_WALL)
6298 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6299 Store[ax-1][ay] = element;
6300 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6301 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6302 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6303 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6309 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6310 Store[ax+1][ay] = element;
6311 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6312 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6313 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6314 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6319 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6320 DrawLevelField(ax, ay);
6322 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6324 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6325 unten_massiv = TRUE;
6326 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6327 links_massiv = TRUE;
6328 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6329 rechts_massiv = TRUE;
6331 if (((oben_massiv && unten_massiv) ||
6332 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6333 element == EL_EXPANDABLE_WALL) &&
6334 ((links_massiv && rechts_massiv) ||
6335 element == EL_EXPANDABLE_WALL_VERTICAL))
6336 Feld[ax][ay] = EL_WALL;
6340 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6342 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6346 void CheckForDragon(int x, int y)
6349 boolean dragon_found = FALSE;
6350 static int xy[4][2] =
6358 for (i = 0; i < 4; i++)
6360 for (j = 0; j < 4; j++)
6362 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6364 if (IN_LEV_FIELD(xx, yy) &&
6365 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6367 if (Feld[xx][yy] == EL_DRAGON)
6368 dragon_found = TRUE;
6377 for (i = 0; i < 4; i++)
6379 for (j = 0; j < 3; j++)
6381 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6383 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6385 Feld[xx][yy] = EL_EMPTY;
6386 DrawLevelField(xx, yy);
6395 static void InitBuggyBase(int x, int y)
6397 int element = Feld[x][y];
6398 int activating_delay = FRAMES_PER_SECOND / 4;
6401 (element == EL_SP_BUGGY_BASE ?
6402 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6403 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6405 element == EL_SP_BUGGY_BASE_ACTIVE ?
6406 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6409 static void WarnBuggyBase(int x, int y)
6412 static int xy[4][2] =
6420 for (i = 0; i < 4; i++)
6422 int xx = x + xy[i][0], yy = y + xy[i][1];
6424 if (IS_PLAYER(xx, yy))
6426 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6433 static void InitTrap(int x, int y)
6435 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6438 static void ActivateTrap(int x, int y)
6440 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6443 static void ChangeActiveTrap(int x, int y)
6445 int graphic = IMG_TRAP_ACTIVE;
6447 /* if new animation frame was drawn, correct crumbled sand border */
6448 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6449 DrawLevelFieldCrumbledSand(x, y);
6452 static void ChangeElementNowExt(int x, int y, int target_element)
6454 int previous_move_direction = MovDir[x][y];
6456 /* check if element under player changes from accessible to unaccessible
6457 (needed for special case of dropping element which then changes) */
6458 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6459 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6466 Feld[x][y] = target_element;
6468 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6470 ResetGfxAnimation(x, y);
6471 ResetRandomAnimationValue(x, y);
6473 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6474 MovDir[x][y] = previous_move_direction;
6477 InitField_WithBug1(x, y, FALSE);
6479 InitField(x, y, FALSE);
6480 if (CAN_MOVE(Feld[x][y]))
6484 DrawLevelField(x, y);
6486 if (GFX_CRUMBLED(Feld[x][y]))
6487 DrawLevelFieldCrumbledSandNeighbours(x, y);
6489 TestIfBadThingTouchesHero(x, y);
6490 TestIfPlayerTouchesCustomElement(x, y);
6491 TestIfElementTouchesCustomElement(x, y);
6493 if (ELEM_IS_PLAYER(target_element))
6494 RelocatePlayer(x, y, target_element);
6497 static boolean ChangeElementNow(int x, int y, int element, int page)
6499 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6501 /* always use default change event to prevent running into a loop */
6502 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6503 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6505 /* do not change already changed elements with same change event */
6507 if (Changed[x][y] & ChangeEvent[x][y])
6514 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6516 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6518 if (change->explode)
6525 if (change->use_content)
6527 boolean complete_change = TRUE;
6528 boolean can_change[3][3];
6531 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6533 boolean half_destructible;
6534 int ex = x + xx - 1;
6535 int ey = y + yy - 1;
6538 can_change[xx][yy] = TRUE;
6540 if (ex == x && ey == y) /* do not check changing element itself */
6543 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6545 can_change[xx][yy] = FALSE; /* do not change empty borders */
6550 if (!IN_LEV_FIELD(ex, ey))
6552 can_change[xx][yy] = FALSE;
6553 complete_change = FALSE;
6560 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6561 e = MovingOrBlocked2Element(ex, ey);
6563 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6565 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6566 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6567 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6569 can_change[xx][yy] = FALSE;
6570 complete_change = FALSE;
6574 if (!change->only_complete || complete_change)
6576 boolean something_has_changed = FALSE;
6578 if (change->only_complete && change->use_random_change &&
6579 RND(100) < change->random)
6582 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6584 int ex = x + xx - 1;
6585 int ey = y + yy - 1;
6587 if (can_change[xx][yy] && (!change->use_random_change ||
6588 RND(100) < change->random))
6590 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6591 RemoveMovingField(ex, ey);
6593 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6595 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6597 something_has_changed = TRUE;
6599 /* for symmetry reasons, freeze newly created border elements */
6600 if (ex != x || ey != y)
6601 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6605 if (something_has_changed)
6606 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6611 ChangeElementNowExt(x, y, change->target_element);
6613 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6619 static void ChangeElement(int x, int y, int page)
6621 int element = MovingOrBlocked2Element(x, y);
6622 struct ElementInfo *ei = &element_info[element];
6623 struct ElementChangeInfo *change = &ei->change_page[page];
6627 if (!CAN_CHANGE(element))
6630 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6631 x, y, element, element_info[element].token_name);
6632 printf("ChangeElement(): This should never happen!\n");
6638 if (ChangeDelay[x][y] == 0) /* initialize element change */
6640 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6641 RND(change->delay_random * change->delay_frames)) + 1;
6643 ResetGfxAnimation(x, y);
6644 ResetRandomAnimationValue(x, y);
6646 if (change->pre_change_function)
6647 change->pre_change_function(x, y);
6650 ChangeDelay[x][y]--;
6652 if (ChangeDelay[x][y] != 0) /* continue element change */
6654 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6656 if (IS_ANIMATED(graphic))
6657 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6659 if (change->change_function)
6660 change->change_function(x, y);
6662 else /* finish element change */
6664 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6666 page = ChangePage[x][y];
6667 ChangePage[x][y] = -1;
6671 if (IS_MOVING(x, y) && !change->explode)
6673 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6676 ChangeDelay[x][y] = 1; /* try change after next move step */
6677 ChangePage[x][y] = page; /* remember page to use for change */
6682 if (ChangeElementNow(x, y, element, page))
6684 if (change->post_change_function)
6685 change->post_change_function(x, y);
6690 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6691 int trigger_element,
6697 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6700 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6702 int element = EL_CUSTOM_START + i;
6704 boolean change_element = FALSE;
6707 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6710 for (j = 0; j < element_info[element].num_change_pages; j++)
6712 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6714 if (change->can_change &&
6716 change->events & CH_EVENT_BIT(trigger_event) &&
6718 change->sides & trigger_side &&
6720 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6722 change->trigger_element == trigger_element
6727 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6728 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6729 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6732 change_element = TRUE;
6739 if (!change_element)
6742 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6745 if (x == lx && y == ly) /* do not change trigger element itself */
6749 if (Feld[x][y] == element)
6751 ChangeDelay[x][y] = 1;
6752 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6753 ChangeElement(x, y, page);
6761 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6764 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6768 static boolean CheckElementSideChange(int x, int y, int element, int side,
6769 int trigger_event, int page)
6771 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6774 if (Feld[x][y] == EL_BLOCKED)
6776 Blocked2Moving(x, y, &x, &y);
6777 element = Feld[x][y];
6783 boolean change_element = FALSE;
6786 for (i = 0; i < element_info[element].num_change_pages; i++)
6788 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6790 if (change->can_change &&
6791 change->events & CH_EVENT_BIT(trigger_event) &&
6792 change->sides & side)
6794 change_element = TRUE;
6801 if (!change_element)
6807 /* !!! this check misses pages with same event, but different side !!! */
6810 page = element_info[element].event_page_nr[trigger_event];
6812 if (!(element_info[element].change_page[page].sides & side))
6816 ChangeDelay[x][y] = 1;
6817 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6818 ChangeElement(x, y, page);
6823 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6825 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6828 static void PlayPlayerSound(struct PlayerInfo *player)
6830 int jx = player->jx, jy = player->jy;
6831 int element = player->element_nr;
6832 int last_action = player->last_action_waiting;
6833 int action = player->action_waiting;
6835 if (player->is_waiting)
6837 if (action != last_action)
6838 PlayLevelSoundElementAction(jx, jy, element, action);
6840 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6844 if (action != last_action)
6845 StopSound(element_info[element].sound[last_action]);
6847 if (last_action == ACTION_SLEEPING)
6848 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6852 static void PlayAllPlayersSound()
6856 for (i = 0; i < MAX_PLAYERS; i++)
6857 if (stored_player[i].active)
6858 PlayPlayerSound(&stored_player[i]);
6861 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6863 boolean last_waiting = player->is_waiting;
6864 int move_dir = player->MovDir;
6866 player->last_action_waiting = player->action_waiting;
6870 if (!last_waiting) /* not waiting -> waiting */
6872 player->is_waiting = TRUE;
6874 player->frame_counter_bored =
6876 game.player_boring_delay_fixed +
6877 SimpleRND(game.player_boring_delay_random);
6878 player->frame_counter_sleeping =
6880 game.player_sleeping_delay_fixed +
6881 SimpleRND(game.player_sleeping_delay_random);
6883 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6886 if (game.player_sleeping_delay_fixed +
6887 game.player_sleeping_delay_random > 0 &&
6888 player->anim_delay_counter == 0 &&
6889 player->post_delay_counter == 0 &&
6890 FrameCounter >= player->frame_counter_sleeping)
6891 player->is_sleeping = TRUE;
6892 else if (game.player_boring_delay_fixed +
6893 game.player_boring_delay_random > 0 &&
6894 FrameCounter >= player->frame_counter_bored)
6895 player->is_bored = TRUE;
6897 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6898 player->is_bored ? ACTION_BORING :
6901 if (player->is_sleeping)
6903 if (player->num_special_action_sleeping > 0)
6905 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6907 int last_special_action = player->special_action_sleeping;
6908 int num_special_action = player->num_special_action_sleeping;
6909 int special_action =
6910 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6911 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6912 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6913 last_special_action + 1 : ACTION_SLEEPING);
6914 int special_graphic =
6915 el_act_dir2img(player->element_nr, special_action, move_dir);
6917 player->anim_delay_counter =
6918 graphic_info[special_graphic].anim_delay_fixed +
6919 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6920 player->post_delay_counter =
6921 graphic_info[special_graphic].post_delay_fixed +
6922 SimpleRND(graphic_info[special_graphic].post_delay_random);
6924 player->special_action_sleeping = special_action;
6927 if (player->anim_delay_counter > 0)
6929 player->action_waiting = player->special_action_sleeping;
6930 player->anim_delay_counter--;
6932 else if (player->post_delay_counter > 0)
6934 player->post_delay_counter--;
6938 else if (player->is_bored)
6940 if (player->num_special_action_bored > 0)
6942 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6944 int special_action =
6945 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6946 int special_graphic =
6947 el_act_dir2img(player->element_nr, special_action, move_dir);
6949 player->anim_delay_counter =
6950 graphic_info[special_graphic].anim_delay_fixed +
6951 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6952 player->post_delay_counter =
6953 graphic_info[special_graphic].post_delay_fixed +
6954 SimpleRND(graphic_info[special_graphic].post_delay_random);
6956 player->special_action_bored = special_action;
6959 if (player->anim_delay_counter > 0)
6961 player->action_waiting = player->special_action_bored;
6962 player->anim_delay_counter--;
6964 else if (player->post_delay_counter > 0)
6966 player->post_delay_counter--;
6971 else if (last_waiting) /* waiting -> not waiting */
6973 player->is_waiting = FALSE;
6974 player->is_bored = FALSE;
6975 player->is_sleeping = FALSE;
6977 player->frame_counter_bored = -1;
6978 player->frame_counter_sleeping = -1;
6980 player->anim_delay_counter = 0;
6981 player->post_delay_counter = 0;
6983 player->action_waiting = ACTION_DEFAULT;
6985 player->special_action_bored = ACTION_DEFAULT;
6986 player->special_action_sleeping = ACTION_DEFAULT;
6991 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6994 static byte stored_player_action[MAX_PLAYERS];
6995 static int num_stored_actions = 0;
6997 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6998 int left = player_action & JOY_LEFT;
6999 int right = player_action & JOY_RIGHT;
7000 int up = player_action & JOY_UP;
7001 int down = player_action & JOY_DOWN;
7002 int button1 = player_action & JOY_BUTTON_1;
7003 int button2 = player_action & JOY_BUTTON_2;
7004 int dx = (left ? -1 : right ? 1 : 0);
7005 int dy = (up ? -1 : down ? 1 : 0);
7008 stored_player_action[player->index_nr] = 0;
7009 num_stored_actions++;
7013 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7016 if (!player->active || tape.pausing)
7020 printf("::: [%d %d %d %d] [%d %d]\n",
7021 left, right, up, down, button1, button2);
7027 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7031 snapped = SnapField(player, dx, dy);
7035 dropped = DropElement(player);
7037 moved = MovePlayer(player, dx, dy);
7040 if (tape.single_step && tape.recording && !tape.pausing)
7042 if (button1 || (dropped && !moved))
7044 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7045 SnapField(player, 0, 0); /* stop snapping */
7049 SetPlayerWaiting(player, FALSE);
7052 return player_action;
7054 stored_player_action[player->index_nr] = player_action;
7060 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7063 /* no actions for this player (no input at player's configured device) */
7065 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7066 SnapField(player, 0, 0);
7067 CheckGravityMovement(player);
7069 if (player->MovPos == 0)
7070 SetPlayerWaiting(player, TRUE);
7072 if (player->MovPos == 0) /* needed for tape.playing */
7073 player->is_moving = FALSE;
7075 player->is_dropping = FALSE;
7081 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7083 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7085 TapeRecordAction(stored_player_action);
7086 num_stored_actions = 0;
7093 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7095 static byte stored_player_action[MAX_PLAYERS];
7096 static int num_stored_actions = 0;
7097 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7098 int left = player_action & JOY_LEFT;
7099 int right = player_action & JOY_RIGHT;
7100 int up = player_action & JOY_UP;
7101 int down = player_action & JOY_DOWN;
7102 int button1 = player_action & JOY_BUTTON_1;
7103 int button2 = player_action & JOY_BUTTON_2;
7104 int dx = (left ? -1 : right ? 1 : 0);
7105 int dy = (up ? -1 : down ? 1 : 0);
7107 stored_player_action[player->index_nr] = 0;
7108 num_stored_actions++;
7110 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7112 if (!player->active || tape.pausing)
7117 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7120 snapped = SnapField(player, dx, dy);
7124 dropped = DropElement(player);
7126 moved = MovePlayer(player, dx, dy);
7129 if (tape.single_step && tape.recording && !tape.pausing)
7131 if (button1 || (dropped && !moved))
7133 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7134 SnapField(player, 0, 0); /* stop snapping */
7138 stored_player_action[player->index_nr] = player_action;
7142 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7144 /* no actions for this player (no input at player's configured device) */
7146 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7147 SnapField(player, 0, 0);
7148 CheckGravityMovement(player);
7150 if (player->MovPos == 0)
7151 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7153 if (player->MovPos == 0) /* needed for tape.playing */
7154 player->is_moving = FALSE;
7157 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7159 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7161 TapeRecordAction(stored_player_action);
7162 num_stored_actions = 0;
7169 static unsigned long action_delay = 0;
7170 unsigned long action_delay_value;
7171 int magic_wall_x = 0, magic_wall_y = 0;
7172 int i, x, y, element, graphic;
7173 byte *recorded_player_action;
7174 byte summarized_player_action = 0;
7176 byte tape_action[MAX_PLAYERS];
7179 if (game_status != GAME_MODE_PLAYING)
7182 action_delay_value =
7183 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7185 if (tape.playing && tape.index_search && !tape.pausing)
7186 action_delay_value = 0;
7188 /* ---------- main game synchronization point ---------- */
7190 WaitUntilDelayReached(&action_delay, action_delay_value);
7192 if (network_playing && !network_player_action_received)
7196 printf("DEBUG: try to get network player actions in time\n");
7200 #if defined(PLATFORM_UNIX)
7201 /* last chance to get network player actions without main loop delay */
7205 if (game_status != GAME_MODE_PLAYING)
7208 if (!network_player_action_received)
7212 printf("DEBUG: failed to get network player actions in time\n");
7223 printf("::: getting new tape action [%d]\n", FrameCounter);
7226 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7228 for (i = 0; i < MAX_PLAYERS; i++)
7230 summarized_player_action |= stored_player[i].action;
7232 if (!network_playing)
7233 stored_player[i].effective_action = stored_player[i].action;
7236 #if defined(PLATFORM_UNIX)
7237 if (network_playing)
7238 SendToServer_MovePlayer(summarized_player_action);
7241 if (!options.network && !setup.team_mode)
7242 local_player->effective_action = summarized_player_action;
7244 for (i = 0; i < MAX_PLAYERS; i++)
7246 int actual_player_action = stored_player[i].effective_action;
7248 if (stored_player[i].programmed_action)
7249 actual_player_action = stored_player[i].programmed_action;
7251 if (recorded_player_action)
7252 actual_player_action = recorded_player_action[i];
7254 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7256 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7257 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7259 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7264 TapeRecordAction(tape_action);
7267 network_player_action_received = FALSE;
7269 ScrollScreen(NULL, SCROLL_GO_ON);
7275 for (i = 0; i < MAX_PLAYERS; i++)
7276 stored_player[i].Frame++;
7280 /* for downwards compatibility, the following code emulates a fixed bug that
7281 occured when pushing elements (causing elements that just made their last
7282 pushing step to already (if possible) make their first falling step in the
7283 same game frame, which is bad); this code is also needed to use the famous
7284 "spring push bug" which is used in older levels and might be wanted to be
7285 used also in newer levels, but in this case the buggy pushing code is only
7286 affecting the "spring" element and no other elements */
7289 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7291 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7294 for (i = 0; i < MAX_PLAYERS; i++)
7296 struct PlayerInfo *player = &stored_player[i];
7301 if (player->active && player->is_pushing && player->is_moving &&
7303 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7304 Feld[x][y] == EL_SPRING))
7306 if (player->active && player->is_pushing && player->is_moving &&
7310 ContinueMoving(x, y);
7312 /* continue moving after pushing (this is actually a bug) */
7313 if (!IS_MOVING(x, y))
7322 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7324 Changed[x][y] = CE_BITMASK_DEFAULT;
7325 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7328 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7330 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7331 printf("GameActions(): This should never happen!\n");
7333 ChangePage[x][y] = -1;
7338 if (WasJustMoving[x][y] > 0)
7339 WasJustMoving[x][y]--;
7340 if (WasJustFalling[x][y] > 0)
7341 WasJustFalling[x][y]--;
7346 /* reset finished pushing action (not done in ContinueMoving() to allow
7347 continous pushing animation for elements with zero push delay) */
7348 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7350 ResetGfxAnimation(x, y);
7351 DrawLevelField(x, y);
7356 if (IS_BLOCKED(x, y))
7360 Blocked2Moving(x, y, &oldx, &oldy);
7361 if (!IS_MOVING(oldx, oldy))
7363 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7364 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7365 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7366 printf("GameActions(): This should never happen!\n");
7372 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7374 element = Feld[x][y];
7376 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7378 graphic = el2img(element);
7384 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7386 element = graphic = 0;
7390 if (graphic_info[graphic].anim_global_sync)
7391 GfxFrame[x][y] = FrameCounter;
7393 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7394 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7395 ResetRandomAnimationValue(x, y);
7397 SetRandomAnimationValue(x, y);
7400 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7403 if (IS_INACTIVE(element))
7405 if (IS_ANIMATED(graphic))
7406 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7412 /* this may take place after moving, so 'element' may have changed */
7414 if (IS_CHANGING(x, y))
7416 if (IS_CHANGING(x, y) &&
7417 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7421 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7422 element_info[element].event_page_nr[CE_DELAY]);
7424 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7427 element = Feld[x][y];
7428 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7432 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7437 element = Feld[x][y];
7438 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7440 if (element == EL_MOLE)
7441 printf("::: %d, %d, %d [%d]\n",
7442 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7446 if (element == EL_YAMYAM)
7447 printf("::: %d, %d, %d\n",
7448 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7452 if (IS_ANIMATED(graphic) &&
7456 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7459 if (element == EL_BUG)
7460 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7464 if (element == EL_MOLE)
7465 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7469 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7470 EdelsteinFunkeln(x, y);
7472 else if ((element == EL_ACID ||
7473 element == EL_EXIT_OPEN ||
7474 element == EL_SP_EXIT_OPEN ||
7475 element == EL_SP_TERMINAL ||
7476 element == EL_SP_TERMINAL_ACTIVE ||
7477 element == EL_EXTRA_TIME ||
7478 element == EL_SHIELD_NORMAL ||
7479 element == EL_SHIELD_DEADLY) &&
7480 IS_ANIMATED(graphic))
7481 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7482 else if (IS_MOVING(x, y))
7483 ContinueMoving(x, y);
7484 else if (IS_ACTIVE_BOMB(element))
7485 CheckDynamite(x, y);
7487 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7488 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7490 else if (element == EL_AMOEBA_GROWING)
7491 AmoebeWaechst(x, y);
7492 else if (element == EL_AMOEBA_SHRINKING)
7493 AmoebaDisappearing(x, y);
7495 #if !USE_NEW_AMOEBA_CODE
7496 else if (IS_AMOEBALIVE(element))
7497 AmoebeAbleger(x, y);
7500 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7502 else if (element == EL_EXIT_CLOSED)
7504 else if (element == EL_SP_EXIT_CLOSED)
7506 else if (element == EL_EXPANDABLE_WALL_GROWING)
7508 else if (element == EL_EXPANDABLE_WALL ||
7509 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7510 element == EL_EXPANDABLE_WALL_VERTICAL ||
7511 element == EL_EXPANDABLE_WALL_ANY)
7513 else if (element == EL_FLAMES)
7514 CheckForDragon(x, y);
7516 else if (IS_AUTO_CHANGING(element))
7517 ChangeElement(x, y);
7519 else if (element == EL_EXPLOSION)
7520 ; /* drawing of correct explosion animation is handled separately */
7521 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7522 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7525 /* this may take place after moving, so 'element' may have changed */
7526 if (IS_AUTO_CHANGING(Feld[x][y]))
7527 ChangeElement(x, y);
7530 if (IS_BELT_ACTIVE(element))
7531 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7533 if (game.magic_wall_active)
7535 int jx = local_player->jx, jy = local_player->jy;
7537 /* play the element sound at the position nearest to the player */
7538 if ((element == EL_MAGIC_WALL_FULL ||
7539 element == EL_MAGIC_WALL_ACTIVE ||
7540 element == EL_MAGIC_WALL_EMPTYING ||
7541 element == EL_BD_MAGIC_WALL_FULL ||
7542 element == EL_BD_MAGIC_WALL_ACTIVE ||
7543 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7544 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7552 #if USE_NEW_AMOEBA_CODE
7553 /* new experimental amoeba growth stuff */
7555 if (!(FrameCounter % 8))
7558 static unsigned long random = 1684108901;
7560 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7563 x = (random >> 10) % lev_fieldx;
7564 y = (random >> 20) % lev_fieldy;
7566 x = RND(lev_fieldx);
7567 y = RND(lev_fieldy);
7569 element = Feld[x][y];
7571 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7572 if (!IS_PLAYER(x,y) &&
7573 (element == EL_EMPTY ||
7574 element == EL_SAND ||
7575 element == EL_QUICKSAND_EMPTY ||
7576 element == EL_ACID_SPLASH_LEFT ||
7577 element == EL_ACID_SPLASH_RIGHT))
7579 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7580 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7581 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7582 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7583 Feld[x][y] = EL_AMOEBA_DROP;
7586 random = random * 129 + 1;
7592 if (game.explosions_delayed)
7595 game.explosions_delayed = FALSE;
7597 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7599 element = Feld[x][y];
7601 if (ExplodeField[x][y])
7602 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7603 else if (element == EL_EXPLOSION)
7604 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7606 ExplodeField[x][y] = EX_NO_EXPLOSION;
7609 game.explosions_delayed = TRUE;
7612 if (game.magic_wall_active)
7614 if (!(game.magic_wall_time_left % 4))
7616 int element = Feld[magic_wall_x][magic_wall_y];
7618 if (element == EL_BD_MAGIC_WALL_FULL ||
7619 element == EL_BD_MAGIC_WALL_ACTIVE ||
7620 element == EL_BD_MAGIC_WALL_EMPTYING)
7621 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7623 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7626 if (game.magic_wall_time_left > 0)
7628 game.magic_wall_time_left--;
7629 if (!game.magic_wall_time_left)
7631 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7633 element = Feld[x][y];
7635 if (element == EL_MAGIC_WALL_ACTIVE ||
7636 element == EL_MAGIC_WALL_FULL)
7638 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7639 DrawLevelField(x, y);
7641 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7642 element == EL_BD_MAGIC_WALL_FULL)
7644 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7645 DrawLevelField(x, y);
7649 game.magic_wall_active = FALSE;
7654 if (game.light_time_left > 0)
7656 game.light_time_left--;
7658 if (game.light_time_left == 0)
7659 RedrawAllLightSwitchesAndInvisibleElements();
7662 if (game.timegate_time_left > 0)
7664 game.timegate_time_left--;
7666 if (game.timegate_time_left == 0)
7667 CloseAllOpenTimegates();
7670 for (i = 0; i < MAX_PLAYERS; i++)
7672 struct PlayerInfo *player = &stored_player[i];
7674 if (SHIELD_ON(player))
7676 if (player->shield_deadly_time_left)
7677 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7678 else if (player->shield_normal_time_left)
7679 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7683 if (TimeFrames >= FRAMES_PER_SECOND)
7688 for (i = 0; i < MAX_PLAYERS; i++)
7690 struct PlayerInfo *player = &stored_player[i];
7692 if (SHIELD_ON(player))
7694 player->shield_normal_time_left--;
7696 if (player->shield_deadly_time_left > 0)
7697 player->shield_deadly_time_left--;
7701 if (tape.recording || tape.playing)
7702 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7708 if (TimeLeft <= 10 && setup.time_limit)
7709 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7711 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7713 if (!TimeLeft && setup.time_limit)
7714 for (i = 0; i < MAX_PLAYERS; i++)
7715 KillHero(&stored_player[i]);
7717 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7718 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7722 PlayAllPlayersSound();
7724 if (options.debug) /* calculate frames per second */
7726 static unsigned long fps_counter = 0;
7727 static int fps_frames = 0;
7728 unsigned long fps_delay_ms = Counter() - fps_counter;
7732 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7734 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7737 fps_counter = Counter();
7740 redraw_mask |= REDRAW_FPS;
7744 if (stored_player[0].jx != stored_player[0].last_jx ||
7745 stored_player[0].jy != stored_player[0].last_jy)
7746 printf("::: %d, %d, %d, %d, %d\n",
7747 stored_player[0].MovDir,
7748 stored_player[0].MovPos,
7749 stored_player[0].GfxPos,
7750 stored_player[0].Frame,
7751 stored_player[0].StepFrame);
7758 for (i = 0; i < MAX_PLAYERS; i++)
7761 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7763 stored_player[i].Frame += move_frames;
7765 if (stored_player[i].MovPos != 0)
7766 stored_player[i].StepFrame += move_frames;
7768 if (stored_player[i].drop_delay > 0)
7769 stored_player[i].drop_delay--;
7774 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7776 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7778 local_player->show_envelope = 0;
7783 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7785 int min_x = x, min_y = y, max_x = x, max_y = y;
7788 for (i = 0; i < MAX_PLAYERS; i++)
7790 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7792 if (!stored_player[i].active || &stored_player[i] == player)
7795 min_x = MIN(min_x, jx);
7796 min_y = MIN(min_y, jy);
7797 max_x = MAX(max_x, jx);
7798 max_y = MAX(max_y, jy);
7801 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7804 static boolean AllPlayersInVisibleScreen()
7808 for (i = 0; i < MAX_PLAYERS; i++)
7810 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7812 if (!stored_player[i].active)
7815 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7822 void ScrollLevel(int dx, int dy)
7824 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7827 BlitBitmap(drawto_field, drawto_field,
7828 FX + TILEX * (dx == -1) - softscroll_offset,
7829 FY + TILEY * (dy == -1) - softscroll_offset,
7830 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7831 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7832 FX + TILEX * (dx == 1) - softscroll_offset,
7833 FY + TILEY * (dy == 1) - softscroll_offset);
7837 x = (dx == 1 ? BX1 : BX2);
7838 for (y = BY1; y <= BY2; y++)
7839 DrawScreenField(x, y);
7844 y = (dy == 1 ? BY1 : BY2);
7845 for (x = BX1; x <= BX2; x++)
7846 DrawScreenField(x, y);
7849 redraw_mask |= REDRAW_FIELD;
7852 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7854 int nextx = x + dx, nexty = y + dy;
7855 int element = Feld[x][y];
7858 element != EL_SP_PORT_LEFT &&
7859 element != EL_SP_GRAVITY_PORT_LEFT &&
7860 element != EL_SP_PORT_HORIZONTAL &&
7861 element != EL_SP_PORT_ANY) ||
7863 element != EL_SP_PORT_RIGHT &&
7864 element != EL_SP_GRAVITY_PORT_RIGHT &&
7865 element != EL_SP_PORT_HORIZONTAL &&
7866 element != EL_SP_PORT_ANY) ||
7868 element != EL_SP_PORT_UP &&
7869 element != EL_SP_GRAVITY_PORT_UP &&
7870 element != EL_SP_PORT_VERTICAL &&
7871 element != EL_SP_PORT_ANY) ||
7873 element != EL_SP_PORT_DOWN &&
7874 element != EL_SP_GRAVITY_PORT_DOWN &&
7875 element != EL_SP_PORT_VERTICAL &&
7876 element != EL_SP_PORT_ANY) ||
7877 !IN_LEV_FIELD(nextx, nexty) ||
7878 !IS_FREE(nextx, nexty))
7884 static void CheckGravityMovement(struct PlayerInfo *player)
7886 if (game.gravity && !player->programmed_action)
7888 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7889 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7891 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7892 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7893 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7894 int jx = player->jx, jy = player->jy;
7895 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7896 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7897 int new_jx = jx + dx, new_jy = jy + dy;
7898 boolean field_under_player_is_free =
7899 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7900 boolean player_is_moving_to_valid_field =
7901 (IN_LEV_FIELD(new_jx, new_jy) &&
7902 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7903 Feld[new_jx][new_jy] == EL_SAND ||
7904 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7905 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7906 /* !!! extend EL_SAND to anything diggable !!! */
7908 boolean player_is_standing_on_valid_field =
7909 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
7910 (IS_WALKABLE(Feld[jx][jy]) &&
7911 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
7913 if (field_under_player_is_free &&
7914 !player_is_standing_on_valid_field &&
7915 !player_is_moving_to_valid_field)
7916 player->programmed_action = MV_DOWN;
7922 -----------------------------------------------------------------------------
7923 dx, dy: direction (non-diagonal) to try to move the player to
7924 real_dx, real_dy: direction as read from input device (can be diagonal)
7927 boolean MovePlayerOneStep(struct PlayerInfo *player,
7928 int dx, int dy, int real_dx, int real_dy)
7931 static int change_sides[4][2] =
7933 /* enter side leave side */
7934 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7935 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7936 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7937 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7939 int move_direction = (dx == -1 ? MV_LEFT :
7940 dx == +1 ? MV_RIGHT :
7942 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7943 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7944 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7946 int jx = player->jx, jy = player->jy;
7947 int new_jx = jx + dx, new_jy = jy + dy;
7951 if (!player->active || (!dx && !dy))
7952 return MF_NO_ACTION;
7954 player->MovDir = (dx < 0 ? MV_LEFT :
7957 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7959 if (!IN_LEV_FIELD(new_jx, new_jy))
7960 return MF_NO_ACTION;
7962 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7963 return MF_NO_ACTION;
7966 element = MovingOrBlocked2Element(new_jx, new_jy);
7968 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7971 if (DONT_RUN_INTO(element))
7973 if (element == EL_ACID && dx == 0 && dy == 1)
7976 Feld[jx][jy] = EL_PLAYER_1;
7977 InitMovingField(jx, jy, MV_DOWN);
7978 Store[jx][jy] = EL_ACID;
7979 ContinueMoving(jx, jy);
7983 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7988 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
7989 if (can_move != MF_MOVING)
7992 /* check if DigField() has caused relocation of the player */
7993 if (player->jx != jx || player->jy != jy)
7994 return MF_NO_ACTION;
7996 StorePlayer[jx][jy] = 0;
7997 player->last_jx = jx;
7998 player->last_jy = jy;
7999 player->jx = new_jx;
8000 player->jy = new_jy;
8001 StorePlayer[new_jx][new_jy] = player->element_nr;
8004 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8006 player->step_counter++;
8008 player->drop_delay = 0;
8010 PlayerVisit[jx][jy] = FrameCounter;
8012 ScrollPlayer(player, SCROLL_INIT);
8015 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8017 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8018 CE_OTHER_GETS_LEFT);
8019 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8020 CE_LEFT_BY_PLAYER, -1);
8023 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8025 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
8026 enter_side, CE_OTHER_GETS_ENTERED);
8027 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
8028 CE_ENTERED_BY_PLAYER, -1);
8035 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8037 int jx = player->jx, jy = player->jy;
8038 int old_jx = jx, old_jy = jy;
8039 int moved = MF_NO_ACTION;
8042 if (!player->active)
8047 if (player->MovPos == 0)
8049 player->is_moving = FALSE;
8050 player->is_digging = FALSE;
8051 player->is_collecting = FALSE;
8052 player->is_snapping = FALSE;
8053 player->is_pushing = FALSE;
8059 if (!player->active || (!dx && !dy))
8064 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8068 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8069 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8073 /* remove the last programmed player action */
8074 player->programmed_action = 0;
8078 /* should only happen if pre-1.2 tape recordings are played */
8079 /* this is only for backward compatibility */
8081 int original_move_delay_value = player->move_delay_value;
8084 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8088 /* scroll remaining steps with finest movement resolution */
8089 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8091 while (player->MovPos)
8093 ScrollPlayer(player, SCROLL_GO_ON);
8094 ScrollScreen(NULL, SCROLL_GO_ON);
8100 player->move_delay_value = original_move_delay_value;
8103 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8105 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8106 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8110 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8111 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8117 if (moved & MF_MOVING && !ScreenMovPos &&
8118 (player == local_player || !options.network))
8120 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8121 int offset = (setup.scroll_delay ? 3 : 0);
8123 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8125 /* actual player has left the screen -- scroll in that direction */
8126 if (jx != old_jx) /* player has moved horizontally */
8127 scroll_x += (jx - old_jx);
8128 else /* player has moved vertically */
8129 scroll_y += (jy - old_jy);
8133 if (jx != old_jx) /* player has moved horizontally */
8135 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8136 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8137 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8139 /* don't scroll over playfield boundaries */
8140 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8141 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8143 /* don't scroll more than one field at a time */
8144 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8146 /* don't scroll against the player's moving direction */
8147 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8148 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8149 scroll_x = old_scroll_x;
8151 else /* player has moved vertically */
8153 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8154 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8155 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8157 /* don't scroll over playfield boundaries */
8158 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8159 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8161 /* don't scroll more than one field at a time */
8162 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8164 /* don't scroll against the player's moving direction */
8165 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8166 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8167 scroll_y = old_scroll_y;
8171 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8173 if (!options.network && !AllPlayersInVisibleScreen())
8175 scroll_x = old_scroll_x;
8176 scroll_y = old_scroll_y;
8180 ScrollScreen(player, SCROLL_INIT);
8181 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8188 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8190 if (!(moved & MF_MOVING) && !player->is_pushing)
8195 player->StepFrame = 0;
8197 if (moved & MF_MOVING)
8199 if (old_jx != jx && old_jy == jy)
8200 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8201 else if (old_jx == jx && old_jy != jy)
8202 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8204 DrawLevelField(jx, jy); /* for "crumbled sand" */
8206 player->last_move_dir = player->MovDir;
8207 player->is_moving = TRUE;
8209 player->is_snapping = FALSE;
8213 player->is_switching = FALSE;
8216 player->is_dropping = FALSE;
8221 static int change_sides[4][2] =
8223 /* enter side leave side */
8224 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8225 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8226 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8227 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8229 int move_direction = player->MovDir;
8230 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8231 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8234 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8236 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8237 leave_side, CE_OTHER_GETS_LEFT);
8238 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8239 leave_side, CE_LEFT_BY_PLAYER, -1);
8242 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8244 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8245 enter_side, CE_OTHER_GETS_ENTERED);
8246 CheckElementSideChange(jx, jy, Feld[jx][jy],
8247 enter_side, CE_ENTERED_BY_PLAYER, -1);
8258 CheckGravityMovement(player);
8261 player->last_move_dir = MV_NO_MOVING;
8263 player->is_moving = FALSE;
8266 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8268 TestIfHeroTouchesBadThing(jx, jy);
8269 TestIfPlayerTouchesCustomElement(jx, jy);
8272 if (!player->active)
8278 void ScrollPlayer(struct PlayerInfo *player, int mode)
8280 int jx = player->jx, jy = player->jy;
8281 int last_jx = player->last_jx, last_jy = player->last_jy;
8282 int move_stepsize = TILEX / player->move_delay_value;
8284 if (!player->active || !player->MovPos)
8287 if (mode == SCROLL_INIT)
8289 player->actual_frame_counter = FrameCounter;
8290 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8292 if (Feld[last_jx][last_jy] == EL_EMPTY)
8293 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8301 else if (!FrameReached(&player->actual_frame_counter, 1))
8304 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8305 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8307 if (!player->block_last_field &&
8308 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8309 Feld[last_jx][last_jy] = EL_EMPTY;
8311 /* before DrawPlayer() to draw correct player graphic for this case */
8312 if (player->MovPos == 0)
8313 CheckGravityMovement(player);
8316 DrawPlayer(player); /* needed here only to cleanup last field */
8319 if (player->MovPos == 0) /* player reached destination field */
8322 if (player->move_delay_reset_counter > 0)
8324 player->move_delay_reset_counter--;
8326 if (player->move_delay_reset_counter == 0)
8328 /* continue with normal speed after quickly moving through gate */
8329 HALVE_PLAYER_SPEED(player);
8331 /* be able to make the next move without delay */
8332 player->move_delay = 0;
8336 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8338 /* continue with normal speed after quickly moving through gate */
8339 HALVE_PLAYER_SPEED(player);
8341 /* be able to make the next move without delay */
8342 player->move_delay = 0;
8346 if (player->block_last_field &&
8347 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8348 Feld[last_jx][last_jy] = EL_EMPTY;
8350 player->last_jx = jx;
8351 player->last_jy = jy;
8353 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8354 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8355 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8357 DrawPlayer(player); /* needed here only to cleanup last field */
8360 if (local_player->friends_still_needed == 0 ||
8361 IS_SP_ELEMENT(Feld[jx][jy]))
8362 player->LevelSolved = player->GameOver = TRUE;
8365 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8367 TestIfHeroTouchesBadThing(jx, jy);
8368 TestIfPlayerTouchesCustomElement(jx, jy);
8370 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8373 if (!player->active)
8377 if (tape.single_step && tape.recording && !tape.pausing &&
8378 !player->programmed_action)
8379 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8383 void ScrollScreen(struct PlayerInfo *player, int mode)
8385 static unsigned long screen_frame_counter = 0;
8387 if (mode == SCROLL_INIT)
8389 /* set scrolling step size according to actual player's moving speed */
8390 ScrollStepSize = TILEX / player->move_delay_value;
8392 screen_frame_counter = FrameCounter;
8393 ScreenMovDir = player->MovDir;
8394 ScreenMovPos = player->MovPos;
8395 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8398 else if (!FrameReached(&screen_frame_counter, 1))
8403 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8404 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8405 redraw_mask |= REDRAW_FIELD;
8408 ScreenMovDir = MV_NO_MOVING;
8411 void TestIfPlayerTouchesCustomElement(int x, int y)
8413 static int xy[4][2] =
8420 static int change_sides[4][2] =
8422 /* center side border side */
8423 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8424 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8425 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8426 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8428 static int touch_dir[4] =
8435 int center_element = Feld[x][y]; /* should always be non-moving! */
8438 for (i = 0; i < 4; i++)
8440 int xx = x + xy[i][0];
8441 int yy = y + xy[i][1];
8442 int center_side = change_sides[i][0];
8443 int border_side = change_sides[i][1];
8446 if (!IN_LEV_FIELD(xx, yy))
8449 if (IS_PLAYER(x, y))
8451 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8452 border_element = Feld[xx][yy]; /* may be moving! */
8453 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8454 border_element = Feld[xx][yy];
8455 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8456 border_element = MovingOrBlocked2Element(xx, yy);
8458 continue; /* center and border element do not touch */
8460 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8461 CE_OTHER_GETS_TOUCHED);
8462 CheckElementSideChange(xx, yy, border_element, border_side,
8463 CE_TOUCHED_BY_PLAYER, -1);
8465 else if (IS_PLAYER(xx, yy))
8467 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8469 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8471 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8472 continue; /* center and border element do not touch */
8475 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8476 CE_OTHER_GETS_TOUCHED);
8477 CheckElementSideChange(x, y, center_element, center_side,
8478 CE_TOUCHED_BY_PLAYER, -1);
8485 void TestIfElementTouchesCustomElement(int x, int y)
8487 static int xy[4][2] =
8494 static int change_sides[4][2] =
8496 /* center side border side */
8497 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8498 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8499 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8500 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8502 static int touch_dir[4] =
8509 boolean change_center_element = FALSE;
8510 int center_element_change_page = 0;
8511 int center_element = Feld[x][y]; /* should always be non-moving! */
8514 for (i = 0; i < 4; i++)
8516 int xx = x + xy[i][0];
8517 int yy = y + xy[i][1];
8518 int center_side = change_sides[i][0];
8519 int border_side = change_sides[i][1];
8522 if (!IN_LEV_FIELD(xx, yy))
8525 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8526 border_element = Feld[xx][yy]; /* may be moving! */
8527 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8528 border_element = Feld[xx][yy];
8529 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8530 border_element = MovingOrBlocked2Element(xx, yy);
8532 continue; /* center and border element do not touch */
8534 /* check for change of center element (but change it only once) */
8535 if (IS_CUSTOM_ELEMENT(center_element) &&
8536 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8537 !change_center_element)
8539 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8541 struct ElementChangeInfo *change =
8542 &element_info[center_element].change_page[j];
8544 if (change->can_change &&
8545 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8546 change->sides & border_side &&
8548 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8550 change->trigger_element == border_element
8554 change_center_element = TRUE;
8555 center_element_change_page = j;
8562 /* check for change of border element */
8563 if (IS_CUSTOM_ELEMENT(border_element) &&
8564 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8566 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8568 struct ElementChangeInfo *change =
8569 &element_info[border_element].change_page[j];
8571 if (change->can_change &&
8572 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8573 change->sides & center_side &&
8575 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8577 change->trigger_element == center_element
8581 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8582 CE_OTHER_IS_TOUCHING, j);
8589 if (change_center_element)
8590 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8591 CE_OTHER_IS_TOUCHING, center_element_change_page);
8594 void TestIfElementHitsCustomElement(int x, int y, int direction)
8596 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8597 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8598 int hitx = x + dx, hity = y + dy;
8599 int hitting_element = Feld[x][y];
8601 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8602 !IS_FREE(hitx, hity) &&
8603 (!IS_MOVING(hitx, hity) ||
8604 MovDir[hitx][hity] != direction ||
8605 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8608 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8612 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8616 CheckElementSideChange(x, y, hitting_element,
8617 direction, CE_HITTING_SOMETHING, -1);
8619 if (IN_LEV_FIELD(hitx, hity))
8621 int opposite_direction = MV_DIR_OPPOSITE(direction);
8622 int hitting_side = direction;
8623 int touched_side = opposite_direction;
8624 int touched_element = MovingOrBlocked2Element(hitx, hity);
8626 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8627 MovDir[hitx][hity] != direction ||
8628 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8637 CheckElementSideChange(hitx, hity, touched_element,
8638 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8640 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8641 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8643 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8645 struct ElementChangeInfo *change =
8646 &element_info[hitting_element].change_page[i];
8648 if (change->can_change &&
8649 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8650 change->sides & touched_side &&
8653 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8655 change->trigger_element == touched_element
8659 CheckElementSideChange(x, y, hitting_element,
8660 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8666 if (IS_CUSTOM_ELEMENT(touched_element) &&
8667 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8669 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8671 struct ElementChangeInfo *change =
8672 &element_info[touched_element].change_page[i];
8674 if (change->can_change &&
8675 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8676 change->sides & hitting_side &&
8678 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8680 change->trigger_element == hitting_element
8684 CheckElementSideChange(hitx, hity, touched_element,
8685 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8694 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8696 int i, kill_x = -1, kill_y = -1;
8697 static int test_xy[4][2] =
8704 static int test_dir[4] =
8712 for (i = 0; i < 4; i++)
8714 int test_x, test_y, test_move_dir, test_element;
8716 test_x = good_x + test_xy[i][0];
8717 test_y = good_y + test_xy[i][1];
8718 if (!IN_LEV_FIELD(test_x, test_y))
8722 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8725 test_element = Feld[test_x][test_y];
8727 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8730 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8731 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8733 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8734 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8742 if (kill_x != -1 || kill_y != -1)
8744 if (IS_PLAYER(good_x, good_y))
8746 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8748 if (player->shield_deadly_time_left > 0)
8749 Bang(kill_x, kill_y);
8750 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8754 Bang(good_x, good_y);
8758 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8760 int i, kill_x = -1, kill_y = -1;
8761 int bad_element = Feld[bad_x][bad_y];
8762 static int test_xy[4][2] =
8769 static int touch_dir[4] =
8776 static int test_dir[4] =
8784 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8787 for (i = 0; i < 4; i++)
8789 int test_x, test_y, test_move_dir, test_element;
8791 test_x = bad_x + test_xy[i][0];
8792 test_y = bad_y + test_xy[i][1];
8793 if (!IN_LEV_FIELD(test_x, test_y))
8797 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8799 test_element = Feld[test_x][test_y];
8801 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8802 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8804 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8805 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8807 /* good thing is player or penguin that does not move away */
8808 if (IS_PLAYER(test_x, test_y))
8810 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8812 if (bad_element == EL_ROBOT && player->is_moving)
8813 continue; /* robot does not kill player if he is moving */
8815 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8817 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8818 continue; /* center and border element do not touch */
8825 else if (test_element == EL_PENGUIN)
8834 if (kill_x != -1 || kill_y != -1)
8836 if (IS_PLAYER(kill_x, kill_y))
8838 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8840 if (player->shield_deadly_time_left > 0)
8842 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8846 Bang(kill_x, kill_y);
8850 void TestIfHeroTouchesBadThing(int x, int y)
8852 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8855 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8857 TestIfGoodThingHitsBadThing(x, y, move_dir);
8860 void TestIfBadThingTouchesHero(int x, int y)
8862 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8865 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8867 TestIfBadThingHitsGoodThing(x, y, move_dir);
8870 void TestIfFriendTouchesBadThing(int x, int y)
8872 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8875 void TestIfBadThingTouchesFriend(int x, int y)
8877 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8880 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8882 int i, kill_x = bad_x, kill_y = bad_y;
8883 static int xy[4][2] =
8891 for (i = 0; i < 4; i++)
8895 x = bad_x + xy[i][0];
8896 y = bad_y + xy[i][1];
8897 if (!IN_LEV_FIELD(x, y))
8900 element = Feld[x][y];
8901 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8902 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8910 if (kill_x != bad_x || kill_y != bad_y)
8914 void KillHero(struct PlayerInfo *player)
8916 int jx = player->jx, jy = player->jy;
8918 if (!player->active)
8921 /* remove accessible field at the player's position */
8922 Feld[jx][jy] = EL_EMPTY;
8924 /* deactivate shield (else Bang()/Explode() would not work right) */
8925 player->shield_normal_time_left = 0;
8926 player->shield_deadly_time_left = 0;
8932 static void KillHeroUnlessEnemyProtected(int x, int y)
8934 if (!PLAYER_ENEMY_PROTECTED(x, y))
8935 KillHero(PLAYERINFO(x, y));
8938 static void KillHeroUnlessExplosionProtected(int x, int y)
8940 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8941 KillHero(PLAYERINFO(x, y));
8944 void BuryHero(struct PlayerInfo *player)
8946 int jx = player->jx, jy = player->jy;
8948 if (!player->active)
8952 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8954 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8956 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8958 player->GameOver = TRUE;
8962 void RemoveHero(struct PlayerInfo *player)
8964 int jx = player->jx, jy = player->jy;
8965 int i, found = FALSE;
8967 player->present = FALSE;
8968 player->active = FALSE;
8970 if (!ExplodeField[jx][jy])
8971 StorePlayer[jx][jy] = 0;
8973 for (i = 0; i < MAX_PLAYERS; i++)
8974 if (stored_player[i].active)
8978 AllPlayersGone = TRUE;
8985 =============================================================================
8986 checkDiagonalPushing()
8987 -----------------------------------------------------------------------------
8988 check if diagonal input device direction results in pushing of object
8989 (by checking if the alternative direction is walkable, diggable, ...)
8990 =============================================================================
8993 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8994 int x, int y, int real_dx, int real_dy)
8996 int jx, jy, dx, dy, xx, yy;
8998 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9001 /* diagonal direction: check alternative direction */
9006 xx = jx + (dx == 0 ? real_dx : 0);
9007 yy = jy + (dy == 0 ? real_dy : 0);
9009 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9013 =============================================================================
9015 -----------------------------------------------------------------------------
9016 x, y: field next to player (non-diagonal) to try to dig to
9017 real_dx, real_dy: direction as read from input device (can be diagonal)
9018 =============================================================================
9021 int DigField(struct PlayerInfo *player,
9022 int oldx, int oldy, int x, int y,
9023 int real_dx, int real_dy, int mode)
9025 static int change_sides[4] =
9027 CH_SIDE_RIGHT, /* moving left */
9028 CH_SIDE_LEFT, /* moving right */
9029 CH_SIDE_BOTTOM, /* moving up */
9030 CH_SIDE_TOP, /* moving down */
9033 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9035 int jx = oldx, jy = oldy;
9036 int dx = x - jx, dy = y - jy;
9037 int nextx = x + dx, nexty = y + dy;
9038 int move_direction = (dx == -1 ? MV_LEFT :
9039 dx == +1 ? MV_RIGHT :
9041 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9042 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9043 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
9044 int old_element = Feld[jx][jy];
9047 if (player->MovPos == 0)
9049 player->is_digging = FALSE;
9050 player->is_collecting = FALSE;
9053 if (player->MovPos == 0) /* last pushing move finished */
9054 player->is_pushing = FALSE;
9056 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9058 player->is_switching = FALSE;
9059 player->push_delay = 0;
9061 return MF_NO_ACTION;
9064 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9065 return MF_NO_ACTION;
9070 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9072 if (IS_TUBE(Feld[jx][jy]) ||
9073 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9077 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9078 int tube_leave_directions[][2] =
9080 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9081 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9082 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9083 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9084 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9085 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9086 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9087 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9088 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9089 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9090 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9091 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9094 while (tube_leave_directions[i][0] != tube_element)
9097 if (tube_leave_directions[i][0] == -1) /* should not happen */
9101 if (!(tube_leave_directions[i][1] & move_direction))
9102 return MF_NO_ACTION; /* tube has no opening in this direction */
9107 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9108 old_element = Back[jx][jy];
9112 if (IS_WALKABLE(old_element) &&
9113 !(element_info[old_element].access_direction & move_direction))
9114 return MF_NO_ACTION; /* field has no opening in this direction */
9116 element = Feld[x][y];
9118 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9119 game.engine_version >= VERSION_IDENT(2,2,0,0))
9120 return MF_NO_ACTION;
9124 case EL_SP_PORT_LEFT:
9125 case EL_SP_PORT_RIGHT:
9127 case EL_SP_PORT_DOWN:
9128 case EL_SP_PORT_HORIZONTAL:
9129 case EL_SP_PORT_VERTICAL:
9130 case EL_SP_PORT_ANY:
9131 case EL_SP_GRAVITY_PORT_LEFT:
9132 case EL_SP_GRAVITY_PORT_RIGHT:
9133 case EL_SP_GRAVITY_PORT_UP:
9134 case EL_SP_GRAVITY_PORT_DOWN:
9136 if (!canEnterSupaplexPort(x, y, dx, dy))
9137 return MF_NO_ACTION;
9140 element != EL_SP_PORT_LEFT &&
9141 element != EL_SP_GRAVITY_PORT_LEFT &&
9142 element != EL_SP_PORT_HORIZONTAL &&
9143 element != EL_SP_PORT_ANY) ||
9145 element != EL_SP_PORT_RIGHT &&
9146 element != EL_SP_GRAVITY_PORT_RIGHT &&
9147 element != EL_SP_PORT_HORIZONTAL &&
9148 element != EL_SP_PORT_ANY) ||
9150 element != EL_SP_PORT_UP &&
9151 element != EL_SP_GRAVITY_PORT_UP &&
9152 element != EL_SP_PORT_VERTICAL &&
9153 element != EL_SP_PORT_ANY) ||
9155 element != EL_SP_PORT_DOWN &&
9156 element != EL_SP_GRAVITY_PORT_DOWN &&
9157 element != EL_SP_PORT_VERTICAL &&
9158 element != EL_SP_PORT_ANY) ||
9159 !IN_LEV_FIELD(nextx, nexty) ||
9160 !IS_FREE(nextx, nexty))
9161 return MF_NO_ACTION;
9164 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9165 element == EL_SP_GRAVITY_PORT_RIGHT ||
9166 element == EL_SP_GRAVITY_PORT_UP ||
9167 element == EL_SP_GRAVITY_PORT_DOWN)
9168 game.gravity = !game.gravity;
9170 /* automatically move to the next field with double speed */
9171 player->programmed_action = move_direction;
9173 if (player->move_delay_reset_counter == 0)
9175 player->move_delay_reset_counter = 2; /* two double speed steps */
9177 DOUBLE_PLAYER_SPEED(player);
9180 player->move_delay_reset_counter = 2;
9182 DOUBLE_PLAYER_SPEED(player);
9185 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9190 case EL_TUBE_VERTICAL:
9191 case EL_TUBE_HORIZONTAL:
9192 case EL_TUBE_VERTICAL_LEFT:
9193 case EL_TUBE_VERTICAL_RIGHT:
9194 case EL_TUBE_HORIZONTAL_UP:
9195 case EL_TUBE_HORIZONTAL_DOWN:
9196 case EL_TUBE_LEFT_UP:
9197 case EL_TUBE_LEFT_DOWN:
9198 case EL_TUBE_RIGHT_UP:
9199 case EL_TUBE_RIGHT_DOWN:
9202 int tube_enter_directions[][2] =
9204 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9205 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9206 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9207 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9208 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9209 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9210 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9211 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9212 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9213 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9214 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9215 { -1, MV_NO_MOVING }
9218 while (tube_enter_directions[i][0] != element)
9221 if (tube_enter_directions[i][0] == -1) /* should not happen */
9225 if (!(tube_enter_directions[i][1] & move_direction))
9226 return MF_NO_ACTION; /* tube has no opening in this direction */
9228 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9235 if (IS_WALKABLE(element))
9237 int sound_action = ACTION_WALKING;
9239 if (!(element_info[element].access_direction & opposite_direction))
9240 return MF_NO_ACTION; /* field not accessible from this direction */
9242 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9244 if (!player->key[element - EL_GATE_1])
9245 return MF_NO_ACTION;
9247 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9249 if (!player->key[element - EL_GATE_1_GRAY])
9250 return MF_NO_ACTION;
9252 else if (element == EL_EXIT_OPEN ||
9253 element == EL_SP_EXIT_OPEN ||
9254 element == EL_SP_EXIT_OPENING)
9256 sound_action = ACTION_PASSING; /* player is passing exit */
9258 else if (element == EL_EMPTY)
9260 sound_action = ACTION_MOVING; /* nothing to walk on */
9263 /* play sound from background or player, whatever is available */
9264 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9265 PlayLevelSoundElementAction(x, y, element, sound_action);
9267 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9271 else if (IS_PASSABLE(element))
9273 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9274 return MF_NO_ACTION;
9276 if (IS_CUSTOM_ELEMENT(element) &&
9277 !(element_info[element].access_direction & opposite_direction))
9278 return MF_NO_ACTION; /* field not accessible from this direction */
9281 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9282 return MF_NO_ACTION;
9285 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9287 if (!player->key[element - EL_EM_GATE_1])
9288 return MF_NO_ACTION;
9290 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9292 if (!player->key[element - EL_EM_GATE_1_GRAY])
9293 return MF_NO_ACTION;
9296 /* automatically move to the next field with double speed */
9297 player->programmed_action = move_direction;
9299 if (player->move_delay_reset_counter == 0)
9301 player->move_delay_reset_counter = 2; /* two double speed steps */
9303 DOUBLE_PLAYER_SPEED(player);
9306 player->move_delay_reset_counter = 2;
9308 DOUBLE_PLAYER_SPEED(player);
9311 PlayLevelSoundAction(x, y, ACTION_PASSING);
9315 else if (IS_DIGGABLE(element))
9319 if (mode != DF_SNAP)
9322 GfxElement[x][y] = GFX_ELEMENT(element);
9325 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9327 player->is_digging = TRUE;
9330 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9332 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9335 if (mode == DF_SNAP)
9336 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9341 else if (IS_COLLECTIBLE(element))
9345 if (mode != DF_SNAP)
9347 GfxElement[x][y] = element;
9348 player->is_collecting = TRUE;
9351 if (element == EL_SPEED_PILL)
9352 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9353 else if (element == EL_EXTRA_TIME && level.time > 0)
9356 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9358 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9360 player->shield_normal_time_left += 10;
9361 if (element == EL_SHIELD_DEADLY)
9362 player->shield_deadly_time_left += 10;
9364 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9366 if (player->inventory_size < MAX_INVENTORY_SIZE)
9367 player->inventory_element[player->inventory_size++] = element;
9369 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9370 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9372 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9374 player->dynabomb_count++;
9375 player->dynabombs_left++;
9377 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9379 player->dynabomb_size++;
9381 else if (element == EL_DYNABOMB_INCREASE_POWER)
9383 player->dynabomb_xl = TRUE;
9385 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9386 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9388 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9389 element - EL_KEY_1 : element - EL_EM_KEY_1);
9391 player->key[key_nr] = TRUE;
9393 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9394 el2edimg(EL_KEY_1 + key_nr));
9395 redraw_mask |= REDRAW_DOOR_1;
9397 else if (IS_ENVELOPE(element))
9400 player->show_envelope = element;
9402 ShowEnvelope(element - EL_ENVELOPE_1);
9405 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9409 for (i = 0; i < element_info[element].collect_count; i++)
9410 if (player->inventory_size < MAX_INVENTORY_SIZE)
9411 player->inventory_element[player->inventory_size++] = element;
9413 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9414 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9416 else if (element_info[element].collect_count > 0)
9418 local_player->gems_still_needed -=
9419 element_info[element].collect_count;
9420 if (local_player->gems_still_needed < 0)
9421 local_player->gems_still_needed = 0;
9423 DrawText(DX_EMERALDS, DY_EMERALDS,
9424 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9427 RaiseScoreElement(element);
9428 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9430 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9433 if (mode == DF_SNAP)
9434 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9439 else if (IS_PUSHABLE(element))
9441 if (mode == DF_SNAP && element != EL_BD_ROCK)
9442 return MF_NO_ACTION;
9444 if (CAN_FALL(element) && dy)
9445 return MF_NO_ACTION;
9447 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9448 !(element == EL_SPRING && level.use_spring_bug))
9449 return MF_NO_ACTION;
9452 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9453 ((move_direction & MV_VERTICAL &&
9454 ((element_info[element].move_pattern & MV_LEFT &&
9455 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9456 (element_info[element].move_pattern & MV_RIGHT &&
9457 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9458 (move_direction & MV_HORIZONTAL &&
9459 ((element_info[element].move_pattern & MV_UP &&
9460 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9461 (element_info[element].move_pattern & MV_DOWN &&
9462 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9463 return MF_NO_ACTION;
9467 /* do not push elements already moving away faster than player */
9468 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9469 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9470 return MF_NO_ACTION;
9472 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9473 return MF_NO_ACTION;
9477 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9479 if (player->push_delay_value == -1)
9480 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9482 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9484 if (!player->is_pushing)
9485 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9489 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9490 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9491 !player_is_pushing))
9492 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9495 if (!player->is_pushing &&
9496 game.engine_version >= VERSION_IDENT(2,2,0,7))
9497 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9501 printf("::: push delay: %ld [%d, %d] [%d]\n",
9502 player->push_delay_value, FrameCounter, game.engine_version,
9503 player->is_pushing);
9506 player->is_pushing = TRUE;
9508 if (!(IN_LEV_FIELD(nextx, nexty) &&
9509 (IS_FREE(nextx, nexty) ||
9510 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9511 IS_SB_ELEMENT(element)))))
9512 return MF_NO_ACTION;
9514 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9515 return MF_NO_ACTION;
9517 if (player->push_delay == 0) /* new pushing; restart delay */
9518 player->push_delay = FrameCounter;
9520 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9521 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9522 element != EL_SPRING && element != EL_BALLOON)
9524 /* make sure that there is no move delay before next try to push */
9525 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9526 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9528 return MF_NO_ACTION;
9532 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9535 if (IS_SB_ELEMENT(element))
9537 if (element == EL_SOKOBAN_FIELD_FULL)
9539 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9540 local_player->sokobanfields_still_needed++;
9543 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9545 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9546 local_player->sokobanfields_still_needed--;
9549 Feld[x][y] = EL_SOKOBAN_OBJECT;
9551 if (Back[x][y] == Back[nextx][nexty])
9552 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9553 else if (Back[x][y] != 0)
9554 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9557 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9560 if (local_player->sokobanfields_still_needed == 0 &&
9561 game.emulation == EMU_SOKOBAN)
9563 player->LevelSolved = player->GameOver = TRUE;
9564 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9568 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9570 InitMovingField(x, y, move_direction);
9571 GfxAction[x][y] = ACTION_PUSHING;
9573 if (mode == DF_SNAP)
9574 ContinueMoving(x, y);
9576 MovPos[x][y] = (dx != 0 ? dx : dy);
9578 Pushed[x][y] = TRUE;
9579 Pushed[nextx][nexty] = TRUE;
9581 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9582 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9584 player->push_delay_value = -1; /* get new value later */
9586 CheckTriggeredElementSideChange(x, y, element, dig_side,
9587 CE_OTHER_GETS_PUSHED);
9588 CheckElementSideChange(x, y, element, dig_side,
9589 CE_PUSHED_BY_PLAYER, -1);
9593 else if (IS_SWITCHABLE(element))
9595 if (PLAYER_SWITCHING(player, x, y))
9598 player->is_switching = TRUE;
9599 player->switch_x = x;
9600 player->switch_y = y;
9602 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9604 if (element == EL_ROBOT_WHEEL)
9606 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9610 DrawLevelField(x, y);
9612 else if (element == EL_SP_TERMINAL)
9616 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9618 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9620 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9621 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9624 else if (IS_BELT_SWITCH(element))
9626 ToggleBeltSwitch(x, y);
9628 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9629 element == EL_SWITCHGATE_SWITCH_DOWN)
9631 ToggleSwitchgateSwitch(x, y);
9633 else if (element == EL_LIGHT_SWITCH ||
9634 element == EL_LIGHT_SWITCH_ACTIVE)
9636 ToggleLightSwitch(x, y);
9639 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9640 SND_LIGHT_SWITCH_ACTIVATING :
9641 SND_LIGHT_SWITCH_DEACTIVATING);
9644 else if (element == EL_TIMEGATE_SWITCH)
9646 ActivateTimegateSwitch(x, y);
9648 else if (element == EL_BALLOON_SWITCH_LEFT ||
9649 element == EL_BALLOON_SWITCH_RIGHT ||
9650 element == EL_BALLOON_SWITCH_UP ||
9651 element == EL_BALLOON_SWITCH_DOWN ||
9652 element == EL_BALLOON_SWITCH_ANY)
9654 if (element == EL_BALLOON_SWITCH_ANY)
9655 game.balloon_dir = move_direction;
9657 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9658 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9659 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9660 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9663 else if (element == EL_LAMP)
9665 Feld[x][y] = EL_LAMP_ACTIVE;
9666 local_player->lights_still_needed--;
9668 DrawLevelField(x, y);
9670 else if (element == EL_TIME_ORB_FULL)
9672 Feld[x][y] = EL_TIME_ORB_EMPTY;
9674 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9676 DrawLevelField(x, y);
9679 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9687 if (!PLAYER_SWITCHING(player, x, y))
9689 player->is_switching = TRUE;
9690 player->switch_x = x;
9691 player->switch_y = y;
9693 CheckTriggeredElementSideChange(x, y, element, dig_side,
9694 CE_OTHER_IS_SWITCHING);
9695 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9698 CheckTriggeredElementSideChange(x, y, element, dig_side,
9699 CE_OTHER_GETS_PRESSED);
9700 CheckElementSideChange(x, y, element, dig_side,
9701 CE_PRESSED_BY_PLAYER, -1);
9704 return MF_NO_ACTION;
9707 player->push_delay = 0;
9709 if (Feld[x][y] != element) /* really digged/collected something */
9710 player->is_collecting = !player->is_digging;
9715 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9717 int jx = player->jx, jy = player->jy;
9718 int x = jx + dx, y = jy + dy;
9719 int snap_direction = (dx == -1 ? MV_LEFT :
9720 dx == +1 ? MV_RIGHT :
9722 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9724 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9727 if (!player->active || !IN_LEV_FIELD(x, y))
9735 if (player->MovPos == 0)
9736 player->is_pushing = FALSE;
9738 player->is_snapping = FALSE;
9740 if (player->MovPos == 0)
9742 player->is_moving = FALSE;
9743 player->is_digging = FALSE;
9744 player->is_collecting = FALSE;
9750 if (player->is_snapping)
9753 player->MovDir = snap_direction;
9756 if (player->MovPos == 0)
9759 player->is_moving = FALSE;
9760 player->is_digging = FALSE;
9761 player->is_collecting = FALSE;
9764 player->is_dropping = FALSE;
9766 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9769 player->is_snapping = TRUE;
9772 if (player->MovPos == 0)
9775 player->is_moving = FALSE;
9776 player->is_digging = FALSE;
9777 player->is_collecting = FALSE;
9780 DrawLevelField(x, y);
9786 boolean DropElement(struct PlayerInfo *player)
9788 int jx = player->jx, jy = player->jy;
9789 int old_element = Feld[jx][jy];
9792 /* check if player is active, not moving and ready to drop */
9793 if (!player->active || player->MovPos || player->drop_delay > 0)
9796 /* check if player has anything that can be dropped */
9797 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9800 /* check if anything can be dropped at the current position */
9801 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9804 /* collected custom elements can only be dropped on empty fields */
9805 if (player->inventory_size > 0 &&
9806 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9807 && old_element != EL_EMPTY)
9810 if (old_element != EL_EMPTY)
9811 Back[jx][jy] = old_element; /* store old element on this field */
9813 ResetGfxAnimation(jx, jy);
9814 ResetRandomAnimationValue(jx, jy);
9816 if (player->inventory_size > 0)
9818 player->inventory_size--;
9819 new_element = player->inventory_element[player->inventory_size];
9821 if (new_element == EL_DYNAMITE)
9822 new_element = EL_DYNAMITE_ACTIVE;
9823 else if (new_element == EL_SP_DISK_RED)
9824 new_element = EL_SP_DISK_RED_ACTIVE;
9826 Feld[jx][jy] = new_element;
9828 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9829 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9831 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9832 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9834 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9837 /* needed if previous element just changed to "empty" in the last frame */
9838 Changed[jx][jy] = 0; /* allow another change */
9841 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9842 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9844 TestIfElementTouchesCustomElement(jx, jy);
9846 else /* player is dropping a dyna bomb */
9848 player->dynabombs_left--;
9849 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9851 Feld[jx][jy] = new_element;
9853 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9854 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9856 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9863 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9866 InitField_WithBug1(jx, jy, FALSE);
9868 InitField(jx, jy, FALSE);
9869 if (CAN_MOVE(Feld[jx][jy]))
9874 new_element = Feld[jx][jy];
9876 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9877 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9879 int move_stepsize = element_info[new_element].move_stepsize;
9880 int direction, dx, dy, nextx, nexty;
9882 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9883 MovDir[jx][jy] = player->MovDir;
9885 direction = MovDir[jx][jy];
9886 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9887 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9891 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9894 WasJustMoving[jx][jy] = 3;
9896 InitMovingField(jx, jy, direction);
9897 ContinueMoving(jx, jy);
9902 Changed[jx][jy] = 0; /* allow another change */
9905 TestIfElementHitsCustomElement(jx, jy, direction);
9907 CheckElementSideChange(jx, jy, new_element,
9908 direction, CE_HITTING_SOMETHING, -1);
9912 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9916 player->drop_delay = 8 + 8 + 8;
9921 player->is_dropping = TRUE;
9927 /* ------------------------------------------------------------------------- */
9928 /* game sound playing functions */
9929 /* ------------------------------------------------------------------------- */
9931 static int *loop_sound_frame = NULL;
9932 static int *loop_sound_volume = NULL;
9934 void InitPlayLevelSound()
9936 int num_sounds = getSoundListSize();
9938 checked_free(loop_sound_frame);
9939 checked_free(loop_sound_volume);
9941 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9942 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9945 static void PlayLevelSound(int x, int y, int nr)
9947 int sx = SCREENX(x), sy = SCREENY(y);
9948 int volume, stereo_position;
9949 int max_distance = 8;
9950 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9952 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9953 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9956 if (!IN_LEV_FIELD(x, y) ||
9957 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9958 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9961 volume = SOUND_MAX_VOLUME;
9963 if (!IN_SCR_FIELD(sx, sy))
9965 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9966 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9968 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9971 stereo_position = (SOUND_MAX_LEFT +
9972 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9973 (SCR_FIELDX + 2 * max_distance));
9975 if (IS_LOOP_SOUND(nr))
9977 /* This assures that quieter loop sounds do not overwrite louder ones,
9978 while restarting sound volume comparison with each new game frame. */
9980 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9983 loop_sound_volume[nr] = volume;
9984 loop_sound_frame[nr] = FrameCounter;
9987 PlaySoundExt(nr, volume, stereo_position, type);
9990 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9992 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9993 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9994 y < LEVELY(BY1) ? LEVELY(BY1) :
9995 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9999 static void PlayLevelSoundAction(int x, int y, int action)
10001 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10004 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10006 int sound_effect = element_info[element].sound[action];
10008 if (sound_effect != SND_UNDEFINED)
10009 PlayLevelSound(x, y, sound_effect);
10012 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10015 int sound_effect = element_info[element].sound[action];
10017 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10018 PlayLevelSound(x, y, sound_effect);
10021 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10023 int sound_effect = element_info[Feld[x][y]].sound[action];
10025 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10026 PlayLevelSound(x, y, sound_effect);
10029 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10031 int sound_effect = element_info[Feld[x][y]].sound[action];
10033 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10034 StopSound(sound_effect);
10037 static void PlayLevelMusic()
10039 if (levelset.music[level_nr] != MUS_UNDEFINED)
10040 PlayMusic(levelset.music[level_nr]); /* from config file */
10042 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10045 void RaiseScore(int value)
10047 local_player->score += value;
10048 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
10051 void RaiseScoreElement(int element)
10056 case EL_BD_DIAMOND:
10057 case EL_EMERALD_YELLOW:
10058 case EL_EMERALD_RED:
10059 case EL_EMERALD_PURPLE:
10060 case EL_SP_INFOTRON:
10061 RaiseScore(level.score[SC_EMERALD]);
10064 RaiseScore(level.score[SC_DIAMOND]);
10067 RaiseScore(level.score[SC_CRYSTAL]);
10070 RaiseScore(level.score[SC_PEARL]);
10073 case EL_BD_BUTTERFLY:
10074 case EL_SP_ELECTRON:
10075 RaiseScore(level.score[SC_BUG]);
10078 case EL_BD_FIREFLY:
10079 case EL_SP_SNIKSNAK:
10080 RaiseScore(level.score[SC_SPACESHIP]);
10083 case EL_DARK_YAMYAM:
10084 RaiseScore(level.score[SC_YAMYAM]);
10087 RaiseScore(level.score[SC_ROBOT]);
10090 RaiseScore(level.score[SC_PACMAN]);
10093 RaiseScore(level.score[SC_NUT]);
10096 case EL_SP_DISK_RED:
10097 case EL_DYNABOMB_INCREASE_NUMBER:
10098 case EL_DYNABOMB_INCREASE_SIZE:
10099 case EL_DYNABOMB_INCREASE_POWER:
10100 RaiseScore(level.score[SC_DYNAMITE]);
10102 case EL_SHIELD_NORMAL:
10103 case EL_SHIELD_DEADLY:
10104 RaiseScore(level.score[SC_SHIELD]);
10106 case EL_EXTRA_TIME:
10107 RaiseScore(level.score[SC_TIME_BONUS]);
10113 RaiseScore(level.score[SC_KEY]);
10116 RaiseScore(element_info[element].collect_score);
10121 void RequestQuitGame(boolean ask_if_really_quit)
10123 if (AllPlayersGone ||
10124 !ask_if_really_quit ||
10125 level_editor_test_game ||
10126 Request("Do you really want to quit the game ?",
10127 REQ_ASK | REQ_STAY_CLOSED))
10129 #if defined(PLATFORM_UNIX)
10130 if (options.network)
10131 SendToServer_StopPlaying();
10135 game_status = GAME_MODE_MAIN;
10141 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10146 /* ---------- new game button stuff ---------------------------------------- */
10148 /* graphic position values for game buttons */
10149 #define GAME_BUTTON_XSIZE 30
10150 #define GAME_BUTTON_YSIZE 30
10151 #define GAME_BUTTON_XPOS 5
10152 #define GAME_BUTTON_YPOS 215
10153 #define SOUND_BUTTON_XPOS 5
10154 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10156 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10157 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10158 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10159 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10160 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10161 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10168 } gamebutton_info[NUM_GAME_BUTTONS] =
10171 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10176 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10177 GAME_CTRL_ID_PAUSE,
10181 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10186 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10187 SOUND_CTRL_ID_MUSIC,
10188 "background music on/off"
10191 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10192 SOUND_CTRL_ID_LOOPS,
10193 "sound loops on/off"
10196 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10197 SOUND_CTRL_ID_SIMPLE,
10198 "normal sounds on/off"
10202 void CreateGameButtons()
10206 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10208 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10209 struct GadgetInfo *gi;
10212 unsigned long event_mask;
10213 int gd_xoffset, gd_yoffset;
10214 int gd_x1, gd_x2, gd_y1, gd_y2;
10217 gd_xoffset = gamebutton_info[i].x;
10218 gd_yoffset = gamebutton_info[i].y;
10219 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10220 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10222 if (id == GAME_CTRL_ID_STOP ||
10223 id == GAME_CTRL_ID_PAUSE ||
10224 id == GAME_CTRL_ID_PLAY)
10226 button_type = GD_TYPE_NORMAL_BUTTON;
10228 event_mask = GD_EVENT_RELEASED;
10229 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10230 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10234 button_type = GD_TYPE_CHECK_BUTTON;
10236 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10237 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10238 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10239 event_mask = GD_EVENT_PRESSED;
10240 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10241 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10244 gi = CreateGadget(GDI_CUSTOM_ID, id,
10245 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10246 GDI_X, DX + gd_xoffset,
10247 GDI_Y, DY + gd_yoffset,
10248 GDI_WIDTH, GAME_BUTTON_XSIZE,
10249 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10250 GDI_TYPE, button_type,
10251 GDI_STATE, GD_BUTTON_UNPRESSED,
10252 GDI_CHECKED, checked,
10253 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10254 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10255 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10256 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10257 GDI_EVENT_MASK, event_mask,
10258 GDI_CALLBACK_ACTION, HandleGameButtons,
10262 Error(ERR_EXIT, "cannot create gadget");
10264 game_gadget[id] = gi;
10268 void FreeGameButtons()
10272 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10273 FreeGadget(game_gadget[i]);
10276 static void MapGameButtons()
10280 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10281 MapGadget(game_gadget[i]);
10284 void UnmapGameButtons()
10288 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10289 UnmapGadget(game_gadget[i]);
10292 static void HandleGameButtons(struct GadgetInfo *gi)
10294 int id = gi->custom_id;
10296 if (game_status != GAME_MODE_PLAYING)
10301 case GAME_CTRL_ID_STOP:
10302 RequestQuitGame(TRUE);
10305 case GAME_CTRL_ID_PAUSE:
10306 if (options.network)
10308 #if defined(PLATFORM_UNIX)
10310 SendToServer_ContinuePlaying();
10312 SendToServer_PausePlaying();
10316 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10319 case GAME_CTRL_ID_PLAY:
10322 #if defined(PLATFORM_UNIX)
10323 if (options.network)
10324 SendToServer_ContinuePlaying();
10328 tape.pausing = FALSE;
10329 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10334 case SOUND_CTRL_ID_MUSIC:
10335 if (setup.sound_music)
10337 setup.sound_music = FALSE;
10340 else if (audio.music_available)
10342 setup.sound = setup.sound_music = TRUE;
10344 SetAudioMode(setup.sound);
10350 case SOUND_CTRL_ID_LOOPS:
10351 if (setup.sound_loops)
10352 setup.sound_loops = FALSE;
10353 else if (audio.loops_available)
10355 setup.sound = setup.sound_loops = TRUE;
10356 SetAudioMode(setup.sound);
10360 case SOUND_CTRL_ID_SIMPLE:
10361 if (setup.sound_simple)
10362 setup.sound_simple = FALSE;
10363 else if (audio.sound_available)
10365 setup.sound = setup.sound_simple = TRUE;
10366 SetAudioMode(setup.sound);