OSDN Git Service

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