OSDN Git Service

887929c369f9203543aa7a17594861c02bd6937c
[hengbandforosx/hengbandosx.git] / src / info-reader / dungeon-reader.cpp
1 #include "info-reader/dungeon-reader.h"
2 #include "grid/feature.h"
3 #include "info-reader/dungeon-info-tokens-table.h"
4 #include "info-reader/feature-reader.h"
5 #include "info-reader/info-reader-util.h"
6 #include "info-reader/parse-error-types.h"
7 #include "info-reader/race-info-tokens-table.h"
8 #include "io/tokenizer.h"
9 #include "main/angband-headers.h"
10 #include "system/dungeon-info.h"
11 #include "util/string-processor.h"
12 #include "view/display-messages.h"
13
14 /*!
15  * @brief テキストトークンを走査してフラグを一つ得る(ダンジョン用) /
16  * Grab one flag for a dungeon type from a textual string
17  * @param d_ptr 保管先のダンジョン構造体参照ポインタ
18  * @param what 参照元の文字列ポインタ
19  * @return 見つけたらtrue
20  */
21 static bool grab_one_dungeon_flag(dungeon_type *d_ptr, std::string_view what)
22 {
23     if (EnumClassFlagGroup<DungeonFeatureType>::grab_one_flag(d_ptr->flags, dungeon_flags, what)) {
24         return true;
25     }
26
27     msg_format(_("未知のダンジョン・フラグ '%s'。", "Unknown dungeon type flag '%s'."), what.data());
28     return false;
29 }
30
31 /*!
32  * @brief テキストトークンを走査してフラグを一つ得る(モンスターのダンジョン出現条件用1) /
33  * Grab one (basic) flag in a MonsterRaceInfo from a textual string
34  * @param d_ptr 保管先のダンジョン構造体参照ポインタ
35  * @param what 参照元の文字列ポインタ
36  * @return 見つけたらtrue
37  */
38 static bool grab_one_basic_monster_flag(dungeon_type *d_ptr, std::string_view what)
39 {
40     if (info_grab_one_flag(d_ptr->mflags1, r_info_flags1, what)) {
41         return true;
42     }
43
44     if (info_grab_one_flag(d_ptr->mflags2, r_info_flags2, what)) {
45         return true;
46     }
47
48     if (info_grab_one_flag(d_ptr->mflags7, r_info_flags7, what)) {
49         return true;
50     }
51
52     if (info_grab_one_flag(d_ptr->mflags8, r_info_flags8, what)) {
53         return true;
54     }
55
56     if (EnumClassFlagGroup<MonsterResistanceType>::grab_one_flag(d_ptr->mon_resistance_flags, r_info_flagsr, what)) {
57         return true;
58     }
59
60     if (EnumClassFlagGroup<MonsterBehaviorType>::grab_one_flag(d_ptr->mon_behavior_flags, r_info_behavior_flags, what)) {
61         return true;
62     }
63
64     if (EnumClassFlagGroup<MonsterVisualType>::grab_one_flag(d_ptr->mon_visual_flags, r_info_visual_flags, what)) {
65         return true;
66     }
67
68     if (EnumClassFlagGroup<MonsterKindType>::grab_one_flag(d_ptr->mon_kind_flags, r_info_kind_flags, what)) {
69         return true;
70     }
71
72     if (EnumClassFlagGroup<MonsterDropType>::grab_one_flag(d_ptr->mon_drop_flags, r_info_drop_flags, what)) {
73         return true;
74     }
75
76     if (EnumClassFlagGroup<MonsterWildernessType>::grab_one_flag(d_ptr->mon_wilderness_flags, r_info_wilderness_flags, what)) {
77         return true;
78     }
79
80     if (EnumClassFlagGroup<MonsterFeatureType>::grab_one_flag(d_ptr->mon_feature_flags, r_info_feature_flags, what)) {
81         return true;
82     }
83
84     if (EnumClassFlagGroup<MonsterPopulationType>::grab_one_flag(d_ptr->mon_population_flags, r_info_population_flags, what)) {
85         return true;
86     }
87
88     if (EnumClassFlagGroup<MonsterSpeakType>::grab_one_flag(d_ptr->mon_speak_flags, r_info_speak_flags, what)) {
89         return true;
90     }
91
92     if (EnumClassFlagGroup<MonsterBrightnessType>::grab_one_flag(d_ptr->mon_brightness_flags, r_info_brightness_flags, what)) {
93         return true;
94     }
95
96     if (EnumClassFlagGroup<MonsterSpecialType>::grab_one_flag(d_ptr->mon_special_flags, r_info_special_flags, what)) {
97         return true;
98     }
99     if (EnumClassFlagGroup<MonsterMiscType>::grab_one_flag(d_ptr->mon_misc_flags, r_info_misc_flags, what)) {
100         return true;
101     }
102
103     msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
104     return false;
105 }
106
107 /*!
108  * @brief テキストトークンを走査してフラグを一つ得る(モンスターのダンジョン出現条件用2) /
109  * Grab one (spell) flag in a MonsterRaceInfo from a textual string
110  * @param d_ptr 保管先のダンジョン構造体参照ポインタ
111  * @param what 参照元の文字列ポインタ
112  * @return 見つけたらtrue
113  */
114 static bool grab_one_spell_monster_flag(dungeon_type *d_ptr, std::string_view what)
115 {
116     if (EnumClassFlagGroup<MonsterAbilityType>::grab_one_flag(d_ptr->mon_ability_flags, r_info_ability_flags, what)) {
117         return true;
118     }
119
120     msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
121     return false;
122 }
123
124 /*!
125  * @brief ダンジョン情報(DungeonsDefinition)のパース関数 /
126  * @param buf テキスト列
127  * @param head ヘッダ構造体
128  * @return エラーコード
129  */
130 errr parse_dungeons_info(std::string_view buf, angband_header *)
131 {
132     static dungeon_type *d_ptr = nullptr;
133     const auto &tokens = str_split(buf, ':', false);
134
135     if (tokens[0] == "N") {
136         // N:index:name_ja
137         if (tokens.size() < 3 || tokens[1].size() == 0) {
138             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
139         }
140
141         auto i = std::stoi(tokens[1]);
142         if (i < error_idx) {
143             return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
144         }
145         if (i >= static_cast<int>(dungeons_info.size())) {
146             dungeons_info.resize(i + 1);
147         }
148
149         error_idx = i;
150         d_ptr = &dungeons_info[i];
151         d_ptr->idx = static_cast<DUNGEON_IDX>(i);
152 #ifdef JP
153         d_ptr->name = tokens[2];
154 #endif
155     } else if (!d_ptr) {
156         return PARSE_ERROR_MISSING_RECORD_HEADER;
157     } else if (tokens[0] == "E") {
158         // E:name_en
159 #ifndef JP
160         if (tokens.size() < 2 || tokens[1].size() == 0) {
161             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
162         }
163         d_ptr->name = tokens[1];
164 #endif
165     } else if (tokens[0] == "D") {
166         // D:text_ja
167         // D:$text_en
168         if (tokens.size() < 2 || buf.length() < 3) {
169             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
170         }
171 #ifdef JP
172         if (buf[2] == '$') {
173             return PARSE_ERROR_NONE;
174         }
175         d_ptr->text.append(buf.substr(2));
176 #else
177         if (buf[2] != '$') {
178             return PARSE_ERROR_NONE;
179         }
180         if (buf.length() == 3) {
181             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
182         }
183         append_english_text(d_ptr->text, buf.substr(3));
184 #endif
185     } else if (tokens[0] == "W") {
186         // W:min_level:max_level:(1):mode:(2):(3):(4):(5):prob_pit:prob_nest
187         // (1)minimum player level (unused)
188         // (2)minimum level of allocating monster
189         // (3)maximum probability of level boost of allocation monster
190         // (4)maximum probability of dropping good objects
191         // (5)maximum probability of dropping great objects
192         if (tokens.size() < 11) {
193             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
194         }
195
196         info_set_value(d_ptr->mindepth, tokens[1]);
197         info_set_value(d_ptr->maxdepth, tokens[2]);
198         info_set_value(d_ptr->min_plev, tokens[3]);
199         info_set_value(d_ptr->mode, tokens[4]);
200         info_set_value(d_ptr->min_m_alloc_level, tokens[5]);
201         info_set_value(d_ptr->max_m_alloc_chance, tokens[6]);
202         info_set_value(d_ptr->obj_good, tokens[7]);
203         info_set_value(d_ptr->obj_great, tokens[8]);
204         info_set_value(d_ptr->pit, tokens[9], 16);
205         info_set_value(d_ptr->nest, tokens[10], 16);
206     } else if (tokens[0] == "P") {
207         // P:wild_y:wild_x
208         if (tokens.size() < 3) {
209             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
210         }
211
212         info_set_value(d_ptr->dy, tokens[1]);
213         info_set_value(d_ptr->dx, tokens[2]);
214     } else if (tokens[0] == "L") {
215         // L:floor_1:prob_1:floor_2:prob_2:floor_3:prob_3:tunnel_prob
216         if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 2) {
217             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
218         }
219
220         for (size_t i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
221             auto feat_idx = i * 2 + 1;
222             auto per_idx = feat_idx + 1;
223             d_ptr->floor[i].feat = f_tag_to_index(tokens[feat_idx]);
224             if (d_ptr->floor[i].feat < 0) {
225                 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
226             }
227
228             info_set_value(d_ptr->floor[i].percent, tokens[per_idx]);
229         }
230
231         auto tunnel_idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
232         info_set_value(d_ptr->tunnel_percent, tokens[tunnel_idx]);
233     } else if (tokens[0] == "A") {
234         // A:wall_1:prob_1:wall_2:prob_2:wall_3:prob_3:outer_wall:inner_wall:stream_1:stream_2
235         if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 5) {
236             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
237         }
238
239         for (int i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
240             auto feat_idx = i * 2 + 1;
241             auto prob_idx = feat_idx + 1;
242             d_ptr->fill[i].feat = f_tag_to_index(tokens[feat_idx]);
243             if (d_ptr->fill[i].feat < 0) {
244                 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
245             }
246
247             info_set_value(d_ptr->fill[i].percent, tokens[prob_idx]);
248         }
249
250         auto idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
251         d_ptr->outer_wall = f_tag_to_index(tokens[idx++]);
252         if (d_ptr->outer_wall < 0) {
253             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
254         }
255
256         d_ptr->inner_wall = f_tag_to_index(tokens[idx++]);
257         if (d_ptr->inner_wall < 0) {
258             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
259         }
260
261         d_ptr->stream1 = f_tag_to_index(tokens[idx++]);
262         if (d_ptr->stream1 < 0) {
263             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
264         }
265
266         d_ptr->stream2 = f_tag_to_index(tokens[idx]);
267         if (d_ptr->stream2 < 0) {
268             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
269         }
270     } else if (tokens[0] == "F") {
271         // F:flags
272         if (tokens.size() < 2) {
273             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
274         }
275
276         const auto &flags = str_split(tokens[1], '|', true);
277         for (const auto &f : flags) {
278             if (f.size() == 0) {
279                 continue;
280             }
281
282             const auto &f_tokens = str_split(f, '_');
283             if (f_tokens.size() == 3) {
284                 if (f_tokens[0] == "FINAL" && f_tokens[1] == "ARTIFACT") {
285                     info_set_value(d_ptr->final_artifact, f_tokens[2]);
286                     continue;
287                 }
288                 if (f_tokens[0] == "FINAL" && f_tokens[1] == "OBJECT") {
289                     info_set_value(d_ptr->final_object, f_tokens[2]);
290                     continue;
291                 }
292                 if (f_tokens[0] == "FINAL" && f_tokens[1] == "GUARDIAN") {
293                     info_set_value(d_ptr->final_guardian, f_tokens[2]);
294                     continue;
295                 }
296                 if (f_tokens[0] == "MONSTER" && f_tokens[1] == "DIV") {
297                     info_set_value(d_ptr->special_div, f_tokens[2]);
298                     continue;
299                 }
300             }
301
302             if (!grab_one_dungeon_flag(d_ptr, f)) {
303                 return PARSE_ERROR_INVALID_FLAG;
304             }
305         }
306     } else if (tokens[0] == "M") {
307         // M:monsterflags
308         if (tokens[1] == "X") {
309             if (tokens.size() < 3) {
310                 return PARSE_ERROR_TOO_FEW_ARGUMENTS;
311             }
312             uint32_t sex;
313             if (!info_grab_one_const(sex, r_info_sex, tokens[2])) {
314                 return PARSE_ERROR_INVALID_FLAG;
315             }
316             d_ptr->mon_sex = static_cast<MonsterSex>(sex);
317             return 0;
318         }
319         if (tokens.size() < 2) {
320             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
321         }
322
323         const auto &flags = str_split(tokens[1], '|', true);
324         for (const auto &f : flags) {
325             if (f.size() == 0) {
326                 continue;
327             }
328
329             const auto &m_tokens = str_split(f, '_');
330             if (m_tokens[0] == "R" && m_tokens[1] == "CHAR") {
331                 d_ptr->r_chars.insert(d_ptr->r_chars.end(), m_tokens[2].begin(), m_tokens[2].end());
332                 continue;
333             }
334
335             if (!grab_one_basic_monster_flag(d_ptr, f)) {
336                 return PARSE_ERROR_INVALID_FLAG;
337             }
338         }
339     } else if (tokens[0] == "S") {
340         // S: flags
341         if (tokens.size() < 2) {
342             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
343         }
344
345         const auto &flags = str_split(tokens[1], '|', true);
346         for (const auto &f : flags) {
347             if (f.size() == 0) {
348                 continue;
349             }
350
351             const auto &s_tokens = str_split(f, '_');
352             if (s_tokens.size() == 3 && s_tokens[1] == "IN") {
353                 if (s_tokens[0] != "1") {
354                     return PARSE_ERROR_GENERIC;
355                 }
356                 continue; //!< MonsterRaceDefinitions.txtからのコピペ対策
357             }
358
359             if (!grab_one_spell_monster_flag(d_ptr, f)) {
360                 return PARSE_ERROR_INVALID_FLAG;
361             }
362         }
363     } else {
364         return PARSE_ERROR_UNDEFINED_DIRECTIVE;
365     }
366
367     return 0;
368 }