OSDN Git Service

Merge pull request #3848 from Slimebreath6078/feature/Add_Damage_Cap
[hengbandforosx/hengbandosx.git] / src / info-reader / race-reader.cpp
1 #include "info-reader/race-reader.h"
2 #include "info-reader/info-reader-util.h"
3 #include "info-reader/parse-error-types.h"
4 #include "info-reader/race-info-tokens-table.h"
5 #include "main/angband-headers.h"
6 #include "monster-race/monster-race.h"
7 #include "player-ability/player-ability-types.h"
8 #include "system/monster-race-info.h"
9 #include "term/gameterm.h"
10 #include "util/enum-converter.h"
11 #include "util/string-processor.h"
12 #include "view/display-messages.h"
13
14 /*!
15  * @brief テキストトークンを走査してフラグを一つ得る(モンスター用1) /
16  * Grab one (basic) flag in a MonsterRaceInfo from a textual string
17  * @param r_ptr 保管先のモンスター種族構造体参照ポインタ
18  * @param what 参照元の文字列ポインタ
19  * @return 見つけたらtrue
20  */
21 static bool grab_one_basic_flag(MonsterRaceInfo *r_ptr, std::string_view what)
22 {
23     if (info_grab_one_flag(r_ptr->flags1, r_info_flags1, what)) {
24         return true;
25     }
26
27     if (info_grab_one_flag(r_ptr->flags2, r_info_flags2, what)) {
28         return true;
29     }
30
31     if (info_grab_one_flag(r_ptr->flags7, r_info_flags7, what)) {
32         return true;
33     }
34
35     if (info_grab_one_flag(r_ptr->flags8, r_info_flags8, what)) {
36         return true;
37     }
38
39     if (EnumClassFlagGroup<MonsterResistanceType>::grab_one_flag(r_ptr->resistance_flags, r_info_flagsr, what)) {
40         return true;
41     }
42
43     if (EnumClassFlagGroup<MonsterAuraType>::grab_one_flag(r_ptr->aura_flags, r_info_aura_flags, what)) {
44         return true;
45     }
46
47     if (EnumClassFlagGroup<MonsterBehaviorType>::grab_one_flag(r_ptr->behavior_flags, r_info_behavior_flags, what)) {
48         return true;
49     }
50
51     if (EnumClassFlagGroup<MonsterVisualType>::grab_one_flag(r_ptr->visual_flags, r_info_visual_flags, what)) {
52         return true;
53     }
54
55     if (EnumClassFlagGroup<MonsterKindType>::grab_one_flag(r_ptr->kind_flags, r_info_kind_flags, what)) {
56         return true;
57     }
58
59     if (EnumClassFlagGroup<MonsterDropType>::grab_one_flag(r_ptr->drop_flags, r_info_drop_flags, what)) {
60         return true;
61     }
62
63     if (EnumClassFlagGroup<MonsterWildernessType>::grab_one_flag(r_ptr->wilderness_flags, r_info_wilderness_flags, what)) {
64         return true;
65     }
66
67     if (EnumClassFlagGroup<MonsterFeatureType>::grab_one_flag(r_ptr->feature_flags, r_info_feature_flags, what)) {
68         return true;
69     }
70
71     if (EnumClassFlagGroup<MonsterPopulationType>::grab_one_flag(r_ptr->population_flags, r_info_population_flags, what)) {
72         return true;
73     }
74
75     if (EnumClassFlagGroup<MonsterSpeakType>::grab_one_flag(r_ptr->speak_flags, r_info_speak_flags, what)) {
76         return true;
77     }
78
79     if (EnumClassFlagGroup<MonsterBrightnessType>::grab_one_flag(r_ptr->brightness_flags, r_info_brightness_flags, what)) {
80         return true;
81     }
82
83     if (EnumClassFlagGroup<MonsterSpecialType>::grab_one_flag(r_ptr->special_flags, r_info_special_flags, what)) {
84         return true;
85     }
86
87     msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
88     return false;
89 }
90
91 /*!
92  * @brief テキストトークンを走査してフラグを一つ得る(モンスター用2) /
93  * Grab one (spell) flag in a MonsterRaceInfo from a textual string
94  * @param r_ptr 保管先のモンスター種族構造体参照ポインタ
95  * @param what 参照元の文字列ポインタ
96  * @return 見つけたらtrue
97  */
98 static bool grab_one_spell_flag(MonsterRaceInfo *r_ptr, std::string_view what)
99 {
100     if (EnumClassFlagGroup<MonsterAbilityType>::grab_one_flag(r_ptr->ability_flags, r_info_ability_flags, what)) {
101         return true;
102     }
103
104     msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
105     return false;
106 }
107
108 /*!
109  * @brief モンスター種族情報(MonsterRaceDefinition)のパース関数
110  * @param buf テキスト列
111  * @param head ヘッダ構造体
112  * @return エラーコード
113  */
114 errr parse_monraces_info(std::string_view buf, angband_header *)
115 {
116     static MonsterRaceInfo *r_ptr = nullptr;
117     const auto &tokens = str_split(buf, ':', true, 10);
118
119     if (tokens[0] == "N") {
120         // N:index:name_ja
121         if (tokens.size() < 3 || tokens[1].size() == 0) {
122             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
123         }
124
125         auto i = std::stoi(tokens[1]);
126         if (i < error_idx) {
127             return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
128         }
129
130         error_idx = i;
131         r_ptr = &(monraces_info.emplace_hint(monraces_info.end(), i2enum<MonsterRaceId>(i), MonsterRaceInfo{})->second);
132         r_ptr->idx = i2enum<MonsterRaceId>(i);
133 #ifdef JP
134         r_ptr->name = tokens[2];
135 #endif
136     } else if (!r_ptr) {
137         return PARSE_ERROR_MISSING_RECORD_HEADER;
138     } else if (tokens[0] == "E") {
139         // E:name_en
140         if (tokens.size() < 2 || tokens[1].size() == 0) {
141             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
142         }
143 #ifdef JP
144         r_ptr->E_name = tokens[1];
145 #else
146         r_ptr->name = tokens[1];
147 #endif
148     } else if (tokens[0] == "D") {
149         // D:text_ja
150         // D:$text_en
151         if (tokens.size() < 2 || buf.length() < 3) {
152             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
153         }
154 #ifdef JP
155         if (buf[2] == '$') {
156             return PARSE_ERROR_NONE;
157         }
158         r_ptr->text.append(buf.substr(2));
159 #else
160         if (buf[2] != '$') {
161             return PARSE_ERROR_NONE;
162         }
163         if (buf.length() == 3) {
164             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
165         }
166         append_english_text(r_ptr->text, buf.substr(3));
167 #endif
168     } else if (tokens[0] == "G") {
169         // G:color:symbol
170         if (tokens.size() < 3 || tokens[1].size() == 0 || tokens[2].size() == 0) {
171             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
172         }
173
174         auto a = color_char_to_attr(tokens[2][0]);
175         if (a > 127) {
176             return PARSE_ERROR_GENERIC;
177         }
178
179         r_ptr->d_attr = a;
180         r_ptr->d_char = tokens[1][0];
181     } else if (tokens[0] == "I") {
182         // G:speed:hp_dice:affect_range:ac:sleep_degree
183         if (tokens.size() < 6) {
184             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
185         }
186
187         const auto &dice = str_split(tokens[2], 'd', false, 2);
188         if (dice.size() < 2) {
189             return PARSE_ERROR_GENERIC;
190         }
191
192         info_set_value(r_ptr->speed, tokens[1]);
193         info_set_value(r_ptr->hdice, dice[0]);
194         info_set_value(r_ptr->hside, dice.size() == 1 ? "1" : dice[1]);
195         info_set_value(r_ptr->aaf, tokens[3]);
196         info_set_value(r_ptr->ac, tokens[4]);
197         info_set_value(r_ptr->sleep, tokens[5]);
198     } else if (tokens[0] == "W") {
199         // W:level:ratity:exp:next_exp:next_id
200         if ((tokens.size() < 4) || (tokens.size() == 5)) {
201             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
202         }
203
204         info_set_value(r_ptr->level, tokens[1]);
205         info_set_value(r_ptr->rarity, tokens[2]);
206         info_set_value(r_ptr->mexp, tokens[3]);
207
208         if (tokens.size() < 5) {
209             return PARSE_ERROR_NONE;
210         }
211
212         info_set_value(r_ptr->next_exp, tokens[4]);
213         info_set_value(r_ptr->next_r_idx, tokens[5]);
214     } else if (tokens[0] == "R") {
215         // R:reinforcer_idx:number_dice
216         if (tokens.size() < 3) {
217             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
218         }
219         if (tokens[1].size() == 0 || tokens[2].size() == 0) {
220             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
221         }
222
223         const auto &dice = str_split(tokens[2], 'd', false, 2);
224         MonsterRaceId r_idx;
225         DICE_NUMBER dd;
226         DICE_SID ds;
227         info_set_value(r_idx, tokens[1]);
228         info_set_value(dd, dice[0]);
229         info_set_value(ds, dice[1]);
230         r_ptr->reinforces.emplace_back(r_idx, dd, ds);
231     } else if (tokens[0] == "B") {
232         // B:blow_type:blow_effect:dice
233         size_t i = 0;
234         for (; i < 4; i++) {
235             if (r_ptr->blows[i].method == RaceBlowMethodType::NONE) {
236                 break;
237             }
238         }
239
240         if (i >= 4) {
241             return PARSE_ERROR_GENERIC;
242         }
243
244         if (tokens.size() < 3) {
245             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
246         }
247         if (tokens[1].size() == 0 || tokens[2].size() == 0) {
248             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
249         }
250
251         auto rbm = r_info_blow_method.find(tokens[1]);
252         if (rbm == r_info_blow_method.end()) {
253             return PARSE_ERROR_INVALID_FLAG;
254         }
255
256         auto rbe = r_info_blow_effect.find(tokens[2]);
257         if (rbe == r_info_blow_effect.end()) {
258             return PARSE_ERROR_INVALID_FLAG;
259         }
260
261         r_ptr->blows[i].method = rbm->second;
262         r_ptr->blows[i].effect = rbe->second;
263
264         if (tokens.size() < 4) {
265             return PARSE_ERROR_NONE;
266         }
267
268         const auto &dice = str_split(tokens[3], 'd', false, 2);
269         info_set_value(r_ptr->blows[i].d_dice, dice[0]);
270         info_set_value(r_ptr->blows[i].d_side, dice[1]);
271     } else if (tokens[0] == "F") {
272         // F:flags
273         if (tokens.size() < 2 || tokens[1].size() == 0) {
274             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
275         }
276
277         const auto &flags = str_split(tokens[1], '|', true, 10);
278         for (const auto &f : flags) {
279
280             const auto &s_tokens = str_split(f, '_', false, 2);
281             if (s_tokens.size() == 2 && s_tokens[0] == "PERHP") {
282                 info_set_value(r_ptr->cur_hp_per, s_tokens[1]);
283                 continue;
284             }
285
286             if (f.size() == 0) {
287                 continue;
288             }
289
290             if (!grab_one_basic_flag(r_ptr, f)) {
291                 return PARSE_ERROR_INVALID_FLAG;
292             }
293         }
294     } else if (tokens[0] == "S") {
295         // S:flags
296         if (tokens.size() < 2 || tokens[1].size() == 0) {
297             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
298         }
299
300         const auto &flags = str_split(tokens[1], '|', true, 10);
301         for (const auto &f : flags) {
302             if (f.size() == 0) {
303                 continue;
304             }
305
306             const auto &s_tokens = str_split(f, '_', false, 3);
307             if (s_tokens.size() == 3 && s_tokens[1] == "IN") {
308                 if (s_tokens[0] != "1") {
309                     return PARSE_ERROR_GENERIC;
310                 }
311                 RARITY i;
312                 info_set_value(i, s_tokens[2]);
313                 r_ptr->freq_spell = 100 / i;
314                 continue;
315             }
316
317             if (!grab_one_spell_flag(r_ptr, f)) {
318                 return PARSE_ERROR_INVALID_FLAG;
319             }
320         }
321
322     } else if (tokens[0] == "A") {
323         // A:artifact_idx:chance
324         if (tokens.size() < 3) {
325             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
326         }
327
328         FixedArtifactId a_idx;
329         PERCENTAGE chance;
330         info_set_value(a_idx, tokens[1]);
331         info_set_value(chance, tokens[2]);
332         r_ptr->drop_artifacts.emplace_back(a_idx, chance);
333     } else if (tokens[0] == "X") {
334         if (tokens.size() < 2) {
335             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
336         }
337         uint32_t sex;
338         if (!info_grab_one_const(sex, r_info_sex, tokens[1])) {
339             return PARSE_ERROR_INVALID_FLAG;
340         }
341         r_ptr->sex = static_cast<MonsterSex>(sex);
342
343     } else if (tokens[0] == "V") {
344         // V:arena_odds
345         if (tokens.size() < 2) {
346             return PARSE_ERROR_TOO_FEW_ARGUMENTS;
347         }
348
349         info_set_value(r_ptr->arena_ratio, tokens[1]);
350     } else {
351         return PARSE_ERROR_UNDEFINED_DIRECTIVE;
352     }
353
354     return PARSE_ERROR_NONE;
355 }