1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
106 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (DONT_COLLIDE_WITH(e) && \
110 !PLAYER_PROTECTED(x, y))))
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
113 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
115 (DONT_COLLIDE_WITH(e) && \
116 IS_FREE_OR_PLAYER(x, y))))
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
120 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
124 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
127 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
130 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
134 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
135 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
136 Feld[x][y] == EL_DIAMOND))
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
142 #define PACMAN_CAN_ENTER_FIELD(x, y) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
144 IS_AMOEBOID(Feld[x][y])))
146 #define PIG_CAN_ENTER_FIELD(x, y) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 IS_FOOD_PIG(Feld[x][y])))
150 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 IS_FOOD_PENGUIN(Feld[x][y]) || \
153 Feld[x][y] == EL_EXIT_OPEN || \
154 Feld[x][y] == EL_ACID))
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
159 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
167 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
168 #define IS_IN_GROUP(e, g) (element_info[e].in_group[g] == TRUE)
169 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
171 #define CE_ENTER_FIELD_COND(e, x, y) \
172 (!IS_PLAYER(x, y) && \
173 (Feld[x][y] == EL_ACID || \
174 Feld[x][y] == MOVE_ENTER_EL(e) || \
175 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
176 IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
178 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
181 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
182 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
184 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
185 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
187 /* game button identifiers */
188 #define GAME_CTRL_ID_STOP 0
189 #define GAME_CTRL_ID_PAUSE 1
190 #define GAME_CTRL_ID_PLAY 2
191 #define SOUND_CTRL_ID_MUSIC 3
192 #define SOUND_CTRL_ID_LOOPS 4
193 #define SOUND_CTRL_ID_SIMPLE 5
195 #define NUM_GAME_BUTTONS 6
198 /* forward declaration for internal use */
200 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
201 static boolean MovePlayer(struct PlayerInfo *, int, int);
202 static void ScrollPlayer(struct PlayerInfo *, int);
203 static void ScrollScreen(struct PlayerInfo *, int);
205 static void InitBeltMovement(void);
206 static void CloseAllOpenTimegates(void);
207 static void CheckGravityMovement(struct PlayerInfo *);
208 static void KillHeroUnlessProtected(int, int);
210 static void TestIfPlayerTouchesCustomElement(int, int);
211 static void TestIfElementTouchesCustomElement(int, int);
212 static void TestIfElementHitsCustomElement(int, int, int);
214 static void ChangeElement(int, int, int);
215 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
216 static boolean CheckTriggeredElementChange(int, int, int, int);
217 static boolean CheckElementSideChange(int, int, int, int, int, int);
218 static boolean CheckElementChange(int, int, int, int);
220 static void PlayLevelSound(int, int, int);
221 static void PlayLevelSoundNearest(int, int, int);
222 static void PlayLevelSoundAction(int, int, int);
223 static void PlayLevelSoundElementAction(int, int, int, int);
224 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
225 static void PlayLevelSoundActionIfLoop(int, int, int);
226 static void StopLevelSoundActionIfLoop(int, int, int);
227 static void PlayLevelMusic();
229 static void MapGameButtons();
230 static void HandleGameButtons(struct GadgetInfo *);
232 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
235 /* ------------------------------------------------------------------------- */
236 /* definition of elements that automatically change to other elements after */
237 /* a specified time, eventually calling a function when changing */
238 /* ------------------------------------------------------------------------- */
240 /* forward declaration for changer functions */
241 static void InitBuggyBase(int x, int y);
242 static void WarnBuggyBase(int x, int y);
244 static void InitTrap(int x, int y);
245 static void ActivateTrap(int x, int y);
246 static void ChangeActiveTrap(int x, int y);
248 static void InitRobotWheel(int x, int y);
249 static void RunRobotWheel(int x, int y);
250 static void StopRobotWheel(int x, int y);
252 static void InitTimegateWheel(int x, int y);
253 static void RunTimegateWheel(int x, int y);
255 struct ChangingElementInfo
260 void (*pre_change_function)(int x, int y);
261 void (*change_function)(int x, int y);
262 void (*post_change_function)(int x, int y);
265 static struct ChangingElementInfo change_delay_list[] =
316 EL_SWITCHGATE_OPENING,
324 EL_SWITCHGATE_CLOSING,
325 EL_SWITCHGATE_CLOSED,
357 EL_ACID_SPLASH_RIGHT,
366 EL_SP_BUGGY_BASE_ACTIVATING,
373 EL_SP_BUGGY_BASE_ACTIVATING,
374 EL_SP_BUGGY_BASE_ACTIVE,
381 EL_SP_BUGGY_BASE_ACTIVE,
405 EL_ROBOT_WHEEL_ACTIVE,
413 EL_TIMEGATE_SWITCH_ACTIVE,
434 int push_delay_fixed, push_delay_random;
439 { EL_BALLOON, 0, 0 },
441 { EL_SOKOBAN_OBJECT, 2, 0 },
442 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
443 { EL_SATELLITE, 2, 0 },
444 { EL_SP_DISK_YELLOW, 2, 0 },
446 { EL_UNDEFINED, 0, 0 },
454 move_stepsize_list[] =
456 { EL_AMOEBA_DROP, 2 },
457 { EL_AMOEBA_DROPPING, 2 },
458 { EL_QUICKSAND_FILLING, 1 },
459 { EL_QUICKSAND_EMPTYING, 1 },
460 { EL_MAGIC_WALL_FILLING, 2 },
461 { EL_BD_MAGIC_WALL_FILLING, 2 },
462 { EL_MAGIC_WALL_EMPTYING, 2 },
463 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
473 collect_count_list[] =
476 { EL_BD_DIAMOND, 1 },
477 { EL_EMERALD_YELLOW, 1 },
478 { EL_EMERALD_RED, 1 },
479 { EL_EMERALD_PURPLE, 1 },
481 { EL_SP_INFOTRON, 1 },
488 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
490 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
491 CH_EVENT_BIT(CE_DELAY))
492 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
493 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
494 IS_JUST_CHANGING(x, y))
496 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
499 void GetPlayerConfig()
501 if (!audio.sound_available)
502 setup.sound_simple = FALSE;
504 if (!audio.loops_available)
505 setup.sound_loops = FALSE;
507 if (!audio.music_available)
508 setup.sound_music = FALSE;
510 if (!video.fullscreen_available)
511 setup.fullscreen = FALSE;
513 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
515 SetAudioMode(setup.sound);
519 static int getBeltNrFromBeltElement(int element)
521 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
522 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
523 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
526 static int getBeltNrFromBeltActiveElement(int element)
528 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
529 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
530 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
533 static int getBeltNrFromBeltSwitchElement(int element)
535 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
536 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
537 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
540 static int getBeltDirNrFromBeltSwitchElement(int element)
542 static int belt_base_element[4] =
544 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
545 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
546 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
547 EL_CONVEYOR_BELT_4_SWITCH_LEFT
550 int belt_nr = getBeltNrFromBeltSwitchElement(element);
551 int belt_dir_nr = element - belt_base_element[belt_nr];
553 return (belt_dir_nr % 3);
556 static int getBeltDirFromBeltSwitchElement(int element)
558 static int belt_move_dir[3] =
565 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
567 return belt_move_dir[belt_dir_nr];
570 static void InitPlayerField(int x, int y, int element, boolean init_game)
572 if (element == EL_SP_MURPHY)
576 if (stored_player[0].present)
578 Feld[x][y] = EL_SP_MURPHY_CLONE;
584 stored_player[0].use_murphy_graphic = TRUE;
587 Feld[x][y] = EL_PLAYER_1;
593 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
594 int jx = player->jx, jy = player->jy;
596 player->present = TRUE;
598 if (!options.network || player->connected)
600 player->active = TRUE;
602 /* remove potentially duplicate players */
603 if (StorePlayer[jx][jy] == Feld[x][y])
604 StorePlayer[jx][jy] = 0;
606 StorePlayer[x][y] = Feld[x][y];
610 printf("Player %d activated.\n", player->element_nr);
611 printf("[Local player is %d and currently %s.]\n",
612 local_player->element_nr,
613 local_player->active ? "active" : "not active");
617 Feld[x][y] = EL_EMPTY;
618 player->jx = player->last_jx = x;
619 player->jy = player->last_jy = y;
623 static void InitField(int x, int y, boolean init_game)
625 int element = Feld[x][y];
634 InitPlayerField(x, y, element, init_game);
637 case EL_SOKOBAN_FIELD_PLAYER:
638 element = Feld[x][y] = EL_PLAYER_1;
639 InitField(x, y, init_game);
641 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
642 InitField(x, y, init_game);
645 case EL_SOKOBAN_FIELD_EMPTY:
646 local_player->sokobanfields_still_needed++;
650 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
651 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
652 else if (x > 0 && Feld[x-1][y] == EL_ACID)
653 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
654 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
655 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
656 else if (y > 0 && Feld[x][y-1] == EL_ACID)
657 Feld[x][y] = EL_ACID_POOL_BOTTOM;
658 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
659 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
667 case EL_SPACESHIP_RIGHT:
668 case EL_SPACESHIP_UP:
669 case EL_SPACESHIP_LEFT:
670 case EL_SPACESHIP_DOWN:
672 case EL_BD_BUTTERFLY_RIGHT:
673 case EL_BD_BUTTERFLY_UP:
674 case EL_BD_BUTTERFLY_LEFT:
675 case EL_BD_BUTTERFLY_DOWN:
676 case EL_BD_BUTTERFLY:
677 case EL_BD_FIREFLY_RIGHT:
678 case EL_BD_FIREFLY_UP:
679 case EL_BD_FIREFLY_LEFT:
680 case EL_BD_FIREFLY_DOWN:
682 case EL_PACMAN_RIGHT:
706 if (y == lev_fieldy - 1)
708 Feld[x][y] = EL_AMOEBA_GROWING;
709 Store[x][y] = EL_AMOEBA_WET;
713 case EL_DYNAMITE_ACTIVE:
714 case EL_SP_DISK_RED_ACTIVE:
715 case EL_DYNABOMB_PLAYER_1_ACTIVE:
716 case EL_DYNABOMB_PLAYER_2_ACTIVE:
717 case EL_DYNABOMB_PLAYER_3_ACTIVE:
718 case EL_DYNABOMB_PLAYER_4_ACTIVE:
723 local_player->lights_still_needed++;
727 local_player->friends_still_needed++;
732 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
737 Feld[x][y] = EL_EMPTY;
742 case EL_EM_KEY_1_FILE:
743 Feld[x][y] = EL_EM_KEY_1;
745 case EL_EM_KEY_2_FILE:
746 Feld[x][y] = EL_EM_KEY_2;
748 case EL_EM_KEY_3_FILE:
749 Feld[x][y] = EL_EM_KEY_3;
751 case EL_EM_KEY_4_FILE:
752 Feld[x][y] = EL_EM_KEY_4;
756 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
757 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
758 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
759 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
760 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
761 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
762 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
763 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
764 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
765 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
766 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
767 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
770 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
771 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
772 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
774 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
776 game.belt_dir[belt_nr] = belt_dir;
777 game.belt_dir_nr[belt_nr] = belt_dir_nr;
779 else /* more than one switch -- set it like the first switch */
781 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
786 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
788 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
791 case EL_LIGHT_SWITCH_ACTIVE:
793 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
797 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
799 else if (IS_GROUP_ELEMENT(element))
801 struct ElementGroupInfo *group = element_info[element].group;
802 int random_pos = RND(group->num_elements_resolved);
804 Feld[x][y] = group->element_resolved[random_pos];
806 InitField(x, y, init_game);
812 void DrawGameDoorValues()
816 for (i = 0; i < MAX_PLAYERS; i++)
817 for (j = 0; j < 4; j++)
818 if (stored_player[i].key[j])
819 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
820 el2edimg(EL_KEY_1 + j));
822 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
823 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
824 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
825 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
826 DrawText(DX + XX_SCORE, DY + YY_SCORE,
827 int2str(local_player->score, 5), FONT_TEXT_2);
828 DrawText(DX + XX_TIME, DY + YY_TIME,
829 int2str(TimeLeft, 3), FONT_TEXT_2);
832 static void resolve_group_element(int group_element, int recursion_depth)
835 static struct ElementGroupInfo *group;
836 struct ElementGroupInfo *actual_group = element_info[group_element].group;
839 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
841 Error(ERR_WARN, "recursion too deep when resolving group element %d",
842 group_element - EL_GROUP_START + 1);
844 /* replace element which caused too deep recursion by question mark */
845 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
850 if (recursion_depth == 0) /* initialization */
852 group = element_info[group_element].group;
853 group->num_elements_resolved = 0;
854 group_nr = group_element - EL_GROUP_START;
857 for (i = 0; i < actual_group->num_elements; i++)
859 int element = actual_group->element[i];
861 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
864 if (IS_GROUP_ELEMENT(element))
865 resolve_group_element(element, recursion_depth + 1);
868 group->element_resolved[group->num_elements_resolved++] = element;
869 element_info[element].in_group[group_nr] = TRUE;
874 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
876 printf("::: group %d: %d resolved elements\n",
877 group_element - EL_GROUP_START, group->num_elements_resolved);
878 for (i = 0; i < group->num_elements_resolved; i++)
879 printf("::: - %d ['%s']\n", group->element_resolved[i],
880 element_info[group->element_resolved[i]].token_name);
887 =============================================================================
889 -----------------------------------------------------------------------------
890 initialize game engine due to level / tape version number
891 =============================================================================
894 static void InitGameEngine()
898 /* set game engine from tape file when re-playing, else from level file */
899 game.engine_version = (tape.playing ? tape.engine_version :
902 /* dynamically adjust element properties according to game engine version */
903 InitElementPropertiesEngine(game.engine_version);
906 printf("level %d: level version == %06d\n", level_nr, level.game_version);
907 printf(" tape version == %06d [%s] [file: %06d]\n",
908 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
910 printf(" => game.engine_version == %06d\n", game.engine_version);
913 /* ---------- recursively resolve group elements ------------------------- */
915 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
916 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
917 element_info[i].in_group[j] = FALSE;
919 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
920 resolve_group_element(EL_GROUP_START + i, 0);
922 /* ---------- initialize player's initial move delay --------------------- */
924 /* dynamically adjust player properties according to game engine version */
925 game.initial_move_delay =
926 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
927 INITIAL_MOVE_DELAY_OFF);
929 /* dynamically adjust player properties according to level information */
930 game.initial_move_delay_value =
931 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
933 /* ---------- initialize player's initial push delay --------------------- */
935 /* dynamically adjust player properties according to game engine version */
936 game.initial_push_delay_value =
937 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
939 /* ---------- initialize changing elements ------------------------------- */
941 /* initialize changing elements information */
942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
944 struct ElementInfo *ei = &element_info[i];
946 /* this pointer might have been changed in the level editor */
947 ei->change = &ei->change_page[0];
949 if (!IS_CUSTOM_ELEMENT(i))
951 ei->change->target_element = EL_EMPTY_SPACE;
952 ei->change->delay_fixed = 0;
953 ei->change->delay_random = 0;
954 ei->change->delay_frames = 1;
957 ei->change_events = CE_BITMASK_DEFAULT;
958 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
960 ei->event_page_nr[j] = 0;
961 ei->event_page[j] = &ei->change_page[0];
965 /* add changing elements from pre-defined list */
966 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
968 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
969 struct ElementInfo *ei = &element_info[ch_delay->element];
971 ei->change->target_element = ch_delay->target_element;
972 ei->change->delay_fixed = ch_delay->change_delay;
974 ei->change->pre_change_function = ch_delay->pre_change_function;
975 ei->change->change_function = ch_delay->change_function;
976 ei->change->post_change_function = ch_delay->post_change_function;
978 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
982 /* add change events from custom element configuration */
983 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
985 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
987 for (j = 0; j < ei->num_change_pages; j++)
989 if (!ei->change_page[j].can_change)
992 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
994 /* only add event page for the first page found with this event */
995 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
996 !(ei->change_events & CH_EVENT_BIT(k)))
998 ei->change_events |= CH_EVENT_BIT(k);
999 ei->event_page_nr[k] = j;
1000 ei->event_page[k] = &ei->change_page[j];
1008 /* add change events from custom element configuration */
1009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1011 int element = EL_CUSTOM_START + i;
1013 /* only add custom elements that change after fixed/random frame delay */
1014 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1015 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1019 /* ---------- initialize trigger events ---------------------------------- */
1021 /* initialize trigger events information */
1022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1023 trigger_events[i] = EP_BITMASK_DEFAULT;
1026 /* add trigger events from element change event properties */
1027 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1029 struct ElementInfo *ei = &element_info[i];
1031 for (j = 0; j < ei->num_change_pages; j++)
1033 if (!ei->change_page[j].can_change)
1036 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1038 int trigger_element = ei->change_page[j].trigger_element;
1040 trigger_events[trigger_element] |= ei->change_page[j].events;
1045 /* add trigger events from element change event properties */
1046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1047 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1048 trigger_events[element_info[i].change->trigger_element] |=
1049 element_info[i].change->events;
1052 /* ---------- initialize push delay -------------------------------------- */
1054 /* initialize push delay values to default */
1055 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1057 if (!IS_CUSTOM_ELEMENT(i))
1059 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1060 element_info[i].push_delay_random = game.default_push_delay_random;
1064 /* set push delay value for certain elements from pre-defined list */
1065 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1067 int e = push_delay_list[i].element;
1069 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1070 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1073 /* ---------- initialize move stepsize ----------------------------------- */
1075 /* initialize move stepsize values to default */
1076 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1077 if (!IS_CUSTOM_ELEMENT(i))
1078 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1080 /* set move stepsize value for certain elements from pre-defined list */
1081 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1083 int e = move_stepsize_list[i].element;
1085 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1088 /* ---------- initialize gem count --------------------------------------- */
1090 /* initialize gem count values for each element */
1091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1092 if (!IS_CUSTOM_ELEMENT(i))
1093 element_info[i].collect_count = 0;
1095 /* add gem count values for all elements from pre-defined list */
1096 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1097 element_info[collect_count_list[i].element].collect_count =
1098 collect_count_list[i].count;
1103 =============================================================================
1105 -----------------------------------------------------------------------------
1106 initialize and start new game
1107 =============================================================================
1112 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1113 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1114 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1121 #if USE_NEW_AMOEBA_CODE
1122 printf("Using new amoeba code.\n");
1124 printf("Using old amoeba code.\n");
1129 /* don't play tapes over network */
1130 network_playing = (options.network && !tape.playing);
1132 for (i = 0; i < MAX_PLAYERS; i++)
1134 struct PlayerInfo *player = &stored_player[i];
1136 player->index_nr = i;
1137 player->element_nr = EL_PLAYER_1 + i;
1139 player->present = FALSE;
1140 player->active = FALSE;
1143 player->effective_action = 0;
1144 player->programmed_action = 0;
1147 player->gems_still_needed = level.gems_needed;
1148 player->sokobanfields_still_needed = 0;
1149 player->lights_still_needed = 0;
1150 player->friends_still_needed = 0;
1152 for (j = 0; j < 4; j++)
1153 player->key[j] = FALSE;
1155 player->dynabomb_count = 0;
1156 player->dynabomb_size = 1;
1157 player->dynabombs_left = 0;
1158 player->dynabomb_xl = FALSE;
1160 player->MovDir = MV_NO_MOVING;
1163 player->GfxDir = MV_NO_MOVING;
1164 player->GfxAction = ACTION_DEFAULT;
1166 player->StepFrame = 0;
1168 player->use_murphy_graphic = FALSE;
1170 player->actual_frame_counter = 0;
1172 player->step_counter = 0;
1174 player->last_move_dir = MV_NO_MOVING;
1176 player->is_waiting = FALSE;
1177 player->is_moving = FALSE;
1178 player->is_digging = FALSE;
1179 player->is_snapping = FALSE;
1180 player->is_collecting = FALSE;
1181 player->is_pushing = FALSE;
1182 player->is_switching = FALSE;
1183 player->is_dropping = FALSE;
1185 player->is_bored = FALSE;
1186 player->is_sleeping = FALSE;
1188 player->frame_counter_bored = -1;
1189 player->frame_counter_sleeping = -1;
1191 player->anim_delay_counter = 0;
1192 player->post_delay_counter = 0;
1194 player->action_waiting = ACTION_DEFAULT;
1195 player->last_action_waiting = ACTION_DEFAULT;
1196 player->special_action_bored = ACTION_DEFAULT;
1197 player->special_action_sleeping = ACTION_DEFAULT;
1199 player->num_special_action_bored = 0;
1200 player->num_special_action_sleeping = 0;
1202 /* determine number of special actions for bored and sleeping animation */
1203 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1205 boolean found = FALSE;
1207 for (k = 0; k < NUM_DIRECTIONS; k++)
1208 if (el_act_dir2img(player->element_nr, j, k) !=
1209 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1213 player->num_special_action_bored++;
1217 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1219 boolean found = FALSE;
1221 for (k = 0; k < NUM_DIRECTIONS; k++)
1222 if (el_act_dir2img(player->element_nr, j, k) !=
1223 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1227 player->num_special_action_sleeping++;
1232 player->switch_x = -1;
1233 player->switch_y = -1;
1235 player->show_envelope = 0;
1237 player->move_delay = game.initial_move_delay;
1238 player->move_delay_value = game.initial_move_delay_value;
1240 player->move_delay_reset_counter = 0;
1242 player->push_delay = 0;
1243 player->push_delay_value = game.initial_push_delay_value;
1245 player->drop_delay = 0;
1247 player->last_jx = player->last_jy = 0;
1248 player->jx = player->jy = 0;
1250 player->shield_normal_time_left = 0;
1251 player->shield_deadly_time_left = 0;
1253 player->inventory_size = 0;
1255 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1256 SnapField(player, 0, 0);
1258 player->LevelSolved = FALSE;
1259 player->GameOver = FALSE;
1262 network_player_action_received = FALSE;
1264 #if defined(PLATFORM_UNIX)
1265 /* initial null action */
1266 if (network_playing)
1267 SendToServer_MovePlayer(MV_NO_MOVING);
1275 TimeLeft = level.time;
1277 ScreenMovDir = MV_NO_MOVING;
1281 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1283 AllPlayersGone = FALSE;
1285 game.yamyam_content_nr = 0;
1286 game.magic_wall_active = FALSE;
1287 game.magic_wall_time_left = 0;
1288 game.light_time_left = 0;
1289 game.timegate_time_left = 0;
1290 game.switchgate_pos = 0;
1291 game.balloon_dir = MV_NO_MOVING;
1292 game.gravity = level.initial_gravity;
1293 game.explosions_delayed = TRUE;
1295 game.envelope_active = FALSE;
1297 for (i = 0; i < 4; i++)
1299 game.belt_dir[i] = MV_NO_MOVING;
1300 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1303 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1304 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1306 for (x = 0; x < lev_fieldx; x++)
1308 for (y = 0; y < lev_fieldy; y++)
1310 Feld[x][y] = level.field[x][y];
1311 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1312 ChangeDelay[x][y] = 0;
1313 ChangePage[x][y] = -1;
1314 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1316 WasJustMoving[x][y] = 0;
1317 WasJustFalling[x][y] = 0;
1319 Pushed[x][y] = FALSE;
1321 Changed[x][y] = CE_BITMASK_DEFAULT;
1322 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1324 ExplodePhase[x][y] = 0;
1325 ExplodeField[x][y] = EX_NO_EXPLOSION;
1327 RunnerVisit[x][y] = 0;
1328 PlayerVisit[x][y] = 0;
1331 GfxRandom[x][y] = INIT_GFX_RANDOM();
1332 GfxElement[x][y] = EL_UNDEFINED;
1333 GfxAction[x][y] = ACTION_DEFAULT;
1334 GfxDir[x][y] = MV_NO_MOVING;
1338 for (y = 0; y < lev_fieldy; y++)
1340 for (x = 0; x < lev_fieldx; x++)
1342 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1344 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1346 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1349 InitField(x, y, TRUE);
1355 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1356 emulate_sb ? EMU_SOKOBAN :
1357 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1359 /* correct non-moving belts to start moving left */
1360 for (i = 0; i < 4; i++)
1361 if (game.belt_dir[i] == MV_NO_MOVING)
1362 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1364 /* check if any connected player was not found in playfield */
1365 for (i = 0; i < MAX_PLAYERS; i++)
1367 struct PlayerInfo *player = &stored_player[i];
1369 if (player->connected && !player->present)
1371 for (j = 0; j < MAX_PLAYERS; j++)
1373 struct PlayerInfo *some_player = &stored_player[j];
1374 int jx = some_player->jx, jy = some_player->jy;
1376 /* assign first free player found that is present in the playfield */
1377 if (some_player->present && !some_player->connected)
1379 player->present = TRUE;
1380 player->active = TRUE;
1381 some_player->present = FALSE;
1383 StorePlayer[jx][jy] = player->element_nr;
1384 player->jx = player->last_jx = jx;
1385 player->jy = player->last_jy = jy;
1395 /* when playing a tape, eliminate all players who do not participate */
1397 for (i = 0; i < MAX_PLAYERS; i++)
1399 if (stored_player[i].active && !tape.player_participates[i])
1401 struct PlayerInfo *player = &stored_player[i];
1402 int jx = player->jx, jy = player->jy;
1404 player->active = FALSE;
1405 StorePlayer[jx][jy] = 0;
1406 Feld[jx][jy] = EL_EMPTY;
1410 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1412 /* when in single player mode, eliminate all but the first active player */
1414 for (i = 0; i < MAX_PLAYERS; i++)
1416 if (stored_player[i].active)
1418 for (j = i + 1; j < MAX_PLAYERS; j++)
1420 if (stored_player[j].active)
1422 struct PlayerInfo *player = &stored_player[j];
1423 int jx = player->jx, jy = player->jy;
1425 player->active = FALSE;
1426 StorePlayer[jx][jy] = 0;
1427 Feld[jx][jy] = EL_EMPTY;
1434 /* when recording the game, store which players take part in the game */
1437 for (i = 0; i < MAX_PLAYERS; i++)
1438 if (stored_player[i].active)
1439 tape.player_participates[i] = TRUE;
1444 for (i = 0; i < MAX_PLAYERS; i++)
1446 struct PlayerInfo *player = &stored_player[i];
1448 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1453 if (local_player == player)
1454 printf("Player %d is local player.\n", i+1);
1458 if (BorderElement == EL_EMPTY)
1461 SBX_Right = lev_fieldx - SCR_FIELDX;
1463 SBY_Lower = lev_fieldy - SCR_FIELDY;
1468 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1470 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1473 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1474 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1476 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1477 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1479 /* if local player not found, look for custom element that might create
1480 the player (make some assumptions about the right custom element) */
1481 if (!local_player->present)
1483 int start_x = 0, start_y = 0;
1484 int found_rating = 0;
1485 int found_element = EL_UNDEFINED;
1487 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1489 int element = Feld[x][y];
1494 if (!IS_CUSTOM_ELEMENT(element))
1497 if (CAN_CHANGE(element))
1499 for (i = 0; i < element_info[element].num_change_pages; i++)
1501 content = element_info[element].change_page[i].target_element;
1502 is_player = ELEM_IS_PLAYER(content);
1504 if (is_player && (found_rating < 3 || element < found_element))
1510 found_element = element;
1515 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1517 content = element_info[element].content[xx][yy];
1518 is_player = ELEM_IS_PLAYER(content);
1520 if (is_player && (found_rating < 2 || element < found_element))
1522 start_x = x + xx - 1;
1523 start_y = y + yy - 1;
1526 found_element = element;
1529 if (!CAN_CHANGE(element))
1532 for (i = 0; i < element_info[element].num_change_pages; i++)
1534 content = element_info[element].change_page[i].content[xx][yy];
1535 is_player = ELEM_IS_PLAYER(content);
1537 if (is_player && (found_rating < 1 || element < found_element))
1539 start_x = x + xx - 1;
1540 start_y = y + yy - 1;
1543 found_element = element;
1549 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1550 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1553 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1554 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1560 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1561 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1562 local_player->jx - MIDPOSX);
1564 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1565 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1566 local_player->jy - MIDPOSY);
1568 scroll_x = SBX_Left;
1569 scroll_y = SBY_Upper;
1570 if (local_player->jx >= SBX_Left + MIDPOSX)
1571 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1572 local_player->jx - MIDPOSX :
1574 if (local_player->jy >= SBY_Upper + MIDPOSY)
1575 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1576 local_player->jy - MIDPOSY :
1581 CloseDoor(DOOR_CLOSE_1);
1586 /* after drawing the level, correct some elements */
1587 if (game.timegate_time_left == 0)
1588 CloseAllOpenTimegates();
1590 if (setup.soft_scrolling)
1591 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1593 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1596 /* copy default game door content to main double buffer */
1597 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1598 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1601 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1604 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1605 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1606 BlitBitmap(drawto, drawto,
1607 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1608 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1609 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1610 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1613 DrawGameDoorValues();
1617 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1618 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1619 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1623 /* copy actual game door content to door double buffer for OpenDoor() */
1624 BlitBitmap(drawto, bitmap_db_door,
1625 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1627 OpenDoor(DOOR_OPEN_ALL);
1629 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1631 if (setup.sound_music)
1634 KeyboardAutoRepeatOffUnlessAutoplay();
1638 for (i = 0; i < 4; i++)
1639 printf("Player %d %sactive.\n",
1640 i + 1, (stored_player[i].active ? "" : "not "));
1644 printf("::: starting game [%d]\n", FrameCounter);
1648 void InitMovDir(int x, int y)
1650 int i, element = Feld[x][y];
1651 static int xy[4][2] =
1658 static int direction[3][4] =
1660 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1661 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1662 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1671 Feld[x][y] = EL_BUG;
1672 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1675 case EL_SPACESHIP_RIGHT:
1676 case EL_SPACESHIP_UP:
1677 case EL_SPACESHIP_LEFT:
1678 case EL_SPACESHIP_DOWN:
1679 Feld[x][y] = EL_SPACESHIP;
1680 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1683 case EL_BD_BUTTERFLY_RIGHT:
1684 case EL_BD_BUTTERFLY_UP:
1685 case EL_BD_BUTTERFLY_LEFT:
1686 case EL_BD_BUTTERFLY_DOWN:
1687 Feld[x][y] = EL_BD_BUTTERFLY;
1688 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1691 case EL_BD_FIREFLY_RIGHT:
1692 case EL_BD_FIREFLY_UP:
1693 case EL_BD_FIREFLY_LEFT:
1694 case EL_BD_FIREFLY_DOWN:
1695 Feld[x][y] = EL_BD_FIREFLY;
1696 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1699 case EL_PACMAN_RIGHT:
1701 case EL_PACMAN_LEFT:
1702 case EL_PACMAN_DOWN:
1703 Feld[x][y] = EL_PACMAN;
1704 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1707 case EL_SP_SNIKSNAK:
1708 MovDir[x][y] = MV_UP;
1711 case EL_SP_ELECTRON:
1712 MovDir[x][y] = MV_LEFT;
1719 Feld[x][y] = EL_MOLE;
1720 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1724 if (IS_CUSTOM_ELEMENT(element))
1726 struct ElementInfo *ei = &element_info[element];
1727 int move_direction_initial = ei->move_direction_initial;
1728 int move_pattern = ei->move_pattern;
1730 if (move_direction_initial == MV_PREVIOUS)
1732 if (MovDir[x][y] != MV_NO_MOVING)
1735 move_direction_initial = MV_AUTOMATIC;
1738 if (move_direction_initial & MV_ANY_DIRECTION)
1739 MovDir[x][y] = move_direction_initial;
1740 else if (move_direction_initial == MV_RANDOM ||
1741 move_pattern == MV_ALL_DIRECTIONS ||
1742 move_pattern == MV_TURNING_LEFT ||
1743 move_pattern == MV_TURNING_RIGHT ||
1744 move_pattern == MV_TURNING_LEFT_RIGHT ||
1745 move_pattern == MV_TURNING_RIGHT_LEFT ||
1746 move_pattern == MV_TURNING_RANDOM)
1747 MovDir[x][y] = 1 << RND(4);
1748 else if (move_pattern == MV_HORIZONTAL)
1749 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1750 else if (move_pattern == MV_VERTICAL)
1751 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1752 else if (move_pattern & MV_ANY_DIRECTION)
1753 MovDir[x][y] = element_info[element].move_pattern;
1754 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1755 move_pattern == MV_ALONG_RIGHT_SIDE)
1757 for (i = 0; i < 4; i++)
1759 int x1 = x + xy[i][0];
1760 int y1 = y + xy[i][1];
1762 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1764 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1765 MovDir[x][y] = direction[0][i];
1767 MovDir[x][y] = direction[1][i];
1776 MovDir[x][y] = 1 << RND(4);
1778 if (element != EL_BUG &&
1779 element != EL_SPACESHIP &&
1780 element != EL_BD_BUTTERFLY &&
1781 element != EL_BD_FIREFLY)
1784 for (i = 0; i < 4; i++)
1786 int x1 = x + xy[i][0];
1787 int y1 = y + xy[i][1];
1789 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1791 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1793 MovDir[x][y] = direction[0][i];
1796 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1797 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1799 MovDir[x][y] = direction[1][i];
1808 GfxDir[x][y] = MovDir[x][y];
1811 void InitAmoebaNr(int x, int y)
1814 int group_nr = AmoebeNachbarNr(x, y);
1818 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1820 if (AmoebaCnt[i] == 0)
1828 AmoebaNr[x][y] = group_nr;
1829 AmoebaCnt[group_nr]++;
1830 AmoebaCnt2[group_nr]++;
1836 boolean raise_level = FALSE;
1838 if (local_player->MovPos)
1842 if (tape.auto_play) /* tape might already be stopped here */
1843 tape.auto_play_level_solved = TRUE;
1845 if (tape.playing && tape.auto_play)
1846 tape.auto_play_level_solved = TRUE;
1849 local_player->LevelSolved = FALSE;
1851 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1855 if (!tape.playing && setup.sound_loops)
1856 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1857 SND_CTRL_PLAY_LOOP);
1859 while (TimeLeft > 0)
1861 if (!tape.playing && !setup.sound_loops)
1862 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1863 if (TimeLeft > 0 && !(TimeLeft % 10))
1864 RaiseScore(level.score[SC_TIME_BONUS]);
1865 if (TimeLeft > 100 && !(TimeLeft % 10))
1869 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1876 if (!tape.playing && setup.sound_loops)
1877 StopSound(SND_GAME_LEVELTIME_BONUS);
1879 else if (level.time == 0) /* level without time limit */
1881 if (!tape.playing && setup.sound_loops)
1882 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1883 SND_CTRL_PLAY_LOOP);
1885 while (TimePlayed < 999)
1887 if (!tape.playing && !setup.sound_loops)
1888 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1889 if (TimePlayed < 999 && !(TimePlayed % 10))
1890 RaiseScore(level.score[SC_TIME_BONUS]);
1891 if (TimePlayed < 900 && !(TimePlayed % 10))
1895 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1902 if (!tape.playing && setup.sound_loops)
1903 StopSound(SND_GAME_LEVELTIME_BONUS);
1906 /* close exit door after last player */
1907 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1908 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1910 int element = Feld[ExitX][ExitY];
1912 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1913 EL_SP_EXIT_CLOSING);
1915 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1918 /* Hero disappears */
1919 DrawLevelField(ExitX, ExitY);
1925 CloseDoor(DOOR_CLOSE_1);
1930 SaveTape(tape.level_nr); /* Ask to save tape */
1933 if (level_nr == leveldir_current->handicap_level)
1935 leveldir_current->handicap_level++;
1936 SaveLevelSetup_SeriesInfo();
1939 if (level_editor_test_game)
1940 local_player->score = -1; /* no highscore when playing from editor */
1941 else if (level_nr < leveldir_current->last_level)
1942 raise_level = TRUE; /* advance to next level */
1944 if ((hi_pos = NewHiScore()) >= 0)
1946 game_status = GAME_MODE_SCORES;
1947 DrawHallOfFame(hi_pos);
1956 game_status = GAME_MODE_MAIN;
1973 LoadScore(level_nr);
1975 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1976 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1979 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1981 if (local_player->score > highscore[k].Score)
1983 /* player has made it to the hall of fame */
1985 if (k < MAX_SCORE_ENTRIES - 1)
1987 int m = MAX_SCORE_ENTRIES - 1;
1990 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1991 if (!strcmp(setup.player_name, highscore[l].Name))
1993 if (m == k) /* player's new highscore overwrites his old one */
1997 for (l = m; l > k; l--)
1999 strcpy(highscore[l].Name, highscore[l - 1].Name);
2000 highscore[l].Score = highscore[l - 1].Score;
2007 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2008 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2009 highscore[k].Score = local_player->score;
2015 else if (!strncmp(setup.player_name, highscore[k].Name,
2016 MAX_PLAYER_NAME_LEN))
2017 break; /* player already there with a higher score */
2023 SaveScore(level_nr);
2028 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2030 if (player->GfxAction != action || player->GfxDir != dir)
2033 printf("Player frame reset! (%d => %d, %d => %d)\n",
2034 player->GfxAction, action, player->GfxDir, dir);
2037 player->GfxAction = action;
2038 player->GfxDir = dir;
2040 player->StepFrame = 0;
2044 static void ResetRandomAnimationValue(int x, int y)
2046 GfxRandom[x][y] = INIT_GFX_RANDOM();
2049 static void ResetGfxAnimation(int x, int y)
2052 GfxAction[x][y] = ACTION_DEFAULT;
2053 GfxDir[x][y] = MovDir[x][y];
2056 void InitMovingField(int x, int y, int direction)
2058 int element = Feld[x][y];
2059 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2060 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2064 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2065 ResetGfxAnimation(x, y);
2067 MovDir[newx][newy] = MovDir[x][y] = direction;
2068 GfxDir[x][y] = direction;
2070 if (Feld[newx][newy] == EL_EMPTY)
2071 Feld[newx][newy] = EL_BLOCKED;
2073 if (direction == MV_DOWN && CAN_FALL(element))
2074 GfxAction[x][y] = ACTION_FALLING;
2076 GfxAction[x][y] = ACTION_MOVING;
2078 GfxFrame[newx][newy] = GfxFrame[x][y];
2079 GfxRandom[newx][newy] = GfxRandom[x][y];
2080 GfxAction[newx][newy] = GfxAction[x][y];
2081 GfxDir[newx][newy] = GfxDir[x][y];
2084 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2086 int direction = MovDir[x][y];
2087 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2088 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2094 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2096 int oldx = x, oldy = y;
2097 int direction = MovDir[x][y];
2099 if (direction == MV_LEFT)
2101 else if (direction == MV_RIGHT)
2103 else if (direction == MV_UP)
2105 else if (direction == MV_DOWN)
2108 *comes_from_x = oldx;
2109 *comes_from_y = oldy;
2112 int MovingOrBlocked2Element(int x, int y)
2114 int element = Feld[x][y];
2116 if (element == EL_BLOCKED)
2120 Blocked2Moving(x, y, &oldx, &oldy);
2121 return Feld[oldx][oldy];
2127 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2129 /* like MovingOrBlocked2Element(), but if element is moving
2130 and (x,y) is the field the moving element is just leaving,
2131 return EL_BLOCKED instead of the element value */
2132 int element = Feld[x][y];
2134 if (IS_MOVING(x, y))
2136 if (element == EL_BLOCKED)
2140 Blocked2Moving(x, y, &oldx, &oldy);
2141 return Feld[oldx][oldy];
2150 static void RemoveField(int x, int y)
2152 Feld[x][y] = EL_EMPTY;
2159 ChangeDelay[x][y] = 0;
2160 ChangePage[x][y] = -1;
2161 Pushed[x][y] = FALSE;
2163 GfxElement[x][y] = EL_UNDEFINED;
2164 GfxAction[x][y] = ACTION_DEFAULT;
2165 GfxDir[x][y] = MV_NO_MOVING;
2168 void RemoveMovingField(int x, int y)
2170 int oldx = x, oldy = y, newx = x, newy = y;
2171 int element = Feld[x][y];
2172 int next_element = EL_UNDEFINED;
2174 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2177 if (IS_MOVING(x, y))
2179 Moving2Blocked(x, y, &newx, &newy);
2180 if (Feld[newx][newy] != EL_BLOCKED)
2183 else if (element == EL_BLOCKED)
2185 Blocked2Moving(x, y, &oldx, &oldy);
2186 if (!IS_MOVING(oldx, oldy))
2190 if (element == EL_BLOCKED &&
2191 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2192 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2193 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2194 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2195 next_element = get_next_element(Feld[oldx][oldy]);
2197 RemoveField(oldx, oldy);
2198 RemoveField(newx, newy);
2200 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2202 if (next_element != EL_UNDEFINED)
2203 Feld[oldx][oldy] = next_element;
2205 DrawLevelField(oldx, oldy);
2206 DrawLevelField(newx, newy);
2209 void DrawDynamite(int x, int y)
2211 int sx = SCREENX(x), sy = SCREENY(y);
2212 int graphic = el2img(Feld[x][y]);
2215 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2218 if (IS_WALKABLE_INSIDE(Back[x][y]))
2222 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2223 else if (Store[x][y])
2224 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2226 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2229 if (Back[x][y] || Store[x][y])
2230 DrawGraphicThruMask(sx, sy, graphic, frame);
2232 DrawGraphic(sx, sy, graphic, frame);
2234 if (game.emulation == EMU_SUPAPLEX)
2235 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2236 else if (Store[x][y])
2237 DrawGraphicThruMask(sx, sy, graphic, frame);
2239 DrawGraphic(sx, sy, graphic, frame);
2243 void CheckDynamite(int x, int y)
2245 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2249 if (MovDelay[x][y] != 0)
2252 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2259 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2261 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2262 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2263 StopSound(SND_DYNAMITE_ACTIVE);
2265 StopSound(SND_DYNABOMB_ACTIVE);
2271 void RelocatePlayer(int x, int y, int element)
2273 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2275 if (player->GameOver) /* do not reanimate dead player */
2279 RemoveField(x, y); /* temporarily remove newly placed player */
2280 DrawLevelField(x, y);
2283 if (player->present)
2285 while (player->MovPos)
2287 ScrollPlayer(player, SCROLL_GO_ON);
2288 ScrollScreen(NULL, SCROLL_GO_ON);
2294 Delay(GAME_FRAME_DELAY);
2297 DrawPlayer(player); /* needed here only to cleanup last field */
2298 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2300 player->is_moving = FALSE;
2303 Feld[x][y] = element;
2304 InitPlayerField(x, y, element, TRUE);
2306 if (player == local_player)
2308 int scroll_xx = -999, scroll_yy = -999;
2310 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2313 int fx = FX, fy = FY;
2315 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2316 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2317 local_player->jx - MIDPOSX);
2319 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2320 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2321 local_player->jy - MIDPOSY);
2323 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2324 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2329 fx += dx * TILEX / 2;
2330 fy += dy * TILEY / 2;
2332 ScrollLevel(dx, dy);
2335 /* scroll in two steps of half tile size to make things smoother */
2336 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2338 Delay(GAME_FRAME_DELAY);
2340 /* scroll second step to align at full tile size */
2342 Delay(GAME_FRAME_DELAY);
2347 void Explode(int ex, int ey, int phase, int mode)
2351 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2352 int last_phase = num_phase * delay;
2353 int half_phase = (num_phase / 2) * delay;
2354 int first_phase_after_start = EX_PHASE_START + 1;
2356 if (game.explosions_delayed)
2358 ExplodeField[ex][ey] = mode;
2362 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2364 int center_element = Feld[ex][ey];
2367 /* --- This is only really needed (and now handled) in "Impact()". --- */
2368 /* do not explode moving elements that left the explode field in time */
2369 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2370 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2374 if (mode == EX_NORMAL || mode == EX_CENTER)
2375 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2377 /* remove things displayed in background while burning dynamite */
2378 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2381 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2383 /* put moving element to center field (and let it explode there) */
2384 center_element = MovingOrBlocked2Element(ex, ey);
2385 RemoveMovingField(ex, ey);
2386 Feld[ex][ey] = center_element;
2389 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2391 int xx = x - ex + 1;
2392 int yy = y - ey + 1;
2395 if (!IN_LEV_FIELD(x, y) ||
2396 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2397 (x != ex || y != ey)))
2400 element = Feld[x][y];
2402 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2404 element = MovingOrBlocked2Element(x, y);
2406 if (!IS_EXPLOSION_PROOF(element))
2407 RemoveMovingField(x, y);
2413 if (IS_EXPLOSION_PROOF(element))
2416 /* indestructible elements can only explode in center (but not flames) */
2417 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2418 element == EL_FLAMES)
2423 if ((IS_INDESTRUCTIBLE(element) &&
2424 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2425 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2426 element == EL_FLAMES)
2430 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2432 if (IS_ACTIVE_BOMB(element))
2434 /* re-activate things under the bomb like gate or penguin */
2435 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2442 /* save walkable background elements while explosion on same tile */
2444 if (IS_INDESTRUCTIBLE(element))
2445 Back[x][y] = element;
2447 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2448 Back[x][y] = element;
2451 /* ignite explodable elements reached by other explosion */
2452 if (element == EL_EXPLOSION)
2453 element = Store2[x][y];
2456 if (AmoebaNr[x][y] &&
2457 (element == EL_AMOEBA_FULL ||
2458 element == EL_BD_AMOEBA ||
2459 element == EL_AMOEBA_GROWING))
2461 AmoebaCnt[AmoebaNr[x][y]]--;
2462 AmoebaCnt2[AmoebaNr[x][y]]--;
2468 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2470 switch(StorePlayer[ex][ey])
2473 Store[x][y] = EL_EMERALD_RED;
2476 Store[x][y] = EL_EMERALD;
2479 Store[x][y] = EL_EMERALD_PURPLE;
2483 Store[x][y] = EL_EMERALD_YELLOW;
2487 if (game.emulation == EMU_SUPAPLEX)
2488 Store[x][y] = EL_EMPTY;
2490 else if (center_element == EL_MOLE)
2491 Store[x][y] = EL_EMERALD_RED;
2492 else if (center_element == EL_PENGUIN)
2493 Store[x][y] = EL_EMERALD_PURPLE;
2494 else if (center_element == EL_BUG)
2495 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2496 else if (center_element == EL_BD_BUTTERFLY)
2497 Store[x][y] = EL_BD_DIAMOND;
2498 else if (center_element == EL_SP_ELECTRON)
2499 Store[x][y] = EL_SP_INFOTRON;
2500 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2501 Store[x][y] = level.amoeba_content;
2502 else if (center_element == EL_YAMYAM)
2503 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2504 else if (IS_CUSTOM_ELEMENT(center_element) &&
2505 element_info[center_element].content[xx][yy] != EL_EMPTY)
2506 Store[x][y] = element_info[center_element].content[xx][yy];
2507 else if (element == EL_WALL_EMERALD)
2508 Store[x][y] = EL_EMERALD;
2509 else if (element == EL_WALL_DIAMOND)
2510 Store[x][y] = EL_DIAMOND;
2511 else if (element == EL_WALL_BD_DIAMOND)
2512 Store[x][y] = EL_BD_DIAMOND;
2513 else if (element == EL_WALL_EMERALD_YELLOW)
2514 Store[x][y] = EL_EMERALD_YELLOW;
2515 else if (element == EL_WALL_EMERALD_RED)
2516 Store[x][y] = EL_EMERALD_RED;
2517 else if (element == EL_WALL_EMERALD_PURPLE)
2518 Store[x][y] = EL_EMERALD_PURPLE;
2519 else if (element == EL_WALL_PEARL)
2520 Store[x][y] = EL_PEARL;
2521 else if (element == EL_WALL_CRYSTAL)
2522 Store[x][y] = EL_CRYSTAL;
2523 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2524 Store[x][y] = element_info[element].content[1][1];
2526 Store[x][y] = EL_EMPTY;
2528 if (x != ex || y != ey ||
2529 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2530 Store2[x][y] = element;
2533 if (AmoebaNr[x][y] &&
2534 (element == EL_AMOEBA_FULL ||
2535 element == EL_BD_AMOEBA ||
2536 element == EL_AMOEBA_GROWING))
2538 AmoebaCnt[AmoebaNr[x][y]]--;
2539 AmoebaCnt2[AmoebaNr[x][y]]--;
2545 MovDir[x][y] = MovPos[x][y] = 0;
2546 GfxDir[x][y] = MovDir[x][y];
2551 Feld[x][y] = EL_EXPLOSION;
2553 GfxElement[x][y] = center_element;
2555 GfxElement[x][y] = EL_UNDEFINED;
2558 ExplodePhase[x][y] = 1;
2562 if (center_element == EL_YAMYAM)
2563 game.yamyam_content_nr =
2564 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2575 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2579 /* activate this even in non-DEBUG version until cause for crash in
2580 getGraphicAnimationFrame() (see below) is found and eliminated */
2584 if (GfxElement[x][y] == EL_UNDEFINED)
2587 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2588 printf("Explode(): This should never happen!\n");
2591 GfxElement[x][y] = EL_EMPTY;
2595 if (phase == first_phase_after_start)
2597 int element = Store2[x][y];
2599 if (element == EL_BLACK_ORB)
2601 Feld[x][y] = Store2[x][y];
2606 else if (phase == half_phase)
2608 int element = Store2[x][y];
2610 if (IS_PLAYER(x, y))
2611 KillHeroUnlessProtected(x, y);
2612 else if (CAN_EXPLODE_BY_FIRE(element))
2614 Feld[x][y] = Store2[x][y];
2618 else if (element == EL_AMOEBA_TO_DIAMOND)
2619 AmoebeUmwandeln(x, y);
2622 if (phase == last_phase)
2626 element = Feld[x][y] = Store[x][y];
2627 Store[x][y] = Store2[x][y] = 0;
2628 GfxElement[x][y] = EL_UNDEFINED;
2630 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2631 element = Feld[x][y] = Back[x][y];
2634 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2635 GfxDir[x][y] = MV_NO_MOVING;
2636 ChangeDelay[x][y] = 0;
2637 ChangePage[x][y] = -1;
2639 InitField(x, y, FALSE);
2640 if (CAN_MOVE(element))
2642 DrawLevelField(x, y);
2644 TestIfElementTouchesCustomElement(x, y);
2646 if (GFX_CRUMBLED(element))
2647 DrawLevelFieldCrumbledSandNeighbours(x, y);
2649 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2650 StorePlayer[x][y] = 0;
2652 if (ELEM_IS_PLAYER(element))
2653 RelocatePlayer(x, y, element);
2655 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2658 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2660 int stored = Store[x][y];
2661 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2662 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2665 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2668 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2669 element_info[GfxElement[x][y]].token_name,
2674 DrawLevelFieldCrumbledSand(x, y);
2676 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2678 DrawLevelElement(x, y, Back[x][y]);
2679 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2681 else if (IS_WALKABLE_UNDER(Back[x][y]))
2683 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2684 DrawLevelElementThruMask(x, y, Back[x][y]);
2686 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2687 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2691 void DynaExplode(int ex, int ey)
2694 int dynabomb_element = Feld[ex][ey];
2695 int dynabomb_size = 1;
2696 boolean dynabomb_xl = FALSE;
2697 struct PlayerInfo *player;
2698 static int xy[4][2] =
2706 if (IS_ACTIVE_BOMB(dynabomb_element))
2708 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2709 dynabomb_size = player->dynabomb_size;
2710 dynabomb_xl = player->dynabomb_xl;
2711 player->dynabombs_left++;
2714 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2716 for (i = 0; i < 4; i++)
2718 for (j = 1; j <= dynabomb_size; j++)
2720 int x = ex + j * xy[i % 4][0];
2721 int y = ey + j * xy[i % 4][1];
2724 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2727 element = Feld[x][y];
2729 /* do not restart explosions of fields with active bombs */
2730 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2733 Explode(x, y, EX_PHASE_START, EX_BORDER);
2735 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2736 if (element != EL_EMPTY &&
2737 element != EL_SAND &&
2738 element != EL_EXPLOSION &&
2745 void Bang(int x, int y)
2748 int element = MovingOrBlocked2Element(x, y);
2750 int element = Feld[x][y];
2754 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2756 if (IS_PLAYER(x, y))
2759 struct PlayerInfo *player = PLAYERINFO(x, y);
2761 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2762 player->element_nr);
2767 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2769 if (game.emulation == EMU_SUPAPLEX)
2770 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2772 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2777 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2785 case EL_BD_BUTTERFLY:
2788 case EL_DARK_YAMYAM:
2792 RaiseScoreElement(element);
2793 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2795 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2796 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2797 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2798 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2799 case EL_DYNABOMB_INCREASE_NUMBER:
2800 case EL_DYNABOMB_INCREASE_SIZE:
2801 case EL_DYNABOMB_INCREASE_POWER:
2806 case EL_LAMP_ACTIVE:
2807 if (IS_PLAYER(x, y))
2808 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2810 Explode(x, y, EX_PHASE_START, EX_CENTER);
2813 if (CAN_EXPLODE_DYNA(element))
2815 else if (CAN_EXPLODE_1X1(element))
2816 Explode(x, y, EX_PHASE_START, EX_CENTER);
2818 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2822 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2825 void SplashAcid(int x, int y)
2827 int element = Feld[x][y];
2829 if (element != EL_ACID_SPLASH_LEFT &&
2830 element != EL_ACID_SPLASH_RIGHT)
2832 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2834 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2835 (!IN_LEV_FIELD(x-1, y-1) ||
2836 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2837 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2839 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2840 (!IN_LEV_FIELD(x+1, y-1) ||
2841 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2842 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2846 static void InitBeltMovement()
2848 static int belt_base_element[4] =
2850 EL_CONVEYOR_BELT_1_LEFT,
2851 EL_CONVEYOR_BELT_2_LEFT,
2852 EL_CONVEYOR_BELT_3_LEFT,
2853 EL_CONVEYOR_BELT_4_LEFT
2855 static int belt_base_active_element[4] =
2857 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2858 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2859 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2860 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2865 /* set frame order for belt animation graphic according to belt direction */
2866 for (i = 0; i < 4; i++)
2870 for (j = 0; j < 3; j++)
2872 int element = belt_base_active_element[belt_nr] + j;
2873 int graphic = el2img(element);
2875 if (game.belt_dir[i] == MV_LEFT)
2876 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2878 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2882 for (y = 0; y < lev_fieldy; y++)
2884 for (x = 0; x < lev_fieldx; x++)
2886 int element = Feld[x][y];
2888 for (i = 0; i < 4; i++)
2890 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2892 int e_belt_nr = getBeltNrFromBeltElement(element);
2895 if (e_belt_nr == belt_nr)
2897 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2899 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2907 static void ToggleBeltSwitch(int x, int y)
2909 static int belt_base_element[4] =
2911 EL_CONVEYOR_BELT_1_LEFT,
2912 EL_CONVEYOR_BELT_2_LEFT,
2913 EL_CONVEYOR_BELT_3_LEFT,
2914 EL_CONVEYOR_BELT_4_LEFT
2916 static int belt_base_active_element[4] =
2918 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2919 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2920 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2921 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2923 static int belt_base_switch_element[4] =
2925 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2926 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2927 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2928 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2930 static int belt_move_dir[4] =
2938 int element = Feld[x][y];
2939 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2940 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2941 int belt_dir = belt_move_dir[belt_dir_nr];
2944 if (!IS_BELT_SWITCH(element))
2947 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2948 game.belt_dir[belt_nr] = belt_dir;
2950 if (belt_dir_nr == 3)
2953 /* set frame order for belt animation graphic according to belt direction */
2954 for (i = 0; i < 3; i++)
2956 int element = belt_base_active_element[belt_nr] + i;
2957 int graphic = el2img(element);
2959 if (belt_dir == MV_LEFT)
2960 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2962 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2965 for (yy = 0; yy < lev_fieldy; yy++)
2967 for (xx = 0; xx < lev_fieldx; xx++)
2969 int element = Feld[xx][yy];
2971 if (IS_BELT_SWITCH(element))
2973 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2975 if (e_belt_nr == belt_nr)
2977 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2978 DrawLevelField(xx, yy);
2981 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2983 int e_belt_nr = getBeltNrFromBeltElement(element);
2985 if (e_belt_nr == belt_nr)
2987 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2989 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2990 DrawLevelField(xx, yy);
2993 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2995 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2997 if (e_belt_nr == belt_nr)
2999 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3001 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3002 DrawLevelField(xx, yy);
3009 static void ToggleSwitchgateSwitch(int x, int y)
3013 game.switchgate_pos = !game.switchgate_pos;
3015 for (yy = 0; yy < lev_fieldy; yy++)
3017 for (xx = 0; xx < lev_fieldx; xx++)
3019 int element = Feld[xx][yy];
3021 if (element == EL_SWITCHGATE_SWITCH_UP ||
3022 element == EL_SWITCHGATE_SWITCH_DOWN)
3024 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3025 DrawLevelField(xx, yy);
3027 else if (element == EL_SWITCHGATE_OPEN ||
3028 element == EL_SWITCHGATE_OPENING)
3030 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3032 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3034 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3037 else if (element == EL_SWITCHGATE_CLOSED ||
3038 element == EL_SWITCHGATE_CLOSING)
3040 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3042 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3044 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3051 static int getInvisibleActiveFromInvisibleElement(int element)
3053 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3054 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3055 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3059 static int getInvisibleFromInvisibleActiveElement(int element)
3061 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3062 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3063 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3067 static void RedrawAllLightSwitchesAndInvisibleElements()
3071 for (y = 0; y < lev_fieldy; y++)
3073 for (x = 0; x < lev_fieldx; x++)
3075 int element = Feld[x][y];
3077 if (element == EL_LIGHT_SWITCH &&
3078 game.light_time_left > 0)
3080 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3081 DrawLevelField(x, y);
3083 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3084 game.light_time_left == 0)
3086 Feld[x][y] = EL_LIGHT_SWITCH;
3087 DrawLevelField(x, y);
3089 else if (element == EL_INVISIBLE_STEELWALL ||
3090 element == EL_INVISIBLE_WALL ||
3091 element == EL_INVISIBLE_SAND)
3093 if (game.light_time_left > 0)
3094 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3096 DrawLevelField(x, y);
3098 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3099 element == EL_INVISIBLE_WALL_ACTIVE ||
3100 element == EL_INVISIBLE_SAND_ACTIVE)
3102 if (game.light_time_left == 0)
3103 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3105 DrawLevelField(x, y);
3111 static void ToggleLightSwitch(int x, int y)
3113 int element = Feld[x][y];
3115 game.light_time_left =
3116 (element == EL_LIGHT_SWITCH ?
3117 level.time_light * FRAMES_PER_SECOND : 0);
3119 RedrawAllLightSwitchesAndInvisibleElements();
3122 static void ActivateTimegateSwitch(int x, int y)
3126 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3128 for (yy = 0; yy < lev_fieldy; yy++)
3130 for (xx = 0; xx < lev_fieldx; xx++)
3132 int element = Feld[xx][yy];
3134 if (element == EL_TIMEGATE_CLOSED ||
3135 element == EL_TIMEGATE_CLOSING)
3137 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3138 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3142 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3144 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3145 DrawLevelField(xx, yy);
3152 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3155 inline static int getElementMoveStepsize(int x, int y)
3157 int element = Feld[x][y];
3158 int direction = MovDir[x][y];
3159 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3160 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3161 int horiz_move = (dx != 0);
3162 int sign = (horiz_move ? dx : dy);
3163 int step = sign * element_info[element].move_stepsize;
3165 /* special values for move stepsize for spring and things on conveyor belt */
3168 if (CAN_FALL(element) &&
3169 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3170 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3171 else if (element == EL_SPRING)
3172 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3178 void Impact(int x, int y)
3180 boolean lastline = (y == lev_fieldy-1);
3181 boolean object_hit = FALSE;
3182 boolean impact = (lastline || object_hit);
3183 int element = Feld[x][y];
3184 int smashed = EL_UNDEFINED;
3186 if (!lastline) /* check if element below was hit */
3188 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3191 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3192 MovDir[x][y + 1] != MV_DOWN ||
3193 MovPos[x][y + 1] <= TILEY / 2));
3196 object_hit = !IS_FREE(x, y + 1);
3199 /* do not smash moving elements that left the smashed field in time */
3200 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3201 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3205 smashed = MovingOrBlocked2Element(x, y + 1);
3207 impact = (lastline || object_hit);
3210 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3216 /* only reset graphic animation if graphic really changes after impact */
3218 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3220 ResetGfxAnimation(x, y);
3221 DrawLevelField(x, y);
3224 if (impact && CAN_EXPLODE_IMPACT(element))
3229 else if (impact && element == EL_PEARL)
3231 Feld[x][y] = EL_PEARL_BREAKING;
3232 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3235 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3237 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3242 if (impact && element == EL_AMOEBA_DROP)
3244 if (object_hit && IS_PLAYER(x, y + 1))
3245 KillHeroUnlessProtected(x, y + 1);
3246 else if (object_hit && smashed == EL_PENGUIN)
3250 Feld[x][y] = EL_AMOEBA_GROWING;
3251 Store[x][y] = EL_AMOEBA_WET;
3253 ResetRandomAnimationValue(x, y);
3258 if (object_hit) /* check which object was hit */
3260 if (CAN_PASS_MAGIC_WALL(element) &&
3261 (smashed == EL_MAGIC_WALL ||
3262 smashed == EL_BD_MAGIC_WALL))
3265 int activated_magic_wall =
3266 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3267 EL_BD_MAGIC_WALL_ACTIVE);
3269 /* activate magic wall / mill */
3270 for (yy = 0; yy < lev_fieldy; yy++)
3271 for (xx = 0; xx < lev_fieldx; xx++)
3272 if (Feld[xx][yy] == smashed)
3273 Feld[xx][yy] = activated_magic_wall;
3275 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3276 game.magic_wall_active = TRUE;
3278 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3279 SND_MAGIC_WALL_ACTIVATING :
3280 SND_BD_MAGIC_WALL_ACTIVATING));
3283 if (IS_PLAYER(x, y + 1))
3285 if (CAN_SMASH_PLAYER(element))
3287 KillHeroUnlessProtected(x, y + 1);
3291 else if (smashed == EL_PENGUIN)
3293 if (CAN_SMASH_PLAYER(element))
3299 else if (element == EL_BD_DIAMOND)
3301 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3307 else if ((element == EL_SP_INFOTRON ||
3308 element == EL_SP_ZONK) &&
3309 (smashed == EL_SP_SNIKSNAK ||
3310 smashed == EL_SP_ELECTRON ||
3311 smashed == EL_SP_DISK_ORANGE))
3317 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3323 else if (CAN_SMASH_EVERYTHING(element))
3325 if (IS_CLASSIC_ENEMY(smashed) ||
3326 CAN_EXPLODE_SMASHED(smashed))
3331 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3333 if (smashed == EL_LAMP ||
3334 smashed == EL_LAMP_ACTIVE)
3339 else if (smashed == EL_NUT)
3341 Feld[x][y + 1] = EL_NUT_BREAKING;
3342 PlayLevelSound(x, y, SND_NUT_BREAKING);
3343 RaiseScoreElement(EL_NUT);
3346 else if (smashed == EL_PEARL)
3348 Feld[x][y + 1] = EL_PEARL_BREAKING;
3349 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3352 else if (smashed == EL_DIAMOND)
3354 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3355 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3358 else if (IS_BELT_SWITCH(smashed))
3360 ToggleBeltSwitch(x, y + 1);
3362 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3363 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3365 ToggleSwitchgateSwitch(x, y + 1);
3367 else if (smashed == EL_LIGHT_SWITCH ||
3368 smashed == EL_LIGHT_SWITCH_ACTIVE)
3370 ToggleLightSwitch(x, y + 1);
3374 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3376 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3377 CE_OTHER_IS_SWITCHING);
3378 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3384 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3389 /* play sound of magic wall / mill */
3391 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3392 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3394 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3395 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3396 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3397 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3402 /* play sound of object that hits the ground */
3403 if (lastline || object_hit)
3404 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3407 inline static void TurnRoundExt(int x, int y)
3419 { 0, 0 }, { 0, 0 }, { 0, 0 },
3424 int left, right, back;
3428 { MV_DOWN, MV_UP, MV_RIGHT },
3429 { MV_UP, MV_DOWN, MV_LEFT },
3431 { MV_LEFT, MV_RIGHT, MV_DOWN },
3435 { MV_RIGHT, MV_LEFT, MV_UP }
3438 int element = Feld[x][y];
3439 int move_pattern = element_info[element].move_pattern;
3441 int old_move_dir = MovDir[x][y];
3442 int left_dir = turn[old_move_dir].left;
3443 int right_dir = turn[old_move_dir].right;
3444 int back_dir = turn[old_move_dir].back;
3446 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3447 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3448 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3449 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3451 int left_x = x + left_dx, left_y = y + left_dy;
3452 int right_x = x + right_dx, right_y = y + right_dy;
3453 int move_x = x + move_dx, move_y = y + move_dy;
3457 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3459 TestIfBadThingTouchesOtherBadThing(x, y);
3461 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3462 MovDir[x][y] = right_dir;
3463 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3464 MovDir[x][y] = left_dir;
3466 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3468 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3471 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3472 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3474 TestIfBadThingTouchesOtherBadThing(x, y);
3476 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3477 MovDir[x][y] = left_dir;
3478 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3479 MovDir[x][y] = right_dir;
3481 if ((element == EL_SPACESHIP ||
3482 element == EL_SP_SNIKSNAK ||
3483 element == EL_SP_ELECTRON)
3484 && MovDir[x][y] != old_move_dir)
3486 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3489 else if (element == EL_YAMYAM)
3491 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3492 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3494 if (can_turn_left && can_turn_right)
3495 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3496 else if (can_turn_left)
3497 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3498 else if (can_turn_right)
3499 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3501 MovDir[x][y] = back_dir;
3503 MovDelay[x][y] = 16 + 16 * RND(3);
3505 else if (element == EL_DARK_YAMYAM)
3507 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3508 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3510 if (can_turn_left && can_turn_right)
3511 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3512 else if (can_turn_left)
3513 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3514 else if (can_turn_right)
3515 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3517 MovDir[x][y] = back_dir;
3519 MovDelay[x][y] = 16 + 16 * RND(3);
3521 else if (element == EL_PACMAN)
3523 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3524 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3526 if (can_turn_left && can_turn_right)
3527 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3528 else if (can_turn_left)
3529 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3530 else if (can_turn_right)
3531 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3533 MovDir[x][y] = back_dir;
3535 MovDelay[x][y] = 6 + RND(40);
3537 else if (element == EL_PIG)
3539 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3540 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3541 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3542 boolean should_turn_left, should_turn_right, should_move_on;
3544 int rnd = RND(rnd_value);
3546 should_turn_left = (can_turn_left &&
3548 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3549 y + back_dy + left_dy)));
3550 should_turn_right = (can_turn_right &&
3552 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3553 y + back_dy + right_dy)));
3554 should_move_on = (can_move_on &&
3557 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3558 y + move_dy + left_dy) ||
3559 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3560 y + move_dy + right_dy)));
3562 if (should_turn_left || should_turn_right || should_move_on)
3564 if (should_turn_left && should_turn_right && should_move_on)
3565 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3566 rnd < 2 * rnd_value / 3 ? right_dir :
3568 else if (should_turn_left && should_turn_right)
3569 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3570 else if (should_turn_left && should_move_on)
3571 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3572 else if (should_turn_right && should_move_on)
3573 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3574 else if (should_turn_left)
3575 MovDir[x][y] = left_dir;
3576 else if (should_turn_right)
3577 MovDir[x][y] = right_dir;
3578 else if (should_move_on)
3579 MovDir[x][y] = old_move_dir;
3581 else if (can_move_on && rnd > rnd_value / 8)
3582 MovDir[x][y] = old_move_dir;
3583 else if (can_turn_left && can_turn_right)
3584 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3585 else if (can_turn_left && rnd > rnd_value / 8)
3586 MovDir[x][y] = left_dir;
3587 else if (can_turn_right && rnd > rnd_value/8)
3588 MovDir[x][y] = right_dir;
3590 MovDir[x][y] = back_dir;
3592 xx = x + move_xy[MovDir[x][y]].x;
3593 yy = y + move_xy[MovDir[x][y]].y;
3595 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3596 MovDir[x][y] = old_move_dir;
3600 else if (element == EL_DRAGON)
3602 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3603 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3604 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3606 int rnd = RND(rnd_value);
3609 if (FrameCounter < 1 && x == 0 && y == 29)
3610 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3613 if (can_move_on && rnd > rnd_value / 8)
3614 MovDir[x][y] = old_move_dir;
3615 else if (can_turn_left && can_turn_right)
3616 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3617 else if (can_turn_left && rnd > rnd_value / 8)
3618 MovDir[x][y] = left_dir;
3619 else if (can_turn_right && rnd > rnd_value / 8)
3620 MovDir[x][y] = right_dir;
3622 MovDir[x][y] = back_dir;
3624 xx = x + move_xy[MovDir[x][y]].x;
3625 yy = y + move_xy[MovDir[x][y]].y;
3628 if (FrameCounter < 1 && x == 0 && y == 29)
3629 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3630 xx, yy, Feld[xx][yy],
3635 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3636 MovDir[x][y] = old_move_dir;
3638 if (!IS_FREE(xx, yy))
3639 MovDir[x][y] = old_move_dir;
3643 if (FrameCounter < 1 && x == 0 && y == 29)
3644 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3649 else if (element == EL_MOLE)
3651 boolean can_move_on =
3652 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3653 IS_AMOEBOID(Feld[move_x][move_y]) ||
3654 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3657 boolean can_turn_left =
3658 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3659 IS_AMOEBOID(Feld[left_x][left_y])));
3661 boolean can_turn_right =
3662 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3663 IS_AMOEBOID(Feld[right_x][right_y])));
3665 if (can_turn_left && can_turn_right)
3666 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3667 else if (can_turn_left)
3668 MovDir[x][y] = left_dir;
3670 MovDir[x][y] = right_dir;
3673 if (MovDir[x][y] != old_move_dir)
3676 else if (element == EL_BALLOON)
3678 MovDir[x][y] = game.balloon_dir;
3681 else if (element == EL_SPRING)
3683 if (MovDir[x][y] & MV_HORIZONTAL &&
3684 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3685 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3686 MovDir[x][y] = MV_NO_MOVING;
3690 else if (element == EL_ROBOT ||
3691 element == EL_SATELLITE ||
3692 element == EL_PENGUIN)
3694 int attr_x = -1, attr_y = -1;
3705 for (i = 0; i < MAX_PLAYERS; i++)
3707 struct PlayerInfo *player = &stored_player[i];
3708 int jx = player->jx, jy = player->jy;
3710 if (!player->active)
3714 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3722 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3728 if (element == EL_PENGUIN)
3731 static int xy[4][2] =
3739 for (i = 0; i < 4; i++)
3741 int ex = x + xy[i % 4][0];
3742 int ey = y + xy[i % 4][1];
3744 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3753 MovDir[x][y] = MV_NO_MOVING;
3755 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3756 else if (attr_x > x)
3757 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3759 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3760 else if (attr_y > y)
3761 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3763 if (element == EL_ROBOT)
3767 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3768 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3769 Moving2Blocked(x, y, &newx, &newy);
3771 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3772 MovDelay[x][y] = 8 + 8 * !RND(3);
3774 MovDelay[x][y] = 16;
3776 else if (element == EL_PENGUIN)
3782 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3784 boolean first_horiz = RND(2);
3785 int new_move_dir = MovDir[x][y];
3788 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3789 Moving2Blocked(x, y, &newx, &newy);
3791 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3795 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3796 Moving2Blocked(x, y, &newx, &newy);
3798 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3801 MovDir[x][y] = old_move_dir;
3805 else /* (element == EL_SATELLITE) */
3811 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3813 boolean first_horiz = RND(2);
3814 int new_move_dir = MovDir[x][y];
3817 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3818 Moving2Blocked(x, y, &newx, &newy);
3820 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3824 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3825 Moving2Blocked(x, y, &newx, &newy);
3827 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3830 MovDir[x][y] = old_move_dir;
3835 else if (move_pattern == MV_TURNING_LEFT ||
3836 move_pattern == MV_TURNING_RIGHT ||
3837 move_pattern == MV_TURNING_LEFT_RIGHT ||
3838 move_pattern == MV_TURNING_RIGHT_LEFT ||
3839 move_pattern == MV_TURNING_RANDOM ||
3840 move_pattern == MV_ALL_DIRECTIONS)
3842 boolean can_turn_left =
3843 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3844 boolean can_turn_right =
3845 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3847 if (move_pattern == MV_TURNING_LEFT)
3848 MovDir[x][y] = left_dir;
3849 else if (move_pattern == MV_TURNING_RIGHT)
3850 MovDir[x][y] = right_dir;
3851 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3852 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3853 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3854 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3855 else if (move_pattern == MV_TURNING_RANDOM)
3856 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3857 can_turn_right && !can_turn_left ? right_dir :
3858 RND(2) ? left_dir : right_dir);
3859 else if (can_turn_left && can_turn_right)
3860 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3861 else if (can_turn_left)
3862 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3863 else if (can_turn_right)
3864 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3866 MovDir[x][y] = back_dir;
3868 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3870 else if (move_pattern == MV_HORIZONTAL ||
3871 move_pattern == MV_VERTICAL)
3873 if (move_pattern & old_move_dir)
3874 MovDir[x][y] = back_dir;
3875 else if (move_pattern == MV_HORIZONTAL)
3876 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3877 else if (move_pattern == MV_VERTICAL)
3878 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3880 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3882 else if (move_pattern & MV_ANY_DIRECTION)
3884 MovDir[x][y] = move_pattern;
3885 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3887 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3889 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3890 MovDir[x][y] = left_dir;
3891 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3892 MovDir[x][y] = right_dir;
3894 if (MovDir[x][y] != old_move_dir)
3895 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3897 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3899 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3900 MovDir[x][y] = right_dir;
3901 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3902 MovDir[x][y] = left_dir;
3904 if (MovDir[x][y] != old_move_dir)
3905 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3907 else if (move_pattern == MV_TOWARDS_PLAYER ||
3908 move_pattern == MV_AWAY_FROM_PLAYER)
3910 int attr_x = -1, attr_y = -1;
3912 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3923 for (i = 0; i < MAX_PLAYERS; i++)
3925 struct PlayerInfo *player = &stored_player[i];
3926 int jx = player->jx, jy = player->jy;
3928 if (!player->active)
3932 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3940 MovDir[x][y] = MV_NO_MOVING;
3942 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3943 else if (attr_x > x)
3944 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3946 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3947 else if (attr_y > y)
3948 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3950 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3952 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3954 boolean first_horiz = RND(2);
3955 int new_move_dir = MovDir[x][y];
3958 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3959 Moving2Blocked(x, y, &newx, &newy);
3961 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3965 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3966 Moving2Blocked(x, y, &newx, &newy);
3968 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3971 MovDir[x][y] = old_move_dir;
3974 else if (move_pattern == MV_WHEN_PUSHED ||
3975 move_pattern == MV_WHEN_DROPPED)
3977 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3978 MovDir[x][y] = MV_NO_MOVING;
3982 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
3984 static int test_xy[7][2] =
3994 static int test_dir[7] =
4004 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4005 int move_preference = -1000000; /* start with very low preference */
4006 int new_move_dir = MV_NO_MOVING;
4007 int start_test = RND(4);
4010 for (i = 0; i < 4; i++)
4012 int move_dir = test_dir[start_test + i];
4013 int move_dir_preference;
4015 xx = x + test_xy[start_test + i][0];
4016 yy = y + test_xy[start_test + i][1];
4018 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4019 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4021 new_move_dir = move_dir;
4026 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4029 move_dir_preference = -1 * RunnerVisit[xx][yy];
4030 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4031 move_dir_preference = PlayerVisit[xx][yy];
4033 if (move_dir_preference > move_preference)
4035 /* prefer field that has not been visited for the longest time */
4036 move_preference = move_dir_preference;
4037 new_move_dir = move_dir;
4039 else if (move_dir_preference == move_preference &&
4040 move_dir == old_move_dir)
4042 /* prefer last direction when all directions are preferred equally */
4043 move_preference = move_dir_preference;
4044 new_move_dir = move_dir;
4048 MovDir[x][y] = new_move_dir;
4049 if (old_move_dir != new_move_dir)
4054 static void TurnRound(int x, int y)
4056 int direction = MovDir[x][y];
4059 GfxDir[x][y] = MovDir[x][y];
4065 GfxDir[x][y] = MovDir[x][y];
4068 if (direction != MovDir[x][y])
4073 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4076 GfxAction[x][y] = ACTION_WAITING;
4080 static boolean JustBeingPushed(int x, int y)
4084 for (i = 0; i < MAX_PLAYERS; i++)
4086 struct PlayerInfo *player = &stored_player[i];
4088 if (player->active && player->is_pushing && player->MovPos)
4090 int next_jx = player->jx + (player->jx - player->last_jx);
4091 int next_jy = player->jy + (player->jy - player->last_jy);
4093 if (x == next_jx && y == next_jy)
4101 void StartMoving(int x, int y)
4103 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4104 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4105 int element = Feld[x][y];
4111 if (MovDelay[x][y] == 0)
4112 GfxAction[x][y] = ACTION_DEFAULT;
4114 /* !!! this should be handled more generic (not only for mole) !!! */
4115 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4116 GfxAction[x][y] = ACTION_DEFAULT;
4119 if (CAN_FALL(element) && y < lev_fieldy - 1)
4121 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4122 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4123 if (JustBeingPushed(x, y))
4126 if (element == EL_QUICKSAND_FULL)
4128 if (IS_FREE(x, y + 1))
4130 InitMovingField(x, y, MV_DOWN);
4131 started_moving = TRUE;
4133 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4134 Store[x][y] = EL_ROCK;
4136 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4138 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4141 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4143 if (!MovDelay[x][y])
4144 MovDelay[x][y] = TILEY + 1;
4153 Feld[x][y] = EL_QUICKSAND_EMPTY;
4154 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4155 Store[x][y + 1] = Store[x][y];
4158 PlayLevelSoundAction(x, y, ACTION_FILLING);
4160 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4164 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4165 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4167 InitMovingField(x, y, MV_DOWN);
4168 started_moving = TRUE;
4170 Feld[x][y] = EL_QUICKSAND_FILLING;
4171 Store[x][y] = element;
4173 PlayLevelSoundAction(x, y, ACTION_FILLING);
4175 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4178 else if (element == EL_MAGIC_WALL_FULL)
4180 if (IS_FREE(x, y + 1))
4182 InitMovingField(x, y, MV_DOWN);
4183 started_moving = TRUE;
4185 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4186 Store[x][y] = EL_CHANGED(Store[x][y]);
4188 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4190 if (!MovDelay[x][y])
4191 MovDelay[x][y] = TILEY/4 + 1;
4200 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4201 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4202 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4206 else if (element == EL_BD_MAGIC_WALL_FULL)
4208 if (IS_FREE(x, y + 1))
4210 InitMovingField(x, y, MV_DOWN);
4211 started_moving = TRUE;
4213 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4214 Store[x][y] = EL_CHANGED2(Store[x][y]);
4216 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4218 if (!MovDelay[x][y])
4219 MovDelay[x][y] = TILEY/4 + 1;
4228 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4229 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4230 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4234 else if (CAN_PASS_MAGIC_WALL(element) &&
4235 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4236 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4238 InitMovingField(x, y, MV_DOWN);
4239 started_moving = TRUE;
4242 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4243 EL_BD_MAGIC_WALL_FILLING);
4244 Store[x][y] = element;
4247 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4249 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4254 InitMovingField(x, y, MV_DOWN);
4255 started_moving = TRUE;
4257 Store[x][y] = EL_ACID;
4259 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4260 GfxAction[x][y + 1] = ACTION_ACTIVE;
4264 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4265 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4266 (Feld[x][y + 1] == EL_BLOCKED)) ||
4267 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4268 CAN_SMASH(element) && WasJustFalling[x][y] &&
4269 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4273 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4274 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4275 WasJustMoving[x][y] && !Pushed[x][y + 1])
4277 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4278 WasJustMoving[x][y])
4283 /* this is needed for a special case not covered by calling "Impact()"
4284 from "ContinueMoving()": if an element moves to a tile directly below
4285 another element which was just falling on that tile (which was empty
4286 in the previous frame), the falling element above would just stop
4287 instead of smashing the element below (in previous version, the above
4288 element was just checked for "moving" instead of "falling", resulting
4289 in incorrect smashes caused by horizontal movement of the above
4290 element; also, the case of the player being the element to smash was
4291 simply not covered here... :-/ ) */
4294 WasJustMoving[x][y] = 0;
4295 WasJustFalling[x][y] = 0;
4300 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4302 if (MovDir[x][y] == MV_NO_MOVING)
4304 InitMovingField(x, y, MV_DOWN);
4305 started_moving = TRUE;
4308 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4310 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4311 MovDir[x][y] = MV_DOWN;
4313 InitMovingField(x, y, MV_DOWN);
4314 started_moving = TRUE;
4316 else if (element == EL_AMOEBA_DROP)
4318 Feld[x][y] = EL_AMOEBA_GROWING;
4319 Store[x][y] = EL_AMOEBA_WET;
4321 /* Store[x][y + 1] must be zero, because:
4322 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4325 #if OLD_GAME_BEHAVIOUR
4326 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4328 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4329 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4330 element != EL_DX_SUPABOMB)
4333 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4334 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4335 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4336 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4339 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4340 (IS_FREE(x - 1, y + 1) ||
4341 Feld[x - 1][y + 1] == EL_ACID));
4342 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4343 (IS_FREE(x + 1, y + 1) ||
4344 Feld[x + 1][y + 1] == EL_ACID));
4345 boolean can_fall_any = (can_fall_left || can_fall_right);
4346 boolean can_fall_both = (can_fall_left && can_fall_right);
4348 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4350 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4352 if (slippery_type == SLIPPERY_ONLY_LEFT)
4353 can_fall_right = FALSE;
4354 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4355 can_fall_left = FALSE;
4356 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4357 can_fall_right = FALSE;
4358 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4359 can_fall_left = FALSE;
4361 can_fall_any = (can_fall_left || can_fall_right);
4362 can_fall_both = (can_fall_left && can_fall_right);
4367 if (can_fall_both &&
4368 (game.emulation != EMU_BOULDERDASH &&
4369 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4370 can_fall_left = !(can_fall_right = RND(2));
4372 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4373 started_moving = TRUE;
4376 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4378 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4379 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4380 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4381 int belt_dir = game.belt_dir[belt_nr];
4383 if ((belt_dir == MV_LEFT && left_is_free) ||
4384 (belt_dir == MV_RIGHT && right_is_free))
4386 InitMovingField(x, y, belt_dir);
4387 started_moving = TRUE;
4389 GfxAction[x][y] = ACTION_DEFAULT;
4394 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4395 if (CAN_MOVE(element) && !started_moving)
4397 int move_pattern = element_info[element].move_pattern;
4400 Moving2Blocked(x, y, &newx, &newy);
4403 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4406 if ((element == EL_SATELLITE ||
4407 element == EL_BALLOON ||
4408 element == EL_SPRING)
4409 && JustBeingPushed(x, y))
4414 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4415 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4416 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4419 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4420 element, element_info[element].token_name,
4421 WasJustMoving[x][y],
4422 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4423 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4424 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4425 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4429 WasJustMoving[x][y] = 0;
4432 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4435 if (Feld[x][y] != element) /* element has changed */
4437 element = Feld[x][y];
4438 move_pattern = element_info[element].move_pattern;
4440 if (!CAN_MOVE(element))
4444 if (Feld[x][y] != element) /* element has changed */
4452 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4453 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4455 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4457 Moving2Blocked(x, y, &newx, &newy);
4458 if (Feld[newx][newy] == EL_BLOCKED)
4459 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4465 if (FrameCounter < 1 && x == 0 && y == 29)
4466 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4469 if (!MovDelay[x][y]) /* start new movement phase */
4471 /* all objects that can change their move direction after each step
4472 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4474 if (element != EL_YAMYAM &&
4475 element != EL_DARK_YAMYAM &&
4476 element != EL_PACMAN &&
4477 !(move_pattern & MV_ANY_DIRECTION) &&
4478 move_pattern != MV_TURNING_LEFT &&
4479 move_pattern != MV_TURNING_RIGHT &&
4480 move_pattern != MV_TURNING_LEFT_RIGHT &&
4481 move_pattern != MV_TURNING_RIGHT_LEFT &&
4482 move_pattern != MV_TURNING_RANDOM)
4487 if (FrameCounter < 1 && x == 0 && y == 29)
4488 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4491 if (MovDelay[x][y] && (element == EL_BUG ||
4492 element == EL_SPACESHIP ||
4493 element == EL_SP_SNIKSNAK ||
4494 element == EL_SP_ELECTRON ||
4495 element == EL_MOLE))
4496 DrawLevelField(x, y);
4500 if (MovDelay[x][y]) /* wait some time before next movement */
4505 if (element == EL_YAMYAM)
4508 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4509 DrawLevelElementAnimation(x, y, element);
4513 if (MovDelay[x][y]) /* element still has to wait some time */
4516 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4517 ResetGfxAnimation(x, y);
4521 if (GfxAction[x][y] != ACTION_WAITING)
4522 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4524 GfxAction[x][y] = ACTION_WAITING;
4528 if (element == EL_ROBOT ||
4530 element == EL_PACMAN ||
4532 element == EL_YAMYAM ||
4533 element == EL_DARK_YAMYAM)
4536 DrawLevelElementAnimation(x, y, element);
4538 DrawLevelElementAnimationIfNeeded(x, y, element);
4540 PlayLevelSoundAction(x, y, ACTION_WAITING);
4542 else if (element == EL_SP_ELECTRON)
4543 DrawLevelElementAnimationIfNeeded(x, y, element);
4544 else if (element == EL_DRAGON)
4547 int dir = MovDir[x][y];
4548 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4549 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4550 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4551 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4552 dir == MV_UP ? IMG_FLAMES_1_UP :
4553 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4554 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4557 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4560 GfxAction[x][y] = ACTION_ATTACKING;
4562 if (IS_PLAYER(x, y))
4563 DrawPlayerField(x, y);
4565 DrawLevelField(x, y);
4567 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4569 for (i = 1; i <= 3; i++)
4571 int xx = x + i * dx;
4572 int yy = y + i * dy;
4573 int sx = SCREENX(xx);
4574 int sy = SCREENY(yy);
4575 int flame_graphic = graphic + (i - 1);
4577 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4582 int flamed = MovingOrBlocked2Element(xx, yy);
4584 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4587 RemoveMovingField(xx, yy);
4589 Feld[xx][yy] = EL_FLAMES;
4590 if (IN_SCR_FIELD(sx, sy))
4592 DrawLevelFieldCrumbledSand(xx, yy);
4593 DrawGraphic(sx, sy, flame_graphic, frame);
4598 if (Feld[xx][yy] == EL_FLAMES)
4599 Feld[xx][yy] = EL_EMPTY;
4600 DrawLevelField(xx, yy);
4605 if (MovDelay[x][y]) /* element still has to wait some time */
4607 PlayLevelSoundAction(x, y, ACTION_WAITING);
4613 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4614 for all other elements GfxAction will be set by InitMovingField() */
4615 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4616 GfxAction[x][y] = ACTION_MOVING;
4620 /* now make next step */
4622 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4624 if (DONT_COLLIDE_WITH(element) &&
4625 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4626 !PLAYER_PROTECTED(newx, newy))
4629 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4633 /* player killed by element which is deadly when colliding with */
4635 KillHero(PLAYERINFO(newx, newy));
4640 else if ((element == EL_PENGUIN ||
4641 element == EL_ROBOT ||
4642 element == EL_SATELLITE ||
4643 element == EL_BALLOON ||
4644 IS_CUSTOM_ELEMENT(element)) &&
4645 IN_LEV_FIELD(newx, newy) &&
4646 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4649 Store[x][y] = EL_ACID;
4651 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4653 if (Feld[newx][newy] == EL_EXIT_OPEN)
4657 DrawLevelField(x, y);
4659 Feld[x][y] = EL_EMPTY;
4660 DrawLevelField(x, y);
4663 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4664 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4665 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4667 local_player->friends_still_needed--;
4668 if (!local_player->friends_still_needed &&
4669 !local_player->GameOver && AllPlayersGone)
4670 local_player->LevelSolved = local_player->GameOver = TRUE;
4674 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4676 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4677 DrawLevelField(newx, newy);
4679 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4681 else if (!IS_FREE(newx, newy))
4683 GfxAction[x][y] = ACTION_WAITING;
4685 if (IS_PLAYER(x, y))
4686 DrawPlayerField(x, y);
4688 DrawLevelField(x, y);
4693 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4695 if (IS_FOOD_PIG(Feld[newx][newy]))
4697 if (IS_MOVING(newx, newy))
4698 RemoveMovingField(newx, newy);
4701 Feld[newx][newy] = EL_EMPTY;
4702 DrawLevelField(newx, newy);
4705 PlayLevelSound(x, y, SND_PIG_DIGGING);
4707 else if (!IS_FREE(newx, newy))
4709 if (IS_PLAYER(x, y))
4710 DrawPlayerField(x, y);
4712 DrawLevelField(x, y);
4721 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4724 else if (IS_CUSTOM_ELEMENT(element) &&
4725 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4729 !IS_FREE(newx, newy)
4735 printf("::: '%s' digs '%s' [%d]\n",
4736 element_info[element].token_name,
4737 element_info[Feld[newx][newy]].token_name,
4738 StorePlayer[newx][newy]);
4741 if (!IS_FREE(newx, newy))
4743 int new_element = Feld[newx][newy];
4746 /* no element can dig solid indestructible elements */
4747 if (IS_INDESTRUCTIBLE(new_element) &&
4748 !IS_DIGGABLE(new_element) &&
4749 !IS_COLLECTIBLE(new_element))
4752 if (AmoebaNr[newx][newy] &&
4753 (new_element == EL_AMOEBA_FULL ||
4754 new_element == EL_BD_AMOEBA ||
4755 new_element == EL_AMOEBA_GROWING))
4757 AmoebaCnt[AmoebaNr[newx][newy]]--;
4758 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4761 if (IS_MOVING(newx, newy))
4762 RemoveMovingField(newx, newy);
4765 RemoveField(newx, newy);
4766 DrawLevelField(newx, newy);
4769 sound = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4770 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4773 PlayLevelSoundAction(x, y, sound);
4776 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4778 RunnerVisit[x][y] = FrameCounter;
4779 PlayerVisit[x][y] /= 8; /* expire player visit path */
4785 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4787 if (!IS_FREE(newx, newy))
4789 if (IS_PLAYER(x, y))
4790 DrawPlayerField(x, y);
4792 DrawLevelField(x, y);
4798 boolean wanna_flame = !RND(10);
4799 int dx = newx - x, dy = newy - y;
4800 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4801 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4802 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4803 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4804 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4805 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4808 IS_CLASSIC_ENEMY(element1) ||
4809 IS_CLASSIC_ENEMY(element2)) &&
4810 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4811 element1 != EL_FLAMES && element2 != EL_FLAMES)
4814 ResetGfxAnimation(x, y);
4815 GfxAction[x][y] = ACTION_ATTACKING;
4818 if (IS_PLAYER(x, y))
4819 DrawPlayerField(x, y);
4821 DrawLevelField(x, y);
4823 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4825 MovDelay[x][y] = 50;
4827 Feld[newx][newy] = EL_FLAMES;
4828 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4829 Feld[newx1][newy1] = EL_FLAMES;
4830 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4831 Feld[newx2][newy2] = EL_FLAMES;
4837 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4838 Feld[newx][newy] == EL_DIAMOND)
4840 if (IS_MOVING(newx, newy))
4841 RemoveMovingField(newx, newy);
4844 Feld[newx][newy] = EL_EMPTY;
4845 DrawLevelField(newx, newy);
4848 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4850 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4851 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4853 if (AmoebaNr[newx][newy])
4855 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4856 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4857 Feld[newx][newy] == EL_BD_AMOEBA)
4858 AmoebaCnt[AmoebaNr[newx][newy]]--;
4861 if (IS_MOVING(newx, newy))
4862 RemoveMovingField(newx, newy);
4865 Feld[newx][newy] = EL_EMPTY;
4866 DrawLevelField(newx, newy);
4869 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4871 else if ((element == EL_PACMAN || element == EL_MOLE)
4872 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4874 if (AmoebaNr[newx][newy])
4876 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4877 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4878 Feld[newx][newy] == EL_BD_AMOEBA)
4879 AmoebaCnt[AmoebaNr[newx][newy]]--;
4882 if (element == EL_MOLE)
4884 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4885 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4887 ResetGfxAnimation(x, y);
4888 GfxAction[x][y] = ACTION_DIGGING;
4889 DrawLevelField(x, y);
4891 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4893 return; /* wait for shrinking amoeba */
4895 else /* element == EL_PACMAN */
4897 Feld[newx][newy] = EL_EMPTY;
4898 DrawLevelField(newx, newy);
4899 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4902 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4903 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4904 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4906 /* wait for shrinking amoeba to completely disappear */
4909 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4911 /* object was running against a wall */
4916 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4917 DrawLevelElementAnimation(x, y, element);
4919 if (element == EL_BUG ||
4920 element == EL_SPACESHIP ||
4921 element == EL_SP_SNIKSNAK)
4922 DrawLevelField(x, y);
4923 else if (element == EL_MOLE)
4924 DrawLevelField(x, y);
4925 else if (element == EL_BD_BUTTERFLY ||
4926 element == EL_BD_FIREFLY)
4927 DrawLevelElementAnimationIfNeeded(x, y, element);
4928 else if (element == EL_SATELLITE)
4929 DrawLevelElementAnimationIfNeeded(x, y, element);
4930 else if (element == EL_SP_ELECTRON)
4931 DrawLevelElementAnimationIfNeeded(x, y, element);
4934 if (DONT_TOUCH(element))
4935 TestIfBadThingTouchesHero(x, y);
4938 PlayLevelSoundAction(x, y, ACTION_WAITING);
4944 InitMovingField(x, y, MovDir[x][y]);
4946 PlayLevelSoundAction(x, y, ACTION_MOVING);
4950 ContinueMoving(x, y);
4953 void ContinueMoving(int x, int y)
4955 int element = Feld[x][y];
4956 int direction = MovDir[x][y];
4957 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4958 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4959 int newx = x + dx, newy = y + dy;
4961 int nextx = newx + dx, nexty = newy + dy;
4963 boolean pushed = Pushed[x][y];
4965 MovPos[x][y] += getElementMoveStepsize(x, y);
4967 if (pushed) /* special case: moving object pushed by player */
4968 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4970 if (ABS(MovPos[x][y]) < TILEX)
4972 DrawLevelField(x, y);
4974 return; /* element is still moving */
4977 /* element reached destination field */
4979 Feld[x][y] = EL_EMPTY;
4980 Feld[newx][newy] = element;
4981 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4983 if (element == EL_MOLE)
4985 Feld[x][y] = EL_SAND;
4987 DrawLevelFieldCrumbledSandNeighbours(x, y);
4989 else if (element == EL_QUICKSAND_FILLING)
4991 element = Feld[newx][newy] = get_next_element(element);
4992 Store[newx][newy] = Store[x][y];
4994 else if (element == EL_QUICKSAND_EMPTYING)
4996 Feld[x][y] = get_next_element(element);
4997 element = Feld[newx][newy] = Store[x][y];
4999 else if (element == EL_MAGIC_WALL_FILLING)
5001 element = Feld[newx][newy] = get_next_element(element);
5002 if (!game.magic_wall_active)
5003 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5004 Store[newx][newy] = Store[x][y];
5006 else if (element == EL_MAGIC_WALL_EMPTYING)
5008 Feld[x][y] = get_next_element(element);
5009 if (!game.magic_wall_active)
5010 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5011 element = Feld[newx][newy] = Store[x][y];
5013 else if (element == EL_BD_MAGIC_WALL_FILLING)
5015 element = Feld[newx][newy] = get_next_element(element);
5016 if (!game.magic_wall_active)
5017 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5018 Store[newx][newy] = Store[x][y];
5020 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5022 Feld[x][y] = get_next_element(element);
5023 if (!game.magic_wall_active)
5024 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5025 element = Feld[newx][newy] = Store[x][y];
5027 else if (element == EL_AMOEBA_DROPPING)
5029 Feld[x][y] = get_next_element(element);
5030 element = Feld[newx][newy] = Store[x][y];
5032 else if (element == EL_SOKOBAN_OBJECT)
5035 Feld[x][y] = Back[x][y];
5037 if (Back[newx][newy])
5038 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5040 Back[x][y] = Back[newx][newy] = 0;
5042 else if (Store[x][y] == EL_ACID)
5044 element = Feld[newx][newy] = EL_ACID;
5048 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5049 MovDelay[newx][newy] = 0;
5051 /* copy element change control values to new field */
5052 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5053 ChangePage[newx][newy] = ChangePage[x][y];
5054 Changed[newx][newy] = Changed[x][y];
5055 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5057 ChangeDelay[x][y] = 0;
5058 ChangePage[x][y] = -1;
5059 Changed[x][y] = CE_BITMASK_DEFAULT;
5060 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5062 /* copy animation control values to new field */
5063 GfxFrame[newx][newy] = GfxFrame[x][y];
5064 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5065 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5066 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5068 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5070 ResetGfxAnimation(x, y); /* reset animation values for old field */
5073 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y))
5075 int new_element = element_info[element].move_leave_element;
5077 Feld[x][y] = new_element;
5079 if (new_element != EL_EMPTY)
5081 InitField(x, y, FALSE);
5083 TestIfElementTouchesCustomElement(x, y);
5085 if (GFX_CRUMBLED(new_element))
5086 DrawLevelFieldCrumbledSandNeighbours(x, y);
5092 /* 2.1.1 (does not work correctly for spring) */
5093 if (!CAN_MOVE(element))
5094 MovDir[newx][newy] = 0;
5098 /* (does not work for falling objects that slide horizontally) */
5099 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5100 MovDir[newx][newy] = 0;
5103 if (!CAN_MOVE(element) ||
5104 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5105 MovDir[newx][newy] = 0;
5108 if (!CAN_MOVE(element) ||
5109 (CAN_FALL(element) && direction == MV_DOWN))
5110 GfxDir[x][y] = MovDir[newx][newy] = 0;
5115 DrawLevelField(x, y);
5116 DrawLevelField(newx, newy);
5118 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5120 /* prevent pushed element from moving on in pushed direction */
5121 if (pushed && CAN_MOVE(element) &&
5122 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5123 !(element_info[element].move_pattern & direction))
5124 TurnRound(newx, newy);
5126 if (!pushed) /* special case: moving object pushed by player */
5128 WasJustMoving[newx][newy] = 3;
5130 if (CAN_FALL(element) && direction == MV_DOWN)
5131 WasJustFalling[newx][newy] = 3;
5134 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5136 TestIfBadThingTouchesHero(newx, newy);
5137 TestIfBadThingTouchesFriend(newx, newy);
5139 if (!IS_CUSTOM_ELEMENT(element))
5140 TestIfBadThingTouchesOtherBadThing(newx, newy);
5142 else if (element == EL_PENGUIN)
5143 TestIfFriendTouchesBadThing(newx, newy);
5145 if (CAN_FALL(element) && direction == MV_DOWN &&
5146 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5150 TestIfElementTouchesCustomElement(x, y); /* for empty space */
5154 if (ChangePage[newx][newy] != -1) /* delayed change */
5155 ChangeElement(newx, newy, ChangePage[newx][newy]);
5160 TestIfElementHitsCustomElement(newx, newy, direction);
5164 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5166 int hitting_element = Feld[newx][newy];
5168 /* !!! fix side (direction) orientation here and elsewhere !!! */
5169 CheckElementSideChange(newx, newy, hitting_element,
5170 direction, CE_HITTING_SOMETHING, -1);
5173 if (IN_LEV_FIELD(nextx, nexty))
5175 static int opposite_directions[] =
5182 int move_dir_bit = MV_DIR_BIT(direction);
5183 int opposite_direction = opposite_directions[move_dir_bit];
5184 int hitting_side = direction;
5185 int touched_side = opposite_direction;
5186 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5187 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5188 MovDir[nextx][nexty] != direction ||
5189 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5195 CheckElementSideChange(nextx, nexty, touched_element,
5196 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5198 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5199 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5201 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5203 struct ElementChangeInfo *change =
5204 &element_info[hitting_element].change_page[i];
5206 if (change->can_change &&
5207 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5208 change->sides & touched_side &&
5209 change->trigger_element == touched_element)
5211 CheckElementSideChange(newx, newy, hitting_element,
5212 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5218 if (IS_CUSTOM_ELEMENT(touched_element) &&
5219 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5221 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5223 struct ElementChangeInfo *change =
5224 &element_info[touched_element].change_page[i];
5226 if (change->can_change &&
5227 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5228 change->sides & hitting_side &&
5229 change->trigger_element == hitting_element)
5231 CheckElementSideChange(nextx, nexty, touched_element,
5232 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5243 TestIfPlayerTouchesCustomElement(newx, newy);
5244 TestIfElementTouchesCustomElement(newx, newy);
5247 int AmoebeNachbarNr(int ax, int ay)
5250 int element = Feld[ax][ay];
5252 static int xy[4][2] =
5260 for (i = 0; i < 4; i++)
5262 int x = ax + xy[i][0];
5263 int y = ay + xy[i][1];
5265 if (!IN_LEV_FIELD(x, y))
5268 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5269 group_nr = AmoebaNr[x][y];
5275 void AmoebenVereinigen(int ax, int ay)
5277 int i, x, y, xx, yy;
5278 int new_group_nr = AmoebaNr[ax][ay];
5279 static int xy[4][2] =
5287 if (new_group_nr == 0)
5290 for (i = 0; i < 4; i++)
5295 if (!IN_LEV_FIELD(x, y))
5298 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5299 Feld[x][y] == EL_BD_AMOEBA ||
5300 Feld[x][y] == EL_AMOEBA_DEAD) &&
5301 AmoebaNr[x][y] != new_group_nr)
5303 int old_group_nr = AmoebaNr[x][y];
5305 if (old_group_nr == 0)
5308 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5309 AmoebaCnt[old_group_nr] = 0;
5310 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5311 AmoebaCnt2[old_group_nr] = 0;
5313 for (yy = 0; yy < lev_fieldy; yy++)
5315 for (xx = 0; xx < lev_fieldx; xx++)
5317 if (AmoebaNr[xx][yy] == old_group_nr)
5318 AmoebaNr[xx][yy] = new_group_nr;
5325 void AmoebeUmwandeln(int ax, int ay)
5329 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5331 int group_nr = AmoebaNr[ax][ay];
5336 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5337 printf("AmoebeUmwandeln(): This should never happen!\n");
5342 for (y = 0; y < lev_fieldy; y++)
5344 for (x = 0; x < lev_fieldx; x++)
5346 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5349 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5353 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5354 SND_AMOEBA_TURNING_TO_GEM :
5355 SND_AMOEBA_TURNING_TO_ROCK));
5360 static int xy[4][2] =
5368 for (i = 0; i < 4; i++)
5373 if (!IN_LEV_FIELD(x, y))
5376 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5378 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5379 SND_AMOEBA_TURNING_TO_GEM :
5380 SND_AMOEBA_TURNING_TO_ROCK));
5387 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5390 int group_nr = AmoebaNr[ax][ay];
5391 boolean done = FALSE;
5396 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5397 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5402 for (y = 0; y < lev_fieldy; y++)
5404 for (x = 0; x < lev_fieldx; x++)
5406 if (AmoebaNr[x][y] == group_nr &&
5407 (Feld[x][y] == EL_AMOEBA_DEAD ||
5408 Feld[x][y] == EL_BD_AMOEBA ||
5409 Feld[x][y] == EL_AMOEBA_GROWING))
5412 Feld[x][y] = new_element;
5413 InitField(x, y, FALSE);
5414 DrawLevelField(x, y);
5421 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5422 SND_BD_AMOEBA_TURNING_TO_ROCK :
5423 SND_BD_AMOEBA_TURNING_TO_GEM));
5426 void AmoebeWaechst(int x, int y)
5428 static unsigned long sound_delay = 0;
5429 static unsigned long sound_delay_value = 0;
5431 if (!MovDelay[x][y]) /* start new growing cycle */
5435 if (DelayReached(&sound_delay, sound_delay_value))
5438 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5440 if (Store[x][y] == EL_BD_AMOEBA)
5441 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5443 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5445 sound_delay_value = 30;
5449 if (MovDelay[x][y]) /* wait some time before growing bigger */
5452 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5454 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5455 6 - MovDelay[x][y]);
5457 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5460 if (!MovDelay[x][y])
5462 Feld[x][y] = Store[x][y];
5464 DrawLevelField(x, y);
5469 void AmoebaDisappearing(int x, int y)
5471 static unsigned long sound_delay = 0;
5472 static unsigned long sound_delay_value = 0;
5474 if (!MovDelay[x][y]) /* start new shrinking cycle */
5478 if (DelayReached(&sound_delay, sound_delay_value))
5479 sound_delay_value = 30;
5482 if (MovDelay[x][y]) /* wait some time before shrinking */
5485 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5487 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5488 6 - MovDelay[x][y]);
5490 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5493 if (!MovDelay[x][y])
5495 Feld[x][y] = EL_EMPTY;
5496 DrawLevelField(x, y);
5498 /* don't let mole enter this field in this cycle;
5499 (give priority to objects falling to this field from above) */
5505 void AmoebeAbleger(int ax, int ay)
5508 int element = Feld[ax][ay];
5509 int graphic = el2img(element);
5510 int newax = ax, neway = ay;
5511 static int xy[4][2] =
5519 if (!level.amoeba_speed)
5521 Feld[ax][ay] = EL_AMOEBA_DEAD;
5522 DrawLevelField(ax, ay);
5526 if (IS_ANIMATED(graphic))
5527 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5529 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5530 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5532 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5535 if (MovDelay[ax][ay])
5539 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5542 int x = ax + xy[start][0];
5543 int y = ay + xy[start][1];
5545 if (!IN_LEV_FIELD(x, y))
5548 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5549 if (IS_FREE(x, y) ||
5550 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5556 if (newax == ax && neway == ay)
5559 else /* normal or "filled" (BD style) amoeba */
5562 boolean waiting_for_player = FALSE;
5564 for (i = 0; i < 4; i++)
5566 int j = (start + i) % 4;
5567 int x = ax + xy[j][0];
5568 int y = ay + xy[j][1];
5570 if (!IN_LEV_FIELD(x, y))
5573 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5574 if (IS_FREE(x, y) ||
5575 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5581 else if (IS_PLAYER(x, y))
5582 waiting_for_player = TRUE;
5585 if (newax == ax && neway == ay) /* amoeba cannot grow */
5587 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5589 Feld[ax][ay] = EL_AMOEBA_DEAD;
5590 DrawLevelField(ax, ay);
5591 AmoebaCnt[AmoebaNr[ax][ay]]--;
5593 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5595 if (element == EL_AMOEBA_FULL)
5596 AmoebeUmwandeln(ax, ay);
5597 else if (element == EL_BD_AMOEBA)
5598 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5603 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5605 /* amoeba gets larger by growing in some direction */
5607 int new_group_nr = AmoebaNr[ax][ay];
5610 if (new_group_nr == 0)
5612 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5613 printf("AmoebeAbleger(): This should never happen!\n");
5618 AmoebaNr[newax][neway] = new_group_nr;
5619 AmoebaCnt[new_group_nr]++;
5620 AmoebaCnt2[new_group_nr]++;
5622 /* if amoeba touches other amoeba(s) after growing, unify them */
5623 AmoebenVereinigen(newax, neway);
5625 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5627 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5633 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5634 (neway == lev_fieldy - 1 && newax != ax))
5636 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5637 Store[newax][neway] = element;
5639 else if (neway == ay)
5641 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5643 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5645 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5650 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5651 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5652 Store[ax][ay] = EL_AMOEBA_DROP;
5653 ContinueMoving(ax, ay);
5657 DrawLevelField(newax, neway);
5660 void Life(int ax, int ay)
5663 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5665 int element = Feld[ax][ay];
5666 int graphic = el2img(element);
5667 boolean changed = FALSE;
5669 if (IS_ANIMATED(graphic))
5670 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5675 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5676 MovDelay[ax][ay] = life_time;
5678 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5681 if (MovDelay[ax][ay])
5685 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5687 int xx = ax+x1, yy = ay+y1;
5690 if (!IN_LEV_FIELD(xx, yy))
5693 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5695 int x = xx+x2, y = yy+y2;
5697 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5700 if (((Feld[x][y] == element ||
5701 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5703 (IS_FREE(x, y) && Stop[x][y]))
5707 if (xx == ax && yy == ay) /* field in the middle */
5709 if (nachbarn < life[0] || nachbarn > life[1])
5711 Feld[xx][yy] = EL_EMPTY;
5713 DrawLevelField(xx, yy);
5714 Stop[xx][yy] = TRUE;
5718 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5719 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5720 { /* free border field */
5721 if (nachbarn >= life[2] && nachbarn <= life[3])
5723 Feld[xx][yy] = element;
5724 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5726 DrawLevelField(xx, yy);
5727 Stop[xx][yy] = TRUE;
5734 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5735 SND_GAME_OF_LIFE_GROWING);
5738 static void InitRobotWheel(int x, int y)
5740 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5743 static void RunRobotWheel(int x, int y)
5745 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5748 static void StopRobotWheel(int x, int y)
5750 if (ZX == x && ZY == y)
5754 static void InitTimegateWheel(int x, int y)
5756 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5759 static void RunTimegateWheel(int x, int y)
5761 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5764 void CheckExit(int x, int y)
5766 if (local_player->gems_still_needed > 0 ||
5767 local_player->sokobanfields_still_needed > 0 ||
5768 local_player->lights_still_needed > 0)
5770 int element = Feld[x][y];
5771 int graphic = el2img(element);
5773 if (IS_ANIMATED(graphic))
5774 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5779 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5782 Feld[x][y] = EL_EXIT_OPENING;
5784 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5787 void CheckExitSP(int x, int y)
5789 if (local_player->gems_still_needed > 0)
5791 int element = Feld[x][y];
5792 int graphic = el2img(element);
5794 if (IS_ANIMATED(graphic))
5795 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5800 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5803 Feld[x][y] = EL_SP_EXIT_OPENING;
5805 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5808 static void CloseAllOpenTimegates()
5812 for (y = 0; y < lev_fieldy; y++)
5814 for (x = 0; x < lev_fieldx; x++)
5816 int element = Feld[x][y];
5818 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5820 Feld[x][y] = EL_TIMEGATE_CLOSING;
5822 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5824 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5831 void EdelsteinFunkeln(int x, int y)
5833 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5836 if (Feld[x][y] == EL_BD_DIAMOND)
5839 if (MovDelay[x][y] == 0) /* next animation frame */
5840 MovDelay[x][y] = 11 * !SimpleRND(500);
5842 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5846 if (setup.direct_draw && MovDelay[x][y])
5847 SetDrawtoField(DRAW_BUFFERED);
5849 DrawLevelElementAnimation(x, y, Feld[x][y]);
5851 if (MovDelay[x][y] != 0)
5853 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5854 10 - MovDelay[x][y]);
5856 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5858 if (setup.direct_draw)
5862 dest_x = FX + SCREENX(x) * TILEX;
5863 dest_y = FY + SCREENY(y) * TILEY;
5865 BlitBitmap(drawto_field, window,
5866 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5867 SetDrawtoField(DRAW_DIRECT);
5873 void MauerWaechst(int x, int y)
5877 if (!MovDelay[x][y]) /* next animation frame */
5878 MovDelay[x][y] = 3 * delay;
5880 if (MovDelay[x][y]) /* wait some time before next frame */
5884 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5886 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5887 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5889 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5892 if (!MovDelay[x][y])
5894 if (MovDir[x][y] == MV_LEFT)
5896 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5897 DrawLevelField(x - 1, y);
5899 else if (MovDir[x][y] == MV_RIGHT)
5901 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5902 DrawLevelField(x + 1, y);
5904 else if (MovDir[x][y] == MV_UP)
5906 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5907 DrawLevelField(x, y - 1);
5911 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5912 DrawLevelField(x, y + 1);
5915 Feld[x][y] = Store[x][y];
5917 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5918 DrawLevelField(x, y);
5923 void MauerAbleger(int ax, int ay)
5925 int element = Feld[ax][ay];
5926 int graphic = el2img(element);
5927 boolean oben_frei = FALSE, unten_frei = FALSE;
5928 boolean links_frei = FALSE, rechts_frei = FALSE;
5929 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5930 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5931 boolean new_wall = FALSE;
5933 if (IS_ANIMATED(graphic))
5934 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5936 if (!MovDelay[ax][ay]) /* start building new wall */
5937 MovDelay[ax][ay] = 6;
5939 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5942 if (MovDelay[ax][ay])
5946 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5948 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5950 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5952 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5955 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5956 element == EL_EXPANDABLE_WALL_ANY)
5960 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5961 Store[ax][ay-1] = element;
5962 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5963 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5964 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5965 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5970 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5971 Store[ax][ay+1] = element;
5972 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5973 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5974 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5975 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5980 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5981 element == EL_EXPANDABLE_WALL_ANY ||
5982 element == EL_EXPANDABLE_WALL)
5986 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5987 Store[ax-1][ay] = element;
5988 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5989 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5990 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5991 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5997 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5998 Store[ax+1][ay] = element;
5999 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6000 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6001 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6002 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6007 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6008 DrawLevelField(ax, ay);
6010 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6012 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6013 unten_massiv = TRUE;
6014 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6015 links_massiv = TRUE;
6016 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6017 rechts_massiv = TRUE;
6019 if (((oben_massiv && unten_massiv) ||
6020 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6021 element == EL_EXPANDABLE_WALL) &&
6022 ((links_massiv && rechts_massiv) ||
6023 element == EL_EXPANDABLE_WALL_VERTICAL))
6024 Feld[ax][ay] = EL_WALL;
6028 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6030 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6034 void CheckForDragon(int x, int y)
6037 boolean dragon_found = FALSE;
6038 static int xy[4][2] =
6046 for (i = 0; i < 4; i++)
6048 for (j = 0; j < 4; j++)
6050 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6052 if (IN_LEV_FIELD(xx, yy) &&
6053 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6055 if (Feld[xx][yy] == EL_DRAGON)
6056 dragon_found = TRUE;
6065 for (i = 0; i < 4; i++)
6067 for (j = 0; j < 3; j++)
6069 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6071 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6073 Feld[xx][yy] = EL_EMPTY;
6074 DrawLevelField(xx, yy);
6083 static void InitBuggyBase(int x, int y)
6085 int element = Feld[x][y];
6086 int activating_delay = FRAMES_PER_SECOND / 4;
6089 (element == EL_SP_BUGGY_BASE ?
6090 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6091 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6093 element == EL_SP_BUGGY_BASE_ACTIVE ?
6094 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6097 static void WarnBuggyBase(int x, int y)
6100 static int xy[4][2] =
6108 for (i = 0; i < 4; i++)
6110 int xx = x + xy[i][0], yy = y + xy[i][1];
6112 if (IS_PLAYER(xx, yy))
6114 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6121 static void InitTrap(int x, int y)
6123 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6126 static void ActivateTrap(int x, int y)
6128 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6131 static void ChangeActiveTrap(int x, int y)
6133 int graphic = IMG_TRAP_ACTIVE;
6135 /* if new animation frame was drawn, correct crumbled sand border */
6136 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6137 DrawLevelFieldCrumbledSand(x, y);
6140 static void ChangeElementNowExt(int x, int y, int target_element)
6142 int previous_move_direction = MovDir[x][y];
6144 /* check if element under player changes from accessible to unaccessible
6145 (needed for special case of dropping element which then changes) */
6146 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6147 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6154 Feld[x][y] = target_element;
6156 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6158 ResetGfxAnimation(x, y);
6159 ResetRandomAnimationValue(x, y);
6161 if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
6162 MovDir[x][y] = previous_move_direction;
6164 InitField(x, y, FALSE);
6165 if (CAN_MOVE(Feld[x][y]))
6168 DrawLevelField(x, y);
6170 if (GFX_CRUMBLED(Feld[x][y]))
6171 DrawLevelFieldCrumbledSandNeighbours(x, y);
6173 TestIfBadThingTouchesHero(x, y);
6174 TestIfPlayerTouchesCustomElement(x, y);
6175 TestIfElementTouchesCustomElement(x, y);
6177 if (ELEM_IS_PLAYER(target_element))
6178 RelocatePlayer(x, y, target_element);
6181 static boolean ChangeElementNow(int x, int y, int element, int page)
6183 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6185 /* always use default change event to prevent running into a loop */
6186 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6187 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6189 /* do not change already changed elements with same change event */
6191 if (Changed[x][y] & ChangeEvent[x][y])
6198 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6200 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6202 if (change->explode)
6209 if (change->use_content)
6211 boolean complete_change = TRUE;
6212 boolean can_change[3][3];
6215 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6217 boolean half_destructible;
6218 int ex = x + xx - 1;
6219 int ey = y + yy - 1;
6222 can_change[xx][yy] = TRUE;
6224 if (ex == x && ey == y) /* do not check changing element itself */
6227 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6229 can_change[xx][yy] = FALSE; /* do not change empty borders */
6234 if (!IN_LEV_FIELD(ex, ey))
6236 can_change[xx][yy] = FALSE;
6237 complete_change = FALSE;
6244 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6245 e = MovingOrBlocked2Element(ex, ey);
6247 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6249 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6250 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6251 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6253 can_change[xx][yy] = FALSE;
6254 complete_change = FALSE;
6258 if (!change->only_complete || complete_change)
6260 boolean something_has_changed = FALSE;
6262 if (change->only_complete && change->use_random_change &&
6263 RND(100) < change->random)
6266 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6268 int ex = x + xx - 1;
6269 int ey = y + yy - 1;
6271 if (can_change[xx][yy] && (!change->use_random_change ||
6272 RND(100) < change->random))
6274 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6275 RemoveMovingField(ex, ey);
6277 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6279 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6281 something_has_changed = TRUE;
6283 /* for symmetry reasons, freeze newly created border elements */
6284 if (ex != x || ey != y)
6285 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6289 if (something_has_changed)
6290 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6295 ChangeElementNowExt(x, y, change->target_element);
6297 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6303 static void ChangeElement(int x, int y, int page)
6305 int element = MovingOrBlocked2Element(x, y);
6306 struct ElementInfo *ei = &element_info[element];
6307 struct ElementChangeInfo *change = &ei->change_page[page];
6311 if (!CAN_CHANGE(element))
6314 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6315 x, y, element, element_info[element].token_name);
6316 printf("ChangeElement(): This should never happen!\n");
6322 if (ChangeDelay[x][y] == 0) /* initialize element change */
6324 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6325 RND(change->delay_random * change->delay_frames)) + 1;
6327 ResetGfxAnimation(x, y);
6328 ResetRandomAnimationValue(x, y);
6330 if (change->pre_change_function)
6331 change->pre_change_function(x, y);
6334 ChangeDelay[x][y]--;
6336 if (ChangeDelay[x][y] != 0) /* continue element change */
6338 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6340 if (IS_ANIMATED(graphic))
6341 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6343 if (change->change_function)
6344 change->change_function(x, y);
6346 else /* finish element change */
6348 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6350 page = ChangePage[x][y];
6351 ChangePage[x][y] = -1;
6355 if (IS_MOVING(x, y) && !change->explode)
6357 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6360 ChangeDelay[x][y] = 1; /* try change after next move step */
6361 ChangePage[x][y] = page; /* remember page to use for change */
6366 if (ChangeElementNow(x, y, element, page))
6368 if (change->post_change_function)
6369 change->post_change_function(x, y);
6374 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6375 int trigger_element,
6381 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6384 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6386 int element = EL_CUSTOM_START + i;
6388 boolean change_element = FALSE;
6391 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6394 for (j = 0; j < element_info[element].num_change_pages; j++)
6396 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6398 if (change->can_change &&
6400 change->events & CH_EVENT_BIT(trigger_event) &&
6402 change->sides & trigger_side &&
6403 change->trigger_element == trigger_element)
6406 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6407 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6408 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6411 change_element = TRUE;
6418 if (!change_element)
6421 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6424 if (x == lx && y == ly) /* do not change trigger element itself */
6428 if (Feld[x][y] == element)
6430 ChangeDelay[x][y] = 1;
6431 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6432 ChangeElement(x, y, page);
6440 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6443 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6447 static boolean CheckElementSideChange(int x, int y, int element, int side,
6448 int trigger_event, int page)
6450 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6453 if (Feld[x][y] == EL_BLOCKED)
6455 Blocked2Moving(x, y, &x, &y);
6456 element = Feld[x][y];
6460 page = element_info[element].event_page_nr[trigger_event];
6462 if (!(element_info[element].change_page[page].sides & side))
6465 ChangeDelay[x][y] = 1;
6466 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6467 ChangeElement(x, y, page);
6472 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6474 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6477 static void PlayPlayerSound(struct PlayerInfo *player)
6479 int jx = player->jx, jy = player->jy;
6480 int element = player->element_nr;
6481 int last_action = player->last_action_waiting;
6482 int action = player->action_waiting;
6484 if (player->is_waiting)
6486 if (action != last_action)
6487 PlayLevelSoundElementAction(jx, jy, element, action);
6489 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6493 if (action != last_action)
6494 StopSound(element_info[element].sound[last_action]);
6496 if (last_action == ACTION_SLEEPING)
6497 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6501 static void PlayAllPlayersSound()
6505 for (i = 0; i < MAX_PLAYERS; i++)
6506 if (stored_player[i].active)
6507 PlayPlayerSound(&stored_player[i]);
6510 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6512 boolean last_waiting = player->is_waiting;
6513 int move_dir = player->MovDir;
6515 player->last_action_waiting = player->action_waiting;
6519 if (!last_waiting) /* not waiting -> waiting */
6521 player->is_waiting = TRUE;
6523 player->frame_counter_bored =
6525 game.player_boring_delay_fixed +
6526 SimpleRND(game.player_boring_delay_random);
6527 player->frame_counter_sleeping =
6529 game.player_sleeping_delay_fixed +
6530 SimpleRND(game.player_sleeping_delay_random);
6532 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6535 if (game.player_sleeping_delay_fixed +
6536 game.player_sleeping_delay_random > 0 &&
6537 player->anim_delay_counter == 0 &&
6538 player->post_delay_counter == 0 &&
6539 FrameCounter >= player->frame_counter_sleeping)
6540 player->is_sleeping = TRUE;
6541 else if (game.player_boring_delay_fixed +
6542 game.player_boring_delay_random > 0 &&
6543 FrameCounter >= player->frame_counter_bored)
6544 player->is_bored = TRUE;
6546 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6547 player->is_bored ? ACTION_BORING :
6550 if (player->is_sleeping)
6552 if (player->num_special_action_sleeping > 0)
6554 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6556 int last_special_action = player->special_action_sleeping;
6557 int num_special_action = player->num_special_action_sleeping;
6558 int special_action =
6559 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6560 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6561 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6562 last_special_action + 1 : ACTION_SLEEPING);
6563 int special_graphic =
6564 el_act_dir2img(player->element_nr, special_action, move_dir);
6566 player->anim_delay_counter =
6567 graphic_info[special_graphic].anim_delay_fixed +
6568 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6569 player->post_delay_counter =
6570 graphic_info[special_graphic].post_delay_fixed +
6571 SimpleRND(graphic_info[special_graphic].post_delay_random);
6573 player->special_action_sleeping = special_action;
6576 if (player->anim_delay_counter > 0)
6578 player->action_waiting = player->special_action_sleeping;
6579 player->anim_delay_counter--;
6581 else if (player->post_delay_counter > 0)
6583 player->post_delay_counter--;
6587 else if (player->is_bored)
6589 if (player->num_special_action_bored > 0)
6591 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6593 int special_action =
6594 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6595 int special_graphic =
6596 el_act_dir2img(player->element_nr, special_action, move_dir);
6598 player->anim_delay_counter =
6599 graphic_info[special_graphic].anim_delay_fixed +
6600 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6601 player->post_delay_counter =
6602 graphic_info[special_graphic].post_delay_fixed +
6603 SimpleRND(graphic_info[special_graphic].post_delay_random);
6605 player->special_action_bored = special_action;
6608 if (player->anim_delay_counter > 0)
6610 player->action_waiting = player->special_action_bored;
6611 player->anim_delay_counter--;
6613 else if (player->post_delay_counter > 0)
6615 player->post_delay_counter--;
6620 else if (last_waiting) /* waiting -> not waiting */
6622 player->is_waiting = FALSE;
6623 player->is_bored = FALSE;
6624 player->is_sleeping = FALSE;
6626 player->frame_counter_bored = -1;
6627 player->frame_counter_sleeping = -1;
6629 player->anim_delay_counter = 0;
6630 player->post_delay_counter = 0;
6632 player->action_waiting = ACTION_DEFAULT;
6634 player->special_action_bored = ACTION_DEFAULT;
6635 player->special_action_sleeping = ACTION_DEFAULT;
6640 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6643 static byte stored_player_action[MAX_PLAYERS];
6644 static int num_stored_actions = 0;
6646 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6647 int left = player_action & JOY_LEFT;
6648 int right = player_action & JOY_RIGHT;
6649 int up = player_action & JOY_UP;
6650 int down = player_action & JOY_DOWN;
6651 int button1 = player_action & JOY_BUTTON_1;
6652 int button2 = player_action & JOY_BUTTON_2;
6653 int dx = (left ? -1 : right ? 1 : 0);
6654 int dy = (up ? -1 : down ? 1 : 0);
6657 stored_player_action[player->index_nr] = 0;
6658 num_stored_actions++;
6662 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6665 if (!player->active || tape.pausing)
6669 printf("::: [%d %d %d %d] [%d %d]\n",
6670 left, right, up, down, button1, button2);
6676 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6680 snapped = SnapField(player, dx, dy);
6684 dropped = DropElement(player);
6686 moved = MovePlayer(player, dx, dy);
6689 if (tape.single_step && tape.recording && !tape.pausing)
6691 if (button1 || (dropped && !moved))
6693 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6694 SnapField(player, 0, 0); /* stop snapping */
6698 SetPlayerWaiting(player, FALSE);
6701 return player_action;
6703 stored_player_action[player->index_nr] = player_action;
6709 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6712 /* no actions for this player (no input at player's configured device) */
6714 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6715 SnapField(player, 0, 0);
6716 CheckGravityMovement(player);
6718 if (player->MovPos == 0)
6719 SetPlayerWaiting(player, TRUE);
6721 if (player->MovPos == 0) /* needed for tape.playing */
6722 player->is_moving = FALSE;
6724 player->is_dropping = FALSE;
6730 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6732 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6734 TapeRecordAction(stored_player_action);
6735 num_stored_actions = 0;
6742 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6744 static byte stored_player_action[MAX_PLAYERS];
6745 static int num_stored_actions = 0;
6746 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6747 int left = player_action & JOY_LEFT;
6748 int right = player_action & JOY_RIGHT;
6749 int up = player_action & JOY_UP;
6750 int down = player_action & JOY_DOWN;
6751 int button1 = player_action & JOY_BUTTON_1;
6752 int button2 = player_action & JOY_BUTTON_2;
6753 int dx = (left ? -1 : right ? 1 : 0);
6754 int dy = (up ? -1 : down ? 1 : 0);
6756 stored_player_action[player->index_nr] = 0;
6757 num_stored_actions++;
6759 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6761 if (!player->active || tape.pausing)
6766 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6769 snapped = SnapField(player, dx, dy);
6773 dropped = DropElement(player);
6775 moved = MovePlayer(player, dx, dy);
6778 if (tape.single_step && tape.recording && !tape.pausing)
6780 if (button1 || (dropped && !moved))
6782 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6783 SnapField(player, 0, 0); /* stop snapping */
6787 stored_player_action[player->index_nr] = player_action;
6791 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6793 /* no actions for this player (no input at player's configured device) */
6795 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6796 SnapField(player, 0, 0);
6797 CheckGravityMovement(player);
6799 if (player->MovPos == 0)
6800 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6802 if (player->MovPos == 0) /* needed for tape.playing */
6803 player->is_moving = FALSE;
6806 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6808 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6810 TapeRecordAction(stored_player_action);
6811 num_stored_actions = 0;
6818 static unsigned long action_delay = 0;
6819 unsigned long action_delay_value;
6820 int magic_wall_x = 0, magic_wall_y = 0;
6821 int i, x, y, element, graphic;
6822 byte *recorded_player_action;
6823 byte summarized_player_action = 0;
6825 byte tape_action[MAX_PLAYERS];
6828 if (game_status != GAME_MODE_PLAYING)
6831 action_delay_value =
6832 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6834 if (tape.playing && tape.index_search && !tape.pausing)
6835 action_delay_value = 0;
6837 /* ---------- main game synchronization point ---------- */
6839 WaitUntilDelayReached(&action_delay, action_delay_value);
6841 if (network_playing && !network_player_action_received)
6845 printf("DEBUG: try to get network player actions in time\n");
6849 #if defined(PLATFORM_UNIX)
6850 /* last chance to get network player actions without main loop delay */
6854 if (game_status != GAME_MODE_PLAYING)
6857 if (!network_player_action_received)
6861 printf("DEBUG: failed to get network player actions in time\n");
6872 printf("::: getting new tape action [%d]\n", FrameCounter);
6875 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6877 for (i = 0; i < MAX_PLAYERS; i++)
6879 summarized_player_action |= stored_player[i].action;
6881 if (!network_playing)
6882 stored_player[i].effective_action = stored_player[i].action;
6885 #if defined(PLATFORM_UNIX)
6886 if (network_playing)
6887 SendToServer_MovePlayer(summarized_player_action);
6890 if (!options.network && !setup.team_mode)
6891 local_player->effective_action = summarized_player_action;
6893 for (i = 0; i < MAX_PLAYERS; i++)
6895 int actual_player_action = stored_player[i].effective_action;
6897 if (stored_player[i].programmed_action)
6898 actual_player_action = stored_player[i].programmed_action;
6900 if (recorded_player_action)
6901 actual_player_action = recorded_player_action[i];
6903 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6905 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6906 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6908 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6913 TapeRecordAction(tape_action);
6916 network_player_action_received = FALSE;
6918 ScrollScreen(NULL, SCROLL_GO_ON);
6924 for (i = 0; i < MAX_PLAYERS; i++)
6925 stored_player[i].Frame++;
6929 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6931 for (i = 0; i < MAX_PLAYERS; i++)
6933 struct PlayerInfo *player = &stored_player[i];
6937 if (player->active && player->is_pushing && player->is_moving &&
6940 ContinueMoving(x, y);
6942 /* continue moving after pushing (this is actually a bug) */
6943 if (!IS_MOVING(x, y))
6952 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6954 Changed[x][y] = CE_BITMASK_DEFAULT;
6955 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6958 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6960 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6961 printf("GameActions(): This should never happen!\n");
6963 ChangePage[x][y] = -1;
6968 if (WasJustMoving[x][y] > 0)
6969 WasJustMoving[x][y]--;
6970 if (WasJustFalling[x][y] > 0)
6971 WasJustFalling[x][y]--;
6976 /* reset finished pushing action (not done in ContinueMoving() to allow
6977 continous pushing animation for elements with zero push delay) */
6978 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6980 ResetGfxAnimation(x, y);
6981 DrawLevelField(x, y);
6986 if (IS_BLOCKED(x, y))
6990 Blocked2Moving(x, y, &oldx, &oldy);
6991 if (!IS_MOVING(oldx, oldy))
6993 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6994 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6995 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6996 printf("GameActions(): This should never happen!\n");
7002 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7004 element = Feld[x][y];
7006 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7008 graphic = el2img(element);
7014 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7016 element = graphic = 0;
7020 if (graphic_info[graphic].anim_global_sync)
7021 GfxFrame[x][y] = FrameCounter;
7023 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7024 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7025 ResetRandomAnimationValue(x, y);
7027 SetRandomAnimationValue(x, y);
7030 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7033 if (IS_INACTIVE(element))
7035 if (IS_ANIMATED(graphic))
7036 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7042 /* this may take place after moving, so 'element' may have changed */
7044 if (IS_CHANGING(x, y))
7046 if (IS_CHANGING(x, y) &&
7047 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7051 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7052 element_info[element].event_page_nr[CE_DELAY]);
7054 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7057 element = Feld[x][y];
7058 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7062 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7067 element = Feld[x][y];
7068 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7070 if (element == EL_MOLE)
7071 printf("::: %d, %d, %d [%d]\n",
7072 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7076 if (element == EL_YAMYAM)
7077 printf("::: %d, %d, %d\n",
7078 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7082 if (IS_ANIMATED(graphic) &&
7086 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7089 if (element == EL_BUG)
7090 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7094 if (element == EL_MOLE)
7095 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7099 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7100 EdelsteinFunkeln(x, y);
7102 else if ((element == EL_ACID ||
7103 element == EL_EXIT_OPEN ||
7104 element == EL_SP_EXIT_OPEN ||
7105 element == EL_SP_TERMINAL ||
7106 element == EL_SP_TERMINAL_ACTIVE ||
7107 element == EL_EXTRA_TIME ||
7108 element == EL_SHIELD_NORMAL ||
7109 element == EL_SHIELD_DEADLY) &&
7110 IS_ANIMATED(graphic))
7111 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7112 else if (IS_MOVING(x, y))
7113 ContinueMoving(x, y);
7114 else if (IS_ACTIVE_BOMB(element))
7115 CheckDynamite(x, y);
7117 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7118 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7120 else if (element == EL_AMOEBA_GROWING)
7121 AmoebeWaechst(x, y);
7122 else if (element == EL_AMOEBA_SHRINKING)
7123 AmoebaDisappearing(x, y);
7125 #if !USE_NEW_AMOEBA_CODE
7126 else if (IS_AMOEBALIVE(element))
7127 AmoebeAbleger(x, y);
7130 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7132 else if (element == EL_EXIT_CLOSED)
7134 else if (element == EL_SP_EXIT_CLOSED)
7136 else if (element == EL_EXPANDABLE_WALL_GROWING)
7138 else if (element == EL_EXPANDABLE_WALL ||
7139 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7140 element == EL_EXPANDABLE_WALL_VERTICAL ||
7141 element == EL_EXPANDABLE_WALL_ANY)
7143 else if (element == EL_FLAMES)
7144 CheckForDragon(x, y);
7146 else if (IS_AUTO_CHANGING(element))
7147 ChangeElement(x, y);
7149 else if (element == EL_EXPLOSION)
7150 ; /* drawing of correct explosion animation is handled separately */
7151 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7152 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7155 /* this may take place after moving, so 'element' may have changed */
7156 if (IS_AUTO_CHANGING(Feld[x][y]))
7157 ChangeElement(x, y);
7160 if (IS_BELT_ACTIVE(element))
7161 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7163 if (game.magic_wall_active)
7165 int jx = local_player->jx, jy = local_player->jy;
7167 /* play the element sound at the position nearest to the player */
7168 if ((element == EL_MAGIC_WALL_FULL ||
7169 element == EL_MAGIC_WALL_ACTIVE ||
7170 element == EL_MAGIC_WALL_EMPTYING ||
7171 element == EL_BD_MAGIC_WALL_FULL ||
7172 element == EL_BD_MAGIC_WALL_ACTIVE ||
7173 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7174 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7182 #if USE_NEW_AMOEBA_CODE
7183 /* new experimental amoeba growth stuff */
7185 if (!(FrameCounter % 8))
7188 static unsigned long random = 1684108901;
7190 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7193 x = (random >> 10) % lev_fieldx;
7194 y = (random >> 20) % lev_fieldy;
7196 x = RND(lev_fieldx);
7197 y = RND(lev_fieldy);
7199 element = Feld[x][y];
7201 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7202 if (!IS_PLAYER(x,y) &&
7203 (element == EL_EMPTY ||
7204 element == EL_SAND ||
7205 element == EL_QUICKSAND_EMPTY ||
7206 element == EL_ACID_SPLASH_LEFT ||
7207 element == EL_ACID_SPLASH_RIGHT))
7209 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7210 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7211 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7212 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7213 Feld[x][y] = EL_AMOEBA_DROP;
7216 random = random * 129 + 1;
7222 if (game.explosions_delayed)
7225 game.explosions_delayed = FALSE;
7227 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7229 element = Feld[x][y];
7231 if (ExplodeField[x][y])
7232 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7233 else if (element == EL_EXPLOSION)
7234 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7236 ExplodeField[x][y] = EX_NO_EXPLOSION;
7239 game.explosions_delayed = TRUE;
7242 if (game.magic_wall_active)
7244 if (!(game.magic_wall_time_left % 4))
7246 int element = Feld[magic_wall_x][magic_wall_y];
7248 if (element == EL_BD_MAGIC_WALL_FULL ||
7249 element == EL_BD_MAGIC_WALL_ACTIVE ||
7250 element == EL_BD_MAGIC_WALL_EMPTYING)
7251 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7253 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7256 if (game.magic_wall_time_left > 0)
7258 game.magic_wall_time_left--;
7259 if (!game.magic_wall_time_left)
7261 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7263 element = Feld[x][y];
7265 if (element == EL_MAGIC_WALL_ACTIVE ||
7266 element == EL_MAGIC_WALL_FULL)
7268 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7269 DrawLevelField(x, y);
7271 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7272 element == EL_BD_MAGIC_WALL_FULL)
7274 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7275 DrawLevelField(x, y);
7279 game.magic_wall_active = FALSE;
7284 if (game.light_time_left > 0)
7286 game.light_time_left--;
7288 if (game.light_time_left == 0)
7289 RedrawAllLightSwitchesAndInvisibleElements();
7292 if (game.timegate_time_left > 0)
7294 game.timegate_time_left--;
7296 if (game.timegate_time_left == 0)
7297 CloseAllOpenTimegates();
7300 for (i = 0; i < MAX_PLAYERS; i++)
7302 struct PlayerInfo *player = &stored_player[i];
7304 if (SHIELD_ON(player))
7306 if (player->shield_deadly_time_left)
7307 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7308 else if (player->shield_normal_time_left)
7309 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7313 if (TimeFrames >= FRAMES_PER_SECOND)
7318 for (i = 0; i < MAX_PLAYERS; i++)
7320 struct PlayerInfo *player = &stored_player[i];
7322 if (SHIELD_ON(player))
7324 player->shield_normal_time_left--;
7326 if (player->shield_deadly_time_left > 0)
7327 player->shield_deadly_time_left--;
7331 if (tape.recording || tape.playing)
7332 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7338 if (TimeLeft <= 10 && setup.time_limit)
7339 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7341 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7343 if (!TimeLeft && setup.time_limit)
7344 for (i = 0; i < MAX_PLAYERS; i++)
7345 KillHero(&stored_player[i]);
7347 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7348 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7352 PlayAllPlayersSound();
7354 if (options.debug) /* calculate frames per second */
7356 static unsigned long fps_counter = 0;
7357 static int fps_frames = 0;
7358 unsigned long fps_delay_ms = Counter() - fps_counter;
7362 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7364 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7367 fps_counter = Counter();
7370 redraw_mask |= REDRAW_FPS;
7374 if (stored_player[0].jx != stored_player[0].last_jx ||
7375 stored_player[0].jy != stored_player[0].last_jy)
7376 printf("::: %d, %d, %d, %d, %d\n",
7377 stored_player[0].MovDir,
7378 stored_player[0].MovPos,
7379 stored_player[0].GfxPos,
7380 stored_player[0].Frame,
7381 stored_player[0].StepFrame);
7388 for (i = 0; i < MAX_PLAYERS; i++)
7391 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7393 stored_player[i].Frame += move_frames;
7395 if (stored_player[i].MovPos != 0)
7396 stored_player[i].StepFrame += move_frames;
7398 if (stored_player[i].drop_delay > 0)
7399 stored_player[i].drop_delay--;
7404 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7406 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7408 local_player->show_envelope = 0;
7413 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7415 int min_x = x, min_y = y, max_x = x, max_y = y;
7418 for (i = 0; i < MAX_PLAYERS; i++)
7420 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7422 if (!stored_player[i].active || &stored_player[i] == player)
7425 min_x = MIN(min_x, jx);
7426 min_y = MIN(min_y, jy);
7427 max_x = MAX(max_x, jx);
7428 max_y = MAX(max_y, jy);
7431 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7434 static boolean AllPlayersInVisibleScreen()
7438 for (i = 0; i < MAX_PLAYERS; i++)
7440 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7442 if (!stored_player[i].active)
7445 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7452 void ScrollLevel(int dx, int dy)
7454 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7457 BlitBitmap(drawto_field, drawto_field,
7458 FX + TILEX * (dx == -1) - softscroll_offset,
7459 FY + TILEY * (dy == -1) - softscroll_offset,
7460 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7461 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7462 FX + TILEX * (dx == 1) - softscroll_offset,
7463 FY + TILEY * (dy == 1) - softscroll_offset);
7467 x = (dx == 1 ? BX1 : BX2);
7468 for (y = BY1; y <= BY2; y++)
7469 DrawScreenField(x, y);
7474 y = (dy == 1 ? BY1 : BY2);
7475 for (x = BX1; x <= BX2; x++)
7476 DrawScreenField(x, y);
7479 redraw_mask |= REDRAW_FIELD;
7482 static void CheckGravityMovement(struct PlayerInfo *player)
7484 if (game.gravity && !player->programmed_action)
7486 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7487 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7489 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7490 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7491 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7492 int jx = player->jx, jy = player->jy;
7493 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7494 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7495 int new_jx = jx + dx, new_jy = jy + dy;
7496 boolean field_under_player_is_free =
7497 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7498 boolean player_is_moving_to_valid_field =
7499 (IN_LEV_FIELD(new_jx, new_jy) &&
7500 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7501 Feld[new_jx][new_jy] == EL_SAND));
7502 /* !!! extend EL_SAND to anything diggable !!! */
7504 if (field_under_player_is_free &&
7505 !player_is_moving_to_valid_field &&
7506 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7507 player->programmed_action = MV_DOWN;
7513 -----------------------------------------------------------------------------
7514 dx, dy: direction (non-diagonal) to try to move the player to
7515 real_dx, real_dy: direction as read from input device (can be diagonal)
7518 boolean MovePlayerOneStep(struct PlayerInfo *player,
7519 int dx, int dy, int real_dx, int real_dy)
7522 static int change_sides[4][2] =
7524 /* enter side leave side */
7525 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7526 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7527 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7528 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7530 int move_direction = (dx == -1 ? MV_LEFT :
7531 dx == +1 ? MV_RIGHT :
7533 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7534 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7535 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7537 int jx = player->jx, jy = player->jy;
7538 int new_jx = jx + dx, new_jy = jy + dy;
7542 if (!player->active || (!dx && !dy))
7543 return MF_NO_ACTION;
7545 player->MovDir = (dx < 0 ? MV_LEFT :
7548 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7550 if (!IN_LEV_FIELD(new_jx, new_jy))
7551 return MF_NO_ACTION;
7553 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7554 return MF_NO_ACTION;
7557 element = MovingOrBlocked2Element(new_jx, new_jy);
7559 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7562 if (DONT_RUN_INTO(element))
7564 if (element == EL_ACID && dx == 0 && dy == 1)
7567 Feld[jx][jy] = EL_PLAYER_1;
7568 InitMovingField(jx, jy, MV_DOWN);
7569 Store[jx][jy] = EL_ACID;
7570 ContinueMoving(jx, jy);
7574 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7579 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7580 if (can_move != MF_MOVING)
7583 /* check if DigField() has caused relocation of the player */
7584 if (player->jx != jx || player->jy != jy)
7585 return MF_NO_ACTION;
7587 StorePlayer[jx][jy] = 0;
7588 player->last_jx = jx;
7589 player->last_jy = jy;
7590 player->jx = new_jx;
7591 player->jy = new_jy;
7592 StorePlayer[new_jx][new_jy] = player->element_nr;
7595 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7597 player->step_counter++;
7599 player->drop_delay = 0;
7601 PlayerVisit[jx][jy] = FrameCounter;
7603 ScrollPlayer(player, SCROLL_INIT);
7606 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7608 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7609 CE_OTHER_GETS_LEFT);
7610 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7611 CE_LEFT_BY_PLAYER, -1);
7614 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7616 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7617 enter_side, CE_OTHER_GETS_ENTERED);
7618 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7619 CE_ENTERED_BY_PLAYER, -1);
7626 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7628 int jx = player->jx, jy = player->jy;
7629 int old_jx = jx, old_jy = jy;
7630 int moved = MF_NO_ACTION;
7633 if (!player->active)
7638 if (player->MovPos == 0)
7640 player->is_moving = FALSE;
7641 player->is_digging = FALSE;
7642 player->is_collecting = FALSE;
7643 player->is_snapping = FALSE;
7644 player->is_pushing = FALSE;
7650 if (!player->active || (!dx && !dy))
7655 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7659 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7660 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7664 /* remove the last programmed player action */
7665 player->programmed_action = 0;
7669 /* should only happen if pre-1.2 tape recordings are played */
7670 /* this is only for backward compatibility */
7672 int original_move_delay_value = player->move_delay_value;
7675 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7679 /* scroll remaining steps with finest movement resolution */
7680 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7682 while (player->MovPos)
7684 ScrollPlayer(player, SCROLL_GO_ON);
7685 ScrollScreen(NULL, SCROLL_GO_ON);
7691 player->move_delay_value = original_move_delay_value;
7694 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7696 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7697 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7701 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7702 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7708 if (moved & MF_MOVING && !ScreenMovPos &&
7709 (player == local_player || !options.network))
7711 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7712 int offset = (setup.scroll_delay ? 3 : 0);
7714 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7716 /* actual player has left the screen -- scroll in that direction */
7717 if (jx != old_jx) /* player has moved horizontally */
7718 scroll_x += (jx - old_jx);
7719 else /* player has moved vertically */
7720 scroll_y += (jy - old_jy);
7724 if (jx != old_jx) /* player has moved horizontally */
7726 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7727 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7728 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7730 /* don't scroll over playfield boundaries */
7731 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7732 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7734 /* don't scroll more than one field at a time */
7735 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7737 /* don't scroll against the player's moving direction */
7738 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7739 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7740 scroll_x = old_scroll_x;
7742 else /* player has moved vertically */
7744 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7745 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7746 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7748 /* don't scroll over playfield boundaries */
7749 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7750 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7752 /* don't scroll more than one field at a time */
7753 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7755 /* don't scroll against the player's moving direction */
7756 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7757 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7758 scroll_y = old_scroll_y;
7762 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7764 if (!options.network && !AllPlayersInVisibleScreen())
7766 scroll_x = old_scroll_x;
7767 scroll_y = old_scroll_y;
7771 ScrollScreen(player, SCROLL_INIT);
7772 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7779 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7781 if (!(moved & MF_MOVING) && !player->is_pushing)
7786 player->StepFrame = 0;
7788 if (moved & MF_MOVING)
7790 if (old_jx != jx && old_jy == jy)
7791 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7792 else if (old_jx == jx && old_jy != jy)
7793 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7795 DrawLevelField(jx, jy); /* for "crumbled sand" */
7797 player->last_move_dir = player->MovDir;
7798 player->is_moving = TRUE;
7800 player->is_snapping = FALSE;
7804 player->is_switching = FALSE;
7807 player->is_dropping = FALSE;
7812 static int change_sides[4][2] =
7814 /* enter side leave side */
7815 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7816 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7817 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7818 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7820 int move_direction = player->MovDir;
7821 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7822 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7825 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7827 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7828 leave_side, CE_OTHER_GETS_LEFT);
7829 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7830 leave_side, CE_LEFT_BY_PLAYER, -1);
7833 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7835 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7836 enter_side, CE_OTHER_GETS_ENTERED);
7837 CheckElementSideChange(jx, jy, Feld[jx][jy],
7838 enter_side, CE_ENTERED_BY_PLAYER, -1);
7849 CheckGravityMovement(player);
7852 player->last_move_dir = MV_NO_MOVING;
7854 player->is_moving = FALSE;
7857 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7859 TestIfHeroTouchesBadThing(jx, jy);
7860 TestIfPlayerTouchesCustomElement(jx, jy);
7863 if (!player->active)
7869 void ScrollPlayer(struct PlayerInfo *player, int mode)
7871 int jx = player->jx, jy = player->jy;
7872 int last_jx = player->last_jx, last_jy = player->last_jy;
7873 int move_stepsize = TILEX / player->move_delay_value;
7875 if (!player->active || !player->MovPos)
7878 if (mode == SCROLL_INIT)
7880 player->actual_frame_counter = FrameCounter;
7881 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7883 if (Feld[last_jx][last_jy] == EL_EMPTY)
7884 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7891 else if (!FrameReached(&player->actual_frame_counter, 1))
7894 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7895 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7897 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7898 Feld[last_jx][last_jy] = EL_EMPTY;
7900 /* before DrawPlayer() to draw correct player graphic for this case */
7901 if (player->MovPos == 0)
7902 CheckGravityMovement(player);
7905 DrawPlayer(player); /* needed here only to cleanup last field */
7908 if (player->MovPos == 0) /* player reached destination field */
7911 if (player->move_delay_reset_counter > 0)
7913 player->move_delay_reset_counter--;
7915 if (player->move_delay_reset_counter == 0)
7917 /* continue with normal speed after quickly moving through gate */
7918 HALVE_PLAYER_SPEED(player);
7920 /* be able to make the next move without delay */
7921 player->move_delay = 0;
7925 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7927 /* continue with normal speed after quickly moving through gate */
7928 HALVE_PLAYER_SPEED(player);
7930 /* be able to make the next move without delay */
7931 player->move_delay = 0;
7935 player->last_jx = jx;
7936 player->last_jy = jy;
7938 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7939 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7940 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7942 DrawPlayer(player); /* needed here only to cleanup last field */
7945 if (local_player->friends_still_needed == 0 ||
7946 IS_SP_ELEMENT(Feld[jx][jy]))
7947 player->LevelSolved = player->GameOver = TRUE;
7950 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7952 TestIfHeroTouchesBadThing(jx, jy);
7953 TestIfPlayerTouchesCustomElement(jx, jy);
7955 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7958 if (!player->active)
7962 if (tape.single_step && tape.recording && !tape.pausing &&
7963 !player->programmed_action)
7964 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7968 void ScrollScreen(struct PlayerInfo *player, int mode)
7970 static unsigned long screen_frame_counter = 0;
7972 if (mode == SCROLL_INIT)
7974 /* set scrolling step size according to actual player's moving speed */
7975 ScrollStepSize = TILEX / player->move_delay_value;
7977 screen_frame_counter = FrameCounter;
7978 ScreenMovDir = player->MovDir;
7979 ScreenMovPos = player->MovPos;
7980 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7983 else if (!FrameReached(&screen_frame_counter, 1))
7988 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7989 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7990 redraw_mask |= REDRAW_FIELD;
7993 ScreenMovDir = MV_NO_MOVING;
7996 void TestIfPlayerTouchesCustomElement(int x, int y)
7998 static int xy[4][2] =
8005 static int change_sides[4][2] =
8007 /* center side border side */
8008 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8009 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8010 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8011 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8013 static int touch_dir[4] =
8020 int center_element = Feld[x][y]; /* should always be non-moving! */
8023 for (i = 0; i < 4; i++)
8025 int xx = x + xy[i][0];
8026 int yy = y + xy[i][1];
8027 int center_side = change_sides[i][0];
8028 int border_side = change_sides[i][1];
8031 if (!IN_LEV_FIELD(xx, yy))
8034 if (IS_PLAYER(x, y))
8036 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8037 border_element = Feld[xx][yy]; /* may be moving! */
8038 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8039 border_element = Feld[xx][yy];
8040 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8041 border_element = MovingOrBlocked2Element(xx, yy);
8043 continue; /* center and border element do not touch */
8045 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8046 CE_OTHER_GETS_TOUCHED);
8047 CheckElementSideChange(xx, yy, border_element, border_side,
8048 CE_TOUCHED_BY_PLAYER, -1);
8050 else if (IS_PLAYER(xx, yy))
8052 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8054 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8056 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8057 continue; /* center and border element do not touch */
8060 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8061 CE_OTHER_GETS_TOUCHED);
8062 CheckElementSideChange(x, y, center_element, center_side,
8063 CE_TOUCHED_BY_PLAYER, -1);
8070 void TestIfElementTouchesCustomElement(int x, int y)
8072 static int xy[4][2] =
8079 static int change_sides[4][2] =
8081 /* center side border side */
8082 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8083 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8084 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8085 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8087 static int touch_dir[4] =
8094 boolean change_center_element = FALSE;
8095 int center_element_change_page = 0;
8096 int center_element = Feld[x][y]; /* should always be non-moving! */
8099 for (i = 0; i < 4; i++)
8101 int xx = x + xy[i][0];
8102 int yy = y + xy[i][1];
8103 int center_side = change_sides[i][0];
8104 int border_side = change_sides[i][1];
8107 if (!IN_LEV_FIELD(xx, yy))
8110 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8111 border_element = Feld[xx][yy]; /* may be moving! */
8112 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8113 border_element = Feld[xx][yy];
8114 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8115 border_element = MovingOrBlocked2Element(xx, yy);
8117 continue; /* center and border element do not touch */
8119 /* check for change of center element (but change it only once) */
8120 if (IS_CUSTOM_ELEMENT(center_element) &&
8121 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8122 !change_center_element)
8124 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8126 struct ElementChangeInfo *change =
8127 &element_info[center_element].change_page[j];
8129 if (change->can_change &&
8130 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8131 change->sides & border_side &&
8132 change->trigger_element == border_element)
8134 change_center_element = TRUE;
8135 center_element_change_page = j;
8142 /* check for change of border element */
8143 if (IS_CUSTOM_ELEMENT(border_element) &&
8144 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8146 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8148 struct ElementChangeInfo *change =
8149 &element_info[border_element].change_page[j];
8151 if (change->can_change &&
8152 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8153 change->sides & center_side &&
8154 change->trigger_element == center_element)
8156 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8157 CE_OTHER_IS_TOUCHING, j);
8164 if (change_center_element)
8165 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8166 CE_OTHER_IS_TOUCHING, center_element_change_page);
8169 void TestIfElementHitsCustomElement(int x, int y, int direction)
8171 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8172 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8173 int hitx = x + dx, hity = y + dy;
8174 int hitting_element = Feld[x][y];
8176 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8177 !IS_FREE(hitx, hity) &&
8178 (!IS_MOVING(hitx, hity) ||
8179 MovDir[hitx][hity] != direction ||
8180 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8183 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8187 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8191 CheckElementSideChange(x, y, hitting_element,
8192 direction, CE_HITTING_SOMETHING, -1);
8194 if (IN_LEV_FIELD(hitx, hity))
8196 static int opposite_directions[] =
8203 int move_dir_bit = MV_DIR_BIT(direction);
8204 int opposite_direction = opposite_directions[move_dir_bit];
8205 int hitting_side = direction;
8206 int touched_side = opposite_direction;
8207 int touched_element = MovingOrBlocked2Element(hitx, hity);
8209 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8210 MovDir[hitx][hity] != direction ||
8211 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8220 CheckElementSideChange(hitx, hity, touched_element,
8221 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8223 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8224 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8226 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8228 struct ElementChangeInfo *change =
8229 &element_info[hitting_element].change_page[i];
8231 if (change->can_change &&
8232 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8233 change->sides & touched_side &&
8234 change->trigger_element == touched_element)
8236 CheckElementSideChange(x, y, hitting_element,
8237 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8243 if (IS_CUSTOM_ELEMENT(touched_element) &&
8244 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8246 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8248 struct ElementChangeInfo *change =
8249 &element_info[touched_element].change_page[i];
8251 if (change->can_change &&
8252 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8253 change->sides & hitting_side &&
8254 change->trigger_element == hitting_element)
8256 CheckElementSideChange(hitx, hity, touched_element,
8257 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8266 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8268 int i, kill_x = -1, kill_y = -1;
8269 static int test_xy[4][2] =
8276 static int test_dir[4] =
8284 for (i = 0; i < 4; i++)
8286 int test_x, test_y, test_move_dir, test_element;
8288 test_x = good_x + test_xy[i][0];
8289 test_y = good_y + test_xy[i][1];
8290 if (!IN_LEV_FIELD(test_x, test_y))
8294 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8297 test_element = Feld[test_x][test_y];
8299 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8302 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8303 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8305 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8306 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8314 if (kill_x != -1 || kill_y != -1)
8316 if (IS_PLAYER(good_x, good_y))
8318 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8320 if (player->shield_deadly_time_left > 0)
8321 Bang(kill_x, kill_y);
8322 else if (!PLAYER_PROTECTED(good_x, good_y))
8326 Bang(good_x, good_y);
8330 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8332 int i, kill_x = -1, kill_y = -1;
8333 int bad_element = Feld[bad_x][bad_y];
8334 static int test_xy[4][2] =
8341 static int touch_dir[4] =
8348 static int test_dir[4] =
8356 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8359 for (i = 0; i < 4; i++)
8361 int test_x, test_y, test_move_dir, test_element;
8363 test_x = bad_x + test_xy[i][0];
8364 test_y = bad_y + test_xy[i][1];
8365 if (!IN_LEV_FIELD(test_x, test_y))
8369 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8371 test_element = Feld[test_x][test_y];
8373 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8374 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8376 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8377 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8379 /* good thing is player or penguin that does not move away */
8380 if (IS_PLAYER(test_x, test_y))
8382 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8384 if (bad_element == EL_ROBOT && player->is_moving)
8385 continue; /* robot does not kill player if he is moving */
8387 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8389 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8390 continue; /* center and border element do not touch */
8397 else if (test_element == EL_PENGUIN)
8406 if (kill_x != -1 || kill_y != -1)
8408 if (IS_PLAYER(kill_x, kill_y))
8410 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8412 if (player->shield_deadly_time_left > 0)
8414 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8418 Bang(kill_x, kill_y);
8422 void TestIfHeroTouchesBadThing(int x, int y)
8424 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8427 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8429 TestIfGoodThingHitsBadThing(x, y, move_dir);
8432 void TestIfBadThingTouchesHero(int x, int y)
8434 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8437 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8439 TestIfBadThingHitsGoodThing(x, y, move_dir);
8442 void TestIfFriendTouchesBadThing(int x, int y)
8444 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8447 void TestIfBadThingTouchesFriend(int x, int y)
8449 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8452 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8454 int i, kill_x = bad_x, kill_y = bad_y;
8455 static int xy[4][2] =
8463 for (i = 0; i < 4; i++)
8467 x = bad_x + xy[i][0];
8468 y = bad_y + xy[i][1];
8469 if (!IN_LEV_FIELD(x, y))
8472 element = Feld[x][y];
8473 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8474 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8482 if (kill_x != bad_x || kill_y != bad_y)
8486 void KillHero(struct PlayerInfo *player)
8488 int jx = player->jx, jy = player->jy;
8490 if (!player->active)
8493 /* remove accessible field at the player's position */
8494 Feld[jx][jy] = EL_EMPTY;
8496 /* deactivate shield (else Bang()/Explode() would not work right) */
8497 player->shield_normal_time_left = 0;
8498 player->shield_deadly_time_left = 0;
8504 static void KillHeroUnlessProtected(int x, int y)
8506 if (!PLAYER_PROTECTED(x, y))
8507 KillHero(PLAYERINFO(x, y));
8510 void BuryHero(struct PlayerInfo *player)
8512 int jx = player->jx, jy = player->jy;
8514 if (!player->active)
8518 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8520 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8522 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8524 player->GameOver = TRUE;
8528 void RemoveHero(struct PlayerInfo *player)
8530 int jx = player->jx, jy = player->jy;
8531 int i, found = FALSE;
8533 player->present = FALSE;
8534 player->active = FALSE;
8536 if (!ExplodeField[jx][jy])
8537 StorePlayer[jx][jy] = 0;
8539 for (i = 0; i < MAX_PLAYERS; i++)
8540 if (stored_player[i].active)
8544 AllPlayersGone = TRUE;
8551 =============================================================================
8552 checkDiagonalPushing()
8553 -----------------------------------------------------------------------------
8554 check if diagonal input device direction results in pushing of object
8555 (by checking if the alternative direction is walkable, diggable, ...)
8556 =============================================================================
8559 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8560 int x, int y, int real_dx, int real_dy)
8562 int jx, jy, dx, dy, xx, yy;
8564 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8567 /* diagonal direction: check alternative direction */
8572 xx = jx + (dx == 0 ? real_dx : 0);
8573 yy = jy + (dy == 0 ? real_dy : 0);
8575 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8579 =============================================================================
8581 -----------------------------------------------------------------------------
8582 x, y: field next to player (non-diagonal) to try to dig to
8583 real_dx, real_dy: direction as read from input device (can be diagonal)
8584 =============================================================================
8587 int DigField(struct PlayerInfo *player,
8588 int x, int y, int real_dx, int real_dy, int mode)
8590 static int change_sides[4] =
8592 CH_SIDE_RIGHT, /* moving left */
8593 CH_SIDE_LEFT, /* moving right */
8594 CH_SIDE_BOTTOM, /* moving up */
8595 CH_SIDE_TOP, /* moving down */
8597 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8598 int jx = player->jx, jy = player->jy;
8599 int dx = x - jx, dy = y - jy;
8600 int nextx = x + dx, nexty = y + dy;
8601 int move_direction = (dx == -1 ? MV_LEFT :
8602 dx == +1 ? MV_RIGHT :
8604 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8605 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8608 if (player->MovPos == 0)
8610 player->is_digging = FALSE;
8611 player->is_collecting = FALSE;
8614 if (player->MovPos == 0) /* last pushing move finished */
8615 player->is_pushing = FALSE;
8617 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8619 player->is_switching = FALSE;
8620 player->push_delay = 0;
8622 return MF_NO_ACTION;
8625 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8626 return MF_NO_ACTION;
8629 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8631 if (IS_TUBE(Feld[jx][jy]) ||
8632 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8636 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8637 int tube_leave_directions[][2] =
8639 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8640 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8641 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8642 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8643 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8644 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8645 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8646 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8647 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8648 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8649 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8650 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8653 while (tube_leave_directions[i][0] != tube_element)
8656 if (tube_leave_directions[i][0] == -1) /* should not happen */
8660 if (!(tube_leave_directions[i][1] & move_direction))
8661 return MF_NO_ACTION; /* tube has no opening in this direction */
8664 element = Feld[x][y];
8666 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8667 game.engine_version >= VERSION_IDENT(2,2,0,0))
8668 return MF_NO_ACTION;
8672 case EL_SP_PORT_LEFT:
8673 case EL_SP_PORT_RIGHT:
8675 case EL_SP_PORT_DOWN:
8676 case EL_SP_PORT_HORIZONTAL:
8677 case EL_SP_PORT_VERTICAL:
8678 case EL_SP_PORT_ANY:
8679 case EL_SP_GRAVITY_PORT_LEFT:
8680 case EL_SP_GRAVITY_PORT_RIGHT:
8681 case EL_SP_GRAVITY_PORT_UP:
8682 case EL_SP_GRAVITY_PORT_DOWN:
8684 element != EL_SP_PORT_LEFT &&
8685 element != EL_SP_GRAVITY_PORT_LEFT &&
8686 element != EL_SP_PORT_HORIZONTAL &&
8687 element != EL_SP_PORT_ANY) ||
8689 element != EL_SP_PORT_RIGHT &&
8690 element != EL_SP_GRAVITY_PORT_RIGHT &&
8691 element != EL_SP_PORT_HORIZONTAL &&
8692 element != EL_SP_PORT_ANY) ||
8694 element != EL_SP_PORT_UP &&
8695 element != EL_SP_GRAVITY_PORT_UP &&
8696 element != EL_SP_PORT_VERTICAL &&
8697 element != EL_SP_PORT_ANY) ||
8699 element != EL_SP_PORT_DOWN &&
8700 element != EL_SP_GRAVITY_PORT_DOWN &&
8701 element != EL_SP_PORT_VERTICAL &&
8702 element != EL_SP_PORT_ANY) ||
8703 !IN_LEV_FIELD(nextx, nexty) ||
8704 !IS_FREE(nextx, nexty))
8705 return MF_NO_ACTION;
8707 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8708 element == EL_SP_GRAVITY_PORT_RIGHT ||
8709 element == EL_SP_GRAVITY_PORT_UP ||
8710 element == EL_SP_GRAVITY_PORT_DOWN)
8711 game.gravity = !game.gravity;
8713 /* automatically move to the next field with double speed */
8714 player->programmed_action = move_direction;
8716 if (player->move_delay_reset_counter == 0)
8718 player->move_delay_reset_counter = 2; /* two double speed steps */
8720 DOUBLE_PLAYER_SPEED(player);
8723 player->move_delay_reset_counter = 2;
8725 DOUBLE_PLAYER_SPEED(player);
8728 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8732 case EL_TUBE_VERTICAL:
8733 case EL_TUBE_HORIZONTAL:
8734 case EL_TUBE_VERTICAL_LEFT:
8735 case EL_TUBE_VERTICAL_RIGHT:
8736 case EL_TUBE_HORIZONTAL_UP:
8737 case EL_TUBE_HORIZONTAL_DOWN:
8738 case EL_TUBE_LEFT_UP:
8739 case EL_TUBE_LEFT_DOWN:
8740 case EL_TUBE_RIGHT_UP:
8741 case EL_TUBE_RIGHT_DOWN:
8744 int tube_enter_directions[][2] =
8746 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8747 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8748 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8749 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8750 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8751 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8752 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8753 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8754 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8755 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8756 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8757 { -1, MV_NO_MOVING }
8760 while (tube_enter_directions[i][0] != element)
8763 if (tube_enter_directions[i][0] == -1) /* should not happen */
8767 if (!(tube_enter_directions[i][1] & move_direction))
8768 return MF_NO_ACTION; /* tube has no opening in this direction */
8770 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8776 if (IS_WALKABLE(element))
8778 int sound_action = ACTION_WALKING;
8780 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8782 if (!player->key[element - EL_GATE_1])
8783 return MF_NO_ACTION;
8785 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8787 if (!player->key[element - EL_GATE_1_GRAY])
8788 return MF_NO_ACTION;
8790 else if (element == EL_EXIT_OPEN ||
8791 element == EL_SP_EXIT_OPEN ||
8792 element == EL_SP_EXIT_OPENING)
8794 sound_action = ACTION_PASSING; /* player is passing exit */
8796 else if (element == EL_EMPTY)
8798 sound_action = ACTION_MOVING; /* nothing to walk on */
8801 /* play sound from background or player, whatever is available */
8802 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8803 PlayLevelSoundElementAction(x, y, element, sound_action);
8805 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8809 else if (IS_PASSABLE(element))
8811 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8812 return MF_NO_ACTION;
8815 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8816 return MF_NO_ACTION;
8819 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8821 if (!player->key[element - EL_EM_GATE_1])
8822 return MF_NO_ACTION;
8824 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8826 if (!player->key[element - EL_EM_GATE_1_GRAY])
8827 return MF_NO_ACTION;
8830 /* automatically move to the next field with double speed */
8831 player->programmed_action = move_direction;
8833 if (player->move_delay_reset_counter == 0)
8835 player->move_delay_reset_counter = 2; /* two double speed steps */
8837 DOUBLE_PLAYER_SPEED(player);
8840 player->move_delay_reset_counter = 2;
8842 DOUBLE_PLAYER_SPEED(player);
8845 PlayLevelSoundAction(x, y, ACTION_PASSING);
8849 else if (IS_DIGGABLE(element))
8853 if (mode != DF_SNAP)
8856 GfxElement[x][y] = GFX_ELEMENT(element);
8859 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8861 player->is_digging = TRUE;
8864 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8866 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8869 if (mode == DF_SNAP)
8870 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8875 else if (IS_COLLECTIBLE(element))
8879 if (mode != DF_SNAP)
8881 GfxElement[x][y] = element;
8882 player->is_collecting = TRUE;
8885 if (element == EL_SPEED_PILL)
8886 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8887 else if (element == EL_EXTRA_TIME && level.time > 0)
8890 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8892 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8894 player->shield_normal_time_left += 10;
8895 if (element == EL_SHIELD_DEADLY)
8896 player->shield_deadly_time_left += 10;
8898 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8900 if (player->inventory_size < MAX_INVENTORY_SIZE)
8901 player->inventory_element[player->inventory_size++] = element;
8903 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8904 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8906 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8908 player->dynabomb_count++;
8909 player->dynabombs_left++;
8911 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8913 player->dynabomb_size++;
8915 else if (element == EL_DYNABOMB_INCREASE_POWER)
8917 player->dynabomb_xl = TRUE;
8919 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8920 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8922 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8923 element - EL_KEY_1 : element - EL_EM_KEY_1);
8925 player->key[key_nr] = TRUE;
8927 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8928 el2edimg(EL_KEY_1 + key_nr));
8929 redraw_mask |= REDRAW_DOOR_1;
8931 else if (IS_ENVELOPE(element))
8934 player->show_envelope = element;
8936 ShowEnvelope(element - EL_ENVELOPE_1);
8939 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8943 for (i = 0; i < element_info[element].collect_count; i++)
8944 if (player->inventory_size < MAX_INVENTORY_SIZE)
8945 player->inventory_element[player->inventory_size++] = element;
8947 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8948 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8950 else if (element_info[element].collect_count > 0)
8952 local_player->gems_still_needed -=
8953 element_info[element].collect_count;
8954 if (local_player->gems_still_needed < 0)
8955 local_player->gems_still_needed = 0;
8957 DrawText(DX_EMERALDS, DY_EMERALDS,
8958 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8961 RaiseScoreElement(element);
8962 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8964 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8967 if (mode == DF_SNAP)
8968 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8973 else if (IS_PUSHABLE(element))
8975 if (mode == DF_SNAP && element != EL_BD_ROCK)
8976 return MF_NO_ACTION;
8978 if (CAN_FALL(element) && dy)
8979 return MF_NO_ACTION;
8981 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8982 !(element == EL_SPRING && use_spring_bug))
8983 return MF_NO_ACTION;
8986 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8987 ((move_direction & MV_VERTICAL &&
8988 ((element_info[element].move_pattern & MV_LEFT &&
8989 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8990 (element_info[element].move_pattern & MV_RIGHT &&
8991 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8992 (move_direction & MV_HORIZONTAL &&
8993 ((element_info[element].move_pattern & MV_UP &&
8994 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8995 (element_info[element].move_pattern & MV_DOWN &&
8996 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8997 return MF_NO_ACTION;
9001 /* do not push elements already moving away faster than player */
9002 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9003 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9004 return MF_NO_ACTION;
9006 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9007 return MF_NO_ACTION;
9011 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9013 if (player->push_delay_value == -1)
9014 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9016 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9018 if (!player->is_pushing)
9019 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9023 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9024 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9025 !player_is_pushing))
9026 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9029 if (!player->is_pushing &&
9030 game.engine_version >= VERSION_IDENT(2,2,0,7))
9031 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9035 printf("::: push delay: %ld [%d, %d] [%d]\n",
9036 player->push_delay_value, FrameCounter, game.engine_version,
9037 player->is_pushing);
9040 player->is_pushing = TRUE;
9042 if (!(IN_LEV_FIELD(nextx, nexty) &&
9043 (IS_FREE(nextx, nexty) ||
9044 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9045 IS_SB_ELEMENT(element)))))
9046 return MF_NO_ACTION;
9048 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9049 return MF_NO_ACTION;
9051 if (player->push_delay == 0) /* new pushing; restart delay */
9052 player->push_delay = FrameCounter;
9054 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9055 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9056 element != EL_SPRING && element != EL_BALLOON)
9058 /* make sure that there is no move delay before next try to push */
9059 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9060 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9062 return MF_NO_ACTION;
9066 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9069 if (IS_SB_ELEMENT(element))
9071 if (element == EL_SOKOBAN_FIELD_FULL)
9073 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9074 local_player->sokobanfields_still_needed++;
9077 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9079 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9080 local_player->sokobanfields_still_needed--;
9083 Feld[x][y] = EL_SOKOBAN_OBJECT;
9085 if (Back[x][y] == Back[nextx][nexty])
9086 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9087 else if (Back[x][y] != 0)
9088 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9091 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9094 if (local_player->sokobanfields_still_needed == 0 &&
9095 game.emulation == EMU_SOKOBAN)
9097 player->LevelSolved = player->GameOver = TRUE;
9098 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9102 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9104 InitMovingField(x, y, move_direction);
9105 GfxAction[x][y] = ACTION_PUSHING;
9107 if (mode == DF_SNAP)
9108 ContinueMoving(x, y);
9110 MovPos[x][y] = (dx != 0 ? dx : dy);
9112 Pushed[x][y] = TRUE;
9113 Pushed[nextx][nexty] = TRUE;
9115 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9116 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9118 player->push_delay_value = -1; /* get new value later */
9120 CheckTriggeredElementSideChange(x, y, element, dig_side,
9121 CE_OTHER_GETS_PUSHED);
9122 CheckElementSideChange(x, y, element, dig_side,
9123 CE_PUSHED_BY_PLAYER, -1);
9127 else if (IS_SWITCHABLE(element))
9129 if (PLAYER_SWITCHING(player, x, y))
9132 player->is_switching = TRUE;
9133 player->switch_x = x;
9134 player->switch_y = y;
9136 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9138 if (element == EL_ROBOT_WHEEL)
9140 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9144 DrawLevelField(x, y);
9146 else if (element == EL_SP_TERMINAL)
9150 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9152 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9154 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9155 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9158 else if (IS_BELT_SWITCH(element))
9160 ToggleBeltSwitch(x, y);
9162 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9163 element == EL_SWITCHGATE_SWITCH_DOWN)
9165 ToggleSwitchgateSwitch(x, y);
9167 else if (element == EL_LIGHT_SWITCH ||
9168 element == EL_LIGHT_SWITCH_ACTIVE)
9170 ToggleLightSwitch(x, y);
9173 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9174 SND_LIGHT_SWITCH_ACTIVATING :
9175 SND_LIGHT_SWITCH_DEACTIVATING);
9178 else if (element == EL_TIMEGATE_SWITCH)
9180 ActivateTimegateSwitch(x, y);
9182 else if (element == EL_BALLOON_SWITCH_LEFT ||
9183 element == EL_BALLOON_SWITCH_RIGHT ||
9184 element == EL_BALLOON_SWITCH_UP ||
9185 element == EL_BALLOON_SWITCH_DOWN ||
9186 element == EL_BALLOON_SWITCH_ANY)
9188 if (element == EL_BALLOON_SWITCH_ANY)
9189 game.balloon_dir = move_direction;
9191 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9192 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9193 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9194 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9197 else if (element == EL_LAMP)
9199 Feld[x][y] = EL_LAMP_ACTIVE;
9200 local_player->lights_still_needed--;
9202 DrawLevelField(x, y);
9204 else if (element == EL_TIME_ORB_FULL)
9206 Feld[x][y] = EL_TIME_ORB_EMPTY;
9208 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9210 DrawLevelField(x, y);
9213 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9221 if (!PLAYER_SWITCHING(player, x, y))
9223 player->is_switching = TRUE;
9224 player->switch_x = x;
9225 player->switch_y = y;
9227 CheckTriggeredElementSideChange(x, y, element, dig_side,
9228 CE_OTHER_IS_SWITCHING);
9229 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9232 CheckTriggeredElementSideChange(x, y, element, dig_side,
9233 CE_OTHER_GETS_PRESSED);
9234 CheckElementSideChange(x, y, element, dig_side,
9235 CE_PRESSED_BY_PLAYER, -1);
9238 return MF_NO_ACTION;
9241 player->push_delay = 0;
9243 if (Feld[x][y] != element) /* really digged/collected something */
9244 player->is_collecting = !player->is_digging;
9249 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9251 int jx = player->jx, jy = player->jy;
9252 int x = jx + dx, y = jy + dy;
9253 int snap_direction = (dx == -1 ? MV_LEFT :
9254 dx == +1 ? MV_RIGHT :
9256 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9258 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9261 if (!player->active || !IN_LEV_FIELD(x, y))
9269 if (player->MovPos == 0)
9270 player->is_pushing = FALSE;
9272 player->is_snapping = FALSE;
9274 if (player->MovPos == 0)
9276 player->is_moving = FALSE;
9277 player->is_digging = FALSE;
9278 player->is_collecting = FALSE;
9284 if (player->is_snapping)
9287 player->MovDir = snap_direction;
9290 if (player->MovPos == 0)
9293 player->is_moving = FALSE;
9294 player->is_digging = FALSE;
9295 player->is_collecting = FALSE;
9298 player->is_dropping = FALSE;
9300 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9303 player->is_snapping = TRUE;
9306 if (player->MovPos == 0)
9309 player->is_moving = FALSE;
9310 player->is_digging = FALSE;
9311 player->is_collecting = FALSE;
9314 DrawLevelField(x, y);
9320 boolean DropElement(struct PlayerInfo *player)
9322 int jx = player->jx, jy = player->jy;
9323 int old_element = Feld[jx][jy];
9326 /* check if player is active, not moving and ready to drop */
9327 if (!player->active || player->MovPos || player->drop_delay > 0)
9330 /* check if player has anything that can be dropped */
9331 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9334 /* check if anything can be dropped at the current position */
9335 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9338 /* collected custom elements can only be dropped on empty fields */
9339 if (player->inventory_size > 0 &&
9340 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9341 && old_element != EL_EMPTY)
9344 if (old_element != EL_EMPTY)
9345 Back[jx][jy] = old_element; /* store old element on this field */
9347 ResetGfxAnimation(jx, jy);
9348 ResetRandomAnimationValue(jx, jy);
9350 if (player->inventory_size > 0)
9352 player->inventory_size--;
9353 new_element = player->inventory_element[player->inventory_size];
9355 if (new_element == EL_DYNAMITE)
9356 new_element = EL_DYNAMITE_ACTIVE;
9357 else if (new_element == EL_SP_DISK_RED)
9358 new_element = EL_SP_DISK_RED_ACTIVE;
9360 Feld[jx][jy] = new_element;
9362 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9363 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9365 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9366 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9368 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9370 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9371 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9373 TestIfElementTouchesCustomElement(jx, jy);
9375 else /* player is dropping a dyna bomb */
9377 player->dynabombs_left--;
9378 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9380 Feld[jx][jy] = new_element;
9382 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9383 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9385 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9392 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9394 InitField(jx, jy, FALSE);
9395 if (CAN_MOVE(Feld[jx][jy]))
9399 new_element = Feld[jx][jy];
9401 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9402 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9404 int move_stepsize = element_info[new_element].move_stepsize;
9405 int direction, dx, dy, nextx, nexty;
9407 if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
9408 MovDir[jx][jy] = player->MovDir;
9410 direction = MovDir[jx][jy];
9411 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9412 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9416 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9419 WasJustMoving[jx][jy] = 3;
9421 InitMovingField(jx, jy, direction);
9422 ContinueMoving(jx, jy);
9427 Changed[jx][jy] = 0; /* allow another change */
9430 TestIfElementHitsCustomElement(jx, jy, direction);
9432 CheckElementSideChange(jx, jy, new_element,
9433 direction, CE_HITTING_SOMETHING, -1);
9437 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9441 player->drop_delay = 8 + 8 + 8;
9446 player->is_dropping = TRUE;
9452 /* ------------------------------------------------------------------------- */
9453 /* game sound playing functions */
9454 /* ------------------------------------------------------------------------- */
9456 static int *loop_sound_frame = NULL;
9457 static int *loop_sound_volume = NULL;
9459 void InitPlayLevelSound()
9461 int num_sounds = getSoundListSize();
9463 checked_free(loop_sound_frame);
9464 checked_free(loop_sound_volume);
9466 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9467 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9470 static void PlayLevelSound(int x, int y, int nr)
9472 int sx = SCREENX(x), sy = SCREENY(y);
9473 int volume, stereo_position;
9474 int max_distance = 8;
9475 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9477 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9478 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9481 if (!IN_LEV_FIELD(x, y) ||
9482 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9483 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9486 volume = SOUND_MAX_VOLUME;
9488 if (!IN_SCR_FIELD(sx, sy))
9490 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9491 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9493 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9496 stereo_position = (SOUND_MAX_LEFT +
9497 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9498 (SCR_FIELDX + 2 * max_distance));
9500 if (IS_LOOP_SOUND(nr))
9502 /* This assures that quieter loop sounds do not overwrite louder ones,
9503 while restarting sound volume comparison with each new game frame. */
9505 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9508 loop_sound_volume[nr] = volume;
9509 loop_sound_frame[nr] = FrameCounter;
9512 PlaySoundExt(nr, volume, stereo_position, type);
9515 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9517 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9518 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9519 y < LEVELY(BY1) ? LEVELY(BY1) :
9520 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9524 static void PlayLevelSoundAction(int x, int y, int action)
9526 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9529 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9531 int sound_effect = element_info[element].sound[action];
9533 if (sound_effect != SND_UNDEFINED)
9534 PlayLevelSound(x, y, sound_effect);
9537 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9540 int sound_effect = element_info[element].sound[action];
9542 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9543 PlayLevelSound(x, y, sound_effect);
9546 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9548 int sound_effect = element_info[Feld[x][y]].sound[action];
9550 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9551 PlayLevelSound(x, y, sound_effect);
9554 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9556 int sound_effect = element_info[Feld[x][y]].sound[action];
9558 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9559 StopSound(sound_effect);
9562 static void PlayLevelMusic()
9564 if (levelset.music[level_nr] != MUS_UNDEFINED)
9565 PlayMusic(levelset.music[level_nr]); /* from config file */
9567 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9570 void RaiseScore(int value)
9572 local_player->score += value;
9573 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9576 void RaiseScoreElement(int element)
9582 case EL_EMERALD_YELLOW:
9583 case EL_EMERALD_RED:
9584 case EL_EMERALD_PURPLE:
9585 case EL_SP_INFOTRON:
9586 RaiseScore(level.score[SC_EMERALD]);
9589 RaiseScore(level.score[SC_DIAMOND]);
9592 RaiseScore(level.score[SC_CRYSTAL]);
9595 RaiseScore(level.score[SC_PEARL]);
9598 case EL_BD_BUTTERFLY:
9599 case EL_SP_ELECTRON:
9600 RaiseScore(level.score[SC_BUG]);
9604 case EL_SP_SNIKSNAK:
9605 RaiseScore(level.score[SC_SPACESHIP]);
9608 case EL_DARK_YAMYAM:
9609 RaiseScore(level.score[SC_YAMYAM]);
9612 RaiseScore(level.score[SC_ROBOT]);
9615 RaiseScore(level.score[SC_PACMAN]);
9618 RaiseScore(level.score[SC_NUT]);
9621 case EL_SP_DISK_RED:
9622 case EL_DYNABOMB_INCREASE_NUMBER:
9623 case EL_DYNABOMB_INCREASE_SIZE:
9624 case EL_DYNABOMB_INCREASE_POWER:
9625 RaiseScore(level.score[SC_DYNAMITE]);
9627 case EL_SHIELD_NORMAL:
9628 case EL_SHIELD_DEADLY:
9629 RaiseScore(level.score[SC_SHIELD]);
9632 RaiseScore(level.score[SC_TIME_BONUS]);
9638 RaiseScore(level.score[SC_KEY]);
9641 RaiseScore(element_info[element].collect_score);
9646 void RequestQuitGame(boolean ask_if_really_quit)
9648 if (AllPlayersGone ||
9649 !ask_if_really_quit ||
9650 level_editor_test_game ||
9651 Request("Do you really want to quit the game ?",
9652 REQ_ASK | REQ_STAY_CLOSED))
9654 #if defined(PLATFORM_UNIX)
9655 if (options.network)
9656 SendToServer_StopPlaying();
9660 game_status = GAME_MODE_MAIN;
9666 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9671 /* ---------- new game button stuff ---------------------------------------- */
9673 /* graphic position values for game buttons */
9674 #define GAME_BUTTON_XSIZE 30
9675 #define GAME_BUTTON_YSIZE 30
9676 #define GAME_BUTTON_XPOS 5
9677 #define GAME_BUTTON_YPOS 215
9678 #define SOUND_BUTTON_XPOS 5
9679 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9681 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9682 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9683 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9684 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9685 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9686 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9693 } gamebutton_info[NUM_GAME_BUTTONS] =
9696 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9701 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9706 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9711 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9712 SOUND_CTRL_ID_MUSIC,
9713 "background music on/off"
9716 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9717 SOUND_CTRL_ID_LOOPS,
9718 "sound loops on/off"
9721 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9722 SOUND_CTRL_ID_SIMPLE,
9723 "normal sounds on/off"
9727 void CreateGameButtons()
9731 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9733 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9734 struct GadgetInfo *gi;
9737 unsigned long event_mask;
9738 int gd_xoffset, gd_yoffset;
9739 int gd_x1, gd_x2, gd_y1, gd_y2;
9742 gd_xoffset = gamebutton_info[i].x;
9743 gd_yoffset = gamebutton_info[i].y;
9744 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9745 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9747 if (id == GAME_CTRL_ID_STOP ||
9748 id == GAME_CTRL_ID_PAUSE ||
9749 id == GAME_CTRL_ID_PLAY)
9751 button_type = GD_TYPE_NORMAL_BUTTON;
9753 event_mask = GD_EVENT_RELEASED;
9754 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9755 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9759 button_type = GD_TYPE_CHECK_BUTTON;
9761 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9762 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9763 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9764 event_mask = GD_EVENT_PRESSED;
9765 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9766 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9769 gi = CreateGadget(GDI_CUSTOM_ID, id,
9770 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9771 GDI_X, DX + gd_xoffset,
9772 GDI_Y, DY + gd_yoffset,
9773 GDI_WIDTH, GAME_BUTTON_XSIZE,
9774 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9775 GDI_TYPE, button_type,
9776 GDI_STATE, GD_BUTTON_UNPRESSED,
9777 GDI_CHECKED, checked,
9778 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9779 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9780 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9781 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9782 GDI_EVENT_MASK, event_mask,
9783 GDI_CALLBACK_ACTION, HandleGameButtons,
9787 Error(ERR_EXIT, "cannot create gadget");
9789 game_gadget[id] = gi;
9793 void FreeGameButtons()
9797 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9798 FreeGadget(game_gadget[i]);
9801 static void MapGameButtons()
9805 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9806 MapGadget(game_gadget[i]);
9809 void UnmapGameButtons()
9813 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9814 UnmapGadget(game_gadget[i]);
9817 static void HandleGameButtons(struct GadgetInfo *gi)
9819 int id = gi->custom_id;
9821 if (game_status != GAME_MODE_PLAYING)
9826 case GAME_CTRL_ID_STOP:
9827 RequestQuitGame(TRUE);
9830 case GAME_CTRL_ID_PAUSE:
9831 if (options.network)
9833 #if defined(PLATFORM_UNIX)
9835 SendToServer_ContinuePlaying();
9837 SendToServer_PausePlaying();
9841 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9844 case GAME_CTRL_ID_PLAY:
9847 #if defined(PLATFORM_UNIX)
9848 if (options.network)
9849 SendToServer_ContinuePlaying();
9853 tape.pausing = FALSE;
9854 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9859 case SOUND_CTRL_ID_MUSIC:
9860 if (setup.sound_music)
9862 setup.sound_music = FALSE;
9865 else if (audio.music_available)
9867 setup.sound = setup.sound_music = TRUE;
9869 SetAudioMode(setup.sound);
9875 case SOUND_CTRL_ID_LOOPS:
9876 if (setup.sound_loops)
9877 setup.sound_loops = FALSE;
9878 else if (audio.loops_available)
9880 setup.sound = setup.sound_loops = TRUE;
9881 SetAudioMode(setup.sound);
9885 case SOUND_CTRL_ID_SIMPLE:
9886 if (setup.sound_simple)
9887 setup.sound_simple = FALSE;
9888 else if (audio.sound_available)
9890 setup.sound = setup.sound_simple = TRUE;
9891 SetAudioMode(setup.sound);