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 move dig/leave ---------------------------------- */
1090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1092 element_info[i].can_leave_element = FALSE;
1093 element_info[i].can_leave_element_last = FALSE;
1096 /* ---------- initialize gem count --------------------------------------- */
1098 /* initialize gem count values for each element */
1099 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1100 if (!IS_CUSTOM_ELEMENT(i))
1101 element_info[i].collect_count = 0;
1103 /* add gem count values for all elements from pre-defined list */
1104 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1105 element_info[collect_count_list[i].element].collect_count =
1106 collect_count_list[i].count;
1111 =============================================================================
1113 -----------------------------------------------------------------------------
1114 initialize and start new game
1115 =============================================================================
1120 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1121 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1122 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1129 #if USE_NEW_AMOEBA_CODE
1130 printf("Using new amoeba code.\n");
1132 printf("Using old amoeba code.\n");
1137 /* don't play tapes over network */
1138 network_playing = (options.network && !tape.playing);
1140 for (i = 0; i < MAX_PLAYERS; i++)
1142 struct PlayerInfo *player = &stored_player[i];
1144 player->index_nr = i;
1145 player->element_nr = EL_PLAYER_1 + i;
1147 player->present = FALSE;
1148 player->active = FALSE;
1151 player->effective_action = 0;
1152 player->programmed_action = 0;
1155 player->gems_still_needed = level.gems_needed;
1156 player->sokobanfields_still_needed = 0;
1157 player->lights_still_needed = 0;
1158 player->friends_still_needed = 0;
1160 for (j = 0; j < 4; j++)
1161 player->key[j] = FALSE;
1163 player->dynabomb_count = 0;
1164 player->dynabomb_size = 1;
1165 player->dynabombs_left = 0;
1166 player->dynabomb_xl = FALSE;
1168 player->MovDir = MV_NO_MOVING;
1171 player->GfxDir = MV_NO_MOVING;
1172 player->GfxAction = ACTION_DEFAULT;
1174 player->StepFrame = 0;
1176 player->use_murphy_graphic = FALSE;
1178 player->actual_frame_counter = 0;
1180 player->step_counter = 0;
1182 player->last_move_dir = MV_NO_MOVING;
1184 player->is_waiting = FALSE;
1185 player->is_moving = FALSE;
1186 player->is_digging = FALSE;
1187 player->is_snapping = FALSE;
1188 player->is_collecting = FALSE;
1189 player->is_pushing = FALSE;
1190 player->is_switching = FALSE;
1191 player->is_dropping = FALSE;
1193 player->is_bored = FALSE;
1194 player->is_sleeping = FALSE;
1196 player->frame_counter_bored = -1;
1197 player->frame_counter_sleeping = -1;
1199 player->anim_delay_counter = 0;
1200 player->post_delay_counter = 0;
1202 player->action_waiting = ACTION_DEFAULT;
1203 player->last_action_waiting = ACTION_DEFAULT;
1204 player->special_action_bored = ACTION_DEFAULT;
1205 player->special_action_sleeping = ACTION_DEFAULT;
1207 player->num_special_action_bored = 0;
1208 player->num_special_action_sleeping = 0;
1210 /* determine number of special actions for bored and sleeping animation */
1211 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1213 boolean found = FALSE;
1215 for (k = 0; k < NUM_DIRECTIONS; k++)
1216 if (el_act_dir2img(player->element_nr, j, k) !=
1217 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1221 player->num_special_action_bored++;
1225 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1227 boolean found = FALSE;
1229 for (k = 0; k < NUM_DIRECTIONS; k++)
1230 if (el_act_dir2img(player->element_nr, j, k) !=
1231 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1235 player->num_special_action_sleeping++;
1240 player->switch_x = -1;
1241 player->switch_y = -1;
1243 player->show_envelope = 0;
1245 player->move_delay = game.initial_move_delay;
1246 player->move_delay_value = game.initial_move_delay_value;
1248 player->move_delay_reset_counter = 0;
1250 player->push_delay = 0;
1251 player->push_delay_value = game.initial_push_delay_value;
1253 player->drop_delay = 0;
1255 player->last_jx = player->last_jy = 0;
1256 player->jx = player->jy = 0;
1258 player->shield_normal_time_left = 0;
1259 player->shield_deadly_time_left = 0;
1261 player->inventory_size = 0;
1263 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1264 SnapField(player, 0, 0);
1266 player->LevelSolved = FALSE;
1267 player->GameOver = FALSE;
1270 network_player_action_received = FALSE;
1272 #if defined(PLATFORM_UNIX)
1273 /* initial null action */
1274 if (network_playing)
1275 SendToServer_MovePlayer(MV_NO_MOVING);
1283 TimeLeft = level.time;
1285 ScreenMovDir = MV_NO_MOVING;
1289 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1291 AllPlayersGone = FALSE;
1293 game.yamyam_content_nr = 0;
1294 game.magic_wall_active = FALSE;
1295 game.magic_wall_time_left = 0;
1296 game.light_time_left = 0;
1297 game.timegate_time_left = 0;
1298 game.switchgate_pos = 0;
1299 game.balloon_dir = MV_NO_MOVING;
1300 game.gravity = level.initial_gravity;
1301 game.explosions_delayed = TRUE;
1303 game.envelope_active = FALSE;
1305 for (i = 0; i < 4; i++)
1307 game.belt_dir[i] = MV_NO_MOVING;
1308 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1311 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1312 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1314 for (x = 0; x < lev_fieldx; x++)
1316 for (y = 0; y < lev_fieldy; y++)
1318 Feld[x][y] = level.field[x][y];
1319 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1320 ChangeDelay[x][y] = 0;
1321 ChangePage[x][y] = -1;
1322 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1324 WasJustMoving[x][y] = 0;
1325 WasJustFalling[x][y] = 0;
1327 Pushed[x][y] = FALSE;
1329 Changed[x][y] = CE_BITMASK_DEFAULT;
1330 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1332 ExplodePhase[x][y] = 0;
1333 ExplodeField[x][y] = EX_NO_EXPLOSION;
1335 RunnerVisit[x][y] = 0;
1336 PlayerVisit[x][y] = 0;
1339 GfxRandom[x][y] = INIT_GFX_RANDOM();
1340 GfxElement[x][y] = EL_UNDEFINED;
1341 GfxAction[x][y] = ACTION_DEFAULT;
1342 GfxDir[x][y] = MV_NO_MOVING;
1346 for (y = 0; y < lev_fieldy; y++)
1348 for (x = 0; x < lev_fieldx; x++)
1350 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1352 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1354 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1357 InitField(x, y, TRUE);
1363 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1364 emulate_sb ? EMU_SOKOBAN :
1365 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1367 /* correct non-moving belts to start moving left */
1368 for (i = 0; i < 4; i++)
1369 if (game.belt_dir[i] == MV_NO_MOVING)
1370 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1372 /* check if any connected player was not found in playfield */
1373 for (i = 0; i < MAX_PLAYERS; i++)
1375 struct PlayerInfo *player = &stored_player[i];
1377 if (player->connected && !player->present)
1379 for (j = 0; j < MAX_PLAYERS; j++)
1381 struct PlayerInfo *some_player = &stored_player[j];
1382 int jx = some_player->jx, jy = some_player->jy;
1384 /* assign first free player found that is present in the playfield */
1385 if (some_player->present && !some_player->connected)
1387 player->present = TRUE;
1388 player->active = TRUE;
1389 some_player->present = FALSE;
1391 StorePlayer[jx][jy] = player->element_nr;
1392 player->jx = player->last_jx = jx;
1393 player->jy = player->last_jy = jy;
1403 /* when playing a tape, eliminate all players who do not participate */
1405 for (i = 0; i < MAX_PLAYERS; i++)
1407 if (stored_player[i].active && !tape.player_participates[i])
1409 struct PlayerInfo *player = &stored_player[i];
1410 int jx = player->jx, jy = player->jy;
1412 player->active = FALSE;
1413 StorePlayer[jx][jy] = 0;
1414 Feld[jx][jy] = EL_EMPTY;
1418 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1420 /* when in single player mode, eliminate all but the first active player */
1422 for (i = 0; i < MAX_PLAYERS; i++)
1424 if (stored_player[i].active)
1426 for (j = i + 1; j < MAX_PLAYERS; j++)
1428 if (stored_player[j].active)
1430 struct PlayerInfo *player = &stored_player[j];
1431 int jx = player->jx, jy = player->jy;
1433 player->active = FALSE;
1434 StorePlayer[jx][jy] = 0;
1435 Feld[jx][jy] = EL_EMPTY;
1442 /* when recording the game, store which players take part in the game */
1445 for (i = 0; i < MAX_PLAYERS; i++)
1446 if (stored_player[i].active)
1447 tape.player_participates[i] = TRUE;
1452 for (i = 0; i < MAX_PLAYERS; i++)
1454 struct PlayerInfo *player = &stored_player[i];
1456 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1461 if (local_player == player)
1462 printf("Player %d is local player.\n", i+1);
1466 if (BorderElement == EL_EMPTY)
1469 SBX_Right = lev_fieldx - SCR_FIELDX;
1471 SBY_Lower = lev_fieldy - SCR_FIELDY;
1476 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1478 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1481 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1482 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1484 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1485 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1487 /* if local player not found, look for custom element that might create
1488 the player (make some assumptions about the right custom element) */
1489 if (!local_player->present)
1491 int start_x = 0, start_y = 0;
1492 int found_rating = 0;
1493 int found_element = EL_UNDEFINED;
1495 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1497 int element = Feld[x][y];
1502 if (!IS_CUSTOM_ELEMENT(element))
1505 if (CAN_CHANGE(element))
1507 for (i = 0; i < element_info[element].num_change_pages; i++)
1509 content = element_info[element].change_page[i].target_element;
1510 is_player = ELEM_IS_PLAYER(content);
1512 if (is_player && (found_rating < 3 || element < found_element))
1518 found_element = element;
1523 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1525 content = element_info[element].content[xx][yy];
1526 is_player = ELEM_IS_PLAYER(content);
1528 if (is_player && (found_rating < 2 || element < found_element))
1530 start_x = x + xx - 1;
1531 start_y = y + yy - 1;
1534 found_element = element;
1537 if (!CAN_CHANGE(element))
1540 for (i = 0; i < element_info[element].num_change_pages; i++)
1542 content = element_info[element].change_page[i].content[xx][yy];
1543 is_player = ELEM_IS_PLAYER(content);
1545 if (is_player && (found_rating < 1 || element < found_element))
1547 start_x = x + xx - 1;
1548 start_y = y + yy - 1;
1551 found_element = element;
1557 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1558 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1561 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1562 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1568 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1569 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1570 local_player->jx - MIDPOSX);
1572 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1573 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1574 local_player->jy - MIDPOSY);
1576 scroll_x = SBX_Left;
1577 scroll_y = SBY_Upper;
1578 if (local_player->jx >= SBX_Left + MIDPOSX)
1579 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1580 local_player->jx - MIDPOSX :
1582 if (local_player->jy >= SBY_Upper + MIDPOSY)
1583 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1584 local_player->jy - MIDPOSY :
1589 CloseDoor(DOOR_CLOSE_1);
1594 /* after drawing the level, correct some elements */
1595 if (game.timegate_time_left == 0)
1596 CloseAllOpenTimegates();
1598 if (setup.soft_scrolling)
1599 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1601 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1604 /* copy default game door content to main double buffer */
1605 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1606 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1609 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1612 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1613 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1614 BlitBitmap(drawto, drawto,
1615 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1616 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1617 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1618 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1621 DrawGameDoorValues();
1625 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1626 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1627 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1631 /* copy actual game door content to door double buffer for OpenDoor() */
1632 BlitBitmap(drawto, bitmap_db_door,
1633 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1635 OpenDoor(DOOR_OPEN_ALL);
1637 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1639 if (setup.sound_music)
1642 KeyboardAutoRepeatOffUnlessAutoplay();
1646 for (i = 0; i < 4; i++)
1647 printf("Player %d %sactive.\n",
1648 i + 1, (stored_player[i].active ? "" : "not "));
1652 printf("::: starting game [%d]\n", FrameCounter);
1656 void InitMovDir(int x, int y)
1658 int i, element = Feld[x][y];
1659 static int xy[4][2] =
1666 static int direction[3][4] =
1668 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1669 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1670 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1679 Feld[x][y] = EL_BUG;
1680 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1683 case EL_SPACESHIP_RIGHT:
1684 case EL_SPACESHIP_UP:
1685 case EL_SPACESHIP_LEFT:
1686 case EL_SPACESHIP_DOWN:
1687 Feld[x][y] = EL_SPACESHIP;
1688 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1691 case EL_BD_BUTTERFLY_RIGHT:
1692 case EL_BD_BUTTERFLY_UP:
1693 case EL_BD_BUTTERFLY_LEFT:
1694 case EL_BD_BUTTERFLY_DOWN:
1695 Feld[x][y] = EL_BD_BUTTERFLY;
1696 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1699 case EL_BD_FIREFLY_RIGHT:
1700 case EL_BD_FIREFLY_UP:
1701 case EL_BD_FIREFLY_LEFT:
1702 case EL_BD_FIREFLY_DOWN:
1703 Feld[x][y] = EL_BD_FIREFLY;
1704 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1707 case EL_PACMAN_RIGHT:
1709 case EL_PACMAN_LEFT:
1710 case EL_PACMAN_DOWN:
1711 Feld[x][y] = EL_PACMAN;
1712 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1715 case EL_SP_SNIKSNAK:
1716 MovDir[x][y] = MV_UP;
1719 case EL_SP_ELECTRON:
1720 MovDir[x][y] = MV_LEFT;
1727 Feld[x][y] = EL_MOLE;
1728 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1732 if (IS_CUSTOM_ELEMENT(element))
1734 struct ElementInfo *ei = &element_info[element];
1735 int move_direction_initial = ei->move_direction_initial;
1736 int move_pattern = ei->move_pattern;
1738 if (move_direction_initial == MV_PREVIOUS)
1740 if (MovDir[x][y] != MV_NO_MOVING)
1743 move_direction_initial = MV_AUTOMATIC;
1746 if (move_direction_initial == MV_RANDOM)
1747 MovDir[x][y] = 1 << RND(4);
1748 else if (move_direction_initial & MV_ANY_DIRECTION)
1749 MovDir[x][y] = move_direction_initial;
1750 else if (move_pattern == MV_ALL_DIRECTIONS ||
1751 move_pattern == MV_TURNING_LEFT ||
1752 move_pattern == MV_TURNING_RIGHT ||
1753 move_pattern == MV_TURNING_LEFT_RIGHT ||
1754 move_pattern == MV_TURNING_RIGHT_LEFT ||
1755 move_pattern == MV_TURNING_RANDOM)
1756 MovDir[x][y] = 1 << RND(4);
1757 else if (move_pattern == MV_HORIZONTAL)
1758 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1759 else if (move_pattern == MV_VERTICAL)
1760 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1761 else if (move_pattern & MV_ANY_DIRECTION)
1762 MovDir[x][y] = element_info[element].move_pattern;
1763 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1764 move_pattern == MV_ALONG_RIGHT_SIDE)
1766 for (i = 0; i < 4; i++)
1768 int x1 = x + xy[i][0];
1769 int y1 = y + xy[i][1];
1771 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1773 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1774 MovDir[x][y] = direction[0][i];
1776 MovDir[x][y] = direction[1][i];
1785 MovDir[x][y] = 1 << RND(4);
1787 if (element != EL_BUG &&
1788 element != EL_SPACESHIP &&
1789 element != EL_BD_BUTTERFLY &&
1790 element != EL_BD_FIREFLY)
1793 for (i = 0; i < 4; i++)
1795 int x1 = x + xy[i][0];
1796 int y1 = y + xy[i][1];
1798 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1800 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1802 MovDir[x][y] = direction[0][i];
1805 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1806 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1808 MovDir[x][y] = direction[1][i];
1817 GfxDir[x][y] = MovDir[x][y];
1820 void InitAmoebaNr(int x, int y)
1823 int group_nr = AmoebeNachbarNr(x, y);
1827 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1829 if (AmoebaCnt[i] == 0)
1837 AmoebaNr[x][y] = group_nr;
1838 AmoebaCnt[group_nr]++;
1839 AmoebaCnt2[group_nr]++;
1845 boolean raise_level = FALSE;
1847 if (local_player->MovPos)
1851 if (tape.auto_play) /* tape might already be stopped here */
1852 tape.auto_play_level_solved = TRUE;
1854 if (tape.playing && tape.auto_play)
1855 tape.auto_play_level_solved = TRUE;
1858 local_player->LevelSolved = FALSE;
1860 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1864 if (!tape.playing && setup.sound_loops)
1865 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1866 SND_CTRL_PLAY_LOOP);
1868 while (TimeLeft > 0)
1870 if (!tape.playing && !setup.sound_loops)
1871 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1872 if (TimeLeft > 0 && !(TimeLeft % 10))
1873 RaiseScore(level.score[SC_TIME_BONUS]);
1874 if (TimeLeft > 100 && !(TimeLeft % 10))
1878 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1885 if (!tape.playing && setup.sound_loops)
1886 StopSound(SND_GAME_LEVELTIME_BONUS);
1888 else if (level.time == 0) /* level without time limit */
1890 if (!tape.playing && setup.sound_loops)
1891 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1892 SND_CTRL_PLAY_LOOP);
1894 while (TimePlayed < 999)
1896 if (!tape.playing && !setup.sound_loops)
1897 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1898 if (TimePlayed < 999 && !(TimePlayed % 10))
1899 RaiseScore(level.score[SC_TIME_BONUS]);
1900 if (TimePlayed < 900 && !(TimePlayed % 10))
1904 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1911 if (!tape.playing && setup.sound_loops)
1912 StopSound(SND_GAME_LEVELTIME_BONUS);
1915 /* close exit door after last player */
1916 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1917 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1919 int element = Feld[ExitX][ExitY];
1921 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1922 EL_SP_EXIT_CLOSING);
1924 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1927 /* Hero disappears */
1928 DrawLevelField(ExitX, ExitY);
1934 CloseDoor(DOOR_CLOSE_1);
1939 SaveTape(tape.level_nr); /* Ask to save tape */
1942 if (level_nr == leveldir_current->handicap_level)
1944 leveldir_current->handicap_level++;
1945 SaveLevelSetup_SeriesInfo();
1948 if (level_editor_test_game)
1949 local_player->score = -1; /* no highscore when playing from editor */
1950 else if (level_nr < leveldir_current->last_level)
1951 raise_level = TRUE; /* advance to next level */
1953 if ((hi_pos = NewHiScore()) >= 0)
1955 game_status = GAME_MODE_SCORES;
1956 DrawHallOfFame(hi_pos);
1965 game_status = GAME_MODE_MAIN;
1982 LoadScore(level_nr);
1984 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1985 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1988 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1990 if (local_player->score > highscore[k].Score)
1992 /* player has made it to the hall of fame */
1994 if (k < MAX_SCORE_ENTRIES - 1)
1996 int m = MAX_SCORE_ENTRIES - 1;
1999 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2000 if (!strcmp(setup.player_name, highscore[l].Name))
2002 if (m == k) /* player's new highscore overwrites his old one */
2006 for (l = m; l > k; l--)
2008 strcpy(highscore[l].Name, highscore[l - 1].Name);
2009 highscore[l].Score = highscore[l - 1].Score;
2016 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2017 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2018 highscore[k].Score = local_player->score;
2024 else if (!strncmp(setup.player_name, highscore[k].Name,
2025 MAX_PLAYER_NAME_LEN))
2026 break; /* player already there with a higher score */
2032 SaveScore(level_nr);
2037 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2039 if (player->GfxAction != action || player->GfxDir != dir)
2042 printf("Player frame reset! (%d => %d, %d => %d)\n",
2043 player->GfxAction, action, player->GfxDir, dir);
2046 player->GfxAction = action;
2047 player->GfxDir = dir;
2049 player->StepFrame = 0;
2053 static void ResetRandomAnimationValue(int x, int y)
2055 GfxRandom[x][y] = INIT_GFX_RANDOM();
2058 static void ResetGfxAnimation(int x, int y)
2061 GfxAction[x][y] = ACTION_DEFAULT;
2062 GfxDir[x][y] = MovDir[x][y];
2065 void InitMovingField(int x, int y, int direction)
2067 int element = Feld[x][y];
2068 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2069 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2073 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2074 ResetGfxAnimation(x, y);
2076 MovDir[newx][newy] = MovDir[x][y] = direction;
2077 GfxDir[x][y] = direction;
2079 if (Feld[newx][newy] == EL_EMPTY)
2080 Feld[newx][newy] = EL_BLOCKED;
2082 if (direction == MV_DOWN && CAN_FALL(element))
2083 GfxAction[x][y] = ACTION_FALLING;
2085 GfxAction[x][y] = ACTION_MOVING;
2087 GfxFrame[newx][newy] = GfxFrame[x][y];
2088 GfxRandom[newx][newy] = GfxRandom[x][y];
2089 GfxAction[newx][newy] = GfxAction[x][y];
2090 GfxDir[newx][newy] = GfxDir[x][y];
2093 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2095 int direction = MovDir[x][y];
2096 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2097 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2103 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2105 int oldx = x, oldy = y;
2106 int direction = MovDir[x][y];
2108 if (direction == MV_LEFT)
2110 else if (direction == MV_RIGHT)
2112 else if (direction == MV_UP)
2114 else if (direction == MV_DOWN)
2117 *comes_from_x = oldx;
2118 *comes_from_y = oldy;
2121 int MovingOrBlocked2Element(int x, int y)
2123 int element = Feld[x][y];
2125 if (element == EL_BLOCKED)
2129 Blocked2Moving(x, y, &oldx, &oldy);
2130 return Feld[oldx][oldy];
2136 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2138 /* like MovingOrBlocked2Element(), but if element is moving
2139 and (x,y) is the field the moving element is just leaving,
2140 return EL_BLOCKED instead of the element value */
2141 int element = Feld[x][y];
2143 if (IS_MOVING(x, y))
2145 if (element == EL_BLOCKED)
2149 Blocked2Moving(x, y, &oldx, &oldy);
2150 return Feld[oldx][oldy];
2159 static void RemoveField(int x, int y)
2161 Feld[x][y] = EL_EMPTY;
2168 ChangeDelay[x][y] = 0;
2169 ChangePage[x][y] = -1;
2170 Pushed[x][y] = FALSE;
2172 GfxElement[x][y] = EL_UNDEFINED;
2173 GfxAction[x][y] = ACTION_DEFAULT;
2174 GfxDir[x][y] = MV_NO_MOVING;
2177 void RemoveMovingField(int x, int y)
2179 int oldx = x, oldy = y, newx = x, newy = y;
2180 int element = Feld[x][y];
2181 int next_element = EL_UNDEFINED;
2183 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2186 if (IS_MOVING(x, y))
2188 Moving2Blocked(x, y, &newx, &newy);
2189 if (Feld[newx][newy] != EL_BLOCKED)
2192 else if (element == EL_BLOCKED)
2194 Blocked2Moving(x, y, &oldx, &oldy);
2195 if (!IS_MOVING(oldx, oldy))
2199 if (element == EL_BLOCKED &&
2200 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2201 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2202 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2203 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2204 next_element = get_next_element(Feld[oldx][oldy]);
2206 RemoveField(oldx, oldy);
2207 RemoveField(newx, newy);
2209 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2211 if (next_element != EL_UNDEFINED)
2212 Feld[oldx][oldy] = next_element;
2214 DrawLevelField(oldx, oldy);
2215 DrawLevelField(newx, newy);
2218 void DrawDynamite(int x, int y)
2220 int sx = SCREENX(x), sy = SCREENY(y);
2221 int graphic = el2img(Feld[x][y]);
2224 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2227 if (IS_WALKABLE_INSIDE(Back[x][y]))
2231 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2232 else if (Store[x][y])
2233 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2235 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2238 if (Back[x][y] || Store[x][y])
2239 DrawGraphicThruMask(sx, sy, graphic, frame);
2241 DrawGraphic(sx, sy, graphic, frame);
2243 if (game.emulation == EMU_SUPAPLEX)
2244 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2245 else if (Store[x][y])
2246 DrawGraphicThruMask(sx, sy, graphic, frame);
2248 DrawGraphic(sx, sy, graphic, frame);
2252 void CheckDynamite(int x, int y)
2254 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2258 if (MovDelay[x][y] != 0)
2261 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2268 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2270 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2271 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2272 StopSound(SND_DYNAMITE_ACTIVE);
2274 StopSound(SND_DYNABOMB_ACTIVE);
2280 void RelocatePlayer(int x, int y, int element)
2282 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2284 if (player->GameOver) /* do not reanimate dead player */
2288 RemoveField(x, y); /* temporarily remove newly placed player */
2289 DrawLevelField(x, y);
2292 if (player->present)
2294 while (player->MovPos)
2296 ScrollPlayer(player, SCROLL_GO_ON);
2297 ScrollScreen(NULL, SCROLL_GO_ON);
2303 Delay(GAME_FRAME_DELAY);
2306 DrawPlayer(player); /* needed here only to cleanup last field */
2307 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2309 player->is_moving = FALSE;
2312 Feld[x][y] = element;
2313 InitPlayerField(x, y, element, TRUE);
2315 if (player == local_player)
2317 int scroll_xx = -999, scroll_yy = -999;
2319 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2322 int fx = FX, fy = FY;
2324 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2325 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2326 local_player->jx - MIDPOSX);
2328 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2329 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2330 local_player->jy - MIDPOSY);
2332 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2333 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2338 fx += dx * TILEX / 2;
2339 fy += dy * TILEY / 2;
2341 ScrollLevel(dx, dy);
2344 /* scroll in two steps of half tile size to make things smoother */
2345 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2347 Delay(GAME_FRAME_DELAY);
2349 /* scroll second step to align at full tile size */
2351 Delay(GAME_FRAME_DELAY);
2356 void Explode(int ex, int ey, int phase, int mode)
2360 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2361 int last_phase = num_phase * delay;
2362 int half_phase = (num_phase / 2) * delay;
2363 int first_phase_after_start = EX_PHASE_START + 1;
2365 if (game.explosions_delayed)
2367 ExplodeField[ex][ey] = mode;
2371 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2373 int center_element = Feld[ex][ey];
2376 /* --- This is only really needed (and now handled) in "Impact()". --- */
2377 /* do not explode moving elements that left the explode field in time */
2378 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2379 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2383 if (mode == EX_NORMAL || mode == EX_CENTER)
2384 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2386 /* remove things displayed in background while burning dynamite */
2387 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2390 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2392 /* put moving element to center field (and let it explode there) */
2393 center_element = MovingOrBlocked2Element(ex, ey);
2394 RemoveMovingField(ex, ey);
2395 Feld[ex][ey] = center_element;
2398 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2400 int xx = x - ex + 1;
2401 int yy = y - ey + 1;
2404 if (!IN_LEV_FIELD(x, y) ||
2405 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2406 (x != ex || y != ey)))
2409 element = Feld[x][y];
2411 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2413 element = MovingOrBlocked2Element(x, y);
2415 if (!IS_EXPLOSION_PROOF(element))
2416 RemoveMovingField(x, y);
2422 if (IS_EXPLOSION_PROOF(element))
2425 /* indestructible elements can only explode in center (but not flames) */
2426 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2427 element == EL_FLAMES)
2432 if ((IS_INDESTRUCTIBLE(element) &&
2433 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2434 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2435 element == EL_FLAMES)
2439 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2441 if (IS_ACTIVE_BOMB(element))
2443 /* re-activate things under the bomb like gate or penguin */
2444 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2451 /* save walkable background elements while explosion on same tile */
2453 if (IS_INDESTRUCTIBLE(element))
2454 Back[x][y] = element;
2456 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2457 Back[x][y] = element;
2460 /* ignite explodable elements reached by other explosion */
2461 if (element == EL_EXPLOSION)
2462 element = Store2[x][y];
2465 if (AmoebaNr[x][y] &&
2466 (element == EL_AMOEBA_FULL ||
2467 element == EL_BD_AMOEBA ||
2468 element == EL_AMOEBA_GROWING))
2470 AmoebaCnt[AmoebaNr[x][y]]--;
2471 AmoebaCnt2[AmoebaNr[x][y]]--;
2477 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2479 switch(StorePlayer[ex][ey])
2482 Store[x][y] = EL_EMERALD_RED;
2485 Store[x][y] = EL_EMERALD;
2488 Store[x][y] = EL_EMERALD_PURPLE;
2492 Store[x][y] = EL_EMERALD_YELLOW;
2496 if (game.emulation == EMU_SUPAPLEX)
2497 Store[x][y] = EL_EMPTY;
2499 else if (center_element == EL_MOLE)
2500 Store[x][y] = EL_EMERALD_RED;
2501 else if (center_element == EL_PENGUIN)
2502 Store[x][y] = EL_EMERALD_PURPLE;
2503 else if (center_element == EL_BUG)
2504 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2505 else if (center_element == EL_BD_BUTTERFLY)
2506 Store[x][y] = EL_BD_DIAMOND;
2507 else if (center_element == EL_SP_ELECTRON)
2508 Store[x][y] = EL_SP_INFOTRON;
2509 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2510 Store[x][y] = level.amoeba_content;
2511 else if (center_element == EL_YAMYAM)
2512 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2513 else if (IS_CUSTOM_ELEMENT(center_element) &&
2514 element_info[center_element].content[xx][yy] != EL_EMPTY)
2515 Store[x][y] = element_info[center_element].content[xx][yy];
2516 else if (element == EL_WALL_EMERALD)
2517 Store[x][y] = EL_EMERALD;
2518 else if (element == EL_WALL_DIAMOND)
2519 Store[x][y] = EL_DIAMOND;
2520 else if (element == EL_WALL_BD_DIAMOND)
2521 Store[x][y] = EL_BD_DIAMOND;
2522 else if (element == EL_WALL_EMERALD_YELLOW)
2523 Store[x][y] = EL_EMERALD_YELLOW;
2524 else if (element == EL_WALL_EMERALD_RED)
2525 Store[x][y] = EL_EMERALD_RED;
2526 else if (element == EL_WALL_EMERALD_PURPLE)
2527 Store[x][y] = EL_EMERALD_PURPLE;
2528 else if (element == EL_WALL_PEARL)
2529 Store[x][y] = EL_PEARL;
2530 else if (element == EL_WALL_CRYSTAL)
2531 Store[x][y] = EL_CRYSTAL;
2532 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2533 Store[x][y] = element_info[element].content[1][1];
2535 Store[x][y] = EL_EMPTY;
2537 if (x != ex || y != ey ||
2538 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2539 Store2[x][y] = element;
2542 if (AmoebaNr[x][y] &&
2543 (element == EL_AMOEBA_FULL ||
2544 element == EL_BD_AMOEBA ||
2545 element == EL_AMOEBA_GROWING))
2547 AmoebaCnt[AmoebaNr[x][y]]--;
2548 AmoebaCnt2[AmoebaNr[x][y]]--;
2554 MovDir[x][y] = MovPos[x][y] = 0;
2555 GfxDir[x][y] = MovDir[x][y];
2560 Feld[x][y] = EL_EXPLOSION;
2562 GfxElement[x][y] = center_element;
2564 GfxElement[x][y] = EL_UNDEFINED;
2567 ExplodePhase[x][y] = 1;
2571 if (center_element == EL_YAMYAM)
2572 game.yamyam_content_nr =
2573 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2584 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2588 /* activate this even in non-DEBUG version until cause for crash in
2589 getGraphicAnimationFrame() (see below) is found and eliminated */
2593 if (GfxElement[x][y] == EL_UNDEFINED)
2596 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2597 printf("Explode(): This should never happen!\n");
2600 GfxElement[x][y] = EL_EMPTY;
2604 if (phase == first_phase_after_start)
2606 int element = Store2[x][y];
2608 if (element == EL_BLACK_ORB)
2610 Feld[x][y] = Store2[x][y];
2615 else if (phase == half_phase)
2617 int element = Store2[x][y];
2619 if (IS_PLAYER(x, y))
2620 KillHeroUnlessProtected(x, y);
2621 else if (CAN_EXPLODE_BY_FIRE(element))
2623 Feld[x][y] = Store2[x][y];
2627 else if (element == EL_AMOEBA_TO_DIAMOND)
2628 AmoebeUmwandeln(x, y);
2631 if (phase == last_phase)
2635 element = Feld[x][y] = Store[x][y];
2636 Store[x][y] = Store2[x][y] = 0;
2637 GfxElement[x][y] = EL_UNDEFINED;
2639 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2640 element = Feld[x][y] = Back[x][y];
2643 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2644 GfxDir[x][y] = MV_NO_MOVING;
2645 ChangeDelay[x][y] = 0;
2646 ChangePage[x][y] = -1;
2648 InitField(x, y, FALSE);
2649 if (CAN_MOVE(element))
2651 DrawLevelField(x, y);
2653 TestIfElementTouchesCustomElement(x, y);
2655 if (GFX_CRUMBLED(element))
2656 DrawLevelFieldCrumbledSandNeighbours(x, y);
2658 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2659 StorePlayer[x][y] = 0;
2661 if (ELEM_IS_PLAYER(element))
2662 RelocatePlayer(x, y, element);
2664 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2667 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2669 int stored = Store[x][y];
2670 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2671 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2674 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2677 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2678 element_info[GfxElement[x][y]].token_name,
2683 DrawLevelFieldCrumbledSand(x, y);
2685 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2687 DrawLevelElement(x, y, Back[x][y]);
2688 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2690 else if (IS_WALKABLE_UNDER(Back[x][y]))
2692 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2693 DrawLevelElementThruMask(x, y, Back[x][y]);
2695 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2696 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2700 void DynaExplode(int ex, int ey)
2703 int dynabomb_element = Feld[ex][ey];
2704 int dynabomb_size = 1;
2705 boolean dynabomb_xl = FALSE;
2706 struct PlayerInfo *player;
2707 static int xy[4][2] =
2715 if (IS_ACTIVE_BOMB(dynabomb_element))
2717 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2718 dynabomb_size = player->dynabomb_size;
2719 dynabomb_xl = player->dynabomb_xl;
2720 player->dynabombs_left++;
2723 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2725 for (i = 0; i < 4; i++)
2727 for (j = 1; j <= dynabomb_size; j++)
2729 int x = ex + j * xy[i % 4][0];
2730 int y = ey + j * xy[i % 4][1];
2733 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2736 element = Feld[x][y];
2738 /* do not restart explosions of fields with active bombs */
2739 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2742 Explode(x, y, EX_PHASE_START, EX_BORDER);
2744 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2745 if (element != EL_EMPTY &&
2746 element != EL_SAND &&
2747 element != EL_EXPLOSION &&
2754 void Bang(int x, int y)
2757 int element = MovingOrBlocked2Element(x, y);
2759 int element = Feld[x][y];
2763 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2765 if (IS_PLAYER(x, y))
2768 struct PlayerInfo *player = PLAYERINFO(x, y);
2770 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2771 player->element_nr);
2776 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2778 if (game.emulation == EMU_SUPAPLEX)
2779 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2781 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2786 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2794 case EL_BD_BUTTERFLY:
2797 case EL_DARK_YAMYAM:
2801 RaiseScoreElement(element);
2802 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2804 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2805 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2806 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2807 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2808 case EL_DYNABOMB_INCREASE_NUMBER:
2809 case EL_DYNABOMB_INCREASE_SIZE:
2810 case EL_DYNABOMB_INCREASE_POWER:
2815 case EL_LAMP_ACTIVE:
2816 if (IS_PLAYER(x, y))
2817 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2819 Explode(x, y, EX_PHASE_START, EX_CENTER);
2822 if (CAN_EXPLODE_DYNA(element))
2824 else if (CAN_EXPLODE_1X1(element))
2825 Explode(x, y, EX_PHASE_START, EX_CENTER);
2827 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2831 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2834 void SplashAcid(int x, int y)
2836 int element = Feld[x][y];
2838 if (element != EL_ACID_SPLASH_LEFT &&
2839 element != EL_ACID_SPLASH_RIGHT)
2841 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2843 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2844 (!IN_LEV_FIELD(x-1, y-1) ||
2845 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2846 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2848 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2849 (!IN_LEV_FIELD(x+1, y-1) ||
2850 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2851 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2855 static void InitBeltMovement()
2857 static int belt_base_element[4] =
2859 EL_CONVEYOR_BELT_1_LEFT,
2860 EL_CONVEYOR_BELT_2_LEFT,
2861 EL_CONVEYOR_BELT_3_LEFT,
2862 EL_CONVEYOR_BELT_4_LEFT
2864 static int belt_base_active_element[4] =
2866 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2867 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2868 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2869 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2874 /* set frame order for belt animation graphic according to belt direction */
2875 for (i = 0; i < 4; i++)
2879 for (j = 0; j < 3; j++)
2881 int element = belt_base_active_element[belt_nr] + j;
2882 int graphic = el2img(element);
2884 if (game.belt_dir[i] == MV_LEFT)
2885 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2887 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2891 for (y = 0; y < lev_fieldy; y++)
2893 for (x = 0; x < lev_fieldx; x++)
2895 int element = Feld[x][y];
2897 for (i = 0; i < 4; i++)
2899 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2901 int e_belt_nr = getBeltNrFromBeltElement(element);
2904 if (e_belt_nr == belt_nr)
2906 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2908 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2916 static void ToggleBeltSwitch(int x, int y)
2918 static int belt_base_element[4] =
2920 EL_CONVEYOR_BELT_1_LEFT,
2921 EL_CONVEYOR_BELT_2_LEFT,
2922 EL_CONVEYOR_BELT_3_LEFT,
2923 EL_CONVEYOR_BELT_4_LEFT
2925 static int belt_base_active_element[4] =
2927 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2928 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2929 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2930 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2932 static int belt_base_switch_element[4] =
2934 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2935 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2936 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2937 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2939 static int belt_move_dir[4] =
2947 int element = Feld[x][y];
2948 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2949 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2950 int belt_dir = belt_move_dir[belt_dir_nr];
2953 if (!IS_BELT_SWITCH(element))
2956 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2957 game.belt_dir[belt_nr] = belt_dir;
2959 if (belt_dir_nr == 3)
2962 /* set frame order for belt animation graphic according to belt direction */
2963 for (i = 0; i < 3; i++)
2965 int element = belt_base_active_element[belt_nr] + i;
2966 int graphic = el2img(element);
2968 if (belt_dir == MV_LEFT)
2969 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2971 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2974 for (yy = 0; yy < lev_fieldy; yy++)
2976 for (xx = 0; xx < lev_fieldx; xx++)
2978 int element = Feld[xx][yy];
2980 if (IS_BELT_SWITCH(element))
2982 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2984 if (e_belt_nr == belt_nr)
2986 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2987 DrawLevelField(xx, yy);
2990 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2992 int e_belt_nr = getBeltNrFromBeltElement(element);
2994 if (e_belt_nr == belt_nr)
2996 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2998 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2999 DrawLevelField(xx, yy);
3002 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3004 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3006 if (e_belt_nr == belt_nr)
3008 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3010 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3011 DrawLevelField(xx, yy);
3018 static void ToggleSwitchgateSwitch(int x, int y)
3022 game.switchgate_pos = !game.switchgate_pos;
3024 for (yy = 0; yy < lev_fieldy; yy++)
3026 for (xx = 0; xx < lev_fieldx; xx++)
3028 int element = Feld[xx][yy];
3030 if (element == EL_SWITCHGATE_SWITCH_UP ||
3031 element == EL_SWITCHGATE_SWITCH_DOWN)
3033 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3034 DrawLevelField(xx, yy);
3036 else if (element == EL_SWITCHGATE_OPEN ||
3037 element == EL_SWITCHGATE_OPENING)
3039 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3041 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3043 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3046 else if (element == EL_SWITCHGATE_CLOSED ||
3047 element == EL_SWITCHGATE_CLOSING)
3049 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3051 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3053 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3060 static int getInvisibleActiveFromInvisibleElement(int element)
3062 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3063 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3064 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3068 static int getInvisibleFromInvisibleActiveElement(int element)
3070 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3071 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3072 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3076 static void RedrawAllLightSwitchesAndInvisibleElements()
3080 for (y = 0; y < lev_fieldy; y++)
3082 for (x = 0; x < lev_fieldx; x++)
3084 int element = Feld[x][y];
3086 if (element == EL_LIGHT_SWITCH &&
3087 game.light_time_left > 0)
3089 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3090 DrawLevelField(x, y);
3092 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3093 game.light_time_left == 0)
3095 Feld[x][y] = EL_LIGHT_SWITCH;
3096 DrawLevelField(x, y);
3098 else if (element == EL_INVISIBLE_STEELWALL ||
3099 element == EL_INVISIBLE_WALL ||
3100 element == EL_INVISIBLE_SAND)
3102 if (game.light_time_left > 0)
3103 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3105 DrawLevelField(x, y);
3107 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3108 element == EL_INVISIBLE_WALL_ACTIVE ||
3109 element == EL_INVISIBLE_SAND_ACTIVE)
3111 if (game.light_time_left == 0)
3112 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3114 DrawLevelField(x, y);
3120 static void ToggleLightSwitch(int x, int y)
3122 int element = Feld[x][y];
3124 game.light_time_left =
3125 (element == EL_LIGHT_SWITCH ?
3126 level.time_light * FRAMES_PER_SECOND : 0);
3128 RedrawAllLightSwitchesAndInvisibleElements();
3131 static void ActivateTimegateSwitch(int x, int y)
3135 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3137 for (yy = 0; yy < lev_fieldy; yy++)
3139 for (xx = 0; xx < lev_fieldx; xx++)
3141 int element = Feld[xx][yy];
3143 if (element == EL_TIMEGATE_CLOSED ||
3144 element == EL_TIMEGATE_CLOSING)
3146 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3147 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3151 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3153 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3154 DrawLevelField(xx, yy);
3161 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3164 inline static int getElementMoveStepsize(int x, int y)
3166 int element = Feld[x][y];
3167 int direction = MovDir[x][y];
3168 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3169 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3170 int horiz_move = (dx != 0);
3171 int sign = (horiz_move ? dx : dy);
3172 int step = sign * element_info[element].move_stepsize;
3174 /* special values for move stepsize for spring and things on conveyor belt */
3177 if (CAN_FALL(element) &&
3178 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3179 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3180 else if (element == EL_SPRING)
3181 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3187 void Impact(int x, int y)
3189 boolean lastline = (y == lev_fieldy-1);
3190 boolean object_hit = FALSE;
3191 boolean impact = (lastline || object_hit);
3192 int element = Feld[x][y];
3193 int smashed = EL_UNDEFINED;
3195 if (!lastline) /* check if element below was hit */
3197 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3200 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3201 MovDir[x][y + 1] != MV_DOWN ||
3202 MovPos[x][y + 1] <= TILEY / 2));
3205 object_hit = !IS_FREE(x, y + 1);
3208 /* do not smash moving elements that left the smashed field in time */
3209 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3210 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3214 smashed = MovingOrBlocked2Element(x, y + 1);
3216 impact = (lastline || object_hit);
3219 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3225 /* only reset graphic animation if graphic really changes after impact */
3227 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3229 ResetGfxAnimation(x, y);
3230 DrawLevelField(x, y);
3233 if (impact && CAN_EXPLODE_IMPACT(element))
3238 else if (impact && element == EL_PEARL)
3240 Feld[x][y] = EL_PEARL_BREAKING;
3241 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3244 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3246 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3251 if (impact && element == EL_AMOEBA_DROP)
3253 if (object_hit && IS_PLAYER(x, y + 1))
3254 KillHeroUnlessProtected(x, y + 1);
3255 else if (object_hit && smashed == EL_PENGUIN)
3259 Feld[x][y] = EL_AMOEBA_GROWING;
3260 Store[x][y] = EL_AMOEBA_WET;
3262 ResetRandomAnimationValue(x, y);
3267 if (object_hit) /* check which object was hit */
3269 if (CAN_PASS_MAGIC_WALL(element) &&
3270 (smashed == EL_MAGIC_WALL ||
3271 smashed == EL_BD_MAGIC_WALL))
3274 int activated_magic_wall =
3275 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3276 EL_BD_MAGIC_WALL_ACTIVE);
3278 /* activate magic wall / mill */
3279 for (yy = 0; yy < lev_fieldy; yy++)
3280 for (xx = 0; xx < lev_fieldx; xx++)
3281 if (Feld[xx][yy] == smashed)
3282 Feld[xx][yy] = activated_magic_wall;
3284 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3285 game.magic_wall_active = TRUE;
3287 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3288 SND_MAGIC_WALL_ACTIVATING :
3289 SND_BD_MAGIC_WALL_ACTIVATING));
3292 if (IS_PLAYER(x, y + 1))
3294 if (CAN_SMASH_PLAYER(element))
3296 KillHeroUnlessProtected(x, y + 1);
3300 else if (smashed == EL_PENGUIN)
3302 if (CAN_SMASH_PLAYER(element))
3308 else if (element == EL_BD_DIAMOND)
3310 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3316 else if ((element == EL_SP_INFOTRON ||
3317 element == EL_SP_ZONK) &&
3318 (smashed == EL_SP_SNIKSNAK ||
3319 smashed == EL_SP_ELECTRON ||
3320 smashed == EL_SP_DISK_ORANGE))
3326 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3332 else if (CAN_SMASH_EVERYTHING(element))
3334 if (IS_CLASSIC_ENEMY(smashed) ||
3335 CAN_EXPLODE_SMASHED(smashed))
3340 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3342 if (smashed == EL_LAMP ||
3343 smashed == EL_LAMP_ACTIVE)
3348 else if (smashed == EL_NUT)
3350 Feld[x][y + 1] = EL_NUT_BREAKING;
3351 PlayLevelSound(x, y, SND_NUT_BREAKING);
3352 RaiseScoreElement(EL_NUT);
3355 else if (smashed == EL_PEARL)
3357 Feld[x][y + 1] = EL_PEARL_BREAKING;
3358 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3361 else if (smashed == EL_DIAMOND)
3363 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3364 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3367 else if (IS_BELT_SWITCH(smashed))
3369 ToggleBeltSwitch(x, y + 1);
3371 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3372 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3374 ToggleSwitchgateSwitch(x, y + 1);
3376 else if (smashed == EL_LIGHT_SWITCH ||
3377 smashed == EL_LIGHT_SWITCH_ACTIVE)
3379 ToggleLightSwitch(x, y + 1);
3383 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3385 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3386 CE_OTHER_IS_SWITCHING);
3387 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3393 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3398 /* play sound of magic wall / mill */
3400 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3401 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3403 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3404 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3405 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3406 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3411 /* play sound of object that hits the ground */
3412 if (lastline || object_hit)
3413 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3416 inline static void TurnRoundExt(int x, int y)
3428 { 0, 0 }, { 0, 0 }, { 0, 0 },
3433 int left, right, back;
3437 { MV_DOWN, MV_UP, MV_RIGHT },
3438 { MV_UP, MV_DOWN, MV_LEFT },
3440 { MV_LEFT, MV_RIGHT, MV_DOWN },
3444 { MV_RIGHT, MV_LEFT, MV_UP }
3447 int element = Feld[x][y];
3448 int move_pattern = element_info[element].move_pattern;
3450 int old_move_dir = MovDir[x][y];
3451 int left_dir = turn[old_move_dir].left;
3452 int right_dir = turn[old_move_dir].right;
3453 int back_dir = turn[old_move_dir].back;
3455 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3456 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3457 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3458 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3460 int left_x = x + left_dx, left_y = y + left_dy;
3461 int right_x = x + right_dx, right_y = y + right_dy;
3462 int move_x = x + move_dx, move_y = y + move_dy;
3466 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3468 TestIfBadThingTouchesOtherBadThing(x, y);
3470 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3471 MovDir[x][y] = right_dir;
3472 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3473 MovDir[x][y] = left_dir;
3475 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3477 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3480 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3481 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3483 TestIfBadThingTouchesOtherBadThing(x, y);
3485 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3486 MovDir[x][y] = left_dir;
3487 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3488 MovDir[x][y] = right_dir;
3490 if ((element == EL_SPACESHIP ||
3491 element == EL_SP_SNIKSNAK ||
3492 element == EL_SP_ELECTRON)
3493 && MovDir[x][y] != old_move_dir)
3495 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3498 else if (element == EL_YAMYAM)
3500 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3501 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3503 if (can_turn_left && can_turn_right)
3504 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3505 else if (can_turn_left)
3506 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3507 else if (can_turn_right)
3508 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3510 MovDir[x][y] = back_dir;
3512 MovDelay[x][y] = 16 + 16 * RND(3);
3514 else if (element == EL_DARK_YAMYAM)
3516 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3517 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3519 if (can_turn_left && can_turn_right)
3520 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3521 else if (can_turn_left)
3522 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3523 else if (can_turn_right)
3524 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3526 MovDir[x][y] = back_dir;
3528 MovDelay[x][y] = 16 + 16 * RND(3);
3530 else if (element == EL_PACMAN)
3532 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3533 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3535 if (can_turn_left && can_turn_right)
3536 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3537 else if (can_turn_left)
3538 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3539 else if (can_turn_right)
3540 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3542 MovDir[x][y] = back_dir;
3544 MovDelay[x][y] = 6 + RND(40);
3546 else if (element == EL_PIG)
3548 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3549 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3550 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3551 boolean should_turn_left, should_turn_right, should_move_on;
3553 int rnd = RND(rnd_value);
3555 should_turn_left = (can_turn_left &&
3557 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3558 y + back_dy + left_dy)));
3559 should_turn_right = (can_turn_right &&
3561 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3562 y + back_dy + right_dy)));
3563 should_move_on = (can_move_on &&
3566 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3567 y + move_dy + left_dy) ||
3568 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3569 y + move_dy + right_dy)));
3571 if (should_turn_left || should_turn_right || should_move_on)
3573 if (should_turn_left && should_turn_right && should_move_on)
3574 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3575 rnd < 2 * rnd_value / 3 ? right_dir :
3577 else if (should_turn_left && should_turn_right)
3578 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3579 else if (should_turn_left && should_move_on)
3580 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3581 else if (should_turn_right && should_move_on)
3582 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3583 else if (should_turn_left)
3584 MovDir[x][y] = left_dir;
3585 else if (should_turn_right)
3586 MovDir[x][y] = right_dir;
3587 else if (should_move_on)
3588 MovDir[x][y] = old_move_dir;
3590 else if (can_move_on && rnd > rnd_value / 8)
3591 MovDir[x][y] = old_move_dir;
3592 else if (can_turn_left && can_turn_right)
3593 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3594 else if (can_turn_left && rnd > rnd_value / 8)
3595 MovDir[x][y] = left_dir;
3596 else if (can_turn_right && rnd > rnd_value/8)
3597 MovDir[x][y] = right_dir;
3599 MovDir[x][y] = back_dir;
3601 xx = x + move_xy[MovDir[x][y]].x;
3602 yy = y + move_xy[MovDir[x][y]].y;
3604 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3605 MovDir[x][y] = old_move_dir;
3609 else if (element == EL_DRAGON)
3611 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3612 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3613 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3615 int rnd = RND(rnd_value);
3618 if (FrameCounter < 1 && x == 0 && y == 29)
3619 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3622 if (can_move_on && rnd > rnd_value / 8)
3623 MovDir[x][y] = old_move_dir;
3624 else if (can_turn_left && can_turn_right)
3625 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3626 else if (can_turn_left && rnd > rnd_value / 8)
3627 MovDir[x][y] = left_dir;
3628 else if (can_turn_right && rnd > rnd_value / 8)
3629 MovDir[x][y] = right_dir;
3631 MovDir[x][y] = back_dir;
3633 xx = x + move_xy[MovDir[x][y]].x;
3634 yy = y + move_xy[MovDir[x][y]].y;
3637 if (FrameCounter < 1 && x == 0 && y == 29)
3638 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3639 xx, yy, Feld[xx][yy],
3644 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3645 MovDir[x][y] = old_move_dir;
3647 if (!IS_FREE(xx, yy))
3648 MovDir[x][y] = old_move_dir;
3652 if (FrameCounter < 1 && x == 0 && y == 29)
3653 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3658 else if (element == EL_MOLE)
3660 boolean can_move_on =
3661 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3662 IS_AMOEBOID(Feld[move_x][move_y]) ||
3663 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3666 boolean can_turn_left =
3667 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3668 IS_AMOEBOID(Feld[left_x][left_y])));
3670 boolean can_turn_right =
3671 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3672 IS_AMOEBOID(Feld[right_x][right_y])));
3674 if (can_turn_left && can_turn_right)
3675 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3676 else if (can_turn_left)
3677 MovDir[x][y] = left_dir;
3679 MovDir[x][y] = right_dir;
3682 if (MovDir[x][y] != old_move_dir)
3685 else if (element == EL_BALLOON)
3687 MovDir[x][y] = game.balloon_dir;
3690 else if (element == EL_SPRING)
3692 if (MovDir[x][y] & MV_HORIZONTAL &&
3693 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3694 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3695 MovDir[x][y] = MV_NO_MOVING;
3699 else if (element == EL_ROBOT ||
3700 element == EL_SATELLITE ||
3701 element == EL_PENGUIN)
3703 int attr_x = -1, attr_y = -1;
3714 for (i = 0; i < MAX_PLAYERS; i++)
3716 struct PlayerInfo *player = &stored_player[i];
3717 int jx = player->jx, jy = player->jy;
3719 if (!player->active)
3723 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3731 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3737 if (element == EL_PENGUIN)
3740 static int xy[4][2] =
3748 for (i = 0; i < 4; i++)
3750 int ex = x + xy[i % 4][0];
3751 int ey = y + xy[i % 4][1];
3753 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3762 MovDir[x][y] = MV_NO_MOVING;
3764 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3765 else if (attr_x > x)
3766 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3768 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3769 else if (attr_y > y)
3770 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3772 if (element == EL_ROBOT)
3776 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3777 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3778 Moving2Blocked(x, y, &newx, &newy);
3780 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3781 MovDelay[x][y] = 8 + 8 * !RND(3);
3783 MovDelay[x][y] = 16;
3785 else if (element == EL_PENGUIN)
3791 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3793 boolean first_horiz = RND(2);
3794 int new_move_dir = MovDir[x][y];
3797 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3798 Moving2Blocked(x, y, &newx, &newy);
3800 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3804 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3805 Moving2Blocked(x, y, &newx, &newy);
3807 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3810 MovDir[x][y] = old_move_dir;
3814 else /* (element == EL_SATELLITE) */
3820 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3822 boolean first_horiz = RND(2);
3823 int new_move_dir = MovDir[x][y];
3826 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3827 Moving2Blocked(x, y, &newx, &newy);
3829 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3833 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3834 Moving2Blocked(x, y, &newx, &newy);
3836 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3839 MovDir[x][y] = old_move_dir;
3844 else if (move_pattern == MV_TURNING_LEFT ||
3845 move_pattern == MV_TURNING_RIGHT ||
3846 move_pattern == MV_TURNING_LEFT_RIGHT ||
3847 move_pattern == MV_TURNING_RIGHT_LEFT ||
3848 move_pattern == MV_TURNING_RANDOM ||
3849 move_pattern == MV_ALL_DIRECTIONS)
3851 boolean can_turn_left =
3852 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3853 boolean can_turn_right =
3854 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3856 if (move_pattern == MV_TURNING_LEFT)
3857 MovDir[x][y] = left_dir;
3858 else if (move_pattern == MV_TURNING_RIGHT)
3859 MovDir[x][y] = right_dir;
3860 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3861 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3862 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3863 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3864 else if (move_pattern == MV_TURNING_RANDOM)
3865 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3866 can_turn_right && !can_turn_left ? right_dir :
3867 RND(2) ? left_dir : right_dir);
3868 else if (can_turn_left && can_turn_right)
3869 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3870 else if (can_turn_left)
3871 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3872 else if (can_turn_right)
3873 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3875 MovDir[x][y] = back_dir;
3877 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3879 else if (move_pattern == MV_HORIZONTAL ||
3880 move_pattern == MV_VERTICAL)
3882 if (move_pattern & old_move_dir)
3883 MovDir[x][y] = back_dir;
3884 else if (move_pattern == MV_HORIZONTAL)
3885 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3886 else if (move_pattern == MV_VERTICAL)
3887 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3889 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3891 else if (move_pattern & MV_ANY_DIRECTION)
3893 MovDir[x][y] = move_pattern;
3894 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3896 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3898 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3899 MovDir[x][y] = left_dir;
3900 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3901 MovDir[x][y] = right_dir;
3903 if (MovDir[x][y] != old_move_dir)
3904 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3906 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3908 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3909 MovDir[x][y] = right_dir;
3910 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3911 MovDir[x][y] = left_dir;
3913 if (MovDir[x][y] != old_move_dir)
3914 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3916 else if (move_pattern == MV_TOWARDS_PLAYER ||
3917 move_pattern == MV_AWAY_FROM_PLAYER)
3919 int attr_x = -1, attr_y = -1;
3921 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3932 for (i = 0; i < MAX_PLAYERS; i++)
3934 struct PlayerInfo *player = &stored_player[i];
3935 int jx = player->jx, jy = player->jy;
3937 if (!player->active)
3941 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3949 MovDir[x][y] = MV_NO_MOVING;
3951 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3952 else if (attr_x > x)
3953 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3955 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3956 else if (attr_y > y)
3957 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3959 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3961 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3963 boolean first_horiz = RND(2);
3964 int new_move_dir = MovDir[x][y];
3967 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3968 Moving2Blocked(x, y, &newx, &newy);
3970 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3974 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3975 Moving2Blocked(x, y, &newx, &newy);
3977 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3980 MovDir[x][y] = old_move_dir;
3983 else if (move_pattern == MV_WHEN_PUSHED ||
3984 move_pattern == MV_WHEN_DROPPED)
3986 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3987 MovDir[x][y] = MV_NO_MOVING;
3991 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
3993 static int test_xy[7][2] =
4003 static int test_dir[7] =
4013 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4014 int move_preference = -1000000; /* start with very low preference */
4015 int new_move_dir = MV_NO_MOVING;
4016 int start_test = RND(4);
4019 for (i = 0; i < 4; i++)
4021 int move_dir = test_dir[start_test + i];
4022 int move_dir_preference;
4024 xx = x + test_xy[start_test + i][0];
4025 yy = y + test_xy[start_test + i][1];
4027 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4028 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4030 new_move_dir = move_dir;
4035 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4038 move_dir_preference = -1 * RunnerVisit[xx][yy];
4039 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4040 move_dir_preference = PlayerVisit[xx][yy];
4042 if (move_dir_preference > move_preference)
4044 /* prefer field that has not been visited for the longest time */
4045 move_preference = move_dir_preference;
4046 new_move_dir = move_dir;
4048 else if (move_dir_preference == move_preference &&
4049 move_dir == old_move_dir)
4051 /* prefer last direction when all directions are preferred equally */
4052 move_preference = move_dir_preference;
4053 new_move_dir = move_dir;
4057 MovDir[x][y] = new_move_dir;
4058 if (old_move_dir != new_move_dir)
4063 static void TurnRound(int x, int y)
4065 int direction = MovDir[x][y];
4068 GfxDir[x][y] = MovDir[x][y];
4074 GfxDir[x][y] = MovDir[x][y];
4077 if (direction != MovDir[x][y])
4082 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4085 GfxAction[x][y] = ACTION_WAITING;
4089 static boolean JustBeingPushed(int x, int y)
4093 for (i = 0; i < MAX_PLAYERS; i++)
4095 struct PlayerInfo *player = &stored_player[i];
4097 if (player->active && player->is_pushing && player->MovPos)
4099 int next_jx = player->jx + (player->jx - player->last_jx);
4100 int next_jy = player->jy + (player->jy - player->last_jy);
4102 if (x == next_jx && y == next_jy)
4110 void StartMoving(int x, int y)
4112 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4113 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4114 int element = Feld[x][y];
4120 if (MovDelay[x][y] == 0)
4121 GfxAction[x][y] = ACTION_DEFAULT;
4123 /* !!! this should be handled more generic (not only for mole) !!! */
4124 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4125 GfxAction[x][y] = ACTION_DEFAULT;
4128 if (CAN_FALL(element) && y < lev_fieldy - 1)
4130 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4131 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4132 if (JustBeingPushed(x, y))
4135 if (element == EL_QUICKSAND_FULL)
4137 if (IS_FREE(x, y + 1))
4139 InitMovingField(x, y, MV_DOWN);
4140 started_moving = TRUE;
4142 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4143 Store[x][y] = EL_ROCK;
4145 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4147 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4150 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4152 if (!MovDelay[x][y])
4153 MovDelay[x][y] = TILEY + 1;
4162 Feld[x][y] = EL_QUICKSAND_EMPTY;
4163 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4164 Store[x][y + 1] = Store[x][y];
4167 PlayLevelSoundAction(x, y, ACTION_FILLING);
4169 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4173 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4174 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4176 InitMovingField(x, y, MV_DOWN);
4177 started_moving = TRUE;
4179 Feld[x][y] = EL_QUICKSAND_FILLING;
4180 Store[x][y] = element;
4182 PlayLevelSoundAction(x, y, ACTION_FILLING);
4184 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4187 else if (element == EL_MAGIC_WALL_FULL)
4189 if (IS_FREE(x, y + 1))
4191 InitMovingField(x, y, MV_DOWN);
4192 started_moving = TRUE;
4194 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4195 Store[x][y] = EL_CHANGED(Store[x][y]);
4197 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4199 if (!MovDelay[x][y])
4200 MovDelay[x][y] = TILEY/4 + 1;
4209 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4210 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4211 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4215 else if (element == EL_BD_MAGIC_WALL_FULL)
4217 if (IS_FREE(x, y + 1))
4219 InitMovingField(x, y, MV_DOWN);
4220 started_moving = TRUE;
4222 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4223 Store[x][y] = EL_CHANGED2(Store[x][y]);
4225 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4227 if (!MovDelay[x][y])
4228 MovDelay[x][y] = TILEY/4 + 1;
4237 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4238 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4239 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4243 else if (CAN_PASS_MAGIC_WALL(element) &&
4244 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4245 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4247 InitMovingField(x, y, MV_DOWN);
4248 started_moving = TRUE;
4251 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4252 EL_BD_MAGIC_WALL_FILLING);
4253 Store[x][y] = element;
4256 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4258 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4263 InitMovingField(x, y, MV_DOWN);
4264 started_moving = TRUE;
4266 Store[x][y] = EL_ACID;
4268 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4269 GfxAction[x][y + 1] = ACTION_ACTIVE;
4273 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4274 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4275 (Feld[x][y + 1] == EL_BLOCKED)) ||
4276 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4277 CAN_SMASH(element) && WasJustFalling[x][y] &&
4278 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4282 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4283 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4284 WasJustMoving[x][y] && !Pushed[x][y + 1])
4286 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4287 WasJustMoving[x][y])
4292 /* this is needed for a special case not covered by calling "Impact()"
4293 from "ContinueMoving()": if an element moves to a tile directly below
4294 another element which was just falling on that tile (which was empty
4295 in the previous frame), the falling element above would just stop
4296 instead of smashing the element below (in previous version, the above
4297 element was just checked for "moving" instead of "falling", resulting
4298 in incorrect smashes caused by horizontal movement of the above
4299 element; also, the case of the player being the element to smash was
4300 simply not covered here... :-/ ) */
4303 WasJustMoving[x][y] = 0;
4304 WasJustFalling[x][y] = 0;
4309 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4311 if (MovDir[x][y] == MV_NO_MOVING)
4313 InitMovingField(x, y, MV_DOWN);
4314 started_moving = TRUE;
4317 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4319 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4320 MovDir[x][y] = MV_DOWN;
4322 InitMovingField(x, y, MV_DOWN);
4323 started_moving = TRUE;
4325 else if (element == EL_AMOEBA_DROP)
4327 Feld[x][y] = EL_AMOEBA_GROWING;
4328 Store[x][y] = EL_AMOEBA_WET;
4330 /* Store[x][y + 1] must be zero, because:
4331 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4334 #if OLD_GAME_BEHAVIOUR
4335 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4337 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4338 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4339 element != EL_DX_SUPABOMB)
4342 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4343 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4344 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4345 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4348 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4349 (IS_FREE(x - 1, y + 1) ||
4350 Feld[x - 1][y + 1] == EL_ACID));
4351 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4352 (IS_FREE(x + 1, y + 1) ||
4353 Feld[x + 1][y + 1] == EL_ACID));
4354 boolean can_fall_any = (can_fall_left || can_fall_right);
4355 boolean can_fall_both = (can_fall_left && can_fall_right);
4357 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4359 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4361 if (slippery_type == SLIPPERY_ONLY_LEFT)
4362 can_fall_right = FALSE;
4363 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4364 can_fall_left = FALSE;
4365 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4366 can_fall_right = FALSE;
4367 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4368 can_fall_left = FALSE;
4370 can_fall_any = (can_fall_left || can_fall_right);
4371 can_fall_both = (can_fall_left && can_fall_right);
4376 if (can_fall_both &&
4377 (game.emulation != EMU_BOULDERDASH &&
4378 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4379 can_fall_left = !(can_fall_right = RND(2));
4381 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4382 started_moving = TRUE;
4385 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4387 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4388 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4389 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4390 int belt_dir = game.belt_dir[belt_nr];
4392 if ((belt_dir == MV_LEFT && left_is_free) ||
4393 (belt_dir == MV_RIGHT && right_is_free))
4395 InitMovingField(x, y, belt_dir);
4396 started_moving = TRUE;
4398 GfxAction[x][y] = ACTION_DEFAULT;
4403 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4404 if (CAN_MOVE(element) && !started_moving)
4406 int move_pattern = element_info[element].move_pattern;
4409 Moving2Blocked(x, y, &newx, &newy);
4412 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4415 if ((element == EL_SATELLITE ||
4416 element == EL_BALLOON ||
4417 element == EL_SPRING)
4418 && JustBeingPushed(x, y))
4423 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4424 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4425 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4428 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4429 element, element_info[element].token_name,
4430 WasJustMoving[x][y],
4431 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4432 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4433 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4434 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4438 WasJustMoving[x][y] = 0;
4441 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4444 if (Feld[x][y] != element) /* element has changed */
4446 element = Feld[x][y];
4447 move_pattern = element_info[element].move_pattern;
4449 if (!CAN_MOVE(element))
4453 if (Feld[x][y] != element) /* element has changed */
4461 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4462 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4464 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4466 Moving2Blocked(x, y, &newx, &newy);
4467 if (Feld[newx][newy] == EL_BLOCKED)
4468 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4474 if (FrameCounter < 1 && x == 0 && y == 29)
4475 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4478 if (!MovDelay[x][y]) /* start new movement phase */
4480 /* all objects that can change their move direction after each step
4481 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4483 if (element != EL_YAMYAM &&
4484 element != EL_DARK_YAMYAM &&
4485 element != EL_PACMAN &&
4486 !(move_pattern & MV_ANY_DIRECTION) &&
4487 move_pattern != MV_TURNING_LEFT &&
4488 move_pattern != MV_TURNING_RIGHT &&
4489 move_pattern != MV_TURNING_LEFT_RIGHT &&
4490 move_pattern != MV_TURNING_RIGHT_LEFT &&
4491 move_pattern != MV_TURNING_RANDOM)
4496 if (FrameCounter < 1 && x == 0 && y == 29)
4497 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4500 if (MovDelay[x][y] && (element == EL_BUG ||
4501 element == EL_SPACESHIP ||
4502 element == EL_SP_SNIKSNAK ||
4503 element == EL_SP_ELECTRON ||
4504 element == EL_MOLE))
4505 DrawLevelField(x, y);
4509 if (MovDelay[x][y]) /* wait some time before next movement */
4514 if (element == EL_YAMYAM)
4517 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4518 DrawLevelElementAnimation(x, y, element);
4522 if (MovDelay[x][y]) /* element still has to wait some time */
4525 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4526 ResetGfxAnimation(x, y);
4530 if (GfxAction[x][y] != ACTION_WAITING)
4531 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4533 GfxAction[x][y] = ACTION_WAITING;
4537 if (element == EL_ROBOT ||
4539 element == EL_PACMAN ||
4541 element == EL_YAMYAM ||
4542 element == EL_DARK_YAMYAM)
4545 DrawLevelElementAnimation(x, y, element);
4547 DrawLevelElementAnimationIfNeeded(x, y, element);
4549 PlayLevelSoundAction(x, y, ACTION_WAITING);
4551 else if (element == EL_SP_ELECTRON)
4552 DrawLevelElementAnimationIfNeeded(x, y, element);
4553 else if (element == EL_DRAGON)
4556 int dir = MovDir[x][y];
4557 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4558 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4559 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4560 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4561 dir == MV_UP ? IMG_FLAMES_1_UP :
4562 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4563 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4566 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4569 GfxAction[x][y] = ACTION_ATTACKING;
4571 if (IS_PLAYER(x, y))
4572 DrawPlayerField(x, y);
4574 DrawLevelField(x, y);
4576 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4578 for (i = 1; i <= 3; i++)
4580 int xx = x + i * dx;
4581 int yy = y + i * dy;
4582 int sx = SCREENX(xx);
4583 int sy = SCREENY(yy);
4584 int flame_graphic = graphic + (i - 1);
4586 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4591 int flamed = MovingOrBlocked2Element(xx, yy);
4593 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4596 RemoveMovingField(xx, yy);
4598 Feld[xx][yy] = EL_FLAMES;
4599 if (IN_SCR_FIELD(sx, sy))
4601 DrawLevelFieldCrumbledSand(xx, yy);
4602 DrawGraphic(sx, sy, flame_graphic, frame);
4607 if (Feld[xx][yy] == EL_FLAMES)
4608 Feld[xx][yy] = EL_EMPTY;
4609 DrawLevelField(xx, yy);
4614 if (MovDelay[x][y]) /* element still has to wait some time */
4616 PlayLevelSoundAction(x, y, ACTION_WAITING);
4622 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4623 for all other elements GfxAction will be set by InitMovingField() */
4624 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4625 GfxAction[x][y] = ACTION_MOVING;
4629 /* now make next step */
4631 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4633 if (DONT_COLLIDE_WITH(element) &&
4634 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4635 !PLAYER_PROTECTED(newx, newy))
4638 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4642 /* player killed by element which is deadly when colliding with */
4644 KillHero(PLAYERINFO(newx, newy));
4649 else if ((element == EL_PENGUIN ||
4650 element == EL_ROBOT ||
4651 element == EL_SATELLITE ||
4652 element == EL_BALLOON ||
4653 IS_CUSTOM_ELEMENT(element)) &&
4654 IN_LEV_FIELD(newx, newy) &&
4655 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4658 Store[x][y] = EL_ACID;
4660 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4662 if (Feld[newx][newy] == EL_EXIT_OPEN)
4666 DrawLevelField(x, y);
4668 Feld[x][y] = EL_EMPTY;
4669 DrawLevelField(x, y);
4672 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4673 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4674 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4676 local_player->friends_still_needed--;
4677 if (!local_player->friends_still_needed &&
4678 !local_player->GameOver && AllPlayersGone)
4679 local_player->LevelSolved = local_player->GameOver = TRUE;
4683 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4685 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4686 DrawLevelField(newx, newy);
4688 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4690 else if (!IS_FREE(newx, newy))
4692 GfxAction[x][y] = ACTION_WAITING;
4694 if (IS_PLAYER(x, y))
4695 DrawPlayerField(x, y);
4697 DrawLevelField(x, y);
4702 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4704 if (IS_FOOD_PIG(Feld[newx][newy]))
4706 if (IS_MOVING(newx, newy))
4707 RemoveMovingField(newx, newy);
4710 Feld[newx][newy] = EL_EMPTY;
4711 DrawLevelField(newx, newy);
4714 PlayLevelSound(x, y, SND_PIG_DIGGING);
4716 else if (!IS_FREE(newx, newy))
4718 if (IS_PLAYER(x, y))
4719 DrawPlayerField(x, y);
4721 DrawLevelField(x, y);
4730 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4733 else if (IS_CUSTOM_ELEMENT(element) &&
4734 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4738 !IS_FREE(newx, newy)
4743 int new_element = Feld[newx][newy];
4746 printf("::: '%s' digs '%s' [%d]\n",
4747 element_info[element].token_name,
4748 element_info[Feld[newx][newy]].token_name,
4749 StorePlayer[newx][newy]);
4752 if (!IS_FREE(newx, newy))
4754 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4755 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4758 /* no element can dig solid indestructible elements */
4759 if (IS_INDESTRUCTIBLE(new_element) &&
4760 !IS_DIGGABLE(new_element) &&
4761 !IS_COLLECTIBLE(new_element))
4764 if (AmoebaNr[newx][newy] &&
4765 (new_element == EL_AMOEBA_FULL ||
4766 new_element == EL_BD_AMOEBA ||
4767 new_element == EL_AMOEBA_GROWING))
4769 AmoebaCnt[AmoebaNr[newx][newy]]--;
4770 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4773 if (IS_MOVING(newx, newy))
4774 RemoveMovingField(newx, newy);
4777 RemoveField(newx, newy);
4778 DrawLevelField(newx, newy);
4781 PlayLevelSoundAction(x, y, action);
4784 if (new_element == element_info[element].move_enter_element)
4785 element_info[element].can_leave_element = TRUE;
4787 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4789 RunnerVisit[x][y] = FrameCounter;
4790 PlayerVisit[x][y] /= 8; /* expire player visit path */
4796 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4798 if (!IS_FREE(newx, newy))
4800 if (IS_PLAYER(x, y))
4801 DrawPlayerField(x, y);
4803 DrawLevelField(x, y);
4809 boolean wanna_flame = !RND(10);
4810 int dx = newx - x, dy = newy - y;
4811 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4812 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4813 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4814 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4815 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4816 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4819 IS_CLASSIC_ENEMY(element1) ||
4820 IS_CLASSIC_ENEMY(element2)) &&
4821 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4822 element1 != EL_FLAMES && element2 != EL_FLAMES)
4825 ResetGfxAnimation(x, y);
4826 GfxAction[x][y] = ACTION_ATTACKING;
4829 if (IS_PLAYER(x, y))
4830 DrawPlayerField(x, y);
4832 DrawLevelField(x, y);
4834 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4836 MovDelay[x][y] = 50;
4838 Feld[newx][newy] = EL_FLAMES;
4839 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4840 Feld[newx1][newy1] = EL_FLAMES;
4841 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4842 Feld[newx2][newy2] = EL_FLAMES;
4848 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4849 Feld[newx][newy] == EL_DIAMOND)
4851 if (IS_MOVING(newx, newy))
4852 RemoveMovingField(newx, newy);
4855 Feld[newx][newy] = EL_EMPTY;
4856 DrawLevelField(newx, newy);
4859 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4861 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4862 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4864 if (AmoebaNr[newx][newy])
4866 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4867 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4868 Feld[newx][newy] == EL_BD_AMOEBA)
4869 AmoebaCnt[AmoebaNr[newx][newy]]--;
4872 if (IS_MOVING(newx, newy))
4873 RemoveMovingField(newx, newy);
4876 Feld[newx][newy] = EL_EMPTY;
4877 DrawLevelField(newx, newy);
4880 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4882 else if ((element == EL_PACMAN || element == EL_MOLE)
4883 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4885 if (AmoebaNr[newx][newy])
4887 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4888 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4889 Feld[newx][newy] == EL_BD_AMOEBA)
4890 AmoebaCnt[AmoebaNr[newx][newy]]--;
4893 if (element == EL_MOLE)
4895 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4896 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4898 ResetGfxAnimation(x, y);
4899 GfxAction[x][y] = ACTION_DIGGING;
4900 DrawLevelField(x, y);
4902 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4904 return; /* wait for shrinking amoeba */
4906 else /* element == EL_PACMAN */
4908 Feld[newx][newy] = EL_EMPTY;
4909 DrawLevelField(newx, newy);
4910 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4913 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4914 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4915 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4917 /* wait for shrinking amoeba to completely disappear */
4920 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4922 /* object was running against a wall */
4927 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4928 DrawLevelElementAnimation(x, y, element);
4930 if (element == EL_BUG ||
4931 element == EL_SPACESHIP ||
4932 element == EL_SP_SNIKSNAK)
4933 DrawLevelField(x, y);
4934 else if (element == EL_MOLE)
4935 DrawLevelField(x, y);
4936 else if (element == EL_BD_BUTTERFLY ||
4937 element == EL_BD_FIREFLY)
4938 DrawLevelElementAnimationIfNeeded(x, y, element);
4939 else if (element == EL_SATELLITE)
4940 DrawLevelElementAnimationIfNeeded(x, y, element);
4941 else if (element == EL_SP_ELECTRON)
4942 DrawLevelElementAnimationIfNeeded(x, y, element);
4945 if (DONT_TOUCH(element))
4946 TestIfBadThingTouchesHero(x, y);
4949 PlayLevelSoundAction(x, y, ACTION_WAITING);
4955 InitMovingField(x, y, MovDir[x][y]);
4957 PlayLevelSoundAction(x, y, ACTION_MOVING);
4961 ContinueMoving(x, y);
4964 void ContinueMoving(int x, int y)
4966 int element = Feld[x][y];
4967 struct ElementInfo *ei = &element_info[element];
4968 int direction = MovDir[x][y];
4969 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4970 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4971 int newx = x + dx, newy = y + dy;
4973 int nextx = newx + dx, nexty = newy + dy;
4975 boolean pushed = Pushed[x][y];
4977 MovPos[x][y] += getElementMoveStepsize(x, y);
4979 if (pushed) /* special case: moving object pushed by player */
4980 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4982 if (ABS(MovPos[x][y]) < TILEX)
4984 DrawLevelField(x, y);
4986 return; /* element is still moving */
4989 /* element reached destination field */
4991 Feld[x][y] = EL_EMPTY;
4992 Feld[newx][newy] = element;
4993 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4995 if (element == EL_MOLE)
4997 Feld[x][y] = EL_SAND;
4999 DrawLevelFieldCrumbledSandNeighbours(x, y);
5001 else if (element == EL_QUICKSAND_FILLING)
5003 element = Feld[newx][newy] = get_next_element(element);
5004 Store[newx][newy] = Store[x][y];
5006 else if (element == EL_QUICKSAND_EMPTYING)
5008 Feld[x][y] = get_next_element(element);
5009 element = Feld[newx][newy] = Store[x][y];
5011 else if (element == EL_MAGIC_WALL_FILLING)
5013 element = Feld[newx][newy] = get_next_element(element);
5014 if (!game.magic_wall_active)
5015 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5016 Store[newx][newy] = Store[x][y];
5018 else if (element == EL_MAGIC_WALL_EMPTYING)
5020 Feld[x][y] = get_next_element(element);
5021 if (!game.magic_wall_active)
5022 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5023 element = Feld[newx][newy] = Store[x][y];
5025 else if (element == EL_BD_MAGIC_WALL_FILLING)
5027 element = Feld[newx][newy] = get_next_element(element);
5028 if (!game.magic_wall_active)
5029 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5030 Store[newx][newy] = Store[x][y];
5032 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5034 Feld[x][y] = get_next_element(element);
5035 if (!game.magic_wall_active)
5036 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5037 element = Feld[newx][newy] = Store[x][y];
5039 else if (element == EL_AMOEBA_DROPPING)
5041 Feld[x][y] = get_next_element(element);
5042 element = Feld[newx][newy] = Store[x][y];
5044 else if (element == EL_SOKOBAN_OBJECT)
5047 Feld[x][y] = Back[x][y];
5049 if (Back[newx][newy])
5050 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5052 Back[x][y] = Back[newx][newy] = 0;
5054 else if (Store[x][y] == EL_ACID)
5056 element = Feld[newx][newy] = EL_ACID;
5060 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5061 MovDelay[newx][newy] = 0;
5063 /* copy element change control values to new field */
5064 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5065 ChangePage[newx][newy] = ChangePage[x][y];
5066 Changed[newx][newy] = Changed[x][y];
5067 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5069 ChangeDelay[x][y] = 0;
5070 ChangePage[x][y] = -1;
5071 Changed[x][y] = CE_BITMASK_DEFAULT;
5072 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5074 /* copy animation control values to new field */
5075 GfxFrame[newx][newy] = GfxFrame[x][y];
5076 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5077 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5078 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5080 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5082 ResetGfxAnimation(x, y); /* reset animation values for old field */
5085 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5086 ei->move_leave_element != EL_EMPTY &&
5087 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5088 ei->can_leave_element_last))
5090 Feld[x][y] = ei->move_leave_element;
5091 InitField(x, y, FALSE);
5093 if (GFX_CRUMBLED(Feld[x][y]))
5094 DrawLevelFieldCrumbledSandNeighbours(x, y);
5097 ei->can_leave_element_last = ei->can_leave_element;
5098 ei->can_leave_element = FALSE;
5102 /* 2.1.1 (does not work correctly for spring) */
5103 if (!CAN_MOVE(element))
5104 MovDir[newx][newy] = 0;
5108 /* (does not work for falling objects that slide horizontally) */
5109 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5110 MovDir[newx][newy] = 0;
5113 if (!CAN_MOVE(element) ||
5114 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5115 MovDir[newx][newy] = 0;
5118 if (!CAN_MOVE(element) ||
5119 (CAN_FALL(element) && direction == MV_DOWN))
5120 GfxDir[x][y] = MovDir[newx][newy] = 0;
5125 DrawLevelField(x, y);
5126 DrawLevelField(newx, newy);
5128 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5130 /* prevent pushed element from moving on in pushed direction */
5131 if (pushed && CAN_MOVE(element) &&
5132 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5133 !(element_info[element].move_pattern & direction))
5134 TurnRound(newx, newy);
5136 if (!pushed) /* special case: moving object pushed by player */
5138 WasJustMoving[newx][newy] = 3;
5140 if (CAN_FALL(element) && direction == MV_DOWN)
5141 WasJustFalling[newx][newy] = 3;
5144 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5146 TestIfBadThingTouchesHero(newx, newy);
5147 TestIfBadThingTouchesFriend(newx, newy);
5149 if (!IS_CUSTOM_ELEMENT(element))
5150 TestIfBadThingTouchesOtherBadThing(newx, newy);
5152 else if (element == EL_PENGUIN)
5153 TestIfFriendTouchesBadThing(newx, newy);
5155 if (CAN_FALL(element) && direction == MV_DOWN &&
5156 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5160 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5164 if (ChangePage[newx][newy] != -1) /* delayed change */
5165 ChangeElement(newx, newy, ChangePage[newx][newy]);
5170 TestIfElementHitsCustomElement(newx, newy, direction);
5174 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5176 int hitting_element = Feld[newx][newy];
5178 /* !!! fix side (direction) orientation here and elsewhere !!! */
5179 CheckElementSideChange(newx, newy, hitting_element,
5180 direction, CE_HITTING_SOMETHING, -1);
5183 if (IN_LEV_FIELD(nextx, nexty))
5185 static int opposite_directions[] =
5192 int move_dir_bit = MV_DIR_BIT(direction);
5193 int opposite_direction = opposite_directions[move_dir_bit];
5194 int hitting_side = direction;
5195 int touched_side = opposite_direction;
5196 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5197 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5198 MovDir[nextx][nexty] != direction ||
5199 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5205 CheckElementSideChange(nextx, nexty, touched_element,
5206 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5208 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5209 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5211 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5213 struct ElementChangeInfo *change =
5214 &element_info[hitting_element].change_page[i];
5216 if (change->can_change &&
5217 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5218 change->sides & touched_side &&
5219 change->trigger_element == touched_element)
5221 CheckElementSideChange(newx, newy, hitting_element,
5222 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5228 if (IS_CUSTOM_ELEMENT(touched_element) &&
5229 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5231 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5233 struct ElementChangeInfo *change =
5234 &element_info[touched_element].change_page[i];
5236 if (change->can_change &&
5237 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5238 change->sides & hitting_side &&
5239 change->trigger_element == hitting_element)
5241 CheckElementSideChange(nextx, nexty, touched_element,
5242 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5253 TestIfPlayerTouchesCustomElement(newx, newy);
5254 TestIfElementTouchesCustomElement(newx, newy);
5257 int AmoebeNachbarNr(int ax, int ay)
5260 int element = Feld[ax][ay];
5262 static int xy[4][2] =
5270 for (i = 0; i < 4; i++)
5272 int x = ax + xy[i][0];
5273 int y = ay + xy[i][1];
5275 if (!IN_LEV_FIELD(x, y))
5278 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5279 group_nr = AmoebaNr[x][y];
5285 void AmoebenVereinigen(int ax, int ay)
5287 int i, x, y, xx, yy;
5288 int new_group_nr = AmoebaNr[ax][ay];
5289 static int xy[4][2] =
5297 if (new_group_nr == 0)
5300 for (i = 0; i < 4; i++)
5305 if (!IN_LEV_FIELD(x, y))
5308 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5309 Feld[x][y] == EL_BD_AMOEBA ||
5310 Feld[x][y] == EL_AMOEBA_DEAD) &&
5311 AmoebaNr[x][y] != new_group_nr)
5313 int old_group_nr = AmoebaNr[x][y];
5315 if (old_group_nr == 0)
5318 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5319 AmoebaCnt[old_group_nr] = 0;
5320 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5321 AmoebaCnt2[old_group_nr] = 0;
5323 for (yy = 0; yy < lev_fieldy; yy++)
5325 for (xx = 0; xx < lev_fieldx; xx++)
5327 if (AmoebaNr[xx][yy] == old_group_nr)
5328 AmoebaNr[xx][yy] = new_group_nr;
5335 void AmoebeUmwandeln(int ax, int ay)
5339 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5341 int group_nr = AmoebaNr[ax][ay];
5346 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5347 printf("AmoebeUmwandeln(): This should never happen!\n");
5352 for (y = 0; y < lev_fieldy; y++)
5354 for (x = 0; x < lev_fieldx; x++)
5356 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5359 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5363 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5364 SND_AMOEBA_TURNING_TO_GEM :
5365 SND_AMOEBA_TURNING_TO_ROCK));
5370 static int xy[4][2] =
5378 for (i = 0; i < 4; i++)
5383 if (!IN_LEV_FIELD(x, y))
5386 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5388 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5389 SND_AMOEBA_TURNING_TO_GEM :
5390 SND_AMOEBA_TURNING_TO_ROCK));
5397 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5400 int group_nr = AmoebaNr[ax][ay];
5401 boolean done = FALSE;
5406 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5407 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5412 for (y = 0; y < lev_fieldy; y++)
5414 for (x = 0; x < lev_fieldx; x++)
5416 if (AmoebaNr[x][y] == group_nr &&
5417 (Feld[x][y] == EL_AMOEBA_DEAD ||
5418 Feld[x][y] == EL_BD_AMOEBA ||
5419 Feld[x][y] == EL_AMOEBA_GROWING))
5422 Feld[x][y] = new_element;
5423 InitField(x, y, FALSE);
5424 DrawLevelField(x, y);
5431 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5432 SND_BD_AMOEBA_TURNING_TO_ROCK :
5433 SND_BD_AMOEBA_TURNING_TO_GEM));
5436 void AmoebeWaechst(int x, int y)
5438 static unsigned long sound_delay = 0;
5439 static unsigned long sound_delay_value = 0;
5441 if (!MovDelay[x][y]) /* start new growing cycle */
5445 if (DelayReached(&sound_delay, sound_delay_value))
5448 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5450 if (Store[x][y] == EL_BD_AMOEBA)
5451 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5453 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5455 sound_delay_value = 30;
5459 if (MovDelay[x][y]) /* wait some time before growing bigger */
5462 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5464 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5465 6 - MovDelay[x][y]);
5467 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5470 if (!MovDelay[x][y])
5472 Feld[x][y] = Store[x][y];
5474 DrawLevelField(x, y);
5479 void AmoebaDisappearing(int x, int y)
5481 static unsigned long sound_delay = 0;
5482 static unsigned long sound_delay_value = 0;
5484 if (!MovDelay[x][y]) /* start new shrinking cycle */
5488 if (DelayReached(&sound_delay, sound_delay_value))
5489 sound_delay_value = 30;
5492 if (MovDelay[x][y]) /* wait some time before shrinking */
5495 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5497 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5498 6 - MovDelay[x][y]);
5500 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5503 if (!MovDelay[x][y])
5505 Feld[x][y] = EL_EMPTY;
5506 DrawLevelField(x, y);
5508 /* don't let mole enter this field in this cycle;
5509 (give priority to objects falling to this field from above) */
5515 void AmoebeAbleger(int ax, int ay)
5518 int element = Feld[ax][ay];
5519 int graphic = el2img(element);
5520 int newax = ax, neway = ay;
5521 static int xy[4][2] =
5529 if (!level.amoeba_speed)
5531 Feld[ax][ay] = EL_AMOEBA_DEAD;
5532 DrawLevelField(ax, ay);
5536 if (IS_ANIMATED(graphic))
5537 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5539 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5540 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5542 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5545 if (MovDelay[ax][ay])
5549 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5552 int x = ax + xy[start][0];
5553 int y = ay + xy[start][1];
5555 if (!IN_LEV_FIELD(x, y))
5558 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5559 if (IS_FREE(x, y) ||
5560 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5566 if (newax == ax && neway == ay)
5569 else /* normal or "filled" (BD style) amoeba */
5572 boolean waiting_for_player = FALSE;
5574 for (i = 0; i < 4; i++)
5576 int j = (start + i) % 4;
5577 int x = ax + xy[j][0];
5578 int y = ay + xy[j][1];
5580 if (!IN_LEV_FIELD(x, y))
5583 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5584 if (IS_FREE(x, y) ||
5585 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5591 else if (IS_PLAYER(x, y))
5592 waiting_for_player = TRUE;
5595 if (newax == ax && neway == ay) /* amoeba cannot grow */
5597 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5599 Feld[ax][ay] = EL_AMOEBA_DEAD;
5600 DrawLevelField(ax, ay);
5601 AmoebaCnt[AmoebaNr[ax][ay]]--;
5603 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5605 if (element == EL_AMOEBA_FULL)
5606 AmoebeUmwandeln(ax, ay);
5607 else if (element == EL_BD_AMOEBA)
5608 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5613 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5615 /* amoeba gets larger by growing in some direction */
5617 int new_group_nr = AmoebaNr[ax][ay];
5620 if (new_group_nr == 0)
5622 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5623 printf("AmoebeAbleger(): This should never happen!\n");
5628 AmoebaNr[newax][neway] = new_group_nr;
5629 AmoebaCnt[new_group_nr]++;
5630 AmoebaCnt2[new_group_nr]++;
5632 /* if amoeba touches other amoeba(s) after growing, unify them */
5633 AmoebenVereinigen(newax, neway);
5635 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5637 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5643 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5644 (neway == lev_fieldy - 1 && newax != ax))
5646 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5647 Store[newax][neway] = element;
5649 else if (neway == ay)
5651 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5653 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5655 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5660 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5661 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5662 Store[ax][ay] = EL_AMOEBA_DROP;
5663 ContinueMoving(ax, ay);
5667 DrawLevelField(newax, neway);
5670 void Life(int ax, int ay)
5673 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5675 int element = Feld[ax][ay];
5676 int graphic = el2img(element);
5677 boolean changed = FALSE;
5679 if (IS_ANIMATED(graphic))
5680 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5685 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5686 MovDelay[ax][ay] = life_time;
5688 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5691 if (MovDelay[ax][ay])
5695 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5697 int xx = ax+x1, yy = ay+y1;
5700 if (!IN_LEV_FIELD(xx, yy))
5703 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5705 int x = xx+x2, y = yy+y2;
5707 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5710 if (((Feld[x][y] == element ||
5711 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5713 (IS_FREE(x, y) && Stop[x][y]))
5717 if (xx == ax && yy == ay) /* field in the middle */
5719 if (nachbarn < life[0] || nachbarn > life[1])
5721 Feld[xx][yy] = EL_EMPTY;
5723 DrawLevelField(xx, yy);
5724 Stop[xx][yy] = TRUE;
5728 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5729 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5730 { /* free border field */
5731 if (nachbarn >= life[2] && nachbarn <= life[3])
5733 Feld[xx][yy] = element;
5734 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5736 DrawLevelField(xx, yy);
5737 Stop[xx][yy] = TRUE;
5744 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5745 SND_GAME_OF_LIFE_GROWING);
5748 static void InitRobotWheel(int x, int y)
5750 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5753 static void RunRobotWheel(int x, int y)
5755 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5758 static void StopRobotWheel(int x, int y)
5760 if (ZX == x && ZY == y)
5764 static void InitTimegateWheel(int x, int y)
5766 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5769 static void RunTimegateWheel(int x, int y)
5771 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5774 void CheckExit(int x, int y)
5776 if (local_player->gems_still_needed > 0 ||
5777 local_player->sokobanfields_still_needed > 0 ||
5778 local_player->lights_still_needed > 0)
5780 int element = Feld[x][y];
5781 int graphic = el2img(element);
5783 if (IS_ANIMATED(graphic))
5784 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5789 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5792 Feld[x][y] = EL_EXIT_OPENING;
5794 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5797 void CheckExitSP(int x, int y)
5799 if (local_player->gems_still_needed > 0)
5801 int element = Feld[x][y];
5802 int graphic = el2img(element);
5804 if (IS_ANIMATED(graphic))
5805 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5810 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5813 Feld[x][y] = EL_SP_EXIT_OPENING;
5815 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5818 static void CloseAllOpenTimegates()
5822 for (y = 0; y < lev_fieldy; y++)
5824 for (x = 0; x < lev_fieldx; x++)
5826 int element = Feld[x][y];
5828 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5830 Feld[x][y] = EL_TIMEGATE_CLOSING;
5832 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5834 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5841 void EdelsteinFunkeln(int x, int y)
5843 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5846 if (Feld[x][y] == EL_BD_DIAMOND)
5849 if (MovDelay[x][y] == 0) /* next animation frame */
5850 MovDelay[x][y] = 11 * !SimpleRND(500);
5852 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5856 if (setup.direct_draw && MovDelay[x][y])
5857 SetDrawtoField(DRAW_BUFFERED);
5859 DrawLevelElementAnimation(x, y, Feld[x][y]);
5861 if (MovDelay[x][y] != 0)
5863 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5864 10 - MovDelay[x][y]);
5866 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5868 if (setup.direct_draw)
5872 dest_x = FX + SCREENX(x) * TILEX;
5873 dest_y = FY + SCREENY(y) * TILEY;
5875 BlitBitmap(drawto_field, window,
5876 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5877 SetDrawtoField(DRAW_DIRECT);
5883 void MauerWaechst(int x, int y)
5887 if (!MovDelay[x][y]) /* next animation frame */
5888 MovDelay[x][y] = 3 * delay;
5890 if (MovDelay[x][y]) /* wait some time before next frame */
5894 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5896 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5897 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5899 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5902 if (!MovDelay[x][y])
5904 if (MovDir[x][y] == MV_LEFT)
5906 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5907 DrawLevelField(x - 1, y);
5909 else if (MovDir[x][y] == MV_RIGHT)
5911 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5912 DrawLevelField(x + 1, y);
5914 else if (MovDir[x][y] == MV_UP)
5916 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5917 DrawLevelField(x, y - 1);
5921 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5922 DrawLevelField(x, y + 1);
5925 Feld[x][y] = Store[x][y];
5927 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5928 DrawLevelField(x, y);
5933 void MauerAbleger(int ax, int ay)
5935 int element = Feld[ax][ay];
5936 int graphic = el2img(element);
5937 boolean oben_frei = FALSE, unten_frei = FALSE;
5938 boolean links_frei = FALSE, rechts_frei = FALSE;
5939 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5940 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5941 boolean new_wall = FALSE;
5943 if (IS_ANIMATED(graphic))
5944 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5946 if (!MovDelay[ax][ay]) /* start building new wall */
5947 MovDelay[ax][ay] = 6;
5949 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5952 if (MovDelay[ax][ay])
5956 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5958 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5960 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5962 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5965 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5966 element == EL_EXPANDABLE_WALL_ANY)
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_UP;
5973 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5974 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5975 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5980 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5981 Store[ax][ay+1] = element;
5982 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5983 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5984 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5985 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5990 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5991 element == EL_EXPANDABLE_WALL_ANY ||
5992 element == EL_EXPANDABLE_WALL)
5996 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5997 Store[ax-1][ay] = element;
5998 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5999 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6000 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6001 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6007 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6008 Store[ax+1][ay] = element;
6009 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6010 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6011 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6012 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6017 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6018 DrawLevelField(ax, ay);
6020 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6022 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6023 unten_massiv = TRUE;
6024 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6025 links_massiv = TRUE;
6026 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6027 rechts_massiv = TRUE;
6029 if (((oben_massiv && unten_massiv) ||
6030 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6031 element == EL_EXPANDABLE_WALL) &&
6032 ((links_massiv && rechts_massiv) ||
6033 element == EL_EXPANDABLE_WALL_VERTICAL))
6034 Feld[ax][ay] = EL_WALL;
6038 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6040 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6044 void CheckForDragon(int x, int y)
6047 boolean dragon_found = FALSE;
6048 static int xy[4][2] =
6056 for (i = 0; i < 4; i++)
6058 for (j = 0; j < 4; j++)
6060 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6062 if (IN_LEV_FIELD(xx, yy) &&
6063 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6065 if (Feld[xx][yy] == EL_DRAGON)
6066 dragon_found = TRUE;
6075 for (i = 0; i < 4; i++)
6077 for (j = 0; j < 3; j++)
6079 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6081 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6083 Feld[xx][yy] = EL_EMPTY;
6084 DrawLevelField(xx, yy);
6093 static void InitBuggyBase(int x, int y)
6095 int element = Feld[x][y];
6096 int activating_delay = FRAMES_PER_SECOND / 4;
6099 (element == EL_SP_BUGGY_BASE ?
6100 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6101 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6103 element == EL_SP_BUGGY_BASE_ACTIVE ?
6104 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6107 static void WarnBuggyBase(int x, int y)
6110 static int xy[4][2] =
6118 for (i = 0; i < 4; i++)
6120 int xx = x + xy[i][0], yy = y + xy[i][1];
6122 if (IS_PLAYER(xx, yy))
6124 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6131 static void InitTrap(int x, int y)
6133 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6136 static void ActivateTrap(int x, int y)
6138 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6141 static void ChangeActiveTrap(int x, int y)
6143 int graphic = IMG_TRAP_ACTIVE;
6145 /* if new animation frame was drawn, correct crumbled sand border */
6146 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6147 DrawLevelFieldCrumbledSand(x, y);
6150 static void ChangeElementNowExt(int x, int y, int target_element)
6152 int previous_move_direction = MovDir[x][y];
6154 /* check if element under player changes from accessible to unaccessible
6155 (needed for special case of dropping element which then changes) */
6156 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6157 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6164 Feld[x][y] = target_element;
6166 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6168 ResetGfxAnimation(x, y);
6169 ResetRandomAnimationValue(x, y);
6171 if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
6172 MovDir[x][y] = previous_move_direction;
6174 InitField(x, y, FALSE);
6175 if (CAN_MOVE(Feld[x][y]))
6178 DrawLevelField(x, y);
6180 if (GFX_CRUMBLED(Feld[x][y]))
6181 DrawLevelFieldCrumbledSandNeighbours(x, y);
6183 TestIfBadThingTouchesHero(x, y);
6184 TestIfPlayerTouchesCustomElement(x, y);
6185 TestIfElementTouchesCustomElement(x, y);
6187 if (ELEM_IS_PLAYER(target_element))
6188 RelocatePlayer(x, y, target_element);
6191 static boolean ChangeElementNow(int x, int y, int element, int page)
6193 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6195 /* always use default change event to prevent running into a loop */
6196 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6197 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6199 /* do not change already changed elements with same change event */
6201 if (Changed[x][y] & ChangeEvent[x][y])
6208 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6210 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6212 if (change->explode)
6219 if (change->use_content)
6221 boolean complete_change = TRUE;
6222 boolean can_change[3][3];
6225 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6227 boolean half_destructible;
6228 int ex = x + xx - 1;
6229 int ey = y + yy - 1;
6232 can_change[xx][yy] = TRUE;
6234 if (ex == x && ey == y) /* do not check changing element itself */
6237 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6239 can_change[xx][yy] = FALSE; /* do not change empty borders */
6244 if (!IN_LEV_FIELD(ex, ey))
6246 can_change[xx][yy] = FALSE;
6247 complete_change = FALSE;
6254 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6255 e = MovingOrBlocked2Element(ex, ey);
6257 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6259 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6260 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6261 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6263 can_change[xx][yy] = FALSE;
6264 complete_change = FALSE;
6268 if (!change->only_complete || complete_change)
6270 boolean something_has_changed = FALSE;
6272 if (change->only_complete && change->use_random_change &&
6273 RND(100) < change->random)
6276 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6278 int ex = x + xx - 1;
6279 int ey = y + yy - 1;
6281 if (can_change[xx][yy] && (!change->use_random_change ||
6282 RND(100) < change->random))
6284 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6285 RemoveMovingField(ex, ey);
6287 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6289 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6291 something_has_changed = TRUE;
6293 /* for symmetry reasons, freeze newly created border elements */
6294 if (ex != x || ey != y)
6295 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6299 if (something_has_changed)
6300 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6305 ChangeElementNowExt(x, y, change->target_element);
6307 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6313 static void ChangeElement(int x, int y, int page)
6315 int element = MovingOrBlocked2Element(x, y);
6316 struct ElementInfo *ei = &element_info[element];
6317 struct ElementChangeInfo *change = &ei->change_page[page];
6321 if (!CAN_CHANGE(element))
6324 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6325 x, y, element, element_info[element].token_name);
6326 printf("ChangeElement(): This should never happen!\n");
6332 if (ChangeDelay[x][y] == 0) /* initialize element change */
6334 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6335 RND(change->delay_random * change->delay_frames)) + 1;
6337 ResetGfxAnimation(x, y);
6338 ResetRandomAnimationValue(x, y);
6340 if (change->pre_change_function)
6341 change->pre_change_function(x, y);
6344 ChangeDelay[x][y]--;
6346 if (ChangeDelay[x][y] != 0) /* continue element change */
6348 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6350 if (IS_ANIMATED(graphic))
6351 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6353 if (change->change_function)
6354 change->change_function(x, y);
6356 else /* finish element change */
6358 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6360 page = ChangePage[x][y];
6361 ChangePage[x][y] = -1;
6365 if (IS_MOVING(x, y) && !change->explode)
6367 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6370 ChangeDelay[x][y] = 1; /* try change after next move step */
6371 ChangePage[x][y] = page; /* remember page to use for change */
6376 if (ChangeElementNow(x, y, element, page))
6378 if (change->post_change_function)
6379 change->post_change_function(x, y);
6384 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6385 int trigger_element,
6391 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6394 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6396 int element = EL_CUSTOM_START + i;
6398 boolean change_element = FALSE;
6401 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6404 for (j = 0; j < element_info[element].num_change_pages; j++)
6406 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6408 if (change->can_change &&
6410 change->events & CH_EVENT_BIT(trigger_event) &&
6412 change->sides & trigger_side &&
6413 change->trigger_element == trigger_element)
6416 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6417 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6418 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6421 change_element = TRUE;
6428 if (!change_element)
6431 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6434 if (x == lx && y == ly) /* do not change trigger element itself */
6438 if (Feld[x][y] == element)
6440 ChangeDelay[x][y] = 1;
6441 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6442 ChangeElement(x, y, page);
6450 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6453 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6457 static boolean CheckElementSideChange(int x, int y, int element, int side,
6458 int trigger_event, int page)
6460 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6463 if (Feld[x][y] == EL_BLOCKED)
6465 Blocked2Moving(x, y, &x, &y);
6466 element = Feld[x][y];
6470 page = element_info[element].event_page_nr[trigger_event];
6472 if (!(element_info[element].change_page[page].sides & side))
6475 ChangeDelay[x][y] = 1;
6476 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6477 ChangeElement(x, y, page);
6482 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6484 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6487 static void PlayPlayerSound(struct PlayerInfo *player)
6489 int jx = player->jx, jy = player->jy;
6490 int element = player->element_nr;
6491 int last_action = player->last_action_waiting;
6492 int action = player->action_waiting;
6494 if (player->is_waiting)
6496 if (action != last_action)
6497 PlayLevelSoundElementAction(jx, jy, element, action);
6499 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6503 if (action != last_action)
6504 StopSound(element_info[element].sound[last_action]);
6506 if (last_action == ACTION_SLEEPING)
6507 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6511 static void PlayAllPlayersSound()
6515 for (i = 0; i < MAX_PLAYERS; i++)
6516 if (stored_player[i].active)
6517 PlayPlayerSound(&stored_player[i]);
6520 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6522 boolean last_waiting = player->is_waiting;
6523 int move_dir = player->MovDir;
6525 player->last_action_waiting = player->action_waiting;
6529 if (!last_waiting) /* not waiting -> waiting */
6531 player->is_waiting = TRUE;
6533 player->frame_counter_bored =
6535 game.player_boring_delay_fixed +
6536 SimpleRND(game.player_boring_delay_random);
6537 player->frame_counter_sleeping =
6539 game.player_sleeping_delay_fixed +
6540 SimpleRND(game.player_sleeping_delay_random);
6542 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6545 if (game.player_sleeping_delay_fixed +
6546 game.player_sleeping_delay_random > 0 &&
6547 player->anim_delay_counter == 0 &&
6548 player->post_delay_counter == 0 &&
6549 FrameCounter >= player->frame_counter_sleeping)
6550 player->is_sleeping = TRUE;
6551 else if (game.player_boring_delay_fixed +
6552 game.player_boring_delay_random > 0 &&
6553 FrameCounter >= player->frame_counter_bored)
6554 player->is_bored = TRUE;
6556 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6557 player->is_bored ? ACTION_BORING :
6560 if (player->is_sleeping)
6562 if (player->num_special_action_sleeping > 0)
6564 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6566 int last_special_action = player->special_action_sleeping;
6567 int num_special_action = player->num_special_action_sleeping;
6568 int special_action =
6569 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6570 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6571 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6572 last_special_action + 1 : ACTION_SLEEPING);
6573 int special_graphic =
6574 el_act_dir2img(player->element_nr, special_action, move_dir);
6576 player->anim_delay_counter =
6577 graphic_info[special_graphic].anim_delay_fixed +
6578 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6579 player->post_delay_counter =
6580 graphic_info[special_graphic].post_delay_fixed +
6581 SimpleRND(graphic_info[special_graphic].post_delay_random);
6583 player->special_action_sleeping = special_action;
6586 if (player->anim_delay_counter > 0)
6588 player->action_waiting = player->special_action_sleeping;
6589 player->anim_delay_counter--;
6591 else if (player->post_delay_counter > 0)
6593 player->post_delay_counter--;
6597 else if (player->is_bored)
6599 if (player->num_special_action_bored > 0)
6601 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6603 int special_action =
6604 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6605 int special_graphic =
6606 el_act_dir2img(player->element_nr, special_action, move_dir);
6608 player->anim_delay_counter =
6609 graphic_info[special_graphic].anim_delay_fixed +
6610 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6611 player->post_delay_counter =
6612 graphic_info[special_graphic].post_delay_fixed +
6613 SimpleRND(graphic_info[special_graphic].post_delay_random);
6615 player->special_action_bored = special_action;
6618 if (player->anim_delay_counter > 0)
6620 player->action_waiting = player->special_action_bored;
6621 player->anim_delay_counter--;
6623 else if (player->post_delay_counter > 0)
6625 player->post_delay_counter--;
6630 else if (last_waiting) /* waiting -> not waiting */
6632 player->is_waiting = FALSE;
6633 player->is_bored = FALSE;
6634 player->is_sleeping = FALSE;
6636 player->frame_counter_bored = -1;
6637 player->frame_counter_sleeping = -1;
6639 player->anim_delay_counter = 0;
6640 player->post_delay_counter = 0;
6642 player->action_waiting = ACTION_DEFAULT;
6644 player->special_action_bored = ACTION_DEFAULT;
6645 player->special_action_sleeping = ACTION_DEFAULT;
6650 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6653 static byte stored_player_action[MAX_PLAYERS];
6654 static int num_stored_actions = 0;
6656 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6657 int left = player_action & JOY_LEFT;
6658 int right = player_action & JOY_RIGHT;
6659 int up = player_action & JOY_UP;
6660 int down = player_action & JOY_DOWN;
6661 int button1 = player_action & JOY_BUTTON_1;
6662 int button2 = player_action & JOY_BUTTON_2;
6663 int dx = (left ? -1 : right ? 1 : 0);
6664 int dy = (up ? -1 : down ? 1 : 0);
6667 stored_player_action[player->index_nr] = 0;
6668 num_stored_actions++;
6672 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6675 if (!player->active || tape.pausing)
6679 printf("::: [%d %d %d %d] [%d %d]\n",
6680 left, right, up, down, button1, button2);
6686 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6690 snapped = SnapField(player, dx, dy);
6694 dropped = DropElement(player);
6696 moved = MovePlayer(player, dx, dy);
6699 if (tape.single_step && tape.recording && !tape.pausing)
6701 if (button1 || (dropped && !moved))
6703 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6704 SnapField(player, 0, 0); /* stop snapping */
6708 SetPlayerWaiting(player, FALSE);
6711 return player_action;
6713 stored_player_action[player->index_nr] = player_action;
6719 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6722 /* no actions for this player (no input at player's configured device) */
6724 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6725 SnapField(player, 0, 0);
6726 CheckGravityMovement(player);
6728 if (player->MovPos == 0)
6729 SetPlayerWaiting(player, TRUE);
6731 if (player->MovPos == 0) /* needed for tape.playing */
6732 player->is_moving = FALSE;
6734 player->is_dropping = FALSE;
6740 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6742 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6744 TapeRecordAction(stored_player_action);
6745 num_stored_actions = 0;
6752 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6754 static byte stored_player_action[MAX_PLAYERS];
6755 static int num_stored_actions = 0;
6756 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6757 int left = player_action & JOY_LEFT;
6758 int right = player_action & JOY_RIGHT;
6759 int up = player_action & JOY_UP;
6760 int down = player_action & JOY_DOWN;
6761 int button1 = player_action & JOY_BUTTON_1;
6762 int button2 = player_action & JOY_BUTTON_2;
6763 int dx = (left ? -1 : right ? 1 : 0);
6764 int dy = (up ? -1 : down ? 1 : 0);
6766 stored_player_action[player->index_nr] = 0;
6767 num_stored_actions++;
6769 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6771 if (!player->active || tape.pausing)
6776 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6779 snapped = SnapField(player, dx, dy);
6783 dropped = DropElement(player);
6785 moved = MovePlayer(player, dx, dy);
6788 if (tape.single_step && tape.recording && !tape.pausing)
6790 if (button1 || (dropped && !moved))
6792 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6793 SnapField(player, 0, 0); /* stop snapping */
6797 stored_player_action[player->index_nr] = player_action;
6801 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6803 /* no actions for this player (no input at player's configured device) */
6805 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6806 SnapField(player, 0, 0);
6807 CheckGravityMovement(player);
6809 if (player->MovPos == 0)
6810 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6812 if (player->MovPos == 0) /* needed for tape.playing */
6813 player->is_moving = FALSE;
6816 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6818 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6820 TapeRecordAction(stored_player_action);
6821 num_stored_actions = 0;
6828 static unsigned long action_delay = 0;
6829 unsigned long action_delay_value;
6830 int magic_wall_x = 0, magic_wall_y = 0;
6831 int i, x, y, element, graphic;
6832 byte *recorded_player_action;
6833 byte summarized_player_action = 0;
6835 byte tape_action[MAX_PLAYERS];
6838 if (game_status != GAME_MODE_PLAYING)
6841 action_delay_value =
6842 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6844 if (tape.playing && tape.index_search && !tape.pausing)
6845 action_delay_value = 0;
6847 /* ---------- main game synchronization point ---------- */
6849 WaitUntilDelayReached(&action_delay, action_delay_value);
6851 if (network_playing && !network_player_action_received)
6855 printf("DEBUG: try to get network player actions in time\n");
6859 #if defined(PLATFORM_UNIX)
6860 /* last chance to get network player actions without main loop delay */
6864 if (game_status != GAME_MODE_PLAYING)
6867 if (!network_player_action_received)
6871 printf("DEBUG: failed to get network player actions in time\n");
6882 printf("::: getting new tape action [%d]\n", FrameCounter);
6885 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6887 for (i = 0; i < MAX_PLAYERS; i++)
6889 summarized_player_action |= stored_player[i].action;
6891 if (!network_playing)
6892 stored_player[i].effective_action = stored_player[i].action;
6895 #if defined(PLATFORM_UNIX)
6896 if (network_playing)
6897 SendToServer_MovePlayer(summarized_player_action);
6900 if (!options.network && !setup.team_mode)
6901 local_player->effective_action = summarized_player_action;
6903 for (i = 0; i < MAX_PLAYERS; i++)
6905 int actual_player_action = stored_player[i].effective_action;
6907 if (stored_player[i].programmed_action)
6908 actual_player_action = stored_player[i].programmed_action;
6910 if (recorded_player_action)
6911 actual_player_action = recorded_player_action[i];
6913 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6915 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6916 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6918 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6923 TapeRecordAction(tape_action);
6926 network_player_action_received = FALSE;
6928 ScrollScreen(NULL, SCROLL_GO_ON);
6934 for (i = 0; i < MAX_PLAYERS; i++)
6935 stored_player[i].Frame++;
6939 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6941 for (i = 0; i < MAX_PLAYERS; i++)
6943 struct PlayerInfo *player = &stored_player[i];
6947 if (player->active && player->is_pushing && player->is_moving &&
6950 ContinueMoving(x, y);
6952 /* continue moving after pushing (this is actually a bug) */
6953 if (!IS_MOVING(x, y))
6962 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6964 Changed[x][y] = CE_BITMASK_DEFAULT;
6965 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6968 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6970 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6971 printf("GameActions(): This should never happen!\n");
6973 ChangePage[x][y] = -1;
6978 if (WasJustMoving[x][y] > 0)
6979 WasJustMoving[x][y]--;
6980 if (WasJustFalling[x][y] > 0)
6981 WasJustFalling[x][y]--;
6986 /* reset finished pushing action (not done in ContinueMoving() to allow
6987 continous pushing animation for elements with zero push delay) */
6988 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6990 ResetGfxAnimation(x, y);
6991 DrawLevelField(x, y);
6996 if (IS_BLOCKED(x, y))
7000 Blocked2Moving(x, y, &oldx, &oldy);
7001 if (!IS_MOVING(oldx, oldy))
7003 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7004 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7005 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7006 printf("GameActions(): This should never happen!\n");
7012 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7014 element = Feld[x][y];
7016 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7018 graphic = el2img(element);
7024 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7026 element = graphic = 0;
7030 if (graphic_info[graphic].anim_global_sync)
7031 GfxFrame[x][y] = FrameCounter;
7033 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7034 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7035 ResetRandomAnimationValue(x, y);
7037 SetRandomAnimationValue(x, y);
7040 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7043 if (IS_INACTIVE(element))
7045 if (IS_ANIMATED(graphic))
7046 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7052 /* this may take place after moving, so 'element' may have changed */
7054 if (IS_CHANGING(x, y))
7056 if (IS_CHANGING(x, y) &&
7057 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7061 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7062 element_info[element].event_page_nr[CE_DELAY]);
7064 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7067 element = Feld[x][y];
7068 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7072 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7077 element = Feld[x][y];
7078 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7080 if (element == EL_MOLE)
7081 printf("::: %d, %d, %d [%d]\n",
7082 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7086 if (element == EL_YAMYAM)
7087 printf("::: %d, %d, %d\n",
7088 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7092 if (IS_ANIMATED(graphic) &&
7096 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7099 if (element == EL_BUG)
7100 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7104 if (element == EL_MOLE)
7105 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7109 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7110 EdelsteinFunkeln(x, y);
7112 else if ((element == EL_ACID ||
7113 element == EL_EXIT_OPEN ||
7114 element == EL_SP_EXIT_OPEN ||
7115 element == EL_SP_TERMINAL ||
7116 element == EL_SP_TERMINAL_ACTIVE ||
7117 element == EL_EXTRA_TIME ||
7118 element == EL_SHIELD_NORMAL ||
7119 element == EL_SHIELD_DEADLY) &&
7120 IS_ANIMATED(graphic))
7121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7122 else if (IS_MOVING(x, y))
7123 ContinueMoving(x, y);
7124 else if (IS_ACTIVE_BOMB(element))
7125 CheckDynamite(x, y);
7127 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7128 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7130 else if (element == EL_AMOEBA_GROWING)
7131 AmoebeWaechst(x, y);
7132 else if (element == EL_AMOEBA_SHRINKING)
7133 AmoebaDisappearing(x, y);
7135 #if !USE_NEW_AMOEBA_CODE
7136 else if (IS_AMOEBALIVE(element))
7137 AmoebeAbleger(x, y);
7140 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7142 else if (element == EL_EXIT_CLOSED)
7144 else if (element == EL_SP_EXIT_CLOSED)
7146 else if (element == EL_EXPANDABLE_WALL_GROWING)
7148 else if (element == EL_EXPANDABLE_WALL ||
7149 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7150 element == EL_EXPANDABLE_WALL_VERTICAL ||
7151 element == EL_EXPANDABLE_WALL_ANY)
7153 else if (element == EL_FLAMES)
7154 CheckForDragon(x, y);
7156 else if (IS_AUTO_CHANGING(element))
7157 ChangeElement(x, y);
7159 else if (element == EL_EXPLOSION)
7160 ; /* drawing of correct explosion animation is handled separately */
7161 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7162 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7165 /* this may take place after moving, so 'element' may have changed */
7166 if (IS_AUTO_CHANGING(Feld[x][y]))
7167 ChangeElement(x, y);
7170 if (IS_BELT_ACTIVE(element))
7171 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7173 if (game.magic_wall_active)
7175 int jx = local_player->jx, jy = local_player->jy;
7177 /* play the element sound at the position nearest to the player */
7178 if ((element == EL_MAGIC_WALL_FULL ||
7179 element == EL_MAGIC_WALL_ACTIVE ||
7180 element == EL_MAGIC_WALL_EMPTYING ||
7181 element == EL_BD_MAGIC_WALL_FULL ||
7182 element == EL_BD_MAGIC_WALL_ACTIVE ||
7183 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7184 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7192 #if USE_NEW_AMOEBA_CODE
7193 /* new experimental amoeba growth stuff */
7195 if (!(FrameCounter % 8))
7198 static unsigned long random = 1684108901;
7200 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7203 x = (random >> 10) % lev_fieldx;
7204 y = (random >> 20) % lev_fieldy;
7206 x = RND(lev_fieldx);
7207 y = RND(lev_fieldy);
7209 element = Feld[x][y];
7211 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7212 if (!IS_PLAYER(x,y) &&
7213 (element == EL_EMPTY ||
7214 element == EL_SAND ||
7215 element == EL_QUICKSAND_EMPTY ||
7216 element == EL_ACID_SPLASH_LEFT ||
7217 element == EL_ACID_SPLASH_RIGHT))
7219 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7220 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7221 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7222 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7223 Feld[x][y] = EL_AMOEBA_DROP;
7226 random = random * 129 + 1;
7232 if (game.explosions_delayed)
7235 game.explosions_delayed = FALSE;
7237 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7239 element = Feld[x][y];
7241 if (ExplodeField[x][y])
7242 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7243 else if (element == EL_EXPLOSION)
7244 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7246 ExplodeField[x][y] = EX_NO_EXPLOSION;
7249 game.explosions_delayed = TRUE;
7252 if (game.magic_wall_active)
7254 if (!(game.magic_wall_time_left % 4))
7256 int element = Feld[magic_wall_x][magic_wall_y];
7258 if (element == EL_BD_MAGIC_WALL_FULL ||
7259 element == EL_BD_MAGIC_WALL_ACTIVE ||
7260 element == EL_BD_MAGIC_WALL_EMPTYING)
7261 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7263 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7266 if (game.magic_wall_time_left > 0)
7268 game.magic_wall_time_left--;
7269 if (!game.magic_wall_time_left)
7271 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7273 element = Feld[x][y];
7275 if (element == EL_MAGIC_WALL_ACTIVE ||
7276 element == EL_MAGIC_WALL_FULL)
7278 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7279 DrawLevelField(x, y);
7281 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7282 element == EL_BD_MAGIC_WALL_FULL)
7284 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7285 DrawLevelField(x, y);
7289 game.magic_wall_active = FALSE;
7294 if (game.light_time_left > 0)
7296 game.light_time_left--;
7298 if (game.light_time_left == 0)
7299 RedrawAllLightSwitchesAndInvisibleElements();
7302 if (game.timegate_time_left > 0)
7304 game.timegate_time_left--;
7306 if (game.timegate_time_left == 0)
7307 CloseAllOpenTimegates();
7310 for (i = 0; i < MAX_PLAYERS; i++)
7312 struct PlayerInfo *player = &stored_player[i];
7314 if (SHIELD_ON(player))
7316 if (player->shield_deadly_time_left)
7317 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7318 else if (player->shield_normal_time_left)
7319 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7323 if (TimeFrames >= FRAMES_PER_SECOND)
7328 for (i = 0; i < MAX_PLAYERS; i++)
7330 struct PlayerInfo *player = &stored_player[i];
7332 if (SHIELD_ON(player))
7334 player->shield_normal_time_left--;
7336 if (player->shield_deadly_time_left > 0)
7337 player->shield_deadly_time_left--;
7341 if (tape.recording || tape.playing)
7342 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7348 if (TimeLeft <= 10 && setup.time_limit)
7349 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7351 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7353 if (!TimeLeft && setup.time_limit)
7354 for (i = 0; i < MAX_PLAYERS; i++)
7355 KillHero(&stored_player[i]);
7357 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7358 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7362 PlayAllPlayersSound();
7364 if (options.debug) /* calculate frames per second */
7366 static unsigned long fps_counter = 0;
7367 static int fps_frames = 0;
7368 unsigned long fps_delay_ms = Counter() - fps_counter;
7372 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7374 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7377 fps_counter = Counter();
7380 redraw_mask |= REDRAW_FPS;
7384 if (stored_player[0].jx != stored_player[0].last_jx ||
7385 stored_player[0].jy != stored_player[0].last_jy)
7386 printf("::: %d, %d, %d, %d, %d\n",
7387 stored_player[0].MovDir,
7388 stored_player[0].MovPos,
7389 stored_player[0].GfxPos,
7390 stored_player[0].Frame,
7391 stored_player[0].StepFrame);
7398 for (i = 0; i < MAX_PLAYERS; i++)
7401 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7403 stored_player[i].Frame += move_frames;
7405 if (stored_player[i].MovPos != 0)
7406 stored_player[i].StepFrame += move_frames;
7408 if (stored_player[i].drop_delay > 0)
7409 stored_player[i].drop_delay--;
7414 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7416 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7418 local_player->show_envelope = 0;
7423 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7425 int min_x = x, min_y = y, max_x = x, max_y = y;
7428 for (i = 0; i < MAX_PLAYERS; i++)
7430 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7432 if (!stored_player[i].active || &stored_player[i] == player)
7435 min_x = MIN(min_x, jx);
7436 min_y = MIN(min_y, jy);
7437 max_x = MAX(max_x, jx);
7438 max_y = MAX(max_y, jy);
7441 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7444 static boolean AllPlayersInVisibleScreen()
7448 for (i = 0; i < MAX_PLAYERS; i++)
7450 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7452 if (!stored_player[i].active)
7455 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7462 void ScrollLevel(int dx, int dy)
7464 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7467 BlitBitmap(drawto_field, drawto_field,
7468 FX + TILEX * (dx == -1) - softscroll_offset,
7469 FY + TILEY * (dy == -1) - softscroll_offset,
7470 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7471 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7472 FX + TILEX * (dx == 1) - softscroll_offset,
7473 FY + TILEY * (dy == 1) - softscroll_offset);
7477 x = (dx == 1 ? BX1 : BX2);
7478 for (y = BY1; y <= BY2; y++)
7479 DrawScreenField(x, y);
7484 y = (dy == 1 ? BY1 : BY2);
7485 for (x = BX1; x <= BX2; x++)
7486 DrawScreenField(x, y);
7489 redraw_mask |= REDRAW_FIELD;
7492 static void CheckGravityMovement(struct PlayerInfo *player)
7494 if (game.gravity && !player->programmed_action)
7496 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7497 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7499 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7500 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7501 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7502 int jx = player->jx, jy = player->jy;
7503 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7504 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7505 int new_jx = jx + dx, new_jy = jy + dy;
7506 boolean field_under_player_is_free =
7507 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7508 boolean player_is_moving_to_valid_field =
7509 (IN_LEV_FIELD(new_jx, new_jy) &&
7510 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7511 Feld[new_jx][new_jy] == EL_SAND));
7512 /* !!! extend EL_SAND to anything diggable !!! */
7514 if (field_under_player_is_free &&
7515 !player_is_moving_to_valid_field &&
7516 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7517 player->programmed_action = MV_DOWN;
7523 -----------------------------------------------------------------------------
7524 dx, dy: direction (non-diagonal) to try to move the player to
7525 real_dx, real_dy: direction as read from input device (can be diagonal)
7528 boolean MovePlayerOneStep(struct PlayerInfo *player,
7529 int dx, int dy, int real_dx, int real_dy)
7532 static int change_sides[4][2] =
7534 /* enter side leave side */
7535 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7536 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7537 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7538 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7540 int move_direction = (dx == -1 ? MV_LEFT :
7541 dx == +1 ? MV_RIGHT :
7543 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7544 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7545 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7547 int jx = player->jx, jy = player->jy;
7548 int new_jx = jx + dx, new_jy = jy + dy;
7552 if (!player->active || (!dx && !dy))
7553 return MF_NO_ACTION;
7555 player->MovDir = (dx < 0 ? MV_LEFT :
7558 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7560 if (!IN_LEV_FIELD(new_jx, new_jy))
7561 return MF_NO_ACTION;
7563 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7564 return MF_NO_ACTION;
7567 element = MovingOrBlocked2Element(new_jx, new_jy);
7569 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7572 if (DONT_RUN_INTO(element))
7574 if (element == EL_ACID && dx == 0 && dy == 1)
7577 Feld[jx][jy] = EL_PLAYER_1;
7578 InitMovingField(jx, jy, MV_DOWN);
7579 Store[jx][jy] = EL_ACID;
7580 ContinueMoving(jx, jy);
7584 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7589 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7590 if (can_move != MF_MOVING)
7593 /* check if DigField() has caused relocation of the player */
7594 if (player->jx != jx || player->jy != jy)
7595 return MF_NO_ACTION;
7597 StorePlayer[jx][jy] = 0;
7598 player->last_jx = jx;
7599 player->last_jy = jy;
7600 player->jx = new_jx;
7601 player->jy = new_jy;
7602 StorePlayer[new_jx][new_jy] = player->element_nr;
7605 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7607 player->step_counter++;
7609 player->drop_delay = 0;
7611 PlayerVisit[jx][jy] = FrameCounter;
7613 ScrollPlayer(player, SCROLL_INIT);
7616 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7618 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7619 CE_OTHER_GETS_LEFT);
7620 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7621 CE_LEFT_BY_PLAYER, -1);
7624 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7626 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7627 enter_side, CE_OTHER_GETS_ENTERED);
7628 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7629 CE_ENTERED_BY_PLAYER, -1);
7636 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7638 int jx = player->jx, jy = player->jy;
7639 int old_jx = jx, old_jy = jy;
7640 int moved = MF_NO_ACTION;
7643 if (!player->active)
7648 if (player->MovPos == 0)
7650 player->is_moving = FALSE;
7651 player->is_digging = FALSE;
7652 player->is_collecting = FALSE;
7653 player->is_snapping = FALSE;
7654 player->is_pushing = FALSE;
7660 if (!player->active || (!dx && !dy))
7665 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7669 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7670 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7674 /* remove the last programmed player action */
7675 player->programmed_action = 0;
7679 /* should only happen if pre-1.2 tape recordings are played */
7680 /* this is only for backward compatibility */
7682 int original_move_delay_value = player->move_delay_value;
7685 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7689 /* scroll remaining steps with finest movement resolution */
7690 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7692 while (player->MovPos)
7694 ScrollPlayer(player, SCROLL_GO_ON);
7695 ScrollScreen(NULL, SCROLL_GO_ON);
7701 player->move_delay_value = original_move_delay_value;
7704 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7706 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7707 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7711 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7712 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7718 if (moved & MF_MOVING && !ScreenMovPos &&
7719 (player == local_player || !options.network))
7721 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7722 int offset = (setup.scroll_delay ? 3 : 0);
7724 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7726 /* actual player has left the screen -- scroll in that direction */
7727 if (jx != old_jx) /* player has moved horizontally */
7728 scroll_x += (jx - old_jx);
7729 else /* player has moved vertically */
7730 scroll_y += (jy - old_jy);
7734 if (jx != old_jx) /* player has moved horizontally */
7736 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7737 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7738 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7740 /* don't scroll over playfield boundaries */
7741 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7742 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7744 /* don't scroll more than one field at a time */
7745 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7747 /* don't scroll against the player's moving direction */
7748 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7749 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7750 scroll_x = old_scroll_x;
7752 else /* player has moved vertically */
7754 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7755 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7756 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7758 /* don't scroll over playfield boundaries */
7759 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7760 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7762 /* don't scroll more than one field at a time */
7763 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7765 /* don't scroll against the player's moving direction */
7766 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7767 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7768 scroll_y = old_scroll_y;
7772 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7774 if (!options.network && !AllPlayersInVisibleScreen())
7776 scroll_x = old_scroll_x;
7777 scroll_y = old_scroll_y;
7781 ScrollScreen(player, SCROLL_INIT);
7782 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7789 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7791 if (!(moved & MF_MOVING) && !player->is_pushing)
7796 player->StepFrame = 0;
7798 if (moved & MF_MOVING)
7800 if (old_jx != jx && old_jy == jy)
7801 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7802 else if (old_jx == jx && old_jy != jy)
7803 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7805 DrawLevelField(jx, jy); /* for "crumbled sand" */
7807 player->last_move_dir = player->MovDir;
7808 player->is_moving = TRUE;
7810 player->is_snapping = FALSE;
7814 player->is_switching = FALSE;
7817 player->is_dropping = FALSE;
7822 static int change_sides[4][2] =
7824 /* enter side leave side */
7825 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7826 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7827 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7828 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7830 int move_direction = player->MovDir;
7831 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7832 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7835 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7837 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7838 leave_side, CE_OTHER_GETS_LEFT);
7839 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7840 leave_side, CE_LEFT_BY_PLAYER, -1);
7843 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7845 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7846 enter_side, CE_OTHER_GETS_ENTERED);
7847 CheckElementSideChange(jx, jy, Feld[jx][jy],
7848 enter_side, CE_ENTERED_BY_PLAYER, -1);
7859 CheckGravityMovement(player);
7862 player->last_move_dir = MV_NO_MOVING;
7864 player->is_moving = FALSE;
7867 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7869 TestIfHeroTouchesBadThing(jx, jy);
7870 TestIfPlayerTouchesCustomElement(jx, jy);
7873 if (!player->active)
7879 void ScrollPlayer(struct PlayerInfo *player, int mode)
7881 int jx = player->jx, jy = player->jy;
7882 int last_jx = player->last_jx, last_jy = player->last_jy;
7883 int move_stepsize = TILEX / player->move_delay_value;
7885 if (!player->active || !player->MovPos)
7888 if (mode == SCROLL_INIT)
7890 player->actual_frame_counter = FrameCounter;
7891 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7893 if (Feld[last_jx][last_jy] == EL_EMPTY)
7894 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7901 else if (!FrameReached(&player->actual_frame_counter, 1))
7904 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7905 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7907 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7908 Feld[last_jx][last_jy] = EL_EMPTY;
7910 /* before DrawPlayer() to draw correct player graphic for this case */
7911 if (player->MovPos == 0)
7912 CheckGravityMovement(player);
7915 DrawPlayer(player); /* needed here only to cleanup last field */
7918 if (player->MovPos == 0) /* player reached destination field */
7921 if (player->move_delay_reset_counter > 0)
7923 player->move_delay_reset_counter--;
7925 if (player->move_delay_reset_counter == 0)
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 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7937 /* continue with normal speed after quickly moving through gate */
7938 HALVE_PLAYER_SPEED(player);
7940 /* be able to make the next move without delay */
7941 player->move_delay = 0;
7945 player->last_jx = jx;
7946 player->last_jy = jy;
7948 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7949 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7950 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7952 DrawPlayer(player); /* needed here only to cleanup last field */
7955 if (local_player->friends_still_needed == 0 ||
7956 IS_SP_ELEMENT(Feld[jx][jy]))
7957 player->LevelSolved = player->GameOver = TRUE;
7960 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7962 TestIfHeroTouchesBadThing(jx, jy);
7963 TestIfPlayerTouchesCustomElement(jx, jy);
7965 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7968 if (!player->active)
7972 if (tape.single_step && tape.recording && !tape.pausing &&
7973 !player->programmed_action)
7974 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7978 void ScrollScreen(struct PlayerInfo *player, int mode)
7980 static unsigned long screen_frame_counter = 0;
7982 if (mode == SCROLL_INIT)
7984 /* set scrolling step size according to actual player's moving speed */
7985 ScrollStepSize = TILEX / player->move_delay_value;
7987 screen_frame_counter = FrameCounter;
7988 ScreenMovDir = player->MovDir;
7989 ScreenMovPos = player->MovPos;
7990 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7993 else if (!FrameReached(&screen_frame_counter, 1))
7998 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7999 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8000 redraw_mask |= REDRAW_FIELD;
8003 ScreenMovDir = MV_NO_MOVING;
8006 void TestIfPlayerTouchesCustomElement(int x, int y)
8008 static int xy[4][2] =
8015 static int change_sides[4][2] =
8017 /* center side border side */
8018 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8019 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8020 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8021 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8023 static int touch_dir[4] =
8030 int center_element = Feld[x][y]; /* should always be non-moving! */
8033 for (i = 0; i < 4; i++)
8035 int xx = x + xy[i][0];
8036 int yy = y + xy[i][1];
8037 int center_side = change_sides[i][0];
8038 int border_side = change_sides[i][1];
8041 if (!IN_LEV_FIELD(xx, yy))
8044 if (IS_PLAYER(x, y))
8046 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8047 border_element = Feld[xx][yy]; /* may be moving! */
8048 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8049 border_element = Feld[xx][yy];
8050 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8051 border_element = MovingOrBlocked2Element(xx, yy);
8053 continue; /* center and border element do not touch */
8055 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8056 CE_OTHER_GETS_TOUCHED);
8057 CheckElementSideChange(xx, yy, border_element, border_side,
8058 CE_TOUCHED_BY_PLAYER, -1);
8060 else if (IS_PLAYER(xx, yy))
8062 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8064 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8066 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8067 continue; /* center and border element do not touch */
8070 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8071 CE_OTHER_GETS_TOUCHED);
8072 CheckElementSideChange(x, y, center_element, center_side,
8073 CE_TOUCHED_BY_PLAYER, -1);
8080 void TestIfElementTouchesCustomElement(int x, int y)
8082 static int xy[4][2] =
8089 static int change_sides[4][2] =
8091 /* center side border side */
8092 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8093 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8094 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8095 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8097 static int touch_dir[4] =
8104 boolean change_center_element = FALSE;
8105 int center_element_change_page = 0;
8106 int center_element = Feld[x][y]; /* should always be non-moving! */
8109 for (i = 0; i < 4; i++)
8111 int xx = x + xy[i][0];
8112 int yy = y + xy[i][1];
8113 int center_side = change_sides[i][0];
8114 int border_side = change_sides[i][1];
8117 if (!IN_LEV_FIELD(xx, yy))
8120 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8121 border_element = Feld[xx][yy]; /* may be moving! */
8122 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8123 border_element = Feld[xx][yy];
8124 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8125 border_element = MovingOrBlocked2Element(xx, yy);
8127 continue; /* center and border element do not touch */
8129 /* check for change of center element (but change it only once) */
8130 if (IS_CUSTOM_ELEMENT(center_element) &&
8131 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8132 !change_center_element)
8134 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8136 struct ElementChangeInfo *change =
8137 &element_info[center_element].change_page[j];
8139 if (change->can_change &&
8140 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8141 change->sides & border_side &&
8142 change->trigger_element == border_element)
8144 change_center_element = TRUE;
8145 center_element_change_page = j;
8152 /* check for change of border element */
8153 if (IS_CUSTOM_ELEMENT(border_element) &&
8154 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8156 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8158 struct ElementChangeInfo *change =
8159 &element_info[border_element].change_page[j];
8161 if (change->can_change &&
8162 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8163 change->sides & center_side &&
8164 change->trigger_element == center_element)
8166 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8167 CE_OTHER_IS_TOUCHING, j);
8174 if (change_center_element)
8175 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8176 CE_OTHER_IS_TOUCHING, center_element_change_page);
8179 void TestIfElementHitsCustomElement(int x, int y, int direction)
8181 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8182 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8183 int hitx = x + dx, hity = y + dy;
8184 int hitting_element = Feld[x][y];
8186 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8187 !IS_FREE(hitx, hity) &&
8188 (!IS_MOVING(hitx, hity) ||
8189 MovDir[hitx][hity] != direction ||
8190 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8193 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8197 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8201 CheckElementSideChange(x, y, hitting_element,
8202 direction, CE_HITTING_SOMETHING, -1);
8204 if (IN_LEV_FIELD(hitx, hity))
8206 static int opposite_directions[] =
8213 int move_dir_bit = MV_DIR_BIT(direction);
8214 int opposite_direction = opposite_directions[move_dir_bit];
8215 int hitting_side = direction;
8216 int touched_side = opposite_direction;
8217 int touched_element = MovingOrBlocked2Element(hitx, hity);
8219 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8220 MovDir[hitx][hity] != direction ||
8221 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8230 CheckElementSideChange(hitx, hity, touched_element,
8231 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8233 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8234 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8236 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8238 struct ElementChangeInfo *change =
8239 &element_info[hitting_element].change_page[i];
8241 if (change->can_change &&
8242 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8243 change->sides & touched_side &&
8244 change->trigger_element == touched_element)
8246 CheckElementSideChange(x, y, hitting_element,
8247 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8253 if (IS_CUSTOM_ELEMENT(touched_element) &&
8254 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8256 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8258 struct ElementChangeInfo *change =
8259 &element_info[touched_element].change_page[i];
8261 if (change->can_change &&
8262 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8263 change->sides & hitting_side &&
8264 change->trigger_element == hitting_element)
8266 CheckElementSideChange(hitx, hity, touched_element,
8267 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8276 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8278 int i, kill_x = -1, kill_y = -1;
8279 static int test_xy[4][2] =
8286 static int test_dir[4] =
8294 for (i = 0; i < 4; i++)
8296 int test_x, test_y, test_move_dir, test_element;
8298 test_x = good_x + test_xy[i][0];
8299 test_y = good_y + test_xy[i][1];
8300 if (!IN_LEV_FIELD(test_x, test_y))
8304 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8307 test_element = Feld[test_x][test_y];
8309 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8312 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8313 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8315 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8316 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8324 if (kill_x != -1 || kill_y != -1)
8326 if (IS_PLAYER(good_x, good_y))
8328 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8330 if (player->shield_deadly_time_left > 0)
8331 Bang(kill_x, kill_y);
8332 else if (!PLAYER_PROTECTED(good_x, good_y))
8336 Bang(good_x, good_y);
8340 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8342 int i, kill_x = -1, kill_y = -1;
8343 int bad_element = Feld[bad_x][bad_y];
8344 static int test_xy[4][2] =
8351 static int touch_dir[4] =
8358 static int test_dir[4] =
8366 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8369 for (i = 0; i < 4; i++)
8371 int test_x, test_y, test_move_dir, test_element;
8373 test_x = bad_x + test_xy[i][0];
8374 test_y = bad_y + test_xy[i][1];
8375 if (!IN_LEV_FIELD(test_x, test_y))
8379 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8381 test_element = Feld[test_x][test_y];
8383 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8384 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8386 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8387 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8389 /* good thing is player or penguin that does not move away */
8390 if (IS_PLAYER(test_x, test_y))
8392 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8394 if (bad_element == EL_ROBOT && player->is_moving)
8395 continue; /* robot does not kill player if he is moving */
8397 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8399 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8400 continue; /* center and border element do not touch */
8407 else if (test_element == EL_PENGUIN)
8416 if (kill_x != -1 || kill_y != -1)
8418 if (IS_PLAYER(kill_x, kill_y))
8420 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8422 if (player->shield_deadly_time_left > 0)
8424 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8428 Bang(kill_x, kill_y);
8432 void TestIfHeroTouchesBadThing(int x, int y)
8434 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8437 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8439 TestIfGoodThingHitsBadThing(x, y, move_dir);
8442 void TestIfBadThingTouchesHero(int x, int y)
8444 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8447 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8449 TestIfBadThingHitsGoodThing(x, y, move_dir);
8452 void TestIfFriendTouchesBadThing(int x, int y)
8454 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8457 void TestIfBadThingTouchesFriend(int x, int y)
8459 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8462 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8464 int i, kill_x = bad_x, kill_y = bad_y;
8465 static int xy[4][2] =
8473 for (i = 0; i < 4; i++)
8477 x = bad_x + xy[i][0];
8478 y = bad_y + xy[i][1];
8479 if (!IN_LEV_FIELD(x, y))
8482 element = Feld[x][y];
8483 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8484 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8492 if (kill_x != bad_x || kill_y != bad_y)
8496 void KillHero(struct PlayerInfo *player)
8498 int jx = player->jx, jy = player->jy;
8500 if (!player->active)
8503 /* remove accessible field at the player's position */
8504 Feld[jx][jy] = EL_EMPTY;
8506 /* deactivate shield (else Bang()/Explode() would not work right) */
8507 player->shield_normal_time_left = 0;
8508 player->shield_deadly_time_left = 0;
8514 static void KillHeroUnlessProtected(int x, int y)
8516 if (!PLAYER_PROTECTED(x, y))
8517 KillHero(PLAYERINFO(x, y));
8520 void BuryHero(struct PlayerInfo *player)
8522 int jx = player->jx, jy = player->jy;
8524 if (!player->active)
8528 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8530 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8532 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8534 player->GameOver = TRUE;
8538 void RemoveHero(struct PlayerInfo *player)
8540 int jx = player->jx, jy = player->jy;
8541 int i, found = FALSE;
8543 player->present = FALSE;
8544 player->active = FALSE;
8546 if (!ExplodeField[jx][jy])
8547 StorePlayer[jx][jy] = 0;
8549 for (i = 0; i < MAX_PLAYERS; i++)
8550 if (stored_player[i].active)
8554 AllPlayersGone = TRUE;
8561 =============================================================================
8562 checkDiagonalPushing()
8563 -----------------------------------------------------------------------------
8564 check if diagonal input device direction results in pushing of object
8565 (by checking if the alternative direction is walkable, diggable, ...)
8566 =============================================================================
8569 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8570 int x, int y, int real_dx, int real_dy)
8572 int jx, jy, dx, dy, xx, yy;
8574 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8577 /* diagonal direction: check alternative direction */
8582 xx = jx + (dx == 0 ? real_dx : 0);
8583 yy = jy + (dy == 0 ? real_dy : 0);
8585 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8589 =============================================================================
8591 -----------------------------------------------------------------------------
8592 x, y: field next to player (non-diagonal) to try to dig to
8593 real_dx, real_dy: direction as read from input device (can be diagonal)
8594 =============================================================================
8597 int DigField(struct PlayerInfo *player,
8598 int x, int y, int real_dx, int real_dy, int mode)
8600 static int change_sides[4] =
8602 CH_SIDE_RIGHT, /* moving left */
8603 CH_SIDE_LEFT, /* moving right */
8604 CH_SIDE_BOTTOM, /* moving up */
8605 CH_SIDE_TOP, /* moving down */
8607 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8608 int jx = player->jx, jy = player->jy;
8609 int dx = x - jx, dy = y - jy;
8610 int nextx = x + dx, nexty = y + dy;
8611 int move_direction = (dx == -1 ? MV_LEFT :
8612 dx == +1 ? MV_RIGHT :
8614 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8615 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8618 if (player->MovPos == 0)
8620 player->is_digging = FALSE;
8621 player->is_collecting = FALSE;
8624 if (player->MovPos == 0) /* last pushing move finished */
8625 player->is_pushing = FALSE;
8627 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8629 player->is_switching = FALSE;
8630 player->push_delay = 0;
8632 return MF_NO_ACTION;
8635 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8636 return MF_NO_ACTION;
8639 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8641 if (IS_TUBE(Feld[jx][jy]) ||
8642 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8646 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8647 int tube_leave_directions[][2] =
8649 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8650 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8651 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8652 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8653 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8654 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8655 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8656 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8657 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8658 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8659 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8660 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8663 while (tube_leave_directions[i][0] != tube_element)
8666 if (tube_leave_directions[i][0] == -1) /* should not happen */
8670 if (!(tube_leave_directions[i][1] & move_direction))
8671 return MF_NO_ACTION; /* tube has no opening in this direction */
8674 element = Feld[x][y];
8676 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8677 game.engine_version >= VERSION_IDENT(2,2,0,0))
8678 return MF_NO_ACTION;
8682 case EL_SP_PORT_LEFT:
8683 case EL_SP_PORT_RIGHT:
8685 case EL_SP_PORT_DOWN:
8686 case EL_SP_PORT_HORIZONTAL:
8687 case EL_SP_PORT_VERTICAL:
8688 case EL_SP_PORT_ANY:
8689 case EL_SP_GRAVITY_PORT_LEFT:
8690 case EL_SP_GRAVITY_PORT_RIGHT:
8691 case EL_SP_GRAVITY_PORT_UP:
8692 case EL_SP_GRAVITY_PORT_DOWN:
8694 element != EL_SP_PORT_LEFT &&
8695 element != EL_SP_GRAVITY_PORT_LEFT &&
8696 element != EL_SP_PORT_HORIZONTAL &&
8697 element != EL_SP_PORT_ANY) ||
8699 element != EL_SP_PORT_RIGHT &&
8700 element != EL_SP_GRAVITY_PORT_RIGHT &&
8701 element != EL_SP_PORT_HORIZONTAL &&
8702 element != EL_SP_PORT_ANY) ||
8704 element != EL_SP_PORT_UP &&
8705 element != EL_SP_GRAVITY_PORT_UP &&
8706 element != EL_SP_PORT_VERTICAL &&
8707 element != EL_SP_PORT_ANY) ||
8709 element != EL_SP_PORT_DOWN &&
8710 element != EL_SP_GRAVITY_PORT_DOWN &&
8711 element != EL_SP_PORT_VERTICAL &&
8712 element != EL_SP_PORT_ANY) ||
8713 !IN_LEV_FIELD(nextx, nexty) ||
8714 !IS_FREE(nextx, nexty))
8715 return MF_NO_ACTION;
8717 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8718 element == EL_SP_GRAVITY_PORT_RIGHT ||
8719 element == EL_SP_GRAVITY_PORT_UP ||
8720 element == EL_SP_GRAVITY_PORT_DOWN)
8721 game.gravity = !game.gravity;
8723 /* automatically move to the next field with double speed */
8724 player->programmed_action = move_direction;
8726 if (player->move_delay_reset_counter == 0)
8728 player->move_delay_reset_counter = 2; /* two double speed steps */
8730 DOUBLE_PLAYER_SPEED(player);
8733 player->move_delay_reset_counter = 2;
8735 DOUBLE_PLAYER_SPEED(player);
8738 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8742 case EL_TUBE_VERTICAL:
8743 case EL_TUBE_HORIZONTAL:
8744 case EL_TUBE_VERTICAL_LEFT:
8745 case EL_TUBE_VERTICAL_RIGHT:
8746 case EL_TUBE_HORIZONTAL_UP:
8747 case EL_TUBE_HORIZONTAL_DOWN:
8748 case EL_TUBE_LEFT_UP:
8749 case EL_TUBE_LEFT_DOWN:
8750 case EL_TUBE_RIGHT_UP:
8751 case EL_TUBE_RIGHT_DOWN:
8754 int tube_enter_directions[][2] =
8756 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8757 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8758 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8759 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8760 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8761 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8762 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8763 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8764 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8765 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8766 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8767 { -1, MV_NO_MOVING }
8770 while (tube_enter_directions[i][0] != element)
8773 if (tube_enter_directions[i][0] == -1) /* should not happen */
8777 if (!(tube_enter_directions[i][1] & move_direction))
8778 return MF_NO_ACTION; /* tube has no opening in this direction */
8780 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8786 if (IS_WALKABLE(element))
8788 int sound_action = ACTION_WALKING;
8790 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8792 if (!player->key[element - EL_GATE_1])
8793 return MF_NO_ACTION;
8795 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8797 if (!player->key[element - EL_GATE_1_GRAY])
8798 return MF_NO_ACTION;
8800 else if (element == EL_EXIT_OPEN ||
8801 element == EL_SP_EXIT_OPEN ||
8802 element == EL_SP_EXIT_OPENING)
8804 sound_action = ACTION_PASSING; /* player is passing exit */
8806 else if (element == EL_EMPTY)
8808 sound_action = ACTION_MOVING; /* nothing to walk on */
8811 /* play sound from background or player, whatever is available */
8812 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8813 PlayLevelSoundElementAction(x, y, element, sound_action);
8815 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8819 else if (IS_PASSABLE(element))
8821 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8822 return MF_NO_ACTION;
8825 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8826 return MF_NO_ACTION;
8829 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8831 if (!player->key[element - EL_EM_GATE_1])
8832 return MF_NO_ACTION;
8834 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8836 if (!player->key[element - EL_EM_GATE_1_GRAY])
8837 return MF_NO_ACTION;
8840 /* automatically move to the next field with double speed */
8841 player->programmed_action = move_direction;
8843 if (player->move_delay_reset_counter == 0)
8845 player->move_delay_reset_counter = 2; /* two double speed steps */
8847 DOUBLE_PLAYER_SPEED(player);
8850 player->move_delay_reset_counter = 2;
8852 DOUBLE_PLAYER_SPEED(player);
8855 PlayLevelSoundAction(x, y, ACTION_PASSING);
8859 else if (IS_DIGGABLE(element))
8863 if (mode != DF_SNAP)
8866 GfxElement[x][y] = GFX_ELEMENT(element);
8869 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8871 player->is_digging = TRUE;
8874 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8876 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8879 if (mode == DF_SNAP)
8880 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8885 else if (IS_COLLECTIBLE(element))
8889 if (mode != DF_SNAP)
8891 GfxElement[x][y] = element;
8892 player->is_collecting = TRUE;
8895 if (element == EL_SPEED_PILL)
8896 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8897 else if (element == EL_EXTRA_TIME && level.time > 0)
8900 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8902 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8904 player->shield_normal_time_left += 10;
8905 if (element == EL_SHIELD_DEADLY)
8906 player->shield_deadly_time_left += 10;
8908 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8910 if (player->inventory_size < MAX_INVENTORY_SIZE)
8911 player->inventory_element[player->inventory_size++] = element;
8913 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8914 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8916 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8918 player->dynabomb_count++;
8919 player->dynabombs_left++;
8921 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8923 player->dynabomb_size++;
8925 else if (element == EL_DYNABOMB_INCREASE_POWER)
8927 player->dynabomb_xl = TRUE;
8929 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8930 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8932 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8933 element - EL_KEY_1 : element - EL_EM_KEY_1);
8935 player->key[key_nr] = TRUE;
8937 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8938 el2edimg(EL_KEY_1 + key_nr));
8939 redraw_mask |= REDRAW_DOOR_1;
8941 else if (IS_ENVELOPE(element))
8944 player->show_envelope = element;
8946 ShowEnvelope(element - EL_ENVELOPE_1);
8949 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8953 for (i = 0; i < element_info[element].collect_count; i++)
8954 if (player->inventory_size < MAX_INVENTORY_SIZE)
8955 player->inventory_element[player->inventory_size++] = element;
8957 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8958 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8960 else if (element_info[element].collect_count > 0)
8962 local_player->gems_still_needed -=
8963 element_info[element].collect_count;
8964 if (local_player->gems_still_needed < 0)
8965 local_player->gems_still_needed = 0;
8967 DrawText(DX_EMERALDS, DY_EMERALDS,
8968 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8971 RaiseScoreElement(element);
8972 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8974 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8977 if (mode == DF_SNAP)
8978 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8983 else if (IS_PUSHABLE(element))
8985 if (mode == DF_SNAP && element != EL_BD_ROCK)
8986 return MF_NO_ACTION;
8988 if (CAN_FALL(element) && dy)
8989 return MF_NO_ACTION;
8991 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8992 !(element == EL_SPRING && use_spring_bug))
8993 return MF_NO_ACTION;
8996 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8997 ((move_direction & MV_VERTICAL &&
8998 ((element_info[element].move_pattern & MV_LEFT &&
8999 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9000 (element_info[element].move_pattern & MV_RIGHT &&
9001 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9002 (move_direction & MV_HORIZONTAL &&
9003 ((element_info[element].move_pattern & MV_UP &&
9004 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9005 (element_info[element].move_pattern & MV_DOWN &&
9006 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9007 return MF_NO_ACTION;
9011 /* do not push elements already moving away faster than player */
9012 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9013 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9014 return MF_NO_ACTION;
9016 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9017 return MF_NO_ACTION;
9021 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9023 if (player->push_delay_value == -1)
9024 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9026 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9028 if (!player->is_pushing)
9029 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9033 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9034 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9035 !player_is_pushing))
9036 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9039 if (!player->is_pushing &&
9040 game.engine_version >= VERSION_IDENT(2,2,0,7))
9041 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9045 printf("::: push delay: %ld [%d, %d] [%d]\n",
9046 player->push_delay_value, FrameCounter, game.engine_version,
9047 player->is_pushing);
9050 player->is_pushing = TRUE;
9052 if (!(IN_LEV_FIELD(nextx, nexty) &&
9053 (IS_FREE(nextx, nexty) ||
9054 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9055 IS_SB_ELEMENT(element)))))
9056 return MF_NO_ACTION;
9058 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9059 return MF_NO_ACTION;
9061 if (player->push_delay == 0) /* new pushing; restart delay */
9062 player->push_delay = FrameCounter;
9064 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9065 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9066 element != EL_SPRING && element != EL_BALLOON)
9068 /* make sure that there is no move delay before next try to push */
9069 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9070 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9072 return MF_NO_ACTION;
9076 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9079 if (IS_SB_ELEMENT(element))
9081 if (element == EL_SOKOBAN_FIELD_FULL)
9083 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9084 local_player->sokobanfields_still_needed++;
9087 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9089 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9090 local_player->sokobanfields_still_needed--;
9093 Feld[x][y] = EL_SOKOBAN_OBJECT;
9095 if (Back[x][y] == Back[nextx][nexty])
9096 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9097 else if (Back[x][y] != 0)
9098 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9101 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9104 if (local_player->sokobanfields_still_needed == 0 &&
9105 game.emulation == EMU_SOKOBAN)
9107 player->LevelSolved = player->GameOver = TRUE;
9108 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9112 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9114 InitMovingField(x, y, move_direction);
9115 GfxAction[x][y] = ACTION_PUSHING;
9117 if (mode == DF_SNAP)
9118 ContinueMoving(x, y);
9120 MovPos[x][y] = (dx != 0 ? dx : dy);
9122 Pushed[x][y] = TRUE;
9123 Pushed[nextx][nexty] = TRUE;
9125 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9126 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9128 player->push_delay_value = -1; /* get new value later */
9130 CheckTriggeredElementSideChange(x, y, element, dig_side,
9131 CE_OTHER_GETS_PUSHED);
9132 CheckElementSideChange(x, y, element, dig_side,
9133 CE_PUSHED_BY_PLAYER, -1);
9137 else if (IS_SWITCHABLE(element))
9139 if (PLAYER_SWITCHING(player, x, y))
9142 player->is_switching = TRUE;
9143 player->switch_x = x;
9144 player->switch_y = y;
9146 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9148 if (element == EL_ROBOT_WHEEL)
9150 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9154 DrawLevelField(x, y);
9156 else if (element == EL_SP_TERMINAL)
9160 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9162 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9164 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9165 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9168 else if (IS_BELT_SWITCH(element))
9170 ToggleBeltSwitch(x, y);
9172 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9173 element == EL_SWITCHGATE_SWITCH_DOWN)
9175 ToggleSwitchgateSwitch(x, y);
9177 else if (element == EL_LIGHT_SWITCH ||
9178 element == EL_LIGHT_SWITCH_ACTIVE)
9180 ToggleLightSwitch(x, y);
9183 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9184 SND_LIGHT_SWITCH_ACTIVATING :
9185 SND_LIGHT_SWITCH_DEACTIVATING);
9188 else if (element == EL_TIMEGATE_SWITCH)
9190 ActivateTimegateSwitch(x, y);
9192 else if (element == EL_BALLOON_SWITCH_LEFT ||
9193 element == EL_BALLOON_SWITCH_RIGHT ||
9194 element == EL_BALLOON_SWITCH_UP ||
9195 element == EL_BALLOON_SWITCH_DOWN ||
9196 element == EL_BALLOON_SWITCH_ANY)
9198 if (element == EL_BALLOON_SWITCH_ANY)
9199 game.balloon_dir = move_direction;
9201 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9202 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9203 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9204 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9207 else if (element == EL_LAMP)
9209 Feld[x][y] = EL_LAMP_ACTIVE;
9210 local_player->lights_still_needed--;
9212 DrawLevelField(x, y);
9214 else if (element == EL_TIME_ORB_FULL)
9216 Feld[x][y] = EL_TIME_ORB_EMPTY;
9218 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9220 DrawLevelField(x, y);
9223 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9231 if (!PLAYER_SWITCHING(player, x, y))
9233 player->is_switching = TRUE;
9234 player->switch_x = x;
9235 player->switch_y = y;
9237 CheckTriggeredElementSideChange(x, y, element, dig_side,
9238 CE_OTHER_IS_SWITCHING);
9239 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9242 CheckTriggeredElementSideChange(x, y, element, dig_side,
9243 CE_OTHER_GETS_PRESSED);
9244 CheckElementSideChange(x, y, element, dig_side,
9245 CE_PRESSED_BY_PLAYER, -1);
9248 return MF_NO_ACTION;
9251 player->push_delay = 0;
9253 if (Feld[x][y] != element) /* really digged/collected something */
9254 player->is_collecting = !player->is_digging;
9259 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9261 int jx = player->jx, jy = player->jy;
9262 int x = jx + dx, y = jy + dy;
9263 int snap_direction = (dx == -1 ? MV_LEFT :
9264 dx == +1 ? MV_RIGHT :
9266 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9268 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9271 if (!player->active || !IN_LEV_FIELD(x, y))
9279 if (player->MovPos == 0)
9280 player->is_pushing = FALSE;
9282 player->is_snapping = FALSE;
9284 if (player->MovPos == 0)
9286 player->is_moving = FALSE;
9287 player->is_digging = FALSE;
9288 player->is_collecting = FALSE;
9294 if (player->is_snapping)
9297 player->MovDir = snap_direction;
9300 if (player->MovPos == 0)
9303 player->is_moving = FALSE;
9304 player->is_digging = FALSE;
9305 player->is_collecting = FALSE;
9308 player->is_dropping = FALSE;
9310 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9313 player->is_snapping = TRUE;
9316 if (player->MovPos == 0)
9319 player->is_moving = FALSE;
9320 player->is_digging = FALSE;
9321 player->is_collecting = FALSE;
9324 DrawLevelField(x, y);
9330 boolean DropElement(struct PlayerInfo *player)
9332 int jx = player->jx, jy = player->jy;
9333 int old_element = Feld[jx][jy];
9336 /* check if player is active, not moving and ready to drop */
9337 if (!player->active || player->MovPos || player->drop_delay > 0)
9340 /* check if player has anything that can be dropped */
9341 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9344 /* check if anything can be dropped at the current position */
9345 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9348 /* collected custom elements can only be dropped on empty fields */
9349 if (player->inventory_size > 0 &&
9350 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9351 && old_element != EL_EMPTY)
9354 if (old_element != EL_EMPTY)
9355 Back[jx][jy] = old_element; /* store old element on this field */
9357 ResetGfxAnimation(jx, jy);
9358 ResetRandomAnimationValue(jx, jy);
9360 if (player->inventory_size > 0)
9362 player->inventory_size--;
9363 new_element = player->inventory_element[player->inventory_size];
9365 if (new_element == EL_DYNAMITE)
9366 new_element = EL_DYNAMITE_ACTIVE;
9367 else if (new_element == EL_SP_DISK_RED)
9368 new_element = EL_SP_DISK_RED_ACTIVE;
9370 Feld[jx][jy] = new_element;
9372 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9373 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9375 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9376 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9378 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9380 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9381 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9383 TestIfElementTouchesCustomElement(jx, jy);
9385 else /* player is dropping a dyna bomb */
9387 player->dynabombs_left--;
9388 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9390 Feld[jx][jy] = new_element;
9392 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9393 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9395 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9402 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9404 InitField(jx, jy, FALSE);
9405 if (CAN_MOVE(Feld[jx][jy]))
9409 new_element = Feld[jx][jy];
9411 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9412 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9414 int move_stepsize = element_info[new_element].move_stepsize;
9415 int direction, dx, dy, nextx, nexty;
9417 if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
9418 MovDir[jx][jy] = player->MovDir;
9420 direction = MovDir[jx][jy];
9421 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9422 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9426 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9429 WasJustMoving[jx][jy] = 3;
9431 InitMovingField(jx, jy, direction);
9432 ContinueMoving(jx, jy);
9437 Changed[jx][jy] = 0; /* allow another change */
9440 TestIfElementHitsCustomElement(jx, jy, direction);
9442 CheckElementSideChange(jx, jy, new_element,
9443 direction, CE_HITTING_SOMETHING, -1);
9447 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9451 player->drop_delay = 8 + 8 + 8;
9456 player->is_dropping = TRUE;
9462 /* ------------------------------------------------------------------------- */
9463 /* game sound playing functions */
9464 /* ------------------------------------------------------------------------- */
9466 static int *loop_sound_frame = NULL;
9467 static int *loop_sound_volume = NULL;
9469 void InitPlayLevelSound()
9471 int num_sounds = getSoundListSize();
9473 checked_free(loop_sound_frame);
9474 checked_free(loop_sound_volume);
9476 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9477 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9480 static void PlayLevelSound(int x, int y, int nr)
9482 int sx = SCREENX(x), sy = SCREENY(y);
9483 int volume, stereo_position;
9484 int max_distance = 8;
9485 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9487 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9488 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9491 if (!IN_LEV_FIELD(x, y) ||
9492 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9493 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9496 volume = SOUND_MAX_VOLUME;
9498 if (!IN_SCR_FIELD(sx, sy))
9500 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9501 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9503 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9506 stereo_position = (SOUND_MAX_LEFT +
9507 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9508 (SCR_FIELDX + 2 * max_distance));
9510 if (IS_LOOP_SOUND(nr))
9512 /* This assures that quieter loop sounds do not overwrite louder ones,
9513 while restarting sound volume comparison with each new game frame. */
9515 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9518 loop_sound_volume[nr] = volume;
9519 loop_sound_frame[nr] = FrameCounter;
9522 PlaySoundExt(nr, volume, stereo_position, type);
9525 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9527 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9528 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9529 y < LEVELY(BY1) ? LEVELY(BY1) :
9530 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9534 static void PlayLevelSoundAction(int x, int y, int action)
9536 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9539 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9541 int sound_effect = element_info[element].sound[action];
9543 if (sound_effect != SND_UNDEFINED)
9544 PlayLevelSound(x, y, sound_effect);
9547 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9550 int sound_effect = element_info[element].sound[action];
9552 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9553 PlayLevelSound(x, y, sound_effect);
9556 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9558 int sound_effect = element_info[Feld[x][y]].sound[action];
9560 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9561 PlayLevelSound(x, y, sound_effect);
9564 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9566 int sound_effect = element_info[Feld[x][y]].sound[action];
9568 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9569 StopSound(sound_effect);
9572 static void PlayLevelMusic()
9574 if (levelset.music[level_nr] != MUS_UNDEFINED)
9575 PlayMusic(levelset.music[level_nr]); /* from config file */
9577 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9580 void RaiseScore(int value)
9582 local_player->score += value;
9583 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9586 void RaiseScoreElement(int element)
9592 case EL_EMERALD_YELLOW:
9593 case EL_EMERALD_RED:
9594 case EL_EMERALD_PURPLE:
9595 case EL_SP_INFOTRON:
9596 RaiseScore(level.score[SC_EMERALD]);
9599 RaiseScore(level.score[SC_DIAMOND]);
9602 RaiseScore(level.score[SC_CRYSTAL]);
9605 RaiseScore(level.score[SC_PEARL]);
9608 case EL_BD_BUTTERFLY:
9609 case EL_SP_ELECTRON:
9610 RaiseScore(level.score[SC_BUG]);
9614 case EL_SP_SNIKSNAK:
9615 RaiseScore(level.score[SC_SPACESHIP]);
9618 case EL_DARK_YAMYAM:
9619 RaiseScore(level.score[SC_YAMYAM]);
9622 RaiseScore(level.score[SC_ROBOT]);
9625 RaiseScore(level.score[SC_PACMAN]);
9628 RaiseScore(level.score[SC_NUT]);
9631 case EL_SP_DISK_RED:
9632 case EL_DYNABOMB_INCREASE_NUMBER:
9633 case EL_DYNABOMB_INCREASE_SIZE:
9634 case EL_DYNABOMB_INCREASE_POWER:
9635 RaiseScore(level.score[SC_DYNAMITE]);
9637 case EL_SHIELD_NORMAL:
9638 case EL_SHIELD_DEADLY:
9639 RaiseScore(level.score[SC_SHIELD]);
9642 RaiseScore(level.score[SC_TIME_BONUS]);
9648 RaiseScore(level.score[SC_KEY]);
9651 RaiseScore(element_info[element].collect_score);
9656 void RequestQuitGame(boolean ask_if_really_quit)
9658 if (AllPlayersGone ||
9659 !ask_if_really_quit ||
9660 level_editor_test_game ||
9661 Request("Do you really want to quit the game ?",
9662 REQ_ASK | REQ_STAY_CLOSED))
9664 #if defined(PLATFORM_UNIX)
9665 if (options.network)
9666 SendToServer_StopPlaying();
9670 game_status = GAME_MODE_MAIN;
9676 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9681 /* ---------- new game button stuff ---------------------------------------- */
9683 /* graphic position values for game buttons */
9684 #define GAME_BUTTON_XSIZE 30
9685 #define GAME_BUTTON_YSIZE 30
9686 #define GAME_BUTTON_XPOS 5
9687 #define GAME_BUTTON_YPOS 215
9688 #define SOUND_BUTTON_XPOS 5
9689 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9691 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9692 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9693 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9694 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9695 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9696 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9703 } gamebutton_info[NUM_GAME_BUTTONS] =
9706 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9711 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9716 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9721 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9722 SOUND_CTRL_ID_MUSIC,
9723 "background music on/off"
9726 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9727 SOUND_CTRL_ID_LOOPS,
9728 "sound loops on/off"
9731 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9732 SOUND_CTRL_ID_SIMPLE,
9733 "normal sounds on/off"
9737 void CreateGameButtons()
9741 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9743 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9744 struct GadgetInfo *gi;
9747 unsigned long event_mask;
9748 int gd_xoffset, gd_yoffset;
9749 int gd_x1, gd_x2, gd_y1, gd_y2;
9752 gd_xoffset = gamebutton_info[i].x;
9753 gd_yoffset = gamebutton_info[i].y;
9754 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9755 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9757 if (id == GAME_CTRL_ID_STOP ||
9758 id == GAME_CTRL_ID_PAUSE ||
9759 id == GAME_CTRL_ID_PLAY)
9761 button_type = GD_TYPE_NORMAL_BUTTON;
9763 event_mask = GD_EVENT_RELEASED;
9764 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9765 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9769 button_type = GD_TYPE_CHECK_BUTTON;
9771 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9772 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9773 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9774 event_mask = GD_EVENT_PRESSED;
9775 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9776 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9779 gi = CreateGadget(GDI_CUSTOM_ID, id,
9780 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9781 GDI_X, DX + gd_xoffset,
9782 GDI_Y, DY + gd_yoffset,
9783 GDI_WIDTH, GAME_BUTTON_XSIZE,
9784 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9785 GDI_TYPE, button_type,
9786 GDI_STATE, GD_BUTTON_UNPRESSED,
9787 GDI_CHECKED, checked,
9788 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9789 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9790 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9791 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9792 GDI_EVENT_MASK, event_mask,
9793 GDI_CALLBACK_ACTION, HandleGameButtons,
9797 Error(ERR_EXIT, "cannot create gadget");
9799 game_gadget[id] = gi;
9803 void FreeGameButtons()
9807 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9808 FreeGadget(game_gadget[i]);
9811 static void MapGameButtons()
9815 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9816 MapGadget(game_gadget[i]);
9819 void UnmapGameButtons()
9823 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9824 UnmapGadget(game_gadget[i]);
9827 static void HandleGameButtons(struct GadgetInfo *gi)
9829 int id = gi->custom_id;
9831 if (game_status != GAME_MODE_PLAYING)
9836 case GAME_CTRL_ID_STOP:
9837 RequestQuitGame(TRUE);
9840 case GAME_CTRL_ID_PAUSE:
9841 if (options.network)
9843 #if defined(PLATFORM_UNIX)
9845 SendToServer_ContinuePlaying();
9847 SendToServer_PausePlaying();
9851 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9854 case GAME_CTRL_ID_PLAY:
9857 #if defined(PLATFORM_UNIX)
9858 if (options.network)
9859 SendToServer_ContinuePlaying();
9863 tape.pausing = FALSE;
9864 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9869 case SOUND_CTRL_ID_MUSIC:
9870 if (setup.sound_music)
9872 setup.sound_music = FALSE;
9875 else if (audio.music_available)
9877 setup.sound = setup.sound_music = TRUE;
9879 SetAudioMode(setup.sound);
9885 case SOUND_CTRL_ID_LOOPS:
9886 if (setup.sound_loops)
9887 setup.sound_loops = FALSE;
9888 else if (audio.loops_available)
9890 setup.sound = setup.sound_loops = TRUE;
9891 SetAudioMode(setup.sound);
9895 case SOUND_CTRL_ID_SIMPLE:
9896 if (setup.sound_simple)
9897 setup.sound_simple = FALSE;
9898 else if (audio.sound_available)
9900 setup.sound = setup.sound_simple = TRUE;
9901 SetAudioMode(setup.sound);