OSDN Git Service

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