1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
108 IS_FREE_OR_PLAYER(x, y))))
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
115 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
118 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
121 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
125 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
126 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
127 Feld[x][y] == EL_DIAMOND))
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
131 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
133 #define PACMAN_CAN_ENTER_FIELD(x, y) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
135 IS_AMOEBOID(Feld[x][y])))
137 #define PIG_CAN_ENTER_FIELD(x, y) \
138 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 IS_FOOD_PIG(Feld[x][y])))
141 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 IS_FOOD_PENGUIN(Feld[x][y]) || \
144 Feld[x][y] == EL_EXIT_OPEN || \
145 Feld[x][y] == EL_ACID))
148 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
149 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
151 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
153 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
156 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
157 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
159 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
160 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
162 /* game button identifiers */
163 #define GAME_CTRL_ID_STOP 0
164 #define GAME_CTRL_ID_PAUSE 1
165 #define GAME_CTRL_ID_PLAY 2
166 #define SOUND_CTRL_ID_MUSIC 3
167 #define SOUND_CTRL_ID_LOOPS 4
168 #define SOUND_CTRL_ID_SIMPLE 5
170 #define NUM_GAME_BUTTONS 6
173 /* forward declaration for internal use */
175 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
176 static boolean MovePlayer(struct PlayerInfo *, int, int);
177 static void ScrollPlayer(struct PlayerInfo *, int);
178 static void ScrollScreen(struct PlayerInfo *, int);
180 static void InitBeltMovement(void);
181 static void CloseAllOpenTimegates(void);
182 static void CheckGravityMovement(struct PlayerInfo *);
183 static void KillHeroUnlessProtected(int, int);
185 static void TestIfPlayerTouchesCustomElement(int, int);
186 static void TestIfElementTouchesCustomElement(int, int);
187 static void TestIfElementHitsCustomElement(int, int, int);
189 static void ChangeElement(int, int, int);
190 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
191 static boolean CheckTriggeredElementChange(int, int, int, int);
192 static boolean CheckElementSideChange(int, int, int, int, int, int);
193 static boolean CheckElementChange(int, int, int, int);
195 static void PlayLevelSound(int, int, int);
196 static void PlayLevelSoundNearest(int, int, int);
197 static void PlayLevelSoundAction(int, int, int);
198 static void PlayLevelSoundElementAction(int, int, int, int);
199 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
200 static void PlayLevelSoundActionIfLoop(int, int, int);
201 static void StopLevelSoundActionIfLoop(int, int, int);
202 static void PlayLevelMusic();
204 static void MapGameButtons();
205 static void HandleGameButtons(struct GadgetInfo *);
207 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
210 /* ------------------------------------------------------------------------- */
211 /* definition of elements that automatically change to other elements after */
212 /* a specified time, eventually calling a function when changing */
213 /* ------------------------------------------------------------------------- */
215 /* forward declaration for changer functions */
216 static void InitBuggyBase(int x, int y);
217 static void WarnBuggyBase(int x, int y);
219 static void InitTrap(int x, int y);
220 static void ActivateTrap(int x, int y);
221 static void ChangeActiveTrap(int x, int y);
223 static void InitRobotWheel(int x, int y);
224 static void RunRobotWheel(int x, int y);
225 static void StopRobotWheel(int x, int y);
227 static void InitTimegateWheel(int x, int y);
228 static void RunTimegateWheel(int x, int y);
230 struct ChangingElementInfo
235 void (*pre_change_function)(int x, int y);
236 void (*change_function)(int x, int y);
237 void (*post_change_function)(int x, int y);
240 static struct ChangingElementInfo change_delay_list[] =
291 EL_SWITCHGATE_OPENING,
299 EL_SWITCHGATE_CLOSING,
300 EL_SWITCHGATE_CLOSED,
332 EL_ACID_SPLASH_RIGHT,
341 EL_SP_BUGGY_BASE_ACTIVATING,
348 EL_SP_BUGGY_BASE_ACTIVATING,
349 EL_SP_BUGGY_BASE_ACTIVE,
356 EL_SP_BUGGY_BASE_ACTIVE,
380 EL_ROBOT_WHEEL_ACTIVE,
388 EL_TIMEGATE_SWITCH_ACTIVE,
409 int push_delay_fixed, push_delay_random;
414 { EL_BALLOON, 0, 0 },
416 { EL_SOKOBAN_OBJECT, 2, 0 },
417 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
418 { EL_SATELLITE, 2, 0 },
419 { EL_SP_DISK_YELLOW, 2, 0 },
421 { EL_UNDEFINED, 0, 0 },
429 move_stepsize_list[] =
431 { EL_AMOEBA_DROP, 2 },
432 { EL_AMOEBA_DROPPING, 2 },
433 { EL_QUICKSAND_FILLING, 1 },
434 { EL_QUICKSAND_EMPTYING, 1 },
435 { EL_MAGIC_WALL_FILLING, 2 },
436 { EL_BD_MAGIC_WALL_FILLING, 2 },
437 { EL_MAGIC_WALL_EMPTYING, 2 },
438 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
448 collect_count_list[] =
451 { EL_BD_DIAMOND, 1 },
452 { EL_EMERALD_YELLOW, 1 },
453 { EL_EMERALD_RED, 1 },
454 { EL_EMERALD_PURPLE, 1 },
456 { EL_SP_INFOTRON, 1 },
463 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
465 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
466 CH_EVENT_BIT(CE_DELAY))
467 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
468 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
469 IS_JUST_CHANGING(x, y))
471 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
474 void GetPlayerConfig()
476 if (!audio.sound_available)
477 setup.sound_simple = FALSE;
479 if (!audio.loops_available)
480 setup.sound_loops = FALSE;
482 if (!audio.music_available)
483 setup.sound_music = FALSE;
485 if (!video.fullscreen_available)
486 setup.fullscreen = FALSE;
488 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
490 SetAudioMode(setup.sound);
494 static int getBeltNrFromBeltElement(int element)
496 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
497 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
498 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
501 static int getBeltNrFromBeltActiveElement(int element)
503 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
504 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
505 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
508 static int getBeltNrFromBeltSwitchElement(int element)
510 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
511 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
512 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
515 static int getBeltDirNrFromBeltSwitchElement(int element)
517 static int belt_base_element[4] =
519 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
520 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
521 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
522 EL_CONVEYOR_BELT_4_SWITCH_LEFT
525 int belt_nr = getBeltNrFromBeltSwitchElement(element);
526 int belt_dir_nr = element - belt_base_element[belt_nr];
528 return (belt_dir_nr % 3);
531 static int getBeltDirFromBeltSwitchElement(int element)
533 static int belt_move_dir[3] =
540 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
542 return belt_move_dir[belt_dir_nr];
545 static void InitPlayerField(int x, int y, int element, boolean init_game)
547 if (element == EL_SP_MURPHY)
551 if (stored_player[0].present)
553 Feld[x][y] = EL_SP_MURPHY_CLONE;
559 stored_player[0].use_murphy_graphic = TRUE;
562 Feld[x][y] = EL_PLAYER_1;
568 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
569 int jx = player->jx, jy = player->jy;
571 player->present = TRUE;
573 if (!options.network || player->connected)
575 player->active = TRUE;
577 /* remove potentially duplicate players */
578 if (StorePlayer[jx][jy] == Feld[x][y])
579 StorePlayer[jx][jy] = 0;
581 StorePlayer[x][y] = Feld[x][y];
585 printf("Player %d activated.\n", player->element_nr);
586 printf("[Local player is %d and currently %s.]\n",
587 local_player->element_nr,
588 local_player->active ? "active" : "not active");
592 Feld[x][y] = EL_EMPTY;
593 player->jx = player->last_jx = x;
594 player->jy = player->last_jy = y;
598 static void InitField(int x, int y, boolean init_game)
600 int element = Feld[x][y];
609 InitPlayerField(x, y, element, init_game);
613 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
614 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
615 else if (x > 0 && Feld[x-1][y] == EL_ACID)
616 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
617 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
618 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
619 else if (y > 0 && Feld[x][y-1] == EL_ACID)
620 Feld[x][y] = EL_ACID_POOL_BOTTOM;
621 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
622 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
630 case EL_SPACESHIP_RIGHT:
631 case EL_SPACESHIP_UP:
632 case EL_SPACESHIP_LEFT:
633 case EL_SPACESHIP_DOWN:
635 case EL_BD_BUTTERFLY_RIGHT:
636 case EL_BD_BUTTERFLY_UP:
637 case EL_BD_BUTTERFLY_LEFT:
638 case EL_BD_BUTTERFLY_DOWN:
639 case EL_BD_BUTTERFLY:
640 case EL_BD_FIREFLY_RIGHT:
641 case EL_BD_FIREFLY_UP:
642 case EL_BD_FIREFLY_LEFT:
643 case EL_BD_FIREFLY_DOWN:
645 case EL_PACMAN_RIGHT:
669 if (y == lev_fieldy - 1)
671 Feld[x][y] = EL_AMOEBA_GROWING;
672 Store[x][y] = EL_AMOEBA_WET;
676 case EL_DYNAMITE_ACTIVE:
677 case EL_SP_DISK_RED_ACTIVE:
678 case EL_DYNABOMB_PLAYER_1_ACTIVE:
679 case EL_DYNABOMB_PLAYER_2_ACTIVE:
680 case EL_DYNABOMB_PLAYER_3_ACTIVE:
681 case EL_DYNABOMB_PLAYER_4_ACTIVE:
686 local_player->lights_still_needed++;
689 case EL_SOKOBAN_FIELD_EMPTY:
690 local_player->sokobanfields_still_needed++;
694 local_player->friends_still_needed++;
699 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
704 Feld[x][y] = EL_EMPTY;
709 case EL_EM_KEY_1_FILE:
710 Feld[x][y] = EL_EM_KEY_1;
712 case EL_EM_KEY_2_FILE:
713 Feld[x][y] = EL_EM_KEY_2;
715 case EL_EM_KEY_3_FILE:
716 Feld[x][y] = EL_EM_KEY_3;
718 case EL_EM_KEY_4_FILE:
719 Feld[x][y] = EL_EM_KEY_4;
723 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
724 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
725 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
726 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
727 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
728 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
729 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
730 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
731 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
732 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
733 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
734 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
737 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
738 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
739 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
741 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
743 game.belt_dir[belt_nr] = belt_dir;
744 game.belt_dir_nr[belt_nr] = belt_dir_nr;
746 else /* more than one switch -- set it like the first switch */
748 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
753 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
755 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
758 case EL_LIGHT_SWITCH_ACTIVE:
760 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
764 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
770 void DrawGameDoorValues()
774 for (i = 0; i < MAX_PLAYERS; i++)
775 for (j = 0; j < 4; j++)
776 if (stored_player[i].key[j])
777 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
778 el2edimg(EL_KEY_1 + j));
780 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
781 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
782 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
783 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
784 DrawText(DX + XX_SCORE, DY + YY_SCORE,
785 int2str(local_player->score, 5), FONT_TEXT_2);
786 DrawText(DX + XX_TIME, DY + YY_TIME,
787 int2str(TimeLeft, 3), FONT_TEXT_2);
792 static void resolve_group_element(int group_element, int recursion_depth)
794 static struct ElementGroupInfo *group;
795 struct ElementGroupInfo *actual_group = element_info[group_element].group;
798 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
800 Error(ERR_WARN, "recursion too deep when resolving group element %d",
801 group_element - EL_GROUP_START + 1);
803 /* replace element which caused too deep recursion by question mark */
804 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
809 if (recursion_depth == 0) /* initialization */
811 group = element_info[group_element].group;
812 group->num_elements_resolved = 0;
815 for (i = 0; i < actual_group->num_elements; i++)
817 int element = actual_group->element[i];
819 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
822 if (IS_GROUP_ELEMENT(element))
823 resolve_group_element(element, recursion_depth + 1);
825 group->element_resolved[group->num_elements_resolved++] = element;
829 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
831 printf("::: group %d: %d resolved elements\n",
832 group_element - EL_GROUP_START, group->num_elements_resolved);
833 for (i = 0; i < group->num_elements_resolved; i++)
834 printf("::: - %d ['%s']\n", group->element_resolved[i],
835 element_info[group->element_resolved[i]].token_name);
842 static void resolve_group_element(int group_element, int recursion_depth)
844 static short element_list_count[NUM_FILE_ELEMENTS];
845 struct ElementGroupInfo *group = element_info[group_element].group;
851 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
854 if (recursion_depth == 0) /* initialization */
855 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
856 element_list_count[i] = 0;
858 for (i = 0; i < group->num_elements; i++)
860 int element = group->element[i];
862 if (IS_GROUP_ELEMENT(element))
863 resolve_group_element(element, recursion_depth + 1);
864 else if (element < NUM_FILE_ELEMENTS)
865 element_list_count[group->element[i]]++;
868 if (recursion_depth == 0) /* finalization */
870 group->num_elements_resolved = 0;
872 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
873 for (j = 0; j < element_list_count[i]; j++)
874 if (group->num_elements_resolved < NUM_FILE_ELEMENTS)
875 group->element_resolved[group->num_elements_resolved++] = i;
878 if (group_element <= EL_GROUP_8)
880 printf("::: group %d: %d resolved elements\n",
881 group_element - EL_GROUP_START, group->num_elements_resolved);
882 for (i = 0; i < group->num_elements_resolved; i++)
883 printf("::: - %d ['%s']\n", group->element_resolved[i],
884 element_info[group->element_resolved[i]].token_name);
894 =============================================================================
896 -----------------------------------------------------------------------------
897 initialize game engine due to level / tape version number
898 =============================================================================
901 static void InitGameEngine()
905 /* set game engine from tape file when re-playing, else from level file */
906 game.engine_version = (tape.playing ? tape.engine_version :
909 /* dynamically adjust element properties according to game engine version */
910 InitElementPropertiesEngine(game.engine_version);
913 printf("level %d: level version == %06d\n", level_nr, level.game_version);
914 printf(" tape version == %06d [%s] [file: %06d]\n",
915 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
917 printf(" => game.engine_version == %06d\n", game.engine_version);
920 /* ---------- recursively resolve group elements ------------------------- */
922 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
923 resolve_group_element(EL_GROUP_START + i, 0);
925 /* ---------- initialize player's initial move delay --------------------- */
927 /* dynamically adjust player properties according to game engine version */
928 game.initial_move_delay =
929 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
930 INITIAL_MOVE_DELAY_OFF);
932 /* dynamically adjust player properties according to level information */
933 game.initial_move_delay_value =
934 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
936 /* ---------- initialize player's initial push delay --------------------- */
938 /* dynamically adjust player properties according to game engine version */
939 game.initial_push_delay_value =
940 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
942 /* ---------- initialize changing elements ------------------------------- */
944 /* initialize changing elements information */
945 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
947 struct ElementInfo *ei = &element_info[i];
949 /* this pointer might have been changed in the level editor */
950 ei->change = &ei->change_page[0];
952 if (!IS_CUSTOM_ELEMENT(i))
954 ei->change->target_element = EL_EMPTY_SPACE;
955 ei->change->delay_fixed = 0;
956 ei->change->delay_random = 0;
957 ei->change->delay_frames = 1;
960 ei->change_events = CE_BITMASK_DEFAULT;
961 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
963 ei->event_page_nr[j] = 0;
964 ei->event_page[j] = &ei->change_page[0];
968 /* add changing elements from pre-defined list */
969 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
971 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
972 struct ElementInfo *ei = &element_info[ch_delay->element];
974 ei->change->target_element = ch_delay->target_element;
975 ei->change->delay_fixed = ch_delay->change_delay;
977 ei->change->pre_change_function = ch_delay->pre_change_function;
978 ei->change->change_function = ch_delay->change_function;
979 ei->change->post_change_function = ch_delay->post_change_function;
981 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
985 /* add change events from custom element configuration */
986 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
988 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
990 for (j = 0; j < ei->num_change_pages; j++)
992 if (!ei->change_page[j].can_change)
995 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
997 /* only add event page for the first page found with this event */
998 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
999 !(ei->change_events & CH_EVENT_BIT(k)))
1001 ei->change_events |= CH_EVENT_BIT(k);
1002 ei->event_page_nr[k] = j;
1003 ei->event_page[k] = &ei->change_page[j];
1011 /* add change events from custom element configuration */
1012 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1014 int element = EL_CUSTOM_START + i;
1016 /* only add custom elements that change after fixed/random frame delay */
1017 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1018 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1022 /* ---------- initialize trigger events ---------------------------------- */
1024 /* initialize trigger events information */
1025 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1026 trigger_events[i] = EP_BITMASK_DEFAULT;
1029 /* add trigger events from element change event properties */
1030 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1032 struct ElementInfo *ei = &element_info[i];
1034 for (j = 0; j < ei->num_change_pages; j++)
1036 if (!ei->change_page[j].can_change)
1039 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1041 int trigger_element = ei->change_page[j].trigger_element;
1043 trigger_events[trigger_element] |= ei->change_page[j].events;
1048 /* add trigger events from element change event properties */
1049 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1050 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1051 trigger_events[element_info[i].change->trigger_element] |=
1052 element_info[i].change->events;
1055 /* ---------- initialize push delay -------------------------------------- */
1057 /* initialize push delay values to default */
1058 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1060 if (!IS_CUSTOM_ELEMENT(i))
1062 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1063 element_info[i].push_delay_random = game.default_push_delay_random;
1067 /* set push delay value for certain elements from pre-defined list */
1068 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1070 int e = push_delay_list[i].element;
1072 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1073 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1076 /* ---------- initialize move stepsize ----------------------------------- */
1078 /* initialize move stepsize values to default */
1079 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1080 if (!IS_CUSTOM_ELEMENT(i))
1081 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1083 /* set move stepsize value for certain elements from pre-defined list */
1084 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1086 int e = move_stepsize_list[i].element;
1088 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1091 /* ---------- initialize gem count --------------------------------------- */
1093 /* initialize gem count values for each element */
1094 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1095 if (!IS_CUSTOM_ELEMENT(i))
1096 element_info[i].collect_count = 0;
1098 /* add gem count values for all elements from pre-defined list */
1099 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1100 element_info[collect_count_list[i].element].collect_count =
1101 collect_count_list[i].count;
1106 =============================================================================
1108 -----------------------------------------------------------------------------
1109 initialize and start new game
1110 =============================================================================
1115 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1116 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1117 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1124 #if USE_NEW_AMOEBA_CODE
1125 printf("Using new amoeba code.\n");
1127 printf("Using old amoeba code.\n");
1132 /* don't play tapes over network */
1133 network_playing = (options.network && !tape.playing);
1135 for (i = 0; i < MAX_PLAYERS; i++)
1137 struct PlayerInfo *player = &stored_player[i];
1139 player->index_nr = i;
1140 player->element_nr = EL_PLAYER_1 + i;
1142 player->present = FALSE;
1143 player->active = FALSE;
1146 player->effective_action = 0;
1147 player->programmed_action = 0;
1150 player->gems_still_needed = level.gems_needed;
1151 player->sokobanfields_still_needed = 0;
1152 player->lights_still_needed = 0;
1153 player->friends_still_needed = 0;
1155 for (j = 0; j < 4; j++)
1156 player->key[j] = FALSE;
1158 player->dynabomb_count = 0;
1159 player->dynabomb_size = 1;
1160 player->dynabombs_left = 0;
1161 player->dynabomb_xl = FALSE;
1163 player->MovDir = MV_NO_MOVING;
1166 player->GfxDir = MV_NO_MOVING;
1167 player->GfxAction = ACTION_DEFAULT;
1169 player->StepFrame = 0;
1171 player->use_murphy_graphic = FALSE;
1173 player->actual_frame_counter = 0;
1175 player->step_counter = 0;
1177 player->last_move_dir = MV_NO_MOVING;
1179 player->is_waiting = FALSE;
1180 player->is_moving = FALSE;
1181 player->is_digging = FALSE;
1182 player->is_snapping = FALSE;
1183 player->is_collecting = FALSE;
1184 player->is_pushing = FALSE;
1185 player->is_switching = FALSE;
1186 player->is_dropping = FALSE;
1188 player->is_bored = FALSE;
1189 player->is_sleeping = FALSE;
1191 player->frame_counter_bored = -1;
1192 player->frame_counter_sleeping = -1;
1194 player->anim_delay_counter = 0;
1195 player->post_delay_counter = 0;
1197 player->action_waiting = ACTION_DEFAULT;
1198 player->last_action_waiting = ACTION_DEFAULT;
1199 player->special_action_bored = ACTION_DEFAULT;
1200 player->special_action_sleeping = ACTION_DEFAULT;
1202 player->num_special_action_bored = 0;
1203 player->num_special_action_sleeping = 0;
1205 /* determine number of special actions for bored and sleeping animation */
1206 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1208 boolean found = FALSE;
1210 for (k = 0; k < NUM_DIRECTIONS; k++)
1211 if (el_act_dir2img(player->element_nr, j, k) !=
1212 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1216 player->num_special_action_bored++;
1220 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1222 boolean found = FALSE;
1224 for (k = 0; k < NUM_DIRECTIONS; k++)
1225 if (el_act_dir2img(player->element_nr, j, k) !=
1226 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1230 player->num_special_action_sleeping++;
1235 player->switch_x = -1;
1236 player->switch_y = -1;
1238 player->show_envelope = 0;
1240 player->move_delay = game.initial_move_delay;
1241 player->move_delay_value = game.initial_move_delay_value;
1243 player->move_delay_reset_counter = 0;
1245 player->push_delay = 0;
1246 player->push_delay_value = game.initial_push_delay_value;
1248 player->drop_delay = 0;
1250 player->last_jx = player->last_jy = 0;
1251 player->jx = player->jy = 0;
1253 player->shield_normal_time_left = 0;
1254 player->shield_deadly_time_left = 0;
1256 player->inventory_size = 0;
1258 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1259 SnapField(player, 0, 0);
1261 player->LevelSolved = FALSE;
1262 player->GameOver = FALSE;
1265 network_player_action_received = FALSE;
1267 #if defined(PLATFORM_UNIX)
1268 /* initial null action */
1269 if (network_playing)
1270 SendToServer_MovePlayer(MV_NO_MOVING);
1278 TimeLeft = level.time;
1280 ScreenMovDir = MV_NO_MOVING;
1284 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1286 AllPlayersGone = FALSE;
1288 game.yamyam_content_nr = 0;
1289 game.magic_wall_active = FALSE;
1290 game.magic_wall_time_left = 0;
1291 game.light_time_left = 0;
1292 game.timegate_time_left = 0;
1293 game.switchgate_pos = 0;
1294 game.balloon_dir = MV_NO_MOVING;
1295 game.gravity = level.initial_gravity;
1296 game.explosions_delayed = TRUE;
1298 game.envelope_active = FALSE;
1300 for (i = 0; i < 4; i++)
1302 game.belt_dir[i] = MV_NO_MOVING;
1303 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1306 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1307 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1309 for (x = 0; x < lev_fieldx; x++)
1311 for (y = 0; y < lev_fieldy; y++)
1313 Feld[x][y] = level.field[x][y];
1314 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1315 ChangeDelay[x][y] = 0;
1316 ChangePage[x][y] = -1;
1317 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1319 WasJustMoving[x][y] = 0;
1320 WasJustFalling[x][y] = 0;
1322 Pushed[x][y] = FALSE;
1324 Changed[x][y] = CE_BITMASK_DEFAULT;
1325 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1327 ExplodePhase[x][y] = 0;
1328 ExplodeField[x][y] = EX_NO_EXPLOSION;
1330 RunnerVisit[x][y] = 0;
1331 PlayerVisit[x][y] = 0;
1334 GfxRandom[x][y] = INIT_GFX_RANDOM();
1335 GfxElement[x][y] = EL_UNDEFINED;
1336 GfxAction[x][y] = ACTION_DEFAULT;
1337 GfxDir[x][y] = MV_NO_MOVING;
1341 for (y = 0; y < lev_fieldy; y++)
1343 for (x = 0; x < lev_fieldx; x++)
1345 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1347 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1349 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1352 InitField(x, y, TRUE);
1358 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1359 emulate_sb ? EMU_SOKOBAN :
1360 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1362 /* correct non-moving belts to start moving left */
1363 for (i = 0; i < 4; i++)
1364 if (game.belt_dir[i] == MV_NO_MOVING)
1365 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1367 /* check if any connected player was not found in playfield */
1368 for (i = 0; i < MAX_PLAYERS; i++)
1370 struct PlayerInfo *player = &stored_player[i];
1372 if (player->connected && !player->present)
1374 for (j = 0; j < MAX_PLAYERS; j++)
1376 struct PlayerInfo *some_player = &stored_player[j];
1377 int jx = some_player->jx, jy = some_player->jy;
1379 /* assign first free player found that is present in the playfield */
1380 if (some_player->present && !some_player->connected)
1382 player->present = TRUE;
1383 player->active = TRUE;
1384 some_player->present = FALSE;
1386 StorePlayer[jx][jy] = player->element_nr;
1387 player->jx = player->last_jx = jx;
1388 player->jy = player->last_jy = jy;
1398 /* when playing a tape, eliminate all players who do not participate */
1400 for (i = 0; i < MAX_PLAYERS; i++)
1402 if (stored_player[i].active && !tape.player_participates[i])
1404 struct PlayerInfo *player = &stored_player[i];
1405 int jx = player->jx, jy = player->jy;
1407 player->active = FALSE;
1408 StorePlayer[jx][jy] = 0;
1409 Feld[jx][jy] = EL_EMPTY;
1413 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1415 /* when in single player mode, eliminate all but the first active player */
1417 for (i = 0; i < MAX_PLAYERS; i++)
1419 if (stored_player[i].active)
1421 for (j = i + 1; j < MAX_PLAYERS; j++)
1423 if (stored_player[j].active)
1425 struct PlayerInfo *player = &stored_player[j];
1426 int jx = player->jx, jy = player->jy;
1428 player->active = FALSE;
1429 StorePlayer[jx][jy] = 0;
1430 Feld[jx][jy] = EL_EMPTY;
1437 /* when recording the game, store which players take part in the game */
1440 for (i = 0; i < MAX_PLAYERS; i++)
1441 if (stored_player[i].active)
1442 tape.player_participates[i] = TRUE;
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 struct PlayerInfo *player = &stored_player[i];
1451 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1456 if (local_player == player)
1457 printf("Player %d is local player.\n", i+1);
1461 if (BorderElement == EL_EMPTY)
1464 SBX_Right = lev_fieldx - SCR_FIELDX;
1466 SBY_Lower = lev_fieldy - SCR_FIELDY;
1471 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1473 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1476 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1477 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1479 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1480 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1482 /* if local player not found, look for custom element that might create
1483 the player (make some assumptions about the right custom element) */
1484 if (!local_player->present)
1486 int start_x = 0, start_y = 0;
1487 int found_rating = 0;
1488 int found_element = EL_UNDEFINED;
1490 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1492 int element = Feld[x][y];
1497 if (!IS_CUSTOM_ELEMENT(element))
1500 if (CAN_CHANGE(element))
1502 for (i = 0; i < element_info[element].num_change_pages; i++)
1504 content = element_info[element].change_page[i].target_element;
1505 is_player = ELEM_IS_PLAYER(content);
1507 if (is_player && (found_rating < 3 || element < found_element))
1513 found_element = element;
1518 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1520 content = element_info[element].content[xx][yy];
1521 is_player = ELEM_IS_PLAYER(content);
1523 if (is_player && (found_rating < 2 || element < found_element))
1525 start_x = x + xx - 1;
1526 start_y = y + yy - 1;
1529 found_element = element;
1532 if (!CAN_CHANGE(element))
1535 for (i = 0; i < element_info[element].num_change_pages; i++)
1537 content = element_info[element].change_page[i].content[xx][yy];
1538 is_player = ELEM_IS_PLAYER(content);
1540 if (is_player && (found_rating < 1 || element < found_element))
1542 start_x = x + xx - 1;
1543 start_y = y + yy - 1;
1546 found_element = element;
1552 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1553 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1556 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1557 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1563 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1564 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1565 local_player->jx - MIDPOSX);
1567 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1568 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1569 local_player->jy - MIDPOSY);
1571 scroll_x = SBX_Left;
1572 scroll_y = SBY_Upper;
1573 if (local_player->jx >= SBX_Left + MIDPOSX)
1574 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1575 local_player->jx - MIDPOSX :
1577 if (local_player->jy >= SBY_Upper + MIDPOSY)
1578 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1579 local_player->jy - MIDPOSY :
1584 CloseDoor(DOOR_CLOSE_1);
1589 /* after drawing the level, correct some elements */
1590 if (game.timegate_time_left == 0)
1591 CloseAllOpenTimegates();
1593 if (setup.soft_scrolling)
1594 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1596 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1599 /* copy default game door content to main double buffer */
1600 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1601 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1604 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1607 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1608 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1609 BlitBitmap(drawto, drawto,
1610 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1611 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1612 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1613 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1616 DrawGameDoorValues();
1620 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1621 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1622 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1626 /* copy actual game door content to door double buffer for OpenDoor() */
1627 BlitBitmap(drawto, bitmap_db_door,
1628 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1630 OpenDoor(DOOR_OPEN_ALL);
1632 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1634 if (setup.sound_music)
1637 KeyboardAutoRepeatOffUnlessAutoplay();
1641 for (i = 0; i < 4; i++)
1642 printf("Player %d %sactive.\n",
1643 i + 1, (stored_player[i].active ? "" : "not "));
1647 printf("::: starting game [%d]\n", FrameCounter);
1651 void InitMovDir(int x, int y)
1653 int i, element = Feld[x][y];
1654 static int xy[4][2] =
1661 static int direction[3][4] =
1663 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1664 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1665 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1674 Feld[x][y] = EL_BUG;
1675 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1678 case EL_SPACESHIP_RIGHT:
1679 case EL_SPACESHIP_UP:
1680 case EL_SPACESHIP_LEFT:
1681 case EL_SPACESHIP_DOWN:
1682 Feld[x][y] = EL_SPACESHIP;
1683 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1686 case EL_BD_BUTTERFLY_RIGHT:
1687 case EL_BD_BUTTERFLY_UP:
1688 case EL_BD_BUTTERFLY_LEFT:
1689 case EL_BD_BUTTERFLY_DOWN:
1690 Feld[x][y] = EL_BD_BUTTERFLY;
1691 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1694 case EL_BD_FIREFLY_RIGHT:
1695 case EL_BD_FIREFLY_UP:
1696 case EL_BD_FIREFLY_LEFT:
1697 case EL_BD_FIREFLY_DOWN:
1698 Feld[x][y] = EL_BD_FIREFLY;
1699 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1702 case EL_PACMAN_RIGHT:
1704 case EL_PACMAN_LEFT:
1705 case EL_PACMAN_DOWN:
1706 Feld[x][y] = EL_PACMAN;
1707 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1710 case EL_SP_SNIKSNAK:
1711 MovDir[x][y] = MV_UP;
1714 case EL_SP_ELECTRON:
1715 MovDir[x][y] = MV_LEFT;
1722 Feld[x][y] = EL_MOLE;
1723 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1727 if (IS_CUSTOM_ELEMENT(element))
1729 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1730 MovDir[x][y] = element_info[element].move_direction_initial;
1731 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1732 element_info[element].move_pattern == MV_TURNING_LEFT ||
1733 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1734 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1735 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1736 element_info[element].move_pattern == MV_TURNING_RANDOM)
1737 MovDir[x][y] = 1 << RND(4);
1738 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1739 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1740 else if (element_info[element].move_pattern == MV_VERTICAL)
1741 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1742 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1743 MovDir[x][y] = element_info[element].move_pattern;
1744 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1745 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1747 for (i = 0; i < 4; i++)
1749 int x1 = x + xy[i][0];
1750 int y1 = y + xy[i][1];
1752 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1754 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1755 MovDir[x][y] = direction[0][i];
1757 MovDir[x][y] = direction[1][i];
1766 MovDir[x][y] = 1 << RND(4);
1768 if (element != EL_BUG &&
1769 element != EL_SPACESHIP &&
1770 element != EL_BD_BUTTERFLY &&
1771 element != EL_BD_FIREFLY)
1774 for (i = 0; i < 4; i++)
1776 int x1 = x + xy[i][0];
1777 int y1 = y + xy[i][1];
1779 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1781 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1783 MovDir[x][y] = direction[0][i];
1786 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1787 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1789 MovDir[x][y] = direction[1][i];
1798 GfxDir[x][y] = MovDir[x][y];
1801 void InitAmoebaNr(int x, int y)
1804 int group_nr = AmoebeNachbarNr(x, y);
1808 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1810 if (AmoebaCnt[i] == 0)
1818 AmoebaNr[x][y] = group_nr;
1819 AmoebaCnt[group_nr]++;
1820 AmoebaCnt2[group_nr]++;
1826 boolean raise_level = FALSE;
1828 if (local_player->MovPos)
1832 if (tape.auto_play) /* tape might already be stopped here */
1833 tape.auto_play_level_solved = TRUE;
1835 if (tape.playing && tape.auto_play)
1836 tape.auto_play_level_solved = TRUE;
1839 local_player->LevelSolved = FALSE;
1841 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1845 if (!tape.playing && setup.sound_loops)
1846 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1847 SND_CTRL_PLAY_LOOP);
1849 while (TimeLeft > 0)
1851 if (!tape.playing && !setup.sound_loops)
1852 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1853 if (TimeLeft > 0 && !(TimeLeft % 10))
1854 RaiseScore(level.score[SC_TIME_BONUS]);
1855 if (TimeLeft > 100 && !(TimeLeft % 10))
1859 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1866 if (!tape.playing && setup.sound_loops)
1867 StopSound(SND_GAME_LEVELTIME_BONUS);
1869 else if (level.time == 0) /* level without time limit */
1871 if (!tape.playing && setup.sound_loops)
1872 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1873 SND_CTRL_PLAY_LOOP);
1875 while (TimePlayed < 999)
1877 if (!tape.playing && !setup.sound_loops)
1878 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1879 if (TimePlayed < 999 && !(TimePlayed % 10))
1880 RaiseScore(level.score[SC_TIME_BONUS]);
1881 if (TimePlayed < 900 && !(TimePlayed % 10))
1885 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1892 if (!tape.playing && setup.sound_loops)
1893 StopSound(SND_GAME_LEVELTIME_BONUS);
1896 /* close exit door after last player */
1897 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1898 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1900 int element = Feld[ExitX][ExitY];
1902 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1903 EL_SP_EXIT_CLOSING);
1905 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1908 /* Hero disappears */
1909 DrawLevelField(ExitX, ExitY);
1915 CloseDoor(DOOR_CLOSE_1);
1920 SaveTape(tape.level_nr); /* Ask to save tape */
1923 if (level_nr == leveldir_current->handicap_level)
1925 leveldir_current->handicap_level++;
1926 SaveLevelSetup_SeriesInfo();
1929 if (level_editor_test_game)
1930 local_player->score = -1; /* no highscore when playing from editor */
1931 else if (level_nr < leveldir_current->last_level)
1932 raise_level = TRUE; /* advance to next level */
1934 if ((hi_pos = NewHiScore()) >= 0)
1936 game_status = GAME_MODE_SCORES;
1937 DrawHallOfFame(hi_pos);
1946 game_status = GAME_MODE_MAIN;
1963 LoadScore(level_nr);
1965 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1966 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1969 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1971 if (local_player->score > highscore[k].Score)
1973 /* player has made it to the hall of fame */
1975 if (k < MAX_SCORE_ENTRIES - 1)
1977 int m = MAX_SCORE_ENTRIES - 1;
1980 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1981 if (!strcmp(setup.player_name, highscore[l].Name))
1983 if (m == k) /* player's new highscore overwrites his old one */
1987 for (l = m; l > k; l--)
1989 strcpy(highscore[l].Name, highscore[l - 1].Name);
1990 highscore[l].Score = highscore[l - 1].Score;
1997 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1998 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1999 highscore[k].Score = local_player->score;
2005 else if (!strncmp(setup.player_name, highscore[k].Name,
2006 MAX_PLAYER_NAME_LEN))
2007 break; /* player already there with a higher score */
2013 SaveScore(level_nr);
2018 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2020 if (player->GfxAction != action || player->GfxDir != dir)
2023 printf("Player frame reset! (%d => %d, %d => %d)\n",
2024 player->GfxAction, action, player->GfxDir, dir);
2027 player->GfxAction = action;
2028 player->GfxDir = dir;
2030 player->StepFrame = 0;
2034 static void ResetRandomAnimationValue(int x, int y)
2036 GfxRandom[x][y] = INIT_GFX_RANDOM();
2039 static void ResetGfxAnimation(int x, int y)
2042 GfxAction[x][y] = ACTION_DEFAULT;
2043 GfxDir[x][y] = MovDir[x][y];
2046 void InitMovingField(int x, int y, int direction)
2048 int element = Feld[x][y];
2049 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2050 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2054 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2055 ResetGfxAnimation(x, y);
2057 MovDir[newx][newy] = MovDir[x][y] = direction;
2058 GfxDir[x][y] = direction;
2060 if (Feld[newx][newy] == EL_EMPTY)
2061 Feld[newx][newy] = EL_BLOCKED;
2063 if (direction == MV_DOWN && CAN_FALL(element))
2064 GfxAction[x][y] = ACTION_FALLING;
2066 GfxAction[x][y] = ACTION_MOVING;
2068 GfxFrame[newx][newy] = GfxFrame[x][y];
2069 GfxRandom[newx][newy] = GfxRandom[x][y];
2070 GfxAction[newx][newy] = GfxAction[x][y];
2071 GfxDir[newx][newy] = GfxDir[x][y];
2074 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2076 int direction = MovDir[x][y];
2077 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2078 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2084 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2086 int oldx = x, oldy = y;
2087 int direction = MovDir[x][y];
2089 if (direction == MV_LEFT)
2091 else if (direction == MV_RIGHT)
2093 else if (direction == MV_UP)
2095 else if (direction == MV_DOWN)
2098 *comes_from_x = oldx;
2099 *comes_from_y = oldy;
2102 int MovingOrBlocked2Element(int x, int y)
2104 int element = Feld[x][y];
2106 if (element == EL_BLOCKED)
2110 Blocked2Moving(x, y, &oldx, &oldy);
2111 return Feld[oldx][oldy];
2117 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2119 /* like MovingOrBlocked2Element(), but if element is moving
2120 and (x,y) is the field the moving element is just leaving,
2121 return EL_BLOCKED instead of the element value */
2122 int element = Feld[x][y];
2124 if (IS_MOVING(x, y))
2126 if (element == EL_BLOCKED)
2130 Blocked2Moving(x, y, &oldx, &oldy);
2131 return Feld[oldx][oldy];
2140 static void RemoveField(int x, int y)
2142 Feld[x][y] = EL_EMPTY;
2149 ChangeDelay[x][y] = 0;
2150 ChangePage[x][y] = -1;
2151 Pushed[x][y] = FALSE;
2153 GfxElement[x][y] = EL_UNDEFINED;
2154 GfxAction[x][y] = ACTION_DEFAULT;
2155 GfxDir[x][y] = MV_NO_MOVING;
2158 void RemoveMovingField(int x, int y)
2160 int oldx = x, oldy = y, newx = x, newy = y;
2161 int element = Feld[x][y];
2162 int next_element = EL_UNDEFINED;
2164 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2167 if (IS_MOVING(x, y))
2169 Moving2Blocked(x, y, &newx, &newy);
2170 if (Feld[newx][newy] != EL_BLOCKED)
2173 else if (element == EL_BLOCKED)
2175 Blocked2Moving(x, y, &oldx, &oldy);
2176 if (!IS_MOVING(oldx, oldy))
2180 if (element == EL_BLOCKED &&
2181 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2182 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2183 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2184 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2185 next_element = get_next_element(Feld[oldx][oldy]);
2187 RemoveField(oldx, oldy);
2188 RemoveField(newx, newy);
2190 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2192 if (next_element != EL_UNDEFINED)
2193 Feld[oldx][oldy] = next_element;
2195 DrawLevelField(oldx, oldy);
2196 DrawLevelField(newx, newy);
2199 void DrawDynamite(int x, int y)
2201 int sx = SCREENX(x), sy = SCREENY(y);
2202 int graphic = el2img(Feld[x][y]);
2205 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2208 if (IS_WALKABLE_INSIDE(Back[x][y]))
2212 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2213 else if (Store[x][y])
2214 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2216 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2219 if (Back[x][y] || Store[x][y])
2220 DrawGraphicThruMask(sx, sy, graphic, frame);
2222 DrawGraphic(sx, sy, graphic, frame);
2224 if (game.emulation == EMU_SUPAPLEX)
2225 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2226 else if (Store[x][y])
2227 DrawGraphicThruMask(sx, sy, graphic, frame);
2229 DrawGraphic(sx, sy, graphic, frame);
2233 void CheckDynamite(int x, int y)
2235 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2239 if (MovDelay[x][y] != 0)
2242 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2249 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2251 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2252 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2253 StopSound(SND_DYNAMITE_ACTIVE);
2255 StopSound(SND_DYNABOMB_ACTIVE);
2261 void RelocatePlayer(int x, int y, int element)
2263 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2265 if (player->GameOver) /* do not reanimate dead player */
2269 RemoveField(x, y); /* temporarily remove newly placed player */
2270 DrawLevelField(x, y);
2273 if (player->present)
2275 while (player->MovPos)
2277 ScrollPlayer(player, SCROLL_GO_ON);
2278 ScrollScreen(NULL, SCROLL_GO_ON);
2284 Delay(GAME_FRAME_DELAY);
2287 DrawPlayer(player); /* needed here only to cleanup last field */
2288 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2290 player->is_moving = FALSE;
2293 Feld[x][y] = element;
2294 InitPlayerField(x, y, element, TRUE);
2296 if (player == local_player)
2298 int scroll_xx = -999, scroll_yy = -999;
2300 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2303 int fx = FX, fy = FY;
2305 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2306 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2307 local_player->jx - MIDPOSX);
2309 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2310 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2311 local_player->jy - MIDPOSY);
2313 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2314 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2319 fx += dx * TILEX / 2;
2320 fy += dy * TILEY / 2;
2322 ScrollLevel(dx, dy);
2325 /* scroll in two steps of half tile size to make things smoother */
2326 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2328 Delay(GAME_FRAME_DELAY);
2330 /* scroll second step to align at full tile size */
2332 Delay(GAME_FRAME_DELAY);
2337 void Explode(int ex, int ey, int phase, int mode)
2341 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2342 int last_phase = num_phase * delay;
2343 int half_phase = (num_phase / 2) * delay;
2344 int first_phase_after_start = EX_PHASE_START + 1;
2346 if (game.explosions_delayed)
2348 ExplodeField[ex][ey] = mode;
2352 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2354 int center_element = Feld[ex][ey];
2357 /* --- This is only really needed (and now handled) in "Impact()". --- */
2358 /* do not explode moving elements that left the explode field in time */
2359 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2360 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2364 if (mode == EX_NORMAL || mode == EX_CENTER)
2365 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2367 /* remove things displayed in background while burning dynamite */
2368 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2371 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2373 /* put moving element to center field (and let it explode there) */
2374 center_element = MovingOrBlocked2Element(ex, ey);
2375 RemoveMovingField(ex, ey);
2376 Feld[ex][ey] = center_element;
2379 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2381 int xx = x - ex + 1;
2382 int yy = y - ey + 1;
2385 if (!IN_LEV_FIELD(x, y) ||
2386 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2387 (x != ex || y != ey)))
2390 element = Feld[x][y];
2392 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2394 element = MovingOrBlocked2Element(x, y);
2396 if (!IS_EXPLOSION_PROOF(element))
2397 RemoveMovingField(x, y);
2403 if (IS_EXPLOSION_PROOF(element))
2406 /* indestructible elements can only explode in center (but not flames) */
2407 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2408 element == EL_FLAMES)
2413 if ((IS_INDESTRUCTIBLE(element) &&
2414 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2415 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2416 element == EL_FLAMES)
2420 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2422 if (IS_ACTIVE_BOMB(element))
2424 /* re-activate things under the bomb like gate or penguin */
2425 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2432 /* save walkable background elements while explosion on same tile */
2434 if (IS_INDESTRUCTIBLE(element))
2435 Back[x][y] = element;
2437 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2438 Back[x][y] = element;
2441 /* ignite explodable elements reached by other explosion */
2442 if (element == EL_EXPLOSION)
2443 element = Store2[x][y];
2446 if (AmoebaNr[x][y] &&
2447 (element == EL_AMOEBA_FULL ||
2448 element == EL_BD_AMOEBA ||
2449 element == EL_AMOEBA_GROWING))
2451 AmoebaCnt[AmoebaNr[x][y]]--;
2452 AmoebaCnt2[AmoebaNr[x][y]]--;
2458 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2460 switch(StorePlayer[ex][ey])
2463 Store[x][y] = EL_EMERALD_RED;
2466 Store[x][y] = EL_EMERALD;
2469 Store[x][y] = EL_EMERALD_PURPLE;
2473 Store[x][y] = EL_EMERALD_YELLOW;
2477 if (game.emulation == EMU_SUPAPLEX)
2478 Store[x][y] = EL_EMPTY;
2480 else if (center_element == EL_MOLE)
2481 Store[x][y] = EL_EMERALD_RED;
2482 else if (center_element == EL_PENGUIN)
2483 Store[x][y] = EL_EMERALD_PURPLE;
2484 else if (center_element == EL_BUG)
2485 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2486 else if (center_element == EL_BD_BUTTERFLY)
2487 Store[x][y] = EL_BD_DIAMOND;
2488 else if (center_element == EL_SP_ELECTRON)
2489 Store[x][y] = EL_SP_INFOTRON;
2490 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2491 Store[x][y] = level.amoeba_content;
2492 else if (center_element == EL_YAMYAM)
2493 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2494 else if (IS_CUSTOM_ELEMENT(center_element) &&
2495 element_info[center_element].content[xx][yy] != EL_EMPTY)
2496 Store[x][y] = element_info[center_element].content[xx][yy];
2497 else if (element == EL_WALL_EMERALD)
2498 Store[x][y] = EL_EMERALD;
2499 else if (element == EL_WALL_DIAMOND)
2500 Store[x][y] = EL_DIAMOND;
2501 else if (element == EL_WALL_BD_DIAMOND)
2502 Store[x][y] = EL_BD_DIAMOND;
2503 else if (element == EL_WALL_EMERALD_YELLOW)
2504 Store[x][y] = EL_EMERALD_YELLOW;
2505 else if (element == EL_WALL_EMERALD_RED)
2506 Store[x][y] = EL_EMERALD_RED;
2507 else if (element == EL_WALL_EMERALD_PURPLE)
2508 Store[x][y] = EL_EMERALD_PURPLE;
2509 else if (element == EL_WALL_PEARL)
2510 Store[x][y] = EL_PEARL;
2511 else if (element == EL_WALL_CRYSTAL)
2512 Store[x][y] = EL_CRYSTAL;
2513 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2514 Store[x][y] = element_info[element].content[1][1];
2516 Store[x][y] = EL_EMPTY;
2518 if (x != ex || y != ey ||
2519 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2520 Store2[x][y] = element;
2523 if (AmoebaNr[x][y] &&
2524 (element == EL_AMOEBA_FULL ||
2525 element == EL_BD_AMOEBA ||
2526 element == EL_AMOEBA_GROWING))
2528 AmoebaCnt[AmoebaNr[x][y]]--;
2529 AmoebaCnt2[AmoebaNr[x][y]]--;
2535 MovDir[x][y] = MovPos[x][y] = 0;
2536 GfxDir[x][y] = MovDir[x][y];
2541 Feld[x][y] = EL_EXPLOSION;
2543 GfxElement[x][y] = center_element;
2545 GfxElement[x][y] = EL_UNDEFINED;
2548 ExplodePhase[x][y] = 1;
2552 if (center_element == EL_YAMYAM)
2553 game.yamyam_content_nr =
2554 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2565 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2569 /* activate this even in non-DEBUG version until cause for crash in
2570 getGraphicAnimationFrame() (see below) is found and eliminated */
2574 if (GfxElement[x][y] == EL_UNDEFINED)
2577 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2578 printf("Explode(): This should never happen!\n");
2581 GfxElement[x][y] = EL_EMPTY;
2585 if (phase == first_phase_after_start)
2587 int element = Store2[x][y];
2589 if (element == EL_BLACK_ORB)
2591 Feld[x][y] = Store2[x][y];
2596 else if (phase == half_phase)
2598 int element = Store2[x][y];
2600 if (IS_PLAYER(x, y))
2601 KillHeroUnlessProtected(x, y);
2602 else if (CAN_EXPLODE_BY_FIRE(element))
2604 Feld[x][y] = Store2[x][y];
2608 else if (element == EL_AMOEBA_TO_DIAMOND)
2609 AmoebeUmwandeln(x, y);
2612 if (phase == last_phase)
2616 element = Feld[x][y] = Store[x][y];
2617 Store[x][y] = Store2[x][y] = 0;
2618 GfxElement[x][y] = EL_UNDEFINED;
2620 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2621 element = Feld[x][y] = Back[x][y];
2624 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2625 GfxDir[x][y] = MV_NO_MOVING;
2626 ChangeDelay[x][y] = 0;
2627 ChangePage[x][y] = -1;
2629 InitField(x, y, FALSE);
2630 if (CAN_MOVE(element))
2632 DrawLevelField(x, y);
2634 TestIfElementTouchesCustomElement(x, y);
2636 if (GFX_CRUMBLED(element))
2637 DrawLevelFieldCrumbledSandNeighbours(x, y);
2639 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2640 StorePlayer[x][y] = 0;
2642 if (ELEM_IS_PLAYER(element))
2643 RelocatePlayer(x, y, element);
2645 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2648 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2650 int stored = Store[x][y];
2651 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2652 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2655 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2658 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2659 element_info[GfxElement[x][y]].token_name,
2664 DrawLevelFieldCrumbledSand(x, y);
2666 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2668 DrawLevelElement(x, y, Back[x][y]);
2669 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2671 else if (IS_WALKABLE_UNDER(Back[x][y]))
2673 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2674 DrawLevelElementThruMask(x, y, Back[x][y]);
2676 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2677 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2681 void DynaExplode(int ex, int ey)
2684 int dynabomb_element = Feld[ex][ey];
2685 int dynabomb_size = 1;
2686 boolean dynabomb_xl = FALSE;
2687 struct PlayerInfo *player;
2688 static int xy[4][2] =
2696 if (IS_ACTIVE_BOMB(dynabomb_element))
2698 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2699 dynabomb_size = player->dynabomb_size;
2700 dynabomb_xl = player->dynabomb_xl;
2701 player->dynabombs_left++;
2704 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2706 for (i = 0; i < 4; i++)
2708 for (j = 1; j <= dynabomb_size; j++)
2710 int x = ex + j * xy[i % 4][0];
2711 int y = ey + j * xy[i % 4][1];
2714 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2717 element = Feld[x][y];
2719 /* do not restart explosions of fields with active bombs */
2720 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2723 Explode(x, y, EX_PHASE_START, EX_BORDER);
2725 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2726 if (element != EL_EMPTY &&
2727 element != EL_SAND &&
2728 element != EL_EXPLOSION &&
2735 void Bang(int x, int y)
2738 int element = MovingOrBlocked2Element(x, y);
2740 int element = Feld[x][y];
2744 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2746 if (IS_PLAYER(x, y))
2749 struct PlayerInfo *player = PLAYERINFO(x, y);
2751 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2752 player->element_nr);
2757 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2759 if (game.emulation == EMU_SUPAPLEX)
2760 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2762 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2767 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2775 case EL_BD_BUTTERFLY:
2778 case EL_DARK_YAMYAM:
2782 RaiseScoreElement(element);
2783 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2785 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2786 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2787 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2788 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2789 case EL_DYNABOMB_INCREASE_NUMBER:
2790 case EL_DYNABOMB_INCREASE_SIZE:
2791 case EL_DYNABOMB_INCREASE_POWER:
2796 case EL_LAMP_ACTIVE:
2797 if (IS_PLAYER(x, y))
2798 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2800 Explode(x, y, EX_PHASE_START, EX_CENTER);
2803 if (CAN_EXPLODE_DYNA(element))
2805 else if (CAN_EXPLODE_1X1(element))
2806 Explode(x, y, EX_PHASE_START, EX_CENTER);
2808 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2812 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2815 void SplashAcid(int x, int y)
2817 int element = Feld[x][y];
2819 if (element != EL_ACID_SPLASH_LEFT &&
2820 element != EL_ACID_SPLASH_RIGHT)
2822 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2824 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2825 (!IN_LEV_FIELD(x-1, y-1) ||
2826 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2827 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2829 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2830 (!IN_LEV_FIELD(x+1, y-1) ||
2831 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2832 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2836 static void InitBeltMovement()
2838 static int belt_base_element[4] =
2840 EL_CONVEYOR_BELT_1_LEFT,
2841 EL_CONVEYOR_BELT_2_LEFT,
2842 EL_CONVEYOR_BELT_3_LEFT,
2843 EL_CONVEYOR_BELT_4_LEFT
2845 static int belt_base_active_element[4] =
2847 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2848 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2849 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2850 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2855 /* set frame order for belt animation graphic according to belt direction */
2856 for (i = 0; i < 4; i++)
2860 for (j = 0; j < 3; j++)
2862 int element = belt_base_active_element[belt_nr] + j;
2863 int graphic = el2img(element);
2865 if (game.belt_dir[i] == MV_LEFT)
2866 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2868 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2872 for (y = 0; y < lev_fieldy; y++)
2874 for (x = 0; x < lev_fieldx; x++)
2876 int element = Feld[x][y];
2878 for (i = 0; i < 4; i++)
2880 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2882 int e_belt_nr = getBeltNrFromBeltElement(element);
2885 if (e_belt_nr == belt_nr)
2887 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2889 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2897 static void ToggleBeltSwitch(int x, int y)
2899 static int belt_base_element[4] =
2901 EL_CONVEYOR_BELT_1_LEFT,
2902 EL_CONVEYOR_BELT_2_LEFT,
2903 EL_CONVEYOR_BELT_3_LEFT,
2904 EL_CONVEYOR_BELT_4_LEFT
2906 static int belt_base_active_element[4] =
2908 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2909 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2910 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2911 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2913 static int belt_base_switch_element[4] =
2915 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2916 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2917 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2918 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2920 static int belt_move_dir[4] =
2928 int element = Feld[x][y];
2929 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2930 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2931 int belt_dir = belt_move_dir[belt_dir_nr];
2934 if (!IS_BELT_SWITCH(element))
2937 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2938 game.belt_dir[belt_nr] = belt_dir;
2940 if (belt_dir_nr == 3)
2943 /* set frame order for belt animation graphic according to belt direction */
2944 for (i = 0; i < 3; i++)
2946 int element = belt_base_active_element[belt_nr] + i;
2947 int graphic = el2img(element);
2949 if (belt_dir == MV_LEFT)
2950 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2952 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2955 for (yy = 0; yy < lev_fieldy; yy++)
2957 for (xx = 0; xx < lev_fieldx; xx++)
2959 int element = Feld[xx][yy];
2961 if (IS_BELT_SWITCH(element))
2963 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2965 if (e_belt_nr == belt_nr)
2967 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2968 DrawLevelField(xx, yy);
2971 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2973 int e_belt_nr = getBeltNrFromBeltElement(element);
2975 if (e_belt_nr == belt_nr)
2977 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2979 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2980 DrawLevelField(xx, yy);
2983 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2985 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2987 if (e_belt_nr == belt_nr)
2989 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2991 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2992 DrawLevelField(xx, yy);
2999 static void ToggleSwitchgateSwitch(int x, int y)
3003 game.switchgate_pos = !game.switchgate_pos;
3005 for (yy = 0; yy < lev_fieldy; yy++)
3007 for (xx = 0; xx < lev_fieldx; xx++)
3009 int element = Feld[xx][yy];
3011 if (element == EL_SWITCHGATE_SWITCH_UP ||
3012 element == EL_SWITCHGATE_SWITCH_DOWN)
3014 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3015 DrawLevelField(xx, yy);
3017 else if (element == EL_SWITCHGATE_OPEN ||
3018 element == EL_SWITCHGATE_OPENING)
3020 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3022 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3024 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3027 else if (element == EL_SWITCHGATE_CLOSED ||
3028 element == EL_SWITCHGATE_CLOSING)
3030 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3032 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3034 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3041 static int getInvisibleActiveFromInvisibleElement(int element)
3043 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3044 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3045 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3049 static int getInvisibleFromInvisibleActiveElement(int element)
3051 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3052 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3053 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3057 static void RedrawAllLightSwitchesAndInvisibleElements()
3061 for (y = 0; y < lev_fieldy; y++)
3063 for (x = 0; x < lev_fieldx; x++)
3065 int element = Feld[x][y];
3067 if (element == EL_LIGHT_SWITCH &&
3068 game.light_time_left > 0)
3070 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3071 DrawLevelField(x, y);
3073 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3074 game.light_time_left == 0)
3076 Feld[x][y] = EL_LIGHT_SWITCH;
3077 DrawLevelField(x, y);
3079 else if (element == EL_INVISIBLE_STEELWALL ||
3080 element == EL_INVISIBLE_WALL ||
3081 element == EL_INVISIBLE_SAND)
3083 if (game.light_time_left > 0)
3084 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3086 DrawLevelField(x, y);
3088 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3089 element == EL_INVISIBLE_WALL_ACTIVE ||
3090 element == EL_INVISIBLE_SAND_ACTIVE)
3092 if (game.light_time_left == 0)
3093 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3095 DrawLevelField(x, y);
3101 static void ToggleLightSwitch(int x, int y)
3103 int element = Feld[x][y];
3105 game.light_time_left =
3106 (element == EL_LIGHT_SWITCH ?
3107 level.time_light * FRAMES_PER_SECOND : 0);
3109 RedrawAllLightSwitchesAndInvisibleElements();
3112 static void ActivateTimegateSwitch(int x, int y)
3116 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3118 for (yy = 0; yy < lev_fieldy; yy++)
3120 for (xx = 0; xx < lev_fieldx; xx++)
3122 int element = Feld[xx][yy];
3124 if (element == EL_TIMEGATE_CLOSED ||
3125 element == EL_TIMEGATE_CLOSING)
3127 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3128 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3132 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3134 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3135 DrawLevelField(xx, yy);
3142 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3145 inline static int getElementMoveStepsize(int x, int y)
3147 int element = Feld[x][y];
3148 int direction = MovDir[x][y];
3149 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3150 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3151 int horiz_move = (dx != 0);
3152 int sign = (horiz_move ? dx : dy);
3153 int step = sign * element_info[element].move_stepsize;
3155 /* special values for move stepsize for spring and things on conveyor belt */
3158 if (CAN_FALL(element) &&
3159 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3160 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3161 else if (element == EL_SPRING)
3162 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3168 void Impact(int x, int y)
3170 boolean lastline = (y == lev_fieldy-1);
3171 boolean object_hit = FALSE;
3172 boolean impact = (lastline || object_hit);
3173 int element = Feld[x][y];
3174 int smashed = EL_UNDEFINED;
3176 if (!lastline) /* check if element below was hit */
3178 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3181 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3182 MovDir[x][y + 1] != MV_DOWN ||
3183 MovPos[x][y + 1] <= TILEY / 2));
3186 object_hit = !IS_FREE(x, y + 1);
3189 /* do not smash moving elements that left the smashed field in time */
3190 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3191 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3195 smashed = MovingOrBlocked2Element(x, y + 1);
3197 impact = (lastline || object_hit);
3200 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3206 /* only reset graphic animation if graphic really changes after impact */
3208 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3210 ResetGfxAnimation(x, y);
3211 DrawLevelField(x, y);
3214 if (impact && CAN_EXPLODE_IMPACT(element))
3219 else if (impact && element == EL_PEARL)
3221 Feld[x][y] = EL_PEARL_BREAKING;
3222 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3225 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3227 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3232 if (impact && element == EL_AMOEBA_DROP)
3234 if (object_hit && IS_PLAYER(x, y + 1))
3235 KillHeroUnlessProtected(x, y + 1);
3236 else if (object_hit && smashed == EL_PENGUIN)
3240 Feld[x][y] = EL_AMOEBA_GROWING;
3241 Store[x][y] = EL_AMOEBA_WET;
3243 ResetRandomAnimationValue(x, y);
3248 if (object_hit) /* check which object was hit */
3250 if (CAN_PASS_MAGIC_WALL(element) &&
3251 (smashed == EL_MAGIC_WALL ||
3252 smashed == EL_BD_MAGIC_WALL))
3255 int activated_magic_wall =
3256 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3257 EL_BD_MAGIC_WALL_ACTIVE);
3259 /* activate magic wall / mill */
3260 for (yy = 0; yy < lev_fieldy; yy++)
3261 for (xx = 0; xx < lev_fieldx; xx++)
3262 if (Feld[xx][yy] == smashed)
3263 Feld[xx][yy] = activated_magic_wall;
3265 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3266 game.magic_wall_active = TRUE;
3268 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3269 SND_MAGIC_WALL_ACTIVATING :
3270 SND_BD_MAGIC_WALL_ACTIVATING));
3273 if (IS_PLAYER(x, y + 1))
3275 if (CAN_SMASH_PLAYER(element))
3277 KillHeroUnlessProtected(x, y + 1);
3281 else if (smashed == EL_PENGUIN)
3283 if (CAN_SMASH_PLAYER(element))
3289 else if (element == EL_BD_DIAMOND)
3291 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3297 else if ((element == EL_SP_INFOTRON ||
3298 element == EL_SP_ZONK) &&
3299 (smashed == EL_SP_SNIKSNAK ||
3300 smashed == EL_SP_ELECTRON ||
3301 smashed == EL_SP_DISK_ORANGE))
3307 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3313 else if (CAN_SMASH_EVERYTHING(element))
3315 if (IS_CLASSIC_ENEMY(smashed) ||
3316 CAN_EXPLODE_SMASHED(smashed))
3321 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3323 if (smashed == EL_LAMP ||
3324 smashed == EL_LAMP_ACTIVE)
3329 else if (smashed == EL_NUT)
3331 Feld[x][y + 1] = EL_NUT_BREAKING;
3332 PlayLevelSound(x, y, SND_NUT_BREAKING);
3333 RaiseScoreElement(EL_NUT);
3336 else if (smashed == EL_PEARL)
3338 Feld[x][y + 1] = EL_PEARL_BREAKING;
3339 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3342 else if (smashed == EL_DIAMOND)
3344 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3345 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3348 else if (IS_BELT_SWITCH(smashed))
3350 ToggleBeltSwitch(x, y + 1);
3352 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3353 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3355 ToggleSwitchgateSwitch(x, y + 1);
3357 else if (smashed == EL_LIGHT_SWITCH ||
3358 smashed == EL_LIGHT_SWITCH_ACTIVE)
3360 ToggleLightSwitch(x, y + 1);
3364 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3366 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3367 CE_OTHER_IS_SWITCHING);
3368 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3374 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3379 /* play sound of magic wall / mill */
3381 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3382 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3384 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3385 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3386 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3387 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3392 /* play sound of object that hits the ground */
3393 if (lastline || object_hit)
3394 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3397 inline static void TurnRoundExt(int x, int y)
3409 { 0, 0 }, { 0, 0 }, { 0, 0 },
3414 int left, right, back;
3418 { MV_DOWN, MV_UP, MV_RIGHT },
3419 { MV_UP, MV_DOWN, MV_LEFT },
3421 { MV_LEFT, MV_RIGHT, MV_DOWN },
3425 { MV_RIGHT, MV_LEFT, MV_UP }
3428 int element = Feld[x][y];
3429 int move_pattern = element_info[element].move_pattern;
3431 int old_move_dir = MovDir[x][y];
3432 int left_dir = turn[old_move_dir].left;
3433 int right_dir = turn[old_move_dir].right;
3434 int back_dir = turn[old_move_dir].back;
3436 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3437 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3438 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3439 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3441 int left_x = x + left_dx, left_y = y + left_dy;
3442 int right_x = x + right_dx, right_y = y + right_dy;
3443 int move_x = x + move_dx, move_y = y + move_dy;
3447 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3449 TestIfBadThingTouchesOtherBadThing(x, y);
3451 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3452 MovDir[x][y] = right_dir;
3453 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3454 MovDir[x][y] = left_dir;
3456 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3458 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3461 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3462 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3464 TestIfBadThingTouchesOtherBadThing(x, y);
3466 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3467 MovDir[x][y] = left_dir;
3468 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3469 MovDir[x][y] = right_dir;
3471 if ((element == EL_SPACESHIP ||
3472 element == EL_SP_SNIKSNAK ||
3473 element == EL_SP_ELECTRON)
3474 && MovDir[x][y] != old_move_dir)
3476 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3479 else if (element == EL_YAMYAM)
3481 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3482 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3484 if (can_turn_left && can_turn_right)
3485 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3486 else if (can_turn_left)
3487 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3488 else if (can_turn_right)
3489 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3491 MovDir[x][y] = back_dir;
3493 MovDelay[x][y] = 16 + 16 * RND(3);
3495 else if (element == EL_DARK_YAMYAM)
3497 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3498 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3500 if (can_turn_left && can_turn_right)
3501 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3502 else if (can_turn_left)
3503 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3504 else if (can_turn_right)
3505 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3507 MovDir[x][y] = back_dir;
3509 MovDelay[x][y] = 16 + 16 * RND(3);
3511 else if (element == EL_PACMAN)
3513 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3514 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3516 if (can_turn_left && can_turn_right)
3517 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3518 else if (can_turn_left)
3519 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3520 else if (can_turn_right)
3521 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3523 MovDir[x][y] = back_dir;
3525 MovDelay[x][y] = 6 + RND(40);
3527 else if (element == EL_PIG)
3529 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3530 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3531 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3532 boolean should_turn_left, should_turn_right, should_move_on;
3534 int rnd = RND(rnd_value);
3536 should_turn_left = (can_turn_left &&
3538 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3539 y + back_dy + left_dy)));
3540 should_turn_right = (can_turn_right &&
3542 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3543 y + back_dy + right_dy)));
3544 should_move_on = (can_move_on &&
3547 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3548 y + move_dy + left_dy) ||
3549 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3550 y + move_dy + right_dy)));
3552 if (should_turn_left || should_turn_right || should_move_on)
3554 if (should_turn_left && should_turn_right && should_move_on)
3555 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3556 rnd < 2 * rnd_value / 3 ? right_dir :
3558 else if (should_turn_left && should_turn_right)
3559 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3560 else if (should_turn_left && should_move_on)
3561 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3562 else if (should_turn_right && should_move_on)
3563 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3564 else if (should_turn_left)
3565 MovDir[x][y] = left_dir;
3566 else if (should_turn_right)
3567 MovDir[x][y] = right_dir;
3568 else if (should_move_on)
3569 MovDir[x][y] = old_move_dir;
3571 else if (can_move_on && rnd > rnd_value / 8)
3572 MovDir[x][y] = old_move_dir;
3573 else if (can_turn_left && can_turn_right)
3574 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3575 else if (can_turn_left && rnd > rnd_value / 8)
3576 MovDir[x][y] = left_dir;
3577 else if (can_turn_right && rnd > rnd_value/8)
3578 MovDir[x][y] = right_dir;
3580 MovDir[x][y] = back_dir;
3582 xx = x + move_xy[MovDir[x][y]].x;
3583 yy = y + move_xy[MovDir[x][y]].y;
3585 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3586 MovDir[x][y] = old_move_dir;
3590 else if (element == EL_DRAGON)
3592 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3593 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3594 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3596 int rnd = RND(rnd_value);
3599 if (FrameCounter < 1 && x == 0 && y == 29)
3600 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3603 if (can_move_on && rnd > rnd_value / 8)
3604 MovDir[x][y] = old_move_dir;
3605 else if (can_turn_left && can_turn_right)
3606 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3607 else if (can_turn_left && rnd > rnd_value / 8)
3608 MovDir[x][y] = left_dir;
3609 else if (can_turn_right && rnd > rnd_value / 8)
3610 MovDir[x][y] = right_dir;
3612 MovDir[x][y] = back_dir;
3614 xx = x + move_xy[MovDir[x][y]].x;
3615 yy = y + move_xy[MovDir[x][y]].y;
3618 if (FrameCounter < 1 && x == 0 && y == 29)
3619 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3620 xx, yy, Feld[xx][yy],
3625 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3626 MovDir[x][y] = old_move_dir;
3628 if (!IS_FREE(xx, yy))
3629 MovDir[x][y] = old_move_dir;
3633 if (FrameCounter < 1 && x == 0 && y == 29)
3634 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3639 else if (element == EL_MOLE)
3641 boolean can_move_on =
3642 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3643 IS_AMOEBOID(Feld[move_x][move_y]) ||
3644 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3647 boolean can_turn_left =
3648 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3649 IS_AMOEBOID(Feld[left_x][left_y])));
3651 boolean can_turn_right =
3652 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3653 IS_AMOEBOID(Feld[right_x][right_y])));
3655 if (can_turn_left && can_turn_right)
3656 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3657 else if (can_turn_left)
3658 MovDir[x][y] = left_dir;
3660 MovDir[x][y] = right_dir;
3663 if (MovDir[x][y] != old_move_dir)
3666 else if (element == EL_BALLOON)
3668 MovDir[x][y] = game.balloon_dir;
3671 else if (element == EL_SPRING)
3673 if (MovDir[x][y] & MV_HORIZONTAL &&
3674 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3675 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3676 MovDir[x][y] = MV_NO_MOVING;
3680 else if (element == EL_ROBOT ||
3681 element == EL_SATELLITE ||
3682 element == EL_PENGUIN)
3684 int attr_x = -1, attr_y = -1;
3695 for (i = 0; i < MAX_PLAYERS; i++)
3697 struct PlayerInfo *player = &stored_player[i];
3698 int jx = player->jx, jy = player->jy;
3700 if (!player->active)
3704 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3712 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3718 if (element == EL_PENGUIN)
3721 static int xy[4][2] =
3729 for (i = 0; i < 4; i++)
3731 int ex = x + xy[i % 4][0];
3732 int ey = y + xy[i % 4][1];
3734 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3743 MovDir[x][y] = MV_NO_MOVING;
3745 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3746 else if (attr_x > x)
3747 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3749 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3750 else if (attr_y > y)
3751 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3753 if (element == EL_ROBOT)
3757 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3758 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3759 Moving2Blocked(x, y, &newx, &newy);
3761 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3762 MovDelay[x][y] = 8 + 8 * !RND(3);
3764 MovDelay[x][y] = 16;
3766 else if (element == EL_PENGUIN)
3772 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3774 boolean first_horiz = RND(2);
3775 int new_move_dir = MovDir[x][y];
3778 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3779 Moving2Blocked(x, y, &newx, &newy);
3781 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3785 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3786 Moving2Blocked(x, y, &newx, &newy);
3788 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3791 MovDir[x][y] = old_move_dir;
3795 else /* (element == EL_SATELLITE) */
3801 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3803 boolean first_horiz = RND(2);
3804 int new_move_dir = MovDir[x][y];
3807 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3808 Moving2Blocked(x, y, &newx, &newy);
3810 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3814 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3815 Moving2Blocked(x, y, &newx, &newy);
3817 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3820 MovDir[x][y] = old_move_dir;
3825 else if (move_pattern == MV_TURNING_LEFT ||
3826 move_pattern == MV_TURNING_RIGHT ||
3827 move_pattern == MV_TURNING_LEFT_RIGHT ||
3828 move_pattern == MV_TURNING_RIGHT_LEFT ||
3829 move_pattern == MV_TURNING_RANDOM ||
3830 move_pattern == MV_ALL_DIRECTIONS)
3832 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3833 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3835 if (move_pattern == MV_TURNING_LEFT)
3836 MovDir[x][y] = left_dir;
3837 else if (move_pattern == MV_TURNING_RIGHT)
3838 MovDir[x][y] = right_dir;
3839 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3840 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3841 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3842 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3843 else if (move_pattern == MV_TURNING_RANDOM)
3844 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3845 can_turn_right && !can_turn_left ? right_dir :
3846 RND(2) ? left_dir : right_dir);
3847 else if (can_turn_left && can_turn_right)
3848 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3849 else if (can_turn_left)
3850 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3851 else if (can_turn_right)
3852 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3854 MovDir[x][y] = back_dir;
3856 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3858 else if (move_pattern == MV_HORIZONTAL ||
3859 move_pattern == MV_VERTICAL)
3861 if (move_pattern & old_move_dir)
3862 MovDir[x][y] = back_dir;
3863 else if (move_pattern == MV_HORIZONTAL)
3864 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3865 else if (move_pattern == MV_VERTICAL)
3866 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3868 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3870 else if (move_pattern & MV_ANY_DIRECTION)
3872 MovDir[x][y] = move_pattern;
3873 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3875 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3877 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3878 MovDir[x][y] = left_dir;
3879 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3880 MovDir[x][y] = right_dir;
3882 if (MovDir[x][y] != old_move_dir)
3883 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3885 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3887 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3888 MovDir[x][y] = right_dir;
3889 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3890 MovDir[x][y] = left_dir;
3892 if (MovDir[x][y] != old_move_dir)
3893 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3895 else if (move_pattern == MV_TOWARDS_PLAYER ||
3896 move_pattern == MV_AWAY_FROM_PLAYER)
3898 int attr_x = -1, attr_y = -1;
3900 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3911 for (i = 0; i < MAX_PLAYERS; i++)
3913 struct PlayerInfo *player = &stored_player[i];
3914 int jx = player->jx, jy = player->jy;
3916 if (!player->active)
3920 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3928 MovDir[x][y] = MV_NO_MOVING;
3930 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3931 else if (attr_x > x)
3932 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3934 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3935 else if (attr_y > y)
3936 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3938 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3940 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3942 boolean first_horiz = RND(2);
3943 int new_move_dir = MovDir[x][y];
3946 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3947 Moving2Blocked(x, y, &newx, &newy);
3949 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3953 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3954 Moving2Blocked(x, y, &newx, &newy);
3956 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3959 MovDir[x][y] = old_move_dir;
3962 else if (move_pattern == MV_WHEN_PUSHED ||
3963 move_pattern == MV_WHEN_DROPPED)
3965 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3966 MovDir[x][y] = MV_NO_MOVING;
3970 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3971 element == EL_MAZE_RUNNER)
3973 static int test_xy[7][2] =
3983 static int test_dir[7] =
3993 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3994 int move_preference = -1000000; /* start with very low preference */
3995 int new_move_dir = MV_NO_MOVING;
3996 int start_test = RND(4);
3999 for (i = 0; i < 4; i++)
4001 int move_dir = test_dir[start_test + i];
4002 int move_dir_preference;
4004 xx = x + test_xy[start_test + i][0];
4005 yy = y + test_xy[start_test + i][1];
4007 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4008 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4010 new_move_dir = move_dir;
4015 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
4018 move_dir_preference = -1 * RunnerVisit[xx][yy];
4019 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4020 move_dir_preference = PlayerVisit[xx][yy];
4022 if (move_dir_preference > move_preference)
4024 /* prefer field that has not been visited for the longest time */
4025 move_preference = move_dir_preference;
4026 new_move_dir = move_dir;
4028 else if (move_dir_preference == move_preference &&
4029 move_dir == old_move_dir)
4031 /* prefer last direction when all directions are preferred equally */
4032 move_preference = move_dir_preference;
4033 new_move_dir = move_dir;
4037 MovDir[x][y] = new_move_dir;
4038 if (old_move_dir != new_move_dir)
4043 static void TurnRound(int x, int y)
4045 int direction = MovDir[x][y];
4048 GfxDir[x][y] = MovDir[x][y];
4054 GfxDir[x][y] = MovDir[x][y];
4057 if (direction != MovDir[x][y])
4062 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4065 GfxAction[x][y] = ACTION_WAITING;
4069 static boolean JustBeingPushed(int x, int y)
4073 for (i = 0; i < MAX_PLAYERS; i++)
4075 struct PlayerInfo *player = &stored_player[i];
4077 if (player->active && player->is_pushing && player->MovPos)
4079 int next_jx = player->jx + (player->jx - player->last_jx);
4080 int next_jy = player->jy + (player->jy - player->last_jy);
4082 if (x == next_jx && y == next_jy)
4090 void StartMoving(int x, int y)
4092 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4093 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4094 int element = Feld[x][y];
4100 if (MovDelay[x][y] == 0)
4101 GfxAction[x][y] = ACTION_DEFAULT;
4103 /* !!! this should be handled more generic (not only for mole) !!! */
4104 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4105 GfxAction[x][y] = ACTION_DEFAULT;
4108 if (CAN_FALL(element) && y < lev_fieldy - 1)
4110 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4111 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4112 if (JustBeingPushed(x, y))
4115 if (element == EL_QUICKSAND_FULL)
4117 if (IS_FREE(x, y + 1))
4119 InitMovingField(x, y, MV_DOWN);
4120 started_moving = TRUE;
4122 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4123 Store[x][y] = EL_ROCK;
4125 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4127 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4130 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4132 if (!MovDelay[x][y])
4133 MovDelay[x][y] = TILEY + 1;
4142 Feld[x][y] = EL_QUICKSAND_EMPTY;
4143 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4144 Store[x][y + 1] = Store[x][y];
4147 PlayLevelSoundAction(x, y, ACTION_FILLING);
4149 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4153 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4154 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4156 InitMovingField(x, y, MV_DOWN);
4157 started_moving = TRUE;
4159 Feld[x][y] = EL_QUICKSAND_FILLING;
4160 Store[x][y] = element;
4162 PlayLevelSoundAction(x, y, ACTION_FILLING);
4164 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4167 else if (element == EL_MAGIC_WALL_FULL)
4169 if (IS_FREE(x, y + 1))
4171 InitMovingField(x, y, MV_DOWN);
4172 started_moving = TRUE;
4174 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4175 Store[x][y] = EL_CHANGED(Store[x][y]);
4177 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4179 if (!MovDelay[x][y])
4180 MovDelay[x][y] = TILEY/4 + 1;
4189 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4190 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4191 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4195 else if (element == EL_BD_MAGIC_WALL_FULL)
4197 if (IS_FREE(x, y + 1))
4199 InitMovingField(x, y, MV_DOWN);
4200 started_moving = TRUE;
4202 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4203 Store[x][y] = EL_CHANGED2(Store[x][y]);
4205 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4207 if (!MovDelay[x][y])
4208 MovDelay[x][y] = TILEY/4 + 1;
4217 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4218 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4219 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4223 else if (CAN_PASS_MAGIC_WALL(element) &&
4224 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4225 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4227 InitMovingField(x, y, MV_DOWN);
4228 started_moving = TRUE;
4231 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4232 EL_BD_MAGIC_WALL_FILLING);
4233 Store[x][y] = element;
4236 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4238 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4243 InitMovingField(x, y, MV_DOWN);
4244 started_moving = TRUE;
4246 Store[x][y] = EL_ACID;
4248 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4249 GfxAction[x][y + 1] = ACTION_ACTIVE;
4253 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4254 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4255 (Feld[x][y + 1] == EL_BLOCKED)) ||
4256 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4257 CAN_SMASH(element) && WasJustFalling[x][y] &&
4258 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4262 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4263 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4264 WasJustMoving[x][y] && !Pushed[x][y + 1])
4266 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4267 WasJustMoving[x][y])
4272 /* this is needed for a special case not covered by calling "Impact()"
4273 from "ContinueMoving()": if an element moves to a tile directly below
4274 another element which was just falling on that tile (which was empty
4275 in the previous frame), the falling element above would just stop
4276 instead of smashing the element below (in previous version, the above
4277 element was just checked for "moving" instead of "falling", resulting
4278 in incorrect smashes caused by horizontal movement of the above
4279 element; also, the case of the player being the element to smash was
4280 simply not covered here... :-/ ) */
4283 WasJustMoving[x][y] = 0;
4284 WasJustFalling[x][y] = 0;
4289 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4291 if (MovDir[x][y] == MV_NO_MOVING)
4293 InitMovingField(x, y, MV_DOWN);
4294 started_moving = TRUE;
4297 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4299 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4300 MovDir[x][y] = MV_DOWN;
4302 InitMovingField(x, y, MV_DOWN);
4303 started_moving = TRUE;
4305 else if (element == EL_AMOEBA_DROP)
4307 Feld[x][y] = EL_AMOEBA_GROWING;
4308 Store[x][y] = EL_AMOEBA_WET;
4310 /* Store[x][y + 1] must be zero, because:
4311 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4314 #if OLD_GAME_BEHAVIOUR
4315 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4317 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4318 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4319 element != EL_DX_SUPABOMB)
4322 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4323 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4324 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4325 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4328 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4329 (IS_FREE(x - 1, y + 1) ||
4330 Feld[x - 1][y + 1] == EL_ACID));
4331 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4332 (IS_FREE(x + 1, y + 1) ||
4333 Feld[x + 1][y + 1] == EL_ACID));
4334 boolean can_fall_any = (can_fall_left || can_fall_right);
4335 boolean can_fall_both = (can_fall_left && can_fall_right);
4337 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4339 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4341 if (slippery_type == SLIPPERY_ONLY_LEFT)
4342 can_fall_right = FALSE;
4343 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4344 can_fall_left = FALSE;
4345 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4346 can_fall_right = FALSE;
4347 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4348 can_fall_left = FALSE;
4350 can_fall_any = (can_fall_left || can_fall_right);
4351 can_fall_both = (can_fall_left && can_fall_right);
4356 if (can_fall_both &&
4357 (game.emulation != EMU_BOULDERDASH &&
4358 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4359 can_fall_left = !(can_fall_right = RND(2));
4361 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4362 started_moving = TRUE;
4365 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4367 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4368 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4369 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4370 int belt_dir = game.belt_dir[belt_nr];
4372 if ((belt_dir == MV_LEFT && left_is_free) ||
4373 (belt_dir == MV_RIGHT && right_is_free))
4375 InitMovingField(x, y, belt_dir);
4376 started_moving = TRUE;
4378 GfxAction[x][y] = ACTION_DEFAULT;
4383 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4384 if (CAN_MOVE(element) && !started_moving)
4386 int move_pattern = element_info[element].move_pattern;
4389 Moving2Blocked(x, y, &newx, &newy);
4392 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4395 if ((element == EL_SATELLITE ||
4396 element == EL_BALLOON ||
4397 element == EL_SPRING)
4398 && JustBeingPushed(x, y))
4403 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4404 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4405 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4408 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4409 element, element_info[element].token_name,
4410 WasJustMoving[x][y],
4411 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4412 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4413 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4414 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4418 WasJustMoving[x][y] = 0;
4421 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4424 if (Feld[x][y] != element) /* element has changed */
4426 element = Feld[x][y];
4427 move_pattern = element_info[element].move_pattern;
4429 if (!CAN_MOVE(element))
4433 if (Feld[x][y] != element) /* element has changed */
4441 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4442 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4444 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4446 Moving2Blocked(x, y, &newx, &newy);
4447 if (Feld[newx][newy] == EL_BLOCKED)
4448 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4454 if (FrameCounter < 1 && x == 0 && y == 29)
4455 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4458 if (!MovDelay[x][y]) /* start new movement phase */
4460 /* all objects that can change their move direction after each step
4461 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4463 if (element != EL_YAMYAM &&
4464 element != EL_DARK_YAMYAM &&
4465 element != EL_PACMAN &&
4466 !(move_pattern & MV_ANY_DIRECTION) &&
4467 move_pattern != MV_TURNING_LEFT &&
4468 move_pattern != MV_TURNING_RIGHT &&
4469 move_pattern != MV_TURNING_LEFT_RIGHT &&
4470 move_pattern != MV_TURNING_RIGHT_LEFT &&
4471 move_pattern != MV_TURNING_RANDOM)
4476 if (FrameCounter < 1 && x == 0 && y == 29)
4477 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4480 if (MovDelay[x][y] && (element == EL_BUG ||
4481 element == EL_SPACESHIP ||
4482 element == EL_SP_SNIKSNAK ||
4483 element == EL_SP_ELECTRON ||
4484 element == EL_MOLE))
4485 DrawLevelField(x, y);
4489 if (MovDelay[x][y]) /* wait some time before next movement */
4494 if (element == EL_YAMYAM)
4497 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4498 DrawLevelElementAnimation(x, y, element);
4502 if (MovDelay[x][y]) /* element still has to wait some time */
4505 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4506 ResetGfxAnimation(x, y);
4510 if (GfxAction[x][y] != ACTION_WAITING)
4511 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4513 GfxAction[x][y] = ACTION_WAITING;
4517 if (element == EL_ROBOT ||
4519 element == EL_PACMAN ||
4521 element == EL_YAMYAM ||
4522 element == EL_DARK_YAMYAM)
4525 DrawLevelElementAnimation(x, y, element);
4527 DrawLevelElementAnimationIfNeeded(x, y, element);
4529 PlayLevelSoundAction(x, y, ACTION_WAITING);
4531 else if (element == EL_SP_ELECTRON)
4532 DrawLevelElementAnimationIfNeeded(x, y, element);
4533 else if (element == EL_DRAGON)
4536 int dir = MovDir[x][y];
4537 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4538 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4539 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4540 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4541 dir == MV_UP ? IMG_FLAMES_1_UP :
4542 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4543 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4546 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4549 GfxAction[x][y] = ACTION_ATTACKING;
4551 if (IS_PLAYER(x, y))
4552 DrawPlayerField(x, y);
4554 DrawLevelField(x, y);
4556 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4558 for (i = 1; i <= 3; i++)
4560 int xx = x + i * dx;
4561 int yy = y + i * dy;
4562 int sx = SCREENX(xx);
4563 int sy = SCREENY(yy);
4564 int flame_graphic = graphic + (i - 1);
4566 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4571 int flamed = MovingOrBlocked2Element(xx, yy);
4573 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4576 RemoveMovingField(xx, yy);
4578 Feld[xx][yy] = EL_FLAMES;
4579 if (IN_SCR_FIELD(sx, sy))
4581 DrawLevelFieldCrumbledSand(xx, yy);
4582 DrawGraphic(sx, sy, flame_graphic, frame);
4587 if (Feld[xx][yy] == EL_FLAMES)
4588 Feld[xx][yy] = EL_EMPTY;
4589 DrawLevelField(xx, yy);
4594 if (MovDelay[x][y]) /* element still has to wait some time */
4596 PlayLevelSoundAction(x, y, ACTION_WAITING);
4602 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4603 for all other elements GfxAction will be set by InitMovingField() */
4604 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4605 GfxAction[x][y] = ACTION_MOVING;
4609 /* now make next step */
4611 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4613 if (DONT_COLLIDE_WITH(element) &&
4614 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4615 !PLAYER_PROTECTED(newx, newy))
4618 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4621 /* player killed by element which is deadly when colliding with */
4623 KillHero(PLAYERINFO(newx, newy));
4628 else if ((element == EL_PENGUIN ||
4629 element == EL_ROBOT ||
4630 element == EL_SATELLITE ||
4631 element == EL_BALLOON ||
4632 IS_CUSTOM_ELEMENT(element)) &&
4633 IN_LEV_FIELD(newx, newy) &&
4634 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4637 Store[x][y] = EL_ACID;
4639 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4641 if (Feld[newx][newy] == EL_EXIT_OPEN)
4645 DrawLevelField(x, y);
4647 Feld[x][y] = EL_EMPTY;
4648 DrawLevelField(x, y);
4651 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4652 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4653 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4655 local_player->friends_still_needed--;
4656 if (!local_player->friends_still_needed &&
4657 !local_player->GameOver && AllPlayersGone)
4658 local_player->LevelSolved = local_player->GameOver = TRUE;
4662 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4664 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4665 DrawLevelField(newx, newy);
4667 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4669 else if (!IS_FREE(newx, newy))
4671 GfxAction[x][y] = ACTION_WAITING;
4673 if (IS_PLAYER(x, y))
4674 DrawPlayerField(x, y);
4676 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);
4703 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4704 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4707 if (IS_FREE(newx, newy))
4709 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4712 if (IS_MOVING(newx, newy))
4713 RemoveMovingField(newx, newy);
4716 Feld[newx][newy] = EL_EMPTY;
4717 DrawLevelField(newx, newy);
4720 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4722 else if (!IS_FREE(newx, newy))
4725 if (IS_PLAYER(x, y))
4726 DrawPlayerField(x, y);
4728 DrawLevelField(x, y);
4733 RunnerVisit[x][y] = FrameCounter;
4734 PlayerVisit[x][y] /= 8; /* expire player visit path */
4736 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4738 if (!IS_FREE(newx, newy))
4740 if (IS_PLAYER(x, y))
4741 DrawPlayerField(x, y);
4743 DrawLevelField(x, y);
4749 boolean wanna_flame = !RND(10);
4750 int dx = newx - x, dy = newy - y;
4751 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4752 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4753 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4754 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4755 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4756 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4759 IS_CLASSIC_ENEMY(element1) ||
4760 IS_CLASSIC_ENEMY(element2)) &&
4761 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4762 element1 != EL_FLAMES && element2 != EL_FLAMES)
4765 ResetGfxAnimation(x, y);
4766 GfxAction[x][y] = ACTION_ATTACKING;
4769 if (IS_PLAYER(x, y))
4770 DrawPlayerField(x, y);
4772 DrawLevelField(x, y);
4774 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4776 MovDelay[x][y] = 50;
4778 Feld[newx][newy] = EL_FLAMES;
4779 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4780 Feld[newx1][newy1] = EL_FLAMES;
4781 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4782 Feld[newx2][newy2] = EL_FLAMES;
4788 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4789 Feld[newx][newy] == EL_DIAMOND)
4791 if (IS_MOVING(newx, newy))
4792 RemoveMovingField(newx, newy);
4795 Feld[newx][newy] = EL_EMPTY;
4796 DrawLevelField(newx, newy);
4799 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4801 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4802 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4804 if (AmoebaNr[newx][newy])
4806 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4807 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4808 Feld[newx][newy] == EL_BD_AMOEBA)
4809 AmoebaCnt[AmoebaNr[newx][newy]]--;
4812 if (IS_MOVING(newx, newy))
4813 RemoveMovingField(newx, newy);
4816 Feld[newx][newy] = EL_EMPTY;
4817 DrawLevelField(newx, newy);
4820 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4822 else if ((element == EL_PACMAN || element == EL_MOLE)
4823 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4825 if (AmoebaNr[newx][newy])
4827 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4828 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4829 Feld[newx][newy] == EL_BD_AMOEBA)
4830 AmoebaCnt[AmoebaNr[newx][newy]]--;
4833 if (element == EL_MOLE)
4835 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4836 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4838 ResetGfxAnimation(x, y);
4839 GfxAction[x][y] = ACTION_DIGGING;
4840 DrawLevelField(x, y);
4842 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4843 return; /* wait for shrinking amoeba */
4845 else /* element == EL_PACMAN */
4847 Feld[newx][newy] = EL_EMPTY;
4848 DrawLevelField(newx, newy);
4849 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4852 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4853 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4854 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4856 /* wait for shrinking amoeba to completely disappear */
4859 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4861 /* object was running against a wall */
4866 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4867 DrawLevelElementAnimation(x, y, element);
4869 if (element == EL_BUG ||
4870 element == EL_SPACESHIP ||
4871 element == EL_SP_SNIKSNAK)
4872 DrawLevelField(x, y);
4873 else if (element == EL_MOLE)
4874 DrawLevelField(x, y);
4875 else if (element == EL_BD_BUTTERFLY ||
4876 element == EL_BD_FIREFLY)
4877 DrawLevelElementAnimationIfNeeded(x, y, element);
4878 else if (element == EL_SATELLITE)
4879 DrawLevelElementAnimationIfNeeded(x, y, element);
4880 else if (element == EL_SP_ELECTRON)
4881 DrawLevelElementAnimationIfNeeded(x, y, element);
4884 if (DONT_TOUCH(element))
4885 TestIfBadThingTouchesHero(x, y);
4888 PlayLevelSoundAction(x, y, ACTION_WAITING);
4894 InitMovingField(x, y, MovDir[x][y]);
4896 PlayLevelSoundAction(x, y, ACTION_MOVING);
4900 ContinueMoving(x, y);
4903 void ContinueMoving(int x, int y)
4905 int element = Feld[x][y];
4906 int direction = MovDir[x][y];
4907 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4908 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4909 int newx = x + dx, newy = y + dy;
4911 int nextx = newx + dx, nexty = newy + dy;
4913 boolean pushed = Pushed[x][y];
4915 MovPos[x][y] += getElementMoveStepsize(x, y);
4917 if (pushed) /* special case: moving object pushed by player */
4918 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4920 if (ABS(MovPos[x][y]) < TILEX)
4922 DrawLevelField(x, y);
4924 return; /* element is still moving */
4927 /* element reached destination field */
4929 Feld[x][y] = EL_EMPTY;
4930 Feld[newx][newy] = element;
4931 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4933 if (element == EL_MOLE)
4935 Feld[x][y] = EL_SAND;
4937 DrawLevelFieldCrumbledSandNeighbours(x, y);
4939 else if (element == EL_QUICKSAND_FILLING)
4941 element = Feld[newx][newy] = get_next_element(element);
4942 Store[newx][newy] = Store[x][y];
4944 else if (element == EL_QUICKSAND_EMPTYING)
4946 Feld[x][y] = get_next_element(element);
4947 element = Feld[newx][newy] = Store[x][y];
4949 else if (element == EL_MAGIC_WALL_FILLING)
4951 element = Feld[newx][newy] = get_next_element(element);
4952 if (!game.magic_wall_active)
4953 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4954 Store[newx][newy] = Store[x][y];
4956 else if (element == EL_MAGIC_WALL_EMPTYING)
4958 Feld[x][y] = get_next_element(element);
4959 if (!game.magic_wall_active)
4960 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4961 element = Feld[newx][newy] = Store[x][y];
4963 else if (element == EL_BD_MAGIC_WALL_FILLING)
4965 element = Feld[newx][newy] = get_next_element(element);
4966 if (!game.magic_wall_active)
4967 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4968 Store[newx][newy] = Store[x][y];
4970 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4972 Feld[x][y] = get_next_element(element);
4973 if (!game.magic_wall_active)
4974 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4975 element = Feld[newx][newy] = Store[x][y];
4977 else if (element == EL_AMOEBA_DROPPING)
4979 Feld[x][y] = get_next_element(element);
4980 element = Feld[newx][newy] = Store[x][y];
4982 else if (element == EL_SOKOBAN_OBJECT)
4985 Feld[x][y] = Back[x][y];
4987 if (Back[newx][newy])
4988 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4990 Back[x][y] = Back[newx][newy] = 0;
4992 else if (Store[x][y] == EL_ACID)
4994 element = Feld[newx][newy] = EL_ACID;
4998 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4999 MovDelay[newx][newy] = 0;
5001 /* copy element change control values to new field */
5002 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5003 ChangePage[newx][newy] = ChangePage[x][y];
5004 Changed[newx][newy] = Changed[x][y];
5005 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5007 ChangeDelay[x][y] = 0;
5008 ChangePage[x][y] = -1;
5009 Changed[x][y] = CE_BITMASK_DEFAULT;
5010 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5012 /* copy animation control values to new field */
5013 GfxFrame[newx][newy] = GfxFrame[x][y];
5014 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5015 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5016 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5018 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5020 ResetGfxAnimation(x, y); /* reset animation values for old field */
5023 /* 2.1.1 (does not work correctly for spring) */
5024 if (!CAN_MOVE(element))
5025 MovDir[newx][newy] = 0;
5029 /* (does not work for falling objects that slide horizontally) */
5030 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5031 MovDir[newx][newy] = 0;
5034 if (!CAN_MOVE(element) ||
5035 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5036 MovDir[newx][newy] = 0;
5039 if (!CAN_MOVE(element) ||
5040 (CAN_FALL(element) && direction == MV_DOWN))
5041 GfxDir[x][y] = MovDir[newx][newy] = 0;
5046 DrawLevelField(x, y);
5047 DrawLevelField(newx, newy);
5049 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5051 /* prevent pushed element from moving on in pushed direction */
5052 if (pushed && CAN_MOVE(element) &&
5053 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5054 !(element_info[element].move_pattern & direction))
5055 TurnRound(newx, newy);
5057 if (!pushed) /* special case: moving object pushed by player */
5059 WasJustMoving[newx][newy] = 3;
5061 if (CAN_FALL(element) && direction == MV_DOWN)
5062 WasJustFalling[newx][newy] = 3;
5065 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5067 TestIfBadThingTouchesHero(newx, newy);
5068 TestIfBadThingTouchesFriend(newx, newy);
5070 if (!IS_CUSTOM_ELEMENT(element))
5071 TestIfBadThingTouchesOtherBadThing(newx, newy);
5073 else if (element == EL_PENGUIN)
5074 TestIfFriendTouchesBadThing(newx, newy);
5076 if (CAN_FALL(element) && direction == MV_DOWN &&
5077 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5081 TestIfElementTouchesCustomElement(x, y); /* for empty space */
5085 if (ChangePage[newx][newy] != -1) /* delayed change */
5086 ChangeElement(newx, newy, ChangePage[newx][newy]);
5091 TestIfElementHitsCustomElement(newx, newy, direction);
5095 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5097 int hitting_element = Feld[newx][newy];
5099 /* !!! fix side (direction) orientation here and elsewhere !!! */
5100 CheckElementSideChange(newx, newy, hitting_element,
5101 direction, CE_HITTING_SOMETHING, -1);
5104 if (IN_LEV_FIELD(nextx, nexty))
5106 static int opposite_directions[] =
5113 int move_dir_bit = MV_DIR_BIT(direction);
5114 int opposite_direction = opposite_directions[move_dir_bit];
5115 int hitting_side = direction;
5116 int touched_side = opposite_direction;
5117 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5118 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5119 MovDir[nextx][nexty] != direction ||
5120 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5126 CheckElementSideChange(nextx, nexty, touched_element,
5127 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5129 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5130 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5132 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5134 struct ElementChangeInfo *change =
5135 &element_info[hitting_element].change_page[i];
5137 if (change->can_change &&
5138 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5139 change->sides & touched_side &&
5140 change->trigger_element == touched_element)
5142 CheckElementSideChange(newx, newy, hitting_element,
5143 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5149 if (IS_CUSTOM_ELEMENT(touched_element) &&
5150 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5152 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5154 struct ElementChangeInfo *change =
5155 &element_info[touched_element].change_page[i];
5157 if (change->can_change &&
5158 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5159 change->sides & hitting_side &&
5160 change->trigger_element == hitting_element)
5162 CheckElementSideChange(nextx, nexty, touched_element,
5163 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5174 TestIfPlayerTouchesCustomElement(newx, newy);
5175 TestIfElementTouchesCustomElement(newx, newy);
5178 int AmoebeNachbarNr(int ax, int ay)
5181 int element = Feld[ax][ay];
5183 static int xy[4][2] =
5191 for (i = 0; i < 4; i++)
5193 int x = ax + xy[i][0];
5194 int y = ay + xy[i][1];
5196 if (!IN_LEV_FIELD(x, y))
5199 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5200 group_nr = AmoebaNr[x][y];
5206 void AmoebenVereinigen(int ax, int ay)
5208 int i, x, y, xx, yy;
5209 int new_group_nr = AmoebaNr[ax][ay];
5210 static int xy[4][2] =
5218 if (new_group_nr == 0)
5221 for (i = 0; i < 4; i++)
5226 if (!IN_LEV_FIELD(x, y))
5229 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5230 Feld[x][y] == EL_BD_AMOEBA ||
5231 Feld[x][y] == EL_AMOEBA_DEAD) &&
5232 AmoebaNr[x][y] != new_group_nr)
5234 int old_group_nr = AmoebaNr[x][y];
5236 if (old_group_nr == 0)
5239 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5240 AmoebaCnt[old_group_nr] = 0;
5241 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5242 AmoebaCnt2[old_group_nr] = 0;
5244 for (yy = 0; yy < lev_fieldy; yy++)
5246 for (xx = 0; xx < lev_fieldx; xx++)
5248 if (AmoebaNr[xx][yy] == old_group_nr)
5249 AmoebaNr[xx][yy] = new_group_nr;
5256 void AmoebeUmwandeln(int ax, int ay)
5260 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5262 int group_nr = AmoebaNr[ax][ay];
5267 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5268 printf("AmoebeUmwandeln(): This should never happen!\n");
5273 for (y = 0; y < lev_fieldy; y++)
5275 for (x = 0; x < lev_fieldx; x++)
5277 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5280 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5284 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5285 SND_AMOEBA_TURNING_TO_GEM :
5286 SND_AMOEBA_TURNING_TO_ROCK));
5291 static int xy[4][2] =
5299 for (i = 0; i < 4; i++)
5304 if (!IN_LEV_FIELD(x, y))
5307 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5309 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5310 SND_AMOEBA_TURNING_TO_GEM :
5311 SND_AMOEBA_TURNING_TO_ROCK));
5318 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5321 int group_nr = AmoebaNr[ax][ay];
5322 boolean done = FALSE;
5327 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5328 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5333 for (y = 0; y < lev_fieldy; y++)
5335 for (x = 0; x < lev_fieldx; x++)
5337 if (AmoebaNr[x][y] == group_nr &&
5338 (Feld[x][y] == EL_AMOEBA_DEAD ||
5339 Feld[x][y] == EL_BD_AMOEBA ||
5340 Feld[x][y] == EL_AMOEBA_GROWING))
5343 Feld[x][y] = new_element;
5344 InitField(x, y, FALSE);
5345 DrawLevelField(x, y);
5352 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5353 SND_BD_AMOEBA_TURNING_TO_ROCK :
5354 SND_BD_AMOEBA_TURNING_TO_GEM));
5357 void AmoebeWaechst(int x, int y)
5359 static unsigned long sound_delay = 0;
5360 static unsigned long sound_delay_value = 0;
5362 if (!MovDelay[x][y]) /* start new growing cycle */
5366 if (DelayReached(&sound_delay, sound_delay_value))
5369 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5371 if (Store[x][y] == EL_BD_AMOEBA)
5372 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5374 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5376 sound_delay_value = 30;
5380 if (MovDelay[x][y]) /* wait some time before growing bigger */
5383 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5385 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5386 6 - MovDelay[x][y]);
5388 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5391 if (!MovDelay[x][y])
5393 Feld[x][y] = Store[x][y];
5395 DrawLevelField(x, y);
5400 void AmoebaDisappearing(int x, int y)
5402 static unsigned long sound_delay = 0;
5403 static unsigned long sound_delay_value = 0;
5405 if (!MovDelay[x][y]) /* start new shrinking cycle */
5409 if (DelayReached(&sound_delay, sound_delay_value))
5410 sound_delay_value = 30;
5413 if (MovDelay[x][y]) /* wait some time before shrinking */
5416 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5418 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5419 6 - MovDelay[x][y]);
5421 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5424 if (!MovDelay[x][y])
5426 Feld[x][y] = EL_EMPTY;
5427 DrawLevelField(x, y);
5429 /* don't let mole enter this field in this cycle;
5430 (give priority to objects falling to this field from above) */
5436 void AmoebeAbleger(int ax, int ay)
5439 int element = Feld[ax][ay];
5440 int graphic = el2img(element);
5441 int newax = ax, neway = ay;
5442 static int xy[4][2] =
5450 if (!level.amoeba_speed)
5452 Feld[ax][ay] = EL_AMOEBA_DEAD;
5453 DrawLevelField(ax, ay);
5457 if (IS_ANIMATED(graphic))
5458 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5460 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5461 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5463 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5466 if (MovDelay[ax][ay])
5470 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5473 int x = ax + xy[start][0];
5474 int y = ay + xy[start][1];
5476 if (!IN_LEV_FIELD(x, y))
5479 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5480 if (IS_FREE(x, y) ||
5481 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5487 if (newax == ax && neway == ay)
5490 else /* normal or "filled" (BD style) amoeba */
5493 boolean waiting_for_player = FALSE;
5495 for (i = 0; i < 4; i++)
5497 int j = (start + i) % 4;
5498 int x = ax + xy[j][0];
5499 int y = ay + xy[j][1];
5501 if (!IN_LEV_FIELD(x, y))
5504 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5505 if (IS_FREE(x, y) ||
5506 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5512 else if (IS_PLAYER(x, y))
5513 waiting_for_player = TRUE;
5516 if (newax == ax && neway == ay) /* amoeba cannot grow */
5518 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5520 Feld[ax][ay] = EL_AMOEBA_DEAD;
5521 DrawLevelField(ax, ay);
5522 AmoebaCnt[AmoebaNr[ax][ay]]--;
5524 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5526 if (element == EL_AMOEBA_FULL)
5527 AmoebeUmwandeln(ax, ay);
5528 else if (element == EL_BD_AMOEBA)
5529 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5534 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5536 /* amoeba gets larger by growing in some direction */
5538 int new_group_nr = AmoebaNr[ax][ay];
5541 if (new_group_nr == 0)
5543 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5544 printf("AmoebeAbleger(): This should never happen!\n");
5549 AmoebaNr[newax][neway] = new_group_nr;
5550 AmoebaCnt[new_group_nr]++;
5551 AmoebaCnt2[new_group_nr]++;
5553 /* if amoeba touches other amoeba(s) after growing, unify them */
5554 AmoebenVereinigen(newax, neway);
5556 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5558 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5564 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5565 (neway == lev_fieldy - 1 && newax != ax))
5567 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5568 Store[newax][neway] = element;
5570 else if (neway == ay)
5572 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5574 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5576 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5581 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5582 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5583 Store[ax][ay] = EL_AMOEBA_DROP;
5584 ContinueMoving(ax, ay);
5588 DrawLevelField(newax, neway);
5591 void Life(int ax, int ay)
5594 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5596 int element = Feld[ax][ay];
5597 int graphic = el2img(element);
5598 boolean changed = FALSE;
5600 if (IS_ANIMATED(graphic))
5601 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5606 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5607 MovDelay[ax][ay] = life_time;
5609 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5612 if (MovDelay[ax][ay])
5616 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5618 int xx = ax+x1, yy = ay+y1;
5621 if (!IN_LEV_FIELD(xx, yy))
5624 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5626 int x = xx+x2, y = yy+y2;
5628 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5631 if (((Feld[x][y] == element ||
5632 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5634 (IS_FREE(x, y) && Stop[x][y]))
5638 if (xx == ax && yy == ay) /* field in the middle */
5640 if (nachbarn < life[0] || nachbarn > life[1])
5642 Feld[xx][yy] = EL_EMPTY;
5644 DrawLevelField(xx, yy);
5645 Stop[xx][yy] = TRUE;
5649 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5650 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5651 { /* free border field */
5652 if (nachbarn >= life[2] && nachbarn <= life[3])
5654 Feld[xx][yy] = element;
5655 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5657 DrawLevelField(xx, yy);
5658 Stop[xx][yy] = TRUE;
5665 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5666 SND_GAME_OF_LIFE_GROWING);
5669 static void InitRobotWheel(int x, int y)
5671 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5674 static void RunRobotWheel(int x, int y)
5676 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5679 static void StopRobotWheel(int x, int y)
5681 if (ZX == x && ZY == y)
5685 static void InitTimegateWheel(int x, int y)
5687 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5690 static void RunTimegateWheel(int x, int y)
5692 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5695 void CheckExit(int x, int y)
5697 if (local_player->gems_still_needed > 0 ||
5698 local_player->sokobanfields_still_needed > 0 ||
5699 local_player->lights_still_needed > 0)
5701 int element = Feld[x][y];
5702 int graphic = el2img(element);
5704 if (IS_ANIMATED(graphic))
5705 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5710 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5713 Feld[x][y] = EL_EXIT_OPENING;
5715 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5718 void CheckExitSP(int x, int y)
5720 if (local_player->gems_still_needed > 0)
5722 int element = Feld[x][y];
5723 int graphic = el2img(element);
5725 if (IS_ANIMATED(graphic))
5726 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5731 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5734 Feld[x][y] = EL_SP_EXIT_OPENING;
5736 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5739 static void CloseAllOpenTimegates()
5743 for (y = 0; y < lev_fieldy; y++)
5745 for (x = 0; x < lev_fieldx; x++)
5747 int element = Feld[x][y];
5749 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5751 Feld[x][y] = EL_TIMEGATE_CLOSING;
5753 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5755 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5762 void EdelsteinFunkeln(int x, int y)
5764 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5767 if (Feld[x][y] == EL_BD_DIAMOND)
5770 if (MovDelay[x][y] == 0) /* next animation frame */
5771 MovDelay[x][y] = 11 * !SimpleRND(500);
5773 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5777 if (setup.direct_draw && MovDelay[x][y])
5778 SetDrawtoField(DRAW_BUFFERED);
5780 DrawLevelElementAnimation(x, y, Feld[x][y]);
5782 if (MovDelay[x][y] != 0)
5784 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5785 10 - MovDelay[x][y]);
5787 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5789 if (setup.direct_draw)
5793 dest_x = FX + SCREENX(x) * TILEX;
5794 dest_y = FY + SCREENY(y) * TILEY;
5796 BlitBitmap(drawto_field, window,
5797 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5798 SetDrawtoField(DRAW_DIRECT);
5804 void MauerWaechst(int x, int y)
5808 if (!MovDelay[x][y]) /* next animation frame */
5809 MovDelay[x][y] = 3 * delay;
5811 if (MovDelay[x][y]) /* wait some time before next frame */
5815 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5817 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5818 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5820 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5823 if (!MovDelay[x][y])
5825 if (MovDir[x][y] == MV_LEFT)
5827 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5828 DrawLevelField(x - 1, y);
5830 else if (MovDir[x][y] == MV_RIGHT)
5832 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5833 DrawLevelField(x + 1, y);
5835 else if (MovDir[x][y] == MV_UP)
5837 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5838 DrawLevelField(x, y - 1);
5842 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5843 DrawLevelField(x, y + 1);
5846 Feld[x][y] = Store[x][y];
5848 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5849 DrawLevelField(x, y);
5854 void MauerAbleger(int ax, int ay)
5856 int element = Feld[ax][ay];
5857 int graphic = el2img(element);
5858 boolean oben_frei = FALSE, unten_frei = FALSE;
5859 boolean links_frei = FALSE, rechts_frei = FALSE;
5860 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5861 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5862 boolean new_wall = FALSE;
5864 if (IS_ANIMATED(graphic))
5865 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5867 if (!MovDelay[ax][ay]) /* start building new wall */
5868 MovDelay[ax][ay] = 6;
5870 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5873 if (MovDelay[ax][ay])
5877 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5879 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5881 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5883 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5886 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5887 element == EL_EXPANDABLE_WALL_ANY)
5891 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5892 Store[ax][ay-1] = element;
5893 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5894 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5895 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5896 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5901 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5902 Store[ax][ay+1] = element;
5903 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5904 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5905 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5906 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5911 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5912 element == EL_EXPANDABLE_WALL_ANY ||
5913 element == EL_EXPANDABLE_WALL)
5917 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5918 Store[ax-1][ay] = element;
5919 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5920 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5921 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5922 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5928 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5929 Store[ax+1][ay] = element;
5930 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5931 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5932 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5933 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5938 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5939 DrawLevelField(ax, ay);
5941 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5943 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5944 unten_massiv = TRUE;
5945 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5946 links_massiv = TRUE;
5947 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5948 rechts_massiv = TRUE;
5950 if (((oben_massiv && unten_massiv) ||
5951 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5952 element == EL_EXPANDABLE_WALL) &&
5953 ((links_massiv && rechts_massiv) ||
5954 element == EL_EXPANDABLE_WALL_VERTICAL))
5955 Feld[ax][ay] = EL_WALL;
5959 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5961 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5965 void CheckForDragon(int x, int y)
5968 boolean dragon_found = FALSE;
5969 static int xy[4][2] =
5977 for (i = 0; i < 4; i++)
5979 for (j = 0; j < 4; j++)
5981 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5983 if (IN_LEV_FIELD(xx, yy) &&
5984 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5986 if (Feld[xx][yy] == EL_DRAGON)
5987 dragon_found = TRUE;
5996 for (i = 0; i < 4; i++)
5998 for (j = 0; j < 3; j++)
6000 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6002 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6004 Feld[xx][yy] = EL_EMPTY;
6005 DrawLevelField(xx, yy);
6014 static void InitBuggyBase(int x, int y)
6016 int element = Feld[x][y];
6017 int activating_delay = FRAMES_PER_SECOND / 4;
6020 (element == EL_SP_BUGGY_BASE ?
6021 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6022 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6024 element == EL_SP_BUGGY_BASE_ACTIVE ?
6025 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6028 static void WarnBuggyBase(int x, int y)
6031 static int xy[4][2] =
6039 for (i = 0; i < 4; i++)
6041 int xx = x + xy[i][0], yy = y + xy[i][1];
6043 if (IS_PLAYER(xx, yy))
6045 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6052 static void InitTrap(int x, int y)
6054 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6057 static void ActivateTrap(int x, int y)
6059 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6062 static void ChangeActiveTrap(int x, int y)
6064 int graphic = IMG_TRAP_ACTIVE;
6066 /* if new animation frame was drawn, correct crumbled sand border */
6067 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6068 DrawLevelFieldCrumbledSand(x, y);
6071 static void ChangeElementNowExt(int x, int y, int target_element)
6073 /* check if element under player changes from accessible to unaccessible
6074 (needed for special case of dropping element which then changes) */
6075 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6076 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6083 Feld[x][y] = target_element;
6085 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6087 ResetGfxAnimation(x, y);
6088 ResetRandomAnimationValue(x, y);
6090 InitField(x, y, FALSE);
6091 if (CAN_MOVE(Feld[x][y]))
6094 DrawLevelField(x, y);
6096 if (GFX_CRUMBLED(Feld[x][y]))
6097 DrawLevelFieldCrumbledSandNeighbours(x, y);
6099 TestIfBadThingTouchesHero(x, y);
6100 TestIfPlayerTouchesCustomElement(x, y);
6101 TestIfElementTouchesCustomElement(x, y);
6103 if (ELEM_IS_PLAYER(target_element))
6104 RelocatePlayer(x, y, target_element);
6107 static boolean ChangeElementNow(int x, int y, int element, int page)
6109 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6111 /* always use default change event to prevent running into a loop */
6112 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6113 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6115 /* do not change already changed elements with same change event */
6117 if (Changed[x][y] & ChangeEvent[x][y])
6124 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6126 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6128 if (change->explode)
6135 if (change->use_content)
6137 boolean complete_change = TRUE;
6138 boolean can_change[3][3];
6141 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6143 boolean half_destructible;
6144 int ex = x + xx - 1;
6145 int ey = y + yy - 1;
6148 can_change[xx][yy] = TRUE;
6150 if (ex == x && ey == y) /* do not check changing element itself */
6153 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6155 can_change[xx][yy] = FALSE; /* do not change empty borders */
6160 if (!IN_LEV_FIELD(ex, ey))
6162 can_change[xx][yy] = FALSE;
6163 complete_change = FALSE;
6170 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6171 e = MovingOrBlocked2Element(ex, ey);
6173 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6175 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6176 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6177 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6179 can_change[xx][yy] = FALSE;
6180 complete_change = FALSE;
6184 if (!change->only_complete || complete_change)
6186 boolean something_has_changed = FALSE;
6188 if (change->only_complete && change->use_random_change &&
6189 RND(100) < change->random)
6192 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6194 int ex = x + xx - 1;
6195 int ey = y + yy - 1;
6197 if (can_change[xx][yy] && (!change->use_random_change ||
6198 RND(100) < change->random))
6200 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6201 RemoveMovingField(ex, ey);
6203 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6205 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6207 something_has_changed = TRUE;
6209 /* for symmetry reasons, freeze newly created border elements */
6210 if (ex != x || ey != y)
6211 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6215 if (something_has_changed)
6216 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6221 ChangeElementNowExt(x, y, change->target_element);
6223 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6229 static void ChangeElement(int x, int y, int page)
6231 int element = MovingOrBlocked2Element(x, y);
6232 struct ElementInfo *ei = &element_info[element];
6233 struct ElementChangeInfo *change = &ei->change_page[page];
6237 if (!CAN_CHANGE(element))
6240 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6241 x, y, element, element_info[element].token_name);
6242 printf("ChangeElement(): This should never happen!\n");
6248 if (ChangeDelay[x][y] == 0) /* initialize element change */
6250 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6251 RND(change->delay_random * change->delay_frames)) + 1;
6253 ResetGfxAnimation(x, y);
6254 ResetRandomAnimationValue(x, y);
6256 if (change->pre_change_function)
6257 change->pre_change_function(x, y);
6260 ChangeDelay[x][y]--;
6262 if (ChangeDelay[x][y] != 0) /* continue element change */
6264 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6266 if (IS_ANIMATED(graphic))
6267 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6269 if (change->change_function)
6270 change->change_function(x, y);
6272 else /* finish element change */
6274 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6276 page = ChangePage[x][y];
6277 ChangePage[x][y] = -1;
6281 if (IS_MOVING(x, y) && !change->explode)
6283 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6286 ChangeDelay[x][y] = 1; /* try change after next move step */
6287 ChangePage[x][y] = page; /* remember page to use for change */
6292 if (ChangeElementNow(x, y, element, page))
6294 if (change->post_change_function)
6295 change->post_change_function(x, y);
6300 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6301 int trigger_element,
6307 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6310 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6312 int element = EL_CUSTOM_START + i;
6314 boolean change_element = FALSE;
6317 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6320 for (j = 0; j < element_info[element].num_change_pages; j++)
6322 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6324 if (change->can_change &&
6326 change->events & CH_EVENT_BIT(trigger_event) &&
6328 change->sides & trigger_side &&
6329 change->trigger_element == trigger_element)
6332 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6333 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6334 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6337 change_element = TRUE;
6344 if (!change_element)
6347 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6350 if (x == lx && y == ly) /* do not change trigger element itself */
6354 if (Feld[x][y] == element)
6356 ChangeDelay[x][y] = 1;
6357 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6358 ChangeElement(x, y, page);
6366 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6369 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6373 static boolean CheckElementSideChange(int x, int y, int element, int side,
6374 int trigger_event, int page)
6376 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6379 if (Feld[x][y] == EL_BLOCKED)
6381 Blocked2Moving(x, y, &x, &y);
6382 element = Feld[x][y];
6386 page = element_info[element].event_page_nr[trigger_event];
6388 if (!(element_info[element].change_page[page].sides & side))
6391 ChangeDelay[x][y] = 1;
6392 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6393 ChangeElement(x, y, page);
6398 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6400 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6403 static void PlayPlayerSound(struct PlayerInfo *player)
6405 int jx = player->jx, jy = player->jy;
6406 int element = player->element_nr;
6407 int last_action = player->last_action_waiting;
6408 int action = player->action_waiting;
6410 if (player->is_waiting)
6412 if (action != last_action)
6413 PlayLevelSoundElementAction(jx, jy, element, action);
6415 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6419 if (action != last_action)
6420 StopSound(element_info[element].sound[last_action]);
6422 if (last_action == ACTION_SLEEPING)
6423 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6427 static void PlayAllPlayersSound()
6431 for (i = 0; i < MAX_PLAYERS; i++)
6432 if (stored_player[i].active)
6433 PlayPlayerSound(&stored_player[i]);
6436 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6438 boolean last_waiting = player->is_waiting;
6439 int move_dir = player->MovDir;
6441 player->last_action_waiting = player->action_waiting;
6445 if (!last_waiting) /* not waiting -> waiting */
6447 player->is_waiting = TRUE;
6449 player->frame_counter_bored =
6451 game.player_boring_delay_fixed +
6452 SimpleRND(game.player_boring_delay_random);
6453 player->frame_counter_sleeping =
6455 game.player_sleeping_delay_fixed +
6456 SimpleRND(game.player_sleeping_delay_random);
6458 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6461 if (game.player_sleeping_delay_fixed +
6462 game.player_sleeping_delay_random > 0 &&
6463 player->anim_delay_counter == 0 &&
6464 player->post_delay_counter == 0 &&
6465 FrameCounter >= player->frame_counter_sleeping)
6466 player->is_sleeping = TRUE;
6467 else if (game.player_boring_delay_fixed +
6468 game.player_boring_delay_random > 0 &&
6469 FrameCounter >= player->frame_counter_bored)
6470 player->is_bored = TRUE;
6472 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6473 player->is_bored ? ACTION_BORING :
6476 if (player->is_sleeping)
6478 if (player->num_special_action_sleeping > 0)
6480 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6482 int last_special_action = player->special_action_sleeping;
6483 int num_special_action = player->num_special_action_sleeping;
6484 int special_action =
6485 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6486 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6487 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6488 last_special_action + 1 : ACTION_SLEEPING);
6489 int special_graphic =
6490 el_act_dir2img(player->element_nr, special_action, move_dir);
6492 player->anim_delay_counter =
6493 graphic_info[special_graphic].anim_delay_fixed +
6494 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6495 player->post_delay_counter =
6496 graphic_info[special_graphic].post_delay_fixed +
6497 SimpleRND(graphic_info[special_graphic].post_delay_random);
6499 player->special_action_sleeping = special_action;
6502 if (player->anim_delay_counter > 0)
6504 player->action_waiting = player->special_action_sleeping;
6505 player->anim_delay_counter--;
6507 else if (player->post_delay_counter > 0)
6509 player->post_delay_counter--;
6513 else if (player->is_bored)
6515 if (player->num_special_action_bored > 0)
6517 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6519 int special_action =
6520 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6521 int special_graphic =
6522 el_act_dir2img(player->element_nr, special_action, move_dir);
6524 player->anim_delay_counter =
6525 graphic_info[special_graphic].anim_delay_fixed +
6526 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6527 player->post_delay_counter =
6528 graphic_info[special_graphic].post_delay_fixed +
6529 SimpleRND(graphic_info[special_graphic].post_delay_random);
6531 player->special_action_bored = special_action;
6534 if (player->anim_delay_counter > 0)
6536 player->action_waiting = player->special_action_bored;
6537 player->anim_delay_counter--;
6539 else if (player->post_delay_counter > 0)
6541 player->post_delay_counter--;
6546 else if (last_waiting) /* waiting -> not waiting */
6548 player->is_waiting = FALSE;
6549 player->is_bored = FALSE;
6550 player->is_sleeping = FALSE;
6552 player->frame_counter_bored = -1;
6553 player->frame_counter_sleeping = -1;
6555 player->anim_delay_counter = 0;
6556 player->post_delay_counter = 0;
6558 player->action_waiting = ACTION_DEFAULT;
6560 player->special_action_bored = ACTION_DEFAULT;
6561 player->special_action_sleeping = ACTION_DEFAULT;
6566 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6569 static byte stored_player_action[MAX_PLAYERS];
6570 static int num_stored_actions = 0;
6572 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6573 int left = player_action & JOY_LEFT;
6574 int right = player_action & JOY_RIGHT;
6575 int up = player_action & JOY_UP;
6576 int down = player_action & JOY_DOWN;
6577 int button1 = player_action & JOY_BUTTON_1;
6578 int button2 = player_action & JOY_BUTTON_2;
6579 int dx = (left ? -1 : right ? 1 : 0);
6580 int dy = (up ? -1 : down ? 1 : 0);
6583 stored_player_action[player->index_nr] = 0;
6584 num_stored_actions++;
6588 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6591 if (!player->active || tape.pausing)
6595 printf("::: [%d %d %d %d] [%d %d]\n",
6596 left, right, up, down, button1, button2);
6602 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6606 snapped = SnapField(player, dx, dy);
6610 dropped = DropElement(player);
6612 moved = MovePlayer(player, dx, dy);
6615 if (tape.single_step && tape.recording && !tape.pausing)
6617 if (button1 || (dropped && !moved))
6619 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6620 SnapField(player, 0, 0); /* stop snapping */
6624 SetPlayerWaiting(player, FALSE);
6627 return player_action;
6629 stored_player_action[player->index_nr] = player_action;
6635 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6638 /* no actions for this player (no input at player's configured device) */
6640 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6641 SnapField(player, 0, 0);
6642 CheckGravityMovement(player);
6644 if (player->MovPos == 0)
6645 SetPlayerWaiting(player, TRUE);
6647 if (player->MovPos == 0) /* needed for tape.playing */
6648 player->is_moving = FALSE;
6650 player->is_dropping = FALSE;
6656 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6658 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6660 TapeRecordAction(stored_player_action);
6661 num_stored_actions = 0;
6668 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6670 static byte stored_player_action[MAX_PLAYERS];
6671 static int num_stored_actions = 0;
6672 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6673 int left = player_action & JOY_LEFT;
6674 int right = player_action & JOY_RIGHT;
6675 int up = player_action & JOY_UP;
6676 int down = player_action & JOY_DOWN;
6677 int button1 = player_action & JOY_BUTTON_1;
6678 int button2 = player_action & JOY_BUTTON_2;
6679 int dx = (left ? -1 : right ? 1 : 0);
6680 int dy = (up ? -1 : down ? 1 : 0);
6682 stored_player_action[player->index_nr] = 0;
6683 num_stored_actions++;
6685 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6687 if (!player->active || tape.pausing)
6692 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6695 snapped = SnapField(player, dx, dy);
6699 dropped = DropElement(player);
6701 moved = MovePlayer(player, dx, dy);
6704 if (tape.single_step && tape.recording && !tape.pausing)
6706 if (button1 || (dropped && !moved))
6708 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6709 SnapField(player, 0, 0); /* stop snapping */
6713 stored_player_action[player->index_nr] = player_action;
6717 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6719 /* no actions for this player (no input at player's configured device) */
6721 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6722 SnapField(player, 0, 0);
6723 CheckGravityMovement(player);
6725 if (player->MovPos == 0)
6726 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6728 if (player->MovPos == 0) /* needed for tape.playing */
6729 player->is_moving = FALSE;
6732 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6734 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6736 TapeRecordAction(stored_player_action);
6737 num_stored_actions = 0;
6744 static unsigned long action_delay = 0;
6745 unsigned long action_delay_value;
6746 int magic_wall_x = 0, magic_wall_y = 0;
6747 int i, x, y, element, graphic;
6748 byte *recorded_player_action;
6749 byte summarized_player_action = 0;
6751 byte tape_action[MAX_PLAYERS];
6754 if (game_status != GAME_MODE_PLAYING)
6757 action_delay_value =
6758 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6760 if (tape.playing && tape.index_search && !tape.pausing)
6761 action_delay_value = 0;
6763 /* ---------- main game synchronization point ---------- */
6765 WaitUntilDelayReached(&action_delay, action_delay_value);
6767 if (network_playing && !network_player_action_received)
6771 printf("DEBUG: try to get network player actions in time\n");
6775 #if defined(PLATFORM_UNIX)
6776 /* last chance to get network player actions without main loop delay */
6780 if (game_status != GAME_MODE_PLAYING)
6783 if (!network_player_action_received)
6787 printf("DEBUG: failed to get network player actions in time\n");
6798 printf("::: getting new tape action [%d]\n", FrameCounter);
6801 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6803 for (i = 0; i < MAX_PLAYERS; i++)
6805 summarized_player_action |= stored_player[i].action;
6807 if (!network_playing)
6808 stored_player[i].effective_action = stored_player[i].action;
6811 #if defined(PLATFORM_UNIX)
6812 if (network_playing)
6813 SendToServer_MovePlayer(summarized_player_action);
6816 if (!options.network && !setup.team_mode)
6817 local_player->effective_action = summarized_player_action;
6819 for (i = 0; i < MAX_PLAYERS; i++)
6821 int actual_player_action = stored_player[i].effective_action;
6823 if (stored_player[i].programmed_action)
6824 actual_player_action = stored_player[i].programmed_action;
6826 if (recorded_player_action)
6827 actual_player_action = recorded_player_action[i];
6829 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6831 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6832 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6834 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6839 TapeRecordAction(tape_action);
6842 network_player_action_received = FALSE;
6844 ScrollScreen(NULL, SCROLL_GO_ON);
6850 for (i = 0; i < MAX_PLAYERS; i++)
6851 stored_player[i].Frame++;
6855 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6857 for (i = 0; i < MAX_PLAYERS; i++)
6859 struct PlayerInfo *player = &stored_player[i];
6863 if (player->active && player->is_pushing && player->is_moving &&
6866 ContinueMoving(x, y);
6868 /* continue moving after pushing (this is actually a bug) */
6869 if (!IS_MOVING(x, y))
6878 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6880 Changed[x][y] = CE_BITMASK_DEFAULT;
6881 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6884 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6886 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6887 printf("GameActions(): This should never happen!\n");
6889 ChangePage[x][y] = -1;
6894 if (WasJustMoving[x][y] > 0)
6895 WasJustMoving[x][y]--;
6896 if (WasJustFalling[x][y] > 0)
6897 WasJustFalling[x][y]--;
6902 /* reset finished pushing action (not done in ContinueMoving() to allow
6903 continous pushing animation for elements with zero push delay) */
6904 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6906 ResetGfxAnimation(x, y);
6907 DrawLevelField(x, y);
6912 if (IS_BLOCKED(x, y))
6916 Blocked2Moving(x, y, &oldx, &oldy);
6917 if (!IS_MOVING(oldx, oldy))
6919 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6920 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6921 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6922 printf("GameActions(): This should never happen!\n");
6928 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6930 element = Feld[x][y];
6932 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6934 graphic = el2img(element);
6940 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6942 element = graphic = 0;
6946 if (graphic_info[graphic].anim_global_sync)
6947 GfxFrame[x][y] = FrameCounter;
6949 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6950 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6951 ResetRandomAnimationValue(x, y);
6953 SetRandomAnimationValue(x, y);
6956 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6959 if (IS_INACTIVE(element))
6961 if (IS_ANIMATED(graphic))
6962 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6968 /* this may take place after moving, so 'element' may have changed */
6970 if (IS_CHANGING(x, y))
6972 if (IS_CHANGING(x, y) &&
6973 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6977 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6978 element_info[element].event_page_nr[CE_DELAY]);
6980 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6983 element = Feld[x][y];
6984 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6988 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6993 element = Feld[x][y];
6994 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6996 if (element == EL_MOLE)
6997 printf("::: %d, %d, %d [%d]\n",
6998 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7002 if (element == EL_YAMYAM)
7003 printf("::: %d, %d, %d\n",
7004 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7008 if (IS_ANIMATED(graphic) &&
7012 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7015 if (element == EL_BUG)
7016 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7020 if (element == EL_MOLE)
7021 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7025 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7026 EdelsteinFunkeln(x, y);
7028 else if ((element == EL_ACID ||
7029 element == EL_EXIT_OPEN ||
7030 element == EL_SP_EXIT_OPEN ||
7031 element == EL_SP_TERMINAL ||
7032 element == EL_SP_TERMINAL_ACTIVE ||
7033 element == EL_EXTRA_TIME ||
7034 element == EL_SHIELD_NORMAL ||
7035 element == EL_SHIELD_DEADLY) &&
7036 IS_ANIMATED(graphic))
7037 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7038 else if (IS_MOVING(x, y))
7039 ContinueMoving(x, y);
7040 else if (IS_ACTIVE_BOMB(element))
7041 CheckDynamite(x, y);
7043 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7044 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7046 else if (element == EL_AMOEBA_GROWING)
7047 AmoebeWaechst(x, y);
7048 else if (element == EL_AMOEBA_SHRINKING)
7049 AmoebaDisappearing(x, y);
7051 #if !USE_NEW_AMOEBA_CODE
7052 else if (IS_AMOEBALIVE(element))
7053 AmoebeAbleger(x, y);
7056 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7058 else if (element == EL_EXIT_CLOSED)
7060 else if (element == EL_SP_EXIT_CLOSED)
7062 else if (element == EL_EXPANDABLE_WALL_GROWING)
7064 else if (element == EL_EXPANDABLE_WALL ||
7065 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7066 element == EL_EXPANDABLE_WALL_VERTICAL ||
7067 element == EL_EXPANDABLE_WALL_ANY)
7069 else if (element == EL_FLAMES)
7070 CheckForDragon(x, y);
7072 else if (IS_AUTO_CHANGING(element))
7073 ChangeElement(x, y);
7075 else if (element == EL_EXPLOSION)
7076 ; /* drawing of correct explosion animation is handled separately */
7077 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7078 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7081 /* this may take place after moving, so 'element' may have changed */
7082 if (IS_AUTO_CHANGING(Feld[x][y]))
7083 ChangeElement(x, y);
7086 if (IS_BELT_ACTIVE(element))
7087 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7089 if (game.magic_wall_active)
7091 int jx = local_player->jx, jy = local_player->jy;
7093 /* play the element sound at the position nearest to the player */
7094 if ((element == EL_MAGIC_WALL_FULL ||
7095 element == EL_MAGIC_WALL_ACTIVE ||
7096 element == EL_MAGIC_WALL_EMPTYING ||
7097 element == EL_BD_MAGIC_WALL_FULL ||
7098 element == EL_BD_MAGIC_WALL_ACTIVE ||
7099 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7100 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7108 #if USE_NEW_AMOEBA_CODE
7109 /* new experimental amoeba growth stuff */
7111 if (!(FrameCounter % 8))
7114 static unsigned long random = 1684108901;
7116 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7119 x = (random >> 10) % lev_fieldx;
7120 y = (random >> 20) % lev_fieldy;
7122 x = RND(lev_fieldx);
7123 y = RND(lev_fieldy);
7125 element = Feld[x][y];
7127 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7128 if (!IS_PLAYER(x,y) &&
7129 (element == EL_EMPTY ||
7130 element == EL_SAND ||
7131 element == EL_QUICKSAND_EMPTY ||
7132 element == EL_ACID_SPLASH_LEFT ||
7133 element == EL_ACID_SPLASH_RIGHT))
7135 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7136 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7137 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7138 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7139 Feld[x][y] = EL_AMOEBA_DROP;
7142 random = random * 129 + 1;
7148 if (game.explosions_delayed)
7151 game.explosions_delayed = FALSE;
7153 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7155 element = Feld[x][y];
7157 if (ExplodeField[x][y])
7158 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7159 else if (element == EL_EXPLOSION)
7160 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7162 ExplodeField[x][y] = EX_NO_EXPLOSION;
7165 game.explosions_delayed = TRUE;
7168 if (game.magic_wall_active)
7170 if (!(game.magic_wall_time_left % 4))
7172 int element = Feld[magic_wall_x][magic_wall_y];
7174 if (element == EL_BD_MAGIC_WALL_FULL ||
7175 element == EL_BD_MAGIC_WALL_ACTIVE ||
7176 element == EL_BD_MAGIC_WALL_EMPTYING)
7177 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7179 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7182 if (game.magic_wall_time_left > 0)
7184 game.magic_wall_time_left--;
7185 if (!game.magic_wall_time_left)
7187 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7189 element = Feld[x][y];
7191 if (element == EL_MAGIC_WALL_ACTIVE ||
7192 element == EL_MAGIC_WALL_FULL)
7194 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7195 DrawLevelField(x, y);
7197 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7198 element == EL_BD_MAGIC_WALL_FULL)
7200 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7201 DrawLevelField(x, y);
7205 game.magic_wall_active = FALSE;
7210 if (game.light_time_left > 0)
7212 game.light_time_left--;
7214 if (game.light_time_left == 0)
7215 RedrawAllLightSwitchesAndInvisibleElements();
7218 if (game.timegate_time_left > 0)
7220 game.timegate_time_left--;
7222 if (game.timegate_time_left == 0)
7223 CloseAllOpenTimegates();
7226 for (i = 0; i < MAX_PLAYERS; i++)
7228 struct PlayerInfo *player = &stored_player[i];
7230 if (SHIELD_ON(player))
7232 if (player->shield_deadly_time_left)
7233 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7234 else if (player->shield_normal_time_left)
7235 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7239 if (TimeFrames >= FRAMES_PER_SECOND)
7244 for (i = 0; i < MAX_PLAYERS; i++)
7246 struct PlayerInfo *player = &stored_player[i];
7248 if (SHIELD_ON(player))
7250 player->shield_normal_time_left--;
7252 if (player->shield_deadly_time_left > 0)
7253 player->shield_deadly_time_left--;
7257 if (tape.recording || tape.playing)
7258 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7264 if (TimeLeft <= 10 && setup.time_limit)
7265 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7267 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7269 if (!TimeLeft && setup.time_limit)
7270 for (i = 0; i < MAX_PLAYERS; i++)
7271 KillHero(&stored_player[i]);
7273 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7274 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7278 PlayAllPlayersSound();
7280 if (options.debug) /* calculate frames per second */
7282 static unsigned long fps_counter = 0;
7283 static int fps_frames = 0;
7284 unsigned long fps_delay_ms = Counter() - fps_counter;
7288 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7290 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7293 fps_counter = Counter();
7296 redraw_mask |= REDRAW_FPS;
7300 if (stored_player[0].jx != stored_player[0].last_jx ||
7301 stored_player[0].jy != stored_player[0].last_jy)
7302 printf("::: %d, %d, %d, %d, %d\n",
7303 stored_player[0].MovDir,
7304 stored_player[0].MovPos,
7305 stored_player[0].GfxPos,
7306 stored_player[0].Frame,
7307 stored_player[0].StepFrame);
7314 for (i = 0; i < MAX_PLAYERS; i++)
7317 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7319 stored_player[i].Frame += move_frames;
7321 if (stored_player[i].MovPos != 0)
7322 stored_player[i].StepFrame += move_frames;
7324 if (stored_player[i].drop_delay > 0)
7325 stored_player[i].drop_delay--;
7330 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7332 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7334 local_player->show_envelope = 0;
7339 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7341 int min_x = x, min_y = y, max_x = x, max_y = y;
7344 for (i = 0; i < MAX_PLAYERS; i++)
7346 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7348 if (!stored_player[i].active || &stored_player[i] == player)
7351 min_x = MIN(min_x, jx);
7352 min_y = MIN(min_y, jy);
7353 max_x = MAX(max_x, jx);
7354 max_y = MAX(max_y, jy);
7357 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7360 static boolean AllPlayersInVisibleScreen()
7364 for (i = 0; i < MAX_PLAYERS; i++)
7366 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7368 if (!stored_player[i].active)
7371 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7378 void ScrollLevel(int dx, int dy)
7380 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7383 BlitBitmap(drawto_field, drawto_field,
7384 FX + TILEX * (dx == -1) - softscroll_offset,
7385 FY + TILEY * (dy == -1) - softscroll_offset,
7386 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7387 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7388 FX + TILEX * (dx == 1) - softscroll_offset,
7389 FY + TILEY * (dy == 1) - softscroll_offset);
7393 x = (dx == 1 ? BX1 : BX2);
7394 for (y = BY1; y <= BY2; y++)
7395 DrawScreenField(x, y);
7400 y = (dy == 1 ? BY1 : BY2);
7401 for (x = BX1; x <= BX2; x++)
7402 DrawScreenField(x, y);
7405 redraw_mask |= REDRAW_FIELD;
7408 static void CheckGravityMovement(struct PlayerInfo *player)
7410 if (game.gravity && !player->programmed_action)
7412 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7413 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7415 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7416 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7417 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7418 int jx = player->jx, jy = player->jy;
7419 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7420 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7421 int new_jx = jx + dx, new_jy = jy + dy;
7422 boolean field_under_player_is_free =
7423 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7424 boolean player_is_moving_to_valid_field =
7425 (IN_LEV_FIELD(new_jx, new_jy) &&
7426 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7427 Feld[new_jx][new_jy] == EL_SAND));
7428 /* !!! extend EL_SAND to anything diggable !!! */
7430 if (field_under_player_is_free &&
7431 !player_is_moving_to_valid_field &&
7432 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7433 player->programmed_action = MV_DOWN;
7439 -----------------------------------------------------------------------------
7440 dx, dy: direction (non-diagonal) to try to move the player to
7441 real_dx, real_dy: direction as read from input device (can be diagonal)
7444 boolean MovePlayerOneStep(struct PlayerInfo *player,
7445 int dx, int dy, int real_dx, int real_dy)
7448 static int change_sides[4][2] =
7450 /* enter side leave side */
7451 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7452 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7453 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7454 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7456 int move_direction = (dx == -1 ? MV_LEFT :
7457 dx == +1 ? MV_RIGHT :
7459 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7460 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7461 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7463 int jx = player->jx, jy = player->jy;
7464 int new_jx = jx + dx, new_jy = jy + dy;
7468 if (!player->active || (!dx && !dy))
7469 return MF_NO_ACTION;
7471 player->MovDir = (dx < 0 ? MV_LEFT :
7474 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7476 if (!IN_LEV_FIELD(new_jx, new_jy))
7477 return MF_NO_ACTION;
7479 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7480 return MF_NO_ACTION;
7483 element = MovingOrBlocked2Element(new_jx, new_jy);
7485 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7488 if (DONT_RUN_INTO(element))
7490 if (element == EL_ACID && dx == 0 && dy == 1)
7493 Feld[jx][jy] = EL_PLAYER_1;
7494 InitMovingField(jx, jy, MV_DOWN);
7495 Store[jx][jy] = EL_ACID;
7496 ContinueMoving(jx, jy);
7500 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7505 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7506 if (can_move != MF_MOVING)
7509 /* check if DigField() has caused relocation of the player */
7510 if (player->jx != jx || player->jy != jy)
7511 return MF_NO_ACTION;
7513 StorePlayer[jx][jy] = 0;
7514 player->last_jx = jx;
7515 player->last_jy = jy;
7516 player->jx = new_jx;
7517 player->jy = new_jy;
7518 StorePlayer[new_jx][new_jy] = player->element_nr;
7521 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7523 player->step_counter++;
7525 player->drop_delay = 0;
7527 PlayerVisit[jx][jy] = FrameCounter;
7529 ScrollPlayer(player, SCROLL_INIT);
7532 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7534 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7535 CE_OTHER_GETS_LEFT);
7536 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7537 CE_LEFT_BY_PLAYER, -1);
7540 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7542 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7543 enter_side, CE_OTHER_GETS_ENTERED);
7544 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7545 CE_ENTERED_BY_PLAYER, -1);
7552 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7554 int jx = player->jx, jy = player->jy;
7555 int old_jx = jx, old_jy = jy;
7556 int moved = MF_NO_ACTION;
7559 if (!player->active)
7564 if (player->MovPos == 0)
7566 player->is_moving = FALSE;
7567 player->is_digging = FALSE;
7568 player->is_collecting = FALSE;
7569 player->is_snapping = FALSE;
7570 player->is_pushing = FALSE;
7576 if (!player->active || (!dx && !dy))
7581 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7585 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7586 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7590 /* remove the last programmed player action */
7591 player->programmed_action = 0;
7595 /* should only happen if pre-1.2 tape recordings are played */
7596 /* this is only for backward compatibility */
7598 int original_move_delay_value = player->move_delay_value;
7601 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7605 /* scroll remaining steps with finest movement resolution */
7606 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7608 while (player->MovPos)
7610 ScrollPlayer(player, SCROLL_GO_ON);
7611 ScrollScreen(NULL, SCROLL_GO_ON);
7617 player->move_delay_value = original_move_delay_value;
7620 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7622 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7623 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7627 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7628 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7634 if (moved & MF_MOVING && !ScreenMovPos &&
7635 (player == local_player || !options.network))
7637 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7638 int offset = (setup.scroll_delay ? 3 : 0);
7640 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7642 /* actual player has left the screen -- scroll in that direction */
7643 if (jx != old_jx) /* player has moved horizontally */
7644 scroll_x += (jx - old_jx);
7645 else /* player has moved vertically */
7646 scroll_y += (jy - old_jy);
7650 if (jx != old_jx) /* player has moved horizontally */
7652 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7653 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7654 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7656 /* don't scroll over playfield boundaries */
7657 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7658 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7660 /* don't scroll more than one field at a time */
7661 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7663 /* don't scroll against the player's moving direction */
7664 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7665 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7666 scroll_x = old_scroll_x;
7668 else /* player has moved vertically */
7670 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7671 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7672 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7674 /* don't scroll over playfield boundaries */
7675 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7676 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7678 /* don't scroll more than one field at a time */
7679 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7681 /* don't scroll against the player's moving direction */
7682 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7683 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7684 scroll_y = old_scroll_y;
7688 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7690 if (!options.network && !AllPlayersInVisibleScreen())
7692 scroll_x = old_scroll_x;
7693 scroll_y = old_scroll_y;
7697 ScrollScreen(player, SCROLL_INIT);
7698 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7705 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7707 if (!(moved & MF_MOVING) && !player->is_pushing)
7712 player->StepFrame = 0;
7714 if (moved & MF_MOVING)
7716 if (old_jx != jx && old_jy == jy)
7717 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7718 else if (old_jx == jx && old_jy != jy)
7719 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7721 DrawLevelField(jx, jy); /* for "crumbled sand" */
7723 player->last_move_dir = player->MovDir;
7724 player->is_moving = TRUE;
7726 player->is_snapping = FALSE;
7730 player->is_switching = FALSE;
7733 player->is_dropping = FALSE;
7738 static int change_sides[4][2] =
7740 /* enter side leave side */
7741 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7742 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7743 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7744 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7746 int move_direction = player->MovDir;
7747 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7748 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7751 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7753 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7754 leave_side, CE_OTHER_GETS_LEFT);
7755 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7756 leave_side, CE_LEFT_BY_PLAYER, -1);
7759 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7761 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7762 enter_side, CE_OTHER_GETS_ENTERED);
7763 CheckElementSideChange(jx, jy, Feld[jx][jy],
7764 enter_side, CE_ENTERED_BY_PLAYER, -1);
7775 CheckGravityMovement(player);
7778 player->last_move_dir = MV_NO_MOVING;
7780 player->is_moving = FALSE;
7783 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7785 TestIfHeroTouchesBadThing(jx, jy);
7786 TestIfPlayerTouchesCustomElement(jx, jy);
7789 if (!player->active)
7795 void ScrollPlayer(struct PlayerInfo *player, int mode)
7797 int jx = player->jx, jy = player->jy;
7798 int last_jx = player->last_jx, last_jy = player->last_jy;
7799 int move_stepsize = TILEX / player->move_delay_value;
7801 if (!player->active || !player->MovPos)
7804 if (mode == SCROLL_INIT)
7806 player->actual_frame_counter = FrameCounter;
7807 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7809 if (Feld[last_jx][last_jy] == EL_EMPTY)
7810 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7817 else if (!FrameReached(&player->actual_frame_counter, 1))
7820 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7821 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7823 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7824 Feld[last_jx][last_jy] = EL_EMPTY;
7826 /* before DrawPlayer() to draw correct player graphic for this case */
7827 if (player->MovPos == 0)
7828 CheckGravityMovement(player);
7831 DrawPlayer(player); /* needed here only to cleanup last field */
7834 if (player->MovPos == 0) /* player reached destination field */
7837 if (player->move_delay_reset_counter > 0)
7839 player->move_delay_reset_counter--;
7841 if (player->move_delay_reset_counter == 0)
7843 /* continue with normal speed after quickly moving through gate */
7844 HALVE_PLAYER_SPEED(player);
7846 /* be able to make the next move without delay */
7847 player->move_delay = 0;
7851 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7853 /* continue with normal speed after quickly moving through gate */
7854 HALVE_PLAYER_SPEED(player);
7856 /* be able to make the next move without delay */
7857 player->move_delay = 0;
7861 player->last_jx = jx;
7862 player->last_jy = jy;
7864 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7865 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7866 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7868 DrawPlayer(player); /* needed here only to cleanup last field */
7871 if (local_player->friends_still_needed == 0 ||
7872 IS_SP_ELEMENT(Feld[jx][jy]))
7873 player->LevelSolved = player->GameOver = TRUE;
7876 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7878 TestIfHeroTouchesBadThing(jx, jy);
7879 TestIfPlayerTouchesCustomElement(jx, jy);
7881 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7884 if (!player->active)
7888 if (tape.single_step && tape.recording && !tape.pausing &&
7889 !player->programmed_action)
7890 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7894 void ScrollScreen(struct PlayerInfo *player, int mode)
7896 static unsigned long screen_frame_counter = 0;
7898 if (mode == SCROLL_INIT)
7900 /* set scrolling step size according to actual player's moving speed */
7901 ScrollStepSize = TILEX / player->move_delay_value;
7903 screen_frame_counter = FrameCounter;
7904 ScreenMovDir = player->MovDir;
7905 ScreenMovPos = player->MovPos;
7906 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7909 else if (!FrameReached(&screen_frame_counter, 1))
7914 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7915 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7916 redraw_mask |= REDRAW_FIELD;
7919 ScreenMovDir = MV_NO_MOVING;
7922 void TestIfPlayerTouchesCustomElement(int x, int y)
7924 static int xy[4][2] =
7931 static int change_sides[4][2] =
7933 /* center side border side */
7934 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7935 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7936 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7937 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7939 static int touch_dir[4] =
7946 int center_element = Feld[x][y]; /* should always be non-moving! */
7949 for (i = 0; i < 4; i++)
7951 int xx = x + xy[i][0];
7952 int yy = y + xy[i][1];
7953 int center_side = change_sides[i][0];
7954 int border_side = change_sides[i][1];
7957 if (!IN_LEV_FIELD(xx, yy))
7960 if (IS_PLAYER(x, y))
7962 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7963 border_element = Feld[xx][yy]; /* may be moving! */
7964 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7965 border_element = Feld[xx][yy];
7966 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7967 border_element = MovingOrBlocked2Element(xx, yy);
7969 continue; /* center and border element do not touch */
7971 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7972 CE_OTHER_GETS_TOUCHED);
7973 CheckElementSideChange(xx, yy, border_element, border_side,
7974 CE_TOUCHED_BY_PLAYER, -1);
7976 else if (IS_PLAYER(xx, yy))
7978 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7980 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7982 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7983 continue; /* center and border element do not touch */
7986 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7987 CE_OTHER_GETS_TOUCHED);
7988 CheckElementSideChange(x, y, center_element, center_side,
7989 CE_TOUCHED_BY_PLAYER, -1);
7996 void TestIfElementTouchesCustomElement(int x, int y)
7998 static int xy[4][2] =
8005 static int change_sides[4][2] =
8007 /* center side border side */
8008 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8009 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8010 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8011 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8013 static int touch_dir[4] =
8020 boolean change_center_element = FALSE;
8021 int center_element_change_page = 0;
8022 int center_element = Feld[x][y]; /* should always be non-moving! */
8025 for (i = 0; i < 4; i++)
8027 int xx = x + xy[i][0];
8028 int yy = y + xy[i][1];
8029 int center_side = change_sides[i][0];
8030 int border_side = change_sides[i][1];
8033 if (!IN_LEV_FIELD(xx, yy))
8036 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8037 border_element = Feld[xx][yy]; /* may be moving! */
8038 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8039 border_element = Feld[xx][yy];
8040 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8041 border_element = MovingOrBlocked2Element(xx, yy);
8043 continue; /* center and border element do not touch */
8045 /* check for change of center element (but change it only once) */
8046 if (IS_CUSTOM_ELEMENT(center_element) &&
8047 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8048 !change_center_element)
8050 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8052 struct ElementChangeInfo *change =
8053 &element_info[center_element].change_page[j];
8055 if (change->can_change &&
8056 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8057 change->sides & border_side &&
8058 change->trigger_element == border_element)
8060 change_center_element = TRUE;
8061 center_element_change_page = j;
8068 /* check for change of border element */
8069 if (IS_CUSTOM_ELEMENT(border_element) &&
8070 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8072 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8074 struct ElementChangeInfo *change =
8075 &element_info[border_element].change_page[j];
8077 if (change->can_change &&
8078 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8079 change->sides & center_side &&
8080 change->trigger_element == center_element)
8082 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8083 CE_OTHER_IS_TOUCHING, j);
8090 if (change_center_element)
8091 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8092 CE_OTHER_IS_TOUCHING, center_element_change_page);
8095 void TestIfElementHitsCustomElement(int x, int y, int direction)
8097 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8098 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8099 int hitx = x + dx, hity = y + dy;
8100 int hitting_element = Feld[x][y];
8102 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8103 !IS_FREE(hitx, hity) &&
8104 (!IS_MOVING(hitx, hity) ||
8105 MovDir[hitx][hity] != direction ||
8106 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8109 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8113 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8117 CheckElementSideChange(x, y, hitting_element,
8118 direction, CE_HITTING_SOMETHING, -1);
8120 if (IN_LEV_FIELD(hitx, hity))
8122 static int opposite_directions[] =
8129 int move_dir_bit = MV_DIR_BIT(direction);
8130 int opposite_direction = opposite_directions[move_dir_bit];
8131 int hitting_side = direction;
8132 int touched_side = opposite_direction;
8133 int touched_element = MovingOrBlocked2Element(hitx, hity);
8135 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8136 MovDir[hitx][hity] != direction ||
8137 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8146 CheckElementSideChange(hitx, hity, touched_element,
8147 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8149 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8150 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8152 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8154 struct ElementChangeInfo *change =
8155 &element_info[hitting_element].change_page[i];
8157 if (change->can_change &&
8158 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8159 change->sides & touched_side &&
8160 change->trigger_element == touched_element)
8162 CheckElementSideChange(x, y, hitting_element,
8163 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8169 if (IS_CUSTOM_ELEMENT(touched_element) &&
8170 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8172 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8174 struct ElementChangeInfo *change =
8175 &element_info[touched_element].change_page[i];
8177 if (change->can_change &&
8178 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8179 change->sides & hitting_side &&
8180 change->trigger_element == hitting_element)
8182 CheckElementSideChange(hitx, hity, touched_element,
8183 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8192 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8194 int i, kill_x = -1, kill_y = -1;
8195 static int test_xy[4][2] =
8202 static int test_dir[4] =
8210 for (i = 0; i < 4; i++)
8212 int test_x, test_y, test_move_dir, test_element;
8214 test_x = good_x + test_xy[i][0];
8215 test_y = good_y + test_xy[i][1];
8216 if (!IN_LEV_FIELD(test_x, test_y))
8220 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8223 test_element = Feld[test_x][test_y];
8225 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8228 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8229 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8231 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8232 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8240 if (kill_x != -1 || kill_y != -1)
8242 if (IS_PLAYER(good_x, good_y))
8244 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8246 if (player->shield_deadly_time_left > 0)
8247 Bang(kill_x, kill_y);
8248 else if (!PLAYER_PROTECTED(good_x, good_y))
8252 Bang(good_x, good_y);
8256 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8258 int i, kill_x = -1, kill_y = -1;
8259 int bad_element = Feld[bad_x][bad_y];
8260 static int test_xy[4][2] =
8267 static int touch_dir[4] =
8274 static int test_dir[4] =
8282 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8285 for (i = 0; i < 4; i++)
8287 int test_x, test_y, test_move_dir, test_element;
8289 test_x = bad_x + test_xy[i][0];
8290 test_y = bad_y + test_xy[i][1];
8291 if (!IN_LEV_FIELD(test_x, test_y))
8295 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8297 test_element = Feld[test_x][test_y];
8299 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8300 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8302 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8303 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8305 /* good thing is player or penguin that does not move away */
8306 if (IS_PLAYER(test_x, test_y))
8308 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8310 if (bad_element == EL_ROBOT && player->is_moving)
8311 continue; /* robot does not kill player if he is moving */
8313 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8315 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8316 continue; /* center and border element do not touch */
8323 else if (test_element == EL_PENGUIN)
8332 if (kill_x != -1 || kill_y != -1)
8334 if (IS_PLAYER(kill_x, kill_y))
8336 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8338 if (player->shield_deadly_time_left > 0)
8340 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8344 Bang(kill_x, kill_y);
8348 void TestIfHeroTouchesBadThing(int x, int y)
8350 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8353 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8355 TestIfGoodThingHitsBadThing(x, y, move_dir);
8358 void TestIfBadThingTouchesHero(int x, int y)
8360 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8363 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8365 TestIfBadThingHitsGoodThing(x, y, move_dir);
8368 void TestIfFriendTouchesBadThing(int x, int y)
8370 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8373 void TestIfBadThingTouchesFriend(int x, int y)
8375 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8378 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8380 int i, kill_x = bad_x, kill_y = bad_y;
8381 static int xy[4][2] =
8389 for (i = 0; i < 4; i++)
8393 x = bad_x + xy[i][0];
8394 y = bad_y + xy[i][1];
8395 if (!IN_LEV_FIELD(x, y))
8398 element = Feld[x][y];
8399 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8400 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8408 if (kill_x != bad_x || kill_y != bad_y)
8412 void KillHero(struct PlayerInfo *player)
8414 int jx = player->jx, jy = player->jy;
8416 if (!player->active)
8419 /* remove accessible field at the player's position */
8420 Feld[jx][jy] = EL_EMPTY;
8422 /* deactivate shield (else Bang()/Explode() would not work right) */
8423 player->shield_normal_time_left = 0;
8424 player->shield_deadly_time_left = 0;
8430 static void KillHeroUnlessProtected(int x, int y)
8432 if (!PLAYER_PROTECTED(x, y))
8433 KillHero(PLAYERINFO(x, y));
8436 void BuryHero(struct PlayerInfo *player)
8438 int jx = player->jx, jy = player->jy;
8440 if (!player->active)
8444 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8446 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8448 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8450 player->GameOver = TRUE;
8454 void RemoveHero(struct PlayerInfo *player)
8456 int jx = player->jx, jy = player->jy;
8457 int i, found = FALSE;
8459 player->present = FALSE;
8460 player->active = FALSE;
8462 if (!ExplodeField[jx][jy])
8463 StorePlayer[jx][jy] = 0;
8465 for (i = 0; i < MAX_PLAYERS; i++)
8466 if (stored_player[i].active)
8470 AllPlayersGone = TRUE;
8477 =============================================================================
8478 checkDiagonalPushing()
8479 -----------------------------------------------------------------------------
8480 check if diagonal input device direction results in pushing of object
8481 (by checking if the alternative direction is walkable, diggable, ...)
8482 =============================================================================
8485 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8486 int x, int y, int real_dx, int real_dy)
8488 int jx, jy, dx, dy, xx, yy;
8490 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8493 /* diagonal direction: check alternative direction */
8498 xx = jx + (dx == 0 ? real_dx : 0);
8499 yy = jy + (dy == 0 ? real_dy : 0);
8501 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8505 =============================================================================
8507 -----------------------------------------------------------------------------
8508 x, y: field next to player (non-diagonal) to try to dig to
8509 real_dx, real_dy: direction as read from input device (can be diagonal)
8510 =============================================================================
8513 int DigField(struct PlayerInfo *player,
8514 int x, int y, int real_dx, int real_dy, int mode)
8516 static int change_sides[4] =
8518 CH_SIDE_RIGHT, /* moving left */
8519 CH_SIDE_LEFT, /* moving right */
8520 CH_SIDE_BOTTOM, /* moving up */
8521 CH_SIDE_TOP, /* moving down */
8523 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8524 int jx = player->jx, jy = player->jy;
8525 int dx = x - jx, dy = y - jy;
8526 int nextx = x + dx, nexty = y + dy;
8527 int move_direction = (dx == -1 ? MV_LEFT :
8528 dx == +1 ? MV_RIGHT :
8530 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8531 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8534 if (player->MovPos == 0)
8536 player->is_digging = FALSE;
8537 player->is_collecting = FALSE;
8540 if (player->MovPos == 0) /* last pushing move finished */
8541 player->is_pushing = FALSE;
8543 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8545 player->is_switching = FALSE;
8546 player->push_delay = 0;
8548 return MF_NO_ACTION;
8551 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8552 return MF_NO_ACTION;
8555 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8557 if (IS_TUBE(Feld[jx][jy]) ||
8558 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8562 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8563 int tube_leave_directions[][2] =
8565 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8566 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8567 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8568 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8569 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8570 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8571 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8572 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8573 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8574 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8575 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8576 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8579 while (tube_leave_directions[i][0] != tube_element)
8582 if (tube_leave_directions[i][0] == -1) /* should not happen */
8586 if (!(tube_leave_directions[i][1] & move_direction))
8587 return MF_NO_ACTION; /* tube has no opening in this direction */
8590 element = Feld[x][y];
8592 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8593 game.engine_version >= VERSION_IDENT(2,2,0,0))
8594 return MF_NO_ACTION;
8598 case EL_SP_PORT_LEFT:
8599 case EL_SP_PORT_RIGHT:
8601 case EL_SP_PORT_DOWN:
8602 case EL_SP_PORT_HORIZONTAL:
8603 case EL_SP_PORT_VERTICAL:
8604 case EL_SP_PORT_ANY:
8605 case EL_SP_GRAVITY_PORT_LEFT:
8606 case EL_SP_GRAVITY_PORT_RIGHT:
8607 case EL_SP_GRAVITY_PORT_UP:
8608 case EL_SP_GRAVITY_PORT_DOWN:
8610 element != EL_SP_PORT_LEFT &&
8611 element != EL_SP_GRAVITY_PORT_LEFT &&
8612 element != EL_SP_PORT_HORIZONTAL &&
8613 element != EL_SP_PORT_ANY) ||
8615 element != EL_SP_PORT_RIGHT &&
8616 element != EL_SP_GRAVITY_PORT_RIGHT &&
8617 element != EL_SP_PORT_HORIZONTAL &&
8618 element != EL_SP_PORT_ANY) ||
8620 element != EL_SP_PORT_UP &&
8621 element != EL_SP_GRAVITY_PORT_UP &&
8622 element != EL_SP_PORT_VERTICAL &&
8623 element != EL_SP_PORT_ANY) ||
8625 element != EL_SP_PORT_DOWN &&
8626 element != EL_SP_GRAVITY_PORT_DOWN &&
8627 element != EL_SP_PORT_VERTICAL &&
8628 element != EL_SP_PORT_ANY) ||
8629 !IN_LEV_FIELD(nextx, nexty) ||
8630 !IS_FREE(nextx, nexty))
8631 return MF_NO_ACTION;
8633 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8634 element == EL_SP_GRAVITY_PORT_RIGHT ||
8635 element == EL_SP_GRAVITY_PORT_UP ||
8636 element == EL_SP_GRAVITY_PORT_DOWN)
8637 game.gravity = !game.gravity;
8639 /* automatically move to the next field with double speed */
8640 player->programmed_action = move_direction;
8642 if (player->move_delay_reset_counter == 0)
8644 player->move_delay_reset_counter = 2; /* two double speed steps */
8646 DOUBLE_PLAYER_SPEED(player);
8649 player->move_delay_reset_counter = 2;
8651 DOUBLE_PLAYER_SPEED(player);
8654 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8658 case EL_TUBE_VERTICAL:
8659 case EL_TUBE_HORIZONTAL:
8660 case EL_TUBE_VERTICAL_LEFT:
8661 case EL_TUBE_VERTICAL_RIGHT:
8662 case EL_TUBE_HORIZONTAL_UP:
8663 case EL_TUBE_HORIZONTAL_DOWN:
8664 case EL_TUBE_LEFT_UP:
8665 case EL_TUBE_LEFT_DOWN:
8666 case EL_TUBE_RIGHT_UP:
8667 case EL_TUBE_RIGHT_DOWN:
8670 int tube_enter_directions[][2] =
8672 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8673 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8674 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8675 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8676 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8677 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8678 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8679 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8680 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8681 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8682 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8683 { -1, MV_NO_MOVING }
8686 while (tube_enter_directions[i][0] != element)
8689 if (tube_enter_directions[i][0] == -1) /* should not happen */
8693 if (!(tube_enter_directions[i][1] & move_direction))
8694 return MF_NO_ACTION; /* tube has no opening in this direction */
8696 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8702 if (IS_WALKABLE(element))
8704 int sound_action = ACTION_WALKING;
8706 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8708 if (!player->key[element - EL_GATE_1])
8709 return MF_NO_ACTION;
8711 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8713 if (!player->key[element - EL_GATE_1_GRAY])
8714 return MF_NO_ACTION;
8716 else if (element == EL_EXIT_OPEN ||
8717 element == EL_SP_EXIT_OPEN ||
8718 element == EL_SP_EXIT_OPENING)
8720 sound_action = ACTION_PASSING; /* player is passing exit */
8722 else if (element == EL_EMPTY)
8724 sound_action = ACTION_MOVING; /* nothing to walk on */
8727 /* play sound from background or player, whatever is available */
8728 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8729 PlayLevelSoundElementAction(x, y, element, sound_action);
8731 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8735 else if (IS_PASSABLE(element))
8737 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8738 return MF_NO_ACTION;
8741 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8742 return MF_NO_ACTION;
8745 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8747 if (!player->key[element - EL_EM_GATE_1])
8748 return MF_NO_ACTION;
8750 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8752 if (!player->key[element - EL_EM_GATE_1_GRAY])
8753 return MF_NO_ACTION;
8756 /* automatically move to the next field with double speed */
8757 player->programmed_action = move_direction;
8759 if (player->move_delay_reset_counter == 0)
8761 player->move_delay_reset_counter = 2; /* two double speed steps */
8763 DOUBLE_PLAYER_SPEED(player);
8766 player->move_delay_reset_counter = 2;
8768 DOUBLE_PLAYER_SPEED(player);
8771 PlayLevelSoundAction(x, y, ACTION_PASSING);
8775 else if (IS_DIGGABLE(element))
8779 if (mode != DF_SNAP)
8782 GfxElement[x][y] = GFX_ELEMENT(element);
8785 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8787 player->is_digging = TRUE;
8790 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8792 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8795 if (mode == DF_SNAP)
8796 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8801 else if (IS_COLLECTIBLE(element))
8805 if (mode != DF_SNAP)
8807 GfxElement[x][y] = element;
8808 player->is_collecting = TRUE;
8811 if (element == EL_SPEED_PILL)
8812 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8813 else if (element == EL_EXTRA_TIME && level.time > 0)
8816 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8818 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8820 player->shield_normal_time_left += 10;
8821 if (element == EL_SHIELD_DEADLY)
8822 player->shield_deadly_time_left += 10;
8824 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8826 if (player->inventory_size < MAX_INVENTORY_SIZE)
8827 player->inventory_element[player->inventory_size++] = element;
8829 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8830 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8832 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8834 player->dynabomb_count++;
8835 player->dynabombs_left++;
8837 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8839 player->dynabomb_size++;
8841 else if (element == EL_DYNABOMB_INCREASE_POWER)
8843 player->dynabomb_xl = TRUE;
8845 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8846 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8848 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8849 element - EL_KEY_1 : element - EL_EM_KEY_1);
8851 player->key[key_nr] = TRUE;
8853 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8854 el2edimg(EL_KEY_1 + key_nr));
8855 redraw_mask |= REDRAW_DOOR_1;
8857 else if (IS_ENVELOPE(element))
8860 player->show_envelope = element;
8862 ShowEnvelope(element - EL_ENVELOPE_1);
8865 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8869 for (i = 0; i < element_info[element].collect_count; i++)
8870 if (player->inventory_size < MAX_INVENTORY_SIZE)
8871 player->inventory_element[player->inventory_size++] = element;
8873 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8874 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8876 else if (element_info[element].collect_count > 0)
8878 local_player->gems_still_needed -=
8879 element_info[element].collect_count;
8880 if (local_player->gems_still_needed < 0)
8881 local_player->gems_still_needed = 0;
8883 DrawText(DX_EMERALDS, DY_EMERALDS,
8884 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8887 RaiseScoreElement(element);
8888 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8890 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8893 if (mode == DF_SNAP)
8894 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8899 else if (IS_PUSHABLE(element))
8901 if (mode == DF_SNAP && element != EL_BD_ROCK)
8902 return MF_NO_ACTION;
8904 if (CAN_FALL(element) && dy)
8905 return MF_NO_ACTION;
8907 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8908 !(element == EL_SPRING && use_spring_bug))
8909 return MF_NO_ACTION;
8912 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8913 ((move_direction & MV_VERTICAL &&
8914 ((element_info[element].move_pattern & MV_LEFT &&
8915 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8916 (element_info[element].move_pattern & MV_RIGHT &&
8917 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8918 (move_direction & MV_HORIZONTAL &&
8919 ((element_info[element].move_pattern & MV_UP &&
8920 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8921 (element_info[element].move_pattern & MV_DOWN &&
8922 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8923 return MF_NO_ACTION;
8927 /* do not push elements already moving away faster than player */
8928 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8929 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8930 return MF_NO_ACTION;
8932 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8933 return MF_NO_ACTION;
8937 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8939 if (player->push_delay_value == -1)
8940 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8942 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8944 if (!player->is_pushing)
8945 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8949 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8950 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8951 !player_is_pushing))
8952 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8955 if (!player->is_pushing &&
8956 game.engine_version >= VERSION_IDENT(2,2,0,7))
8957 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8961 printf("::: push delay: %ld [%d, %d] [%d]\n",
8962 player->push_delay_value, FrameCounter, game.engine_version,
8963 player->is_pushing);
8966 player->is_pushing = TRUE;
8968 if (!(IN_LEV_FIELD(nextx, nexty) &&
8969 (IS_FREE(nextx, nexty) ||
8970 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8971 IS_SB_ELEMENT(element)))))
8972 return MF_NO_ACTION;
8974 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8975 return MF_NO_ACTION;
8977 if (player->push_delay == 0) /* new pushing; restart delay */
8978 player->push_delay = FrameCounter;
8980 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8981 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8982 element != EL_SPRING && element != EL_BALLOON)
8984 /* make sure that there is no move delay before next try to push */
8985 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8986 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8988 return MF_NO_ACTION;
8992 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8995 if (IS_SB_ELEMENT(element))
8997 if (element == EL_SOKOBAN_FIELD_FULL)
8999 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9000 local_player->sokobanfields_still_needed++;
9003 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9005 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9006 local_player->sokobanfields_still_needed--;
9009 Feld[x][y] = EL_SOKOBAN_OBJECT;
9011 if (Back[x][y] == Back[nextx][nexty])
9012 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9013 else if (Back[x][y] != 0)
9014 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9017 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9020 if (local_player->sokobanfields_still_needed == 0 &&
9021 game.emulation == EMU_SOKOBAN)
9023 player->LevelSolved = player->GameOver = TRUE;
9024 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9028 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9030 InitMovingField(x, y, move_direction);
9031 GfxAction[x][y] = ACTION_PUSHING;
9033 if (mode == DF_SNAP)
9034 ContinueMoving(x, y);
9036 MovPos[x][y] = (dx != 0 ? dx : dy);
9038 Pushed[x][y] = TRUE;
9039 Pushed[nextx][nexty] = TRUE;
9041 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9042 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9044 player->push_delay_value = -1; /* get new value later */
9046 CheckTriggeredElementSideChange(x, y, element, dig_side,
9047 CE_OTHER_GETS_PUSHED);
9048 CheckElementSideChange(x, y, element, dig_side,
9049 CE_PUSHED_BY_PLAYER, -1);
9053 else if (IS_SWITCHABLE(element))
9055 if (PLAYER_SWITCHING(player, x, y))
9058 player->is_switching = TRUE;
9059 player->switch_x = x;
9060 player->switch_y = y;
9062 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9064 if (element == EL_ROBOT_WHEEL)
9066 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9070 DrawLevelField(x, y);
9072 else if (element == EL_SP_TERMINAL)
9076 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9078 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9080 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9081 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9084 else if (IS_BELT_SWITCH(element))
9086 ToggleBeltSwitch(x, y);
9088 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9089 element == EL_SWITCHGATE_SWITCH_DOWN)
9091 ToggleSwitchgateSwitch(x, y);
9093 else if (element == EL_LIGHT_SWITCH ||
9094 element == EL_LIGHT_SWITCH_ACTIVE)
9096 ToggleLightSwitch(x, y);
9099 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9100 SND_LIGHT_SWITCH_ACTIVATING :
9101 SND_LIGHT_SWITCH_DEACTIVATING);
9104 else if (element == EL_TIMEGATE_SWITCH)
9106 ActivateTimegateSwitch(x, y);
9108 else if (element == EL_BALLOON_SWITCH_LEFT ||
9109 element == EL_BALLOON_SWITCH_RIGHT ||
9110 element == EL_BALLOON_SWITCH_UP ||
9111 element == EL_BALLOON_SWITCH_DOWN ||
9112 element == EL_BALLOON_SWITCH_ANY)
9114 if (element == EL_BALLOON_SWITCH_ANY)
9115 game.balloon_dir = move_direction;
9117 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9118 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9119 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9120 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9123 else if (element == EL_LAMP)
9125 Feld[x][y] = EL_LAMP_ACTIVE;
9126 local_player->lights_still_needed--;
9128 DrawLevelField(x, y);
9130 else if (element == EL_TIME_ORB_FULL)
9132 Feld[x][y] = EL_TIME_ORB_EMPTY;
9134 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9136 DrawLevelField(x, y);
9139 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9147 if (!PLAYER_SWITCHING(player, x, y))
9149 player->is_switching = TRUE;
9150 player->switch_x = x;
9151 player->switch_y = y;
9153 CheckTriggeredElementSideChange(x, y, element, dig_side,
9154 CE_OTHER_IS_SWITCHING);
9155 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9158 CheckTriggeredElementSideChange(x, y, element, dig_side,
9159 CE_OTHER_GETS_PRESSED);
9160 CheckElementSideChange(x, y, element, dig_side,
9161 CE_PRESSED_BY_PLAYER, -1);
9164 return MF_NO_ACTION;
9167 player->push_delay = 0;
9169 if (Feld[x][y] != element) /* really digged/collected something */
9170 player->is_collecting = !player->is_digging;
9175 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9177 int jx = player->jx, jy = player->jy;
9178 int x = jx + dx, y = jy + dy;
9179 int snap_direction = (dx == -1 ? MV_LEFT :
9180 dx == +1 ? MV_RIGHT :
9182 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9184 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9187 if (!player->active || !IN_LEV_FIELD(x, y))
9195 if (player->MovPos == 0)
9196 player->is_pushing = FALSE;
9198 player->is_snapping = FALSE;
9200 if (player->MovPos == 0)
9202 player->is_moving = FALSE;
9203 player->is_digging = FALSE;
9204 player->is_collecting = FALSE;
9210 if (player->is_snapping)
9213 player->MovDir = snap_direction;
9216 if (player->MovPos == 0)
9219 player->is_moving = FALSE;
9220 player->is_digging = FALSE;
9221 player->is_collecting = FALSE;
9224 player->is_dropping = FALSE;
9226 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9229 player->is_snapping = TRUE;
9232 if (player->MovPos == 0)
9235 player->is_moving = FALSE;
9236 player->is_digging = FALSE;
9237 player->is_collecting = FALSE;
9240 DrawLevelField(x, y);
9246 boolean DropElement(struct PlayerInfo *player)
9248 int jx = player->jx, jy = player->jy;
9249 int old_element = Feld[jx][jy];
9252 /* check if player is active, not moving and ready to drop */
9253 if (!player->active || player->MovPos || player->drop_delay > 0)
9256 /* check if player has anything that can be dropped */
9257 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9260 /* check if anything can be dropped at the current position */
9261 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9264 /* collected custom elements can only be dropped on empty fields */
9265 if (player->inventory_size > 0 &&
9266 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9267 && old_element != EL_EMPTY)
9270 if (old_element != EL_EMPTY)
9271 Back[jx][jy] = old_element; /* store old element on this field */
9273 ResetGfxAnimation(jx, jy);
9274 ResetRandomAnimationValue(jx, jy);
9276 if (player->inventory_size > 0)
9278 player->inventory_size--;
9279 new_element = player->inventory_element[player->inventory_size];
9281 if (new_element == EL_DYNAMITE)
9282 new_element = EL_DYNAMITE_ACTIVE;
9283 else if (new_element == EL_SP_DISK_RED)
9284 new_element = EL_SP_DISK_RED_ACTIVE;
9286 Feld[jx][jy] = new_element;
9288 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9289 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9291 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9292 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9294 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9296 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9297 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9299 TestIfElementTouchesCustomElement(jx, jy);
9301 else /* player is dropping a dyna bomb */
9303 player->dynabombs_left--;
9304 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9306 Feld[jx][jy] = new_element;
9308 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9309 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9311 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9318 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9320 InitField(jx, jy, FALSE);
9321 if (CAN_MOVE(Feld[jx][jy]))
9325 new_element = Feld[jx][jy];
9327 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9328 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9330 int move_stepsize = element_info[new_element].move_stepsize;
9331 int direction, dx, dy, nextx, nexty;
9333 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9334 MovDir[jx][jy] = player->MovDir;
9336 direction = MovDir[jx][jy];
9337 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9338 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9342 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9345 WasJustMoving[jx][jy] = 3;
9347 InitMovingField(jx, jy, direction);
9348 ContinueMoving(jx, jy);
9353 Changed[jx][jy] = 0; /* allow another change */
9356 TestIfElementHitsCustomElement(jx, jy, direction);
9358 CheckElementSideChange(jx, jy, new_element,
9359 direction, CE_HITTING_SOMETHING, -1);
9363 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9367 player->drop_delay = 8 + 8 + 8;
9372 player->is_dropping = TRUE;
9378 /* ------------------------------------------------------------------------- */
9379 /* game sound playing functions */
9380 /* ------------------------------------------------------------------------- */
9382 static int *loop_sound_frame = NULL;
9383 static int *loop_sound_volume = NULL;
9385 void InitPlayLevelSound()
9387 int num_sounds = getSoundListSize();
9389 checked_free(loop_sound_frame);
9390 checked_free(loop_sound_volume);
9392 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9393 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9396 static void PlayLevelSound(int x, int y, int nr)
9398 int sx = SCREENX(x), sy = SCREENY(y);
9399 int volume, stereo_position;
9400 int max_distance = 8;
9401 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9403 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9404 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9407 if (!IN_LEV_FIELD(x, y) ||
9408 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9409 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9412 volume = SOUND_MAX_VOLUME;
9414 if (!IN_SCR_FIELD(sx, sy))
9416 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9417 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9419 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9422 stereo_position = (SOUND_MAX_LEFT +
9423 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9424 (SCR_FIELDX + 2 * max_distance));
9426 if (IS_LOOP_SOUND(nr))
9428 /* This assures that quieter loop sounds do not overwrite louder ones,
9429 while restarting sound volume comparison with each new game frame. */
9431 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9434 loop_sound_volume[nr] = volume;
9435 loop_sound_frame[nr] = FrameCounter;
9438 PlaySoundExt(nr, volume, stereo_position, type);
9441 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9443 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9444 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9445 y < LEVELY(BY1) ? LEVELY(BY1) :
9446 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9450 static void PlayLevelSoundAction(int x, int y, int action)
9452 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9455 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9457 int sound_effect = element_info[element].sound[action];
9459 if (sound_effect != SND_UNDEFINED)
9460 PlayLevelSound(x, y, sound_effect);
9463 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9466 int sound_effect = element_info[element].sound[action];
9468 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9469 PlayLevelSound(x, y, sound_effect);
9472 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9474 int sound_effect = element_info[Feld[x][y]].sound[action];
9476 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9477 PlayLevelSound(x, y, sound_effect);
9480 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9482 int sound_effect = element_info[Feld[x][y]].sound[action];
9484 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9485 StopSound(sound_effect);
9488 static void PlayLevelMusic()
9490 if (levelset.music[level_nr] != MUS_UNDEFINED)
9491 PlayMusic(levelset.music[level_nr]); /* from config file */
9493 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9496 void RaiseScore(int value)
9498 local_player->score += value;
9499 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9502 void RaiseScoreElement(int element)
9508 case EL_EMERALD_YELLOW:
9509 case EL_EMERALD_RED:
9510 case EL_EMERALD_PURPLE:
9511 case EL_SP_INFOTRON:
9512 RaiseScore(level.score[SC_EMERALD]);
9515 RaiseScore(level.score[SC_DIAMOND]);
9518 RaiseScore(level.score[SC_CRYSTAL]);
9521 RaiseScore(level.score[SC_PEARL]);
9524 case EL_BD_BUTTERFLY:
9525 case EL_SP_ELECTRON:
9526 RaiseScore(level.score[SC_BUG]);
9530 case EL_SP_SNIKSNAK:
9531 RaiseScore(level.score[SC_SPACESHIP]);
9534 case EL_DARK_YAMYAM:
9535 RaiseScore(level.score[SC_YAMYAM]);
9538 RaiseScore(level.score[SC_ROBOT]);
9541 RaiseScore(level.score[SC_PACMAN]);
9544 RaiseScore(level.score[SC_NUT]);
9547 case EL_SP_DISK_RED:
9548 case EL_DYNABOMB_INCREASE_NUMBER:
9549 case EL_DYNABOMB_INCREASE_SIZE:
9550 case EL_DYNABOMB_INCREASE_POWER:
9551 RaiseScore(level.score[SC_DYNAMITE]);
9553 case EL_SHIELD_NORMAL:
9554 case EL_SHIELD_DEADLY:
9555 RaiseScore(level.score[SC_SHIELD]);
9558 RaiseScore(level.score[SC_TIME_BONUS]);
9564 RaiseScore(level.score[SC_KEY]);
9567 RaiseScore(element_info[element].collect_score);
9572 void RequestQuitGame(boolean ask_if_really_quit)
9574 if (AllPlayersGone ||
9575 !ask_if_really_quit ||
9576 level_editor_test_game ||
9577 Request("Do you really want to quit the game ?",
9578 REQ_ASK | REQ_STAY_CLOSED))
9580 #if defined(PLATFORM_UNIX)
9581 if (options.network)
9582 SendToServer_StopPlaying();
9586 game_status = GAME_MODE_MAIN;
9592 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9597 /* ---------- new game button stuff ---------------------------------------- */
9599 /* graphic position values for game buttons */
9600 #define GAME_BUTTON_XSIZE 30
9601 #define GAME_BUTTON_YSIZE 30
9602 #define GAME_BUTTON_XPOS 5
9603 #define GAME_BUTTON_YPOS 215
9604 #define SOUND_BUTTON_XPOS 5
9605 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9607 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9608 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9609 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9610 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9611 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9612 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9619 } gamebutton_info[NUM_GAME_BUTTONS] =
9622 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9627 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9632 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9637 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9638 SOUND_CTRL_ID_MUSIC,
9639 "background music on/off"
9642 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9643 SOUND_CTRL_ID_LOOPS,
9644 "sound loops on/off"
9647 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9648 SOUND_CTRL_ID_SIMPLE,
9649 "normal sounds on/off"
9653 void CreateGameButtons()
9657 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9659 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9660 struct GadgetInfo *gi;
9663 unsigned long event_mask;
9664 int gd_xoffset, gd_yoffset;
9665 int gd_x1, gd_x2, gd_y1, gd_y2;
9668 gd_xoffset = gamebutton_info[i].x;
9669 gd_yoffset = gamebutton_info[i].y;
9670 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9671 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9673 if (id == GAME_CTRL_ID_STOP ||
9674 id == GAME_CTRL_ID_PAUSE ||
9675 id == GAME_CTRL_ID_PLAY)
9677 button_type = GD_TYPE_NORMAL_BUTTON;
9679 event_mask = GD_EVENT_RELEASED;
9680 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9681 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9685 button_type = GD_TYPE_CHECK_BUTTON;
9687 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9688 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9689 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9690 event_mask = GD_EVENT_PRESSED;
9691 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9692 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9695 gi = CreateGadget(GDI_CUSTOM_ID, id,
9696 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9697 GDI_X, DX + gd_xoffset,
9698 GDI_Y, DY + gd_yoffset,
9699 GDI_WIDTH, GAME_BUTTON_XSIZE,
9700 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9701 GDI_TYPE, button_type,
9702 GDI_STATE, GD_BUTTON_UNPRESSED,
9703 GDI_CHECKED, checked,
9704 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9705 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9706 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9707 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9708 GDI_EVENT_MASK, event_mask,
9709 GDI_CALLBACK_ACTION, HandleGameButtons,
9713 Error(ERR_EXIT, "cannot create gadget");
9715 game_gadget[id] = gi;
9719 void FreeGameButtons()
9723 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9724 FreeGadget(game_gadget[i]);
9727 static void MapGameButtons()
9731 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9732 MapGadget(game_gadget[i]);
9735 void UnmapGameButtons()
9739 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9740 UnmapGadget(game_gadget[i]);
9743 static void HandleGameButtons(struct GadgetInfo *gi)
9745 int id = gi->custom_id;
9747 if (game_status != GAME_MODE_PLAYING)
9752 case GAME_CTRL_ID_STOP:
9753 RequestQuitGame(TRUE);
9756 case GAME_CTRL_ID_PAUSE:
9757 if (options.network)
9759 #if defined(PLATFORM_UNIX)
9761 SendToServer_ContinuePlaying();
9763 SendToServer_PausePlaying();
9767 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9770 case GAME_CTRL_ID_PLAY:
9773 #if defined(PLATFORM_UNIX)
9774 if (options.network)
9775 SendToServer_ContinuePlaying();
9779 tape.pausing = FALSE;
9780 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9785 case SOUND_CTRL_ID_MUSIC:
9786 if (setup.sound_music)
9788 setup.sound_music = FALSE;
9791 else if (audio.music_available)
9793 setup.sound = setup.sound_music = TRUE;
9795 SetAudioMode(setup.sound);
9801 case SOUND_CTRL_ID_LOOPS:
9802 if (setup.sound_loops)
9803 setup.sound_loops = FALSE;
9804 else if (audio.loops_available)
9806 setup.sound = setup.sound_loops = TRUE;
9807 SetAudioMode(setup.sound);
9811 case SOUND_CTRL_ID_SIMPLE:
9812 if (setup.sound_simple)
9813 setup.sound_simple = FALSE;
9814 else if (audio.sound_available)
9816 setup.sound = setup.sound_simple = TRUE;
9817 SetAudioMode(setup.sound);