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))
766 else if (IS_GROUP_ELEMENT(element))
768 struct ElementGroupInfo *group = element_info[element].group;
769 int random_pos = RND(group->num_elements_resolved);
771 Feld[x][y] = group->element_resolved[random_pos];
773 InitField(x, y, init_game);
779 void DrawGameDoorValues()
783 for (i = 0; i < MAX_PLAYERS; i++)
784 for (j = 0; j < 4; j++)
785 if (stored_player[i].key[j])
786 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
787 el2edimg(EL_KEY_1 + j));
789 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
790 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
791 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
792 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
793 DrawText(DX + XX_SCORE, DY + YY_SCORE,
794 int2str(local_player->score, 5), FONT_TEXT_2);
795 DrawText(DX + XX_TIME, DY + YY_TIME,
796 int2str(TimeLeft, 3), FONT_TEXT_2);
799 static void resolve_group_element(int group_element, int recursion_depth)
802 static struct ElementGroupInfo *group;
803 struct ElementGroupInfo *actual_group = element_info[group_element].group;
806 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
808 Error(ERR_WARN, "recursion too deep when resolving group element %d",
809 group_element - EL_GROUP_START + 1);
811 /* replace element which caused too deep recursion by question mark */
812 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
817 if (recursion_depth == 0) /* initialization */
819 group = element_info[group_element].group;
820 group->num_elements_resolved = 0;
821 group_nr = group_element - EL_GROUP_START;
824 for (i = 0; i < actual_group->num_elements; i++)
826 int element = actual_group->element[i];
828 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
831 if (IS_GROUP_ELEMENT(element))
832 resolve_group_element(element, recursion_depth + 1);
835 group->element_resolved[group->num_elements_resolved++] = element;
836 element_info[element].in_group[group_nr] = TRUE;
841 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
843 printf("::: group %d: %d resolved elements\n",
844 group_element - EL_GROUP_START, group->num_elements_resolved);
845 for (i = 0; i < group->num_elements_resolved; i++)
846 printf("::: - %d ['%s']\n", group->element_resolved[i],
847 element_info[group->element_resolved[i]].token_name);
854 =============================================================================
856 -----------------------------------------------------------------------------
857 initialize game engine due to level / tape version number
858 =============================================================================
861 static void InitGameEngine()
865 /* set game engine from tape file when re-playing, else from level file */
866 game.engine_version = (tape.playing ? tape.engine_version :
869 /* dynamically adjust element properties according to game engine version */
870 InitElementPropertiesEngine(game.engine_version);
873 printf("level %d: level version == %06d\n", level_nr, level.game_version);
874 printf(" tape version == %06d [%s] [file: %06d]\n",
875 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
877 printf(" => game.engine_version == %06d\n", game.engine_version);
880 /* ---------- recursively resolve group elements ------------------------- */
882 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
883 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
884 element_info[i].in_group[j] = FALSE;
886 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
887 resolve_group_element(EL_GROUP_START + i, 0);
889 /* ---------- initialize player's initial move delay --------------------- */
891 /* dynamically adjust player properties according to game engine version */
892 game.initial_move_delay =
893 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
894 INITIAL_MOVE_DELAY_OFF);
896 /* dynamically adjust player properties according to level information */
897 game.initial_move_delay_value =
898 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
900 /* ---------- initialize player's initial push delay --------------------- */
902 /* dynamically adjust player properties according to game engine version */
903 game.initial_push_delay_value =
904 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
906 /* ---------- initialize changing elements ------------------------------- */
908 /* initialize changing elements information */
909 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
911 struct ElementInfo *ei = &element_info[i];
913 /* this pointer might have been changed in the level editor */
914 ei->change = &ei->change_page[0];
916 if (!IS_CUSTOM_ELEMENT(i))
918 ei->change->target_element = EL_EMPTY_SPACE;
919 ei->change->delay_fixed = 0;
920 ei->change->delay_random = 0;
921 ei->change->delay_frames = 1;
924 ei->change_events = CE_BITMASK_DEFAULT;
925 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
927 ei->event_page_nr[j] = 0;
928 ei->event_page[j] = &ei->change_page[0];
932 /* add changing elements from pre-defined list */
933 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
935 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
936 struct ElementInfo *ei = &element_info[ch_delay->element];
938 ei->change->target_element = ch_delay->target_element;
939 ei->change->delay_fixed = ch_delay->change_delay;
941 ei->change->pre_change_function = ch_delay->pre_change_function;
942 ei->change->change_function = ch_delay->change_function;
943 ei->change->post_change_function = ch_delay->post_change_function;
945 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
949 /* add change events from custom element configuration */
950 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
952 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
954 for (j = 0; j < ei->num_change_pages; j++)
956 if (!ei->change_page[j].can_change)
959 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
961 /* only add event page for the first page found with this event */
962 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
963 !(ei->change_events & CH_EVENT_BIT(k)))
965 ei->change_events |= CH_EVENT_BIT(k);
966 ei->event_page_nr[k] = j;
967 ei->event_page[k] = &ei->change_page[j];
975 /* add change events from custom element configuration */
976 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
978 int element = EL_CUSTOM_START + i;
980 /* only add custom elements that change after fixed/random frame delay */
981 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
982 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
986 /* ---------- initialize trigger events ---------------------------------- */
988 /* initialize trigger events information */
989 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
990 trigger_events[i] = EP_BITMASK_DEFAULT;
993 /* add trigger events from element change event properties */
994 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
996 struct ElementInfo *ei = &element_info[i];
998 for (j = 0; j < ei->num_change_pages; j++)
1000 if (!ei->change_page[j].can_change)
1003 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1005 int trigger_element = ei->change_page[j].trigger_element;
1007 trigger_events[trigger_element] |= ei->change_page[j].events;
1012 /* add trigger events from element change event properties */
1013 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1014 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1015 trigger_events[element_info[i].change->trigger_element] |=
1016 element_info[i].change->events;
1019 /* ---------- initialize push delay -------------------------------------- */
1021 /* initialize push delay values to default */
1022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1024 if (!IS_CUSTOM_ELEMENT(i))
1026 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1027 element_info[i].push_delay_random = game.default_push_delay_random;
1031 /* set push delay value for certain elements from pre-defined list */
1032 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1034 int e = push_delay_list[i].element;
1036 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1037 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1040 /* ---------- initialize move stepsize ----------------------------------- */
1042 /* initialize move stepsize values to default */
1043 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1044 if (!IS_CUSTOM_ELEMENT(i))
1045 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1047 /* set move stepsize value for certain elements from pre-defined list */
1048 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1050 int e = move_stepsize_list[i].element;
1052 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1055 /* ---------- initialize gem count --------------------------------------- */
1057 /* initialize gem count values for each element */
1058 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1059 if (!IS_CUSTOM_ELEMENT(i))
1060 element_info[i].collect_count = 0;
1062 /* add gem count values for all elements from pre-defined list */
1063 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1064 element_info[collect_count_list[i].element].collect_count =
1065 collect_count_list[i].count;
1070 =============================================================================
1072 -----------------------------------------------------------------------------
1073 initialize and start new game
1074 =============================================================================
1079 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1080 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1081 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1088 #if USE_NEW_AMOEBA_CODE
1089 printf("Using new amoeba code.\n");
1091 printf("Using old amoeba code.\n");
1096 /* don't play tapes over network */
1097 network_playing = (options.network && !tape.playing);
1099 for (i = 0; i < MAX_PLAYERS; i++)
1101 struct PlayerInfo *player = &stored_player[i];
1103 player->index_nr = i;
1104 player->element_nr = EL_PLAYER_1 + i;
1106 player->present = FALSE;
1107 player->active = FALSE;
1110 player->effective_action = 0;
1111 player->programmed_action = 0;
1114 player->gems_still_needed = level.gems_needed;
1115 player->sokobanfields_still_needed = 0;
1116 player->lights_still_needed = 0;
1117 player->friends_still_needed = 0;
1119 for (j = 0; j < 4; j++)
1120 player->key[j] = FALSE;
1122 player->dynabomb_count = 0;
1123 player->dynabomb_size = 1;
1124 player->dynabombs_left = 0;
1125 player->dynabomb_xl = FALSE;
1127 player->MovDir = MV_NO_MOVING;
1130 player->GfxDir = MV_NO_MOVING;
1131 player->GfxAction = ACTION_DEFAULT;
1133 player->StepFrame = 0;
1135 player->use_murphy_graphic = FALSE;
1137 player->actual_frame_counter = 0;
1139 player->step_counter = 0;
1141 player->last_move_dir = MV_NO_MOVING;
1143 player->is_waiting = FALSE;
1144 player->is_moving = FALSE;
1145 player->is_digging = FALSE;
1146 player->is_snapping = FALSE;
1147 player->is_collecting = FALSE;
1148 player->is_pushing = FALSE;
1149 player->is_switching = FALSE;
1150 player->is_dropping = FALSE;
1152 player->is_bored = FALSE;
1153 player->is_sleeping = FALSE;
1155 player->frame_counter_bored = -1;
1156 player->frame_counter_sleeping = -1;
1158 player->anim_delay_counter = 0;
1159 player->post_delay_counter = 0;
1161 player->action_waiting = ACTION_DEFAULT;
1162 player->last_action_waiting = ACTION_DEFAULT;
1163 player->special_action_bored = ACTION_DEFAULT;
1164 player->special_action_sleeping = ACTION_DEFAULT;
1166 player->num_special_action_bored = 0;
1167 player->num_special_action_sleeping = 0;
1169 /* determine number of special actions for bored and sleeping animation */
1170 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1172 boolean found = FALSE;
1174 for (k = 0; k < NUM_DIRECTIONS; k++)
1175 if (el_act_dir2img(player->element_nr, j, k) !=
1176 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1180 player->num_special_action_bored++;
1184 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1186 boolean found = FALSE;
1188 for (k = 0; k < NUM_DIRECTIONS; k++)
1189 if (el_act_dir2img(player->element_nr, j, k) !=
1190 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1194 player->num_special_action_sleeping++;
1199 player->switch_x = -1;
1200 player->switch_y = -1;
1202 player->show_envelope = 0;
1204 player->move_delay = game.initial_move_delay;
1205 player->move_delay_value = game.initial_move_delay_value;
1207 player->move_delay_reset_counter = 0;
1209 player->push_delay = 0;
1210 player->push_delay_value = game.initial_push_delay_value;
1212 player->drop_delay = 0;
1214 player->last_jx = player->last_jy = 0;
1215 player->jx = player->jy = 0;
1217 player->shield_normal_time_left = 0;
1218 player->shield_deadly_time_left = 0;
1220 player->inventory_size = 0;
1222 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1223 SnapField(player, 0, 0);
1225 player->LevelSolved = FALSE;
1226 player->GameOver = FALSE;
1229 network_player_action_received = FALSE;
1231 #if defined(PLATFORM_UNIX)
1232 /* initial null action */
1233 if (network_playing)
1234 SendToServer_MovePlayer(MV_NO_MOVING);
1242 TimeLeft = level.time;
1244 ScreenMovDir = MV_NO_MOVING;
1248 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1250 AllPlayersGone = FALSE;
1252 game.yamyam_content_nr = 0;
1253 game.magic_wall_active = FALSE;
1254 game.magic_wall_time_left = 0;
1255 game.light_time_left = 0;
1256 game.timegate_time_left = 0;
1257 game.switchgate_pos = 0;
1258 game.balloon_dir = MV_NO_MOVING;
1259 game.gravity = level.initial_gravity;
1260 game.explosions_delayed = TRUE;
1262 game.envelope_active = FALSE;
1264 for (i = 0; i < 4; i++)
1266 game.belt_dir[i] = MV_NO_MOVING;
1267 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1270 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1271 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1273 for (x = 0; x < lev_fieldx; x++)
1275 for (y = 0; y < lev_fieldy; y++)
1277 Feld[x][y] = level.field[x][y];
1278 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1279 ChangeDelay[x][y] = 0;
1280 ChangePage[x][y] = -1;
1281 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1283 WasJustMoving[x][y] = 0;
1284 WasJustFalling[x][y] = 0;
1286 Pushed[x][y] = FALSE;
1288 Changed[x][y] = CE_BITMASK_DEFAULT;
1289 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1291 ExplodePhase[x][y] = 0;
1292 ExplodeField[x][y] = EX_NO_EXPLOSION;
1294 RunnerVisit[x][y] = 0;
1295 PlayerVisit[x][y] = 0;
1298 GfxRandom[x][y] = INIT_GFX_RANDOM();
1299 GfxElement[x][y] = EL_UNDEFINED;
1300 GfxAction[x][y] = ACTION_DEFAULT;
1301 GfxDir[x][y] = MV_NO_MOVING;
1305 for (y = 0; y < lev_fieldy; y++)
1307 for (x = 0; x < lev_fieldx; x++)
1309 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1311 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1313 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1316 InitField(x, y, TRUE);
1322 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1323 emulate_sb ? EMU_SOKOBAN :
1324 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1326 /* correct non-moving belts to start moving left */
1327 for (i = 0; i < 4; i++)
1328 if (game.belt_dir[i] == MV_NO_MOVING)
1329 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1331 /* check if any connected player was not found in playfield */
1332 for (i = 0; i < MAX_PLAYERS; i++)
1334 struct PlayerInfo *player = &stored_player[i];
1336 if (player->connected && !player->present)
1338 for (j = 0; j < MAX_PLAYERS; j++)
1340 struct PlayerInfo *some_player = &stored_player[j];
1341 int jx = some_player->jx, jy = some_player->jy;
1343 /* assign first free player found that is present in the playfield */
1344 if (some_player->present && !some_player->connected)
1346 player->present = TRUE;
1347 player->active = TRUE;
1348 some_player->present = FALSE;
1350 StorePlayer[jx][jy] = player->element_nr;
1351 player->jx = player->last_jx = jx;
1352 player->jy = player->last_jy = jy;
1362 /* when playing a tape, eliminate all players who do not participate */
1364 for (i = 0; i < MAX_PLAYERS; i++)
1366 if (stored_player[i].active && !tape.player_participates[i])
1368 struct PlayerInfo *player = &stored_player[i];
1369 int jx = player->jx, jy = player->jy;
1371 player->active = FALSE;
1372 StorePlayer[jx][jy] = 0;
1373 Feld[jx][jy] = EL_EMPTY;
1377 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1379 /* when in single player mode, eliminate all but the first active player */
1381 for (i = 0; i < MAX_PLAYERS; i++)
1383 if (stored_player[i].active)
1385 for (j = i + 1; j < MAX_PLAYERS; j++)
1387 if (stored_player[j].active)
1389 struct PlayerInfo *player = &stored_player[j];
1390 int jx = player->jx, jy = player->jy;
1392 player->active = FALSE;
1393 StorePlayer[jx][jy] = 0;
1394 Feld[jx][jy] = EL_EMPTY;
1401 /* when recording the game, store which players take part in the game */
1404 for (i = 0; i < MAX_PLAYERS; i++)
1405 if (stored_player[i].active)
1406 tape.player_participates[i] = TRUE;
1411 for (i = 0; i < MAX_PLAYERS; i++)
1413 struct PlayerInfo *player = &stored_player[i];
1415 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1420 if (local_player == player)
1421 printf("Player %d is local player.\n", i+1);
1425 if (BorderElement == EL_EMPTY)
1428 SBX_Right = lev_fieldx - SCR_FIELDX;
1430 SBY_Lower = lev_fieldy - SCR_FIELDY;
1435 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1437 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1440 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1441 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1443 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1444 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1446 /* if local player not found, look for custom element that might create
1447 the player (make some assumptions about the right custom element) */
1448 if (!local_player->present)
1450 int start_x = 0, start_y = 0;
1451 int found_rating = 0;
1452 int found_element = EL_UNDEFINED;
1454 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1456 int element = Feld[x][y];
1461 if (!IS_CUSTOM_ELEMENT(element))
1464 if (CAN_CHANGE(element))
1466 for (i = 0; i < element_info[element].num_change_pages; i++)
1468 content = element_info[element].change_page[i].target_element;
1469 is_player = ELEM_IS_PLAYER(content);
1471 if (is_player && (found_rating < 3 || element < found_element))
1477 found_element = element;
1482 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1484 content = element_info[element].content[xx][yy];
1485 is_player = ELEM_IS_PLAYER(content);
1487 if (is_player && (found_rating < 2 || element < found_element))
1489 start_x = x + xx - 1;
1490 start_y = y + yy - 1;
1493 found_element = element;
1496 if (!CAN_CHANGE(element))
1499 for (i = 0; i < element_info[element].num_change_pages; i++)
1501 content = element_info[element].change_page[i].content[xx][yy];
1502 is_player = ELEM_IS_PLAYER(content);
1504 if (is_player && (found_rating < 1 || element < found_element))
1506 start_x = x + xx - 1;
1507 start_y = y + yy - 1;
1510 found_element = element;
1516 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1517 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1520 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1521 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1527 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1528 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1529 local_player->jx - MIDPOSX);
1531 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1532 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1533 local_player->jy - MIDPOSY);
1535 scroll_x = SBX_Left;
1536 scroll_y = SBY_Upper;
1537 if (local_player->jx >= SBX_Left + MIDPOSX)
1538 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1539 local_player->jx - MIDPOSX :
1541 if (local_player->jy >= SBY_Upper + MIDPOSY)
1542 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1543 local_player->jy - MIDPOSY :
1548 CloseDoor(DOOR_CLOSE_1);
1553 /* after drawing the level, correct some elements */
1554 if (game.timegate_time_left == 0)
1555 CloseAllOpenTimegates();
1557 if (setup.soft_scrolling)
1558 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1560 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1563 /* copy default game door content to main double buffer */
1564 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1565 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1568 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1571 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1572 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1573 BlitBitmap(drawto, drawto,
1574 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1575 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1576 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1577 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1580 DrawGameDoorValues();
1584 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1585 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1586 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1590 /* copy actual game door content to door double buffer for OpenDoor() */
1591 BlitBitmap(drawto, bitmap_db_door,
1592 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1594 OpenDoor(DOOR_OPEN_ALL);
1596 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1598 if (setup.sound_music)
1601 KeyboardAutoRepeatOffUnlessAutoplay();
1605 for (i = 0; i < 4; i++)
1606 printf("Player %d %sactive.\n",
1607 i + 1, (stored_player[i].active ? "" : "not "));
1611 printf("::: starting game [%d]\n", FrameCounter);
1615 void InitMovDir(int x, int y)
1617 int i, element = Feld[x][y];
1618 static int xy[4][2] =
1625 static int direction[3][4] =
1627 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1628 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1629 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1638 Feld[x][y] = EL_BUG;
1639 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1642 case EL_SPACESHIP_RIGHT:
1643 case EL_SPACESHIP_UP:
1644 case EL_SPACESHIP_LEFT:
1645 case EL_SPACESHIP_DOWN:
1646 Feld[x][y] = EL_SPACESHIP;
1647 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1650 case EL_BD_BUTTERFLY_RIGHT:
1651 case EL_BD_BUTTERFLY_UP:
1652 case EL_BD_BUTTERFLY_LEFT:
1653 case EL_BD_BUTTERFLY_DOWN:
1654 Feld[x][y] = EL_BD_BUTTERFLY;
1655 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1658 case EL_BD_FIREFLY_RIGHT:
1659 case EL_BD_FIREFLY_UP:
1660 case EL_BD_FIREFLY_LEFT:
1661 case EL_BD_FIREFLY_DOWN:
1662 Feld[x][y] = EL_BD_FIREFLY;
1663 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1666 case EL_PACMAN_RIGHT:
1668 case EL_PACMAN_LEFT:
1669 case EL_PACMAN_DOWN:
1670 Feld[x][y] = EL_PACMAN;
1671 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1674 case EL_SP_SNIKSNAK:
1675 MovDir[x][y] = MV_UP;
1678 case EL_SP_ELECTRON:
1679 MovDir[x][y] = MV_LEFT;
1686 Feld[x][y] = EL_MOLE;
1687 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1691 if (IS_CUSTOM_ELEMENT(element))
1693 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1694 MovDir[x][y] = element_info[element].move_direction_initial;
1695 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1696 element_info[element].move_pattern == MV_TURNING_LEFT ||
1697 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1698 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1699 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1700 element_info[element].move_pattern == MV_TURNING_RANDOM)
1701 MovDir[x][y] = 1 << RND(4);
1702 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1703 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1704 else if (element_info[element].move_pattern == MV_VERTICAL)
1705 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1706 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1707 MovDir[x][y] = element_info[element].move_pattern;
1708 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1709 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1711 for (i = 0; i < 4; i++)
1713 int x1 = x + xy[i][0];
1714 int y1 = y + xy[i][1];
1716 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1718 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1719 MovDir[x][y] = direction[0][i];
1721 MovDir[x][y] = direction[1][i];
1730 MovDir[x][y] = 1 << RND(4);
1732 if (element != EL_BUG &&
1733 element != EL_SPACESHIP &&
1734 element != EL_BD_BUTTERFLY &&
1735 element != EL_BD_FIREFLY)
1738 for (i = 0; i < 4; i++)
1740 int x1 = x + xy[i][0];
1741 int y1 = y + xy[i][1];
1743 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1745 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1747 MovDir[x][y] = direction[0][i];
1750 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1751 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1753 MovDir[x][y] = direction[1][i];
1762 GfxDir[x][y] = MovDir[x][y];
1765 void InitAmoebaNr(int x, int y)
1768 int group_nr = AmoebeNachbarNr(x, y);
1772 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1774 if (AmoebaCnt[i] == 0)
1782 AmoebaNr[x][y] = group_nr;
1783 AmoebaCnt[group_nr]++;
1784 AmoebaCnt2[group_nr]++;
1790 boolean raise_level = FALSE;
1792 if (local_player->MovPos)
1796 if (tape.auto_play) /* tape might already be stopped here */
1797 tape.auto_play_level_solved = TRUE;
1799 if (tape.playing && tape.auto_play)
1800 tape.auto_play_level_solved = TRUE;
1803 local_player->LevelSolved = FALSE;
1805 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1809 if (!tape.playing && setup.sound_loops)
1810 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1811 SND_CTRL_PLAY_LOOP);
1813 while (TimeLeft > 0)
1815 if (!tape.playing && !setup.sound_loops)
1816 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1817 if (TimeLeft > 0 && !(TimeLeft % 10))
1818 RaiseScore(level.score[SC_TIME_BONUS]);
1819 if (TimeLeft > 100 && !(TimeLeft % 10))
1823 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1830 if (!tape.playing && setup.sound_loops)
1831 StopSound(SND_GAME_LEVELTIME_BONUS);
1833 else if (level.time == 0) /* level without time limit */
1835 if (!tape.playing && setup.sound_loops)
1836 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1837 SND_CTRL_PLAY_LOOP);
1839 while (TimePlayed < 999)
1841 if (!tape.playing && !setup.sound_loops)
1842 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1843 if (TimePlayed < 999 && !(TimePlayed % 10))
1844 RaiseScore(level.score[SC_TIME_BONUS]);
1845 if (TimePlayed < 900 && !(TimePlayed % 10))
1849 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1856 if (!tape.playing && setup.sound_loops)
1857 StopSound(SND_GAME_LEVELTIME_BONUS);
1860 /* close exit door after last player */
1861 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1862 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1864 int element = Feld[ExitX][ExitY];
1866 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1867 EL_SP_EXIT_CLOSING);
1869 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1872 /* Hero disappears */
1873 DrawLevelField(ExitX, ExitY);
1879 CloseDoor(DOOR_CLOSE_1);
1884 SaveTape(tape.level_nr); /* Ask to save tape */
1887 if (level_nr == leveldir_current->handicap_level)
1889 leveldir_current->handicap_level++;
1890 SaveLevelSetup_SeriesInfo();
1893 if (level_editor_test_game)
1894 local_player->score = -1; /* no highscore when playing from editor */
1895 else if (level_nr < leveldir_current->last_level)
1896 raise_level = TRUE; /* advance to next level */
1898 if ((hi_pos = NewHiScore()) >= 0)
1900 game_status = GAME_MODE_SCORES;
1901 DrawHallOfFame(hi_pos);
1910 game_status = GAME_MODE_MAIN;
1927 LoadScore(level_nr);
1929 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1930 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1933 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1935 if (local_player->score > highscore[k].Score)
1937 /* player has made it to the hall of fame */
1939 if (k < MAX_SCORE_ENTRIES - 1)
1941 int m = MAX_SCORE_ENTRIES - 1;
1944 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1945 if (!strcmp(setup.player_name, highscore[l].Name))
1947 if (m == k) /* player's new highscore overwrites his old one */
1951 for (l = m; l > k; l--)
1953 strcpy(highscore[l].Name, highscore[l - 1].Name);
1954 highscore[l].Score = highscore[l - 1].Score;
1961 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1962 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1963 highscore[k].Score = local_player->score;
1969 else if (!strncmp(setup.player_name, highscore[k].Name,
1970 MAX_PLAYER_NAME_LEN))
1971 break; /* player already there with a higher score */
1977 SaveScore(level_nr);
1982 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1984 if (player->GfxAction != action || player->GfxDir != dir)
1987 printf("Player frame reset! (%d => %d, %d => %d)\n",
1988 player->GfxAction, action, player->GfxDir, dir);
1991 player->GfxAction = action;
1992 player->GfxDir = dir;
1994 player->StepFrame = 0;
1998 static void ResetRandomAnimationValue(int x, int y)
2000 GfxRandom[x][y] = INIT_GFX_RANDOM();
2003 static void ResetGfxAnimation(int x, int y)
2006 GfxAction[x][y] = ACTION_DEFAULT;
2007 GfxDir[x][y] = MovDir[x][y];
2010 void InitMovingField(int x, int y, int direction)
2012 int element = Feld[x][y];
2013 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2014 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2018 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2019 ResetGfxAnimation(x, y);
2021 MovDir[newx][newy] = MovDir[x][y] = direction;
2022 GfxDir[x][y] = direction;
2024 if (Feld[newx][newy] == EL_EMPTY)
2025 Feld[newx][newy] = EL_BLOCKED;
2027 if (direction == MV_DOWN && CAN_FALL(element))
2028 GfxAction[x][y] = ACTION_FALLING;
2030 GfxAction[x][y] = ACTION_MOVING;
2032 GfxFrame[newx][newy] = GfxFrame[x][y];
2033 GfxRandom[newx][newy] = GfxRandom[x][y];
2034 GfxAction[newx][newy] = GfxAction[x][y];
2035 GfxDir[newx][newy] = GfxDir[x][y];
2038 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2040 int direction = MovDir[x][y];
2041 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2042 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2048 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2050 int oldx = x, oldy = y;
2051 int direction = MovDir[x][y];
2053 if (direction == MV_LEFT)
2055 else if (direction == MV_RIGHT)
2057 else if (direction == MV_UP)
2059 else if (direction == MV_DOWN)
2062 *comes_from_x = oldx;
2063 *comes_from_y = oldy;
2066 int MovingOrBlocked2Element(int x, int y)
2068 int element = Feld[x][y];
2070 if (element == EL_BLOCKED)
2074 Blocked2Moving(x, y, &oldx, &oldy);
2075 return Feld[oldx][oldy];
2081 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2083 /* like MovingOrBlocked2Element(), but if element is moving
2084 and (x,y) is the field the moving element is just leaving,
2085 return EL_BLOCKED instead of the element value */
2086 int element = Feld[x][y];
2088 if (IS_MOVING(x, y))
2090 if (element == EL_BLOCKED)
2094 Blocked2Moving(x, y, &oldx, &oldy);
2095 return Feld[oldx][oldy];
2104 static void RemoveField(int x, int y)
2106 Feld[x][y] = EL_EMPTY;
2113 ChangeDelay[x][y] = 0;
2114 ChangePage[x][y] = -1;
2115 Pushed[x][y] = FALSE;
2117 GfxElement[x][y] = EL_UNDEFINED;
2118 GfxAction[x][y] = ACTION_DEFAULT;
2119 GfxDir[x][y] = MV_NO_MOVING;
2122 void RemoveMovingField(int x, int y)
2124 int oldx = x, oldy = y, newx = x, newy = y;
2125 int element = Feld[x][y];
2126 int next_element = EL_UNDEFINED;
2128 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2131 if (IS_MOVING(x, y))
2133 Moving2Blocked(x, y, &newx, &newy);
2134 if (Feld[newx][newy] != EL_BLOCKED)
2137 else if (element == EL_BLOCKED)
2139 Blocked2Moving(x, y, &oldx, &oldy);
2140 if (!IS_MOVING(oldx, oldy))
2144 if (element == EL_BLOCKED &&
2145 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2146 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2147 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2148 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2149 next_element = get_next_element(Feld[oldx][oldy]);
2151 RemoveField(oldx, oldy);
2152 RemoveField(newx, newy);
2154 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2156 if (next_element != EL_UNDEFINED)
2157 Feld[oldx][oldy] = next_element;
2159 DrawLevelField(oldx, oldy);
2160 DrawLevelField(newx, newy);
2163 void DrawDynamite(int x, int y)
2165 int sx = SCREENX(x), sy = SCREENY(y);
2166 int graphic = el2img(Feld[x][y]);
2169 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2172 if (IS_WALKABLE_INSIDE(Back[x][y]))
2176 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2177 else if (Store[x][y])
2178 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2180 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2183 if (Back[x][y] || Store[x][y])
2184 DrawGraphicThruMask(sx, sy, graphic, frame);
2186 DrawGraphic(sx, sy, graphic, frame);
2188 if (game.emulation == EMU_SUPAPLEX)
2189 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2190 else if (Store[x][y])
2191 DrawGraphicThruMask(sx, sy, graphic, frame);
2193 DrawGraphic(sx, sy, graphic, frame);
2197 void CheckDynamite(int x, int y)
2199 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2203 if (MovDelay[x][y] != 0)
2206 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2213 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2215 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2216 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2217 StopSound(SND_DYNAMITE_ACTIVE);
2219 StopSound(SND_DYNABOMB_ACTIVE);
2225 void RelocatePlayer(int x, int y, int element)
2227 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2229 if (player->GameOver) /* do not reanimate dead player */
2233 RemoveField(x, y); /* temporarily remove newly placed player */
2234 DrawLevelField(x, y);
2237 if (player->present)
2239 while (player->MovPos)
2241 ScrollPlayer(player, SCROLL_GO_ON);
2242 ScrollScreen(NULL, SCROLL_GO_ON);
2248 Delay(GAME_FRAME_DELAY);
2251 DrawPlayer(player); /* needed here only to cleanup last field */
2252 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2254 player->is_moving = FALSE;
2257 Feld[x][y] = element;
2258 InitPlayerField(x, y, element, TRUE);
2260 if (player == local_player)
2262 int scroll_xx = -999, scroll_yy = -999;
2264 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2267 int fx = FX, fy = FY;
2269 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2270 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2271 local_player->jx - MIDPOSX);
2273 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2274 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2275 local_player->jy - MIDPOSY);
2277 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2278 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2283 fx += dx * TILEX / 2;
2284 fy += dy * TILEY / 2;
2286 ScrollLevel(dx, dy);
2289 /* scroll in two steps of half tile size to make things smoother */
2290 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2292 Delay(GAME_FRAME_DELAY);
2294 /* scroll second step to align at full tile size */
2296 Delay(GAME_FRAME_DELAY);
2301 void Explode(int ex, int ey, int phase, int mode)
2305 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2306 int last_phase = num_phase * delay;
2307 int half_phase = (num_phase / 2) * delay;
2308 int first_phase_after_start = EX_PHASE_START + 1;
2310 if (game.explosions_delayed)
2312 ExplodeField[ex][ey] = mode;
2316 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2318 int center_element = Feld[ex][ey];
2321 /* --- This is only really needed (and now handled) in "Impact()". --- */
2322 /* do not explode moving elements that left the explode field in time */
2323 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2324 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2328 if (mode == EX_NORMAL || mode == EX_CENTER)
2329 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2331 /* remove things displayed in background while burning dynamite */
2332 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2335 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2337 /* put moving element to center field (and let it explode there) */
2338 center_element = MovingOrBlocked2Element(ex, ey);
2339 RemoveMovingField(ex, ey);
2340 Feld[ex][ey] = center_element;
2343 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2345 int xx = x - ex + 1;
2346 int yy = y - ey + 1;
2349 if (!IN_LEV_FIELD(x, y) ||
2350 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2351 (x != ex || y != ey)))
2354 element = Feld[x][y];
2356 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2358 element = MovingOrBlocked2Element(x, y);
2360 if (!IS_EXPLOSION_PROOF(element))
2361 RemoveMovingField(x, y);
2367 if (IS_EXPLOSION_PROOF(element))
2370 /* indestructible elements can only explode in center (but not flames) */
2371 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2372 element == EL_FLAMES)
2377 if ((IS_INDESTRUCTIBLE(element) &&
2378 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2379 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2380 element == EL_FLAMES)
2384 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2386 if (IS_ACTIVE_BOMB(element))
2388 /* re-activate things under the bomb like gate or penguin */
2389 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2396 /* save walkable background elements while explosion on same tile */
2398 if (IS_INDESTRUCTIBLE(element))
2399 Back[x][y] = element;
2401 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2402 Back[x][y] = element;
2405 /* ignite explodable elements reached by other explosion */
2406 if (element == EL_EXPLOSION)
2407 element = Store2[x][y];
2410 if (AmoebaNr[x][y] &&
2411 (element == EL_AMOEBA_FULL ||
2412 element == EL_BD_AMOEBA ||
2413 element == EL_AMOEBA_GROWING))
2415 AmoebaCnt[AmoebaNr[x][y]]--;
2416 AmoebaCnt2[AmoebaNr[x][y]]--;
2422 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2424 switch(StorePlayer[ex][ey])
2427 Store[x][y] = EL_EMERALD_RED;
2430 Store[x][y] = EL_EMERALD;
2433 Store[x][y] = EL_EMERALD_PURPLE;
2437 Store[x][y] = EL_EMERALD_YELLOW;
2441 if (game.emulation == EMU_SUPAPLEX)
2442 Store[x][y] = EL_EMPTY;
2444 else if (center_element == EL_MOLE)
2445 Store[x][y] = EL_EMERALD_RED;
2446 else if (center_element == EL_PENGUIN)
2447 Store[x][y] = EL_EMERALD_PURPLE;
2448 else if (center_element == EL_BUG)
2449 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2450 else if (center_element == EL_BD_BUTTERFLY)
2451 Store[x][y] = EL_BD_DIAMOND;
2452 else if (center_element == EL_SP_ELECTRON)
2453 Store[x][y] = EL_SP_INFOTRON;
2454 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2455 Store[x][y] = level.amoeba_content;
2456 else if (center_element == EL_YAMYAM)
2457 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2458 else if (IS_CUSTOM_ELEMENT(center_element) &&
2459 element_info[center_element].content[xx][yy] != EL_EMPTY)
2460 Store[x][y] = element_info[center_element].content[xx][yy];
2461 else if (element == EL_WALL_EMERALD)
2462 Store[x][y] = EL_EMERALD;
2463 else if (element == EL_WALL_DIAMOND)
2464 Store[x][y] = EL_DIAMOND;
2465 else if (element == EL_WALL_BD_DIAMOND)
2466 Store[x][y] = EL_BD_DIAMOND;
2467 else if (element == EL_WALL_EMERALD_YELLOW)
2468 Store[x][y] = EL_EMERALD_YELLOW;
2469 else if (element == EL_WALL_EMERALD_RED)
2470 Store[x][y] = EL_EMERALD_RED;
2471 else if (element == EL_WALL_EMERALD_PURPLE)
2472 Store[x][y] = EL_EMERALD_PURPLE;
2473 else if (element == EL_WALL_PEARL)
2474 Store[x][y] = EL_PEARL;
2475 else if (element == EL_WALL_CRYSTAL)
2476 Store[x][y] = EL_CRYSTAL;
2477 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2478 Store[x][y] = element_info[element].content[1][1];
2480 Store[x][y] = EL_EMPTY;
2482 if (x != ex || y != ey ||
2483 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2484 Store2[x][y] = element;
2487 if (AmoebaNr[x][y] &&
2488 (element == EL_AMOEBA_FULL ||
2489 element == EL_BD_AMOEBA ||
2490 element == EL_AMOEBA_GROWING))
2492 AmoebaCnt[AmoebaNr[x][y]]--;
2493 AmoebaCnt2[AmoebaNr[x][y]]--;
2499 MovDir[x][y] = MovPos[x][y] = 0;
2500 GfxDir[x][y] = MovDir[x][y];
2505 Feld[x][y] = EL_EXPLOSION;
2507 GfxElement[x][y] = center_element;
2509 GfxElement[x][y] = EL_UNDEFINED;
2512 ExplodePhase[x][y] = 1;
2516 if (center_element == EL_YAMYAM)
2517 game.yamyam_content_nr =
2518 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2529 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2533 /* activate this even in non-DEBUG version until cause for crash in
2534 getGraphicAnimationFrame() (see below) is found and eliminated */
2538 if (GfxElement[x][y] == EL_UNDEFINED)
2541 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2542 printf("Explode(): This should never happen!\n");
2545 GfxElement[x][y] = EL_EMPTY;
2549 if (phase == first_phase_after_start)
2551 int element = Store2[x][y];
2553 if (element == EL_BLACK_ORB)
2555 Feld[x][y] = Store2[x][y];
2560 else if (phase == half_phase)
2562 int element = Store2[x][y];
2564 if (IS_PLAYER(x, y))
2565 KillHeroUnlessProtected(x, y);
2566 else if (CAN_EXPLODE_BY_FIRE(element))
2568 Feld[x][y] = Store2[x][y];
2572 else if (element == EL_AMOEBA_TO_DIAMOND)
2573 AmoebeUmwandeln(x, y);
2576 if (phase == last_phase)
2580 element = Feld[x][y] = Store[x][y];
2581 Store[x][y] = Store2[x][y] = 0;
2582 GfxElement[x][y] = EL_UNDEFINED;
2584 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2585 element = Feld[x][y] = Back[x][y];
2588 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2589 GfxDir[x][y] = MV_NO_MOVING;
2590 ChangeDelay[x][y] = 0;
2591 ChangePage[x][y] = -1;
2593 InitField(x, y, FALSE);
2594 if (CAN_MOVE(element))
2596 DrawLevelField(x, y);
2598 TestIfElementTouchesCustomElement(x, y);
2600 if (GFX_CRUMBLED(element))
2601 DrawLevelFieldCrumbledSandNeighbours(x, y);
2603 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2604 StorePlayer[x][y] = 0;
2606 if (ELEM_IS_PLAYER(element))
2607 RelocatePlayer(x, y, element);
2609 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2612 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2614 int stored = Store[x][y];
2615 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2616 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2619 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2622 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2623 element_info[GfxElement[x][y]].token_name,
2628 DrawLevelFieldCrumbledSand(x, y);
2630 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2632 DrawLevelElement(x, y, Back[x][y]);
2633 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2635 else if (IS_WALKABLE_UNDER(Back[x][y]))
2637 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2638 DrawLevelElementThruMask(x, y, Back[x][y]);
2640 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2641 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2645 void DynaExplode(int ex, int ey)
2648 int dynabomb_element = Feld[ex][ey];
2649 int dynabomb_size = 1;
2650 boolean dynabomb_xl = FALSE;
2651 struct PlayerInfo *player;
2652 static int xy[4][2] =
2660 if (IS_ACTIVE_BOMB(dynabomb_element))
2662 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2663 dynabomb_size = player->dynabomb_size;
2664 dynabomb_xl = player->dynabomb_xl;
2665 player->dynabombs_left++;
2668 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2670 for (i = 0; i < 4; i++)
2672 for (j = 1; j <= dynabomb_size; j++)
2674 int x = ex + j * xy[i % 4][0];
2675 int y = ey + j * xy[i % 4][1];
2678 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2681 element = Feld[x][y];
2683 /* do not restart explosions of fields with active bombs */
2684 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2687 Explode(x, y, EX_PHASE_START, EX_BORDER);
2689 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2690 if (element != EL_EMPTY &&
2691 element != EL_SAND &&
2692 element != EL_EXPLOSION &&
2699 void Bang(int x, int y)
2702 int element = MovingOrBlocked2Element(x, y);
2704 int element = Feld[x][y];
2708 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2710 if (IS_PLAYER(x, y))
2713 struct PlayerInfo *player = PLAYERINFO(x, y);
2715 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2716 player->element_nr);
2721 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2723 if (game.emulation == EMU_SUPAPLEX)
2724 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2726 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2731 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2739 case EL_BD_BUTTERFLY:
2742 case EL_DARK_YAMYAM:
2746 RaiseScoreElement(element);
2747 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2749 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2750 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2751 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2752 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2753 case EL_DYNABOMB_INCREASE_NUMBER:
2754 case EL_DYNABOMB_INCREASE_SIZE:
2755 case EL_DYNABOMB_INCREASE_POWER:
2760 case EL_LAMP_ACTIVE:
2761 if (IS_PLAYER(x, y))
2762 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2764 Explode(x, y, EX_PHASE_START, EX_CENTER);
2767 if (CAN_EXPLODE_DYNA(element))
2769 else if (CAN_EXPLODE_1X1(element))
2770 Explode(x, y, EX_PHASE_START, EX_CENTER);
2772 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2776 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2779 void SplashAcid(int x, int y)
2781 int element = Feld[x][y];
2783 if (element != EL_ACID_SPLASH_LEFT &&
2784 element != EL_ACID_SPLASH_RIGHT)
2786 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2788 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2789 (!IN_LEV_FIELD(x-1, y-1) ||
2790 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2791 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2793 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2794 (!IN_LEV_FIELD(x+1, y-1) ||
2795 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2796 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2800 static void InitBeltMovement()
2802 static int belt_base_element[4] =
2804 EL_CONVEYOR_BELT_1_LEFT,
2805 EL_CONVEYOR_BELT_2_LEFT,
2806 EL_CONVEYOR_BELT_3_LEFT,
2807 EL_CONVEYOR_BELT_4_LEFT
2809 static int belt_base_active_element[4] =
2811 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2812 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2813 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2814 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2819 /* set frame order for belt animation graphic according to belt direction */
2820 for (i = 0; i < 4; i++)
2824 for (j = 0; j < 3; j++)
2826 int element = belt_base_active_element[belt_nr] + j;
2827 int graphic = el2img(element);
2829 if (game.belt_dir[i] == MV_LEFT)
2830 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2832 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2836 for (y = 0; y < lev_fieldy; y++)
2838 for (x = 0; x < lev_fieldx; x++)
2840 int element = Feld[x][y];
2842 for (i = 0; i < 4; i++)
2844 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2846 int e_belt_nr = getBeltNrFromBeltElement(element);
2849 if (e_belt_nr == belt_nr)
2851 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2853 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2861 static void ToggleBeltSwitch(int x, int y)
2863 static int belt_base_element[4] =
2865 EL_CONVEYOR_BELT_1_LEFT,
2866 EL_CONVEYOR_BELT_2_LEFT,
2867 EL_CONVEYOR_BELT_3_LEFT,
2868 EL_CONVEYOR_BELT_4_LEFT
2870 static int belt_base_active_element[4] =
2872 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2873 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2874 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2875 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2877 static int belt_base_switch_element[4] =
2879 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2880 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2881 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2882 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2884 static int belt_move_dir[4] =
2892 int element = Feld[x][y];
2893 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2894 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2895 int belt_dir = belt_move_dir[belt_dir_nr];
2898 if (!IS_BELT_SWITCH(element))
2901 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2902 game.belt_dir[belt_nr] = belt_dir;
2904 if (belt_dir_nr == 3)
2907 /* set frame order for belt animation graphic according to belt direction */
2908 for (i = 0; i < 3; i++)
2910 int element = belt_base_active_element[belt_nr] + i;
2911 int graphic = el2img(element);
2913 if (belt_dir == MV_LEFT)
2914 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2916 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2919 for (yy = 0; yy < lev_fieldy; yy++)
2921 for (xx = 0; xx < lev_fieldx; xx++)
2923 int element = Feld[xx][yy];
2925 if (IS_BELT_SWITCH(element))
2927 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2929 if (e_belt_nr == belt_nr)
2931 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2932 DrawLevelField(xx, yy);
2935 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2937 int e_belt_nr = getBeltNrFromBeltElement(element);
2939 if (e_belt_nr == belt_nr)
2941 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2943 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2944 DrawLevelField(xx, yy);
2947 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2949 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2951 if (e_belt_nr == belt_nr)
2953 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2955 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2956 DrawLevelField(xx, yy);
2963 static void ToggleSwitchgateSwitch(int x, int y)
2967 game.switchgate_pos = !game.switchgate_pos;
2969 for (yy = 0; yy < lev_fieldy; yy++)
2971 for (xx = 0; xx < lev_fieldx; xx++)
2973 int element = Feld[xx][yy];
2975 if (element == EL_SWITCHGATE_SWITCH_UP ||
2976 element == EL_SWITCHGATE_SWITCH_DOWN)
2978 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2979 DrawLevelField(xx, yy);
2981 else if (element == EL_SWITCHGATE_OPEN ||
2982 element == EL_SWITCHGATE_OPENING)
2984 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2986 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2988 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2991 else if (element == EL_SWITCHGATE_CLOSED ||
2992 element == EL_SWITCHGATE_CLOSING)
2994 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2996 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2998 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3005 static int getInvisibleActiveFromInvisibleElement(int element)
3007 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3008 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3009 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3013 static int getInvisibleFromInvisibleActiveElement(int element)
3015 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3016 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3017 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3021 static void RedrawAllLightSwitchesAndInvisibleElements()
3025 for (y = 0; y < lev_fieldy; y++)
3027 for (x = 0; x < lev_fieldx; x++)
3029 int element = Feld[x][y];
3031 if (element == EL_LIGHT_SWITCH &&
3032 game.light_time_left > 0)
3034 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3035 DrawLevelField(x, y);
3037 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3038 game.light_time_left == 0)
3040 Feld[x][y] = EL_LIGHT_SWITCH;
3041 DrawLevelField(x, y);
3043 else if (element == EL_INVISIBLE_STEELWALL ||
3044 element == EL_INVISIBLE_WALL ||
3045 element == EL_INVISIBLE_SAND)
3047 if (game.light_time_left > 0)
3048 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3050 DrawLevelField(x, y);
3052 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3053 element == EL_INVISIBLE_WALL_ACTIVE ||
3054 element == EL_INVISIBLE_SAND_ACTIVE)
3056 if (game.light_time_left == 0)
3057 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3059 DrawLevelField(x, y);
3065 static void ToggleLightSwitch(int x, int y)
3067 int element = Feld[x][y];
3069 game.light_time_left =
3070 (element == EL_LIGHT_SWITCH ?
3071 level.time_light * FRAMES_PER_SECOND : 0);
3073 RedrawAllLightSwitchesAndInvisibleElements();
3076 static void ActivateTimegateSwitch(int x, int y)
3080 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3082 for (yy = 0; yy < lev_fieldy; yy++)
3084 for (xx = 0; xx < lev_fieldx; xx++)
3086 int element = Feld[xx][yy];
3088 if (element == EL_TIMEGATE_CLOSED ||
3089 element == EL_TIMEGATE_CLOSING)
3091 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3092 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3096 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3098 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3099 DrawLevelField(xx, yy);
3106 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3109 inline static int getElementMoveStepsize(int x, int y)
3111 int element = Feld[x][y];
3112 int direction = MovDir[x][y];
3113 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3114 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3115 int horiz_move = (dx != 0);
3116 int sign = (horiz_move ? dx : dy);
3117 int step = sign * element_info[element].move_stepsize;
3119 /* special values for move stepsize for spring and things on conveyor belt */
3122 if (CAN_FALL(element) &&
3123 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3124 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3125 else if (element == EL_SPRING)
3126 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3132 void Impact(int x, int y)
3134 boolean lastline = (y == lev_fieldy-1);
3135 boolean object_hit = FALSE;
3136 boolean impact = (lastline || object_hit);
3137 int element = Feld[x][y];
3138 int smashed = EL_UNDEFINED;
3140 if (!lastline) /* check if element below was hit */
3142 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3145 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3146 MovDir[x][y + 1] != MV_DOWN ||
3147 MovPos[x][y + 1] <= TILEY / 2));
3150 object_hit = !IS_FREE(x, y + 1);
3153 /* do not smash moving elements that left the smashed field in time */
3154 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3155 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3159 smashed = MovingOrBlocked2Element(x, y + 1);
3161 impact = (lastline || object_hit);
3164 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3170 /* only reset graphic animation if graphic really changes after impact */
3172 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3174 ResetGfxAnimation(x, y);
3175 DrawLevelField(x, y);
3178 if (impact && CAN_EXPLODE_IMPACT(element))
3183 else if (impact && element == EL_PEARL)
3185 Feld[x][y] = EL_PEARL_BREAKING;
3186 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3189 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3191 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3196 if (impact && element == EL_AMOEBA_DROP)
3198 if (object_hit && IS_PLAYER(x, y + 1))
3199 KillHeroUnlessProtected(x, y + 1);
3200 else if (object_hit && smashed == EL_PENGUIN)
3204 Feld[x][y] = EL_AMOEBA_GROWING;
3205 Store[x][y] = EL_AMOEBA_WET;
3207 ResetRandomAnimationValue(x, y);
3212 if (object_hit) /* check which object was hit */
3214 if (CAN_PASS_MAGIC_WALL(element) &&
3215 (smashed == EL_MAGIC_WALL ||
3216 smashed == EL_BD_MAGIC_WALL))
3219 int activated_magic_wall =
3220 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3221 EL_BD_MAGIC_WALL_ACTIVE);
3223 /* activate magic wall / mill */
3224 for (yy = 0; yy < lev_fieldy; yy++)
3225 for (xx = 0; xx < lev_fieldx; xx++)
3226 if (Feld[xx][yy] == smashed)
3227 Feld[xx][yy] = activated_magic_wall;
3229 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3230 game.magic_wall_active = TRUE;
3232 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3233 SND_MAGIC_WALL_ACTIVATING :
3234 SND_BD_MAGIC_WALL_ACTIVATING));
3237 if (IS_PLAYER(x, y + 1))
3239 if (CAN_SMASH_PLAYER(element))
3241 KillHeroUnlessProtected(x, y + 1);
3245 else if (smashed == EL_PENGUIN)
3247 if (CAN_SMASH_PLAYER(element))
3253 else if (element == EL_BD_DIAMOND)
3255 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3261 else if ((element == EL_SP_INFOTRON ||
3262 element == EL_SP_ZONK) &&
3263 (smashed == EL_SP_SNIKSNAK ||
3264 smashed == EL_SP_ELECTRON ||
3265 smashed == EL_SP_DISK_ORANGE))
3271 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3277 else if (CAN_SMASH_EVERYTHING(element))
3279 if (IS_CLASSIC_ENEMY(smashed) ||
3280 CAN_EXPLODE_SMASHED(smashed))
3285 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3287 if (smashed == EL_LAMP ||
3288 smashed == EL_LAMP_ACTIVE)
3293 else if (smashed == EL_NUT)
3295 Feld[x][y + 1] = EL_NUT_BREAKING;
3296 PlayLevelSound(x, y, SND_NUT_BREAKING);
3297 RaiseScoreElement(EL_NUT);
3300 else if (smashed == EL_PEARL)
3302 Feld[x][y + 1] = EL_PEARL_BREAKING;
3303 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3306 else if (smashed == EL_DIAMOND)
3308 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3309 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3312 else if (IS_BELT_SWITCH(smashed))
3314 ToggleBeltSwitch(x, y + 1);
3316 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3317 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3319 ToggleSwitchgateSwitch(x, y + 1);
3321 else if (smashed == EL_LIGHT_SWITCH ||
3322 smashed == EL_LIGHT_SWITCH_ACTIVE)
3324 ToggleLightSwitch(x, y + 1);
3328 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3330 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3331 CE_OTHER_IS_SWITCHING);
3332 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3338 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3343 /* play sound of magic wall / mill */
3345 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3346 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3348 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3349 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3350 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3351 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3356 /* play sound of object that hits the ground */
3357 if (lastline || object_hit)
3358 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3361 inline static void TurnRoundExt(int x, int y)
3373 { 0, 0 }, { 0, 0 }, { 0, 0 },
3378 int left, right, back;
3382 { MV_DOWN, MV_UP, MV_RIGHT },
3383 { MV_UP, MV_DOWN, MV_LEFT },
3385 { MV_LEFT, MV_RIGHT, MV_DOWN },
3389 { MV_RIGHT, MV_LEFT, MV_UP }
3392 int element = Feld[x][y];
3393 int move_pattern = element_info[element].move_pattern;
3395 int old_move_dir = MovDir[x][y];
3396 int left_dir = turn[old_move_dir].left;
3397 int right_dir = turn[old_move_dir].right;
3398 int back_dir = turn[old_move_dir].back;
3400 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3401 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3402 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3403 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3405 int left_x = x + left_dx, left_y = y + left_dy;
3406 int right_x = x + right_dx, right_y = y + right_dy;
3407 int move_x = x + move_dx, move_y = y + move_dy;
3411 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3413 TestIfBadThingTouchesOtherBadThing(x, y);
3415 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3416 MovDir[x][y] = right_dir;
3417 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3418 MovDir[x][y] = left_dir;
3420 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3422 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3425 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3426 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3428 TestIfBadThingTouchesOtherBadThing(x, y);
3430 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3431 MovDir[x][y] = left_dir;
3432 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3433 MovDir[x][y] = right_dir;
3435 if ((element == EL_SPACESHIP ||
3436 element == EL_SP_SNIKSNAK ||
3437 element == EL_SP_ELECTRON)
3438 && MovDir[x][y] != old_move_dir)
3440 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3443 else if (element == EL_YAMYAM)
3445 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3446 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3448 if (can_turn_left && can_turn_right)
3449 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3450 else if (can_turn_left)
3451 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3452 else if (can_turn_right)
3453 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3455 MovDir[x][y] = back_dir;
3457 MovDelay[x][y] = 16 + 16 * RND(3);
3459 else if (element == EL_DARK_YAMYAM)
3461 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3462 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3464 if (can_turn_left && can_turn_right)
3465 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3466 else if (can_turn_left)
3467 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3468 else if (can_turn_right)
3469 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3471 MovDir[x][y] = back_dir;
3473 MovDelay[x][y] = 16 + 16 * RND(3);
3475 else if (element == EL_PACMAN)
3477 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3478 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3480 if (can_turn_left && can_turn_right)
3481 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3482 else if (can_turn_left)
3483 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3484 else if (can_turn_right)
3485 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3487 MovDir[x][y] = back_dir;
3489 MovDelay[x][y] = 6 + RND(40);
3491 else if (element == EL_PIG)
3493 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3494 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3495 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3496 boolean should_turn_left, should_turn_right, should_move_on;
3498 int rnd = RND(rnd_value);
3500 should_turn_left = (can_turn_left &&
3502 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3503 y + back_dy + left_dy)));
3504 should_turn_right = (can_turn_right &&
3506 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3507 y + back_dy + right_dy)));
3508 should_move_on = (can_move_on &&
3511 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3512 y + move_dy + left_dy) ||
3513 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3514 y + move_dy + right_dy)));
3516 if (should_turn_left || should_turn_right || should_move_on)
3518 if (should_turn_left && should_turn_right && should_move_on)
3519 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3520 rnd < 2 * rnd_value / 3 ? right_dir :
3522 else if (should_turn_left && should_turn_right)
3523 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3524 else if (should_turn_left && should_move_on)
3525 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3526 else if (should_turn_right && should_move_on)
3527 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3528 else if (should_turn_left)
3529 MovDir[x][y] = left_dir;
3530 else if (should_turn_right)
3531 MovDir[x][y] = right_dir;
3532 else if (should_move_on)
3533 MovDir[x][y] = old_move_dir;
3535 else if (can_move_on && rnd > rnd_value / 8)
3536 MovDir[x][y] = old_move_dir;
3537 else if (can_turn_left && can_turn_right)
3538 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3539 else if (can_turn_left && rnd > rnd_value / 8)
3540 MovDir[x][y] = left_dir;
3541 else if (can_turn_right && rnd > rnd_value/8)
3542 MovDir[x][y] = right_dir;
3544 MovDir[x][y] = back_dir;
3546 xx = x + move_xy[MovDir[x][y]].x;
3547 yy = y + move_xy[MovDir[x][y]].y;
3549 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3550 MovDir[x][y] = old_move_dir;
3554 else if (element == EL_DRAGON)
3556 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3557 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3558 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3560 int rnd = RND(rnd_value);
3563 if (FrameCounter < 1 && x == 0 && y == 29)
3564 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3567 if (can_move_on && rnd > rnd_value / 8)
3568 MovDir[x][y] = old_move_dir;
3569 else if (can_turn_left && can_turn_right)
3570 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3571 else if (can_turn_left && rnd > rnd_value / 8)
3572 MovDir[x][y] = left_dir;
3573 else if (can_turn_right && rnd > rnd_value / 8)
3574 MovDir[x][y] = right_dir;
3576 MovDir[x][y] = back_dir;
3578 xx = x + move_xy[MovDir[x][y]].x;
3579 yy = y + move_xy[MovDir[x][y]].y;
3582 if (FrameCounter < 1 && x == 0 && y == 29)
3583 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3584 xx, yy, Feld[xx][yy],
3589 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3590 MovDir[x][y] = old_move_dir;
3592 if (!IS_FREE(xx, yy))
3593 MovDir[x][y] = old_move_dir;
3597 if (FrameCounter < 1 && x == 0 && y == 29)
3598 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3603 else if (element == EL_MOLE)
3605 boolean can_move_on =
3606 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3607 IS_AMOEBOID(Feld[move_x][move_y]) ||
3608 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3611 boolean can_turn_left =
3612 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3613 IS_AMOEBOID(Feld[left_x][left_y])));
3615 boolean can_turn_right =
3616 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3617 IS_AMOEBOID(Feld[right_x][right_y])));
3619 if (can_turn_left && can_turn_right)
3620 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3621 else if (can_turn_left)
3622 MovDir[x][y] = left_dir;
3624 MovDir[x][y] = right_dir;
3627 if (MovDir[x][y] != old_move_dir)
3630 else if (element == EL_BALLOON)
3632 MovDir[x][y] = game.balloon_dir;
3635 else if (element == EL_SPRING)
3637 if (MovDir[x][y] & MV_HORIZONTAL &&
3638 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3639 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3640 MovDir[x][y] = MV_NO_MOVING;
3644 else if (element == EL_ROBOT ||
3645 element == EL_SATELLITE ||
3646 element == EL_PENGUIN)
3648 int attr_x = -1, attr_y = -1;
3659 for (i = 0; i < MAX_PLAYERS; i++)
3661 struct PlayerInfo *player = &stored_player[i];
3662 int jx = player->jx, jy = player->jy;
3664 if (!player->active)
3668 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3676 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3682 if (element == EL_PENGUIN)
3685 static int xy[4][2] =
3693 for (i = 0; i < 4; i++)
3695 int ex = x + xy[i % 4][0];
3696 int ey = y + xy[i % 4][1];
3698 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3707 MovDir[x][y] = MV_NO_MOVING;
3709 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3710 else if (attr_x > x)
3711 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3713 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3714 else if (attr_y > y)
3715 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3717 if (element == EL_ROBOT)
3721 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3722 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3723 Moving2Blocked(x, y, &newx, &newy);
3725 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3726 MovDelay[x][y] = 8 + 8 * !RND(3);
3728 MovDelay[x][y] = 16;
3730 else if (element == EL_PENGUIN)
3736 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3738 boolean first_horiz = RND(2);
3739 int new_move_dir = MovDir[x][y];
3742 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3743 Moving2Blocked(x, y, &newx, &newy);
3745 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3749 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3750 Moving2Blocked(x, y, &newx, &newy);
3752 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3755 MovDir[x][y] = old_move_dir;
3759 else /* (element == EL_SATELLITE) */
3765 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3767 boolean first_horiz = RND(2);
3768 int new_move_dir = MovDir[x][y];
3771 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3772 Moving2Blocked(x, y, &newx, &newy);
3774 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3778 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3779 Moving2Blocked(x, y, &newx, &newy);
3781 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3784 MovDir[x][y] = old_move_dir;
3789 else if (move_pattern == MV_TURNING_LEFT ||
3790 move_pattern == MV_TURNING_RIGHT ||
3791 move_pattern == MV_TURNING_LEFT_RIGHT ||
3792 move_pattern == MV_TURNING_RIGHT_LEFT ||
3793 move_pattern == MV_TURNING_RANDOM ||
3794 move_pattern == MV_ALL_DIRECTIONS)
3796 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3797 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3799 if (move_pattern == MV_TURNING_LEFT)
3800 MovDir[x][y] = left_dir;
3801 else if (move_pattern == MV_TURNING_RIGHT)
3802 MovDir[x][y] = right_dir;
3803 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3804 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3805 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3806 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3807 else if (move_pattern == MV_TURNING_RANDOM)
3808 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3809 can_turn_right && !can_turn_left ? right_dir :
3810 RND(2) ? left_dir : right_dir);
3811 else if (can_turn_left && can_turn_right)
3812 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3813 else if (can_turn_left)
3814 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3815 else if (can_turn_right)
3816 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3818 MovDir[x][y] = back_dir;
3820 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3822 else if (move_pattern == MV_HORIZONTAL ||
3823 move_pattern == MV_VERTICAL)
3825 if (move_pattern & old_move_dir)
3826 MovDir[x][y] = back_dir;
3827 else if (move_pattern == MV_HORIZONTAL)
3828 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3829 else if (move_pattern == MV_VERTICAL)
3830 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3832 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3834 else if (move_pattern & MV_ANY_DIRECTION)
3836 MovDir[x][y] = move_pattern;
3837 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3839 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3841 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3842 MovDir[x][y] = left_dir;
3843 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3844 MovDir[x][y] = right_dir;
3846 if (MovDir[x][y] != old_move_dir)
3847 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3849 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3851 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3852 MovDir[x][y] = right_dir;
3853 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3854 MovDir[x][y] = left_dir;
3856 if (MovDir[x][y] != old_move_dir)
3857 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3859 else if (move_pattern == MV_TOWARDS_PLAYER ||
3860 move_pattern == MV_AWAY_FROM_PLAYER)
3862 int attr_x = -1, attr_y = -1;
3864 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3875 for (i = 0; i < MAX_PLAYERS; i++)
3877 struct PlayerInfo *player = &stored_player[i];
3878 int jx = player->jx, jy = player->jy;
3880 if (!player->active)
3884 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3892 MovDir[x][y] = MV_NO_MOVING;
3894 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3895 else if (attr_x > x)
3896 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3898 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3899 else if (attr_y > y)
3900 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3904 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3906 boolean first_horiz = RND(2);
3907 int new_move_dir = MovDir[x][y];
3910 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3911 Moving2Blocked(x, y, &newx, &newy);
3913 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3917 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3918 Moving2Blocked(x, y, &newx, &newy);
3920 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3923 MovDir[x][y] = old_move_dir;
3926 else if (move_pattern == MV_WHEN_PUSHED ||
3927 move_pattern == MV_WHEN_DROPPED)
3929 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3930 MovDir[x][y] = MV_NO_MOVING;
3934 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3935 element == EL_MAZE_RUNNER)
3937 static int test_xy[7][2] =
3947 static int test_dir[7] =
3957 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3958 int move_preference = -1000000; /* start with very low preference */
3959 int new_move_dir = MV_NO_MOVING;
3960 int start_test = RND(4);
3963 for (i = 0; i < 4; i++)
3965 int move_dir = test_dir[start_test + i];
3966 int move_dir_preference;
3968 xx = x + test_xy[start_test + i][0];
3969 yy = y + test_xy[start_test + i][1];
3971 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3972 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3974 new_move_dir = move_dir;
3979 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3982 move_dir_preference = -1 * RunnerVisit[xx][yy];
3983 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3984 move_dir_preference = PlayerVisit[xx][yy];
3986 if (move_dir_preference > move_preference)
3988 /* prefer field that has not been visited for the longest time */
3989 move_preference = move_dir_preference;
3990 new_move_dir = move_dir;
3992 else if (move_dir_preference == move_preference &&
3993 move_dir == old_move_dir)
3995 /* prefer last direction when all directions are preferred equally */
3996 move_preference = move_dir_preference;
3997 new_move_dir = move_dir;
4001 MovDir[x][y] = new_move_dir;
4002 if (old_move_dir != new_move_dir)
4007 static void TurnRound(int x, int y)
4009 int direction = MovDir[x][y];
4012 GfxDir[x][y] = MovDir[x][y];
4018 GfxDir[x][y] = MovDir[x][y];
4021 if (direction != MovDir[x][y])
4026 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4029 GfxAction[x][y] = ACTION_WAITING;
4033 static boolean JustBeingPushed(int x, int y)
4037 for (i = 0; i < MAX_PLAYERS; i++)
4039 struct PlayerInfo *player = &stored_player[i];
4041 if (player->active && player->is_pushing && player->MovPos)
4043 int next_jx = player->jx + (player->jx - player->last_jx);
4044 int next_jy = player->jy + (player->jy - player->last_jy);
4046 if (x == next_jx && y == next_jy)
4054 void StartMoving(int x, int y)
4056 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4057 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4058 int element = Feld[x][y];
4064 if (MovDelay[x][y] == 0)
4065 GfxAction[x][y] = ACTION_DEFAULT;
4067 /* !!! this should be handled more generic (not only for mole) !!! */
4068 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4069 GfxAction[x][y] = ACTION_DEFAULT;
4072 if (CAN_FALL(element) && y < lev_fieldy - 1)
4074 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4075 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4076 if (JustBeingPushed(x, y))
4079 if (element == EL_QUICKSAND_FULL)
4081 if (IS_FREE(x, y + 1))
4083 InitMovingField(x, y, MV_DOWN);
4084 started_moving = TRUE;
4086 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4087 Store[x][y] = EL_ROCK;
4089 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4091 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4094 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4096 if (!MovDelay[x][y])
4097 MovDelay[x][y] = TILEY + 1;
4106 Feld[x][y] = EL_QUICKSAND_EMPTY;
4107 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4108 Store[x][y + 1] = Store[x][y];
4111 PlayLevelSoundAction(x, y, ACTION_FILLING);
4113 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4117 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4118 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4120 InitMovingField(x, y, MV_DOWN);
4121 started_moving = TRUE;
4123 Feld[x][y] = EL_QUICKSAND_FILLING;
4124 Store[x][y] = element;
4126 PlayLevelSoundAction(x, y, ACTION_FILLING);
4128 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4131 else if (element == EL_MAGIC_WALL_FULL)
4133 if (IS_FREE(x, y + 1))
4135 InitMovingField(x, y, MV_DOWN);
4136 started_moving = TRUE;
4138 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4139 Store[x][y] = EL_CHANGED(Store[x][y]);
4141 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4143 if (!MovDelay[x][y])
4144 MovDelay[x][y] = TILEY/4 + 1;
4153 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4154 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4155 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4159 else if (element == EL_BD_MAGIC_WALL_FULL)
4161 if (IS_FREE(x, y + 1))
4163 InitMovingField(x, y, MV_DOWN);
4164 started_moving = TRUE;
4166 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4167 Store[x][y] = EL_CHANGED2(Store[x][y]);
4169 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4171 if (!MovDelay[x][y])
4172 MovDelay[x][y] = TILEY/4 + 1;
4181 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4182 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4183 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4187 else if (CAN_PASS_MAGIC_WALL(element) &&
4188 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4189 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4191 InitMovingField(x, y, MV_DOWN);
4192 started_moving = TRUE;
4195 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4196 EL_BD_MAGIC_WALL_FILLING);
4197 Store[x][y] = element;
4200 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4202 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4207 InitMovingField(x, y, MV_DOWN);
4208 started_moving = TRUE;
4210 Store[x][y] = EL_ACID;
4212 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4213 GfxAction[x][y + 1] = ACTION_ACTIVE;
4217 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4218 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4219 (Feld[x][y + 1] == EL_BLOCKED)) ||
4220 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4221 CAN_SMASH(element) && WasJustFalling[x][y] &&
4222 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4226 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4227 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4228 WasJustMoving[x][y] && !Pushed[x][y + 1])
4230 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4231 WasJustMoving[x][y])
4236 /* this is needed for a special case not covered by calling "Impact()"
4237 from "ContinueMoving()": if an element moves to a tile directly below
4238 another element which was just falling on that tile (which was empty
4239 in the previous frame), the falling element above would just stop
4240 instead of smashing the element below (in previous version, the above
4241 element was just checked for "moving" instead of "falling", resulting
4242 in incorrect smashes caused by horizontal movement of the above
4243 element; also, the case of the player being the element to smash was
4244 simply not covered here... :-/ ) */
4247 WasJustMoving[x][y] = 0;
4248 WasJustFalling[x][y] = 0;
4253 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4255 if (MovDir[x][y] == MV_NO_MOVING)
4257 InitMovingField(x, y, MV_DOWN);
4258 started_moving = TRUE;
4261 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4263 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4264 MovDir[x][y] = MV_DOWN;
4266 InitMovingField(x, y, MV_DOWN);
4267 started_moving = TRUE;
4269 else if (element == EL_AMOEBA_DROP)
4271 Feld[x][y] = EL_AMOEBA_GROWING;
4272 Store[x][y] = EL_AMOEBA_WET;
4274 /* Store[x][y + 1] must be zero, because:
4275 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4278 #if OLD_GAME_BEHAVIOUR
4279 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4281 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4282 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4283 element != EL_DX_SUPABOMB)
4286 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4287 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4288 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4289 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4292 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4293 (IS_FREE(x - 1, y + 1) ||
4294 Feld[x - 1][y + 1] == EL_ACID));
4295 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4296 (IS_FREE(x + 1, y + 1) ||
4297 Feld[x + 1][y + 1] == EL_ACID));
4298 boolean can_fall_any = (can_fall_left || can_fall_right);
4299 boolean can_fall_both = (can_fall_left && can_fall_right);
4301 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4303 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4305 if (slippery_type == SLIPPERY_ONLY_LEFT)
4306 can_fall_right = FALSE;
4307 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4308 can_fall_left = FALSE;
4309 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4310 can_fall_right = FALSE;
4311 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4312 can_fall_left = FALSE;
4314 can_fall_any = (can_fall_left || can_fall_right);
4315 can_fall_both = (can_fall_left && can_fall_right);
4320 if (can_fall_both &&
4321 (game.emulation != EMU_BOULDERDASH &&
4322 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4323 can_fall_left = !(can_fall_right = RND(2));
4325 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4326 started_moving = TRUE;
4329 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4331 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4332 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4333 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4334 int belt_dir = game.belt_dir[belt_nr];
4336 if ((belt_dir == MV_LEFT && left_is_free) ||
4337 (belt_dir == MV_RIGHT && right_is_free))
4339 InitMovingField(x, y, belt_dir);
4340 started_moving = TRUE;
4342 GfxAction[x][y] = ACTION_DEFAULT;
4347 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4348 if (CAN_MOVE(element) && !started_moving)
4350 int move_pattern = element_info[element].move_pattern;
4353 Moving2Blocked(x, y, &newx, &newy);
4356 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4359 if ((element == EL_SATELLITE ||
4360 element == EL_BALLOON ||
4361 element == EL_SPRING)
4362 && JustBeingPushed(x, y))
4367 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4368 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4369 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4372 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4373 element, element_info[element].token_name,
4374 WasJustMoving[x][y],
4375 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4376 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4377 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4378 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4382 WasJustMoving[x][y] = 0;
4385 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4388 if (Feld[x][y] != element) /* element has changed */
4390 element = Feld[x][y];
4391 move_pattern = element_info[element].move_pattern;
4393 if (!CAN_MOVE(element))
4397 if (Feld[x][y] != element) /* element has changed */
4405 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4406 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4408 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4410 Moving2Blocked(x, y, &newx, &newy);
4411 if (Feld[newx][newy] == EL_BLOCKED)
4412 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4418 if (FrameCounter < 1 && x == 0 && y == 29)
4419 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4422 if (!MovDelay[x][y]) /* start new movement phase */
4424 /* all objects that can change their move direction after each step
4425 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4427 if (element != EL_YAMYAM &&
4428 element != EL_DARK_YAMYAM &&
4429 element != EL_PACMAN &&
4430 !(move_pattern & MV_ANY_DIRECTION) &&
4431 move_pattern != MV_TURNING_LEFT &&
4432 move_pattern != MV_TURNING_RIGHT &&
4433 move_pattern != MV_TURNING_LEFT_RIGHT &&
4434 move_pattern != MV_TURNING_RIGHT_LEFT &&
4435 move_pattern != MV_TURNING_RANDOM)
4440 if (FrameCounter < 1 && x == 0 && y == 29)
4441 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4444 if (MovDelay[x][y] && (element == EL_BUG ||
4445 element == EL_SPACESHIP ||
4446 element == EL_SP_SNIKSNAK ||
4447 element == EL_SP_ELECTRON ||
4448 element == EL_MOLE))
4449 DrawLevelField(x, y);
4453 if (MovDelay[x][y]) /* wait some time before next movement */
4458 if (element == EL_YAMYAM)
4461 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4462 DrawLevelElementAnimation(x, y, element);
4466 if (MovDelay[x][y]) /* element still has to wait some time */
4469 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4470 ResetGfxAnimation(x, y);
4474 if (GfxAction[x][y] != ACTION_WAITING)
4475 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4477 GfxAction[x][y] = ACTION_WAITING;
4481 if (element == EL_ROBOT ||
4483 element == EL_PACMAN ||
4485 element == EL_YAMYAM ||
4486 element == EL_DARK_YAMYAM)
4489 DrawLevelElementAnimation(x, y, element);
4491 DrawLevelElementAnimationIfNeeded(x, y, element);
4493 PlayLevelSoundAction(x, y, ACTION_WAITING);
4495 else if (element == EL_SP_ELECTRON)
4496 DrawLevelElementAnimationIfNeeded(x, y, element);
4497 else if (element == EL_DRAGON)
4500 int dir = MovDir[x][y];
4501 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4502 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4503 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4504 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4505 dir == MV_UP ? IMG_FLAMES_1_UP :
4506 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4507 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4510 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4513 GfxAction[x][y] = ACTION_ATTACKING;
4515 if (IS_PLAYER(x, y))
4516 DrawPlayerField(x, y);
4518 DrawLevelField(x, y);
4520 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4522 for (i = 1; i <= 3; i++)
4524 int xx = x + i * dx;
4525 int yy = y + i * dy;
4526 int sx = SCREENX(xx);
4527 int sy = SCREENY(yy);
4528 int flame_graphic = graphic + (i - 1);
4530 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4535 int flamed = MovingOrBlocked2Element(xx, yy);
4537 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4540 RemoveMovingField(xx, yy);
4542 Feld[xx][yy] = EL_FLAMES;
4543 if (IN_SCR_FIELD(sx, sy))
4545 DrawLevelFieldCrumbledSand(xx, yy);
4546 DrawGraphic(sx, sy, flame_graphic, frame);
4551 if (Feld[xx][yy] == EL_FLAMES)
4552 Feld[xx][yy] = EL_EMPTY;
4553 DrawLevelField(xx, yy);
4558 if (MovDelay[x][y]) /* element still has to wait some time */
4560 PlayLevelSoundAction(x, y, ACTION_WAITING);
4566 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4567 for all other elements GfxAction will be set by InitMovingField() */
4568 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4569 GfxAction[x][y] = ACTION_MOVING;
4573 /* now make next step */
4575 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4577 if (DONT_COLLIDE_WITH(element) &&
4578 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4579 !PLAYER_PROTECTED(newx, newy))
4582 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4585 /* player killed by element which is deadly when colliding with */
4587 KillHero(PLAYERINFO(newx, newy));
4592 else if ((element == EL_PENGUIN ||
4593 element == EL_ROBOT ||
4594 element == EL_SATELLITE ||
4595 element == EL_BALLOON ||
4596 IS_CUSTOM_ELEMENT(element)) &&
4597 IN_LEV_FIELD(newx, newy) &&
4598 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4601 Store[x][y] = EL_ACID;
4603 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4605 if (Feld[newx][newy] == EL_EXIT_OPEN)
4609 DrawLevelField(x, y);
4611 Feld[x][y] = EL_EMPTY;
4612 DrawLevelField(x, y);
4615 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4616 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4617 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4619 local_player->friends_still_needed--;
4620 if (!local_player->friends_still_needed &&
4621 !local_player->GameOver && AllPlayersGone)
4622 local_player->LevelSolved = local_player->GameOver = TRUE;
4626 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4628 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4629 DrawLevelField(newx, newy);
4631 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4633 else if (!IS_FREE(newx, newy))
4635 GfxAction[x][y] = ACTION_WAITING;
4637 if (IS_PLAYER(x, y))
4638 DrawPlayerField(x, y);
4640 DrawLevelField(x, y);
4644 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4646 if (IS_FOOD_PIG(Feld[newx][newy]))
4648 if (IS_MOVING(newx, newy))
4649 RemoveMovingField(newx, newy);
4652 Feld[newx][newy] = EL_EMPTY;
4653 DrawLevelField(newx, newy);
4656 PlayLevelSound(x, y, SND_PIG_DIGGING);
4658 else if (!IS_FREE(newx, newy))
4660 if (IS_PLAYER(x, y))
4661 DrawPlayerField(x, y);
4663 DrawLevelField(x, y);
4667 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4668 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4671 if (IS_FREE(newx, newy))
4673 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4676 if (IS_MOVING(newx, newy))
4677 RemoveMovingField(newx, newy);
4680 Feld[newx][newy] = EL_EMPTY;
4681 DrawLevelField(newx, newy);
4684 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4686 else if (!IS_FREE(newx, newy))
4689 if (IS_PLAYER(x, y))
4690 DrawPlayerField(x, y);
4692 DrawLevelField(x, y);
4697 RunnerVisit[x][y] = FrameCounter;
4698 PlayerVisit[x][y] /= 8; /* expire player visit path */
4700 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4702 if (!IS_FREE(newx, newy))
4704 if (IS_PLAYER(x, y))
4705 DrawPlayerField(x, y);
4707 DrawLevelField(x, y);
4713 boolean wanna_flame = !RND(10);
4714 int dx = newx - x, dy = newy - y;
4715 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4716 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4717 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4718 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4719 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4720 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4723 IS_CLASSIC_ENEMY(element1) ||
4724 IS_CLASSIC_ENEMY(element2)) &&
4725 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4726 element1 != EL_FLAMES && element2 != EL_FLAMES)
4729 ResetGfxAnimation(x, y);
4730 GfxAction[x][y] = ACTION_ATTACKING;
4733 if (IS_PLAYER(x, y))
4734 DrawPlayerField(x, y);
4736 DrawLevelField(x, y);
4738 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4740 MovDelay[x][y] = 50;
4742 Feld[newx][newy] = EL_FLAMES;
4743 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4744 Feld[newx1][newy1] = EL_FLAMES;
4745 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4746 Feld[newx2][newy2] = EL_FLAMES;
4752 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4753 Feld[newx][newy] == EL_DIAMOND)
4755 if (IS_MOVING(newx, newy))
4756 RemoveMovingField(newx, newy);
4759 Feld[newx][newy] = EL_EMPTY;
4760 DrawLevelField(newx, newy);
4763 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4765 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4766 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4768 if (AmoebaNr[newx][newy])
4770 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4771 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4772 Feld[newx][newy] == EL_BD_AMOEBA)
4773 AmoebaCnt[AmoebaNr[newx][newy]]--;
4776 if (IS_MOVING(newx, newy))
4777 RemoveMovingField(newx, newy);
4780 Feld[newx][newy] = EL_EMPTY;
4781 DrawLevelField(newx, newy);
4784 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4786 else if ((element == EL_PACMAN || element == EL_MOLE)
4787 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4789 if (AmoebaNr[newx][newy])
4791 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4792 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4793 Feld[newx][newy] == EL_BD_AMOEBA)
4794 AmoebaCnt[AmoebaNr[newx][newy]]--;
4797 if (element == EL_MOLE)
4799 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4800 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4802 ResetGfxAnimation(x, y);
4803 GfxAction[x][y] = ACTION_DIGGING;
4804 DrawLevelField(x, y);
4806 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4807 return; /* wait for shrinking amoeba */
4809 else /* element == EL_PACMAN */
4811 Feld[newx][newy] = EL_EMPTY;
4812 DrawLevelField(newx, newy);
4813 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4816 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4817 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4818 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4820 /* wait for shrinking amoeba to completely disappear */
4823 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4825 /* object was running against a wall */
4830 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4831 DrawLevelElementAnimation(x, y, element);
4833 if (element == EL_BUG ||
4834 element == EL_SPACESHIP ||
4835 element == EL_SP_SNIKSNAK)
4836 DrawLevelField(x, y);
4837 else if (element == EL_MOLE)
4838 DrawLevelField(x, y);
4839 else if (element == EL_BD_BUTTERFLY ||
4840 element == EL_BD_FIREFLY)
4841 DrawLevelElementAnimationIfNeeded(x, y, element);
4842 else if (element == EL_SATELLITE)
4843 DrawLevelElementAnimationIfNeeded(x, y, element);
4844 else if (element == EL_SP_ELECTRON)
4845 DrawLevelElementAnimationIfNeeded(x, y, element);
4848 if (DONT_TOUCH(element))
4849 TestIfBadThingTouchesHero(x, y);
4852 PlayLevelSoundAction(x, y, ACTION_WAITING);
4858 InitMovingField(x, y, MovDir[x][y]);
4860 PlayLevelSoundAction(x, y, ACTION_MOVING);
4864 ContinueMoving(x, y);
4867 void ContinueMoving(int x, int y)
4869 int element = Feld[x][y];
4870 int direction = MovDir[x][y];
4871 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4872 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4873 int newx = x + dx, newy = y + dy;
4875 int nextx = newx + dx, nexty = newy + dy;
4877 boolean pushed = Pushed[x][y];
4879 MovPos[x][y] += getElementMoveStepsize(x, y);
4881 if (pushed) /* special case: moving object pushed by player */
4882 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4884 if (ABS(MovPos[x][y]) < TILEX)
4886 DrawLevelField(x, y);
4888 return; /* element is still moving */
4891 /* element reached destination field */
4893 Feld[x][y] = EL_EMPTY;
4894 Feld[newx][newy] = element;
4895 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4897 if (element == EL_MOLE)
4899 Feld[x][y] = EL_SAND;
4901 DrawLevelFieldCrumbledSandNeighbours(x, y);
4903 else if (element == EL_QUICKSAND_FILLING)
4905 element = Feld[newx][newy] = get_next_element(element);
4906 Store[newx][newy] = Store[x][y];
4908 else if (element == EL_QUICKSAND_EMPTYING)
4910 Feld[x][y] = get_next_element(element);
4911 element = Feld[newx][newy] = Store[x][y];
4913 else if (element == EL_MAGIC_WALL_FILLING)
4915 element = Feld[newx][newy] = get_next_element(element);
4916 if (!game.magic_wall_active)
4917 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4918 Store[newx][newy] = Store[x][y];
4920 else if (element == EL_MAGIC_WALL_EMPTYING)
4922 Feld[x][y] = get_next_element(element);
4923 if (!game.magic_wall_active)
4924 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4925 element = Feld[newx][newy] = Store[x][y];
4927 else if (element == EL_BD_MAGIC_WALL_FILLING)
4929 element = Feld[newx][newy] = get_next_element(element);
4930 if (!game.magic_wall_active)
4931 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4932 Store[newx][newy] = Store[x][y];
4934 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4936 Feld[x][y] = get_next_element(element);
4937 if (!game.magic_wall_active)
4938 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4939 element = Feld[newx][newy] = Store[x][y];
4941 else if (element == EL_AMOEBA_DROPPING)
4943 Feld[x][y] = get_next_element(element);
4944 element = Feld[newx][newy] = Store[x][y];
4946 else if (element == EL_SOKOBAN_OBJECT)
4949 Feld[x][y] = Back[x][y];
4951 if (Back[newx][newy])
4952 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4954 Back[x][y] = Back[newx][newy] = 0;
4956 else if (Store[x][y] == EL_ACID)
4958 element = Feld[newx][newy] = EL_ACID;
4962 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4963 MovDelay[newx][newy] = 0;
4965 /* copy element change control values to new field */
4966 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4967 ChangePage[newx][newy] = ChangePage[x][y];
4968 Changed[newx][newy] = Changed[x][y];
4969 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4971 ChangeDelay[x][y] = 0;
4972 ChangePage[x][y] = -1;
4973 Changed[x][y] = CE_BITMASK_DEFAULT;
4974 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4976 /* copy animation control values to new field */
4977 GfxFrame[newx][newy] = GfxFrame[x][y];
4978 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4979 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4980 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4982 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4984 ResetGfxAnimation(x, y); /* reset animation values for old field */
4987 /* 2.1.1 (does not work correctly for spring) */
4988 if (!CAN_MOVE(element))
4989 MovDir[newx][newy] = 0;
4993 /* (does not work for falling objects that slide horizontally) */
4994 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4995 MovDir[newx][newy] = 0;
4998 if (!CAN_MOVE(element) ||
4999 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5000 MovDir[newx][newy] = 0;
5003 if (!CAN_MOVE(element) ||
5004 (CAN_FALL(element) && direction == MV_DOWN))
5005 GfxDir[x][y] = MovDir[newx][newy] = 0;
5010 DrawLevelField(x, y);
5011 DrawLevelField(newx, newy);
5013 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5015 /* prevent pushed element from moving on in pushed direction */
5016 if (pushed && CAN_MOVE(element) &&
5017 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5018 !(element_info[element].move_pattern & direction))
5019 TurnRound(newx, newy);
5021 if (!pushed) /* special case: moving object pushed by player */
5023 WasJustMoving[newx][newy] = 3;
5025 if (CAN_FALL(element) && direction == MV_DOWN)
5026 WasJustFalling[newx][newy] = 3;
5029 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5031 TestIfBadThingTouchesHero(newx, newy);
5032 TestIfBadThingTouchesFriend(newx, newy);
5034 if (!IS_CUSTOM_ELEMENT(element))
5035 TestIfBadThingTouchesOtherBadThing(newx, newy);
5037 else if (element == EL_PENGUIN)
5038 TestIfFriendTouchesBadThing(newx, newy);
5040 if (CAN_FALL(element) && direction == MV_DOWN &&
5041 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5045 TestIfElementTouchesCustomElement(x, y); /* for empty space */
5049 if (ChangePage[newx][newy] != -1) /* delayed change */
5050 ChangeElement(newx, newy, ChangePage[newx][newy]);
5055 TestIfElementHitsCustomElement(newx, newy, direction);
5059 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5061 int hitting_element = Feld[newx][newy];
5063 /* !!! fix side (direction) orientation here and elsewhere !!! */
5064 CheckElementSideChange(newx, newy, hitting_element,
5065 direction, CE_HITTING_SOMETHING, -1);
5068 if (IN_LEV_FIELD(nextx, nexty))
5070 static int opposite_directions[] =
5077 int move_dir_bit = MV_DIR_BIT(direction);
5078 int opposite_direction = opposite_directions[move_dir_bit];
5079 int hitting_side = direction;
5080 int touched_side = opposite_direction;
5081 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5082 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5083 MovDir[nextx][nexty] != direction ||
5084 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5090 CheckElementSideChange(nextx, nexty, touched_element,
5091 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5093 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5094 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5096 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5098 struct ElementChangeInfo *change =
5099 &element_info[hitting_element].change_page[i];
5101 if (change->can_change &&
5102 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5103 change->sides & touched_side &&
5104 change->trigger_element == touched_element)
5106 CheckElementSideChange(newx, newy, hitting_element,
5107 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5113 if (IS_CUSTOM_ELEMENT(touched_element) &&
5114 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5116 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5118 struct ElementChangeInfo *change =
5119 &element_info[touched_element].change_page[i];
5121 if (change->can_change &&
5122 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5123 change->sides & hitting_side &&
5124 change->trigger_element == hitting_element)
5126 CheckElementSideChange(nextx, nexty, touched_element,
5127 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5138 TestIfPlayerTouchesCustomElement(newx, newy);
5139 TestIfElementTouchesCustomElement(newx, newy);
5142 int AmoebeNachbarNr(int ax, int ay)
5145 int element = Feld[ax][ay];
5147 static int xy[4][2] =
5155 for (i = 0; i < 4; i++)
5157 int x = ax + xy[i][0];
5158 int y = ay + xy[i][1];
5160 if (!IN_LEV_FIELD(x, y))
5163 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5164 group_nr = AmoebaNr[x][y];
5170 void AmoebenVereinigen(int ax, int ay)
5172 int i, x, y, xx, yy;
5173 int new_group_nr = AmoebaNr[ax][ay];
5174 static int xy[4][2] =
5182 if (new_group_nr == 0)
5185 for (i = 0; i < 4; i++)
5190 if (!IN_LEV_FIELD(x, y))
5193 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5194 Feld[x][y] == EL_BD_AMOEBA ||
5195 Feld[x][y] == EL_AMOEBA_DEAD) &&
5196 AmoebaNr[x][y] != new_group_nr)
5198 int old_group_nr = AmoebaNr[x][y];
5200 if (old_group_nr == 0)
5203 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5204 AmoebaCnt[old_group_nr] = 0;
5205 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5206 AmoebaCnt2[old_group_nr] = 0;
5208 for (yy = 0; yy < lev_fieldy; yy++)
5210 for (xx = 0; xx < lev_fieldx; xx++)
5212 if (AmoebaNr[xx][yy] == old_group_nr)
5213 AmoebaNr[xx][yy] = new_group_nr;
5220 void AmoebeUmwandeln(int ax, int ay)
5224 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5226 int group_nr = AmoebaNr[ax][ay];
5231 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5232 printf("AmoebeUmwandeln(): This should never happen!\n");
5237 for (y = 0; y < lev_fieldy; y++)
5239 for (x = 0; x < lev_fieldx; x++)
5241 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5244 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5248 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5249 SND_AMOEBA_TURNING_TO_GEM :
5250 SND_AMOEBA_TURNING_TO_ROCK));
5255 static int xy[4][2] =
5263 for (i = 0; i < 4; i++)
5268 if (!IN_LEV_FIELD(x, y))
5271 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5273 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5274 SND_AMOEBA_TURNING_TO_GEM :
5275 SND_AMOEBA_TURNING_TO_ROCK));
5282 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5285 int group_nr = AmoebaNr[ax][ay];
5286 boolean done = FALSE;
5291 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5292 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5297 for (y = 0; y < lev_fieldy; y++)
5299 for (x = 0; x < lev_fieldx; x++)
5301 if (AmoebaNr[x][y] == group_nr &&
5302 (Feld[x][y] == EL_AMOEBA_DEAD ||
5303 Feld[x][y] == EL_BD_AMOEBA ||
5304 Feld[x][y] == EL_AMOEBA_GROWING))
5307 Feld[x][y] = new_element;
5308 InitField(x, y, FALSE);
5309 DrawLevelField(x, y);
5316 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5317 SND_BD_AMOEBA_TURNING_TO_ROCK :
5318 SND_BD_AMOEBA_TURNING_TO_GEM));
5321 void AmoebeWaechst(int x, int y)
5323 static unsigned long sound_delay = 0;
5324 static unsigned long sound_delay_value = 0;
5326 if (!MovDelay[x][y]) /* start new growing cycle */
5330 if (DelayReached(&sound_delay, sound_delay_value))
5333 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5335 if (Store[x][y] == EL_BD_AMOEBA)
5336 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5338 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5340 sound_delay_value = 30;
5344 if (MovDelay[x][y]) /* wait some time before growing bigger */
5347 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5349 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5350 6 - MovDelay[x][y]);
5352 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5355 if (!MovDelay[x][y])
5357 Feld[x][y] = Store[x][y];
5359 DrawLevelField(x, y);
5364 void AmoebaDisappearing(int x, int y)
5366 static unsigned long sound_delay = 0;
5367 static unsigned long sound_delay_value = 0;
5369 if (!MovDelay[x][y]) /* start new shrinking cycle */
5373 if (DelayReached(&sound_delay, sound_delay_value))
5374 sound_delay_value = 30;
5377 if (MovDelay[x][y]) /* wait some time before shrinking */
5380 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5382 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5383 6 - MovDelay[x][y]);
5385 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5388 if (!MovDelay[x][y])
5390 Feld[x][y] = EL_EMPTY;
5391 DrawLevelField(x, y);
5393 /* don't let mole enter this field in this cycle;
5394 (give priority to objects falling to this field from above) */
5400 void AmoebeAbleger(int ax, int ay)
5403 int element = Feld[ax][ay];
5404 int graphic = el2img(element);
5405 int newax = ax, neway = ay;
5406 static int xy[4][2] =
5414 if (!level.amoeba_speed)
5416 Feld[ax][ay] = EL_AMOEBA_DEAD;
5417 DrawLevelField(ax, ay);
5421 if (IS_ANIMATED(graphic))
5422 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5424 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5425 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5427 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5430 if (MovDelay[ax][ay])
5434 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5437 int x = ax + xy[start][0];
5438 int y = ay + xy[start][1];
5440 if (!IN_LEV_FIELD(x, y))
5443 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5444 if (IS_FREE(x, y) ||
5445 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5451 if (newax == ax && neway == ay)
5454 else /* normal or "filled" (BD style) amoeba */
5457 boolean waiting_for_player = FALSE;
5459 for (i = 0; i < 4; i++)
5461 int j = (start + i) % 4;
5462 int x = ax + xy[j][0];
5463 int y = ay + xy[j][1];
5465 if (!IN_LEV_FIELD(x, y))
5468 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5469 if (IS_FREE(x, y) ||
5470 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5476 else if (IS_PLAYER(x, y))
5477 waiting_for_player = TRUE;
5480 if (newax == ax && neway == ay) /* amoeba cannot grow */
5482 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5484 Feld[ax][ay] = EL_AMOEBA_DEAD;
5485 DrawLevelField(ax, ay);
5486 AmoebaCnt[AmoebaNr[ax][ay]]--;
5488 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5490 if (element == EL_AMOEBA_FULL)
5491 AmoebeUmwandeln(ax, ay);
5492 else if (element == EL_BD_AMOEBA)
5493 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5498 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5500 /* amoeba gets larger by growing in some direction */
5502 int new_group_nr = AmoebaNr[ax][ay];
5505 if (new_group_nr == 0)
5507 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5508 printf("AmoebeAbleger(): This should never happen!\n");
5513 AmoebaNr[newax][neway] = new_group_nr;
5514 AmoebaCnt[new_group_nr]++;
5515 AmoebaCnt2[new_group_nr]++;
5517 /* if amoeba touches other amoeba(s) after growing, unify them */
5518 AmoebenVereinigen(newax, neway);
5520 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5522 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5528 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5529 (neway == lev_fieldy - 1 && newax != ax))
5531 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5532 Store[newax][neway] = element;
5534 else if (neway == ay)
5536 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5538 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5540 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5545 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5546 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5547 Store[ax][ay] = EL_AMOEBA_DROP;
5548 ContinueMoving(ax, ay);
5552 DrawLevelField(newax, neway);
5555 void Life(int ax, int ay)
5558 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5560 int element = Feld[ax][ay];
5561 int graphic = el2img(element);
5562 boolean changed = FALSE;
5564 if (IS_ANIMATED(graphic))
5565 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5570 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5571 MovDelay[ax][ay] = life_time;
5573 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5576 if (MovDelay[ax][ay])
5580 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5582 int xx = ax+x1, yy = ay+y1;
5585 if (!IN_LEV_FIELD(xx, yy))
5588 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5590 int x = xx+x2, y = yy+y2;
5592 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5595 if (((Feld[x][y] == element ||
5596 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5598 (IS_FREE(x, y) && Stop[x][y]))
5602 if (xx == ax && yy == ay) /* field in the middle */
5604 if (nachbarn < life[0] || nachbarn > life[1])
5606 Feld[xx][yy] = EL_EMPTY;
5608 DrawLevelField(xx, yy);
5609 Stop[xx][yy] = TRUE;
5613 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5614 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5615 { /* free border field */
5616 if (nachbarn >= life[2] && nachbarn <= life[3])
5618 Feld[xx][yy] = element;
5619 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5621 DrawLevelField(xx, yy);
5622 Stop[xx][yy] = TRUE;
5629 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5630 SND_GAME_OF_LIFE_GROWING);
5633 static void InitRobotWheel(int x, int y)
5635 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5638 static void RunRobotWheel(int x, int y)
5640 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5643 static void StopRobotWheel(int x, int y)
5645 if (ZX == x && ZY == y)
5649 static void InitTimegateWheel(int x, int y)
5651 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5654 static void RunTimegateWheel(int x, int y)
5656 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5659 void CheckExit(int x, int y)
5661 if (local_player->gems_still_needed > 0 ||
5662 local_player->sokobanfields_still_needed > 0 ||
5663 local_player->lights_still_needed > 0)
5665 int element = Feld[x][y];
5666 int graphic = el2img(element);
5668 if (IS_ANIMATED(graphic))
5669 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5674 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5677 Feld[x][y] = EL_EXIT_OPENING;
5679 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5682 void CheckExitSP(int x, int y)
5684 if (local_player->gems_still_needed > 0)
5686 int element = Feld[x][y];
5687 int graphic = el2img(element);
5689 if (IS_ANIMATED(graphic))
5690 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5695 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5698 Feld[x][y] = EL_SP_EXIT_OPENING;
5700 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5703 static void CloseAllOpenTimegates()
5707 for (y = 0; y < lev_fieldy; y++)
5709 for (x = 0; x < lev_fieldx; x++)
5711 int element = Feld[x][y];
5713 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5715 Feld[x][y] = EL_TIMEGATE_CLOSING;
5717 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5719 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5726 void EdelsteinFunkeln(int x, int y)
5728 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5731 if (Feld[x][y] == EL_BD_DIAMOND)
5734 if (MovDelay[x][y] == 0) /* next animation frame */
5735 MovDelay[x][y] = 11 * !SimpleRND(500);
5737 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5741 if (setup.direct_draw && MovDelay[x][y])
5742 SetDrawtoField(DRAW_BUFFERED);
5744 DrawLevelElementAnimation(x, y, Feld[x][y]);
5746 if (MovDelay[x][y] != 0)
5748 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5749 10 - MovDelay[x][y]);
5751 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5753 if (setup.direct_draw)
5757 dest_x = FX + SCREENX(x) * TILEX;
5758 dest_y = FY + SCREENY(y) * TILEY;
5760 BlitBitmap(drawto_field, window,
5761 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5762 SetDrawtoField(DRAW_DIRECT);
5768 void MauerWaechst(int x, int y)
5772 if (!MovDelay[x][y]) /* next animation frame */
5773 MovDelay[x][y] = 3 * delay;
5775 if (MovDelay[x][y]) /* wait some time before next frame */
5779 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5781 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5782 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5784 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5787 if (!MovDelay[x][y])
5789 if (MovDir[x][y] == MV_LEFT)
5791 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5792 DrawLevelField(x - 1, y);
5794 else if (MovDir[x][y] == MV_RIGHT)
5796 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5797 DrawLevelField(x + 1, y);
5799 else if (MovDir[x][y] == MV_UP)
5801 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5802 DrawLevelField(x, y - 1);
5806 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5807 DrawLevelField(x, y + 1);
5810 Feld[x][y] = Store[x][y];
5812 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5813 DrawLevelField(x, y);
5818 void MauerAbleger(int ax, int ay)
5820 int element = Feld[ax][ay];
5821 int graphic = el2img(element);
5822 boolean oben_frei = FALSE, unten_frei = FALSE;
5823 boolean links_frei = FALSE, rechts_frei = FALSE;
5824 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5825 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5826 boolean new_wall = FALSE;
5828 if (IS_ANIMATED(graphic))
5829 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5831 if (!MovDelay[ax][ay]) /* start building new wall */
5832 MovDelay[ax][ay] = 6;
5834 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5837 if (MovDelay[ax][ay])
5841 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5843 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5845 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5847 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5850 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5851 element == EL_EXPANDABLE_WALL_ANY)
5855 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5856 Store[ax][ay-1] = element;
5857 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5858 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5859 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5860 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5865 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5866 Store[ax][ay+1] = element;
5867 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5868 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5869 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5870 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5875 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5876 element == EL_EXPANDABLE_WALL_ANY ||
5877 element == EL_EXPANDABLE_WALL)
5881 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5882 Store[ax-1][ay] = element;
5883 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5884 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5885 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5886 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5892 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5893 Store[ax+1][ay] = element;
5894 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5895 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5896 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5897 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5902 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5903 DrawLevelField(ax, ay);
5905 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5907 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5908 unten_massiv = TRUE;
5909 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5910 links_massiv = TRUE;
5911 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5912 rechts_massiv = TRUE;
5914 if (((oben_massiv && unten_massiv) ||
5915 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5916 element == EL_EXPANDABLE_WALL) &&
5917 ((links_massiv && rechts_massiv) ||
5918 element == EL_EXPANDABLE_WALL_VERTICAL))
5919 Feld[ax][ay] = EL_WALL;
5923 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5925 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5929 void CheckForDragon(int x, int y)
5932 boolean dragon_found = FALSE;
5933 static int xy[4][2] =
5941 for (i = 0; i < 4; i++)
5943 for (j = 0; j < 4; j++)
5945 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5947 if (IN_LEV_FIELD(xx, yy) &&
5948 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5950 if (Feld[xx][yy] == EL_DRAGON)
5951 dragon_found = TRUE;
5960 for (i = 0; i < 4; i++)
5962 for (j = 0; j < 3; j++)
5964 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5966 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5968 Feld[xx][yy] = EL_EMPTY;
5969 DrawLevelField(xx, yy);
5978 static void InitBuggyBase(int x, int y)
5980 int element = Feld[x][y];
5981 int activating_delay = FRAMES_PER_SECOND / 4;
5984 (element == EL_SP_BUGGY_BASE ?
5985 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5986 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5988 element == EL_SP_BUGGY_BASE_ACTIVE ?
5989 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5992 static void WarnBuggyBase(int x, int y)
5995 static int xy[4][2] =
6003 for (i = 0; i < 4; i++)
6005 int xx = x + xy[i][0], yy = y + xy[i][1];
6007 if (IS_PLAYER(xx, yy))
6009 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6016 static void InitTrap(int x, int y)
6018 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6021 static void ActivateTrap(int x, int y)
6023 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6026 static void ChangeActiveTrap(int x, int y)
6028 int graphic = IMG_TRAP_ACTIVE;
6030 /* if new animation frame was drawn, correct crumbled sand border */
6031 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6032 DrawLevelFieldCrumbledSand(x, y);
6035 static void ChangeElementNowExt(int x, int y, int target_element)
6037 /* check if element under player changes from accessible to unaccessible
6038 (needed for special case of dropping element which then changes) */
6039 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6040 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6047 Feld[x][y] = target_element;
6049 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6051 ResetGfxAnimation(x, y);
6052 ResetRandomAnimationValue(x, y);
6054 InitField(x, y, FALSE);
6055 if (CAN_MOVE(Feld[x][y]))
6058 DrawLevelField(x, y);
6060 if (GFX_CRUMBLED(Feld[x][y]))
6061 DrawLevelFieldCrumbledSandNeighbours(x, y);
6063 TestIfBadThingTouchesHero(x, y);
6064 TestIfPlayerTouchesCustomElement(x, y);
6065 TestIfElementTouchesCustomElement(x, y);
6067 if (ELEM_IS_PLAYER(target_element))
6068 RelocatePlayer(x, y, target_element);
6071 static boolean ChangeElementNow(int x, int y, int element, int page)
6073 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6075 /* always use default change event to prevent running into a loop */
6076 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6077 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6079 /* do not change already changed elements with same change event */
6081 if (Changed[x][y] & ChangeEvent[x][y])
6088 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6090 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6092 if (change->explode)
6099 if (change->use_content)
6101 boolean complete_change = TRUE;
6102 boolean can_change[3][3];
6105 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6107 boolean half_destructible;
6108 int ex = x + xx - 1;
6109 int ey = y + yy - 1;
6112 can_change[xx][yy] = TRUE;
6114 if (ex == x && ey == y) /* do not check changing element itself */
6117 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6119 can_change[xx][yy] = FALSE; /* do not change empty borders */
6124 if (!IN_LEV_FIELD(ex, ey))
6126 can_change[xx][yy] = FALSE;
6127 complete_change = FALSE;
6134 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6135 e = MovingOrBlocked2Element(ex, ey);
6137 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6139 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6140 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6141 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6143 can_change[xx][yy] = FALSE;
6144 complete_change = FALSE;
6148 if (!change->only_complete || complete_change)
6150 boolean something_has_changed = FALSE;
6152 if (change->only_complete && change->use_random_change &&
6153 RND(100) < change->random)
6156 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6158 int ex = x + xx - 1;
6159 int ey = y + yy - 1;
6161 if (can_change[xx][yy] && (!change->use_random_change ||
6162 RND(100) < change->random))
6164 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6165 RemoveMovingField(ex, ey);
6167 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6169 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6171 something_has_changed = TRUE;
6173 /* for symmetry reasons, freeze newly created border elements */
6174 if (ex != x || ey != y)
6175 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6179 if (something_has_changed)
6180 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6185 ChangeElementNowExt(x, y, change->target_element);
6187 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6193 static void ChangeElement(int x, int y, int page)
6195 int element = MovingOrBlocked2Element(x, y);
6196 struct ElementInfo *ei = &element_info[element];
6197 struct ElementChangeInfo *change = &ei->change_page[page];
6201 if (!CAN_CHANGE(element))
6204 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6205 x, y, element, element_info[element].token_name);
6206 printf("ChangeElement(): This should never happen!\n");
6212 if (ChangeDelay[x][y] == 0) /* initialize element change */
6214 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6215 RND(change->delay_random * change->delay_frames)) + 1;
6217 ResetGfxAnimation(x, y);
6218 ResetRandomAnimationValue(x, y);
6220 if (change->pre_change_function)
6221 change->pre_change_function(x, y);
6224 ChangeDelay[x][y]--;
6226 if (ChangeDelay[x][y] != 0) /* continue element change */
6228 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6230 if (IS_ANIMATED(graphic))
6231 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6233 if (change->change_function)
6234 change->change_function(x, y);
6236 else /* finish element change */
6238 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6240 page = ChangePage[x][y];
6241 ChangePage[x][y] = -1;
6245 if (IS_MOVING(x, y) && !change->explode)
6247 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6250 ChangeDelay[x][y] = 1; /* try change after next move step */
6251 ChangePage[x][y] = page; /* remember page to use for change */
6256 if (ChangeElementNow(x, y, element, page))
6258 if (change->post_change_function)
6259 change->post_change_function(x, y);
6264 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6265 int trigger_element,
6271 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6274 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6276 int element = EL_CUSTOM_START + i;
6278 boolean change_element = FALSE;
6281 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6284 for (j = 0; j < element_info[element].num_change_pages; j++)
6286 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6288 if (change->can_change &&
6290 change->events & CH_EVENT_BIT(trigger_event) &&
6292 change->sides & trigger_side &&
6293 change->trigger_element == trigger_element)
6296 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6297 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6298 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6301 change_element = TRUE;
6308 if (!change_element)
6311 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6314 if (x == lx && y == ly) /* do not change trigger element itself */
6318 if (Feld[x][y] == element)
6320 ChangeDelay[x][y] = 1;
6321 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6322 ChangeElement(x, y, page);
6330 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6333 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6337 static boolean CheckElementSideChange(int x, int y, int element, int side,
6338 int trigger_event, int page)
6340 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6343 if (Feld[x][y] == EL_BLOCKED)
6345 Blocked2Moving(x, y, &x, &y);
6346 element = Feld[x][y];
6350 page = element_info[element].event_page_nr[trigger_event];
6352 if (!(element_info[element].change_page[page].sides & side))
6355 ChangeDelay[x][y] = 1;
6356 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6357 ChangeElement(x, y, page);
6362 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6364 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6367 static void PlayPlayerSound(struct PlayerInfo *player)
6369 int jx = player->jx, jy = player->jy;
6370 int element = player->element_nr;
6371 int last_action = player->last_action_waiting;
6372 int action = player->action_waiting;
6374 if (player->is_waiting)
6376 if (action != last_action)
6377 PlayLevelSoundElementAction(jx, jy, element, action);
6379 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6383 if (action != last_action)
6384 StopSound(element_info[element].sound[last_action]);
6386 if (last_action == ACTION_SLEEPING)
6387 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6391 static void PlayAllPlayersSound()
6395 for (i = 0; i < MAX_PLAYERS; i++)
6396 if (stored_player[i].active)
6397 PlayPlayerSound(&stored_player[i]);
6400 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6402 boolean last_waiting = player->is_waiting;
6403 int move_dir = player->MovDir;
6405 player->last_action_waiting = player->action_waiting;
6409 if (!last_waiting) /* not waiting -> waiting */
6411 player->is_waiting = TRUE;
6413 player->frame_counter_bored =
6415 game.player_boring_delay_fixed +
6416 SimpleRND(game.player_boring_delay_random);
6417 player->frame_counter_sleeping =
6419 game.player_sleeping_delay_fixed +
6420 SimpleRND(game.player_sleeping_delay_random);
6422 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6425 if (game.player_sleeping_delay_fixed +
6426 game.player_sleeping_delay_random > 0 &&
6427 player->anim_delay_counter == 0 &&
6428 player->post_delay_counter == 0 &&
6429 FrameCounter >= player->frame_counter_sleeping)
6430 player->is_sleeping = TRUE;
6431 else if (game.player_boring_delay_fixed +
6432 game.player_boring_delay_random > 0 &&
6433 FrameCounter >= player->frame_counter_bored)
6434 player->is_bored = TRUE;
6436 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6437 player->is_bored ? ACTION_BORING :
6440 if (player->is_sleeping)
6442 if (player->num_special_action_sleeping > 0)
6444 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6446 int last_special_action = player->special_action_sleeping;
6447 int num_special_action = player->num_special_action_sleeping;
6448 int special_action =
6449 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6450 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6451 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6452 last_special_action + 1 : ACTION_SLEEPING);
6453 int special_graphic =
6454 el_act_dir2img(player->element_nr, special_action, move_dir);
6456 player->anim_delay_counter =
6457 graphic_info[special_graphic].anim_delay_fixed +
6458 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6459 player->post_delay_counter =
6460 graphic_info[special_graphic].post_delay_fixed +
6461 SimpleRND(graphic_info[special_graphic].post_delay_random);
6463 player->special_action_sleeping = special_action;
6466 if (player->anim_delay_counter > 0)
6468 player->action_waiting = player->special_action_sleeping;
6469 player->anim_delay_counter--;
6471 else if (player->post_delay_counter > 0)
6473 player->post_delay_counter--;
6477 else if (player->is_bored)
6479 if (player->num_special_action_bored > 0)
6481 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6483 int special_action =
6484 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6485 int special_graphic =
6486 el_act_dir2img(player->element_nr, special_action, move_dir);
6488 player->anim_delay_counter =
6489 graphic_info[special_graphic].anim_delay_fixed +
6490 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6491 player->post_delay_counter =
6492 graphic_info[special_graphic].post_delay_fixed +
6493 SimpleRND(graphic_info[special_graphic].post_delay_random);
6495 player->special_action_bored = special_action;
6498 if (player->anim_delay_counter > 0)
6500 player->action_waiting = player->special_action_bored;
6501 player->anim_delay_counter--;
6503 else if (player->post_delay_counter > 0)
6505 player->post_delay_counter--;
6510 else if (last_waiting) /* waiting -> not waiting */
6512 player->is_waiting = FALSE;
6513 player->is_bored = FALSE;
6514 player->is_sleeping = FALSE;
6516 player->frame_counter_bored = -1;
6517 player->frame_counter_sleeping = -1;
6519 player->anim_delay_counter = 0;
6520 player->post_delay_counter = 0;
6522 player->action_waiting = ACTION_DEFAULT;
6524 player->special_action_bored = ACTION_DEFAULT;
6525 player->special_action_sleeping = ACTION_DEFAULT;
6530 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6533 static byte stored_player_action[MAX_PLAYERS];
6534 static int num_stored_actions = 0;
6536 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6537 int left = player_action & JOY_LEFT;
6538 int right = player_action & JOY_RIGHT;
6539 int up = player_action & JOY_UP;
6540 int down = player_action & JOY_DOWN;
6541 int button1 = player_action & JOY_BUTTON_1;
6542 int button2 = player_action & JOY_BUTTON_2;
6543 int dx = (left ? -1 : right ? 1 : 0);
6544 int dy = (up ? -1 : down ? 1 : 0);
6547 stored_player_action[player->index_nr] = 0;
6548 num_stored_actions++;
6552 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6555 if (!player->active || tape.pausing)
6559 printf("::: [%d %d %d %d] [%d %d]\n",
6560 left, right, up, down, button1, button2);
6566 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6570 snapped = SnapField(player, dx, dy);
6574 dropped = DropElement(player);
6576 moved = MovePlayer(player, dx, dy);
6579 if (tape.single_step && tape.recording && !tape.pausing)
6581 if (button1 || (dropped && !moved))
6583 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6584 SnapField(player, 0, 0); /* stop snapping */
6588 SetPlayerWaiting(player, FALSE);
6591 return player_action;
6593 stored_player_action[player->index_nr] = player_action;
6599 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6602 /* no actions for this player (no input at player's configured device) */
6604 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6605 SnapField(player, 0, 0);
6606 CheckGravityMovement(player);
6608 if (player->MovPos == 0)
6609 SetPlayerWaiting(player, TRUE);
6611 if (player->MovPos == 0) /* needed for tape.playing */
6612 player->is_moving = FALSE;
6614 player->is_dropping = FALSE;
6620 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6622 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6624 TapeRecordAction(stored_player_action);
6625 num_stored_actions = 0;
6632 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6634 static byte stored_player_action[MAX_PLAYERS];
6635 static int num_stored_actions = 0;
6636 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6637 int left = player_action & JOY_LEFT;
6638 int right = player_action & JOY_RIGHT;
6639 int up = player_action & JOY_UP;
6640 int down = player_action & JOY_DOWN;
6641 int button1 = player_action & JOY_BUTTON_1;
6642 int button2 = player_action & JOY_BUTTON_2;
6643 int dx = (left ? -1 : right ? 1 : 0);
6644 int dy = (up ? -1 : down ? 1 : 0);
6646 stored_player_action[player->index_nr] = 0;
6647 num_stored_actions++;
6649 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6651 if (!player->active || tape.pausing)
6656 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6659 snapped = SnapField(player, dx, dy);
6663 dropped = DropElement(player);
6665 moved = MovePlayer(player, dx, dy);
6668 if (tape.single_step && tape.recording && !tape.pausing)
6670 if (button1 || (dropped && !moved))
6672 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6673 SnapField(player, 0, 0); /* stop snapping */
6677 stored_player_action[player->index_nr] = player_action;
6681 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6683 /* no actions for this player (no input at player's configured device) */
6685 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6686 SnapField(player, 0, 0);
6687 CheckGravityMovement(player);
6689 if (player->MovPos == 0)
6690 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6692 if (player->MovPos == 0) /* needed for tape.playing */
6693 player->is_moving = FALSE;
6696 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6698 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6700 TapeRecordAction(stored_player_action);
6701 num_stored_actions = 0;
6708 static unsigned long action_delay = 0;
6709 unsigned long action_delay_value;
6710 int magic_wall_x = 0, magic_wall_y = 0;
6711 int i, x, y, element, graphic;
6712 byte *recorded_player_action;
6713 byte summarized_player_action = 0;
6715 byte tape_action[MAX_PLAYERS];
6718 if (game_status != GAME_MODE_PLAYING)
6721 action_delay_value =
6722 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6724 if (tape.playing && tape.index_search && !tape.pausing)
6725 action_delay_value = 0;
6727 /* ---------- main game synchronization point ---------- */
6729 WaitUntilDelayReached(&action_delay, action_delay_value);
6731 if (network_playing && !network_player_action_received)
6735 printf("DEBUG: try to get network player actions in time\n");
6739 #if defined(PLATFORM_UNIX)
6740 /* last chance to get network player actions without main loop delay */
6744 if (game_status != GAME_MODE_PLAYING)
6747 if (!network_player_action_received)
6751 printf("DEBUG: failed to get network player actions in time\n");
6762 printf("::: getting new tape action [%d]\n", FrameCounter);
6765 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6767 for (i = 0; i < MAX_PLAYERS; i++)
6769 summarized_player_action |= stored_player[i].action;
6771 if (!network_playing)
6772 stored_player[i].effective_action = stored_player[i].action;
6775 #if defined(PLATFORM_UNIX)
6776 if (network_playing)
6777 SendToServer_MovePlayer(summarized_player_action);
6780 if (!options.network && !setup.team_mode)
6781 local_player->effective_action = summarized_player_action;
6783 for (i = 0; i < MAX_PLAYERS; i++)
6785 int actual_player_action = stored_player[i].effective_action;
6787 if (stored_player[i].programmed_action)
6788 actual_player_action = stored_player[i].programmed_action;
6790 if (recorded_player_action)
6791 actual_player_action = recorded_player_action[i];
6793 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6795 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6796 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6798 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6803 TapeRecordAction(tape_action);
6806 network_player_action_received = FALSE;
6808 ScrollScreen(NULL, SCROLL_GO_ON);
6814 for (i = 0; i < MAX_PLAYERS; i++)
6815 stored_player[i].Frame++;
6819 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6821 for (i = 0; i < MAX_PLAYERS; i++)
6823 struct PlayerInfo *player = &stored_player[i];
6827 if (player->active && player->is_pushing && player->is_moving &&
6830 ContinueMoving(x, y);
6832 /* continue moving after pushing (this is actually a bug) */
6833 if (!IS_MOVING(x, y))
6842 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6844 Changed[x][y] = CE_BITMASK_DEFAULT;
6845 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6848 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6850 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6851 printf("GameActions(): This should never happen!\n");
6853 ChangePage[x][y] = -1;
6858 if (WasJustMoving[x][y] > 0)
6859 WasJustMoving[x][y]--;
6860 if (WasJustFalling[x][y] > 0)
6861 WasJustFalling[x][y]--;
6866 /* reset finished pushing action (not done in ContinueMoving() to allow
6867 continous pushing animation for elements with zero push delay) */
6868 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6870 ResetGfxAnimation(x, y);
6871 DrawLevelField(x, y);
6876 if (IS_BLOCKED(x, y))
6880 Blocked2Moving(x, y, &oldx, &oldy);
6881 if (!IS_MOVING(oldx, oldy))
6883 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6884 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6885 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6886 printf("GameActions(): This should never happen!\n");
6892 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6894 element = Feld[x][y];
6896 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6898 graphic = el2img(element);
6904 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6906 element = graphic = 0;
6910 if (graphic_info[graphic].anim_global_sync)
6911 GfxFrame[x][y] = FrameCounter;
6913 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6914 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6915 ResetRandomAnimationValue(x, y);
6917 SetRandomAnimationValue(x, y);
6920 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6923 if (IS_INACTIVE(element))
6925 if (IS_ANIMATED(graphic))
6926 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6932 /* this may take place after moving, so 'element' may have changed */
6934 if (IS_CHANGING(x, y))
6936 if (IS_CHANGING(x, y) &&
6937 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6941 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6942 element_info[element].event_page_nr[CE_DELAY]);
6944 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6947 element = Feld[x][y];
6948 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6952 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6957 element = Feld[x][y];
6958 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6960 if (element == EL_MOLE)
6961 printf("::: %d, %d, %d [%d]\n",
6962 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6966 if (element == EL_YAMYAM)
6967 printf("::: %d, %d, %d\n",
6968 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6972 if (IS_ANIMATED(graphic) &&
6976 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6979 if (element == EL_BUG)
6980 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6984 if (element == EL_MOLE)
6985 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6989 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6990 EdelsteinFunkeln(x, y);
6992 else if ((element == EL_ACID ||
6993 element == EL_EXIT_OPEN ||
6994 element == EL_SP_EXIT_OPEN ||
6995 element == EL_SP_TERMINAL ||
6996 element == EL_SP_TERMINAL_ACTIVE ||
6997 element == EL_EXTRA_TIME ||
6998 element == EL_SHIELD_NORMAL ||
6999 element == EL_SHIELD_DEADLY) &&
7000 IS_ANIMATED(graphic))
7001 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7002 else if (IS_MOVING(x, y))
7003 ContinueMoving(x, y);
7004 else if (IS_ACTIVE_BOMB(element))
7005 CheckDynamite(x, y);
7007 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7008 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7010 else if (element == EL_AMOEBA_GROWING)
7011 AmoebeWaechst(x, y);
7012 else if (element == EL_AMOEBA_SHRINKING)
7013 AmoebaDisappearing(x, y);
7015 #if !USE_NEW_AMOEBA_CODE
7016 else if (IS_AMOEBALIVE(element))
7017 AmoebeAbleger(x, y);
7020 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7022 else if (element == EL_EXIT_CLOSED)
7024 else if (element == EL_SP_EXIT_CLOSED)
7026 else if (element == EL_EXPANDABLE_WALL_GROWING)
7028 else if (element == EL_EXPANDABLE_WALL ||
7029 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7030 element == EL_EXPANDABLE_WALL_VERTICAL ||
7031 element == EL_EXPANDABLE_WALL_ANY)
7033 else if (element == EL_FLAMES)
7034 CheckForDragon(x, y);
7036 else if (IS_AUTO_CHANGING(element))
7037 ChangeElement(x, y);
7039 else if (element == EL_EXPLOSION)
7040 ; /* drawing of correct explosion animation is handled separately */
7041 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7042 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7045 /* this may take place after moving, so 'element' may have changed */
7046 if (IS_AUTO_CHANGING(Feld[x][y]))
7047 ChangeElement(x, y);
7050 if (IS_BELT_ACTIVE(element))
7051 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7053 if (game.magic_wall_active)
7055 int jx = local_player->jx, jy = local_player->jy;
7057 /* play the element sound at the position nearest to the player */
7058 if ((element == EL_MAGIC_WALL_FULL ||
7059 element == EL_MAGIC_WALL_ACTIVE ||
7060 element == EL_MAGIC_WALL_EMPTYING ||
7061 element == EL_BD_MAGIC_WALL_FULL ||
7062 element == EL_BD_MAGIC_WALL_ACTIVE ||
7063 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7064 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7072 #if USE_NEW_AMOEBA_CODE
7073 /* new experimental amoeba growth stuff */
7075 if (!(FrameCounter % 8))
7078 static unsigned long random = 1684108901;
7080 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7083 x = (random >> 10) % lev_fieldx;
7084 y = (random >> 20) % lev_fieldy;
7086 x = RND(lev_fieldx);
7087 y = RND(lev_fieldy);
7089 element = Feld[x][y];
7091 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7092 if (!IS_PLAYER(x,y) &&
7093 (element == EL_EMPTY ||
7094 element == EL_SAND ||
7095 element == EL_QUICKSAND_EMPTY ||
7096 element == EL_ACID_SPLASH_LEFT ||
7097 element == EL_ACID_SPLASH_RIGHT))
7099 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7100 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7101 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7102 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7103 Feld[x][y] = EL_AMOEBA_DROP;
7106 random = random * 129 + 1;
7112 if (game.explosions_delayed)
7115 game.explosions_delayed = FALSE;
7117 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7119 element = Feld[x][y];
7121 if (ExplodeField[x][y])
7122 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7123 else if (element == EL_EXPLOSION)
7124 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7126 ExplodeField[x][y] = EX_NO_EXPLOSION;
7129 game.explosions_delayed = TRUE;
7132 if (game.magic_wall_active)
7134 if (!(game.magic_wall_time_left % 4))
7136 int element = Feld[magic_wall_x][magic_wall_y];
7138 if (element == EL_BD_MAGIC_WALL_FULL ||
7139 element == EL_BD_MAGIC_WALL_ACTIVE ||
7140 element == EL_BD_MAGIC_WALL_EMPTYING)
7141 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7143 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7146 if (game.magic_wall_time_left > 0)
7148 game.magic_wall_time_left--;
7149 if (!game.magic_wall_time_left)
7151 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7153 element = Feld[x][y];
7155 if (element == EL_MAGIC_WALL_ACTIVE ||
7156 element == EL_MAGIC_WALL_FULL)
7158 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7159 DrawLevelField(x, y);
7161 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7162 element == EL_BD_MAGIC_WALL_FULL)
7164 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7165 DrawLevelField(x, y);
7169 game.magic_wall_active = FALSE;
7174 if (game.light_time_left > 0)
7176 game.light_time_left--;
7178 if (game.light_time_left == 0)
7179 RedrawAllLightSwitchesAndInvisibleElements();
7182 if (game.timegate_time_left > 0)
7184 game.timegate_time_left--;
7186 if (game.timegate_time_left == 0)
7187 CloseAllOpenTimegates();
7190 for (i = 0; i < MAX_PLAYERS; i++)
7192 struct PlayerInfo *player = &stored_player[i];
7194 if (SHIELD_ON(player))
7196 if (player->shield_deadly_time_left)
7197 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7198 else if (player->shield_normal_time_left)
7199 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7203 if (TimeFrames >= FRAMES_PER_SECOND)
7208 for (i = 0; i < MAX_PLAYERS; i++)
7210 struct PlayerInfo *player = &stored_player[i];
7212 if (SHIELD_ON(player))
7214 player->shield_normal_time_left--;
7216 if (player->shield_deadly_time_left > 0)
7217 player->shield_deadly_time_left--;
7221 if (tape.recording || tape.playing)
7222 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7228 if (TimeLeft <= 10 && setup.time_limit)
7229 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7231 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7233 if (!TimeLeft && setup.time_limit)
7234 for (i = 0; i < MAX_PLAYERS; i++)
7235 KillHero(&stored_player[i]);
7237 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7238 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7242 PlayAllPlayersSound();
7244 if (options.debug) /* calculate frames per second */
7246 static unsigned long fps_counter = 0;
7247 static int fps_frames = 0;
7248 unsigned long fps_delay_ms = Counter() - fps_counter;
7252 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7254 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7257 fps_counter = Counter();
7260 redraw_mask |= REDRAW_FPS;
7264 if (stored_player[0].jx != stored_player[0].last_jx ||
7265 stored_player[0].jy != stored_player[0].last_jy)
7266 printf("::: %d, %d, %d, %d, %d\n",
7267 stored_player[0].MovDir,
7268 stored_player[0].MovPos,
7269 stored_player[0].GfxPos,
7270 stored_player[0].Frame,
7271 stored_player[0].StepFrame);
7278 for (i = 0; i < MAX_PLAYERS; i++)
7281 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7283 stored_player[i].Frame += move_frames;
7285 if (stored_player[i].MovPos != 0)
7286 stored_player[i].StepFrame += move_frames;
7288 if (stored_player[i].drop_delay > 0)
7289 stored_player[i].drop_delay--;
7294 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7296 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7298 local_player->show_envelope = 0;
7303 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7305 int min_x = x, min_y = y, max_x = x, max_y = y;
7308 for (i = 0; i < MAX_PLAYERS; i++)
7310 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7312 if (!stored_player[i].active || &stored_player[i] == player)
7315 min_x = MIN(min_x, jx);
7316 min_y = MIN(min_y, jy);
7317 max_x = MAX(max_x, jx);
7318 max_y = MAX(max_y, jy);
7321 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7324 static boolean AllPlayersInVisibleScreen()
7328 for (i = 0; i < MAX_PLAYERS; i++)
7330 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7332 if (!stored_player[i].active)
7335 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7342 void ScrollLevel(int dx, int dy)
7344 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7347 BlitBitmap(drawto_field, drawto_field,
7348 FX + TILEX * (dx == -1) - softscroll_offset,
7349 FY + TILEY * (dy == -1) - softscroll_offset,
7350 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7351 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7352 FX + TILEX * (dx == 1) - softscroll_offset,
7353 FY + TILEY * (dy == 1) - softscroll_offset);
7357 x = (dx == 1 ? BX1 : BX2);
7358 for (y = BY1; y <= BY2; y++)
7359 DrawScreenField(x, y);
7364 y = (dy == 1 ? BY1 : BY2);
7365 for (x = BX1; x <= BX2; x++)
7366 DrawScreenField(x, y);
7369 redraw_mask |= REDRAW_FIELD;
7372 static void CheckGravityMovement(struct PlayerInfo *player)
7374 if (game.gravity && !player->programmed_action)
7376 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7377 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7379 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7380 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7381 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7382 int jx = player->jx, jy = player->jy;
7383 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7384 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7385 int new_jx = jx + dx, new_jy = jy + dy;
7386 boolean field_under_player_is_free =
7387 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7388 boolean player_is_moving_to_valid_field =
7389 (IN_LEV_FIELD(new_jx, new_jy) &&
7390 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7391 Feld[new_jx][new_jy] == EL_SAND));
7392 /* !!! extend EL_SAND to anything diggable !!! */
7394 if (field_under_player_is_free &&
7395 !player_is_moving_to_valid_field &&
7396 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7397 player->programmed_action = MV_DOWN;
7403 -----------------------------------------------------------------------------
7404 dx, dy: direction (non-diagonal) to try to move the player to
7405 real_dx, real_dy: direction as read from input device (can be diagonal)
7408 boolean MovePlayerOneStep(struct PlayerInfo *player,
7409 int dx, int dy, int real_dx, int real_dy)
7412 static int change_sides[4][2] =
7414 /* enter side leave side */
7415 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7416 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7417 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7418 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7420 int move_direction = (dx == -1 ? MV_LEFT :
7421 dx == +1 ? MV_RIGHT :
7423 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7424 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7425 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7427 int jx = player->jx, jy = player->jy;
7428 int new_jx = jx + dx, new_jy = jy + dy;
7432 if (!player->active || (!dx && !dy))
7433 return MF_NO_ACTION;
7435 player->MovDir = (dx < 0 ? MV_LEFT :
7438 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7440 if (!IN_LEV_FIELD(new_jx, new_jy))
7441 return MF_NO_ACTION;
7443 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7444 return MF_NO_ACTION;
7447 element = MovingOrBlocked2Element(new_jx, new_jy);
7449 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7452 if (DONT_RUN_INTO(element))
7454 if (element == EL_ACID && dx == 0 && dy == 1)
7457 Feld[jx][jy] = EL_PLAYER_1;
7458 InitMovingField(jx, jy, MV_DOWN);
7459 Store[jx][jy] = EL_ACID;
7460 ContinueMoving(jx, jy);
7464 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7469 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7470 if (can_move != MF_MOVING)
7473 /* check if DigField() has caused relocation of the player */
7474 if (player->jx != jx || player->jy != jy)
7475 return MF_NO_ACTION;
7477 StorePlayer[jx][jy] = 0;
7478 player->last_jx = jx;
7479 player->last_jy = jy;
7480 player->jx = new_jx;
7481 player->jy = new_jy;
7482 StorePlayer[new_jx][new_jy] = player->element_nr;
7485 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7487 player->step_counter++;
7489 player->drop_delay = 0;
7491 PlayerVisit[jx][jy] = FrameCounter;
7493 ScrollPlayer(player, SCROLL_INIT);
7496 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7498 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7499 CE_OTHER_GETS_LEFT);
7500 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7501 CE_LEFT_BY_PLAYER, -1);
7504 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7506 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7507 enter_side, CE_OTHER_GETS_ENTERED);
7508 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7509 CE_ENTERED_BY_PLAYER, -1);
7516 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7518 int jx = player->jx, jy = player->jy;
7519 int old_jx = jx, old_jy = jy;
7520 int moved = MF_NO_ACTION;
7523 if (!player->active)
7528 if (player->MovPos == 0)
7530 player->is_moving = FALSE;
7531 player->is_digging = FALSE;
7532 player->is_collecting = FALSE;
7533 player->is_snapping = FALSE;
7534 player->is_pushing = FALSE;
7540 if (!player->active || (!dx && !dy))
7545 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7549 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7550 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7554 /* remove the last programmed player action */
7555 player->programmed_action = 0;
7559 /* should only happen if pre-1.2 tape recordings are played */
7560 /* this is only for backward compatibility */
7562 int original_move_delay_value = player->move_delay_value;
7565 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7569 /* scroll remaining steps with finest movement resolution */
7570 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7572 while (player->MovPos)
7574 ScrollPlayer(player, SCROLL_GO_ON);
7575 ScrollScreen(NULL, SCROLL_GO_ON);
7581 player->move_delay_value = original_move_delay_value;
7584 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7586 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7587 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7591 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7592 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7598 if (moved & MF_MOVING && !ScreenMovPos &&
7599 (player == local_player || !options.network))
7601 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7602 int offset = (setup.scroll_delay ? 3 : 0);
7604 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7606 /* actual player has left the screen -- scroll in that direction */
7607 if (jx != old_jx) /* player has moved horizontally */
7608 scroll_x += (jx - old_jx);
7609 else /* player has moved vertically */
7610 scroll_y += (jy - old_jy);
7614 if (jx != old_jx) /* player has moved horizontally */
7616 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7617 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7618 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7620 /* don't scroll over playfield boundaries */
7621 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7622 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7624 /* don't scroll more than one field at a time */
7625 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7627 /* don't scroll against the player's moving direction */
7628 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7629 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7630 scroll_x = old_scroll_x;
7632 else /* player has moved vertically */
7634 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7635 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7636 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7638 /* don't scroll over playfield boundaries */
7639 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7640 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7642 /* don't scroll more than one field at a time */
7643 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7645 /* don't scroll against the player's moving direction */
7646 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7647 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7648 scroll_y = old_scroll_y;
7652 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7654 if (!options.network && !AllPlayersInVisibleScreen())
7656 scroll_x = old_scroll_x;
7657 scroll_y = old_scroll_y;
7661 ScrollScreen(player, SCROLL_INIT);
7662 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7669 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7671 if (!(moved & MF_MOVING) && !player->is_pushing)
7676 player->StepFrame = 0;
7678 if (moved & MF_MOVING)
7680 if (old_jx != jx && old_jy == jy)
7681 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7682 else if (old_jx == jx && old_jy != jy)
7683 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7685 DrawLevelField(jx, jy); /* for "crumbled sand" */
7687 player->last_move_dir = player->MovDir;
7688 player->is_moving = TRUE;
7690 player->is_snapping = FALSE;
7694 player->is_switching = FALSE;
7697 player->is_dropping = FALSE;
7702 static int change_sides[4][2] =
7704 /* enter side leave side */
7705 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7706 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7707 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7708 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7710 int move_direction = player->MovDir;
7711 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7712 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7715 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7717 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7718 leave_side, CE_OTHER_GETS_LEFT);
7719 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7720 leave_side, CE_LEFT_BY_PLAYER, -1);
7723 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7725 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7726 enter_side, CE_OTHER_GETS_ENTERED);
7727 CheckElementSideChange(jx, jy, Feld[jx][jy],
7728 enter_side, CE_ENTERED_BY_PLAYER, -1);
7739 CheckGravityMovement(player);
7742 player->last_move_dir = MV_NO_MOVING;
7744 player->is_moving = FALSE;
7747 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7749 TestIfHeroTouchesBadThing(jx, jy);
7750 TestIfPlayerTouchesCustomElement(jx, jy);
7753 if (!player->active)
7759 void ScrollPlayer(struct PlayerInfo *player, int mode)
7761 int jx = player->jx, jy = player->jy;
7762 int last_jx = player->last_jx, last_jy = player->last_jy;
7763 int move_stepsize = TILEX / player->move_delay_value;
7765 if (!player->active || !player->MovPos)
7768 if (mode == SCROLL_INIT)
7770 player->actual_frame_counter = FrameCounter;
7771 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7773 if (Feld[last_jx][last_jy] == EL_EMPTY)
7774 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7781 else if (!FrameReached(&player->actual_frame_counter, 1))
7784 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7785 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7787 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7788 Feld[last_jx][last_jy] = EL_EMPTY;
7790 /* before DrawPlayer() to draw correct player graphic for this case */
7791 if (player->MovPos == 0)
7792 CheckGravityMovement(player);
7795 DrawPlayer(player); /* needed here only to cleanup last field */
7798 if (player->MovPos == 0) /* player reached destination field */
7801 if (player->move_delay_reset_counter > 0)
7803 player->move_delay_reset_counter--;
7805 if (player->move_delay_reset_counter == 0)
7807 /* continue with normal speed after quickly moving through gate */
7808 HALVE_PLAYER_SPEED(player);
7810 /* be able to make the next move without delay */
7811 player->move_delay = 0;
7815 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7817 /* continue with normal speed after quickly moving through gate */
7818 HALVE_PLAYER_SPEED(player);
7820 /* be able to make the next move without delay */
7821 player->move_delay = 0;
7825 player->last_jx = jx;
7826 player->last_jy = jy;
7828 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7829 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7830 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7832 DrawPlayer(player); /* needed here only to cleanup last field */
7835 if (local_player->friends_still_needed == 0 ||
7836 IS_SP_ELEMENT(Feld[jx][jy]))
7837 player->LevelSolved = player->GameOver = TRUE;
7840 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7842 TestIfHeroTouchesBadThing(jx, jy);
7843 TestIfPlayerTouchesCustomElement(jx, jy);
7845 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7848 if (!player->active)
7852 if (tape.single_step && tape.recording && !tape.pausing &&
7853 !player->programmed_action)
7854 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7858 void ScrollScreen(struct PlayerInfo *player, int mode)
7860 static unsigned long screen_frame_counter = 0;
7862 if (mode == SCROLL_INIT)
7864 /* set scrolling step size according to actual player's moving speed */
7865 ScrollStepSize = TILEX / player->move_delay_value;
7867 screen_frame_counter = FrameCounter;
7868 ScreenMovDir = player->MovDir;
7869 ScreenMovPos = player->MovPos;
7870 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7873 else if (!FrameReached(&screen_frame_counter, 1))
7878 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7879 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7880 redraw_mask |= REDRAW_FIELD;
7883 ScreenMovDir = MV_NO_MOVING;
7886 void TestIfPlayerTouchesCustomElement(int x, int y)
7888 static int xy[4][2] =
7895 static int change_sides[4][2] =
7897 /* center side border side */
7898 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7899 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7900 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7901 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7903 static int touch_dir[4] =
7910 int center_element = Feld[x][y]; /* should always be non-moving! */
7913 for (i = 0; i < 4; i++)
7915 int xx = x + xy[i][0];
7916 int yy = y + xy[i][1];
7917 int center_side = change_sides[i][0];
7918 int border_side = change_sides[i][1];
7921 if (!IN_LEV_FIELD(xx, yy))
7924 if (IS_PLAYER(x, y))
7926 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7927 border_element = Feld[xx][yy]; /* may be moving! */
7928 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7929 border_element = Feld[xx][yy];
7930 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7931 border_element = MovingOrBlocked2Element(xx, yy);
7933 continue; /* center and border element do not touch */
7935 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7936 CE_OTHER_GETS_TOUCHED);
7937 CheckElementSideChange(xx, yy, border_element, border_side,
7938 CE_TOUCHED_BY_PLAYER, -1);
7940 else if (IS_PLAYER(xx, yy))
7942 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7944 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7946 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7947 continue; /* center and border element do not touch */
7950 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7951 CE_OTHER_GETS_TOUCHED);
7952 CheckElementSideChange(x, y, center_element, center_side,
7953 CE_TOUCHED_BY_PLAYER, -1);
7960 void TestIfElementTouchesCustomElement(int x, int y)
7962 static int xy[4][2] =
7969 static int change_sides[4][2] =
7971 /* center side border side */
7972 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7973 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7974 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7975 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7977 static int touch_dir[4] =
7984 boolean change_center_element = FALSE;
7985 int center_element_change_page = 0;
7986 int center_element = Feld[x][y]; /* should always be non-moving! */
7989 for (i = 0; i < 4; i++)
7991 int xx = x + xy[i][0];
7992 int yy = y + xy[i][1];
7993 int center_side = change_sides[i][0];
7994 int border_side = change_sides[i][1];
7997 if (!IN_LEV_FIELD(xx, yy))
8000 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8001 border_element = Feld[xx][yy]; /* may be moving! */
8002 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8003 border_element = Feld[xx][yy];
8004 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8005 border_element = MovingOrBlocked2Element(xx, yy);
8007 continue; /* center and border element do not touch */
8009 /* check for change of center element (but change it only once) */
8010 if (IS_CUSTOM_ELEMENT(center_element) &&
8011 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8012 !change_center_element)
8014 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8016 struct ElementChangeInfo *change =
8017 &element_info[center_element].change_page[j];
8019 if (change->can_change &&
8020 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8021 change->sides & border_side &&
8022 change->trigger_element == border_element)
8024 change_center_element = TRUE;
8025 center_element_change_page = j;
8032 /* check for change of border element */
8033 if (IS_CUSTOM_ELEMENT(border_element) &&
8034 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8036 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8038 struct ElementChangeInfo *change =
8039 &element_info[border_element].change_page[j];
8041 if (change->can_change &&
8042 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8043 change->sides & center_side &&
8044 change->trigger_element == center_element)
8046 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8047 CE_OTHER_IS_TOUCHING, j);
8054 if (change_center_element)
8055 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8056 CE_OTHER_IS_TOUCHING, center_element_change_page);
8059 void TestIfElementHitsCustomElement(int x, int y, int direction)
8061 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8062 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8063 int hitx = x + dx, hity = y + dy;
8064 int hitting_element = Feld[x][y];
8066 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8067 !IS_FREE(hitx, hity) &&
8068 (!IS_MOVING(hitx, hity) ||
8069 MovDir[hitx][hity] != direction ||
8070 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8073 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8077 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8081 CheckElementSideChange(x, y, hitting_element,
8082 direction, CE_HITTING_SOMETHING, -1);
8084 if (IN_LEV_FIELD(hitx, hity))
8086 static int opposite_directions[] =
8093 int move_dir_bit = MV_DIR_BIT(direction);
8094 int opposite_direction = opposite_directions[move_dir_bit];
8095 int hitting_side = direction;
8096 int touched_side = opposite_direction;
8097 int touched_element = MovingOrBlocked2Element(hitx, hity);
8099 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8100 MovDir[hitx][hity] != direction ||
8101 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8110 CheckElementSideChange(hitx, hity, touched_element,
8111 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8113 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8114 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8116 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8118 struct ElementChangeInfo *change =
8119 &element_info[hitting_element].change_page[i];
8121 if (change->can_change &&
8122 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8123 change->sides & touched_side &&
8124 change->trigger_element == touched_element)
8126 CheckElementSideChange(x, y, hitting_element,
8127 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8133 if (IS_CUSTOM_ELEMENT(touched_element) &&
8134 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8136 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8138 struct ElementChangeInfo *change =
8139 &element_info[touched_element].change_page[i];
8141 if (change->can_change &&
8142 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8143 change->sides & hitting_side &&
8144 change->trigger_element == hitting_element)
8146 CheckElementSideChange(hitx, hity, touched_element,
8147 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8156 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8158 int i, kill_x = -1, kill_y = -1;
8159 static int test_xy[4][2] =
8166 static int test_dir[4] =
8174 for (i = 0; i < 4; i++)
8176 int test_x, test_y, test_move_dir, test_element;
8178 test_x = good_x + test_xy[i][0];
8179 test_y = good_y + test_xy[i][1];
8180 if (!IN_LEV_FIELD(test_x, test_y))
8184 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8187 test_element = Feld[test_x][test_y];
8189 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8192 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8193 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8195 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8196 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8204 if (kill_x != -1 || kill_y != -1)
8206 if (IS_PLAYER(good_x, good_y))
8208 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8210 if (player->shield_deadly_time_left > 0)
8211 Bang(kill_x, kill_y);
8212 else if (!PLAYER_PROTECTED(good_x, good_y))
8216 Bang(good_x, good_y);
8220 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8222 int i, kill_x = -1, kill_y = -1;
8223 int bad_element = Feld[bad_x][bad_y];
8224 static int test_xy[4][2] =
8231 static int touch_dir[4] =
8238 static int test_dir[4] =
8246 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8249 for (i = 0; i < 4; i++)
8251 int test_x, test_y, test_move_dir, test_element;
8253 test_x = bad_x + test_xy[i][0];
8254 test_y = bad_y + test_xy[i][1];
8255 if (!IN_LEV_FIELD(test_x, test_y))
8259 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8261 test_element = Feld[test_x][test_y];
8263 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8264 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8266 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8267 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8269 /* good thing is player or penguin that does not move away */
8270 if (IS_PLAYER(test_x, test_y))
8272 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8274 if (bad_element == EL_ROBOT && player->is_moving)
8275 continue; /* robot does not kill player if he is moving */
8277 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8279 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8280 continue; /* center and border element do not touch */
8287 else if (test_element == EL_PENGUIN)
8296 if (kill_x != -1 || kill_y != -1)
8298 if (IS_PLAYER(kill_x, kill_y))
8300 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8302 if (player->shield_deadly_time_left > 0)
8304 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8308 Bang(kill_x, kill_y);
8312 void TestIfHeroTouchesBadThing(int x, int y)
8314 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8317 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8319 TestIfGoodThingHitsBadThing(x, y, move_dir);
8322 void TestIfBadThingTouchesHero(int x, int y)
8324 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8327 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8329 TestIfBadThingHitsGoodThing(x, y, move_dir);
8332 void TestIfFriendTouchesBadThing(int x, int y)
8334 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8337 void TestIfBadThingTouchesFriend(int x, int y)
8339 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8342 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8344 int i, kill_x = bad_x, kill_y = bad_y;
8345 static int xy[4][2] =
8353 for (i = 0; i < 4; i++)
8357 x = bad_x + xy[i][0];
8358 y = bad_y + xy[i][1];
8359 if (!IN_LEV_FIELD(x, y))
8362 element = Feld[x][y];
8363 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8364 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8372 if (kill_x != bad_x || kill_y != bad_y)
8376 void KillHero(struct PlayerInfo *player)
8378 int jx = player->jx, jy = player->jy;
8380 if (!player->active)
8383 /* remove accessible field at the player's position */
8384 Feld[jx][jy] = EL_EMPTY;
8386 /* deactivate shield (else Bang()/Explode() would not work right) */
8387 player->shield_normal_time_left = 0;
8388 player->shield_deadly_time_left = 0;
8394 static void KillHeroUnlessProtected(int x, int y)
8396 if (!PLAYER_PROTECTED(x, y))
8397 KillHero(PLAYERINFO(x, y));
8400 void BuryHero(struct PlayerInfo *player)
8402 int jx = player->jx, jy = player->jy;
8404 if (!player->active)
8408 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8410 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8412 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8414 player->GameOver = TRUE;
8418 void RemoveHero(struct PlayerInfo *player)
8420 int jx = player->jx, jy = player->jy;
8421 int i, found = FALSE;
8423 player->present = FALSE;
8424 player->active = FALSE;
8426 if (!ExplodeField[jx][jy])
8427 StorePlayer[jx][jy] = 0;
8429 for (i = 0; i < MAX_PLAYERS; i++)
8430 if (stored_player[i].active)
8434 AllPlayersGone = TRUE;
8441 =============================================================================
8442 checkDiagonalPushing()
8443 -----------------------------------------------------------------------------
8444 check if diagonal input device direction results in pushing of object
8445 (by checking if the alternative direction is walkable, diggable, ...)
8446 =============================================================================
8449 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8450 int x, int y, int real_dx, int real_dy)
8452 int jx, jy, dx, dy, xx, yy;
8454 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8457 /* diagonal direction: check alternative direction */
8462 xx = jx + (dx == 0 ? real_dx : 0);
8463 yy = jy + (dy == 0 ? real_dy : 0);
8465 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8469 =============================================================================
8471 -----------------------------------------------------------------------------
8472 x, y: field next to player (non-diagonal) to try to dig to
8473 real_dx, real_dy: direction as read from input device (can be diagonal)
8474 =============================================================================
8477 int DigField(struct PlayerInfo *player,
8478 int x, int y, int real_dx, int real_dy, int mode)
8480 static int change_sides[4] =
8482 CH_SIDE_RIGHT, /* moving left */
8483 CH_SIDE_LEFT, /* moving right */
8484 CH_SIDE_BOTTOM, /* moving up */
8485 CH_SIDE_TOP, /* moving down */
8487 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8488 int jx = player->jx, jy = player->jy;
8489 int dx = x - jx, dy = y - jy;
8490 int nextx = x + dx, nexty = y + dy;
8491 int move_direction = (dx == -1 ? MV_LEFT :
8492 dx == +1 ? MV_RIGHT :
8494 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8495 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8498 if (player->MovPos == 0)
8500 player->is_digging = FALSE;
8501 player->is_collecting = FALSE;
8504 if (player->MovPos == 0) /* last pushing move finished */
8505 player->is_pushing = FALSE;
8507 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8509 player->is_switching = FALSE;
8510 player->push_delay = 0;
8512 return MF_NO_ACTION;
8515 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8516 return MF_NO_ACTION;
8519 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8521 if (IS_TUBE(Feld[jx][jy]) ||
8522 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8526 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8527 int tube_leave_directions[][2] =
8529 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8530 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8531 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8532 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8533 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8534 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8535 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8536 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8537 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8538 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8539 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8540 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8543 while (tube_leave_directions[i][0] != tube_element)
8546 if (tube_leave_directions[i][0] == -1) /* should not happen */
8550 if (!(tube_leave_directions[i][1] & move_direction))
8551 return MF_NO_ACTION; /* tube has no opening in this direction */
8554 element = Feld[x][y];
8556 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8557 game.engine_version >= VERSION_IDENT(2,2,0,0))
8558 return MF_NO_ACTION;
8562 case EL_SP_PORT_LEFT:
8563 case EL_SP_PORT_RIGHT:
8565 case EL_SP_PORT_DOWN:
8566 case EL_SP_PORT_HORIZONTAL:
8567 case EL_SP_PORT_VERTICAL:
8568 case EL_SP_PORT_ANY:
8569 case EL_SP_GRAVITY_PORT_LEFT:
8570 case EL_SP_GRAVITY_PORT_RIGHT:
8571 case EL_SP_GRAVITY_PORT_UP:
8572 case EL_SP_GRAVITY_PORT_DOWN:
8574 element != EL_SP_PORT_LEFT &&
8575 element != EL_SP_GRAVITY_PORT_LEFT &&
8576 element != EL_SP_PORT_HORIZONTAL &&
8577 element != EL_SP_PORT_ANY) ||
8579 element != EL_SP_PORT_RIGHT &&
8580 element != EL_SP_GRAVITY_PORT_RIGHT &&
8581 element != EL_SP_PORT_HORIZONTAL &&
8582 element != EL_SP_PORT_ANY) ||
8584 element != EL_SP_PORT_UP &&
8585 element != EL_SP_GRAVITY_PORT_UP &&
8586 element != EL_SP_PORT_VERTICAL &&
8587 element != EL_SP_PORT_ANY) ||
8589 element != EL_SP_PORT_DOWN &&
8590 element != EL_SP_GRAVITY_PORT_DOWN &&
8591 element != EL_SP_PORT_VERTICAL &&
8592 element != EL_SP_PORT_ANY) ||
8593 !IN_LEV_FIELD(nextx, nexty) ||
8594 !IS_FREE(nextx, nexty))
8595 return MF_NO_ACTION;
8597 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8598 element == EL_SP_GRAVITY_PORT_RIGHT ||
8599 element == EL_SP_GRAVITY_PORT_UP ||
8600 element == EL_SP_GRAVITY_PORT_DOWN)
8601 game.gravity = !game.gravity;
8603 /* automatically move to the next field with double speed */
8604 player->programmed_action = move_direction;
8606 if (player->move_delay_reset_counter == 0)
8608 player->move_delay_reset_counter = 2; /* two double speed steps */
8610 DOUBLE_PLAYER_SPEED(player);
8613 player->move_delay_reset_counter = 2;
8615 DOUBLE_PLAYER_SPEED(player);
8618 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8622 case EL_TUBE_VERTICAL:
8623 case EL_TUBE_HORIZONTAL:
8624 case EL_TUBE_VERTICAL_LEFT:
8625 case EL_TUBE_VERTICAL_RIGHT:
8626 case EL_TUBE_HORIZONTAL_UP:
8627 case EL_TUBE_HORIZONTAL_DOWN:
8628 case EL_TUBE_LEFT_UP:
8629 case EL_TUBE_LEFT_DOWN:
8630 case EL_TUBE_RIGHT_UP:
8631 case EL_TUBE_RIGHT_DOWN:
8634 int tube_enter_directions[][2] =
8636 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8637 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8638 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8639 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8640 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8641 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8642 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8643 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8644 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8645 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8646 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8647 { -1, MV_NO_MOVING }
8650 while (tube_enter_directions[i][0] != element)
8653 if (tube_enter_directions[i][0] == -1) /* should not happen */
8657 if (!(tube_enter_directions[i][1] & move_direction))
8658 return MF_NO_ACTION; /* tube has no opening in this direction */
8660 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8666 if (IS_WALKABLE(element))
8668 int sound_action = ACTION_WALKING;
8670 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8672 if (!player->key[element - EL_GATE_1])
8673 return MF_NO_ACTION;
8675 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8677 if (!player->key[element - EL_GATE_1_GRAY])
8678 return MF_NO_ACTION;
8680 else if (element == EL_EXIT_OPEN ||
8681 element == EL_SP_EXIT_OPEN ||
8682 element == EL_SP_EXIT_OPENING)
8684 sound_action = ACTION_PASSING; /* player is passing exit */
8686 else if (element == EL_EMPTY)
8688 sound_action = ACTION_MOVING; /* nothing to walk on */
8691 /* play sound from background or player, whatever is available */
8692 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8693 PlayLevelSoundElementAction(x, y, element, sound_action);
8695 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8699 else if (IS_PASSABLE(element))
8701 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8702 return MF_NO_ACTION;
8705 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8706 return MF_NO_ACTION;
8709 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8711 if (!player->key[element - EL_EM_GATE_1])
8712 return MF_NO_ACTION;
8714 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8716 if (!player->key[element - EL_EM_GATE_1_GRAY])
8717 return MF_NO_ACTION;
8720 /* automatically move to the next field with double speed */
8721 player->programmed_action = move_direction;
8723 if (player->move_delay_reset_counter == 0)
8725 player->move_delay_reset_counter = 2; /* two double speed steps */
8727 DOUBLE_PLAYER_SPEED(player);
8730 player->move_delay_reset_counter = 2;
8732 DOUBLE_PLAYER_SPEED(player);
8735 PlayLevelSoundAction(x, y, ACTION_PASSING);
8739 else if (IS_DIGGABLE(element))
8743 if (mode != DF_SNAP)
8746 GfxElement[x][y] = GFX_ELEMENT(element);
8749 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8751 player->is_digging = TRUE;
8754 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8756 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8759 if (mode == DF_SNAP)
8760 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8765 else if (IS_COLLECTIBLE(element))
8769 if (mode != DF_SNAP)
8771 GfxElement[x][y] = element;
8772 player->is_collecting = TRUE;
8775 if (element == EL_SPEED_PILL)
8776 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8777 else if (element == EL_EXTRA_TIME && level.time > 0)
8780 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8782 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8784 player->shield_normal_time_left += 10;
8785 if (element == EL_SHIELD_DEADLY)
8786 player->shield_deadly_time_left += 10;
8788 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8790 if (player->inventory_size < MAX_INVENTORY_SIZE)
8791 player->inventory_element[player->inventory_size++] = element;
8793 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8794 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8796 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8798 player->dynabomb_count++;
8799 player->dynabombs_left++;
8801 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8803 player->dynabomb_size++;
8805 else if (element == EL_DYNABOMB_INCREASE_POWER)
8807 player->dynabomb_xl = TRUE;
8809 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8810 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8812 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8813 element - EL_KEY_1 : element - EL_EM_KEY_1);
8815 player->key[key_nr] = TRUE;
8817 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8818 el2edimg(EL_KEY_1 + key_nr));
8819 redraw_mask |= REDRAW_DOOR_1;
8821 else if (IS_ENVELOPE(element))
8824 player->show_envelope = element;
8826 ShowEnvelope(element - EL_ENVELOPE_1);
8829 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8833 for (i = 0; i < element_info[element].collect_count; i++)
8834 if (player->inventory_size < MAX_INVENTORY_SIZE)
8835 player->inventory_element[player->inventory_size++] = element;
8837 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8838 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8840 else if (element_info[element].collect_count > 0)
8842 local_player->gems_still_needed -=
8843 element_info[element].collect_count;
8844 if (local_player->gems_still_needed < 0)
8845 local_player->gems_still_needed = 0;
8847 DrawText(DX_EMERALDS, DY_EMERALDS,
8848 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8851 RaiseScoreElement(element);
8852 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8854 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8857 if (mode == DF_SNAP)
8858 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8863 else if (IS_PUSHABLE(element))
8865 if (mode == DF_SNAP && element != EL_BD_ROCK)
8866 return MF_NO_ACTION;
8868 if (CAN_FALL(element) && dy)
8869 return MF_NO_ACTION;
8871 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8872 !(element == EL_SPRING && use_spring_bug))
8873 return MF_NO_ACTION;
8876 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8877 ((move_direction & MV_VERTICAL &&
8878 ((element_info[element].move_pattern & MV_LEFT &&
8879 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8880 (element_info[element].move_pattern & MV_RIGHT &&
8881 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8882 (move_direction & MV_HORIZONTAL &&
8883 ((element_info[element].move_pattern & MV_UP &&
8884 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8885 (element_info[element].move_pattern & MV_DOWN &&
8886 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8887 return MF_NO_ACTION;
8891 /* do not push elements already moving away faster than player */
8892 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8893 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8894 return MF_NO_ACTION;
8896 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8897 return MF_NO_ACTION;
8901 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8903 if (player->push_delay_value == -1)
8904 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8906 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8908 if (!player->is_pushing)
8909 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8913 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8914 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8915 !player_is_pushing))
8916 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8919 if (!player->is_pushing &&
8920 game.engine_version >= VERSION_IDENT(2,2,0,7))
8921 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8925 printf("::: push delay: %ld [%d, %d] [%d]\n",
8926 player->push_delay_value, FrameCounter, game.engine_version,
8927 player->is_pushing);
8930 player->is_pushing = TRUE;
8932 if (!(IN_LEV_FIELD(nextx, nexty) &&
8933 (IS_FREE(nextx, nexty) ||
8934 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8935 IS_SB_ELEMENT(element)))))
8936 return MF_NO_ACTION;
8938 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8939 return MF_NO_ACTION;
8941 if (player->push_delay == 0) /* new pushing; restart delay */
8942 player->push_delay = FrameCounter;
8944 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8945 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8946 element != EL_SPRING && element != EL_BALLOON)
8948 /* make sure that there is no move delay before next try to push */
8949 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8950 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8952 return MF_NO_ACTION;
8956 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8959 if (IS_SB_ELEMENT(element))
8961 if (element == EL_SOKOBAN_FIELD_FULL)
8963 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8964 local_player->sokobanfields_still_needed++;
8967 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8969 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8970 local_player->sokobanfields_still_needed--;
8973 Feld[x][y] = EL_SOKOBAN_OBJECT;
8975 if (Back[x][y] == Back[nextx][nexty])
8976 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8977 else if (Back[x][y] != 0)
8978 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8981 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8984 if (local_player->sokobanfields_still_needed == 0 &&
8985 game.emulation == EMU_SOKOBAN)
8987 player->LevelSolved = player->GameOver = TRUE;
8988 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8992 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8994 InitMovingField(x, y, move_direction);
8995 GfxAction[x][y] = ACTION_PUSHING;
8997 if (mode == DF_SNAP)
8998 ContinueMoving(x, y);
9000 MovPos[x][y] = (dx != 0 ? dx : dy);
9002 Pushed[x][y] = TRUE;
9003 Pushed[nextx][nexty] = TRUE;
9005 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9006 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9008 player->push_delay_value = -1; /* get new value later */
9010 CheckTriggeredElementSideChange(x, y, element, dig_side,
9011 CE_OTHER_GETS_PUSHED);
9012 CheckElementSideChange(x, y, element, dig_side,
9013 CE_PUSHED_BY_PLAYER, -1);
9017 else if (IS_SWITCHABLE(element))
9019 if (PLAYER_SWITCHING(player, x, y))
9022 player->is_switching = TRUE;
9023 player->switch_x = x;
9024 player->switch_y = y;
9026 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9028 if (element == EL_ROBOT_WHEEL)
9030 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9034 DrawLevelField(x, y);
9036 else if (element == EL_SP_TERMINAL)
9040 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9042 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9044 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9045 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9048 else if (IS_BELT_SWITCH(element))
9050 ToggleBeltSwitch(x, y);
9052 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9053 element == EL_SWITCHGATE_SWITCH_DOWN)
9055 ToggleSwitchgateSwitch(x, y);
9057 else if (element == EL_LIGHT_SWITCH ||
9058 element == EL_LIGHT_SWITCH_ACTIVE)
9060 ToggleLightSwitch(x, y);
9063 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9064 SND_LIGHT_SWITCH_ACTIVATING :
9065 SND_LIGHT_SWITCH_DEACTIVATING);
9068 else if (element == EL_TIMEGATE_SWITCH)
9070 ActivateTimegateSwitch(x, y);
9072 else if (element == EL_BALLOON_SWITCH_LEFT ||
9073 element == EL_BALLOON_SWITCH_RIGHT ||
9074 element == EL_BALLOON_SWITCH_UP ||
9075 element == EL_BALLOON_SWITCH_DOWN ||
9076 element == EL_BALLOON_SWITCH_ANY)
9078 if (element == EL_BALLOON_SWITCH_ANY)
9079 game.balloon_dir = move_direction;
9081 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9082 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9083 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9084 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9087 else if (element == EL_LAMP)
9089 Feld[x][y] = EL_LAMP_ACTIVE;
9090 local_player->lights_still_needed--;
9092 DrawLevelField(x, y);
9094 else if (element == EL_TIME_ORB_FULL)
9096 Feld[x][y] = EL_TIME_ORB_EMPTY;
9098 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9100 DrawLevelField(x, y);
9103 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9111 if (!PLAYER_SWITCHING(player, x, y))
9113 player->is_switching = TRUE;
9114 player->switch_x = x;
9115 player->switch_y = y;
9117 CheckTriggeredElementSideChange(x, y, element, dig_side,
9118 CE_OTHER_IS_SWITCHING);
9119 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9122 CheckTriggeredElementSideChange(x, y, element, dig_side,
9123 CE_OTHER_GETS_PRESSED);
9124 CheckElementSideChange(x, y, element, dig_side,
9125 CE_PRESSED_BY_PLAYER, -1);
9128 return MF_NO_ACTION;
9131 player->push_delay = 0;
9133 if (Feld[x][y] != element) /* really digged/collected something */
9134 player->is_collecting = !player->is_digging;
9139 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9141 int jx = player->jx, jy = player->jy;
9142 int x = jx + dx, y = jy + dy;
9143 int snap_direction = (dx == -1 ? MV_LEFT :
9144 dx == +1 ? MV_RIGHT :
9146 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9148 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9151 if (!player->active || !IN_LEV_FIELD(x, y))
9159 if (player->MovPos == 0)
9160 player->is_pushing = FALSE;
9162 player->is_snapping = FALSE;
9164 if (player->MovPos == 0)
9166 player->is_moving = FALSE;
9167 player->is_digging = FALSE;
9168 player->is_collecting = FALSE;
9174 if (player->is_snapping)
9177 player->MovDir = snap_direction;
9180 if (player->MovPos == 0)
9183 player->is_moving = FALSE;
9184 player->is_digging = FALSE;
9185 player->is_collecting = FALSE;
9188 player->is_dropping = FALSE;
9190 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9193 player->is_snapping = TRUE;
9196 if (player->MovPos == 0)
9199 player->is_moving = FALSE;
9200 player->is_digging = FALSE;
9201 player->is_collecting = FALSE;
9204 DrawLevelField(x, y);
9210 boolean DropElement(struct PlayerInfo *player)
9212 int jx = player->jx, jy = player->jy;
9213 int old_element = Feld[jx][jy];
9216 /* check if player is active, not moving and ready to drop */
9217 if (!player->active || player->MovPos || player->drop_delay > 0)
9220 /* check if player has anything that can be dropped */
9221 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9224 /* check if anything can be dropped at the current position */
9225 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9228 /* collected custom elements can only be dropped on empty fields */
9229 if (player->inventory_size > 0 &&
9230 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9231 && old_element != EL_EMPTY)
9234 if (old_element != EL_EMPTY)
9235 Back[jx][jy] = old_element; /* store old element on this field */
9237 ResetGfxAnimation(jx, jy);
9238 ResetRandomAnimationValue(jx, jy);
9240 if (player->inventory_size > 0)
9242 player->inventory_size--;
9243 new_element = player->inventory_element[player->inventory_size];
9245 if (new_element == EL_DYNAMITE)
9246 new_element = EL_DYNAMITE_ACTIVE;
9247 else if (new_element == EL_SP_DISK_RED)
9248 new_element = EL_SP_DISK_RED_ACTIVE;
9250 Feld[jx][jy] = new_element;
9252 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9253 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9255 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9256 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9258 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9260 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9261 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9263 TestIfElementTouchesCustomElement(jx, jy);
9265 else /* player is dropping a dyna bomb */
9267 player->dynabombs_left--;
9268 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9270 Feld[jx][jy] = new_element;
9272 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9273 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9275 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9282 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9284 InitField(jx, jy, FALSE);
9285 if (CAN_MOVE(Feld[jx][jy]))
9289 new_element = Feld[jx][jy];
9291 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9292 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9294 int move_stepsize = element_info[new_element].move_stepsize;
9295 int direction, dx, dy, nextx, nexty;
9297 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9298 MovDir[jx][jy] = player->MovDir;
9300 direction = MovDir[jx][jy];
9301 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9302 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9306 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9309 WasJustMoving[jx][jy] = 3;
9311 InitMovingField(jx, jy, direction);
9312 ContinueMoving(jx, jy);
9317 Changed[jx][jy] = 0; /* allow another change */
9320 TestIfElementHitsCustomElement(jx, jy, direction);
9322 CheckElementSideChange(jx, jy, new_element,
9323 direction, CE_HITTING_SOMETHING, -1);
9327 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9331 player->drop_delay = 8 + 8 + 8;
9336 player->is_dropping = TRUE;
9342 /* ------------------------------------------------------------------------- */
9343 /* game sound playing functions */
9344 /* ------------------------------------------------------------------------- */
9346 static int *loop_sound_frame = NULL;
9347 static int *loop_sound_volume = NULL;
9349 void InitPlayLevelSound()
9351 int num_sounds = getSoundListSize();
9353 checked_free(loop_sound_frame);
9354 checked_free(loop_sound_volume);
9356 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9357 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9360 static void PlayLevelSound(int x, int y, int nr)
9362 int sx = SCREENX(x), sy = SCREENY(y);
9363 int volume, stereo_position;
9364 int max_distance = 8;
9365 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9367 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9368 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9371 if (!IN_LEV_FIELD(x, y) ||
9372 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9373 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9376 volume = SOUND_MAX_VOLUME;
9378 if (!IN_SCR_FIELD(sx, sy))
9380 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9381 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9383 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9386 stereo_position = (SOUND_MAX_LEFT +
9387 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9388 (SCR_FIELDX + 2 * max_distance));
9390 if (IS_LOOP_SOUND(nr))
9392 /* This assures that quieter loop sounds do not overwrite louder ones,
9393 while restarting sound volume comparison with each new game frame. */
9395 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9398 loop_sound_volume[nr] = volume;
9399 loop_sound_frame[nr] = FrameCounter;
9402 PlaySoundExt(nr, volume, stereo_position, type);
9405 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9407 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9408 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9409 y < LEVELY(BY1) ? LEVELY(BY1) :
9410 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9414 static void PlayLevelSoundAction(int x, int y, int action)
9416 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9419 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9421 int sound_effect = element_info[element].sound[action];
9423 if (sound_effect != SND_UNDEFINED)
9424 PlayLevelSound(x, y, sound_effect);
9427 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9430 int sound_effect = element_info[element].sound[action];
9432 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9433 PlayLevelSound(x, y, sound_effect);
9436 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9438 int sound_effect = element_info[Feld[x][y]].sound[action];
9440 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9441 PlayLevelSound(x, y, sound_effect);
9444 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9446 int sound_effect = element_info[Feld[x][y]].sound[action];
9448 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9449 StopSound(sound_effect);
9452 static void PlayLevelMusic()
9454 if (levelset.music[level_nr] != MUS_UNDEFINED)
9455 PlayMusic(levelset.music[level_nr]); /* from config file */
9457 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9460 void RaiseScore(int value)
9462 local_player->score += value;
9463 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9466 void RaiseScoreElement(int element)
9472 case EL_EMERALD_YELLOW:
9473 case EL_EMERALD_RED:
9474 case EL_EMERALD_PURPLE:
9475 case EL_SP_INFOTRON:
9476 RaiseScore(level.score[SC_EMERALD]);
9479 RaiseScore(level.score[SC_DIAMOND]);
9482 RaiseScore(level.score[SC_CRYSTAL]);
9485 RaiseScore(level.score[SC_PEARL]);
9488 case EL_BD_BUTTERFLY:
9489 case EL_SP_ELECTRON:
9490 RaiseScore(level.score[SC_BUG]);
9494 case EL_SP_SNIKSNAK:
9495 RaiseScore(level.score[SC_SPACESHIP]);
9498 case EL_DARK_YAMYAM:
9499 RaiseScore(level.score[SC_YAMYAM]);
9502 RaiseScore(level.score[SC_ROBOT]);
9505 RaiseScore(level.score[SC_PACMAN]);
9508 RaiseScore(level.score[SC_NUT]);
9511 case EL_SP_DISK_RED:
9512 case EL_DYNABOMB_INCREASE_NUMBER:
9513 case EL_DYNABOMB_INCREASE_SIZE:
9514 case EL_DYNABOMB_INCREASE_POWER:
9515 RaiseScore(level.score[SC_DYNAMITE]);
9517 case EL_SHIELD_NORMAL:
9518 case EL_SHIELD_DEADLY:
9519 RaiseScore(level.score[SC_SHIELD]);
9522 RaiseScore(level.score[SC_TIME_BONUS]);
9528 RaiseScore(level.score[SC_KEY]);
9531 RaiseScore(element_info[element].collect_score);
9536 void RequestQuitGame(boolean ask_if_really_quit)
9538 if (AllPlayersGone ||
9539 !ask_if_really_quit ||
9540 level_editor_test_game ||
9541 Request("Do you really want to quit the game ?",
9542 REQ_ASK | REQ_STAY_CLOSED))
9544 #if defined(PLATFORM_UNIX)
9545 if (options.network)
9546 SendToServer_StopPlaying();
9550 game_status = GAME_MODE_MAIN;
9556 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9561 /* ---------- new game button stuff ---------------------------------------- */
9563 /* graphic position values for game buttons */
9564 #define GAME_BUTTON_XSIZE 30
9565 #define GAME_BUTTON_YSIZE 30
9566 #define GAME_BUTTON_XPOS 5
9567 #define GAME_BUTTON_YPOS 215
9568 #define SOUND_BUTTON_XPOS 5
9569 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9571 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9572 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9573 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9574 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9575 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9576 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9583 } gamebutton_info[NUM_GAME_BUTTONS] =
9586 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9591 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9596 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9601 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9602 SOUND_CTRL_ID_MUSIC,
9603 "background music on/off"
9606 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9607 SOUND_CTRL_ID_LOOPS,
9608 "sound loops on/off"
9611 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9612 SOUND_CTRL_ID_SIMPLE,
9613 "normal sounds on/off"
9617 void CreateGameButtons()
9621 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9623 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9624 struct GadgetInfo *gi;
9627 unsigned long event_mask;
9628 int gd_xoffset, gd_yoffset;
9629 int gd_x1, gd_x2, gd_y1, gd_y2;
9632 gd_xoffset = gamebutton_info[i].x;
9633 gd_yoffset = gamebutton_info[i].y;
9634 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9635 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9637 if (id == GAME_CTRL_ID_STOP ||
9638 id == GAME_CTRL_ID_PAUSE ||
9639 id == GAME_CTRL_ID_PLAY)
9641 button_type = GD_TYPE_NORMAL_BUTTON;
9643 event_mask = GD_EVENT_RELEASED;
9644 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9645 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9649 button_type = GD_TYPE_CHECK_BUTTON;
9651 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9652 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9653 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9654 event_mask = GD_EVENT_PRESSED;
9655 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9656 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9659 gi = CreateGadget(GDI_CUSTOM_ID, id,
9660 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9661 GDI_X, DX + gd_xoffset,
9662 GDI_Y, DY + gd_yoffset,
9663 GDI_WIDTH, GAME_BUTTON_XSIZE,
9664 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9665 GDI_TYPE, button_type,
9666 GDI_STATE, GD_BUTTON_UNPRESSED,
9667 GDI_CHECKED, checked,
9668 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9669 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9670 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9671 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9672 GDI_EVENT_MASK, event_mask,
9673 GDI_CALLBACK_ACTION, HandleGameButtons,
9677 Error(ERR_EXIT, "cannot create gadget");
9679 game_gadget[id] = gi;
9683 void FreeGameButtons()
9687 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9688 FreeGadget(game_gadget[i]);
9691 static void MapGameButtons()
9695 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9696 MapGadget(game_gadget[i]);
9699 void UnmapGameButtons()
9703 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9704 UnmapGadget(game_gadget[i]);
9707 static void HandleGameButtons(struct GadgetInfo *gi)
9709 int id = gi->custom_id;
9711 if (game_status != GAME_MODE_PLAYING)
9716 case GAME_CTRL_ID_STOP:
9717 RequestQuitGame(TRUE);
9720 case GAME_CTRL_ID_PAUSE:
9721 if (options.network)
9723 #if defined(PLATFORM_UNIX)
9725 SendToServer_ContinuePlaying();
9727 SendToServer_PausePlaying();
9731 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9734 case GAME_CTRL_ID_PLAY:
9737 #if defined(PLATFORM_UNIX)
9738 if (options.network)
9739 SendToServer_ContinuePlaying();
9743 tape.pausing = FALSE;
9744 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9749 case SOUND_CTRL_ID_MUSIC:
9750 if (setup.sound_music)
9752 setup.sound_music = FALSE;
9755 else if (audio.music_available)
9757 setup.sound = setup.sound_music = TRUE;
9759 SetAudioMode(setup.sound);
9765 case SOUND_CTRL_ID_LOOPS:
9766 if (setup.sound_loops)
9767 setup.sound_loops = FALSE;
9768 else if (audio.loops_available)
9770 setup.sound = setup.sound_loops = TRUE;
9771 SetAudioMode(setup.sound);
9775 case SOUND_CTRL_ID_SIMPLE:
9776 if (setup.sound_simple)
9777 setup.sound_simple = FALSE;
9778 else if (audio.sound_available)
9780 setup.sound = setup.sound_simple = TRUE;
9781 SetAudioMode(setup.sound);