1 #include "info-reader/dungeon-reader.h"
2 #include "dungeon/dungeon.h"
3 #include "grid/feature.h"
4 #include "info-reader/dungeon-info-tokens-table.h"
5 #include "info-reader/feature-reader.h"
6 #include "info-reader/info-reader-util.h"
7 #include "info-reader/parse-error-types.h"
8 #include "info-reader/race-info-tokens-table.h"
9 #include "io/tokenizer.h"
10 #include "main/angband-headers.h"
11 #include "util/string-processor.h"
12 #include "view/display-messages.h"
15 * @brief テキストトークンを走査してフラグを一つ得る(ダンジョン用) /
16 * Grab one flag for a dungeon type from a textual string
17 * @param d_ptr 保管先のダンジョン構造体参照ポインタ
18 * @param what 参照元の文字列ポインタ
21 static bool grab_one_dungeon_flag(dungeon_type *d_ptr, std::string_view what)
23 if (EnumClassFlagGroup<DungeonFeatureType>::grab_one_flag(d_ptr->flags, d_info_flags, what))
26 msg_format(_("未知のダンジョン・フラグ '%s'。", "Unknown dungeon type flag '%s'."), what.data());
31 * @brief テキストトークンを走査してフラグを一つ得る(モンスターのダンジョン出現条件用1) /
32 * Grab one (basic) flag in a monster_race from a textual string
33 * @param d_ptr 保管先のダンジョン構造体参照ポインタ
34 * @param what 参照元の文字列ポインタ
37 static bool grab_one_basic_monster_flag(dungeon_type *d_ptr, std::string_view what)
39 if (info_grab_one_flag(d_ptr->mflags1, r_info_flags1, what))
42 if (info_grab_one_flag(d_ptr->mflags2, r_info_flags2, what))
45 if (info_grab_one_flag(d_ptr->mflags3, r_info_flags3, what))
48 if (info_grab_one_flag(d_ptr->mflags7, r_info_flags7, what))
51 if (info_grab_one_flag(d_ptr->mflags8, r_info_flags8, what))
54 if (info_grab_one_flag(d_ptr->mflags9, r_info_flags9, what))
57 if (info_grab_one_flag(d_ptr->mflagsr, r_info_flagsr, what))
60 if (EnumClassFlagGroup<MonsterBehaviorType>::grab_one_flag(d_ptr->mon_behavior_flags, r_info_behavior_flags, what))
63 msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
68 * @brief テキストトークンを走査してフラグを一つ得る(モンスターのダンジョン出現条件用2) /
69 * Grab one (spell) flag in a monster_race from a textual string
70 * @param d_ptr 保管先のダンジョン構造体参照ポインタ
71 * @param what 参照元の文字列ポインタ
74 static bool grab_one_spell_monster_flag(dungeon_type *d_ptr, std::string_view what)
76 if (EnumClassFlagGroup<MonsterAbilityType>::grab_one_flag(d_ptr->mon_ability_flags, r_info_ability_flags, what))
79 msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
84 * @brief ダンジョン情報(d_info)のパース関数 /
85 * Initialize the "d_info" array, by parsing an ascii "template" file
90 errr parse_d_info(std::string_view buf, angband_header *)
92 static dungeon_type *d_ptr = nullptr;
93 const auto &tokens = str_split(buf, ':', false);
95 if (tokens[0] == "N") {
97 if (tokens.size() < 3 || tokens[1].size() == 0)
98 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
100 auto i = std::stoi(tokens[1]);
102 return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
103 if (i >= static_cast<int>(d_info.size())) {
104 d_info.resize(i + 1);
109 d_ptr->idx = static_cast<DUNGEON_IDX>(i);
111 d_ptr->name = tokens[2];
114 return PARSE_ERROR_MISSING_RECORD_HEADER;
115 else if (tokens[0] == "E") {
118 if (tokens.size() < 2 || tokens[1].size() == 0)
119 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
120 d_ptr->name = tokens[1];
122 } else if (tokens[0] == "D") {
125 if (tokens.size() < 2 || tokens[1].size() == 0)
126 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
128 if (tokens[1][0] == '$')
129 return PARSE_ERROR_NONE;
130 d_ptr->text.append(buf.substr(2));
132 if (tokens[1][0] != '$')
133 return PARSE_ERROR_NONE;
134 append_english_text(d_ptr->text, buf.substr(3));
136 } else if (tokens[0] == "W") {
137 // W:min_level:max_level:(1):mode:(2):(3):(4):(5):prob_pit:prob_nest
138 // (1)minimum player level (unused)
139 // (2)minimum level of allocating monster
140 // (3)maximum probability of level boost of allocation monster
141 // (4)maximum probability of dropping good objects
142 // (5)maximum probability of dropping great objects
143 if (tokens.size() < 11)
144 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
146 info_set_value(d_ptr->mindepth, tokens[1]);
147 info_set_value(d_ptr->maxdepth, tokens[2]);
148 info_set_value(d_ptr->min_plev, tokens[3]);
149 info_set_value(d_ptr->mode, tokens[4]);
150 info_set_value(d_ptr->min_m_alloc_level, tokens[5]);
151 info_set_value(d_ptr->max_m_alloc_chance, tokens[6]);
152 info_set_value(d_ptr->obj_good, tokens[7]);
153 info_set_value(d_ptr->obj_great, tokens[8]);
154 info_set_value(d_ptr->pit, tokens[9], 16);
155 info_set_value(d_ptr->nest, tokens[10], 16);
156 } else if (tokens[0] == "P") {
158 if (tokens.size() < 3)
159 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
161 info_set_value(d_ptr->dy, tokens[1]);
162 info_set_value(d_ptr->dx, tokens[2]);
163 } else if (tokens[0] == "L") {
164 // L:floor_1:prob_1:floor_2:prob_2:floor_3:prob_3:tunnel_prob
165 if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 2)
166 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
168 for (size_t i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
169 auto feat_idx = i * 2 + 1;
170 auto per_idx = feat_idx + 1;
171 d_ptr->floor[i].feat = f_tag_to_index(tokens[feat_idx]);
172 if (d_ptr->floor[i].feat < 0)
173 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
175 info_set_value(d_ptr->floor[i].percent, tokens[per_idx]);
178 auto tunnel_idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
179 info_set_value(d_ptr->tunnel_percent, tokens[tunnel_idx]);
180 } else if (tokens[0] == "A") {
181 // A:wall_1:prob_1:wall_2:prob_2:wall_3:prob_3:outer_wall:inner_wall:stream_1:stream_2
182 if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 5)
183 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
185 for (int i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
186 auto feat_idx = i * 2 + 1;
187 auto prob_idx = feat_idx + 1;
188 d_ptr->fill[i].feat = f_tag_to_index(tokens[feat_idx]);
189 if (d_ptr->fill[i].feat < 0)
190 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
192 info_set_value(d_ptr->fill[i].percent, tokens[prob_idx]);
195 auto idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
196 d_ptr->outer_wall = f_tag_to_index(tokens[idx++]);
197 if (d_ptr->outer_wall < 0)
198 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
200 d_ptr->inner_wall = f_tag_to_index(tokens[idx++]);
201 if (d_ptr->inner_wall < 0)
202 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
204 d_ptr->stream1 = f_tag_to_index(tokens[idx++]);
205 if (d_ptr->stream1 < 0)
206 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
208 d_ptr->stream2 = f_tag_to_index(tokens[idx]);
209 if (d_ptr->stream2 < 0)
210 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
211 } else if (tokens[0] == "F") {
213 if (tokens.size() < 2)
214 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
216 const auto &flags = str_split(tokens[1], '|', true);
217 for (const auto &f : flags) {
221 const auto &f_tokens = str_split(f, '_');
222 if (f_tokens.size() == 3) {
223 if (f_tokens[0] == "FINAL" && f_tokens[1] == "ARTIFACT") {
224 info_set_value(d_ptr->final_artifact, f_tokens[2]);
227 if (f_tokens[0] == "FINAL" && f_tokens[1] == "OBJECT") {
228 info_set_value(d_ptr->final_object, f_tokens[2]);
231 if (f_tokens[0] == "FINAL" && f_tokens[1] == "GUARDIAN") {
232 info_set_value(d_ptr->final_guardian, f_tokens[2]);
235 if (f_tokens[0] == "MONSTER" && f_tokens[1] == "DIV") {
236 info_set_value(d_ptr->special_div, f_tokens[2]);
241 if (!grab_one_dungeon_flag(d_ptr, f))
242 return PARSE_ERROR_INVALID_FLAG;
244 } else if (tokens[0] == "M") {
246 if (tokens.size() < 2)
247 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
249 const auto &flags = str_split(tokens[1], '|', true);
250 for (const auto &f : flags) {
254 const auto &m_tokens = str_split(f, '_');
255 if (m_tokens[0] == "R" && m_tokens[1] == "CHAR") {
256 if (m_tokens[2].size() > 4)
257 return PARSE_ERROR_GENERIC;
259 strcpy(d_ptr->r_char, m_tokens[2].c_str());
263 if (!grab_one_basic_monster_flag(d_ptr, f))
264 return PARSE_ERROR_INVALID_FLAG;
266 } else if (tokens[0] == "S") {
268 if (tokens.size() < 2)
269 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
271 const auto &flags = str_split(tokens[1], '|', true);
272 for (const auto &f : flags) {
276 const auto &s_tokens = str_split(f, '_');
277 if (s_tokens.size() == 3 && s_tokens[1] == "IN") {
278 if (s_tokens[0] != "1")
279 return PARSE_ERROR_GENERIC;
280 continue; //!< r_info.txtからのコピペ対策
283 if (!grab_one_spell_monster_flag(d_ptr, f))
284 return PARSE_ERROR_INVALID_FLAG;
287 return PARSE_ERROR_UNDEFINED_DIRECTIVE;