OSDN Git Service

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