OSDN Git Service

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