OSDN Git Service

Tolerate info lines starting with "D::"
[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     msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
97     return false;
98 }
99
100 /*!
101  * @brief テキストトークンを走査してフラグを一つ得る(モンスターのダンジョン出現条件用2) /
102  * Grab one (spell) flag in a MonsterRaceInfo from a textual string
103  * @param d_ptr 保管先のダンジョン構造体参照ポインタ
104  * @param what 参照元の文字列ポインタ
105  * @return 見つけたらtrue
106  */
107 static bool grab_one_spell_monster_flag(dungeon_type *d_ptr, std::string_view what)
108 {
109     if (EnumClassFlagGroup<MonsterAbilityType>::grab_one_flag(d_ptr->mon_ability_flags, r_info_ability_flags, what)) {
110         return true;
111     }
112
113     msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
114     return false;
115 }
116
117 /*!
118  * @brief ダンジョン情報(DungeonsDefinition)のパース関数 /
119  * @param buf テキスト列
120  * @param head ヘッダ構造体
121  * @return エラーコード
122  */
123 errr parse_dungeons_info(std::string_view buf, angband_header *)
124 {
125     static dungeon_type *d_ptr = nullptr;
126     const auto &tokens = str_split(buf, ':', false);
127
128     if (tokens[0] == "N") {
129         // N:index:name_ja
130         if (tokens.size() < 3 || tokens[1].size() == 0) {
131             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
132         }
133
134         auto i = std::stoi(tokens[1]);
135         if (i < error_idx) {
136             return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
137         }
138         if (i >= static_cast<int>(dungeons_info.size())) {
139             dungeons_info.resize(i + 1);
140         }
141
142         error_idx = i;
143         d_ptr = &dungeons_info[i];
144         d_ptr->idx = static_cast<DUNGEON_IDX>(i);
145 #ifdef JP
146         d_ptr->name = tokens[2];
147 #endif
148     } else if (!d_ptr) {
149         return PARSE_ERROR_MISSING_RECORD_HEADER;
150     } else if (tokens[0] == "E") {
151         // E:name_en
152 #ifndef JP
153         if (tokens.size() < 2 || tokens[1].size() == 0) {
154             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
155         }
156         d_ptr->name = tokens[1];
157 #endif
158     } else if (tokens[0] == "D") {
159         // D:text_ja
160         // D:$text_en
161         if (tokens.size() < 2 || buf.length() < 3) {
162             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
163         }
164 #ifdef JP
165         if (buf[2] == '$') {
166             return PARSE_ERROR_NONE;
167         }
168         d_ptr->text.append(buf.substr(2));
169 #else
170         if (buf[2] != '$') {
171             return PARSE_ERROR_NONE;
172         }
173         if (buf.length() == 3) {
174             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
175         }
176         append_english_text(d_ptr->text, buf.substr(3));
177 #endif
178     } else if (tokens[0] == "W") {
179         // W:min_level:max_level:(1):mode:(2):(3):(4):(5):prob_pit:prob_nest
180         // (1)minimum player level (unused)
181         // (2)minimum level of allocating monster
182         // (3)maximum probability of level boost of allocation monster
183         // (4)maximum probability of dropping good objects
184         // (5)maximum probability of dropping great objects
185         if (tokens.size() < 11) {
186             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
187         }
188
189         info_set_value(d_ptr->mindepth, tokens[1]);
190         info_set_value(d_ptr->maxdepth, tokens[2]);
191         info_set_value(d_ptr->min_plev, tokens[3]);
192         info_set_value(d_ptr->mode, tokens[4]);
193         info_set_value(d_ptr->min_m_alloc_level, tokens[5]);
194         info_set_value(d_ptr->max_m_alloc_chance, tokens[6]);
195         info_set_value(d_ptr->obj_good, tokens[7]);
196         info_set_value(d_ptr->obj_great, tokens[8]);
197         info_set_value(d_ptr->pit, tokens[9], 16);
198         info_set_value(d_ptr->nest, tokens[10], 16);
199     } else if (tokens[0] == "P") {
200         // P:wild_y:wild_x
201         if (tokens.size() < 3) {
202             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
203         }
204
205         info_set_value(d_ptr->dy, tokens[1]);
206         info_set_value(d_ptr->dx, tokens[2]);
207     } else if (tokens[0] == "L") {
208         // L:floor_1:prob_1:floor_2:prob_2:floor_3:prob_3:tunnel_prob
209         if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 2) {
210             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
211         }
212
213         for (size_t i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
214             auto feat_idx = i * 2 + 1;
215             auto per_idx = feat_idx + 1;
216             d_ptr->floor[i].feat = f_tag_to_index(tokens[feat_idx]);
217             if (d_ptr->floor[i].feat < 0) {
218                 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
219             }
220
221             info_set_value(d_ptr->floor[i].percent, tokens[per_idx]);
222         }
223
224         auto tunnel_idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
225         info_set_value(d_ptr->tunnel_percent, tokens[tunnel_idx]);
226     } else if (tokens[0] == "A") {
227         // A:wall_1:prob_1:wall_2:prob_2:wall_3:prob_3:outer_wall:inner_wall:stream_1:stream_2
228         if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 5) {
229             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
230         }
231
232         for (int i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
233             auto feat_idx = i * 2 + 1;
234             auto prob_idx = feat_idx + 1;
235             d_ptr->fill[i].feat = f_tag_to_index(tokens[feat_idx]);
236             if (d_ptr->fill[i].feat < 0) {
237                 return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
238             }
239
240             info_set_value(d_ptr->fill[i].percent, tokens[prob_idx]);
241         }
242
243         auto idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
244         d_ptr->outer_wall = f_tag_to_index(tokens[idx++]);
245         if (d_ptr->outer_wall < 0) {
246             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
247         }
248
249         d_ptr->inner_wall = f_tag_to_index(tokens[idx++]);
250         if (d_ptr->inner_wall < 0) {
251             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
252         }
253
254         d_ptr->stream1 = f_tag_to_index(tokens[idx++]);
255         if (d_ptr->stream1 < 0) {
256             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
257         }
258
259         d_ptr->stream2 = f_tag_to_index(tokens[idx]);
260         if (d_ptr->stream2 < 0) {
261             return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
262         }
263     } else if (tokens[0] == "F") {
264         // F:flags
265         if (tokens.size() < 2) {
266             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
267         }
268
269         const auto &flags = str_split(tokens[1], '|', true);
270         for (const auto &f : flags) {
271             if (f.size() == 0) {
272                 continue;
273             }
274
275             const auto &f_tokens = str_split(f, '_');
276             if (f_tokens.size() == 3) {
277                 if (f_tokens[0] == "FINAL" && f_tokens[1] == "ARTIFACT") {
278                     info_set_value(d_ptr->final_artifact, f_tokens[2]);
279                     continue;
280                 }
281                 if (f_tokens[0] == "FINAL" && f_tokens[1] == "OBJECT") {
282                     info_set_value(d_ptr->final_object, f_tokens[2]);
283                     continue;
284                 }
285                 if (f_tokens[0] == "FINAL" && f_tokens[1] == "GUARDIAN") {
286                     info_set_value(d_ptr->final_guardian, f_tokens[2]);
287                     continue;
288                 }
289                 if (f_tokens[0] == "MONSTER" && f_tokens[1] == "DIV") {
290                     info_set_value(d_ptr->special_div, f_tokens[2]);
291                     continue;
292                 }
293             }
294
295             if (!grab_one_dungeon_flag(d_ptr, f)) {
296                 return PARSE_ERROR_INVALID_FLAG;
297             }
298         }
299     } else if (tokens[0] == "M") {
300         // M:monsterflags
301         if (tokens.size() < 2) {
302             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
303         }
304
305         const auto &flags = str_split(tokens[1], '|', true);
306         for (const auto &f : flags) {
307             if (f.size() == 0) {
308                 continue;
309             }
310
311             const auto &m_tokens = str_split(f, '_');
312             if (m_tokens[0] == "R" && m_tokens[1] == "CHAR") {
313                 d_ptr->r_chars.insert(d_ptr->r_chars.end(), m_tokens[2].begin(), m_tokens[2].end());
314                 continue;
315             }
316
317             if (!grab_one_basic_monster_flag(d_ptr, f)) {
318                 return PARSE_ERROR_INVALID_FLAG;
319             }
320         }
321     } else if (tokens[0] == "S") {
322         // S: flags
323         if (tokens.size() < 2) {
324             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
325         }
326
327         const auto &flags = str_split(tokens[1], '|', true);
328         for (const auto &f : flags) {
329             if (f.size() == 0) {
330                 continue;
331             }
332
333             const auto &s_tokens = str_split(f, '_');
334             if (s_tokens.size() == 3 && s_tokens[1] == "IN") {
335                 if (s_tokens[0] != "1") {
336                     return PARSE_ERROR_GENERIC;
337                 }
338                 continue; //!< MonsterRaceDefinitions.txtからのコピペ対策
339             }
340
341             if (!grab_one_spell_monster_flag(d_ptr, f)) {
342                 return PARSE_ERROR_INVALID_FLAG;
343             }
344         }
345     } else {
346         return PARSE_ERROR_UNDEFINED_DIRECTIVE;
347     }
348
349     return 0;
350 }