1 #include "flavor/named-item-describer.h"
2 #include "artifact/fixed-art-types.h"
3 #include "flavor/flavor-util.h"
4 #include "flavor/object-flavor-types.h"
5 #include "flavor/tval-description-switcher.h"
6 #include "game-option/text-display-options.h"
7 #include "locale/english.h"
8 #include "mind/mind-weaponsmith.h"
9 #include "object-enchant/object-ego.h"
10 #include "object-enchant/special-object-flags.h"
11 #include "object-enchant/tr-types.h"
12 #include "object/object-flags.h"
13 #include "perception/object-perception.h"
14 #include "system/artifact-type-definition.h"
15 #include "system/item-entity.h"
16 #include "system/player-type-definition.h"
17 #include "util/bit-flags-calculator.h"
18 #include "util/quarks.h"
19 #include "util/string-processor.h"
22 #include "monster-race/monster-race.h"
23 #include "monster-race/race-flags1.h"
24 #include "object/tval-types.h"
25 #include "system/monster-race-info.h"
29 static std::string get_fullname_if_set(const ItemEntity &item, const describe_option_type &opt)
31 if (!opt.aware || object_flags(&item).has_not(TR_FULL_NAME)) {
35 const auto fixed_art_id = item.fixed_artifact_idx;
36 const auto is_known_artifact = opt.known && item.is_fixed_artifact() && none_bits(opt.mode, OD_BASE_NAME);
37 return is_known_artifact ? ArtifactsInfo::get_instance().get_artifact(fixed_art_id).name : item.get_baseitem().name;
44 * @param item アイテムへの参照
46 * @return アイテムの個数を記述した文字列
48 static std::string describe_item_count_ja(const ItemEntity &item, const describe_option_type &opt)
50 if (any_bits(opt.mode, OD_OMIT_PREFIX) || (item.number <= 1)) {
54 return describe_count_with_counter_suffix(item) + "の ";
58 * @brief アーティファクトであるマークを記述する
60 * 英語の場合アーティファクトは The が付くので分かるが、日本語では分からないので
61 * 固定アーティファクトなら「★」、ランダムアーティファクトなら「☆」マークをつける.
63 * @param item アイテムへの参照
66 static std::string describe_artifact_mark_ja(const ItemEntity &item, const describe_option_type &opt)
68 if (!opt.known || any_bits(opt.mode, OD_BASE_NAME)) {
72 if (item.is_fixed_artifact()) {
74 } else if (item.is_random_artifact()) {
82 * @brief アイテムの固有名称(アイテム本体の手前に表記するもの)を記述する
85 * 固定/ランダムアーティファクト: ★リリアのダガー → "リリアの"
86 * エゴアイテム: (聖戦者)ロング・ソード → "(聖戦者)"
88 * @param item アイテムへの参照
92 static std::string describe_unique_name_before_body_ja(const ItemEntity &item, const describe_option_type &opt)
94 if (!opt.known || any_bits(opt.mode, OD_BASE_NAME)) {
98 if (item.is_random_artifact()) {
99 const std::string_view name_sv = item.randart_name.value();
101 /* '『' から始まらない伝説のアイテムの名前は最初に付加する */
102 /* 英語版のセーブファイルから来た 'of XXX' は,「XXXの」と表示する */
103 if (name_sv.starts_with("of ")) {
104 std::stringstream ss;
105 ss << name_sv.substr(3) << "の";
107 } else if (!name_sv.starts_with("『") && !name_sv.starts_with("《") && !name_sv.starts_with('\'')) {
108 return item.randart_name.value();
112 if (item.is_fixed_artifact() && object_flags(&item).has_not(TR_FULL_NAME)) {
113 const auto &artifact = ArtifactsInfo::get_instance().get_artifact(item.fixed_artifact_idx);
114 /* '『' から始まらない伝説のアイテムの名前は最初に付加する */
115 if (artifact.name.find("『", 0, 2) != 0) {
116 return artifact.name;
123 return item.get_ego().name;
129 static std::optional<std::string> describe_random_artifact_name_after_body_ja(const ItemEntity &item)
131 if (!item.is_random_artifact()) {
135 const std::string_view name_sv = item.randart_name.value();
136 if (name_sv.starts_with("『") || name_sv.starts_with("《")) {
137 return item.randart_name;
140 if (!name_sv.starts_with('\'')) {
144 // "'foobar'" の foobar の部分を取り出し『foobar』と表記する
145 // (英語版のセーブファイルのランダムアーティファクトを考慮)
146 return format("『%s』", name_sv.substr(1, name_sv.length() - 2).data());
149 static std::string describe_fake_artifact_name_after_body_ja(const ItemEntity &item)
151 if (!item.is_inscribed()) {
155 auto str = item.inscription->data();
173 auto str_aux = angband_strchr(item.inscription->data(), '#');
174 return format("『%s』", str_aux + 1);
178 * @brief アイテムの固有名称(アイテム本体の後に表記するもの)を記述する。
181 * 固定/ランダムアーティファクト: ★ロング・ソード『リンギル』 → "『リンギル』"
182 * 銘刻みによる疑似アーティファクト: ロング・ソード『AIR』 → "『AIR』"
183 * エゴアイテムはアイテム本体の後に記述されるタイプのものは存在しない。
185 * 銘刻みによる疑似アーティファクトは、銘の最後に #foobar と刻むことにより
186 * ゲーム上の表記が「アイテム名『foobar』」のようになる機能。
188 * @param item アイテムへの参照
192 static std::string describe_unique_name_after_body_ja(const ItemEntity &item, const describe_option_type &opt)
194 if (!opt.known || any_bits(opt.mode, OD_BASE_NAME)) {
198 if (auto body = describe_random_artifact_name_after_body_ja(item); body.has_value()) {
202 if (item.is_fixed_artifact()) {
203 const auto &artifact = ArtifactsInfo::get_instance().get_artifact(item.fixed_artifact_idx);
204 if (artifact.name.find("『", 0, 2) == 0) {
205 return artifact.name;
211 return describe_fake_artifact_name_after_body_ja(item);
215 static std::string describe_vowel(const ItemEntity &item, std::string_view basename, std::string_view modstr)
218 switch (basename[0]) {
220 vowel = is_a_vowel(modstr[0]);
223 vowel = is_a_vowel(baseitems_info[item.bi_id].name[0]);
226 vowel = is_a_vowel(basename[0]);
230 return (vowel) ? "an " : "a ";
233 static std::string describe_prefix_en(const ItemEntity &item)
235 if (item.number <= 0) {
239 if (item.number == 1) {
243 return std::to_string(item.number) + ' ';
246 static std::string describe_item_count_or_article_en(const ItemEntity &item, const describe_option_type &opt, std::string_view basename, std::string_view modstr)
248 if (any_bits(opt.mode, OD_OMIT_PREFIX)) {
252 if (auto prefix = describe_prefix_en(item); !prefix.empty()) {
256 const auto corpse_r_idx = i2enum<MonsterRaceId>(item.pval);
257 auto is_unique_corpse = item.bi_key.tval() == ItemKindType::CORPSE;
258 is_unique_corpse &= monraces_info[corpse_r_idx].kind_flags.has(MonsterKindType::UNIQUE);
259 if ((opt.known && item.is_fixed_or_random_artifact()) || is_unique_corpse) {
263 return describe_vowel(item, basename, modstr);
266 static std::string describe_item_count_or_definite_article_en(const ItemEntity &item, const describe_option_type &opt)
268 if (any_bits(opt.mode, OD_OMIT_PREFIX)) {
272 if (auto prefix = describe_prefix_en(item); !prefix.empty()) {
276 if (opt.known && item.is_fixed_or_random_artifact()) {
283 static std::string describe_unique_name_after_body_en(const ItemEntity &item, const describe_option_type &opt)
285 if (!opt.known || object_flags(&item).has(TR_FULL_NAME) || any_bits(opt.mode, OD_BASE_NAME)) {
289 std::stringstream ss;
291 if (item.is_random_artifact()) {
292 ss << ' ' << item.randart_name.value();
296 if (item.is_fixed_artifact()) {
297 const auto &artifact = ArtifactsInfo::get_instance().get_artifact(item.fixed_artifact_idx);
298 ss << ' ' << artifact.name;
303 ss << ' ' << egos_info[item.ego_idx].name;
306 if (item.is_inscribed()) {
307 if (auto str = angband_strchr(item.inscription->data(), '#'); str != nullptr) {
308 ss << ' ' << str + 1;
317 * @brief アイテム本体の名称を記述する
319 * 基本的には basename の内容がそのまま記述されるが、basename 上の特定の文字に対して以下の修正が行われる。
321 * - '#' が modstr の内容で置き換えられる:
322 * 主に未鑑定名やモンスターボール・像・死体などのモンスター名の部分に使用される
323 * - '%' が item のベースアイテム名で置き換えられる:
324 * BaseitemDefinition.txt でカテゴリ内における名称のみが記述されているものに使用される。
325 * たとえば薬の場合 basename は「%の薬」となっており、BaseitemDefinition.txt 内で記述されている
326 * "体力回復" により '%' が置き換えられ「体力回復の薬」と表記されるといった具合。
327 * - '~' がアイテムが複数の場合複数形表現で置き換えられる(英語版のみ)
329 * @param item アイテムへの参照
331 * @param basename アイテム本体のベースとなる文字列
332 * @param modstr ベースとなる文字列上の特定の文字を置き換える文字列
335 static std::string describe_body(const ItemEntity &item, [[maybe_unused]] const describe_option_type &opt, std::string_view basename, std::string_view modstr)
338 auto pluralize = [&opt, &item](auto &ss, auto it) {
339 if (none_bits(opt.mode, OD_NO_PLURAL) && (item.number != 1)) {
340 char k = *std::next(it, -1);
341 if ((k == 's') || (k == 'h')) {
348 std::stringstream ss;
350 for (auto it = basename.begin(), it_end = basename.end(); it != it_end; ++it) {
357 const auto &baseitem = item.get_baseitem();
361 for (auto ib = baseitem.name.begin(), ib_end = baseitem.name.end(); ib != ib_end; ++ib) {
396 * @param item アイテムへの参照
398 * @return std::string 記述したアイテム名
400 std::string describe_named_item(PlayerType *player_ptr, const ItemEntity &item, const describe_option_type &opt)
402 auto [basename, modstr] = switch_tval_description(item, opt);
403 if (auto name = get_fullname_if_set(item, opt); !name.empty()) {
404 basename = std::move(name);
406 std::string_view basename_sv = basename;
407 std::stringstream ss;
410 if (basename_sv[0] == '&') {
411 basename_sv.remove_prefix(2);
413 ss << describe_item_count_ja(item, opt)
414 << describe_artifact_mark_ja(item, opt);
416 if (basename_sv[0] == '&') {
417 basename_sv.remove_prefix(2);
418 ss << describe_item_count_or_article_en(item, opt, basename_sv, modstr);
420 ss << describe_item_count_or_definite_article_en(item, opt);
425 if (item.is_smith() && none_bits(opt.mode, OD_BASE_NAME)) {
426 ss << format("鍛冶師%sの", player_ptr->name);
429 ss << describe_unique_name_before_body_ja(item, opt);
431 if (item.is_spell_book()) {
432 // svalは0から数えているので表示用に+1している
433 ss << format("Lv%d ", item.bi_key.sval().value() + 1);
436 ss << describe_body(item, opt, basename_sv, modstr);
439 ss << describe_unique_name_after_body_ja(item, opt);
441 if (item.is_smith() && none_bits(opt.mode, OD_BASE_NAME)) {
442 ss << format(" of %s the Smith", player_ptr->name);
445 ss << describe_unique_name_after_body_en(item, opt);