OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / flavor / flavor-util.cpp
1 #include "flavor/flavor-util.h"
2 #include "flavor/flag-inscriptions-table.h"
3 #include "object-enchant/tr-flags.h"
4 #include "object-enchant/tr-types.h"
5 #include "object/object-flags.h"
6 #include "object/tval-types.h"
7 #include "sv-definition/sv-food-types.h"
8 #include "system/artifact-type-definition.h"
9 #include "system/item-entity.h"
10 #include "util/string-processor.h"
11 #include <sstream>
12
13 static bool has_lite_flag(const TrFlags &flags)
14 {
15     return flags.has(TR_LITE_1) || flags.has(TR_LITE_2) || flags.has(TR_LITE_3);
16 }
17
18 static bool has_dark_flag(const TrFlags &flags)
19 {
20     return flags.has(TR_LITE_M1) || flags.has(TR_LITE_M2) || flags.has(TR_LITE_M3);
21 }
22
23 /*!
24  * @brief get_inscriptionのサブセットとしてアイテムの特性フラグを表す文字列を返す
25  * @param fi_vec 参照する特性表示記号テーブル
26  * @param flags 対応するアイテムの特性フラグ
27  * @param is_kanji trueならば漢字記述/falseならば英語記述
28  * @return アイテムの特性フラグを表す文字列
29  */
30 static std::string inscribe_flags_aux(const std::vector<flag_insc_table> &fi_vec, const TrFlags &flags, [[maybe_unused]] bool is_kanji)
31 {
32     std::stringstream ss;
33
34     for (const auto &fi : fi_vec) {
35         if (flags.has(fi.flag) && (!fi.except_flag.has_value() || flags.has_not(fi.except_flag.value()))) {
36             const auto flag_str = _(is_kanji ? fi.japanese : fi.english, fi.english);
37             ss << flag_str;
38         }
39     }
40
41     return ss.str();
42 }
43
44 /*!
45  * @brief オブジェクトの特性表示記号テーブル1つに従いオブジェクトの特性フラグ配列に1つでも該当の特性があるかを返す / Special variation of has_flag for
46  * auto-inscription
47  * @param fi_vec 参照する特性表示記号テーブル
48  * @param flags 対応するオブジェクトのフラグ文字列
49  * @return 1つでも該当の特性があったらTRUEを返す。
50  */
51 static bool has_flag_of(const std::vector<flag_insc_table> &fi_vec, const TrFlags &flags)
52 {
53     for (const auto &fi : fi_vec) {
54         if (flags.has(fi.flag) && (!fi.except_flag.has_value() || flags.has_not(fi.except_flag.value()))) {
55             return true;
56         }
57     }
58
59     return false;
60 }
61
62 /*!
63  * @brief アイテムの特性短縮表記をまとめて提示する。
64  * @param item 特性短縮表記を得たいアイテムの参照
65  * @param is_kanji trueならば漢字表記 / falseなら英語表記
66  * @param all falseならばベースアイテム上で明らかなフラグは省略する
67  * @return アイテムの特性短縮表記
68  */
69 std::string get_ability_abbreviation(const ItemEntity &item, bool is_kanji, bool all)
70 {
71     auto flags = object_flags(&item);
72     if (!all) {
73         const auto &baseitem = item.get_baseitem();
74         flags.reset(baseitem.flags);
75
76         if (item.is_fixed_artifact()) {
77             const auto &artifact = item.get_fixed_artifact();
78             flags.reset(artifact.flags);
79         }
80
81         if (item.is_ego()) {
82             const auto &ego = item.get_ego();
83             flags.reset(ego.flags);
84         }
85     }
86
87     if (has_dark_flag(flags)) {
88         if (flags.has(TR_LITE_1)) {
89             flags.reset(TR_LITE_1);
90         }
91
92         if (flags.has(TR_LITE_2)) {
93             flags.reset(TR_LITE_2);
94         }
95
96         if (flags.has(TR_LITE_3)) {
97             flags.reset(TR_LITE_3);
98         }
99     } else if (has_lite_flag(flags)) {
100         flags.set(TR_LITE_1);
101         if (flags.has(TR_LITE_2)) {
102             flags.reset(TR_LITE_2);
103         }
104
105         if (flags.has(TR_LITE_3)) {
106             flags.reset(TR_LITE_3);
107         }
108     }
109
110     std::stringstream ss;
111     auto prev_tellp = ss.tellp();
112
113     if (has_flag_of(flag_insc_plus, flags) && is_kanji) {
114         ss << '+';
115     }
116
117     ss << inscribe_flags_aux(flag_insc_plus, flags, is_kanji);
118
119     if (has_flag_of(flag_insc_immune, flags)) {
120         if (!is_kanji && (ss.tellp() != prev_tellp)) {
121             ss << ';';
122             prev_tellp = ss.tellp();
123         }
124
125         ss << '*';
126     }
127
128     ss << inscribe_flags_aux(flag_insc_immune, flags, is_kanji);
129
130     if (has_flag_of(flag_insc_vuln, flags)) {
131         if (!is_kanji && (ss.tellp() != prev_tellp)) {
132             ss << ';';
133             prev_tellp = ss.tellp();
134         }
135
136         ss << 'v';
137     }
138
139     ss << inscribe_flags_aux(flag_insc_vuln, flags, is_kanji);
140
141     if (has_flag_of(flag_insc_resistance, flags)) {
142         if (is_kanji) {
143             ss << 'r';
144         } else if (ss.tellp() != prev_tellp) {
145             ss << ';';
146             prev_tellp = ss.tellp();
147         }
148     }
149
150     ss << inscribe_flags_aux(flag_insc_resistance, flags, is_kanji);
151
152     if (has_flag_of(flag_insc_misc, flags) && (ss.tellp() != prev_tellp)) {
153         ss << ';';
154         prev_tellp = ss.tellp();
155     }
156
157     ss << inscribe_flags_aux(flag_insc_misc, flags, is_kanji);
158
159     if (has_flag_of(flag_insc_aura, flags)) {
160         ss << '[';
161     }
162
163     ss << inscribe_flags_aux(flag_insc_aura, flags, is_kanji);
164
165     if (has_flag_of(flag_insc_brand, flags)) {
166         ss << '|';
167     }
168
169     ss << inscribe_flags_aux(flag_insc_brand, flags, is_kanji);
170
171     if (has_flag_of(flag_insc_kill, flags)) {
172         ss << "/X";
173     }
174
175     ss << inscribe_flags_aux(flag_insc_kill, flags, is_kanji);
176
177     if (has_flag_of(flag_insc_slay, flags)) {
178         ss << '/';
179     }
180
181     ss << inscribe_flags_aux(flag_insc_slay, flags, is_kanji);
182
183     if (is_kanji) {
184         if (has_flag_of(flag_insc_esp1, flags) || has_flag_of(flag_insc_esp2, flags)) {
185             ss << '~';
186         }
187
188         ss << inscribe_flags_aux(flag_insc_esp1, flags, is_kanji)
189            << inscribe_flags_aux(flag_insc_esp2, flags, is_kanji);
190     } else {
191         if (has_flag_of(flag_insc_esp1, flags)) {
192             ss << '~';
193         }
194
195         ss << inscribe_flags_aux(flag_insc_esp1, flags, is_kanji);
196
197         if (has_flag_of(flag_insc_esp2, flags)) {
198             ss << '~';
199         }
200
201         ss << inscribe_flags_aux(flag_insc_esp2, flags, is_kanji);
202     }
203
204     if (has_flag_of(flag_insc_sust, flags)) {
205         ss << '(';
206     }
207
208     ss << inscribe_flags_aux(flag_insc_sust, flags, is_kanji);
209
210     return ss.str();
211 }
212
213 /*!
214  * @brief オブジェクト名の特性短縮表記+刻み内容を提示する。 / Get object inscription with auto inscription of object flags.
215  * @param buff 特性短縮表記を格納する文字列ポインタ
216  * @param o_ptr 特性短縮表記を得たいオブジェクト構造体の参照ポインタ
217  */
218 std::string get_inscription(const ItemEntity &item)
219 {
220     if (!item.is_inscribed()) {
221         return {};
222     }
223
224     std::stringstream ss;
225
226     if (!item.is_fully_known()) {
227         for (std::string_view sv = item.inscription.value(); !sv.empty(); sv.remove_prefix(1)) {
228             if (sv.front() == '#') {
229                 break;
230             }
231             if (ss.tellp() > MAX_INSCRIPTION - 1) {
232                 break;
233             }
234 #ifdef JP
235             if (iskanji(sv.front())) {
236                 ss << sv.front();
237                 sv.remove_prefix(1);
238             }
239 #endif
240             ss << sv.front();
241         }
242
243         return ss.str();
244     }
245
246     for (std::string_view sv = item.inscription.value(); !sv.empty(); sv.remove_prefix(1)) {
247         switch (sv.front()) {
248         case '#':
249             return ss.str();
250         case '%': {
251             const auto start_pos = ss.tellp();
252             if (start_pos >= MAX_NLEN) {
253                 break;
254             }
255
256             auto is_kanji = false;
257 #ifdef JP
258             if ((sv.size() > 1) && ('%' == sv[1])) {
259                 sv.remove_prefix(1);
260             } else {
261                 is_kanji = true;
262             }
263 #endif
264
265             auto all = false;
266             if (sv.substr(1, 3) == "all") {
267                 all = true;
268                 sv.remove_prefix(3);
269             }
270
271             ss << get_ability_abbreviation(item, is_kanji, all);
272             if (ss.tellp() == start_pos) {
273                 ss << ' ';
274             }
275             break;
276         }
277         default:
278             ss << sv.front();
279             break;
280         }
281     }
282
283     return ss.str();
284 }
285
286 #ifdef JP
287 /*!
288  * @brief アイテムにふさわしい助数詞をつけて数を記述する
289  * @param item 数を記述したいアイテムの参照
290  * @return 記述した文字列
291  */
292 std::string describe_count_with_counter_suffix(const ItemEntity &item)
293 {
294     std::stringstream ss;
295     ss << item.number;
296
297     switch (item.bi_key.tval()) {
298     case ItemKindType::BOLT:
299     case ItemKindType::ARROW:
300     case ItemKindType::POLEARM:
301     case ItemKindType::STAFF:
302     case ItemKindType::WAND:
303     case ItemKindType::ROD:
304     case ItemKindType::DIGGING:
305         ss << "本";
306         break;
307     case ItemKindType::SCROLL:
308         ss << "巻";
309         break;
310     case ItemKindType::POTION:
311         ss << "服";
312         break;
313     case ItemKindType::LIFE_BOOK:
314     case ItemKindType::SORCERY_BOOK:
315     case ItemKindType::NATURE_BOOK:
316     case ItemKindType::CHAOS_BOOK:
317     case ItemKindType::DEATH_BOOK:
318     case ItemKindType::TRUMP_BOOK:
319     case ItemKindType::ARCANE_BOOK:
320     case ItemKindType::CRAFT_BOOK:
321     case ItemKindType::DEMON_BOOK:
322     case ItemKindType::CRUSADE_BOOK:
323     case ItemKindType::MUSIC_BOOK:
324     case ItemKindType::HISSATSU_BOOK:
325     case ItemKindType::HEX_BOOK:
326         ss << "冊";
327         break;
328     case ItemKindType::SOFT_ARMOR:
329     case ItemKindType::HARD_ARMOR:
330     case ItemKindType::DRAG_ARMOR:
331     case ItemKindType::CLOAK:
332         ss << "着";
333         break;
334     case ItemKindType::SWORD:
335     case ItemKindType::HAFTED:
336     case ItemKindType::BOW:
337         ss << "振";
338         break;
339     case ItemKindType::BOOTS:
340         ss << "足";
341         break;
342
343     case ItemKindType::CARD:
344         ss << "枚";
345         break;
346
347     case ItemKindType::FOOD:
348         if (item.bi_key.sval().value() == SV_FOOD_JERKY) {
349             ss << "切れ";
350             break;
351         }
352         [[fallthrough]];
353     default:
354         if (item.number < 10) {
355             ss << "つ";
356         } else {
357             ss << "個";
358         }
359         break;
360     }
361
362     return ss.str();
363 }
364 #endif