1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
106 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (DONT_COLLIDE_WITH(e) && \
110 !PLAYER_PROTECTED(x, y))))
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
113 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
115 (DONT_COLLIDE_WITH(e) && \
116 IS_FREE_OR_PLAYER(x, y))))
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
120 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
124 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
127 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
130 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
134 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
135 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
136 Feld[x][y] == EL_DIAMOND))
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
142 #define PACMAN_CAN_ENTER_FIELD(x, y) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
144 IS_AMOEBOID(Feld[x][y])))
146 #define PIG_CAN_ENTER_FIELD(x, y) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 IS_FOOD_PIG(Feld[x][y])))
150 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 IS_FOOD_PENGUIN(Feld[x][y]) || \
153 Feld[x][y] == EL_EXIT_OPEN || \
154 Feld[x][y] == EL_ACID))
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
159 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
167 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
168 #define IS_IN_GROUP(e, g) (element_info[e].in_group[g] == TRUE)
169 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
171 #define CE_ENTER_FIELD_COND(e, x, y) \
172 (!IS_PLAYER(x, y) && \
173 (Feld[x][y] == EL_ACID || \
174 Feld[x][y] == MOVE_ENTER_EL(e) || \
175 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
176 IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
178 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
181 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
182 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
184 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
185 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
187 /* game button identifiers */
188 #define GAME_CTRL_ID_STOP 0
189 #define GAME_CTRL_ID_PAUSE 1
190 #define GAME_CTRL_ID_PLAY 2
191 #define SOUND_CTRL_ID_MUSIC 3
192 #define SOUND_CTRL_ID_LOOPS 4
193 #define SOUND_CTRL_ID_SIMPLE 5
195 #define NUM_GAME_BUTTONS 6
198 /* forward declaration for internal use */
200 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
201 static boolean MovePlayer(struct PlayerInfo *, int, int);
202 static void ScrollPlayer(struct PlayerInfo *, int);
203 static void ScrollScreen(struct PlayerInfo *, int);
205 static void InitBeltMovement(void);
206 static void CloseAllOpenTimegates(void);
207 static void CheckGravityMovement(struct PlayerInfo *);
208 static void KillHeroUnlessProtected(int, int);
210 static void TestIfPlayerTouchesCustomElement(int, int);
211 static void TestIfElementTouchesCustomElement(int, int);
212 static void TestIfElementHitsCustomElement(int, int, int);
214 static void ChangeElement(int, int, int);
215 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
216 static boolean CheckTriggeredElementChange(int, int, int, int);
217 static boolean CheckElementSideChange(int, int, int, int, int, int);
218 static boolean CheckElementChange(int, int, int, int);
220 static void PlayLevelSound(int, int, int);
221 static void PlayLevelSoundNearest(int, int, int);
222 static void PlayLevelSoundAction(int, int, int);
223 static void PlayLevelSoundElementAction(int, int, int, int);
224 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
225 static void PlayLevelSoundActionIfLoop(int, int, int);
226 static void StopLevelSoundActionIfLoop(int, int, int);
227 static void PlayLevelMusic();
229 static void MapGameButtons();
230 static void HandleGameButtons(struct GadgetInfo *);
232 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
235 /* ------------------------------------------------------------------------- */
236 /* definition of elements that automatically change to other elements after */
237 /* a specified time, eventually calling a function when changing */
238 /* ------------------------------------------------------------------------- */
240 /* forward declaration for changer functions */
241 static void InitBuggyBase(int x, int y);
242 static void WarnBuggyBase(int x, int y);
244 static void InitTrap(int x, int y);
245 static void ActivateTrap(int x, int y);
246 static void ChangeActiveTrap(int x, int y);
248 static void InitRobotWheel(int x, int y);
249 static void RunRobotWheel(int x, int y);
250 static void StopRobotWheel(int x, int y);
252 static void InitTimegateWheel(int x, int y);
253 static void RunTimegateWheel(int x, int y);
255 struct ChangingElementInfo
260 void (*pre_change_function)(int x, int y);
261 void (*change_function)(int x, int y);
262 void (*post_change_function)(int x, int y);
265 static struct ChangingElementInfo change_delay_list[] =
316 EL_SWITCHGATE_OPENING,
324 EL_SWITCHGATE_CLOSING,
325 EL_SWITCHGATE_CLOSED,
357 EL_ACID_SPLASH_RIGHT,
366 EL_SP_BUGGY_BASE_ACTIVATING,
373 EL_SP_BUGGY_BASE_ACTIVATING,
374 EL_SP_BUGGY_BASE_ACTIVE,
381 EL_SP_BUGGY_BASE_ACTIVE,
405 EL_ROBOT_WHEEL_ACTIVE,
413 EL_TIMEGATE_SWITCH_ACTIVE,
434 int push_delay_fixed, push_delay_random;
439 { EL_BALLOON, 0, 0 },
441 { EL_SOKOBAN_OBJECT, 2, 0 },
442 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
443 { EL_SATELLITE, 2, 0 },
444 { EL_SP_DISK_YELLOW, 2, 0 },
446 { EL_UNDEFINED, 0, 0 },
454 move_stepsize_list[] =
456 { EL_AMOEBA_DROP, 2 },
457 { EL_AMOEBA_DROPPING, 2 },
458 { EL_QUICKSAND_FILLING, 1 },
459 { EL_QUICKSAND_EMPTYING, 1 },
460 { EL_MAGIC_WALL_FILLING, 2 },
461 { EL_BD_MAGIC_WALL_FILLING, 2 },
462 { EL_MAGIC_WALL_EMPTYING, 2 },
463 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
473 collect_count_list[] =
476 { EL_BD_DIAMOND, 1 },
477 { EL_EMERALD_YELLOW, 1 },
478 { EL_EMERALD_RED, 1 },
479 { EL_EMERALD_PURPLE, 1 },
481 { EL_SP_INFOTRON, 1 },
488 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
490 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
491 CH_EVENT_BIT(CE_DELAY))
492 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
493 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
494 IS_JUST_CHANGING(x, y))
496 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
499 void GetPlayerConfig()
501 if (!audio.sound_available)
502 setup.sound_simple = FALSE;
504 if (!audio.loops_available)
505 setup.sound_loops = FALSE;
507 if (!audio.music_available)
508 setup.sound_music = FALSE;
510 if (!video.fullscreen_available)
511 setup.fullscreen = FALSE;
513 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
515 SetAudioMode(setup.sound);
519 static int getBeltNrFromBeltElement(int element)
521 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
522 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
523 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
526 static int getBeltNrFromBeltActiveElement(int element)
528 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
529 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
530 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
533 static int getBeltNrFromBeltSwitchElement(int element)
535 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
536 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
537 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
540 static int getBeltDirNrFromBeltSwitchElement(int element)
542 static int belt_base_element[4] =
544 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
545 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
546 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
547 EL_CONVEYOR_BELT_4_SWITCH_LEFT
550 int belt_nr = getBeltNrFromBeltSwitchElement(element);
551 int belt_dir_nr = element - belt_base_element[belt_nr];
553 return (belt_dir_nr % 3);
556 static int getBeltDirFromBeltSwitchElement(int element)
558 static int belt_move_dir[3] =
565 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
567 return belt_move_dir[belt_dir_nr];
570 static void InitPlayerField(int x, int y, int element, boolean init_game)
572 if (element == EL_SP_MURPHY)
576 if (stored_player[0].present)
578 Feld[x][y] = EL_SP_MURPHY_CLONE;
584 stored_player[0].use_murphy_graphic = TRUE;
587 Feld[x][y] = EL_PLAYER_1;
593 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
594 int jx = player->jx, jy = player->jy;
596 player->present = TRUE;
598 if (!options.network || player->connected)
600 player->active = TRUE;
602 /* remove potentially duplicate players */
603 if (StorePlayer[jx][jy] == Feld[x][y])
604 StorePlayer[jx][jy] = 0;
606 StorePlayer[x][y] = Feld[x][y];
610 printf("Player %d activated.\n", player->element_nr);
611 printf("[Local player is %d and currently %s.]\n",
612 local_player->element_nr,
613 local_player->active ? "active" : "not active");
617 Feld[x][y] = EL_EMPTY;
618 player->jx = player->last_jx = x;
619 player->jy = player->last_jy = y;
623 static void InitField(int x, int y, boolean init_game)
625 int element = Feld[x][y];
634 InitPlayerField(x, y, element, init_game);
637 case EL_SOKOBAN_FIELD_PLAYER:
638 element = Feld[x][y] = EL_PLAYER_1;
639 InitField(x, y, init_game);
641 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
642 InitField(x, y, init_game);
645 case EL_SOKOBAN_FIELD_EMPTY:
646 local_player->sokobanfields_still_needed++;
650 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
651 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
652 else if (x > 0 && Feld[x-1][y] == EL_ACID)
653 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
654 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
655 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
656 else if (y > 0 && Feld[x][y-1] == EL_ACID)
657 Feld[x][y] = EL_ACID_POOL_BOTTOM;
658 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
659 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
667 case EL_SPACESHIP_RIGHT:
668 case EL_SPACESHIP_UP:
669 case EL_SPACESHIP_LEFT:
670 case EL_SPACESHIP_DOWN:
672 case EL_BD_BUTTERFLY_RIGHT:
673 case EL_BD_BUTTERFLY_UP:
674 case EL_BD_BUTTERFLY_LEFT:
675 case EL_BD_BUTTERFLY_DOWN:
676 case EL_BD_BUTTERFLY:
677 case EL_BD_FIREFLY_RIGHT:
678 case EL_BD_FIREFLY_UP:
679 case EL_BD_FIREFLY_LEFT:
680 case EL_BD_FIREFLY_DOWN:
682 case EL_PACMAN_RIGHT:
706 if (y == lev_fieldy - 1)
708 Feld[x][y] = EL_AMOEBA_GROWING;
709 Store[x][y] = EL_AMOEBA_WET;
713 case EL_DYNAMITE_ACTIVE:
714 case EL_SP_DISK_RED_ACTIVE:
715 case EL_DYNABOMB_PLAYER_1_ACTIVE:
716 case EL_DYNABOMB_PLAYER_2_ACTIVE:
717 case EL_DYNABOMB_PLAYER_3_ACTIVE:
718 case EL_DYNABOMB_PLAYER_4_ACTIVE:
723 local_player->lights_still_needed++;
727 local_player->friends_still_needed++;
732 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
737 Feld[x][y] = EL_EMPTY;
742 case EL_EM_KEY_1_FILE:
743 Feld[x][y] = EL_EM_KEY_1;
745 case EL_EM_KEY_2_FILE:
746 Feld[x][y] = EL_EM_KEY_2;
748 case EL_EM_KEY_3_FILE:
749 Feld[x][y] = EL_EM_KEY_3;
751 case EL_EM_KEY_4_FILE:
752 Feld[x][y] = EL_EM_KEY_4;
756 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
757 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
758 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
759 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
760 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
761 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
762 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
763 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
764 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
765 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
766 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
767 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
770 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
771 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
772 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
774 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
776 game.belt_dir[belt_nr] = belt_dir;
777 game.belt_dir_nr[belt_nr] = belt_dir_nr;
779 else /* more than one switch -- set it like the first switch */
781 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
786 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
788 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
791 case EL_LIGHT_SWITCH_ACTIVE:
793 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
797 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
799 else if (IS_GROUP_ELEMENT(element))
801 struct ElementGroupInfo *group = element_info[element].group;
802 int random_pos = RND(group->num_elements_resolved);
804 Feld[x][y] = group->element_resolved[random_pos];
806 InitField(x, y, init_game);
812 void DrawGameDoorValues()
816 for (i = 0; i < MAX_PLAYERS; i++)
817 for (j = 0; j < 4; j++)
818 if (stored_player[i].key[j])
819 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
820 el2edimg(EL_KEY_1 + j));
822 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
823 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
824 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
825 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
826 DrawText(DX + XX_SCORE, DY + YY_SCORE,
827 int2str(local_player->score, 5), FONT_TEXT_2);
828 DrawText(DX + XX_TIME, DY + YY_TIME,
829 int2str(TimeLeft, 3), FONT_TEXT_2);
832 static void resolve_group_element(int group_element, int recursion_depth)
835 static struct ElementGroupInfo *group;
836 struct ElementGroupInfo *actual_group = element_info[group_element].group;
839 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
841 Error(ERR_WARN, "recursion too deep when resolving group element %d",
842 group_element - EL_GROUP_START + 1);
844 /* replace element which caused too deep recursion by question mark */
845 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
850 if (recursion_depth == 0) /* initialization */
852 group = element_info[group_element].group;
853 group->num_elements_resolved = 0;
854 group_nr = group_element - EL_GROUP_START;
857 for (i = 0; i < actual_group->num_elements; i++)
859 int element = actual_group->element[i];
861 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
864 if (IS_GROUP_ELEMENT(element))
865 resolve_group_element(element, recursion_depth + 1);
868 group->element_resolved[group->num_elements_resolved++] = element;
869 element_info[element].in_group[group_nr] = TRUE;
874 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
876 printf("::: group %d: %d resolved elements\n",
877 group_element - EL_GROUP_START, group->num_elements_resolved);
878 for (i = 0; i < group->num_elements_resolved; i++)
879 printf("::: - %d ['%s']\n", group->element_resolved[i],
880 element_info[group->element_resolved[i]].token_name);
887 =============================================================================
889 -----------------------------------------------------------------------------
890 initialize game engine due to level / tape version number
891 =============================================================================
894 static void InitGameEngine()
898 /* set game engine from tape file when re-playing, else from level file */
899 game.engine_version = (tape.playing ? tape.engine_version :
902 /* dynamically adjust element properties according to game engine version */
903 InitElementPropertiesEngine(game.engine_version);
906 printf("level %d: level version == %06d\n", level_nr, level.game_version);
907 printf(" tape version == %06d [%s] [file: %06d]\n",
908 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
910 printf(" => game.engine_version == %06d\n", game.engine_version);
913 /* ---------- recursively resolve group elements ------------------------- */
915 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
916 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
917 element_info[i].in_group[j] = FALSE;
919 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
920 resolve_group_element(EL_GROUP_START + i, 0);
922 /* ---------- initialize player's initial move delay --------------------- */
924 /* dynamically adjust player properties according to game engine version */
925 game.initial_move_delay =
926 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
927 INITIAL_MOVE_DELAY_OFF);
929 /* dynamically adjust player properties according to level information */
930 game.initial_move_delay_value =
931 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
933 /* ---------- initialize player's initial push delay --------------------- */
935 /* dynamically adjust player properties according to game engine version */
936 game.initial_push_delay_value =
937 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
939 /* ---------- initialize changing elements ------------------------------- */
941 /* initialize changing elements information */
942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
944 struct ElementInfo *ei = &element_info[i];
946 /* this pointer might have been changed in the level editor */
947 ei->change = &ei->change_page[0];
949 if (!IS_CUSTOM_ELEMENT(i))
951 ei->change->target_element = EL_EMPTY_SPACE;
952 ei->change->delay_fixed = 0;
953 ei->change->delay_random = 0;
954 ei->change->delay_frames = 1;
957 ei->change_events = CE_BITMASK_DEFAULT;
958 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
960 ei->event_page_nr[j] = 0;
961 ei->event_page[j] = &ei->change_page[0];
965 /* add changing elements from pre-defined list */
966 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
968 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
969 struct ElementInfo *ei = &element_info[ch_delay->element];
971 ei->change->target_element = ch_delay->target_element;
972 ei->change->delay_fixed = ch_delay->change_delay;
974 ei->change->pre_change_function = ch_delay->pre_change_function;
975 ei->change->change_function = ch_delay->change_function;
976 ei->change->post_change_function = ch_delay->post_change_function;
978 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
982 /* add change events from custom element configuration */
983 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
985 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
987 for (j = 0; j < ei->num_change_pages; j++)
989 if (!ei->change_page[j].can_change)
992 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
994 /* only add event page for the first page found with this event */
995 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
996 !(ei->change_events & CH_EVENT_BIT(k)))
998 ei->change_events |= CH_EVENT_BIT(k);
999 ei->event_page_nr[k] = j;
1000 ei->event_page[k] = &ei->change_page[j];
1008 /* add change events from custom element configuration */
1009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1011 int element = EL_CUSTOM_START + i;
1013 /* only add custom elements that change after fixed/random frame delay */
1014 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1015 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1019 /* ---------- initialize trigger events ---------------------------------- */
1021 /* initialize trigger events information */
1022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1023 trigger_events[i] = EP_BITMASK_DEFAULT;
1026 /* add trigger events from element change event properties */
1027 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1029 struct ElementInfo *ei = &element_info[i];
1031 for (j = 0; j < ei->num_change_pages; j++)
1033 if (!ei->change_page[j].can_change)
1036 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1038 int trigger_element = ei->change_page[j].trigger_element;
1040 trigger_events[trigger_element] |= ei->change_page[j].events;
1045 /* add trigger events from element change event properties */
1046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1047 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1048 trigger_events[element_info[i].change->trigger_element] |=
1049 element_info[i].change->events;
1052 /* ---------- initialize push delay -------------------------------------- */
1054 /* initialize push delay values to default */
1055 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1057 if (!IS_CUSTOM_ELEMENT(i))
1059 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1060 element_info[i].push_delay_random = game.default_push_delay_random;
1064 /* set push delay value for certain elements from pre-defined list */
1065 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1067 int e = push_delay_list[i].element;
1069 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1070 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1073 /* ---------- initialize move stepsize ----------------------------------- */
1075 /* initialize move stepsize values to default */
1076 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1077 if (!IS_CUSTOM_ELEMENT(i))
1078 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1080 /* set move stepsize value for certain elements from pre-defined list */
1081 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1083 int e = move_stepsize_list[i].element;
1085 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1088 /* ---------- initialize gem count --------------------------------------- */
1090 /* initialize gem count values for each element */
1091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1092 if (!IS_CUSTOM_ELEMENT(i))
1093 element_info[i].collect_count = 0;
1095 /* add gem count values for all elements from pre-defined list */
1096 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1097 element_info[collect_count_list[i].element].collect_count =
1098 collect_count_list[i].count;
1103 =============================================================================
1105 -----------------------------------------------------------------------------
1106 initialize and start new game
1107 =============================================================================
1112 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1113 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1114 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1121 #if USE_NEW_AMOEBA_CODE
1122 printf("Using new amoeba code.\n");
1124 printf("Using old amoeba code.\n");
1129 /* don't play tapes over network */
1130 network_playing = (options.network && !tape.playing);
1132 for (i = 0; i < MAX_PLAYERS; i++)
1134 struct PlayerInfo *player = &stored_player[i];
1136 player->index_nr = i;
1137 player->element_nr = EL_PLAYER_1 + i;
1139 player->present = FALSE;
1140 player->active = FALSE;
1143 player->effective_action = 0;
1144 player->programmed_action = 0;
1147 player->gems_still_needed = level.gems_needed;
1148 player->sokobanfields_still_needed = 0;
1149 player->lights_still_needed = 0;
1150 player->friends_still_needed = 0;
1152 for (j = 0; j < 4; j++)
1153 player->key[j] = FALSE;
1155 player->dynabomb_count = 0;
1156 player->dynabomb_size = 1;
1157 player->dynabombs_left = 0;
1158 player->dynabomb_xl = FALSE;
1160 player->MovDir = MV_NO_MOVING;
1163 player->GfxDir = MV_NO_MOVING;
1164 player->GfxAction = ACTION_DEFAULT;
1166 player->StepFrame = 0;
1168 player->use_murphy_graphic = FALSE;
1170 player->actual_frame_counter = 0;
1172 player->step_counter = 0;
1174 player->last_move_dir = MV_NO_MOVING;
1176 player->is_waiting = FALSE;
1177 player->is_moving = FALSE;
1178 player->is_digging = FALSE;
1179 player->is_snapping = FALSE;
1180 player->is_collecting = FALSE;
1181 player->is_pushing = FALSE;
1182 player->is_switching = FALSE;
1183 player->is_dropping = FALSE;
1185 player->is_bored = FALSE;
1186 player->is_sleeping = FALSE;
1188 player->frame_counter_bored = -1;
1189 player->frame_counter_sleeping = -1;
1191 player->anim_delay_counter = 0;
1192 player->post_delay_counter = 0;
1194 player->action_waiting = ACTION_DEFAULT;
1195 player->last_action_waiting = ACTION_DEFAULT;
1196 player->special_action_bored = ACTION_DEFAULT;
1197 player->special_action_sleeping = ACTION_DEFAULT;
1199 player->num_special_action_bored = 0;
1200 player->num_special_action_sleeping = 0;
1202 /* determine number of special actions for bored and sleeping animation */
1203 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1205 boolean found = FALSE;
1207 for (k = 0; k < NUM_DIRECTIONS; k++)
1208 if (el_act_dir2img(player->element_nr, j, k) !=
1209 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1213 player->num_special_action_bored++;
1217 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1219 boolean found = FALSE;
1221 for (k = 0; k < NUM_DIRECTIONS; k++)
1222 if (el_act_dir2img(player->element_nr, j, k) !=
1223 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1227 player->num_special_action_sleeping++;
1232 player->switch_x = -1;
1233 player->switch_y = -1;
1235 player->show_envelope = 0;
1237 player->move_delay = game.initial_move_delay;
1238 player->move_delay_value = game.initial_move_delay_value;
1240 player->move_delay_reset_counter = 0;
1242 player->push_delay = 0;
1243 player->push_delay_value = game.initial_push_delay_value;
1245 player->drop_delay = 0;
1247 player->last_jx = player->last_jy = 0;
1248 player->jx = player->jy = 0;
1250 player->shield_normal_time_left = 0;
1251 player->shield_deadly_time_left = 0;
1253 player->inventory_size = 0;
1255 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1256 SnapField(player, 0, 0);
1258 player->LevelSolved = FALSE;
1259 player->GameOver = FALSE;
1262 network_player_action_received = FALSE;
1264 #if defined(PLATFORM_UNIX)
1265 /* initial null action */
1266 if (network_playing)
1267 SendToServer_MovePlayer(MV_NO_MOVING);
1275 TimeLeft = level.time;
1277 ScreenMovDir = MV_NO_MOVING;
1281 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1283 AllPlayersGone = FALSE;
1285 game.yamyam_content_nr = 0;
1286 game.magic_wall_active = FALSE;
1287 game.magic_wall_time_left = 0;
1288 game.light_time_left = 0;
1289 game.timegate_time_left = 0;
1290 game.switchgate_pos = 0;
1291 game.balloon_dir = MV_NO_MOVING;
1292 game.gravity = level.initial_gravity;
1293 game.explosions_delayed = TRUE;
1295 game.envelope_active = FALSE;
1297 for (i = 0; i < 4; i++)
1299 game.belt_dir[i] = MV_NO_MOVING;
1300 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1303 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1304 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1306 for (x = 0; x < lev_fieldx; x++)
1308 for (y = 0; y < lev_fieldy; y++)
1310 Feld[x][y] = level.field[x][y];
1311 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1312 ChangeDelay[x][y] = 0;
1313 ChangePage[x][y] = -1;
1314 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1316 WasJustMoving[x][y] = 0;
1317 WasJustFalling[x][y] = 0;
1319 Pushed[x][y] = FALSE;
1321 Changed[x][y] = CE_BITMASK_DEFAULT;
1322 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1324 ExplodePhase[x][y] = 0;
1325 ExplodeField[x][y] = EX_NO_EXPLOSION;
1327 RunnerVisit[x][y] = 0;
1328 PlayerVisit[x][y] = 0;
1331 GfxRandom[x][y] = INIT_GFX_RANDOM();
1332 GfxElement[x][y] = EL_UNDEFINED;
1333 GfxAction[x][y] = ACTION_DEFAULT;
1334 GfxDir[x][y] = MV_NO_MOVING;
1338 for (y = 0; y < lev_fieldy; y++)
1340 for (x = 0; x < lev_fieldx; x++)
1342 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1344 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1346 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1349 InitField(x, y, TRUE);
1355 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1356 emulate_sb ? EMU_SOKOBAN :
1357 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1359 /* correct non-moving belts to start moving left */
1360 for (i = 0; i < 4; i++)
1361 if (game.belt_dir[i] == MV_NO_MOVING)
1362 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1364 /* check if any connected player was not found in playfield */
1365 for (i = 0; i < MAX_PLAYERS; i++)
1367 struct PlayerInfo *player = &stored_player[i];
1369 if (player->connected && !player->present)
1371 for (j = 0; j < MAX_PLAYERS; j++)
1373 struct PlayerInfo *some_player = &stored_player[j];
1374 int jx = some_player->jx, jy = some_player->jy;
1376 /* assign first free player found that is present in the playfield */
1377 if (some_player->present && !some_player->connected)
1379 player->present = TRUE;
1380 player->active = TRUE;
1381 some_player->present = FALSE;
1383 StorePlayer[jx][jy] = player->element_nr;
1384 player->jx = player->last_jx = jx;
1385 player->jy = player->last_jy = jy;
1395 /* when playing a tape, eliminate all players who do not participate */
1397 for (i = 0; i < MAX_PLAYERS; i++)
1399 if (stored_player[i].active && !tape.player_participates[i])
1401 struct PlayerInfo *player = &stored_player[i];
1402 int jx = player->jx, jy = player->jy;
1404 player->active = FALSE;
1405 StorePlayer[jx][jy] = 0;
1406 Feld[jx][jy] = EL_EMPTY;
1410 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1412 /* when in single player mode, eliminate all but the first active player */
1414 for (i = 0; i < MAX_PLAYERS; i++)
1416 if (stored_player[i].active)
1418 for (j = i + 1; j < MAX_PLAYERS; j++)
1420 if (stored_player[j].active)
1422 struct PlayerInfo *player = &stored_player[j];
1423 int jx = player->jx, jy = player->jy;
1425 player->active = FALSE;
1426 StorePlayer[jx][jy] = 0;
1427 Feld[jx][jy] = EL_EMPTY;
1434 /* when recording the game, store which players take part in the game */
1437 for (i = 0; i < MAX_PLAYERS; i++)
1438 if (stored_player[i].active)
1439 tape.player_participates[i] = TRUE;
1444 for (i = 0; i < MAX_PLAYERS; i++)
1446 struct PlayerInfo *player = &stored_player[i];
1448 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1453 if (local_player == player)
1454 printf("Player %d is local player.\n", i+1);
1458 if (BorderElement == EL_EMPTY)
1461 SBX_Right = lev_fieldx - SCR_FIELDX;
1463 SBY_Lower = lev_fieldy - SCR_FIELDY;
1468 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1470 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1473 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1474 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1476 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1477 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1479 /* if local player not found, look for custom element that might create
1480 the player (make some assumptions about the right custom element) */
1481 if (!local_player->present)
1483 int start_x = 0, start_y = 0;
1484 int found_rating = 0;
1485 int found_element = EL_UNDEFINED;
1487 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1489 int element = Feld[x][y];
1494 if (!IS_CUSTOM_ELEMENT(element))
1497 if (CAN_CHANGE(element))
1499 for (i = 0; i < element_info[element].num_change_pages; i++)
1501 content = element_info[element].change_page[i].target_element;
1502 is_player = ELEM_IS_PLAYER(content);
1504 if (is_player && (found_rating < 3 || element < found_element))
1510 found_element = element;
1515 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1517 content = element_info[element].content[xx][yy];
1518 is_player = ELEM_IS_PLAYER(content);
1520 if (is_player && (found_rating < 2 || element < found_element))
1522 start_x = x + xx - 1;
1523 start_y = y + yy - 1;
1526 found_element = element;
1529 if (!CAN_CHANGE(element))
1532 for (i = 0; i < element_info[element].num_change_pages; i++)
1534 content = element_info[element].change_page[i].content[xx][yy];
1535 is_player = ELEM_IS_PLAYER(content);
1537 if (is_player && (found_rating < 1 || element < found_element))
1539 start_x = x + xx - 1;
1540 start_y = y + yy - 1;
1543 found_element = element;
1549 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1550 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1553 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1554 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1560 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1561 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1562 local_player->jx - MIDPOSX);
1564 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1565 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1566 local_player->jy - MIDPOSY);
1568 scroll_x = SBX_Left;
1569 scroll_y = SBY_Upper;
1570 if (local_player->jx >= SBX_Left + MIDPOSX)
1571 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1572 local_player->jx - MIDPOSX :
1574 if (local_player->jy >= SBY_Upper + MIDPOSY)
1575 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1576 local_player->jy - MIDPOSY :
1581 CloseDoor(DOOR_CLOSE_1);
1586 /* after drawing the level, correct some elements */
1587 if (game.timegate_time_left == 0)
1588 CloseAllOpenTimegates();
1590 if (setup.soft_scrolling)
1591 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1593 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1596 /* copy default game door content to main double buffer */
1597 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1598 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1601 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1604 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1605 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1606 BlitBitmap(drawto, drawto,
1607 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1608 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1609 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1610 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1613 DrawGameDoorValues();
1617 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1618 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1619 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1623 /* copy actual game door content to door double buffer for OpenDoor() */
1624 BlitBitmap(drawto, bitmap_db_door,
1625 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1627 OpenDoor(DOOR_OPEN_ALL);
1629 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1631 if (setup.sound_music)
1634 KeyboardAutoRepeatOffUnlessAutoplay();
1638 for (i = 0; i < 4; i++)
1639 printf("Player %d %sactive.\n",
1640 i + 1, (stored_player[i].active ? "" : "not "));
1644 printf("::: starting game [%d]\n", FrameCounter);
1648 void InitMovDir(int x, int y)
1650 int i, element = Feld[x][y];
1651 static int xy[4][2] =
1658 static int direction[3][4] =
1660 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1661 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1662 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1671 Feld[x][y] = EL_BUG;
1672 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1675 case EL_SPACESHIP_RIGHT:
1676 case EL_SPACESHIP_UP:
1677 case EL_SPACESHIP_LEFT:
1678 case EL_SPACESHIP_DOWN:
1679 Feld[x][y] = EL_SPACESHIP;
1680 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1683 case EL_BD_BUTTERFLY_RIGHT:
1684 case EL_BD_BUTTERFLY_UP:
1685 case EL_BD_BUTTERFLY_LEFT:
1686 case EL_BD_BUTTERFLY_DOWN:
1687 Feld[x][y] = EL_BD_BUTTERFLY;
1688 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1691 case EL_BD_FIREFLY_RIGHT:
1692 case EL_BD_FIREFLY_UP:
1693 case EL_BD_FIREFLY_LEFT:
1694 case EL_BD_FIREFLY_DOWN:
1695 Feld[x][y] = EL_BD_FIREFLY;
1696 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1699 case EL_PACMAN_RIGHT:
1701 case EL_PACMAN_LEFT:
1702 case EL_PACMAN_DOWN:
1703 Feld[x][y] = EL_PACMAN;
1704 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1707 case EL_SP_SNIKSNAK:
1708 MovDir[x][y] = MV_UP;
1711 case EL_SP_ELECTRON:
1712 MovDir[x][y] = MV_LEFT;
1719 Feld[x][y] = EL_MOLE;
1720 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1724 if (IS_CUSTOM_ELEMENT(element))
1726 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1727 MovDir[x][y] = element_info[element].move_direction_initial;
1728 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1729 element_info[element].move_pattern == MV_TURNING_LEFT ||
1730 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1731 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1732 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1733 element_info[element].move_pattern == MV_TURNING_RANDOM)
1734 MovDir[x][y] = 1 << RND(4);
1735 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1736 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1737 else if (element_info[element].move_pattern == MV_VERTICAL)
1738 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1739 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1740 MovDir[x][y] = element_info[element].move_pattern;
1741 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1742 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1744 for (i = 0; i < 4; i++)
1746 int x1 = x + xy[i][0];
1747 int y1 = y + xy[i][1];
1749 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1751 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1752 MovDir[x][y] = direction[0][i];
1754 MovDir[x][y] = direction[1][i];
1763 MovDir[x][y] = 1 << RND(4);
1765 if (element != EL_BUG &&
1766 element != EL_SPACESHIP &&
1767 element != EL_BD_BUTTERFLY &&
1768 element != EL_BD_FIREFLY)
1771 for (i = 0; i < 4; i++)
1773 int x1 = x + xy[i][0];
1774 int y1 = y + xy[i][1];
1776 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1778 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1780 MovDir[x][y] = direction[0][i];
1783 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1784 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1786 MovDir[x][y] = direction[1][i];
1795 GfxDir[x][y] = MovDir[x][y];
1798 void InitAmoebaNr(int x, int y)
1801 int group_nr = AmoebeNachbarNr(x, y);
1805 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1807 if (AmoebaCnt[i] == 0)
1815 AmoebaNr[x][y] = group_nr;
1816 AmoebaCnt[group_nr]++;
1817 AmoebaCnt2[group_nr]++;
1823 boolean raise_level = FALSE;
1825 if (local_player->MovPos)
1829 if (tape.auto_play) /* tape might already be stopped here */
1830 tape.auto_play_level_solved = TRUE;
1832 if (tape.playing && tape.auto_play)
1833 tape.auto_play_level_solved = TRUE;
1836 local_player->LevelSolved = FALSE;
1838 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1842 if (!tape.playing && setup.sound_loops)
1843 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1844 SND_CTRL_PLAY_LOOP);
1846 while (TimeLeft > 0)
1848 if (!tape.playing && !setup.sound_loops)
1849 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1850 if (TimeLeft > 0 && !(TimeLeft % 10))
1851 RaiseScore(level.score[SC_TIME_BONUS]);
1852 if (TimeLeft > 100 && !(TimeLeft % 10))
1856 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1863 if (!tape.playing && setup.sound_loops)
1864 StopSound(SND_GAME_LEVELTIME_BONUS);
1866 else if (level.time == 0) /* level without time limit */
1868 if (!tape.playing && setup.sound_loops)
1869 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1870 SND_CTRL_PLAY_LOOP);
1872 while (TimePlayed < 999)
1874 if (!tape.playing && !setup.sound_loops)
1875 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1876 if (TimePlayed < 999 && !(TimePlayed % 10))
1877 RaiseScore(level.score[SC_TIME_BONUS]);
1878 if (TimePlayed < 900 && !(TimePlayed % 10))
1882 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1889 if (!tape.playing && setup.sound_loops)
1890 StopSound(SND_GAME_LEVELTIME_BONUS);
1893 /* close exit door after last player */
1894 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1895 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1897 int element = Feld[ExitX][ExitY];
1899 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1900 EL_SP_EXIT_CLOSING);
1902 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1905 /* Hero disappears */
1906 DrawLevelField(ExitX, ExitY);
1912 CloseDoor(DOOR_CLOSE_1);
1917 SaveTape(tape.level_nr); /* Ask to save tape */
1920 if (level_nr == leveldir_current->handicap_level)
1922 leveldir_current->handicap_level++;
1923 SaveLevelSetup_SeriesInfo();
1926 if (level_editor_test_game)
1927 local_player->score = -1; /* no highscore when playing from editor */
1928 else if (level_nr < leveldir_current->last_level)
1929 raise_level = TRUE; /* advance to next level */
1931 if ((hi_pos = NewHiScore()) >= 0)
1933 game_status = GAME_MODE_SCORES;
1934 DrawHallOfFame(hi_pos);
1943 game_status = GAME_MODE_MAIN;
1960 LoadScore(level_nr);
1962 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1963 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1966 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1968 if (local_player->score > highscore[k].Score)
1970 /* player has made it to the hall of fame */
1972 if (k < MAX_SCORE_ENTRIES - 1)
1974 int m = MAX_SCORE_ENTRIES - 1;
1977 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1978 if (!strcmp(setup.player_name, highscore[l].Name))
1980 if (m == k) /* player's new highscore overwrites his old one */
1984 for (l = m; l > k; l--)
1986 strcpy(highscore[l].Name, highscore[l - 1].Name);
1987 highscore[l].Score = highscore[l - 1].Score;
1994 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1995 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1996 highscore[k].Score = local_player->score;
2002 else if (!strncmp(setup.player_name, highscore[k].Name,
2003 MAX_PLAYER_NAME_LEN))
2004 break; /* player already there with a higher score */
2010 SaveScore(level_nr);
2015 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2017 if (player->GfxAction != action || player->GfxDir != dir)
2020 printf("Player frame reset! (%d => %d, %d => %d)\n",
2021 player->GfxAction, action, player->GfxDir, dir);
2024 player->GfxAction = action;
2025 player->GfxDir = dir;
2027 player->StepFrame = 0;
2031 static void ResetRandomAnimationValue(int x, int y)
2033 GfxRandom[x][y] = INIT_GFX_RANDOM();
2036 static void ResetGfxAnimation(int x, int y)
2039 GfxAction[x][y] = ACTION_DEFAULT;
2040 GfxDir[x][y] = MovDir[x][y];
2043 void InitMovingField(int x, int y, int direction)
2045 int element = Feld[x][y];
2046 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2047 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2051 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2052 ResetGfxAnimation(x, y);
2054 MovDir[newx][newy] = MovDir[x][y] = direction;
2055 GfxDir[x][y] = direction;
2057 if (Feld[newx][newy] == EL_EMPTY)
2058 Feld[newx][newy] = EL_BLOCKED;
2060 if (direction == MV_DOWN && CAN_FALL(element))
2061 GfxAction[x][y] = ACTION_FALLING;
2063 GfxAction[x][y] = ACTION_MOVING;
2065 GfxFrame[newx][newy] = GfxFrame[x][y];
2066 GfxRandom[newx][newy] = GfxRandom[x][y];
2067 GfxAction[newx][newy] = GfxAction[x][y];
2068 GfxDir[newx][newy] = GfxDir[x][y];
2071 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2073 int direction = MovDir[x][y];
2074 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2075 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2081 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2083 int oldx = x, oldy = y;
2084 int direction = MovDir[x][y];
2086 if (direction == MV_LEFT)
2088 else if (direction == MV_RIGHT)
2090 else if (direction == MV_UP)
2092 else if (direction == MV_DOWN)
2095 *comes_from_x = oldx;
2096 *comes_from_y = oldy;
2099 int MovingOrBlocked2Element(int x, int y)
2101 int element = Feld[x][y];
2103 if (element == EL_BLOCKED)
2107 Blocked2Moving(x, y, &oldx, &oldy);
2108 return Feld[oldx][oldy];
2114 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2116 /* like MovingOrBlocked2Element(), but if element is moving
2117 and (x,y) is the field the moving element is just leaving,
2118 return EL_BLOCKED instead of the element value */
2119 int element = Feld[x][y];
2121 if (IS_MOVING(x, y))
2123 if (element == EL_BLOCKED)
2127 Blocked2Moving(x, y, &oldx, &oldy);
2128 return Feld[oldx][oldy];
2137 static void RemoveField(int x, int y)
2139 Feld[x][y] = EL_EMPTY;
2146 ChangeDelay[x][y] = 0;
2147 ChangePage[x][y] = -1;
2148 Pushed[x][y] = FALSE;
2150 GfxElement[x][y] = EL_UNDEFINED;
2151 GfxAction[x][y] = ACTION_DEFAULT;
2152 GfxDir[x][y] = MV_NO_MOVING;
2155 void RemoveMovingField(int x, int y)
2157 int oldx = x, oldy = y, newx = x, newy = y;
2158 int element = Feld[x][y];
2159 int next_element = EL_UNDEFINED;
2161 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2164 if (IS_MOVING(x, y))
2166 Moving2Blocked(x, y, &newx, &newy);
2167 if (Feld[newx][newy] != EL_BLOCKED)
2170 else if (element == EL_BLOCKED)
2172 Blocked2Moving(x, y, &oldx, &oldy);
2173 if (!IS_MOVING(oldx, oldy))
2177 if (element == EL_BLOCKED &&
2178 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2179 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2180 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2181 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2182 next_element = get_next_element(Feld[oldx][oldy]);
2184 RemoveField(oldx, oldy);
2185 RemoveField(newx, newy);
2187 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2189 if (next_element != EL_UNDEFINED)
2190 Feld[oldx][oldy] = next_element;
2192 DrawLevelField(oldx, oldy);
2193 DrawLevelField(newx, newy);
2196 void DrawDynamite(int x, int y)
2198 int sx = SCREENX(x), sy = SCREENY(y);
2199 int graphic = el2img(Feld[x][y]);
2202 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2205 if (IS_WALKABLE_INSIDE(Back[x][y]))
2209 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2210 else if (Store[x][y])
2211 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2213 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2216 if (Back[x][y] || Store[x][y])
2217 DrawGraphicThruMask(sx, sy, graphic, frame);
2219 DrawGraphic(sx, sy, graphic, frame);
2221 if (game.emulation == EMU_SUPAPLEX)
2222 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2223 else if (Store[x][y])
2224 DrawGraphicThruMask(sx, sy, graphic, frame);
2226 DrawGraphic(sx, sy, graphic, frame);
2230 void CheckDynamite(int x, int y)
2232 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2236 if (MovDelay[x][y] != 0)
2239 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2246 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2248 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2249 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2250 StopSound(SND_DYNAMITE_ACTIVE);
2252 StopSound(SND_DYNABOMB_ACTIVE);
2258 void RelocatePlayer(int x, int y, int element)
2260 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2262 if (player->GameOver) /* do not reanimate dead player */
2266 RemoveField(x, y); /* temporarily remove newly placed player */
2267 DrawLevelField(x, y);
2270 if (player->present)
2272 while (player->MovPos)
2274 ScrollPlayer(player, SCROLL_GO_ON);
2275 ScrollScreen(NULL, SCROLL_GO_ON);
2281 Delay(GAME_FRAME_DELAY);
2284 DrawPlayer(player); /* needed here only to cleanup last field */
2285 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2287 player->is_moving = FALSE;
2290 Feld[x][y] = element;
2291 InitPlayerField(x, y, element, TRUE);
2293 if (player == local_player)
2295 int scroll_xx = -999, scroll_yy = -999;
2297 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2300 int fx = FX, fy = FY;
2302 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2303 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2304 local_player->jx - MIDPOSX);
2306 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2307 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2308 local_player->jy - MIDPOSY);
2310 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2311 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2316 fx += dx * TILEX / 2;
2317 fy += dy * TILEY / 2;
2319 ScrollLevel(dx, dy);
2322 /* scroll in two steps of half tile size to make things smoother */
2323 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2325 Delay(GAME_FRAME_DELAY);
2327 /* scroll second step to align at full tile size */
2329 Delay(GAME_FRAME_DELAY);
2334 void Explode(int ex, int ey, int phase, int mode)
2338 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2339 int last_phase = num_phase * delay;
2340 int half_phase = (num_phase / 2) * delay;
2341 int first_phase_after_start = EX_PHASE_START + 1;
2343 if (game.explosions_delayed)
2345 ExplodeField[ex][ey] = mode;
2349 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2351 int center_element = Feld[ex][ey];
2354 /* --- This is only really needed (and now handled) in "Impact()". --- */
2355 /* do not explode moving elements that left the explode field in time */
2356 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2357 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2361 if (mode == EX_NORMAL || mode == EX_CENTER)
2362 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2364 /* remove things displayed in background while burning dynamite */
2365 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2368 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2370 /* put moving element to center field (and let it explode there) */
2371 center_element = MovingOrBlocked2Element(ex, ey);
2372 RemoveMovingField(ex, ey);
2373 Feld[ex][ey] = center_element;
2376 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2378 int xx = x - ex + 1;
2379 int yy = y - ey + 1;
2382 if (!IN_LEV_FIELD(x, y) ||
2383 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2384 (x != ex || y != ey)))
2387 element = Feld[x][y];
2389 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2391 element = MovingOrBlocked2Element(x, y);
2393 if (!IS_EXPLOSION_PROOF(element))
2394 RemoveMovingField(x, y);
2400 if (IS_EXPLOSION_PROOF(element))
2403 /* indestructible elements can only explode in center (but not flames) */
2404 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2405 element == EL_FLAMES)
2410 if ((IS_INDESTRUCTIBLE(element) &&
2411 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2412 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2413 element == EL_FLAMES)
2417 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2419 if (IS_ACTIVE_BOMB(element))
2421 /* re-activate things under the bomb like gate or penguin */
2422 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2429 /* save walkable background elements while explosion on same tile */
2431 if (IS_INDESTRUCTIBLE(element))
2432 Back[x][y] = element;
2434 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2435 Back[x][y] = element;
2438 /* ignite explodable elements reached by other explosion */
2439 if (element == EL_EXPLOSION)
2440 element = Store2[x][y];
2443 if (AmoebaNr[x][y] &&
2444 (element == EL_AMOEBA_FULL ||
2445 element == EL_BD_AMOEBA ||
2446 element == EL_AMOEBA_GROWING))
2448 AmoebaCnt[AmoebaNr[x][y]]--;
2449 AmoebaCnt2[AmoebaNr[x][y]]--;
2455 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2457 switch(StorePlayer[ex][ey])
2460 Store[x][y] = EL_EMERALD_RED;
2463 Store[x][y] = EL_EMERALD;
2466 Store[x][y] = EL_EMERALD_PURPLE;
2470 Store[x][y] = EL_EMERALD_YELLOW;
2474 if (game.emulation == EMU_SUPAPLEX)
2475 Store[x][y] = EL_EMPTY;
2477 else if (center_element == EL_MOLE)
2478 Store[x][y] = EL_EMERALD_RED;
2479 else if (center_element == EL_PENGUIN)
2480 Store[x][y] = EL_EMERALD_PURPLE;
2481 else if (center_element == EL_BUG)
2482 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2483 else if (center_element == EL_BD_BUTTERFLY)
2484 Store[x][y] = EL_BD_DIAMOND;
2485 else if (center_element == EL_SP_ELECTRON)
2486 Store[x][y] = EL_SP_INFOTRON;
2487 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2488 Store[x][y] = level.amoeba_content;
2489 else if (center_element == EL_YAMYAM)
2490 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2491 else if (IS_CUSTOM_ELEMENT(center_element) &&
2492 element_info[center_element].content[xx][yy] != EL_EMPTY)
2493 Store[x][y] = element_info[center_element].content[xx][yy];
2494 else if (element == EL_WALL_EMERALD)
2495 Store[x][y] = EL_EMERALD;
2496 else if (element == EL_WALL_DIAMOND)
2497 Store[x][y] = EL_DIAMOND;
2498 else if (element == EL_WALL_BD_DIAMOND)
2499 Store[x][y] = EL_BD_DIAMOND;
2500 else if (element == EL_WALL_EMERALD_YELLOW)
2501 Store[x][y] = EL_EMERALD_YELLOW;
2502 else if (element == EL_WALL_EMERALD_RED)
2503 Store[x][y] = EL_EMERALD_RED;
2504 else if (element == EL_WALL_EMERALD_PURPLE)
2505 Store[x][y] = EL_EMERALD_PURPLE;
2506 else if (element == EL_WALL_PEARL)
2507 Store[x][y] = EL_PEARL;
2508 else if (element == EL_WALL_CRYSTAL)
2509 Store[x][y] = EL_CRYSTAL;
2510 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2511 Store[x][y] = element_info[element].content[1][1];
2513 Store[x][y] = EL_EMPTY;
2515 if (x != ex || y != ey ||
2516 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2517 Store2[x][y] = element;
2520 if (AmoebaNr[x][y] &&
2521 (element == EL_AMOEBA_FULL ||
2522 element == EL_BD_AMOEBA ||
2523 element == EL_AMOEBA_GROWING))
2525 AmoebaCnt[AmoebaNr[x][y]]--;
2526 AmoebaCnt2[AmoebaNr[x][y]]--;
2532 MovDir[x][y] = MovPos[x][y] = 0;
2533 GfxDir[x][y] = MovDir[x][y];
2538 Feld[x][y] = EL_EXPLOSION;
2540 GfxElement[x][y] = center_element;
2542 GfxElement[x][y] = EL_UNDEFINED;
2545 ExplodePhase[x][y] = 1;
2549 if (center_element == EL_YAMYAM)
2550 game.yamyam_content_nr =
2551 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2562 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2566 /* activate this even in non-DEBUG version until cause for crash in
2567 getGraphicAnimationFrame() (see below) is found and eliminated */
2571 if (GfxElement[x][y] == EL_UNDEFINED)
2574 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2575 printf("Explode(): This should never happen!\n");
2578 GfxElement[x][y] = EL_EMPTY;
2582 if (phase == first_phase_after_start)
2584 int element = Store2[x][y];
2586 if (element == EL_BLACK_ORB)
2588 Feld[x][y] = Store2[x][y];
2593 else if (phase == half_phase)
2595 int element = Store2[x][y];
2597 if (IS_PLAYER(x, y))
2598 KillHeroUnlessProtected(x, y);
2599 else if (CAN_EXPLODE_BY_FIRE(element))
2601 Feld[x][y] = Store2[x][y];
2605 else if (element == EL_AMOEBA_TO_DIAMOND)
2606 AmoebeUmwandeln(x, y);
2609 if (phase == last_phase)
2613 element = Feld[x][y] = Store[x][y];
2614 Store[x][y] = Store2[x][y] = 0;
2615 GfxElement[x][y] = EL_UNDEFINED;
2617 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2618 element = Feld[x][y] = Back[x][y];
2621 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2622 GfxDir[x][y] = MV_NO_MOVING;
2623 ChangeDelay[x][y] = 0;
2624 ChangePage[x][y] = -1;
2626 InitField(x, y, FALSE);
2627 if (CAN_MOVE(element))
2629 DrawLevelField(x, y);
2631 TestIfElementTouchesCustomElement(x, y);
2633 if (GFX_CRUMBLED(element))
2634 DrawLevelFieldCrumbledSandNeighbours(x, y);
2636 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2637 StorePlayer[x][y] = 0;
2639 if (ELEM_IS_PLAYER(element))
2640 RelocatePlayer(x, y, element);
2642 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2645 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2647 int stored = Store[x][y];
2648 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2649 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2652 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2655 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2656 element_info[GfxElement[x][y]].token_name,
2661 DrawLevelFieldCrumbledSand(x, y);
2663 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2665 DrawLevelElement(x, y, Back[x][y]);
2666 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2668 else if (IS_WALKABLE_UNDER(Back[x][y]))
2670 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2671 DrawLevelElementThruMask(x, y, Back[x][y]);
2673 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2674 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2678 void DynaExplode(int ex, int ey)
2681 int dynabomb_element = Feld[ex][ey];
2682 int dynabomb_size = 1;
2683 boolean dynabomb_xl = FALSE;
2684 struct PlayerInfo *player;
2685 static int xy[4][2] =
2693 if (IS_ACTIVE_BOMB(dynabomb_element))
2695 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2696 dynabomb_size = player->dynabomb_size;
2697 dynabomb_xl = player->dynabomb_xl;
2698 player->dynabombs_left++;
2701 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2703 for (i = 0; i < 4; i++)
2705 for (j = 1; j <= dynabomb_size; j++)
2707 int x = ex + j * xy[i % 4][0];
2708 int y = ey + j * xy[i % 4][1];
2711 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2714 element = Feld[x][y];
2716 /* do not restart explosions of fields with active bombs */
2717 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2720 Explode(x, y, EX_PHASE_START, EX_BORDER);
2722 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2723 if (element != EL_EMPTY &&
2724 element != EL_SAND &&
2725 element != EL_EXPLOSION &&
2732 void Bang(int x, int y)
2735 int element = MovingOrBlocked2Element(x, y);
2737 int element = Feld[x][y];
2741 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2743 if (IS_PLAYER(x, y))
2746 struct PlayerInfo *player = PLAYERINFO(x, y);
2748 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2749 player->element_nr);
2754 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2756 if (game.emulation == EMU_SUPAPLEX)
2757 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2759 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2764 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2772 case EL_BD_BUTTERFLY:
2775 case EL_DARK_YAMYAM:
2779 RaiseScoreElement(element);
2780 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2782 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2783 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2784 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2785 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2786 case EL_DYNABOMB_INCREASE_NUMBER:
2787 case EL_DYNABOMB_INCREASE_SIZE:
2788 case EL_DYNABOMB_INCREASE_POWER:
2793 case EL_LAMP_ACTIVE:
2794 if (IS_PLAYER(x, y))
2795 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2797 Explode(x, y, EX_PHASE_START, EX_CENTER);
2800 if (CAN_EXPLODE_DYNA(element))
2802 else if (CAN_EXPLODE_1X1(element))
2803 Explode(x, y, EX_PHASE_START, EX_CENTER);
2805 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2809 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2812 void SplashAcid(int x, int y)
2814 int element = Feld[x][y];
2816 if (element != EL_ACID_SPLASH_LEFT &&
2817 element != EL_ACID_SPLASH_RIGHT)
2819 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2821 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2822 (!IN_LEV_FIELD(x-1, y-1) ||
2823 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2824 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2826 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2827 (!IN_LEV_FIELD(x+1, y-1) ||
2828 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2829 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2833 static void InitBeltMovement()
2835 static int belt_base_element[4] =
2837 EL_CONVEYOR_BELT_1_LEFT,
2838 EL_CONVEYOR_BELT_2_LEFT,
2839 EL_CONVEYOR_BELT_3_LEFT,
2840 EL_CONVEYOR_BELT_4_LEFT
2842 static int belt_base_active_element[4] =
2844 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2845 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2846 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2847 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2852 /* set frame order for belt animation graphic according to belt direction */
2853 for (i = 0; i < 4; i++)
2857 for (j = 0; j < 3; j++)
2859 int element = belt_base_active_element[belt_nr] + j;
2860 int graphic = el2img(element);
2862 if (game.belt_dir[i] == MV_LEFT)
2863 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2865 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2869 for (y = 0; y < lev_fieldy; y++)
2871 for (x = 0; x < lev_fieldx; x++)
2873 int element = Feld[x][y];
2875 for (i = 0; i < 4; i++)
2877 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2879 int e_belt_nr = getBeltNrFromBeltElement(element);
2882 if (e_belt_nr == belt_nr)
2884 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2886 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2894 static void ToggleBeltSwitch(int x, int y)
2896 static int belt_base_element[4] =
2898 EL_CONVEYOR_BELT_1_LEFT,
2899 EL_CONVEYOR_BELT_2_LEFT,
2900 EL_CONVEYOR_BELT_3_LEFT,
2901 EL_CONVEYOR_BELT_4_LEFT
2903 static int belt_base_active_element[4] =
2905 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2906 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2907 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2908 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2910 static int belt_base_switch_element[4] =
2912 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2913 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2914 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2915 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2917 static int belt_move_dir[4] =
2925 int element = Feld[x][y];
2926 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2927 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2928 int belt_dir = belt_move_dir[belt_dir_nr];
2931 if (!IS_BELT_SWITCH(element))
2934 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2935 game.belt_dir[belt_nr] = belt_dir;
2937 if (belt_dir_nr == 3)
2940 /* set frame order for belt animation graphic according to belt direction */
2941 for (i = 0; i < 3; i++)
2943 int element = belt_base_active_element[belt_nr] + i;
2944 int graphic = el2img(element);
2946 if (belt_dir == MV_LEFT)
2947 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2949 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2952 for (yy = 0; yy < lev_fieldy; yy++)
2954 for (xx = 0; xx < lev_fieldx; xx++)
2956 int element = Feld[xx][yy];
2958 if (IS_BELT_SWITCH(element))
2960 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2962 if (e_belt_nr == belt_nr)
2964 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2965 DrawLevelField(xx, yy);
2968 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2970 int e_belt_nr = getBeltNrFromBeltElement(element);
2972 if (e_belt_nr == belt_nr)
2974 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2976 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2977 DrawLevelField(xx, yy);
2980 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2982 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2984 if (e_belt_nr == belt_nr)
2986 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2988 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2989 DrawLevelField(xx, yy);
2996 static void ToggleSwitchgateSwitch(int x, int y)
3000 game.switchgate_pos = !game.switchgate_pos;
3002 for (yy = 0; yy < lev_fieldy; yy++)
3004 for (xx = 0; xx < lev_fieldx; xx++)
3006 int element = Feld[xx][yy];
3008 if (element == EL_SWITCHGATE_SWITCH_UP ||
3009 element == EL_SWITCHGATE_SWITCH_DOWN)
3011 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3012 DrawLevelField(xx, yy);
3014 else if (element == EL_SWITCHGATE_OPEN ||
3015 element == EL_SWITCHGATE_OPENING)
3017 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3019 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3021 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3024 else if (element == EL_SWITCHGATE_CLOSED ||
3025 element == EL_SWITCHGATE_CLOSING)
3027 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3029 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3031 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3038 static int getInvisibleActiveFromInvisibleElement(int element)
3040 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3041 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3042 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3046 static int getInvisibleFromInvisibleActiveElement(int element)
3048 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3049 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3050 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3054 static void RedrawAllLightSwitchesAndInvisibleElements()
3058 for (y = 0; y < lev_fieldy; y++)
3060 for (x = 0; x < lev_fieldx; x++)
3062 int element = Feld[x][y];
3064 if (element == EL_LIGHT_SWITCH &&
3065 game.light_time_left > 0)
3067 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3068 DrawLevelField(x, y);
3070 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3071 game.light_time_left == 0)
3073 Feld[x][y] = EL_LIGHT_SWITCH;
3074 DrawLevelField(x, y);
3076 else if (element == EL_INVISIBLE_STEELWALL ||
3077 element == EL_INVISIBLE_WALL ||
3078 element == EL_INVISIBLE_SAND)
3080 if (game.light_time_left > 0)
3081 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3083 DrawLevelField(x, y);
3085 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3086 element == EL_INVISIBLE_WALL_ACTIVE ||
3087 element == EL_INVISIBLE_SAND_ACTIVE)
3089 if (game.light_time_left == 0)
3090 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3092 DrawLevelField(x, y);
3098 static void ToggleLightSwitch(int x, int y)
3100 int element = Feld[x][y];
3102 game.light_time_left =
3103 (element == EL_LIGHT_SWITCH ?
3104 level.time_light * FRAMES_PER_SECOND : 0);
3106 RedrawAllLightSwitchesAndInvisibleElements();
3109 static void ActivateTimegateSwitch(int x, int y)
3113 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3115 for (yy = 0; yy < lev_fieldy; yy++)
3117 for (xx = 0; xx < lev_fieldx; xx++)
3119 int element = Feld[xx][yy];
3121 if (element == EL_TIMEGATE_CLOSED ||
3122 element == EL_TIMEGATE_CLOSING)
3124 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3125 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3129 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3131 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3132 DrawLevelField(xx, yy);
3139 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3142 inline static int getElementMoveStepsize(int x, int y)
3144 int element = Feld[x][y];
3145 int direction = MovDir[x][y];
3146 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3147 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3148 int horiz_move = (dx != 0);
3149 int sign = (horiz_move ? dx : dy);
3150 int step = sign * element_info[element].move_stepsize;
3152 /* special values for move stepsize for spring and things on conveyor belt */
3155 if (CAN_FALL(element) &&
3156 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3157 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3158 else if (element == EL_SPRING)
3159 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3165 void Impact(int x, int y)
3167 boolean lastline = (y == lev_fieldy-1);
3168 boolean object_hit = FALSE;
3169 boolean impact = (lastline || object_hit);
3170 int element = Feld[x][y];
3171 int smashed = EL_UNDEFINED;
3173 if (!lastline) /* check if element below was hit */
3175 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3178 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3179 MovDir[x][y + 1] != MV_DOWN ||
3180 MovPos[x][y + 1] <= TILEY / 2));
3183 object_hit = !IS_FREE(x, y + 1);
3186 /* do not smash moving elements that left the smashed field in time */
3187 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3188 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3192 smashed = MovingOrBlocked2Element(x, y + 1);
3194 impact = (lastline || object_hit);
3197 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3203 /* only reset graphic animation if graphic really changes after impact */
3205 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3207 ResetGfxAnimation(x, y);
3208 DrawLevelField(x, y);
3211 if (impact && CAN_EXPLODE_IMPACT(element))
3216 else if (impact && element == EL_PEARL)
3218 Feld[x][y] = EL_PEARL_BREAKING;
3219 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3222 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3224 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3229 if (impact && element == EL_AMOEBA_DROP)
3231 if (object_hit && IS_PLAYER(x, y + 1))
3232 KillHeroUnlessProtected(x, y + 1);
3233 else if (object_hit && smashed == EL_PENGUIN)
3237 Feld[x][y] = EL_AMOEBA_GROWING;
3238 Store[x][y] = EL_AMOEBA_WET;
3240 ResetRandomAnimationValue(x, y);
3245 if (object_hit) /* check which object was hit */
3247 if (CAN_PASS_MAGIC_WALL(element) &&
3248 (smashed == EL_MAGIC_WALL ||
3249 smashed == EL_BD_MAGIC_WALL))
3252 int activated_magic_wall =
3253 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3254 EL_BD_MAGIC_WALL_ACTIVE);
3256 /* activate magic wall / mill */
3257 for (yy = 0; yy < lev_fieldy; yy++)
3258 for (xx = 0; xx < lev_fieldx; xx++)
3259 if (Feld[xx][yy] == smashed)
3260 Feld[xx][yy] = activated_magic_wall;
3262 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3263 game.magic_wall_active = TRUE;
3265 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3266 SND_MAGIC_WALL_ACTIVATING :
3267 SND_BD_MAGIC_WALL_ACTIVATING));
3270 if (IS_PLAYER(x, y + 1))
3272 if (CAN_SMASH_PLAYER(element))
3274 KillHeroUnlessProtected(x, y + 1);
3278 else if (smashed == EL_PENGUIN)
3280 if (CAN_SMASH_PLAYER(element))
3286 else if (element == EL_BD_DIAMOND)
3288 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3294 else if ((element == EL_SP_INFOTRON ||
3295 element == EL_SP_ZONK) &&
3296 (smashed == EL_SP_SNIKSNAK ||
3297 smashed == EL_SP_ELECTRON ||
3298 smashed == EL_SP_DISK_ORANGE))
3304 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3310 else if (CAN_SMASH_EVERYTHING(element))
3312 if (IS_CLASSIC_ENEMY(smashed) ||
3313 CAN_EXPLODE_SMASHED(smashed))
3318 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3320 if (smashed == EL_LAMP ||
3321 smashed == EL_LAMP_ACTIVE)
3326 else if (smashed == EL_NUT)
3328 Feld[x][y + 1] = EL_NUT_BREAKING;
3329 PlayLevelSound(x, y, SND_NUT_BREAKING);
3330 RaiseScoreElement(EL_NUT);
3333 else if (smashed == EL_PEARL)
3335 Feld[x][y + 1] = EL_PEARL_BREAKING;
3336 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3339 else if (smashed == EL_DIAMOND)
3341 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3342 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3345 else if (IS_BELT_SWITCH(smashed))
3347 ToggleBeltSwitch(x, y + 1);
3349 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3350 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3352 ToggleSwitchgateSwitch(x, y + 1);
3354 else if (smashed == EL_LIGHT_SWITCH ||
3355 smashed == EL_LIGHT_SWITCH_ACTIVE)
3357 ToggleLightSwitch(x, y + 1);
3361 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3363 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3364 CE_OTHER_IS_SWITCHING);
3365 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3371 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3376 /* play sound of magic wall / mill */
3378 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3379 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3381 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3382 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3383 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3384 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3389 /* play sound of object that hits the ground */
3390 if (lastline || object_hit)
3391 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3394 inline static void TurnRoundExt(int x, int y)
3406 { 0, 0 }, { 0, 0 }, { 0, 0 },
3411 int left, right, back;
3415 { MV_DOWN, MV_UP, MV_RIGHT },
3416 { MV_UP, MV_DOWN, MV_LEFT },
3418 { MV_LEFT, MV_RIGHT, MV_DOWN },
3422 { MV_RIGHT, MV_LEFT, MV_UP }
3425 int element = Feld[x][y];
3426 int move_pattern = element_info[element].move_pattern;
3428 int old_move_dir = MovDir[x][y];
3429 int left_dir = turn[old_move_dir].left;
3430 int right_dir = turn[old_move_dir].right;
3431 int back_dir = turn[old_move_dir].back;
3433 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3434 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3435 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3436 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3438 int left_x = x + left_dx, left_y = y + left_dy;
3439 int right_x = x + right_dx, right_y = y + right_dy;
3440 int move_x = x + move_dx, move_y = y + move_dy;
3444 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3446 TestIfBadThingTouchesOtherBadThing(x, y);
3448 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3449 MovDir[x][y] = right_dir;
3450 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3451 MovDir[x][y] = left_dir;
3453 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3455 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3458 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3459 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3461 TestIfBadThingTouchesOtherBadThing(x, y);
3463 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3464 MovDir[x][y] = left_dir;
3465 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3466 MovDir[x][y] = right_dir;
3468 if ((element == EL_SPACESHIP ||
3469 element == EL_SP_SNIKSNAK ||
3470 element == EL_SP_ELECTRON)
3471 && MovDir[x][y] != old_move_dir)
3473 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3476 else if (element == EL_YAMYAM)
3478 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3479 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3481 if (can_turn_left && can_turn_right)
3482 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3483 else if (can_turn_left)
3484 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3485 else if (can_turn_right)
3486 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3488 MovDir[x][y] = back_dir;
3490 MovDelay[x][y] = 16 + 16 * RND(3);
3492 else if (element == EL_DARK_YAMYAM)
3494 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3495 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3497 if (can_turn_left && can_turn_right)
3498 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3499 else if (can_turn_left)
3500 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3501 else if (can_turn_right)
3502 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3504 MovDir[x][y] = back_dir;
3506 MovDelay[x][y] = 16 + 16 * RND(3);
3508 else if (element == EL_PACMAN)
3510 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3511 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3513 if (can_turn_left && can_turn_right)
3514 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3515 else if (can_turn_left)
3516 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3517 else if (can_turn_right)
3518 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3520 MovDir[x][y] = back_dir;
3522 MovDelay[x][y] = 6 + RND(40);
3524 else if (element == EL_PIG)
3526 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3527 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3528 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3529 boolean should_turn_left, should_turn_right, should_move_on;
3531 int rnd = RND(rnd_value);
3533 should_turn_left = (can_turn_left &&
3535 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3536 y + back_dy + left_dy)));
3537 should_turn_right = (can_turn_right &&
3539 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3540 y + back_dy + right_dy)));
3541 should_move_on = (can_move_on &&
3544 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3545 y + move_dy + left_dy) ||
3546 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3547 y + move_dy + right_dy)));
3549 if (should_turn_left || should_turn_right || should_move_on)
3551 if (should_turn_left && should_turn_right && should_move_on)
3552 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3553 rnd < 2 * rnd_value / 3 ? right_dir :
3555 else if (should_turn_left && should_turn_right)
3556 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3557 else if (should_turn_left && should_move_on)
3558 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3559 else if (should_turn_right && should_move_on)
3560 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3561 else if (should_turn_left)
3562 MovDir[x][y] = left_dir;
3563 else if (should_turn_right)
3564 MovDir[x][y] = right_dir;
3565 else if (should_move_on)
3566 MovDir[x][y] = old_move_dir;
3568 else if (can_move_on && rnd > rnd_value / 8)
3569 MovDir[x][y] = old_move_dir;
3570 else if (can_turn_left && can_turn_right)
3571 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3572 else if (can_turn_left && rnd > rnd_value / 8)
3573 MovDir[x][y] = left_dir;
3574 else if (can_turn_right && rnd > rnd_value/8)
3575 MovDir[x][y] = right_dir;
3577 MovDir[x][y] = back_dir;
3579 xx = x + move_xy[MovDir[x][y]].x;
3580 yy = y + move_xy[MovDir[x][y]].y;
3582 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3583 MovDir[x][y] = old_move_dir;
3587 else if (element == EL_DRAGON)
3589 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3590 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3591 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3593 int rnd = RND(rnd_value);
3596 if (FrameCounter < 1 && x == 0 && y == 29)
3597 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3600 if (can_move_on && rnd > rnd_value / 8)
3601 MovDir[x][y] = old_move_dir;
3602 else if (can_turn_left && can_turn_right)
3603 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3604 else if (can_turn_left && rnd > rnd_value / 8)
3605 MovDir[x][y] = left_dir;
3606 else if (can_turn_right && rnd > rnd_value / 8)
3607 MovDir[x][y] = right_dir;
3609 MovDir[x][y] = back_dir;
3611 xx = x + move_xy[MovDir[x][y]].x;
3612 yy = y + move_xy[MovDir[x][y]].y;
3615 if (FrameCounter < 1 && x == 0 && y == 29)
3616 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3617 xx, yy, Feld[xx][yy],
3622 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3623 MovDir[x][y] = old_move_dir;
3625 if (!IS_FREE(xx, yy))
3626 MovDir[x][y] = old_move_dir;
3630 if (FrameCounter < 1 && x == 0 && y == 29)
3631 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3636 else if (element == EL_MOLE)
3638 boolean can_move_on =
3639 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3640 IS_AMOEBOID(Feld[move_x][move_y]) ||
3641 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3644 boolean can_turn_left =
3645 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3646 IS_AMOEBOID(Feld[left_x][left_y])));
3648 boolean can_turn_right =
3649 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3650 IS_AMOEBOID(Feld[right_x][right_y])));
3652 if (can_turn_left && can_turn_right)
3653 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3654 else if (can_turn_left)
3655 MovDir[x][y] = left_dir;
3657 MovDir[x][y] = right_dir;
3660 if (MovDir[x][y] != old_move_dir)
3663 else if (element == EL_BALLOON)
3665 MovDir[x][y] = game.balloon_dir;
3668 else if (element == EL_SPRING)
3670 if (MovDir[x][y] & MV_HORIZONTAL &&
3671 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3672 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3673 MovDir[x][y] = MV_NO_MOVING;
3677 else if (element == EL_ROBOT ||
3678 element == EL_SATELLITE ||
3679 element == EL_PENGUIN)
3681 int attr_x = -1, attr_y = -1;
3692 for (i = 0; i < MAX_PLAYERS; i++)
3694 struct PlayerInfo *player = &stored_player[i];
3695 int jx = player->jx, jy = player->jy;
3697 if (!player->active)
3701 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3709 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3715 if (element == EL_PENGUIN)
3718 static int xy[4][2] =
3726 for (i = 0; i < 4; i++)
3728 int ex = x + xy[i % 4][0];
3729 int ey = y + xy[i % 4][1];
3731 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3740 MovDir[x][y] = MV_NO_MOVING;
3742 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3743 else if (attr_x > x)
3744 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3746 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3747 else if (attr_y > y)
3748 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3750 if (element == EL_ROBOT)
3754 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3755 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3756 Moving2Blocked(x, y, &newx, &newy);
3758 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3759 MovDelay[x][y] = 8 + 8 * !RND(3);
3761 MovDelay[x][y] = 16;
3763 else if (element == EL_PENGUIN)
3769 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3771 boolean first_horiz = RND(2);
3772 int new_move_dir = MovDir[x][y];
3775 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3776 Moving2Blocked(x, y, &newx, &newy);
3778 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3782 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3783 Moving2Blocked(x, y, &newx, &newy);
3785 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3788 MovDir[x][y] = old_move_dir;
3792 else /* (element == EL_SATELLITE) */
3798 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3800 boolean first_horiz = RND(2);
3801 int new_move_dir = MovDir[x][y];
3804 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3805 Moving2Blocked(x, y, &newx, &newy);
3807 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3811 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3812 Moving2Blocked(x, y, &newx, &newy);
3814 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3817 MovDir[x][y] = old_move_dir;
3822 else if (move_pattern == MV_TURNING_LEFT ||
3823 move_pattern == MV_TURNING_RIGHT ||
3824 move_pattern == MV_TURNING_LEFT_RIGHT ||
3825 move_pattern == MV_TURNING_RIGHT_LEFT ||
3826 move_pattern == MV_TURNING_RANDOM ||
3827 move_pattern == MV_ALL_DIRECTIONS)
3829 boolean can_turn_left =
3830 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3831 boolean can_turn_right =
3832 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3834 if (move_pattern == MV_TURNING_LEFT)
3835 MovDir[x][y] = left_dir;
3836 else if (move_pattern == MV_TURNING_RIGHT)
3837 MovDir[x][y] = right_dir;
3838 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3839 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3840 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3841 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3842 else if (move_pattern == MV_TURNING_RANDOM)
3843 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3844 can_turn_right && !can_turn_left ? right_dir :
3845 RND(2) ? left_dir : right_dir);
3846 else if (can_turn_left && can_turn_right)
3847 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3848 else if (can_turn_left)
3849 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3850 else if (can_turn_right)
3851 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3853 MovDir[x][y] = back_dir;
3855 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3857 else if (move_pattern == MV_HORIZONTAL ||
3858 move_pattern == MV_VERTICAL)
3860 if (move_pattern & old_move_dir)
3861 MovDir[x][y] = back_dir;
3862 else if (move_pattern == MV_HORIZONTAL)
3863 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3864 else if (move_pattern == MV_VERTICAL)
3865 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3867 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3869 else if (move_pattern & MV_ANY_DIRECTION)
3871 MovDir[x][y] = move_pattern;
3872 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3874 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3876 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3877 MovDir[x][y] = left_dir;
3878 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3879 MovDir[x][y] = right_dir;
3881 if (MovDir[x][y] != old_move_dir)
3882 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3884 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3886 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3887 MovDir[x][y] = right_dir;
3888 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3889 MovDir[x][y] = left_dir;
3891 if (MovDir[x][y] != old_move_dir)
3892 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3894 else if (move_pattern == MV_TOWARDS_PLAYER ||
3895 move_pattern == MV_AWAY_FROM_PLAYER)
3897 int attr_x = -1, attr_y = -1;
3899 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3910 for (i = 0; i < MAX_PLAYERS; i++)
3912 struct PlayerInfo *player = &stored_player[i];
3913 int jx = player->jx, jy = player->jy;
3915 if (!player->active)
3919 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3927 MovDir[x][y] = MV_NO_MOVING;
3929 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3930 else if (attr_x > x)
3931 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3933 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3934 else if (attr_y > y)
3935 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3937 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3939 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3941 boolean first_horiz = RND(2);
3942 int new_move_dir = MovDir[x][y];
3945 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3946 Moving2Blocked(x, y, &newx, &newy);
3948 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3952 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3953 Moving2Blocked(x, y, &newx, &newy);
3955 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3958 MovDir[x][y] = old_move_dir;
3961 else if (move_pattern == MV_WHEN_PUSHED ||
3962 move_pattern == MV_WHEN_DROPPED)
3964 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3965 MovDir[x][y] = MV_NO_MOVING;
3969 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
3971 static int test_xy[7][2] =
3981 static int test_dir[7] =
3991 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3992 int move_preference = -1000000; /* start with very low preference */
3993 int new_move_dir = MV_NO_MOVING;
3994 int start_test = RND(4);
3997 for (i = 0; i < 4; i++)
3999 int move_dir = test_dir[start_test + i];
4000 int move_dir_preference;
4002 xx = x + test_xy[start_test + i][0];
4003 yy = y + test_xy[start_test + i][1];
4005 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4006 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4008 new_move_dir = move_dir;
4013 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4016 move_dir_preference = -1 * RunnerVisit[xx][yy];
4017 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4018 move_dir_preference = PlayerVisit[xx][yy];
4020 if (move_dir_preference > move_preference)
4022 /* prefer field that has not been visited for the longest time */
4023 move_preference = move_dir_preference;
4024 new_move_dir = move_dir;
4026 else if (move_dir_preference == move_preference &&
4027 move_dir == old_move_dir)
4029 /* prefer last direction when all directions are preferred equally */
4030 move_preference = move_dir_preference;
4031 new_move_dir = move_dir;
4035 MovDir[x][y] = new_move_dir;
4036 if (old_move_dir != new_move_dir)
4041 static void TurnRound(int x, int y)
4043 int direction = MovDir[x][y];
4046 GfxDir[x][y] = MovDir[x][y];
4052 GfxDir[x][y] = MovDir[x][y];
4055 if (direction != MovDir[x][y])
4060 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4063 GfxAction[x][y] = ACTION_WAITING;
4067 static boolean JustBeingPushed(int x, int y)
4071 for (i = 0; i < MAX_PLAYERS; i++)
4073 struct PlayerInfo *player = &stored_player[i];
4075 if (player->active && player->is_pushing && player->MovPos)
4077 int next_jx = player->jx + (player->jx - player->last_jx);
4078 int next_jy = player->jy + (player->jy - player->last_jy);
4080 if (x == next_jx && y == next_jy)
4088 void StartMoving(int x, int y)
4090 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4091 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4092 int element = Feld[x][y];
4098 if (MovDelay[x][y] == 0)
4099 GfxAction[x][y] = ACTION_DEFAULT;
4101 /* !!! this should be handled more generic (not only for mole) !!! */
4102 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4103 GfxAction[x][y] = ACTION_DEFAULT;
4106 if (CAN_FALL(element) && y < lev_fieldy - 1)
4108 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4109 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4110 if (JustBeingPushed(x, y))
4113 if (element == EL_QUICKSAND_FULL)
4115 if (IS_FREE(x, y + 1))
4117 InitMovingField(x, y, MV_DOWN);
4118 started_moving = TRUE;
4120 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4121 Store[x][y] = EL_ROCK;
4123 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4125 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4128 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4130 if (!MovDelay[x][y])
4131 MovDelay[x][y] = TILEY + 1;
4140 Feld[x][y] = EL_QUICKSAND_EMPTY;
4141 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4142 Store[x][y + 1] = Store[x][y];
4145 PlayLevelSoundAction(x, y, ACTION_FILLING);
4147 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4151 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4152 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4154 InitMovingField(x, y, MV_DOWN);
4155 started_moving = TRUE;
4157 Feld[x][y] = EL_QUICKSAND_FILLING;
4158 Store[x][y] = element;
4160 PlayLevelSoundAction(x, y, ACTION_FILLING);
4162 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4165 else if (element == EL_MAGIC_WALL_FULL)
4167 if (IS_FREE(x, y + 1))
4169 InitMovingField(x, y, MV_DOWN);
4170 started_moving = TRUE;
4172 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4173 Store[x][y] = EL_CHANGED(Store[x][y]);
4175 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4177 if (!MovDelay[x][y])
4178 MovDelay[x][y] = TILEY/4 + 1;
4187 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4188 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4189 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4193 else if (element == EL_BD_MAGIC_WALL_FULL)
4195 if (IS_FREE(x, y + 1))
4197 InitMovingField(x, y, MV_DOWN);
4198 started_moving = TRUE;
4200 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4201 Store[x][y] = EL_CHANGED2(Store[x][y]);
4203 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4205 if (!MovDelay[x][y])
4206 MovDelay[x][y] = TILEY/4 + 1;
4215 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4216 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4217 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4221 else if (CAN_PASS_MAGIC_WALL(element) &&
4222 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4223 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4225 InitMovingField(x, y, MV_DOWN);
4226 started_moving = TRUE;
4229 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4230 EL_BD_MAGIC_WALL_FILLING);
4231 Store[x][y] = element;
4234 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4236 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4241 InitMovingField(x, y, MV_DOWN);
4242 started_moving = TRUE;
4244 Store[x][y] = EL_ACID;
4246 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4247 GfxAction[x][y + 1] = ACTION_ACTIVE;
4251 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4252 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4253 (Feld[x][y + 1] == EL_BLOCKED)) ||
4254 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4255 CAN_SMASH(element) && WasJustFalling[x][y] &&
4256 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4260 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4261 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4262 WasJustMoving[x][y] && !Pushed[x][y + 1])
4264 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4265 WasJustMoving[x][y])
4270 /* this is needed for a special case not covered by calling "Impact()"
4271 from "ContinueMoving()": if an element moves to a tile directly below
4272 another element which was just falling on that tile (which was empty
4273 in the previous frame), the falling element above would just stop
4274 instead of smashing the element below (in previous version, the above
4275 element was just checked for "moving" instead of "falling", resulting
4276 in incorrect smashes caused by horizontal movement of the above
4277 element; also, the case of the player being the element to smash was
4278 simply not covered here... :-/ ) */
4281 WasJustMoving[x][y] = 0;
4282 WasJustFalling[x][y] = 0;
4287 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4289 if (MovDir[x][y] == MV_NO_MOVING)
4291 InitMovingField(x, y, MV_DOWN);
4292 started_moving = TRUE;
4295 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4297 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4298 MovDir[x][y] = MV_DOWN;
4300 InitMovingField(x, y, MV_DOWN);
4301 started_moving = TRUE;
4303 else if (element == EL_AMOEBA_DROP)
4305 Feld[x][y] = EL_AMOEBA_GROWING;
4306 Store[x][y] = EL_AMOEBA_WET;
4308 /* Store[x][y + 1] must be zero, because:
4309 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4312 #if OLD_GAME_BEHAVIOUR
4313 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4315 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4316 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4317 element != EL_DX_SUPABOMB)
4320 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4321 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4322 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4323 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4326 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4327 (IS_FREE(x - 1, y + 1) ||
4328 Feld[x - 1][y + 1] == EL_ACID));
4329 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4330 (IS_FREE(x + 1, y + 1) ||
4331 Feld[x + 1][y + 1] == EL_ACID));
4332 boolean can_fall_any = (can_fall_left || can_fall_right);
4333 boolean can_fall_both = (can_fall_left && can_fall_right);
4335 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4337 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4339 if (slippery_type == SLIPPERY_ONLY_LEFT)
4340 can_fall_right = FALSE;
4341 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4342 can_fall_left = FALSE;
4343 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4344 can_fall_right = FALSE;
4345 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4346 can_fall_left = FALSE;
4348 can_fall_any = (can_fall_left || can_fall_right);
4349 can_fall_both = (can_fall_left && can_fall_right);
4354 if (can_fall_both &&
4355 (game.emulation != EMU_BOULDERDASH &&
4356 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4357 can_fall_left = !(can_fall_right = RND(2));
4359 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4360 started_moving = TRUE;
4363 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4365 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4366 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4367 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4368 int belt_dir = game.belt_dir[belt_nr];
4370 if ((belt_dir == MV_LEFT && left_is_free) ||
4371 (belt_dir == MV_RIGHT && right_is_free))
4373 InitMovingField(x, y, belt_dir);
4374 started_moving = TRUE;
4376 GfxAction[x][y] = ACTION_DEFAULT;
4381 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4382 if (CAN_MOVE(element) && !started_moving)
4384 int move_pattern = element_info[element].move_pattern;
4387 Moving2Blocked(x, y, &newx, &newy);
4390 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4393 if ((element == EL_SATELLITE ||
4394 element == EL_BALLOON ||
4395 element == EL_SPRING)
4396 && JustBeingPushed(x, y))
4401 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4402 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4403 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4406 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4407 element, element_info[element].token_name,
4408 WasJustMoving[x][y],
4409 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4410 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4411 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4412 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4416 WasJustMoving[x][y] = 0;
4419 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4422 if (Feld[x][y] != element) /* element has changed */
4424 element = Feld[x][y];
4425 move_pattern = element_info[element].move_pattern;
4427 if (!CAN_MOVE(element))
4431 if (Feld[x][y] != element) /* element has changed */
4439 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4440 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4442 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4444 Moving2Blocked(x, y, &newx, &newy);
4445 if (Feld[newx][newy] == EL_BLOCKED)
4446 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4452 if (FrameCounter < 1 && x == 0 && y == 29)
4453 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4456 if (!MovDelay[x][y]) /* start new movement phase */
4458 /* all objects that can change their move direction after each step
4459 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4461 if (element != EL_YAMYAM &&
4462 element != EL_DARK_YAMYAM &&
4463 element != EL_PACMAN &&
4464 !(move_pattern & MV_ANY_DIRECTION) &&
4465 move_pattern != MV_TURNING_LEFT &&
4466 move_pattern != MV_TURNING_RIGHT &&
4467 move_pattern != MV_TURNING_LEFT_RIGHT &&
4468 move_pattern != MV_TURNING_RIGHT_LEFT &&
4469 move_pattern != MV_TURNING_RANDOM)
4474 if (FrameCounter < 1 && x == 0 && y == 29)
4475 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4478 if (MovDelay[x][y] && (element == EL_BUG ||
4479 element == EL_SPACESHIP ||
4480 element == EL_SP_SNIKSNAK ||
4481 element == EL_SP_ELECTRON ||
4482 element == EL_MOLE))
4483 DrawLevelField(x, y);
4487 if (MovDelay[x][y]) /* wait some time before next movement */
4492 if (element == EL_YAMYAM)
4495 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4496 DrawLevelElementAnimation(x, y, element);
4500 if (MovDelay[x][y]) /* element still has to wait some time */
4503 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4504 ResetGfxAnimation(x, y);
4508 if (GfxAction[x][y] != ACTION_WAITING)
4509 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4511 GfxAction[x][y] = ACTION_WAITING;
4515 if (element == EL_ROBOT ||
4517 element == EL_PACMAN ||
4519 element == EL_YAMYAM ||
4520 element == EL_DARK_YAMYAM)
4523 DrawLevelElementAnimation(x, y, element);
4525 DrawLevelElementAnimationIfNeeded(x, y, element);
4527 PlayLevelSoundAction(x, y, ACTION_WAITING);
4529 else if (element == EL_SP_ELECTRON)
4530 DrawLevelElementAnimationIfNeeded(x, y, element);
4531 else if (element == EL_DRAGON)
4534 int dir = MovDir[x][y];
4535 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4536 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4537 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4538 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4539 dir == MV_UP ? IMG_FLAMES_1_UP :
4540 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4541 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4544 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4547 GfxAction[x][y] = ACTION_ATTACKING;
4549 if (IS_PLAYER(x, y))
4550 DrawPlayerField(x, y);
4552 DrawLevelField(x, y);
4554 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4556 for (i = 1; i <= 3; i++)
4558 int xx = x + i * dx;
4559 int yy = y + i * dy;
4560 int sx = SCREENX(xx);
4561 int sy = SCREENY(yy);
4562 int flame_graphic = graphic + (i - 1);
4564 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4569 int flamed = MovingOrBlocked2Element(xx, yy);
4571 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4574 RemoveMovingField(xx, yy);
4576 Feld[xx][yy] = EL_FLAMES;
4577 if (IN_SCR_FIELD(sx, sy))
4579 DrawLevelFieldCrumbledSand(xx, yy);
4580 DrawGraphic(sx, sy, flame_graphic, frame);
4585 if (Feld[xx][yy] == EL_FLAMES)
4586 Feld[xx][yy] = EL_EMPTY;
4587 DrawLevelField(xx, yy);
4592 if (MovDelay[x][y]) /* element still has to wait some time */
4594 PlayLevelSoundAction(x, y, ACTION_WAITING);
4600 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4601 for all other elements GfxAction will be set by InitMovingField() */
4602 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4603 GfxAction[x][y] = ACTION_MOVING;
4607 /* now make next step */
4609 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4611 if (DONT_COLLIDE_WITH(element) &&
4612 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4613 !PLAYER_PROTECTED(newx, newy))
4616 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4620 /* player killed by element which is deadly when colliding with */
4622 KillHero(PLAYERINFO(newx, newy));
4627 else if ((element == EL_PENGUIN ||
4628 element == EL_ROBOT ||
4629 element == EL_SATELLITE ||
4630 element == EL_BALLOON ||
4631 IS_CUSTOM_ELEMENT(element)) &&
4632 IN_LEV_FIELD(newx, newy) &&
4633 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4636 Store[x][y] = EL_ACID;
4638 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4640 if (Feld[newx][newy] == EL_EXIT_OPEN)
4644 DrawLevelField(x, y);
4646 Feld[x][y] = EL_EMPTY;
4647 DrawLevelField(x, y);
4650 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4651 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4652 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4654 local_player->friends_still_needed--;
4655 if (!local_player->friends_still_needed &&
4656 !local_player->GameOver && AllPlayersGone)
4657 local_player->LevelSolved = local_player->GameOver = TRUE;
4661 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4663 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4664 DrawLevelField(newx, newy);
4666 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4668 else if (!IS_FREE(newx, newy))
4670 GfxAction[x][y] = ACTION_WAITING;
4672 if (IS_PLAYER(x, y))
4673 DrawPlayerField(x, y);
4675 DrawLevelField(x, y);
4680 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4682 if (IS_FOOD_PIG(Feld[newx][newy]))
4684 if (IS_MOVING(newx, newy))
4685 RemoveMovingField(newx, newy);
4688 Feld[newx][newy] = EL_EMPTY;
4689 DrawLevelField(newx, newy);
4692 PlayLevelSound(x, y, SND_PIG_DIGGING);
4694 else if (!IS_FREE(newx, newy))
4696 if (IS_PLAYER(x, y))
4697 DrawPlayerField(x, y);
4699 DrawLevelField(x, y);
4708 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4711 else if (IS_CUSTOM_ELEMENT(element) &&
4712 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4716 !IS_FREE(newx, newy)
4722 printf("::: '%s' digs '%s' [%d]\n",
4723 element_info[element].token_name,
4724 element_info[Feld[newx][newy]].token_name,
4725 StorePlayer[newx][newy]);
4728 if (!IS_FREE(newx, newy))
4730 int new_element = Feld[newx][newy];
4733 /* no element can dig solid indestructible elements */
4734 if (IS_INDESTRUCTIBLE(new_element) &&
4735 !IS_DIGGABLE(new_element) &&
4736 !IS_COLLECTIBLE(new_element))
4739 if (AmoebaNr[newx][newy] &&
4740 (new_element == EL_AMOEBA_FULL ||
4741 new_element == EL_BD_AMOEBA ||
4742 new_element == EL_AMOEBA_GROWING))
4744 AmoebaCnt[AmoebaNr[newx][newy]]--;
4745 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4748 if (IS_MOVING(newx, newy))
4749 RemoveMovingField(newx, newy);
4752 RemoveField(newx, newy);
4753 DrawLevelField(newx, newy);
4756 sound = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4757 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4760 PlayLevelSoundAction(x, y, sound);
4763 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4765 RunnerVisit[x][y] = FrameCounter;
4766 PlayerVisit[x][y] /= 8; /* expire player visit path */
4772 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4774 if (!IS_FREE(newx, newy))
4776 if (IS_PLAYER(x, y))
4777 DrawPlayerField(x, y);
4779 DrawLevelField(x, y);
4785 boolean wanna_flame = !RND(10);
4786 int dx = newx - x, dy = newy - y;
4787 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4788 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4789 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4790 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4791 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4792 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4795 IS_CLASSIC_ENEMY(element1) ||
4796 IS_CLASSIC_ENEMY(element2)) &&
4797 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4798 element1 != EL_FLAMES && element2 != EL_FLAMES)
4801 ResetGfxAnimation(x, y);
4802 GfxAction[x][y] = ACTION_ATTACKING;
4805 if (IS_PLAYER(x, y))
4806 DrawPlayerField(x, y);
4808 DrawLevelField(x, y);
4810 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4812 MovDelay[x][y] = 50;
4814 Feld[newx][newy] = EL_FLAMES;
4815 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4816 Feld[newx1][newy1] = EL_FLAMES;
4817 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4818 Feld[newx2][newy2] = EL_FLAMES;
4824 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4825 Feld[newx][newy] == EL_DIAMOND)
4827 if (IS_MOVING(newx, newy))
4828 RemoveMovingField(newx, newy);
4831 Feld[newx][newy] = EL_EMPTY;
4832 DrawLevelField(newx, newy);
4835 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4837 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4838 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4840 if (AmoebaNr[newx][newy])
4842 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4843 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4844 Feld[newx][newy] == EL_BD_AMOEBA)
4845 AmoebaCnt[AmoebaNr[newx][newy]]--;
4848 if (IS_MOVING(newx, newy))
4849 RemoveMovingField(newx, newy);
4852 Feld[newx][newy] = EL_EMPTY;
4853 DrawLevelField(newx, newy);
4856 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4858 else if ((element == EL_PACMAN || element == EL_MOLE)
4859 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4861 if (AmoebaNr[newx][newy])
4863 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4864 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4865 Feld[newx][newy] == EL_BD_AMOEBA)
4866 AmoebaCnt[AmoebaNr[newx][newy]]--;
4869 if (element == EL_MOLE)
4871 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4872 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4874 ResetGfxAnimation(x, y);
4875 GfxAction[x][y] = ACTION_DIGGING;
4876 DrawLevelField(x, y);
4878 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4880 return; /* wait for shrinking amoeba */
4882 else /* element == EL_PACMAN */
4884 Feld[newx][newy] = EL_EMPTY;
4885 DrawLevelField(newx, newy);
4886 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4889 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4890 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4891 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4893 /* wait for shrinking amoeba to completely disappear */
4896 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4898 /* object was running against a wall */
4903 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4904 DrawLevelElementAnimation(x, y, element);
4906 if (element == EL_BUG ||
4907 element == EL_SPACESHIP ||
4908 element == EL_SP_SNIKSNAK)
4909 DrawLevelField(x, y);
4910 else if (element == EL_MOLE)
4911 DrawLevelField(x, y);
4912 else if (element == EL_BD_BUTTERFLY ||
4913 element == EL_BD_FIREFLY)
4914 DrawLevelElementAnimationIfNeeded(x, y, element);
4915 else if (element == EL_SATELLITE)
4916 DrawLevelElementAnimationIfNeeded(x, y, element);
4917 else if (element == EL_SP_ELECTRON)
4918 DrawLevelElementAnimationIfNeeded(x, y, element);
4921 if (DONT_TOUCH(element))
4922 TestIfBadThingTouchesHero(x, y);
4925 PlayLevelSoundAction(x, y, ACTION_WAITING);
4931 InitMovingField(x, y, MovDir[x][y]);
4933 PlayLevelSoundAction(x, y, ACTION_MOVING);
4937 ContinueMoving(x, y);
4940 void ContinueMoving(int x, int y)
4942 int element = Feld[x][y];
4943 int direction = MovDir[x][y];
4944 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4945 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4946 int newx = x + dx, newy = y + dy;
4948 int nextx = newx + dx, nexty = newy + dy;
4950 boolean pushed = Pushed[x][y];
4952 MovPos[x][y] += getElementMoveStepsize(x, y);
4954 if (pushed) /* special case: moving object pushed by player */
4955 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4957 if (ABS(MovPos[x][y]) < TILEX)
4959 DrawLevelField(x, y);
4961 return; /* element is still moving */
4964 /* element reached destination field */
4966 Feld[x][y] = EL_EMPTY;
4967 Feld[newx][newy] = element;
4968 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4970 if (element == EL_MOLE)
4972 Feld[x][y] = EL_SAND;
4974 DrawLevelFieldCrumbledSandNeighbours(x, y);
4976 else if (element == EL_QUICKSAND_FILLING)
4978 element = Feld[newx][newy] = get_next_element(element);
4979 Store[newx][newy] = Store[x][y];
4981 else if (element == EL_QUICKSAND_EMPTYING)
4983 Feld[x][y] = get_next_element(element);
4984 element = Feld[newx][newy] = Store[x][y];
4986 else if (element == EL_MAGIC_WALL_FILLING)
4988 element = Feld[newx][newy] = get_next_element(element);
4989 if (!game.magic_wall_active)
4990 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4991 Store[newx][newy] = Store[x][y];
4993 else if (element == EL_MAGIC_WALL_EMPTYING)
4995 Feld[x][y] = get_next_element(element);
4996 if (!game.magic_wall_active)
4997 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4998 element = Feld[newx][newy] = Store[x][y];
5000 else if (element == EL_BD_MAGIC_WALL_FILLING)
5002 element = Feld[newx][newy] = get_next_element(element);
5003 if (!game.magic_wall_active)
5004 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5005 Store[newx][newy] = Store[x][y];
5007 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5009 Feld[x][y] = get_next_element(element);
5010 if (!game.magic_wall_active)
5011 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5012 element = Feld[newx][newy] = Store[x][y];
5014 else if (element == EL_AMOEBA_DROPPING)
5016 Feld[x][y] = get_next_element(element);
5017 element = Feld[newx][newy] = Store[x][y];
5019 else if (element == EL_SOKOBAN_OBJECT)
5022 Feld[x][y] = Back[x][y];
5024 if (Back[newx][newy])
5025 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5027 Back[x][y] = Back[newx][newy] = 0;
5029 else if (Store[x][y] == EL_ACID)
5031 element = Feld[newx][newy] = EL_ACID;
5035 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5036 MovDelay[newx][newy] = 0;
5038 /* copy element change control values to new field */
5039 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5040 ChangePage[newx][newy] = ChangePage[x][y];
5041 Changed[newx][newy] = Changed[x][y];
5042 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5044 ChangeDelay[x][y] = 0;
5045 ChangePage[x][y] = -1;
5046 Changed[x][y] = CE_BITMASK_DEFAULT;
5047 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5049 /* copy animation control values to new field */
5050 GfxFrame[newx][newy] = GfxFrame[x][y];
5051 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5052 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5053 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5055 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5057 ResetGfxAnimation(x, y); /* reset animation values for old field */
5060 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y))
5062 int new_element = element_info[element].move_leave_element;
5064 Feld[x][y] = new_element;
5066 if (new_element != EL_EMPTY)
5068 InitField(x, y, FALSE);
5070 TestIfElementTouchesCustomElement(x, y);
5072 if (GFX_CRUMBLED(new_element))
5073 DrawLevelFieldCrumbledSandNeighbours(x, y);
5079 /* 2.1.1 (does not work correctly for spring) */
5080 if (!CAN_MOVE(element))
5081 MovDir[newx][newy] = 0;
5085 /* (does not work for falling objects that slide horizontally) */
5086 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5087 MovDir[newx][newy] = 0;
5090 if (!CAN_MOVE(element) ||
5091 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5092 MovDir[newx][newy] = 0;
5095 if (!CAN_MOVE(element) ||
5096 (CAN_FALL(element) && direction == MV_DOWN))
5097 GfxDir[x][y] = MovDir[newx][newy] = 0;
5102 DrawLevelField(x, y);
5103 DrawLevelField(newx, newy);
5105 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5107 /* prevent pushed element from moving on in pushed direction */
5108 if (pushed && CAN_MOVE(element) &&
5109 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5110 !(element_info[element].move_pattern & direction))
5111 TurnRound(newx, newy);
5113 if (!pushed) /* special case: moving object pushed by player */
5115 WasJustMoving[newx][newy] = 3;
5117 if (CAN_FALL(element) && direction == MV_DOWN)
5118 WasJustFalling[newx][newy] = 3;
5121 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5123 TestIfBadThingTouchesHero(newx, newy);
5124 TestIfBadThingTouchesFriend(newx, newy);
5126 if (!IS_CUSTOM_ELEMENT(element))
5127 TestIfBadThingTouchesOtherBadThing(newx, newy);
5129 else if (element == EL_PENGUIN)
5130 TestIfFriendTouchesBadThing(newx, newy);
5132 if (CAN_FALL(element) && direction == MV_DOWN &&
5133 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5137 TestIfElementTouchesCustomElement(x, y); /* for empty space */
5141 if (ChangePage[newx][newy] != -1) /* delayed change */
5142 ChangeElement(newx, newy, ChangePage[newx][newy]);
5147 TestIfElementHitsCustomElement(newx, newy, direction);
5151 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5153 int hitting_element = Feld[newx][newy];
5155 /* !!! fix side (direction) orientation here and elsewhere !!! */
5156 CheckElementSideChange(newx, newy, hitting_element,
5157 direction, CE_HITTING_SOMETHING, -1);
5160 if (IN_LEV_FIELD(nextx, nexty))
5162 static int opposite_directions[] =
5169 int move_dir_bit = MV_DIR_BIT(direction);
5170 int opposite_direction = opposite_directions[move_dir_bit];
5171 int hitting_side = direction;
5172 int touched_side = opposite_direction;
5173 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5174 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5175 MovDir[nextx][nexty] != direction ||
5176 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5182 CheckElementSideChange(nextx, nexty, touched_element,
5183 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5185 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5186 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5188 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5190 struct ElementChangeInfo *change =
5191 &element_info[hitting_element].change_page[i];
5193 if (change->can_change &&
5194 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5195 change->sides & touched_side &&
5196 change->trigger_element == touched_element)
5198 CheckElementSideChange(newx, newy, hitting_element,
5199 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5205 if (IS_CUSTOM_ELEMENT(touched_element) &&
5206 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5208 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5210 struct ElementChangeInfo *change =
5211 &element_info[touched_element].change_page[i];
5213 if (change->can_change &&
5214 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5215 change->sides & hitting_side &&
5216 change->trigger_element == hitting_element)
5218 CheckElementSideChange(nextx, nexty, touched_element,
5219 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5230 TestIfPlayerTouchesCustomElement(newx, newy);
5231 TestIfElementTouchesCustomElement(newx, newy);
5234 int AmoebeNachbarNr(int ax, int ay)
5237 int element = Feld[ax][ay];
5239 static int xy[4][2] =
5247 for (i = 0; i < 4; i++)
5249 int x = ax + xy[i][0];
5250 int y = ay + xy[i][1];
5252 if (!IN_LEV_FIELD(x, y))
5255 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5256 group_nr = AmoebaNr[x][y];
5262 void AmoebenVereinigen(int ax, int ay)
5264 int i, x, y, xx, yy;
5265 int new_group_nr = AmoebaNr[ax][ay];
5266 static int xy[4][2] =
5274 if (new_group_nr == 0)
5277 for (i = 0; i < 4; i++)
5282 if (!IN_LEV_FIELD(x, y))
5285 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5286 Feld[x][y] == EL_BD_AMOEBA ||
5287 Feld[x][y] == EL_AMOEBA_DEAD) &&
5288 AmoebaNr[x][y] != new_group_nr)
5290 int old_group_nr = AmoebaNr[x][y];
5292 if (old_group_nr == 0)
5295 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5296 AmoebaCnt[old_group_nr] = 0;
5297 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5298 AmoebaCnt2[old_group_nr] = 0;
5300 for (yy = 0; yy < lev_fieldy; yy++)
5302 for (xx = 0; xx < lev_fieldx; xx++)
5304 if (AmoebaNr[xx][yy] == old_group_nr)
5305 AmoebaNr[xx][yy] = new_group_nr;
5312 void AmoebeUmwandeln(int ax, int ay)
5316 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5318 int group_nr = AmoebaNr[ax][ay];
5323 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5324 printf("AmoebeUmwandeln(): This should never happen!\n");
5329 for (y = 0; y < lev_fieldy; y++)
5331 for (x = 0; x < lev_fieldx; x++)
5333 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5336 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5340 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5341 SND_AMOEBA_TURNING_TO_GEM :
5342 SND_AMOEBA_TURNING_TO_ROCK));
5347 static int xy[4][2] =
5355 for (i = 0; i < 4; i++)
5360 if (!IN_LEV_FIELD(x, y))
5363 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5365 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5366 SND_AMOEBA_TURNING_TO_GEM :
5367 SND_AMOEBA_TURNING_TO_ROCK));
5374 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5377 int group_nr = AmoebaNr[ax][ay];
5378 boolean done = FALSE;
5383 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5384 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5389 for (y = 0; y < lev_fieldy; y++)
5391 for (x = 0; x < lev_fieldx; x++)
5393 if (AmoebaNr[x][y] == group_nr &&
5394 (Feld[x][y] == EL_AMOEBA_DEAD ||
5395 Feld[x][y] == EL_BD_AMOEBA ||
5396 Feld[x][y] == EL_AMOEBA_GROWING))
5399 Feld[x][y] = new_element;
5400 InitField(x, y, FALSE);
5401 DrawLevelField(x, y);
5408 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5409 SND_BD_AMOEBA_TURNING_TO_ROCK :
5410 SND_BD_AMOEBA_TURNING_TO_GEM));
5413 void AmoebeWaechst(int x, int y)
5415 static unsigned long sound_delay = 0;
5416 static unsigned long sound_delay_value = 0;
5418 if (!MovDelay[x][y]) /* start new growing cycle */
5422 if (DelayReached(&sound_delay, sound_delay_value))
5425 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5427 if (Store[x][y] == EL_BD_AMOEBA)
5428 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5430 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5432 sound_delay_value = 30;
5436 if (MovDelay[x][y]) /* wait some time before growing bigger */
5439 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5441 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5442 6 - MovDelay[x][y]);
5444 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5447 if (!MovDelay[x][y])
5449 Feld[x][y] = Store[x][y];
5451 DrawLevelField(x, y);
5456 void AmoebaDisappearing(int x, int y)
5458 static unsigned long sound_delay = 0;
5459 static unsigned long sound_delay_value = 0;
5461 if (!MovDelay[x][y]) /* start new shrinking cycle */
5465 if (DelayReached(&sound_delay, sound_delay_value))
5466 sound_delay_value = 30;
5469 if (MovDelay[x][y]) /* wait some time before shrinking */
5472 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5474 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5475 6 - MovDelay[x][y]);
5477 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5480 if (!MovDelay[x][y])
5482 Feld[x][y] = EL_EMPTY;
5483 DrawLevelField(x, y);
5485 /* don't let mole enter this field in this cycle;
5486 (give priority to objects falling to this field from above) */
5492 void AmoebeAbleger(int ax, int ay)
5495 int element = Feld[ax][ay];
5496 int graphic = el2img(element);
5497 int newax = ax, neway = ay;
5498 static int xy[4][2] =
5506 if (!level.amoeba_speed)
5508 Feld[ax][ay] = EL_AMOEBA_DEAD;
5509 DrawLevelField(ax, ay);
5513 if (IS_ANIMATED(graphic))
5514 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5516 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5517 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5519 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5522 if (MovDelay[ax][ay])
5526 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5529 int x = ax + xy[start][0];
5530 int y = ay + xy[start][1];
5532 if (!IN_LEV_FIELD(x, y))
5535 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5536 if (IS_FREE(x, y) ||
5537 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5543 if (newax == ax && neway == ay)
5546 else /* normal or "filled" (BD style) amoeba */
5549 boolean waiting_for_player = FALSE;
5551 for (i = 0; i < 4; i++)
5553 int j = (start + i) % 4;
5554 int x = ax + xy[j][0];
5555 int y = ay + xy[j][1];
5557 if (!IN_LEV_FIELD(x, y))
5560 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5561 if (IS_FREE(x, y) ||
5562 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5568 else if (IS_PLAYER(x, y))
5569 waiting_for_player = TRUE;
5572 if (newax == ax && neway == ay) /* amoeba cannot grow */
5574 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5576 Feld[ax][ay] = EL_AMOEBA_DEAD;
5577 DrawLevelField(ax, ay);
5578 AmoebaCnt[AmoebaNr[ax][ay]]--;
5580 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5582 if (element == EL_AMOEBA_FULL)
5583 AmoebeUmwandeln(ax, ay);
5584 else if (element == EL_BD_AMOEBA)
5585 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5590 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5592 /* amoeba gets larger by growing in some direction */
5594 int new_group_nr = AmoebaNr[ax][ay];
5597 if (new_group_nr == 0)
5599 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5600 printf("AmoebeAbleger(): This should never happen!\n");
5605 AmoebaNr[newax][neway] = new_group_nr;
5606 AmoebaCnt[new_group_nr]++;
5607 AmoebaCnt2[new_group_nr]++;
5609 /* if amoeba touches other amoeba(s) after growing, unify them */
5610 AmoebenVereinigen(newax, neway);
5612 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5614 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5620 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5621 (neway == lev_fieldy - 1 && newax != ax))
5623 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5624 Store[newax][neway] = element;
5626 else if (neway == ay)
5628 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5630 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5632 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5637 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5638 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5639 Store[ax][ay] = EL_AMOEBA_DROP;
5640 ContinueMoving(ax, ay);
5644 DrawLevelField(newax, neway);
5647 void Life(int ax, int ay)
5650 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5652 int element = Feld[ax][ay];
5653 int graphic = el2img(element);
5654 boolean changed = FALSE;
5656 if (IS_ANIMATED(graphic))
5657 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5662 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5663 MovDelay[ax][ay] = life_time;
5665 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5668 if (MovDelay[ax][ay])
5672 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5674 int xx = ax+x1, yy = ay+y1;
5677 if (!IN_LEV_FIELD(xx, yy))
5680 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5682 int x = xx+x2, y = yy+y2;
5684 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5687 if (((Feld[x][y] == element ||
5688 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5690 (IS_FREE(x, y) && Stop[x][y]))
5694 if (xx == ax && yy == ay) /* field in the middle */
5696 if (nachbarn < life[0] || nachbarn > life[1])
5698 Feld[xx][yy] = EL_EMPTY;
5700 DrawLevelField(xx, yy);
5701 Stop[xx][yy] = TRUE;
5705 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5706 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5707 { /* free border field */
5708 if (nachbarn >= life[2] && nachbarn <= life[3])
5710 Feld[xx][yy] = element;
5711 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5713 DrawLevelField(xx, yy);
5714 Stop[xx][yy] = TRUE;
5721 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5722 SND_GAME_OF_LIFE_GROWING);
5725 static void InitRobotWheel(int x, int y)
5727 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5730 static void RunRobotWheel(int x, int y)
5732 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5735 static void StopRobotWheel(int x, int y)
5737 if (ZX == x && ZY == y)
5741 static void InitTimegateWheel(int x, int y)
5743 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5746 static void RunTimegateWheel(int x, int y)
5748 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5751 void CheckExit(int x, int y)
5753 if (local_player->gems_still_needed > 0 ||
5754 local_player->sokobanfields_still_needed > 0 ||
5755 local_player->lights_still_needed > 0)
5757 int element = Feld[x][y];
5758 int graphic = el2img(element);
5760 if (IS_ANIMATED(graphic))
5761 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5766 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5769 Feld[x][y] = EL_EXIT_OPENING;
5771 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5774 void CheckExitSP(int x, int y)
5776 if (local_player->gems_still_needed > 0)
5778 int element = Feld[x][y];
5779 int graphic = el2img(element);
5781 if (IS_ANIMATED(graphic))
5782 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5787 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5790 Feld[x][y] = EL_SP_EXIT_OPENING;
5792 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5795 static void CloseAllOpenTimegates()
5799 for (y = 0; y < lev_fieldy; y++)
5801 for (x = 0; x < lev_fieldx; x++)
5803 int element = Feld[x][y];
5805 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5807 Feld[x][y] = EL_TIMEGATE_CLOSING;
5809 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5811 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5818 void EdelsteinFunkeln(int x, int y)
5820 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5823 if (Feld[x][y] == EL_BD_DIAMOND)
5826 if (MovDelay[x][y] == 0) /* next animation frame */
5827 MovDelay[x][y] = 11 * !SimpleRND(500);
5829 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5833 if (setup.direct_draw && MovDelay[x][y])
5834 SetDrawtoField(DRAW_BUFFERED);
5836 DrawLevelElementAnimation(x, y, Feld[x][y]);
5838 if (MovDelay[x][y] != 0)
5840 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5841 10 - MovDelay[x][y]);
5843 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5845 if (setup.direct_draw)
5849 dest_x = FX + SCREENX(x) * TILEX;
5850 dest_y = FY + SCREENY(y) * TILEY;
5852 BlitBitmap(drawto_field, window,
5853 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5854 SetDrawtoField(DRAW_DIRECT);
5860 void MauerWaechst(int x, int y)
5864 if (!MovDelay[x][y]) /* next animation frame */
5865 MovDelay[x][y] = 3 * delay;
5867 if (MovDelay[x][y]) /* wait some time before next frame */
5871 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5873 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5874 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5876 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5879 if (!MovDelay[x][y])
5881 if (MovDir[x][y] == MV_LEFT)
5883 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5884 DrawLevelField(x - 1, y);
5886 else if (MovDir[x][y] == MV_RIGHT)
5888 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5889 DrawLevelField(x + 1, y);
5891 else if (MovDir[x][y] == MV_UP)
5893 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5894 DrawLevelField(x, y - 1);
5898 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5899 DrawLevelField(x, y + 1);
5902 Feld[x][y] = Store[x][y];
5904 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5905 DrawLevelField(x, y);
5910 void MauerAbleger(int ax, int ay)
5912 int element = Feld[ax][ay];
5913 int graphic = el2img(element);
5914 boolean oben_frei = FALSE, unten_frei = FALSE;
5915 boolean links_frei = FALSE, rechts_frei = FALSE;
5916 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5917 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5918 boolean new_wall = FALSE;
5920 if (IS_ANIMATED(graphic))
5921 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5923 if (!MovDelay[ax][ay]) /* start building new wall */
5924 MovDelay[ax][ay] = 6;
5926 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5929 if (MovDelay[ax][ay])
5933 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5935 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5937 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5939 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5942 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5943 element == EL_EXPANDABLE_WALL_ANY)
5947 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5948 Store[ax][ay-1] = element;
5949 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5950 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5951 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5952 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5957 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5958 Store[ax][ay+1] = element;
5959 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5960 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5961 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5962 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5967 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5968 element == EL_EXPANDABLE_WALL_ANY ||
5969 element == EL_EXPANDABLE_WALL)
5973 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5974 Store[ax-1][ay] = element;
5975 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5976 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5977 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5978 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5984 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5985 Store[ax+1][ay] = element;
5986 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5987 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5988 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5989 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5994 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5995 DrawLevelField(ax, ay);
5997 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5999 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6000 unten_massiv = TRUE;
6001 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6002 links_massiv = TRUE;
6003 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6004 rechts_massiv = TRUE;
6006 if (((oben_massiv && unten_massiv) ||
6007 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6008 element == EL_EXPANDABLE_WALL) &&
6009 ((links_massiv && rechts_massiv) ||
6010 element == EL_EXPANDABLE_WALL_VERTICAL))
6011 Feld[ax][ay] = EL_WALL;
6015 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6017 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6021 void CheckForDragon(int x, int y)
6024 boolean dragon_found = FALSE;
6025 static int xy[4][2] =
6033 for (i = 0; i < 4; i++)
6035 for (j = 0; j < 4; j++)
6037 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6039 if (IN_LEV_FIELD(xx, yy) &&
6040 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6042 if (Feld[xx][yy] == EL_DRAGON)
6043 dragon_found = TRUE;
6052 for (i = 0; i < 4; i++)
6054 for (j = 0; j < 3; j++)
6056 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6058 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6060 Feld[xx][yy] = EL_EMPTY;
6061 DrawLevelField(xx, yy);
6070 static void InitBuggyBase(int x, int y)
6072 int element = Feld[x][y];
6073 int activating_delay = FRAMES_PER_SECOND / 4;
6076 (element == EL_SP_BUGGY_BASE ?
6077 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6078 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6080 element == EL_SP_BUGGY_BASE_ACTIVE ?
6081 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6084 static void WarnBuggyBase(int x, int y)
6087 static int xy[4][2] =
6095 for (i = 0; i < 4; i++)
6097 int xx = x + xy[i][0], yy = y + xy[i][1];
6099 if (IS_PLAYER(xx, yy))
6101 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6108 static void InitTrap(int x, int y)
6110 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6113 static void ActivateTrap(int x, int y)
6115 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6118 static void ChangeActiveTrap(int x, int y)
6120 int graphic = IMG_TRAP_ACTIVE;
6122 /* if new animation frame was drawn, correct crumbled sand border */
6123 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6124 DrawLevelFieldCrumbledSand(x, y);
6127 static void ChangeElementNowExt(int x, int y, int target_element)
6129 /* check if element under player changes from accessible to unaccessible
6130 (needed for special case of dropping element which then changes) */
6131 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6132 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6139 Feld[x][y] = target_element;
6141 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6143 ResetGfxAnimation(x, y);
6144 ResetRandomAnimationValue(x, y);
6146 InitField(x, y, FALSE);
6147 if (CAN_MOVE(Feld[x][y]))
6150 DrawLevelField(x, y);
6152 if (GFX_CRUMBLED(Feld[x][y]))
6153 DrawLevelFieldCrumbledSandNeighbours(x, y);
6155 TestIfBadThingTouchesHero(x, y);
6156 TestIfPlayerTouchesCustomElement(x, y);
6157 TestIfElementTouchesCustomElement(x, y);
6159 if (ELEM_IS_PLAYER(target_element))
6160 RelocatePlayer(x, y, target_element);
6163 static boolean ChangeElementNow(int x, int y, int element, int page)
6165 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6167 /* always use default change event to prevent running into a loop */
6168 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6169 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6171 /* do not change already changed elements with same change event */
6173 if (Changed[x][y] & ChangeEvent[x][y])
6180 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6182 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6184 if (change->explode)
6191 if (change->use_content)
6193 boolean complete_change = TRUE;
6194 boolean can_change[3][3];
6197 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6199 boolean half_destructible;
6200 int ex = x + xx - 1;
6201 int ey = y + yy - 1;
6204 can_change[xx][yy] = TRUE;
6206 if (ex == x && ey == y) /* do not check changing element itself */
6209 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6211 can_change[xx][yy] = FALSE; /* do not change empty borders */
6216 if (!IN_LEV_FIELD(ex, ey))
6218 can_change[xx][yy] = FALSE;
6219 complete_change = FALSE;
6226 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6227 e = MovingOrBlocked2Element(ex, ey);
6229 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6231 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6232 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6233 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6235 can_change[xx][yy] = FALSE;
6236 complete_change = FALSE;
6240 if (!change->only_complete || complete_change)
6242 boolean something_has_changed = FALSE;
6244 if (change->only_complete && change->use_random_change &&
6245 RND(100) < change->random)
6248 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6250 int ex = x + xx - 1;
6251 int ey = y + yy - 1;
6253 if (can_change[xx][yy] && (!change->use_random_change ||
6254 RND(100) < change->random))
6256 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6257 RemoveMovingField(ex, ey);
6259 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6261 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6263 something_has_changed = TRUE;
6265 /* for symmetry reasons, freeze newly created border elements */
6266 if (ex != x || ey != y)
6267 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6271 if (something_has_changed)
6272 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6277 ChangeElementNowExt(x, y, change->target_element);
6279 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6285 static void ChangeElement(int x, int y, int page)
6287 int element = MovingOrBlocked2Element(x, y);
6288 struct ElementInfo *ei = &element_info[element];
6289 struct ElementChangeInfo *change = &ei->change_page[page];
6293 if (!CAN_CHANGE(element))
6296 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6297 x, y, element, element_info[element].token_name);
6298 printf("ChangeElement(): This should never happen!\n");
6304 if (ChangeDelay[x][y] == 0) /* initialize element change */
6306 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6307 RND(change->delay_random * change->delay_frames)) + 1;
6309 ResetGfxAnimation(x, y);
6310 ResetRandomAnimationValue(x, y);
6312 if (change->pre_change_function)
6313 change->pre_change_function(x, y);
6316 ChangeDelay[x][y]--;
6318 if (ChangeDelay[x][y] != 0) /* continue element change */
6320 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6322 if (IS_ANIMATED(graphic))
6323 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6325 if (change->change_function)
6326 change->change_function(x, y);
6328 else /* finish element change */
6330 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6332 page = ChangePage[x][y];
6333 ChangePage[x][y] = -1;
6337 if (IS_MOVING(x, y) && !change->explode)
6339 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6342 ChangeDelay[x][y] = 1; /* try change after next move step */
6343 ChangePage[x][y] = page; /* remember page to use for change */
6348 if (ChangeElementNow(x, y, element, page))
6350 if (change->post_change_function)
6351 change->post_change_function(x, y);
6356 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6357 int trigger_element,
6363 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6366 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6368 int element = EL_CUSTOM_START + i;
6370 boolean change_element = FALSE;
6373 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6376 for (j = 0; j < element_info[element].num_change_pages; j++)
6378 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6380 if (change->can_change &&
6382 change->events & CH_EVENT_BIT(trigger_event) &&
6384 change->sides & trigger_side &&
6385 change->trigger_element == trigger_element)
6388 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6389 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6390 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6393 change_element = TRUE;
6400 if (!change_element)
6403 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6406 if (x == lx && y == ly) /* do not change trigger element itself */
6410 if (Feld[x][y] == element)
6412 ChangeDelay[x][y] = 1;
6413 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6414 ChangeElement(x, y, page);
6422 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6425 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6429 static boolean CheckElementSideChange(int x, int y, int element, int side,
6430 int trigger_event, int page)
6432 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6435 if (Feld[x][y] == EL_BLOCKED)
6437 Blocked2Moving(x, y, &x, &y);
6438 element = Feld[x][y];
6442 page = element_info[element].event_page_nr[trigger_event];
6444 if (!(element_info[element].change_page[page].sides & side))
6447 ChangeDelay[x][y] = 1;
6448 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6449 ChangeElement(x, y, page);
6454 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6456 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6459 static void PlayPlayerSound(struct PlayerInfo *player)
6461 int jx = player->jx, jy = player->jy;
6462 int element = player->element_nr;
6463 int last_action = player->last_action_waiting;
6464 int action = player->action_waiting;
6466 if (player->is_waiting)
6468 if (action != last_action)
6469 PlayLevelSoundElementAction(jx, jy, element, action);
6471 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6475 if (action != last_action)
6476 StopSound(element_info[element].sound[last_action]);
6478 if (last_action == ACTION_SLEEPING)
6479 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6483 static void PlayAllPlayersSound()
6487 for (i = 0; i < MAX_PLAYERS; i++)
6488 if (stored_player[i].active)
6489 PlayPlayerSound(&stored_player[i]);
6492 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6494 boolean last_waiting = player->is_waiting;
6495 int move_dir = player->MovDir;
6497 player->last_action_waiting = player->action_waiting;
6501 if (!last_waiting) /* not waiting -> waiting */
6503 player->is_waiting = TRUE;
6505 player->frame_counter_bored =
6507 game.player_boring_delay_fixed +
6508 SimpleRND(game.player_boring_delay_random);
6509 player->frame_counter_sleeping =
6511 game.player_sleeping_delay_fixed +
6512 SimpleRND(game.player_sleeping_delay_random);
6514 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6517 if (game.player_sleeping_delay_fixed +
6518 game.player_sleeping_delay_random > 0 &&
6519 player->anim_delay_counter == 0 &&
6520 player->post_delay_counter == 0 &&
6521 FrameCounter >= player->frame_counter_sleeping)
6522 player->is_sleeping = TRUE;
6523 else if (game.player_boring_delay_fixed +
6524 game.player_boring_delay_random > 0 &&
6525 FrameCounter >= player->frame_counter_bored)
6526 player->is_bored = TRUE;
6528 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6529 player->is_bored ? ACTION_BORING :
6532 if (player->is_sleeping)
6534 if (player->num_special_action_sleeping > 0)
6536 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6538 int last_special_action = player->special_action_sleeping;
6539 int num_special_action = player->num_special_action_sleeping;
6540 int special_action =
6541 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6542 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6543 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6544 last_special_action + 1 : ACTION_SLEEPING);
6545 int special_graphic =
6546 el_act_dir2img(player->element_nr, special_action, move_dir);
6548 player->anim_delay_counter =
6549 graphic_info[special_graphic].anim_delay_fixed +
6550 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6551 player->post_delay_counter =
6552 graphic_info[special_graphic].post_delay_fixed +
6553 SimpleRND(graphic_info[special_graphic].post_delay_random);
6555 player->special_action_sleeping = special_action;
6558 if (player->anim_delay_counter > 0)
6560 player->action_waiting = player->special_action_sleeping;
6561 player->anim_delay_counter--;
6563 else if (player->post_delay_counter > 0)
6565 player->post_delay_counter--;
6569 else if (player->is_bored)
6571 if (player->num_special_action_bored > 0)
6573 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6575 int special_action =
6576 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6577 int special_graphic =
6578 el_act_dir2img(player->element_nr, special_action, move_dir);
6580 player->anim_delay_counter =
6581 graphic_info[special_graphic].anim_delay_fixed +
6582 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6583 player->post_delay_counter =
6584 graphic_info[special_graphic].post_delay_fixed +
6585 SimpleRND(graphic_info[special_graphic].post_delay_random);
6587 player->special_action_bored = special_action;
6590 if (player->anim_delay_counter > 0)
6592 player->action_waiting = player->special_action_bored;
6593 player->anim_delay_counter--;
6595 else if (player->post_delay_counter > 0)
6597 player->post_delay_counter--;
6602 else if (last_waiting) /* waiting -> not waiting */
6604 player->is_waiting = FALSE;
6605 player->is_bored = FALSE;
6606 player->is_sleeping = FALSE;
6608 player->frame_counter_bored = -1;
6609 player->frame_counter_sleeping = -1;
6611 player->anim_delay_counter = 0;
6612 player->post_delay_counter = 0;
6614 player->action_waiting = ACTION_DEFAULT;
6616 player->special_action_bored = ACTION_DEFAULT;
6617 player->special_action_sleeping = ACTION_DEFAULT;
6622 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6625 static byte stored_player_action[MAX_PLAYERS];
6626 static int num_stored_actions = 0;
6628 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6629 int left = player_action & JOY_LEFT;
6630 int right = player_action & JOY_RIGHT;
6631 int up = player_action & JOY_UP;
6632 int down = player_action & JOY_DOWN;
6633 int button1 = player_action & JOY_BUTTON_1;
6634 int button2 = player_action & JOY_BUTTON_2;
6635 int dx = (left ? -1 : right ? 1 : 0);
6636 int dy = (up ? -1 : down ? 1 : 0);
6639 stored_player_action[player->index_nr] = 0;
6640 num_stored_actions++;
6644 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6647 if (!player->active || tape.pausing)
6651 printf("::: [%d %d %d %d] [%d %d]\n",
6652 left, right, up, down, button1, button2);
6658 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6662 snapped = SnapField(player, dx, dy);
6666 dropped = DropElement(player);
6668 moved = MovePlayer(player, dx, dy);
6671 if (tape.single_step && tape.recording && !tape.pausing)
6673 if (button1 || (dropped && !moved))
6675 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6676 SnapField(player, 0, 0); /* stop snapping */
6680 SetPlayerWaiting(player, FALSE);
6683 return player_action;
6685 stored_player_action[player->index_nr] = player_action;
6691 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6694 /* no actions for this player (no input at player's configured device) */
6696 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6697 SnapField(player, 0, 0);
6698 CheckGravityMovement(player);
6700 if (player->MovPos == 0)
6701 SetPlayerWaiting(player, TRUE);
6703 if (player->MovPos == 0) /* needed for tape.playing */
6704 player->is_moving = FALSE;
6706 player->is_dropping = FALSE;
6712 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6714 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6716 TapeRecordAction(stored_player_action);
6717 num_stored_actions = 0;
6724 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6726 static byte stored_player_action[MAX_PLAYERS];
6727 static int num_stored_actions = 0;
6728 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6729 int left = player_action & JOY_LEFT;
6730 int right = player_action & JOY_RIGHT;
6731 int up = player_action & JOY_UP;
6732 int down = player_action & JOY_DOWN;
6733 int button1 = player_action & JOY_BUTTON_1;
6734 int button2 = player_action & JOY_BUTTON_2;
6735 int dx = (left ? -1 : right ? 1 : 0);
6736 int dy = (up ? -1 : down ? 1 : 0);
6738 stored_player_action[player->index_nr] = 0;
6739 num_stored_actions++;
6741 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6743 if (!player->active || tape.pausing)
6748 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6751 snapped = SnapField(player, dx, dy);
6755 dropped = DropElement(player);
6757 moved = MovePlayer(player, dx, dy);
6760 if (tape.single_step && tape.recording && !tape.pausing)
6762 if (button1 || (dropped && !moved))
6764 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6765 SnapField(player, 0, 0); /* stop snapping */
6769 stored_player_action[player->index_nr] = player_action;
6773 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6775 /* no actions for this player (no input at player's configured device) */
6777 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6778 SnapField(player, 0, 0);
6779 CheckGravityMovement(player);
6781 if (player->MovPos == 0)
6782 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6784 if (player->MovPos == 0) /* needed for tape.playing */
6785 player->is_moving = FALSE;
6788 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6790 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6792 TapeRecordAction(stored_player_action);
6793 num_stored_actions = 0;
6800 static unsigned long action_delay = 0;
6801 unsigned long action_delay_value;
6802 int magic_wall_x = 0, magic_wall_y = 0;
6803 int i, x, y, element, graphic;
6804 byte *recorded_player_action;
6805 byte summarized_player_action = 0;
6807 byte tape_action[MAX_PLAYERS];
6810 if (game_status != GAME_MODE_PLAYING)
6813 action_delay_value =
6814 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6816 if (tape.playing && tape.index_search && !tape.pausing)
6817 action_delay_value = 0;
6819 /* ---------- main game synchronization point ---------- */
6821 WaitUntilDelayReached(&action_delay, action_delay_value);
6823 if (network_playing && !network_player_action_received)
6827 printf("DEBUG: try to get network player actions in time\n");
6831 #if defined(PLATFORM_UNIX)
6832 /* last chance to get network player actions without main loop delay */
6836 if (game_status != GAME_MODE_PLAYING)
6839 if (!network_player_action_received)
6843 printf("DEBUG: failed to get network player actions in time\n");
6854 printf("::: getting new tape action [%d]\n", FrameCounter);
6857 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6859 for (i = 0; i < MAX_PLAYERS; i++)
6861 summarized_player_action |= stored_player[i].action;
6863 if (!network_playing)
6864 stored_player[i].effective_action = stored_player[i].action;
6867 #if defined(PLATFORM_UNIX)
6868 if (network_playing)
6869 SendToServer_MovePlayer(summarized_player_action);
6872 if (!options.network && !setup.team_mode)
6873 local_player->effective_action = summarized_player_action;
6875 for (i = 0; i < MAX_PLAYERS; i++)
6877 int actual_player_action = stored_player[i].effective_action;
6879 if (stored_player[i].programmed_action)
6880 actual_player_action = stored_player[i].programmed_action;
6882 if (recorded_player_action)
6883 actual_player_action = recorded_player_action[i];
6885 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6887 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6888 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6890 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6895 TapeRecordAction(tape_action);
6898 network_player_action_received = FALSE;
6900 ScrollScreen(NULL, SCROLL_GO_ON);
6906 for (i = 0; i < MAX_PLAYERS; i++)
6907 stored_player[i].Frame++;
6911 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6913 for (i = 0; i < MAX_PLAYERS; i++)
6915 struct PlayerInfo *player = &stored_player[i];
6919 if (player->active && player->is_pushing && player->is_moving &&
6922 ContinueMoving(x, y);
6924 /* continue moving after pushing (this is actually a bug) */
6925 if (!IS_MOVING(x, y))
6934 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6936 Changed[x][y] = CE_BITMASK_DEFAULT;
6937 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6940 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6942 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6943 printf("GameActions(): This should never happen!\n");
6945 ChangePage[x][y] = -1;
6950 if (WasJustMoving[x][y] > 0)
6951 WasJustMoving[x][y]--;
6952 if (WasJustFalling[x][y] > 0)
6953 WasJustFalling[x][y]--;
6958 /* reset finished pushing action (not done in ContinueMoving() to allow
6959 continous pushing animation for elements with zero push delay) */
6960 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6962 ResetGfxAnimation(x, y);
6963 DrawLevelField(x, y);
6968 if (IS_BLOCKED(x, y))
6972 Blocked2Moving(x, y, &oldx, &oldy);
6973 if (!IS_MOVING(oldx, oldy))
6975 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6976 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6977 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6978 printf("GameActions(): This should never happen!\n");
6984 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6986 element = Feld[x][y];
6988 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6990 graphic = el2img(element);
6996 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6998 element = graphic = 0;
7002 if (graphic_info[graphic].anim_global_sync)
7003 GfxFrame[x][y] = FrameCounter;
7005 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7006 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7007 ResetRandomAnimationValue(x, y);
7009 SetRandomAnimationValue(x, y);
7012 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7015 if (IS_INACTIVE(element))
7017 if (IS_ANIMATED(graphic))
7018 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7024 /* this may take place after moving, so 'element' may have changed */
7026 if (IS_CHANGING(x, y))
7028 if (IS_CHANGING(x, y) &&
7029 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7033 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7034 element_info[element].event_page_nr[CE_DELAY]);
7036 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7039 element = Feld[x][y];
7040 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7044 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7049 element = Feld[x][y];
7050 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7052 if (element == EL_MOLE)
7053 printf("::: %d, %d, %d [%d]\n",
7054 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7058 if (element == EL_YAMYAM)
7059 printf("::: %d, %d, %d\n",
7060 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7064 if (IS_ANIMATED(graphic) &&
7068 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7071 if (element == EL_BUG)
7072 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7076 if (element == EL_MOLE)
7077 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7081 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7082 EdelsteinFunkeln(x, y);
7084 else if ((element == EL_ACID ||
7085 element == EL_EXIT_OPEN ||
7086 element == EL_SP_EXIT_OPEN ||
7087 element == EL_SP_TERMINAL ||
7088 element == EL_SP_TERMINAL_ACTIVE ||
7089 element == EL_EXTRA_TIME ||
7090 element == EL_SHIELD_NORMAL ||
7091 element == EL_SHIELD_DEADLY) &&
7092 IS_ANIMATED(graphic))
7093 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7094 else if (IS_MOVING(x, y))
7095 ContinueMoving(x, y);
7096 else if (IS_ACTIVE_BOMB(element))
7097 CheckDynamite(x, y);
7099 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7100 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7102 else if (element == EL_AMOEBA_GROWING)
7103 AmoebeWaechst(x, y);
7104 else if (element == EL_AMOEBA_SHRINKING)
7105 AmoebaDisappearing(x, y);
7107 #if !USE_NEW_AMOEBA_CODE
7108 else if (IS_AMOEBALIVE(element))
7109 AmoebeAbleger(x, y);
7112 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7114 else if (element == EL_EXIT_CLOSED)
7116 else if (element == EL_SP_EXIT_CLOSED)
7118 else if (element == EL_EXPANDABLE_WALL_GROWING)
7120 else if (element == EL_EXPANDABLE_WALL ||
7121 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7122 element == EL_EXPANDABLE_WALL_VERTICAL ||
7123 element == EL_EXPANDABLE_WALL_ANY)
7125 else if (element == EL_FLAMES)
7126 CheckForDragon(x, y);
7128 else if (IS_AUTO_CHANGING(element))
7129 ChangeElement(x, y);
7131 else if (element == EL_EXPLOSION)
7132 ; /* drawing of correct explosion animation is handled separately */
7133 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7134 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7137 /* this may take place after moving, so 'element' may have changed */
7138 if (IS_AUTO_CHANGING(Feld[x][y]))
7139 ChangeElement(x, y);
7142 if (IS_BELT_ACTIVE(element))
7143 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7145 if (game.magic_wall_active)
7147 int jx = local_player->jx, jy = local_player->jy;
7149 /* play the element sound at the position nearest to the player */
7150 if ((element == EL_MAGIC_WALL_FULL ||
7151 element == EL_MAGIC_WALL_ACTIVE ||
7152 element == EL_MAGIC_WALL_EMPTYING ||
7153 element == EL_BD_MAGIC_WALL_FULL ||
7154 element == EL_BD_MAGIC_WALL_ACTIVE ||
7155 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7156 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7164 #if USE_NEW_AMOEBA_CODE
7165 /* new experimental amoeba growth stuff */
7167 if (!(FrameCounter % 8))
7170 static unsigned long random = 1684108901;
7172 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7175 x = (random >> 10) % lev_fieldx;
7176 y = (random >> 20) % lev_fieldy;
7178 x = RND(lev_fieldx);
7179 y = RND(lev_fieldy);
7181 element = Feld[x][y];
7183 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7184 if (!IS_PLAYER(x,y) &&
7185 (element == EL_EMPTY ||
7186 element == EL_SAND ||
7187 element == EL_QUICKSAND_EMPTY ||
7188 element == EL_ACID_SPLASH_LEFT ||
7189 element == EL_ACID_SPLASH_RIGHT))
7191 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7192 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7193 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7194 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7195 Feld[x][y] = EL_AMOEBA_DROP;
7198 random = random * 129 + 1;
7204 if (game.explosions_delayed)
7207 game.explosions_delayed = FALSE;
7209 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7211 element = Feld[x][y];
7213 if (ExplodeField[x][y])
7214 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7215 else if (element == EL_EXPLOSION)
7216 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7218 ExplodeField[x][y] = EX_NO_EXPLOSION;
7221 game.explosions_delayed = TRUE;
7224 if (game.magic_wall_active)
7226 if (!(game.magic_wall_time_left % 4))
7228 int element = Feld[magic_wall_x][magic_wall_y];
7230 if (element == EL_BD_MAGIC_WALL_FULL ||
7231 element == EL_BD_MAGIC_WALL_ACTIVE ||
7232 element == EL_BD_MAGIC_WALL_EMPTYING)
7233 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7235 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7238 if (game.magic_wall_time_left > 0)
7240 game.magic_wall_time_left--;
7241 if (!game.magic_wall_time_left)
7243 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7245 element = Feld[x][y];
7247 if (element == EL_MAGIC_WALL_ACTIVE ||
7248 element == EL_MAGIC_WALL_FULL)
7250 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7251 DrawLevelField(x, y);
7253 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7254 element == EL_BD_MAGIC_WALL_FULL)
7256 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7257 DrawLevelField(x, y);
7261 game.magic_wall_active = FALSE;
7266 if (game.light_time_left > 0)
7268 game.light_time_left--;
7270 if (game.light_time_left == 0)
7271 RedrawAllLightSwitchesAndInvisibleElements();
7274 if (game.timegate_time_left > 0)
7276 game.timegate_time_left--;
7278 if (game.timegate_time_left == 0)
7279 CloseAllOpenTimegates();
7282 for (i = 0; i < MAX_PLAYERS; i++)
7284 struct PlayerInfo *player = &stored_player[i];
7286 if (SHIELD_ON(player))
7288 if (player->shield_deadly_time_left)
7289 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7290 else if (player->shield_normal_time_left)
7291 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7295 if (TimeFrames >= FRAMES_PER_SECOND)
7300 for (i = 0; i < MAX_PLAYERS; i++)
7302 struct PlayerInfo *player = &stored_player[i];
7304 if (SHIELD_ON(player))
7306 player->shield_normal_time_left--;
7308 if (player->shield_deadly_time_left > 0)
7309 player->shield_deadly_time_left--;
7313 if (tape.recording || tape.playing)
7314 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7320 if (TimeLeft <= 10 && setup.time_limit)
7321 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7323 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7325 if (!TimeLeft && setup.time_limit)
7326 for (i = 0; i < MAX_PLAYERS; i++)
7327 KillHero(&stored_player[i]);
7329 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7330 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7334 PlayAllPlayersSound();
7336 if (options.debug) /* calculate frames per second */
7338 static unsigned long fps_counter = 0;
7339 static int fps_frames = 0;
7340 unsigned long fps_delay_ms = Counter() - fps_counter;
7344 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7346 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7349 fps_counter = Counter();
7352 redraw_mask |= REDRAW_FPS;
7356 if (stored_player[0].jx != stored_player[0].last_jx ||
7357 stored_player[0].jy != stored_player[0].last_jy)
7358 printf("::: %d, %d, %d, %d, %d\n",
7359 stored_player[0].MovDir,
7360 stored_player[0].MovPos,
7361 stored_player[0].GfxPos,
7362 stored_player[0].Frame,
7363 stored_player[0].StepFrame);
7370 for (i = 0; i < MAX_PLAYERS; i++)
7373 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7375 stored_player[i].Frame += move_frames;
7377 if (stored_player[i].MovPos != 0)
7378 stored_player[i].StepFrame += move_frames;
7380 if (stored_player[i].drop_delay > 0)
7381 stored_player[i].drop_delay--;
7386 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7388 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7390 local_player->show_envelope = 0;
7395 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7397 int min_x = x, min_y = y, max_x = x, max_y = y;
7400 for (i = 0; i < MAX_PLAYERS; i++)
7402 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7404 if (!stored_player[i].active || &stored_player[i] == player)
7407 min_x = MIN(min_x, jx);
7408 min_y = MIN(min_y, jy);
7409 max_x = MAX(max_x, jx);
7410 max_y = MAX(max_y, jy);
7413 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7416 static boolean AllPlayersInVisibleScreen()
7420 for (i = 0; i < MAX_PLAYERS; i++)
7422 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7424 if (!stored_player[i].active)
7427 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7434 void ScrollLevel(int dx, int dy)
7436 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7439 BlitBitmap(drawto_field, drawto_field,
7440 FX + TILEX * (dx == -1) - softscroll_offset,
7441 FY + TILEY * (dy == -1) - softscroll_offset,
7442 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7443 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7444 FX + TILEX * (dx == 1) - softscroll_offset,
7445 FY + TILEY * (dy == 1) - softscroll_offset);
7449 x = (dx == 1 ? BX1 : BX2);
7450 for (y = BY1; y <= BY2; y++)
7451 DrawScreenField(x, y);
7456 y = (dy == 1 ? BY1 : BY2);
7457 for (x = BX1; x <= BX2; x++)
7458 DrawScreenField(x, y);
7461 redraw_mask |= REDRAW_FIELD;
7464 static void CheckGravityMovement(struct PlayerInfo *player)
7466 if (game.gravity && !player->programmed_action)
7468 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7469 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7471 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7472 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7473 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7474 int jx = player->jx, jy = player->jy;
7475 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7476 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7477 int new_jx = jx + dx, new_jy = jy + dy;
7478 boolean field_under_player_is_free =
7479 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7480 boolean player_is_moving_to_valid_field =
7481 (IN_LEV_FIELD(new_jx, new_jy) &&
7482 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7483 Feld[new_jx][new_jy] == EL_SAND));
7484 /* !!! extend EL_SAND to anything diggable !!! */
7486 if (field_under_player_is_free &&
7487 !player_is_moving_to_valid_field &&
7488 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7489 player->programmed_action = MV_DOWN;
7495 -----------------------------------------------------------------------------
7496 dx, dy: direction (non-diagonal) to try to move the player to
7497 real_dx, real_dy: direction as read from input device (can be diagonal)
7500 boolean MovePlayerOneStep(struct PlayerInfo *player,
7501 int dx, int dy, int real_dx, int real_dy)
7504 static int change_sides[4][2] =
7506 /* enter side leave side */
7507 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7508 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7509 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7510 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7512 int move_direction = (dx == -1 ? MV_LEFT :
7513 dx == +1 ? MV_RIGHT :
7515 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7516 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7517 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7519 int jx = player->jx, jy = player->jy;
7520 int new_jx = jx + dx, new_jy = jy + dy;
7524 if (!player->active || (!dx && !dy))
7525 return MF_NO_ACTION;
7527 player->MovDir = (dx < 0 ? MV_LEFT :
7530 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7532 if (!IN_LEV_FIELD(new_jx, new_jy))
7533 return MF_NO_ACTION;
7535 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7536 return MF_NO_ACTION;
7539 element = MovingOrBlocked2Element(new_jx, new_jy);
7541 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7544 if (DONT_RUN_INTO(element))
7546 if (element == EL_ACID && dx == 0 && dy == 1)
7549 Feld[jx][jy] = EL_PLAYER_1;
7550 InitMovingField(jx, jy, MV_DOWN);
7551 Store[jx][jy] = EL_ACID;
7552 ContinueMoving(jx, jy);
7556 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7561 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7562 if (can_move != MF_MOVING)
7565 /* check if DigField() has caused relocation of the player */
7566 if (player->jx != jx || player->jy != jy)
7567 return MF_NO_ACTION;
7569 StorePlayer[jx][jy] = 0;
7570 player->last_jx = jx;
7571 player->last_jy = jy;
7572 player->jx = new_jx;
7573 player->jy = new_jy;
7574 StorePlayer[new_jx][new_jy] = player->element_nr;
7577 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7579 player->step_counter++;
7581 player->drop_delay = 0;
7583 PlayerVisit[jx][jy] = FrameCounter;
7585 ScrollPlayer(player, SCROLL_INIT);
7588 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7590 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7591 CE_OTHER_GETS_LEFT);
7592 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7593 CE_LEFT_BY_PLAYER, -1);
7596 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7598 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7599 enter_side, CE_OTHER_GETS_ENTERED);
7600 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7601 CE_ENTERED_BY_PLAYER, -1);
7608 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7610 int jx = player->jx, jy = player->jy;
7611 int old_jx = jx, old_jy = jy;
7612 int moved = MF_NO_ACTION;
7615 if (!player->active)
7620 if (player->MovPos == 0)
7622 player->is_moving = FALSE;
7623 player->is_digging = FALSE;
7624 player->is_collecting = FALSE;
7625 player->is_snapping = FALSE;
7626 player->is_pushing = FALSE;
7632 if (!player->active || (!dx && !dy))
7637 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7641 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7642 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7646 /* remove the last programmed player action */
7647 player->programmed_action = 0;
7651 /* should only happen if pre-1.2 tape recordings are played */
7652 /* this is only for backward compatibility */
7654 int original_move_delay_value = player->move_delay_value;
7657 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7661 /* scroll remaining steps with finest movement resolution */
7662 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7664 while (player->MovPos)
7666 ScrollPlayer(player, SCROLL_GO_ON);
7667 ScrollScreen(NULL, SCROLL_GO_ON);
7673 player->move_delay_value = original_move_delay_value;
7676 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7678 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7679 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7683 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7684 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7690 if (moved & MF_MOVING && !ScreenMovPos &&
7691 (player == local_player || !options.network))
7693 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7694 int offset = (setup.scroll_delay ? 3 : 0);
7696 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7698 /* actual player has left the screen -- scroll in that direction */
7699 if (jx != old_jx) /* player has moved horizontally */
7700 scroll_x += (jx - old_jx);
7701 else /* player has moved vertically */
7702 scroll_y += (jy - old_jy);
7706 if (jx != old_jx) /* player has moved horizontally */
7708 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7709 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7710 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7712 /* don't scroll over playfield boundaries */
7713 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7714 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7716 /* don't scroll more than one field at a time */
7717 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7719 /* don't scroll against the player's moving direction */
7720 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7721 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7722 scroll_x = old_scroll_x;
7724 else /* player has moved vertically */
7726 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7727 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7728 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7730 /* don't scroll over playfield boundaries */
7731 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7732 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7734 /* don't scroll more than one field at a time */
7735 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7737 /* don't scroll against the player's moving direction */
7738 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7739 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7740 scroll_y = old_scroll_y;
7744 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7746 if (!options.network && !AllPlayersInVisibleScreen())
7748 scroll_x = old_scroll_x;
7749 scroll_y = old_scroll_y;
7753 ScrollScreen(player, SCROLL_INIT);
7754 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7761 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7763 if (!(moved & MF_MOVING) && !player->is_pushing)
7768 player->StepFrame = 0;
7770 if (moved & MF_MOVING)
7772 if (old_jx != jx && old_jy == jy)
7773 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7774 else if (old_jx == jx && old_jy != jy)
7775 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7777 DrawLevelField(jx, jy); /* for "crumbled sand" */
7779 player->last_move_dir = player->MovDir;
7780 player->is_moving = TRUE;
7782 player->is_snapping = FALSE;
7786 player->is_switching = FALSE;
7789 player->is_dropping = FALSE;
7794 static int change_sides[4][2] =
7796 /* enter side leave side */
7797 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7798 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7799 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7800 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7802 int move_direction = player->MovDir;
7803 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7804 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7807 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7809 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7810 leave_side, CE_OTHER_GETS_LEFT);
7811 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7812 leave_side, CE_LEFT_BY_PLAYER, -1);
7815 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7817 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7818 enter_side, CE_OTHER_GETS_ENTERED);
7819 CheckElementSideChange(jx, jy, Feld[jx][jy],
7820 enter_side, CE_ENTERED_BY_PLAYER, -1);
7831 CheckGravityMovement(player);
7834 player->last_move_dir = MV_NO_MOVING;
7836 player->is_moving = FALSE;
7839 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7841 TestIfHeroTouchesBadThing(jx, jy);
7842 TestIfPlayerTouchesCustomElement(jx, jy);
7845 if (!player->active)
7851 void ScrollPlayer(struct PlayerInfo *player, int mode)
7853 int jx = player->jx, jy = player->jy;
7854 int last_jx = player->last_jx, last_jy = player->last_jy;
7855 int move_stepsize = TILEX / player->move_delay_value;
7857 if (!player->active || !player->MovPos)
7860 if (mode == SCROLL_INIT)
7862 player->actual_frame_counter = FrameCounter;
7863 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7865 if (Feld[last_jx][last_jy] == EL_EMPTY)
7866 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7873 else if (!FrameReached(&player->actual_frame_counter, 1))
7876 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7877 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7879 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7880 Feld[last_jx][last_jy] = EL_EMPTY;
7882 /* before DrawPlayer() to draw correct player graphic for this case */
7883 if (player->MovPos == 0)
7884 CheckGravityMovement(player);
7887 DrawPlayer(player); /* needed here only to cleanup last field */
7890 if (player->MovPos == 0) /* player reached destination field */
7893 if (player->move_delay_reset_counter > 0)
7895 player->move_delay_reset_counter--;
7897 if (player->move_delay_reset_counter == 0)
7899 /* continue with normal speed after quickly moving through gate */
7900 HALVE_PLAYER_SPEED(player);
7902 /* be able to make the next move without delay */
7903 player->move_delay = 0;
7907 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7909 /* continue with normal speed after quickly moving through gate */
7910 HALVE_PLAYER_SPEED(player);
7912 /* be able to make the next move without delay */
7913 player->move_delay = 0;
7917 player->last_jx = jx;
7918 player->last_jy = jy;
7920 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7921 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7922 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7924 DrawPlayer(player); /* needed here only to cleanup last field */
7927 if (local_player->friends_still_needed == 0 ||
7928 IS_SP_ELEMENT(Feld[jx][jy]))
7929 player->LevelSolved = player->GameOver = TRUE;
7932 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7934 TestIfHeroTouchesBadThing(jx, jy);
7935 TestIfPlayerTouchesCustomElement(jx, jy);
7937 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7940 if (!player->active)
7944 if (tape.single_step && tape.recording && !tape.pausing &&
7945 !player->programmed_action)
7946 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7950 void ScrollScreen(struct PlayerInfo *player, int mode)
7952 static unsigned long screen_frame_counter = 0;
7954 if (mode == SCROLL_INIT)
7956 /* set scrolling step size according to actual player's moving speed */
7957 ScrollStepSize = TILEX / player->move_delay_value;
7959 screen_frame_counter = FrameCounter;
7960 ScreenMovDir = player->MovDir;
7961 ScreenMovPos = player->MovPos;
7962 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7965 else if (!FrameReached(&screen_frame_counter, 1))
7970 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7971 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7972 redraw_mask |= REDRAW_FIELD;
7975 ScreenMovDir = MV_NO_MOVING;
7978 void TestIfPlayerTouchesCustomElement(int x, int y)
7980 static int xy[4][2] =
7987 static int change_sides[4][2] =
7989 /* center side border side */
7990 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7991 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7992 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7993 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7995 static int touch_dir[4] =
8002 int center_element = Feld[x][y]; /* should always be non-moving! */
8005 for (i = 0; i < 4; i++)
8007 int xx = x + xy[i][0];
8008 int yy = y + xy[i][1];
8009 int center_side = change_sides[i][0];
8010 int border_side = change_sides[i][1];
8013 if (!IN_LEV_FIELD(xx, yy))
8016 if (IS_PLAYER(x, y))
8018 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8019 border_element = Feld[xx][yy]; /* may be moving! */
8020 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8021 border_element = Feld[xx][yy];
8022 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8023 border_element = MovingOrBlocked2Element(xx, yy);
8025 continue; /* center and border element do not touch */
8027 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8028 CE_OTHER_GETS_TOUCHED);
8029 CheckElementSideChange(xx, yy, border_element, border_side,
8030 CE_TOUCHED_BY_PLAYER, -1);
8032 else if (IS_PLAYER(xx, yy))
8034 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8036 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8038 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8039 continue; /* center and border element do not touch */
8042 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8043 CE_OTHER_GETS_TOUCHED);
8044 CheckElementSideChange(x, y, center_element, center_side,
8045 CE_TOUCHED_BY_PLAYER, -1);
8052 void TestIfElementTouchesCustomElement(int x, int y)
8054 static int xy[4][2] =
8061 static int change_sides[4][2] =
8063 /* center side border side */
8064 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8065 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8066 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8067 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8069 static int touch_dir[4] =
8076 boolean change_center_element = FALSE;
8077 int center_element_change_page = 0;
8078 int center_element = Feld[x][y]; /* should always be non-moving! */
8081 for (i = 0; i < 4; i++)
8083 int xx = x + xy[i][0];
8084 int yy = y + xy[i][1];
8085 int center_side = change_sides[i][0];
8086 int border_side = change_sides[i][1];
8089 if (!IN_LEV_FIELD(xx, yy))
8092 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8093 border_element = Feld[xx][yy]; /* may be moving! */
8094 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8095 border_element = Feld[xx][yy];
8096 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8097 border_element = MovingOrBlocked2Element(xx, yy);
8099 continue; /* center and border element do not touch */
8101 /* check for change of center element (but change it only once) */
8102 if (IS_CUSTOM_ELEMENT(center_element) &&
8103 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8104 !change_center_element)
8106 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8108 struct ElementChangeInfo *change =
8109 &element_info[center_element].change_page[j];
8111 if (change->can_change &&
8112 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8113 change->sides & border_side &&
8114 change->trigger_element == border_element)
8116 change_center_element = TRUE;
8117 center_element_change_page = j;
8124 /* check for change of border element */
8125 if (IS_CUSTOM_ELEMENT(border_element) &&
8126 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8128 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8130 struct ElementChangeInfo *change =
8131 &element_info[border_element].change_page[j];
8133 if (change->can_change &&
8134 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8135 change->sides & center_side &&
8136 change->trigger_element == center_element)
8138 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8139 CE_OTHER_IS_TOUCHING, j);
8146 if (change_center_element)
8147 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8148 CE_OTHER_IS_TOUCHING, center_element_change_page);
8151 void TestIfElementHitsCustomElement(int x, int y, int direction)
8153 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8154 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8155 int hitx = x + dx, hity = y + dy;
8156 int hitting_element = Feld[x][y];
8158 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8159 !IS_FREE(hitx, hity) &&
8160 (!IS_MOVING(hitx, hity) ||
8161 MovDir[hitx][hity] != direction ||
8162 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8165 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8169 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8173 CheckElementSideChange(x, y, hitting_element,
8174 direction, CE_HITTING_SOMETHING, -1);
8176 if (IN_LEV_FIELD(hitx, hity))
8178 static int opposite_directions[] =
8185 int move_dir_bit = MV_DIR_BIT(direction);
8186 int opposite_direction = opposite_directions[move_dir_bit];
8187 int hitting_side = direction;
8188 int touched_side = opposite_direction;
8189 int touched_element = MovingOrBlocked2Element(hitx, hity);
8191 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8192 MovDir[hitx][hity] != direction ||
8193 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8202 CheckElementSideChange(hitx, hity, touched_element,
8203 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8205 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8206 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8208 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8210 struct ElementChangeInfo *change =
8211 &element_info[hitting_element].change_page[i];
8213 if (change->can_change &&
8214 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8215 change->sides & touched_side &&
8216 change->trigger_element == touched_element)
8218 CheckElementSideChange(x, y, hitting_element,
8219 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8225 if (IS_CUSTOM_ELEMENT(touched_element) &&
8226 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8228 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8230 struct ElementChangeInfo *change =
8231 &element_info[touched_element].change_page[i];
8233 if (change->can_change &&
8234 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8235 change->sides & hitting_side &&
8236 change->trigger_element == hitting_element)
8238 CheckElementSideChange(hitx, hity, touched_element,
8239 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8248 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8250 int i, kill_x = -1, kill_y = -1;
8251 static int test_xy[4][2] =
8258 static int test_dir[4] =
8266 for (i = 0; i < 4; i++)
8268 int test_x, test_y, test_move_dir, test_element;
8270 test_x = good_x + test_xy[i][0];
8271 test_y = good_y + test_xy[i][1];
8272 if (!IN_LEV_FIELD(test_x, test_y))
8276 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8279 test_element = Feld[test_x][test_y];
8281 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8284 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8285 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8287 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8288 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8296 if (kill_x != -1 || kill_y != -1)
8298 if (IS_PLAYER(good_x, good_y))
8300 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8302 if (player->shield_deadly_time_left > 0)
8303 Bang(kill_x, kill_y);
8304 else if (!PLAYER_PROTECTED(good_x, good_y))
8308 Bang(good_x, good_y);
8312 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8314 int i, kill_x = -1, kill_y = -1;
8315 int bad_element = Feld[bad_x][bad_y];
8316 static int test_xy[4][2] =
8323 static int touch_dir[4] =
8330 static int test_dir[4] =
8338 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8341 for (i = 0; i < 4; i++)
8343 int test_x, test_y, test_move_dir, test_element;
8345 test_x = bad_x + test_xy[i][0];
8346 test_y = bad_y + test_xy[i][1];
8347 if (!IN_LEV_FIELD(test_x, test_y))
8351 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8353 test_element = Feld[test_x][test_y];
8355 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8356 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8358 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8359 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8361 /* good thing is player or penguin that does not move away */
8362 if (IS_PLAYER(test_x, test_y))
8364 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8366 if (bad_element == EL_ROBOT && player->is_moving)
8367 continue; /* robot does not kill player if he is moving */
8369 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8371 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8372 continue; /* center and border element do not touch */
8379 else if (test_element == EL_PENGUIN)
8388 if (kill_x != -1 || kill_y != -1)
8390 if (IS_PLAYER(kill_x, kill_y))
8392 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8394 if (player->shield_deadly_time_left > 0)
8396 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8400 Bang(kill_x, kill_y);
8404 void TestIfHeroTouchesBadThing(int x, int y)
8406 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8409 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8411 TestIfGoodThingHitsBadThing(x, y, move_dir);
8414 void TestIfBadThingTouchesHero(int x, int y)
8416 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8419 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8421 TestIfBadThingHitsGoodThing(x, y, move_dir);
8424 void TestIfFriendTouchesBadThing(int x, int y)
8426 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8429 void TestIfBadThingTouchesFriend(int x, int y)
8431 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8434 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8436 int i, kill_x = bad_x, kill_y = bad_y;
8437 static int xy[4][2] =
8445 for (i = 0; i < 4; i++)
8449 x = bad_x + xy[i][0];
8450 y = bad_y + xy[i][1];
8451 if (!IN_LEV_FIELD(x, y))
8454 element = Feld[x][y];
8455 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8456 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8464 if (kill_x != bad_x || kill_y != bad_y)
8468 void KillHero(struct PlayerInfo *player)
8470 int jx = player->jx, jy = player->jy;
8472 if (!player->active)
8475 /* remove accessible field at the player's position */
8476 Feld[jx][jy] = EL_EMPTY;
8478 /* deactivate shield (else Bang()/Explode() would not work right) */
8479 player->shield_normal_time_left = 0;
8480 player->shield_deadly_time_left = 0;
8486 static void KillHeroUnlessProtected(int x, int y)
8488 if (!PLAYER_PROTECTED(x, y))
8489 KillHero(PLAYERINFO(x, y));
8492 void BuryHero(struct PlayerInfo *player)
8494 int jx = player->jx, jy = player->jy;
8496 if (!player->active)
8500 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8502 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8504 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8506 player->GameOver = TRUE;
8510 void RemoveHero(struct PlayerInfo *player)
8512 int jx = player->jx, jy = player->jy;
8513 int i, found = FALSE;
8515 player->present = FALSE;
8516 player->active = FALSE;
8518 if (!ExplodeField[jx][jy])
8519 StorePlayer[jx][jy] = 0;
8521 for (i = 0; i < MAX_PLAYERS; i++)
8522 if (stored_player[i].active)
8526 AllPlayersGone = TRUE;
8533 =============================================================================
8534 checkDiagonalPushing()
8535 -----------------------------------------------------------------------------
8536 check if diagonal input device direction results in pushing of object
8537 (by checking if the alternative direction is walkable, diggable, ...)
8538 =============================================================================
8541 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8542 int x, int y, int real_dx, int real_dy)
8544 int jx, jy, dx, dy, xx, yy;
8546 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8549 /* diagonal direction: check alternative direction */
8554 xx = jx + (dx == 0 ? real_dx : 0);
8555 yy = jy + (dy == 0 ? real_dy : 0);
8557 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8561 =============================================================================
8563 -----------------------------------------------------------------------------
8564 x, y: field next to player (non-diagonal) to try to dig to
8565 real_dx, real_dy: direction as read from input device (can be diagonal)
8566 =============================================================================
8569 int DigField(struct PlayerInfo *player,
8570 int x, int y, int real_dx, int real_dy, int mode)
8572 static int change_sides[4] =
8574 CH_SIDE_RIGHT, /* moving left */
8575 CH_SIDE_LEFT, /* moving right */
8576 CH_SIDE_BOTTOM, /* moving up */
8577 CH_SIDE_TOP, /* moving down */
8579 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8580 int jx = player->jx, jy = player->jy;
8581 int dx = x - jx, dy = y - jy;
8582 int nextx = x + dx, nexty = y + dy;
8583 int move_direction = (dx == -1 ? MV_LEFT :
8584 dx == +1 ? MV_RIGHT :
8586 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8587 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8590 if (player->MovPos == 0)
8592 player->is_digging = FALSE;
8593 player->is_collecting = FALSE;
8596 if (player->MovPos == 0) /* last pushing move finished */
8597 player->is_pushing = FALSE;
8599 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8601 player->is_switching = FALSE;
8602 player->push_delay = 0;
8604 return MF_NO_ACTION;
8607 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8608 return MF_NO_ACTION;
8611 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8613 if (IS_TUBE(Feld[jx][jy]) ||
8614 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8618 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8619 int tube_leave_directions[][2] =
8621 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8622 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8623 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8624 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8625 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8626 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8627 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8628 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8629 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8630 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8631 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8632 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8635 while (tube_leave_directions[i][0] != tube_element)
8638 if (tube_leave_directions[i][0] == -1) /* should not happen */
8642 if (!(tube_leave_directions[i][1] & move_direction))
8643 return MF_NO_ACTION; /* tube has no opening in this direction */
8646 element = Feld[x][y];
8648 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8649 game.engine_version >= VERSION_IDENT(2,2,0,0))
8650 return MF_NO_ACTION;
8654 case EL_SP_PORT_LEFT:
8655 case EL_SP_PORT_RIGHT:
8657 case EL_SP_PORT_DOWN:
8658 case EL_SP_PORT_HORIZONTAL:
8659 case EL_SP_PORT_VERTICAL:
8660 case EL_SP_PORT_ANY:
8661 case EL_SP_GRAVITY_PORT_LEFT:
8662 case EL_SP_GRAVITY_PORT_RIGHT:
8663 case EL_SP_GRAVITY_PORT_UP:
8664 case EL_SP_GRAVITY_PORT_DOWN:
8666 element != EL_SP_PORT_LEFT &&
8667 element != EL_SP_GRAVITY_PORT_LEFT &&
8668 element != EL_SP_PORT_HORIZONTAL &&
8669 element != EL_SP_PORT_ANY) ||
8671 element != EL_SP_PORT_RIGHT &&
8672 element != EL_SP_GRAVITY_PORT_RIGHT &&
8673 element != EL_SP_PORT_HORIZONTAL &&
8674 element != EL_SP_PORT_ANY) ||
8676 element != EL_SP_PORT_UP &&
8677 element != EL_SP_GRAVITY_PORT_UP &&
8678 element != EL_SP_PORT_VERTICAL &&
8679 element != EL_SP_PORT_ANY) ||
8681 element != EL_SP_PORT_DOWN &&
8682 element != EL_SP_GRAVITY_PORT_DOWN &&
8683 element != EL_SP_PORT_VERTICAL &&
8684 element != EL_SP_PORT_ANY) ||
8685 !IN_LEV_FIELD(nextx, nexty) ||
8686 !IS_FREE(nextx, nexty))
8687 return MF_NO_ACTION;
8689 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8690 element == EL_SP_GRAVITY_PORT_RIGHT ||
8691 element == EL_SP_GRAVITY_PORT_UP ||
8692 element == EL_SP_GRAVITY_PORT_DOWN)
8693 game.gravity = !game.gravity;
8695 /* automatically move to the next field with double speed */
8696 player->programmed_action = move_direction;
8698 if (player->move_delay_reset_counter == 0)
8700 player->move_delay_reset_counter = 2; /* two double speed steps */
8702 DOUBLE_PLAYER_SPEED(player);
8705 player->move_delay_reset_counter = 2;
8707 DOUBLE_PLAYER_SPEED(player);
8710 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8714 case EL_TUBE_VERTICAL:
8715 case EL_TUBE_HORIZONTAL:
8716 case EL_TUBE_VERTICAL_LEFT:
8717 case EL_TUBE_VERTICAL_RIGHT:
8718 case EL_TUBE_HORIZONTAL_UP:
8719 case EL_TUBE_HORIZONTAL_DOWN:
8720 case EL_TUBE_LEFT_UP:
8721 case EL_TUBE_LEFT_DOWN:
8722 case EL_TUBE_RIGHT_UP:
8723 case EL_TUBE_RIGHT_DOWN:
8726 int tube_enter_directions[][2] =
8728 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8729 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8730 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8731 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8732 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8733 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8734 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8735 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8736 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8737 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8738 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8739 { -1, MV_NO_MOVING }
8742 while (tube_enter_directions[i][0] != element)
8745 if (tube_enter_directions[i][0] == -1) /* should not happen */
8749 if (!(tube_enter_directions[i][1] & move_direction))
8750 return MF_NO_ACTION; /* tube has no opening in this direction */
8752 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8758 if (IS_WALKABLE(element))
8760 int sound_action = ACTION_WALKING;
8762 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8764 if (!player->key[element - EL_GATE_1])
8765 return MF_NO_ACTION;
8767 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8769 if (!player->key[element - EL_GATE_1_GRAY])
8770 return MF_NO_ACTION;
8772 else if (element == EL_EXIT_OPEN ||
8773 element == EL_SP_EXIT_OPEN ||
8774 element == EL_SP_EXIT_OPENING)
8776 sound_action = ACTION_PASSING; /* player is passing exit */
8778 else if (element == EL_EMPTY)
8780 sound_action = ACTION_MOVING; /* nothing to walk on */
8783 /* play sound from background or player, whatever is available */
8784 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8785 PlayLevelSoundElementAction(x, y, element, sound_action);
8787 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8791 else if (IS_PASSABLE(element))
8793 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8794 return MF_NO_ACTION;
8797 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8798 return MF_NO_ACTION;
8801 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8803 if (!player->key[element - EL_EM_GATE_1])
8804 return MF_NO_ACTION;
8806 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8808 if (!player->key[element - EL_EM_GATE_1_GRAY])
8809 return MF_NO_ACTION;
8812 /* automatically move to the next field with double speed */
8813 player->programmed_action = move_direction;
8815 if (player->move_delay_reset_counter == 0)
8817 player->move_delay_reset_counter = 2; /* two double speed steps */
8819 DOUBLE_PLAYER_SPEED(player);
8822 player->move_delay_reset_counter = 2;
8824 DOUBLE_PLAYER_SPEED(player);
8827 PlayLevelSoundAction(x, y, ACTION_PASSING);
8831 else if (IS_DIGGABLE(element))
8835 if (mode != DF_SNAP)
8838 GfxElement[x][y] = GFX_ELEMENT(element);
8841 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8843 player->is_digging = TRUE;
8846 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8848 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8851 if (mode == DF_SNAP)
8852 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8857 else if (IS_COLLECTIBLE(element))
8861 if (mode != DF_SNAP)
8863 GfxElement[x][y] = element;
8864 player->is_collecting = TRUE;
8867 if (element == EL_SPEED_PILL)
8868 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8869 else if (element == EL_EXTRA_TIME && level.time > 0)
8872 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8874 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8876 player->shield_normal_time_left += 10;
8877 if (element == EL_SHIELD_DEADLY)
8878 player->shield_deadly_time_left += 10;
8880 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8882 if (player->inventory_size < MAX_INVENTORY_SIZE)
8883 player->inventory_element[player->inventory_size++] = element;
8885 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8886 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8888 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8890 player->dynabomb_count++;
8891 player->dynabombs_left++;
8893 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8895 player->dynabomb_size++;
8897 else if (element == EL_DYNABOMB_INCREASE_POWER)
8899 player->dynabomb_xl = TRUE;
8901 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8902 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8904 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8905 element - EL_KEY_1 : element - EL_EM_KEY_1);
8907 player->key[key_nr] = TRUE;
8909 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8910 el2edimg(EL_KEY_1 + key_nr));
8911 redraw_mask |= REDRAW_DOOR_1;
8913 else if (IS_ENVELOPE(element))
8916 player->show_envelope = element;
8918 ShowEnvelope(element - EL_ENVELOPE_1);
8921 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8925 for (i = 0; i < element_info[element].collect_count; i++)
8926 if (player->inventory_size < MAX_INVENTORY_SIZE)
8927 player->inventory_element[player->inventory_size++] = element;
8929 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8930 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8932 else if (element_info[element].collect_count > 0)
8934 local_player->gems_still_needed -=
8935 element_info[element].collect_count;
8936 if (local_player->gems_still_needed < 0)
8937 local_player->gems_still_needed = 0;
8939 DrawText(DX_EMERALDS, DY_EMERALDS,
8940 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8943 RaiseScoreElement(element);
8944 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8946 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8949 if (mode == DF_SNAP)
8950 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8955 else if (IS_PUSHABLE(element))
8957 if (mode == DF_SNAP && element != EL_BD_ROCK)
8958 return MF_NO_ACTION;
8960 if (CAN_FALL(element) && dy)
8961 return MF_NO_ACTION;
8963 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8964 !(element == EL_SPRING && use_spring_bug))
8965 return MF_NO_ACTION;
8968 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8969 ((move_direction & MV_VERTICAL &&
8970 ((element_info[element].move_pattern & MV_LEFT &&
8971 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8972 (element_info[element].move_pattern & MV_RIGHT &&
8973 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8974 (move_direction & MV_HORIZONTAL &&
8975 ((element_info[element].move_pattern & MV_UP &&
8976 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8977 (element_info[element].move_pattern & MV_DOWN &&
8978 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8979 return MF_NO_ACTION;
8983 /* do not push elements already moving away faster than player */
8984 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8985 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8986 return MF_NO_ACTION;
8988 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8989 return MF_NO_ACTION;
8993 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8995 if (player->push_delay_value == -1)
8996 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8998 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9000 if (!player->is_pushing)
9001 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9005 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9006 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9007 !player_is_pushing))
9008 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9011 if (!player->is_pushing &&
9012 game.engine_version >= VERSION_IDENT(2,2,0,7))
9013 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9017 printf("::: push delay: %ld [%d, %d] [%d]\n",
9018 player->push_delay_value, FrameCounter, game.engine_version,
9019 player->is_pushing);
9022 player->is_pushing = TRUE;
9024 if (!(IN_LEV_FIELD(nextx, nexty) &&
9025 (IS_FREE(nextx, nexty) ||
9026 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9027 IS_SB_ELEMENT(element)))))
9028 return MF_NO_ACTION;
9030 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9031 return MF_NO_ACTION;
9033 if (player->push_delay == 0) /* new pushing; restart delay */
9034 player->push_delay = FrameCounter;
9036 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9037 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9038 element != EL_SPRING && element != EL_BALLOON)
9040 /* make sure that there is no move delay before next try to push */
9041 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9042 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9044 return MF_NO_ACTION;
9048 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9051 if (IS_SB_ELEMENT(element))
9053 if (element == EL_SOKOBAN_FIELD_FULL)
9055 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9056 local_player->sokobanfields_still_needed++;
9059 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9061 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9062 local_player->sokobanfields_still_needed--;
9065 Feld[x][y] = EL_SOKOBAN_OBJECT;
9067 if (Back[x][y] == Back[nextx][nexty])
9068 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9069 else if (Back[x][y] != 0)
9070 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9073 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9076 if (local_player->sokobanfields_still_needed == 0 &&
9077 game.emulation == EMU_SOKOBAN)
9079 player->LevelSolved = player->GameOver = TRUE;
9080 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9084 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9086 InitMovingField(x, y, move_direction);
9087 GfxAction[x][y] = ACTION_PUSHING;
9089 if (mode == DF_SNAP)
9090 ContinueMoving(x, y);
9092 MovPos[x][y] = (dx != 0 ? dx : dy);
9094 Pushed[x][y] = TRUE;
9095 Pushed[nextx][nexty] = TRUE;
9097 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9098 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9100 player->push_delay_value = -1; /* get new value later */
9102 CheckTriggeredElementSideChange(x, y, element, dig_side,
9103 CE_OTHER_GETS_PUSHED);
9104 CheckElementSideChange(x, y, element, dig_side,
9105 CE_PUSHED_BY_PLAYER, -1);
9109 else if (IS_SWITCHABLE(element))
9111 if (PLAYER_SWITCHING(player, x, y))
9114 player->is_switching = TRUE;
9115 player->switch_x = x;
9116 player->switch_y = y;
9118 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9120 if (element == EL_ROBOT_WHEEL)
9122 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9126 DrawLevelField(x, y);
9128 else if (element == EL_SP_TERMINAL)
9132 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9134 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9136 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9137 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9140 else if (IS_BELT_SWITCH(element))
9142 ToggleBeltSwitch(x, y);
9144 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9145 element == EL_SWITCHGATE_SWITCH_DOWN)
9147 ToggleSwitchgateSwitch(x, y);
9149 else if (element == EL_LIGHT_SWITCH ||
9150 element == EL_LIGHT_SWITCH_ACTIVE)
9152 ToggleLightSwitch(x, y);
9155 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9156 SND_LIGHT_SWITCH_ACTIVATING :
9157 SND_LIGHT_SWITCH_DEACTIVATING);
9160 else if (element == EL_TIMEGATE_SWITCH)
9162 ActivateTimegateSwitch(x, y);
9164 else if (element == EL_BALLOON_SWITCH_LEFT ||
9165 element == EL_BALLOON_SWITCH_RIGHT ||
9166 element == EL_BALLOON_SWITCH_UP ||
9167 element == EL_BALLOON_SWITCH_DOWN ||
9168 element == EL_BALLOON_SWITCH_ANY)
9170 if (element == EL_BALLOON_SWITCH_ANY)
9171 game.balloon_dir = move_direction;
9173 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9174 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9175 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9176 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9179 else if (element == EL_LAMP)
9181 Feld[x][y] = EL_LAMP_ACTIVE;
9182 local_player->lights_still_needed--;
9184 DrawLevelField(x, y);
9186 else if (element == EL_TIME_ORB_FULL)
9188 Feld[x][y] = EL_TIME_ORB_EMPTY;
9190 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9192 DrawLevelField(x, y);
9195 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9203 if (!PLAYER_SWITCHING(player, x, y))
9205 player->is_switching = TRUE;
9206 player->switch_x = x;
9207 player->switch_y = y;
9209 CheckTriggeredElementSideChange(x, y, element, dig_side,
9210 CE_OTHER_IS_SWITCHING);
9211 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9214 CheckTriggeredElementSideChange(x, y, element, dig_side,
9215 CE_OTHER_GETS_PRESSED);
9216 CheckElementSideChange(x, y, element, dig_side,
9217 CE_PRESSED_BY_PLAYER, -1);
9220 return MF_NO_ACTION;
9223 player->push_delay = 0;
9225 if (Feld[x][y] != element) /* really digged/collected something */
9226 player->is_collecting = !player->is_digging;
9231 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9233 int jx = player->jx, jy = player->jy;
9234 int x = jx + dx, y = jy + dy;
9235 int snap_direction = (dx == -1 ? MV_LEFT :
9236 dx == +1 ? MV_RIGHT :
9238 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9240 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9243 if (!player->active || !IN_LEV_FIELD(x, y))
9251 if (player->MovPos == 0)
9252 player->is_pushing = FALSE;
9254 player->is_snapping = FALSE;
9256 if (player->MovPos == 0)
9258 player->is_moving = FALSE;
9259 player->is_digging = FALSE;
9260 player->is_collecting = FALSE;
9266 if (player->is_snapping)
9269 player->MovDir = snap_direction;
9272 if (player->MovPos == 0)
9275 player->is_moving = FALSE;
9276 player->is_digging = FALSE;
9277 player->is_collecting = FALSE;
9280 player->is_dropping = FALSE;
9282 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9285 player->is_snapping = TRUE;
9288 if (player->MovPos == 0)
9291 player->is_moving = FALSE;
9292 player->is_digging = FALSE;
9293 player->is_collecting = FALSE;
9296 DrawLevelField(x, y);
9302 boolean DropElement(struct PlayerInfo *player)
9304 int jx = player->jx, jy = player->jy;
9305 int old_element = Feld[jx][jy];
9308 /* check if player is active, not moving and ready to drop */
9309 if (!player->active || player->MovPos || player->drop_delay > 0)
9312 /* check if player has anything that can be dropped */
9313 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9316 /* check if anything can be dropped at the current position */
9317 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9320 /* collected custom elements can only be dropped on empty fields */
9321 if (player->inventory_size > 0 &&
9322 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9323 && old_element != EL_EMPTY)
9326 if (old_element != EL_EMPTY)
9327 Back[jx][jy] = old_element; /* store old element on this field */
9329 ResetGfxAnimation(jx, jy);
9330 ResetRandomAnimationValue(jx, jy);
9332 if (player->inventory_size > 0)
9334 player->inventory_size--;
9335 new_element = player->inventory_element[player->inventory_size];
9337 if (new_element == EL_DYNAMITE)
9338 new_element = EL_DYNAMITE_ACTIVE;
9339 else if (new_element == EL_SP_DISK_RED)
9340 new_element = EL_SP_DISK_RED_ACTIVE;
9342 Feld[jx][jy] = new_element;
9344 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9345 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9347 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9348 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9350 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9352 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9353 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9355 TestIfElementTouchesCustomElement(jx, jy);
9357 else /* player is dropping a dyna bomb */
9359 player->dynabombs_left--;
9360 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9362 Feld[jx][jy] = new_element;
9364 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9365 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9367 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9374 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9376 InitField(jx, jy, FALSE);
9377 if (CAN_MOVE(Feld[jx][jy]))
9381 new_element = Feld[jx][jy];
9383 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9384 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9386 int move_stepsize = element_info[new_element].move_stepsize;
9387 int direction, dx, dy, nextx, nexty;
9389 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9390 MovDir[jx][jy] = player->MovDir;
9392 direction = MovDir[jx][jy];
9393 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9394 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9398 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9401 WasJustMoving[jx][jy] = 3;
9403 InitMovingField(jx, jy, direction);
9404 ContinueMoving(jx, jy);
9409 Changed[jx][jy] = 0; /* allow another change */
9412 TestIfElementHitsCustomElement(jx, jy, direction);
9414 CheckElementSideChange(jx, jy, new_element,
9415 direction, CE_HITTING_SOMETHING, -1);
9419 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9423 player->drop_delay = 8 + 8 + 8;
9428 player->is_dropping = TRUE;
9434 /* ------------------------------------------------------------------------- */
9435 /* game sound playing functions */
9436 /* ------------------------------------------------------------------------- */
9438 static int *loop_sound_frame = NULL;
9439 static int *loop_sound_volume = NULL;
9441 void InitPlayLevelSound()
9443 int num_sounds = getSoundListSize();
9445 checked_free(loop_sound_frame);
9446 checked_free(loop_sound_volume);
9448 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9449 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9452 static void PlayLevelSound(int x, int y, int nr)
9454 int sx = SCREENX(x), sy = SCREENY(y);
9455 int volume, stereo_position;
9456 int max_distance = 8;
9457 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9459 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9460 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9463 if (!IN_LEV_FIELD(x, y) ||
9464 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9465 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9468 volume = SOUND_MAX_VOLUME;
9470 if (!IN_SCR_FIELD(sx, sy))
9472 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9473 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9475 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9478 stereo_position = (SOUND_MAX_LEFT +
9479 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9480 (SCR_FIELDX + 2 * max_distance));
9482 if (IS_LOOP_SOUND(nr))
9484 /* This assures that quieter loop sounds do not overwrite louder ones,
9485 while restarting sound volume comparison with each new game frame. */
9487 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9490 loop_sound_volume[nr] = volume;
9491 loop_sound_frame[nr] = FrameCounter;
9494 PlaySoundExt(nr, volume, stereo_position, type);
9497 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9499 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9500 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9501 y < LEVELY(BY1) ? LEVELY(BY1) :
9502 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9506 static void PlayLevelSoundAction(int x, int y, int action)
9508 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9511 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9513 int sound_effect = element_info[element].sound[action];
9515 if (sound_effect != SND_UNDEFINED)
9516 PlayLevelSound(x, y, sound_effect);
9519 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9522 int sound_effect = element_info[element].sound[action];
9524 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9525 PlayLevelSound(x, y, sound_effect);
9528 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9530 int sound_effect = element_info[Feld[x][y]].sound[action];
9532 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9533 PlayLevelSound(x, y, sound_effect);
9536 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9538 int sound_effect = element_info[Feld[x][y]].sound[action];
9540 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9541 StopSound(sound_effect);
9544 static void PlayLevelMusic()
9546 if (levelset.music[level_nr] != MUS_UNDEFINED)
9547 PlayMusic(levelset.music[level_nr]); /* from config file */
9549 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9552 void RaiseScore(int value)
9554 local_player->score += value;
9555 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9558 void RaiseScoreElement(int element)
9564 case EL_EMERALD_YELLOW:
9565 case EL_EMERALD_RED:
9566 case EL_EMERALD_PURPLE:
9567 case EL_SP_INFOTRON:
9568 RaiseScore(level.score[SC_EMERALD]);
9571 RaiseScore(level.score[SC_DIAMOND]);
9574 RaiseScore(level.score[SC_CRYSTAL]);
9577 RaiseScore(level.score[SC_PEARL]);
9580 case EL_BD_BUTTERFLY:
9581 case EL_SP_ELECTRON:
9582 RaiseScore(level.score[SC_BUG]);
9586 case EL_SP_SNIKSNAK:
9587 RaiseScore(level.score[SC_SPACESHIP]);
9590 case EL_DARK_YAMYAM:
9591 RaiseScore(level.score[SC_YAMYAM]);
9594 RaiseScore(level.score[SC_ROBOT]);
9597 RaiseScore(level.score[SC_PACMAN]);
9600 RaiseScore(level.score[SC_NUT]);
9603 case EL_SP_DISK_RED:
9604 case EL_DYNABOMB_INCREASE_NUMBER:
9605 case EL_DYNABOMB_INCREASE_SIZE:
9606 case EL_DYNABOMB_INCREASE_POWER:
9607 RaiseScore(level.score[SC_DYNAMITE]);
9609 case EL_SHIELD_NORMAL:
9610 case EL_SHIELD_DEADLY:
9611 RaiseScore(level.score[SC_SHIELD]);
9614 RaiseScore(level.score[SC_TIME_BONUS]);
9620 RaiseScore(level.score[SC_KEY]);
9623 RaiseScore(element_info[element].collect_score);
9628 void RequestQuitGame(boolean ask_if_really_quit)
9630 if (AllPlayersGone ||
9631 !ask_if_really_quit ||
9632 level_editor_test_game ||
9633 Request("Do you really want to quit the game ?",
9634 REQ_ASK | REQ_STAY_CLOSED))
9636 #if defined(PLATFORM_UNIX)
9637 if (options.network)
9638 SendToServer_StopPlaying();
9642 game_status = GAME_MODE_MAIN;
9648 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9653 /* ---------- new game button stuff ---------------------------------------- */
9655 /* graphic position values for game buttons */
9656 #define GAME_BUTTON_XSIZE 30
9657 #define GAME_BUTTON_YSIZE 30
9658 #define GAME_BUTTON_XPOS 5
9659 #define GAME_BUTTON_YPOS 215
9660 #define SOUND_BUTTON_XPOS 5
9661 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9663 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9664 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9665 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9666 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9667 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9668 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9675 } gamebutton_info[NUM_GAME_BUTTONS] =
9678 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9683 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9688 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9693 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9694 SOUND_CTRL_ID_MUSIC,
9695 "background music on/off"
9698 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9699 SOUND_CTRL_ID_LOOPS,
9700 "sound loops on/off"
9703 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9704 SOUND_CTRL_ID_SIMPLE,
9705 "normal sounds on/off"
9709 void CreateGameButtons()
9713 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9715 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9716 struct GadgetInfo *gi;
9719 unsigned long event_mask;
9720 int gd_xoffset, gd_yoffset;
9721 int gd_x1, gd_x2, gd_y1, gd_y2;
9724 gd_xoffset = gamebutton_info[i].x;
9725 gd_yoffset = gamebutton_info[i].y;
9726 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9727 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9729 if (id == GAME_CTRL_ID_STOP ||
9730 id == GAME_CTRL_ID_PAUSE ||
9731 id == GAME_CTRL_ID_PLAY)
9733 button_type = GD_TYPE_NORMAL_BUTTON;
9735 event_mask = GD_EVENT_RELEASED;
9736 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9737 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9741 button_type = GD_TYPE_CHECK_BUTTON;
9743 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9744 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9745 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9746 event_mask = GD_EVENT_PRESSED;
9747 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9748 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9751 gi = CreateGadget(GDI_CUSTOM_ID, id,
9752 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9753 GDI_X, DX + gd_xoffset,
9754 GDI_Y, DY + gd_yoffset,
9755 GDI_WIDTH, GAME_BUTTON_XSIZE,
9756 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9757 GDI_TYPE, button_type,
9758 GDI_STATE, GD_BUTTON_UNPRESSED,
9759 GDI_CHECKED, checked,
9760 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9761 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9762 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9763 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9764 GDI_EVENT_MASK, event_mask,
9765 GDI_CALLBACK_ACTION, HandleGameButtons,
9769 Error(ERR_EXIT, "cannot create gadget");
9771 game_gadget[id] = gi;
9775 void FreeGameButtons()
9779 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9780 FreeGadget(game_gadget[i]);
9783 static void MapGameButtons()
9787 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9788 MapGadget(game_gadget[i]);
9791 void UnmapGameButtons()
9795 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9796 UnmapGadget(game_gadget[i]);
9799 static void HandleGameButtons(struct GadgetInfo *gi)
9801 int id = gi->custom_id;
9803 if (game_status != GAME_MODE_PLAYING)
9808 case GAME_CTRL_ID_STOP:
9809 RequestQuitGame(TRUE);
9812 case GAME_CTRL_ID_PAUSE:
9813 if (options.network)
9815 #if defined(PLATFORM_UNIX)
9817 SendToServer_ContinuePlaying();
9819 SendToServer_PausePlaying();
9823 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9826 case GAME_CTRL_ID_PLAY:
9829 #if defined(PLATFORM_UNIX)
9830 if (options.network)
9831 SendToServer_ContinuePlaying();
9835 tape.pausing = FALSE;
9836 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9841 case SOUND_CTRL_ID_MUSIC:
9842 if (setup.sound_music)
9844 setup.sound_music = FALSE;
9847 else if (audio.music_available)
9849 setup.sound = setup.sound_music = TRUE;
9851 SetAudioMode(setup.sound);
9857 case SOUND_CTRL_ID_LOOPS:
9858 if (setup.sound_loops)
9859 setup.sound_loops = FALSE;
9860 else if (audio.loops_available)
9862 setup.sound = setup.sound_loops = TRUE;
9863 SetAudioMode(setup.sound);
9867 case SOUND_CTRL_ID_SIMPLE:
9868 if (setup.sound_simple)
9869 setup.sound_simple = FALSE;
9870 else if (audio.sound_available)
9872 setup.sound = setup.sound_simple = TRUE;
9873 SetAudioMode(setup.sound);