OSDN Git Service

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