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 "perception/object-perception.h"
13 #include "system/artifact-type-definition.h"
14 #include "system/item-entity.h"
15 #include "system/player-type-definition.h"
16 #include "util/bit-flags-calculator.h"
17 #include "util/string-processor.h"
20 #include "object/tval-types.h"
21 #include "system/monster-race-info.h"
25 static std::string get_fullname_if_set(const ItemEntity &item, const describe_option_type &opt)
27 if (!opt.aware || item.get_flags().has_not(TR_FULL_NAME)) {
31 const auto is_known_artifact = opt.known && item.is_fixed_artifact() && none_bits(opt.mode, OD_BASE_NAME);
32 return is_known_artifact ? item.get_fixed_artifact().name : item.get_baseitem().name;
39 * @param item アイテムへの参照
41 * @return アイテムの個数を記述した文字列
43 static std::string describe_item_count_ja(const ItemEntity &item, const describe_option_type &opt)
45 if (any_bits(opt.mode, OD_OMIT_PREFIX) || (item.number <= 1)) {
49 return describe_count_with_counter_suffix(item) + "の ";
53 * @brief アーティファクトであるマークを記述する
55 * 英語の場合アーティファクトは The が付くので分かるが、日本語では分からないので
56 * 固定アーティファクトなら「★」、ランダムアーティファクトなら「☆」マークをつける.
58 * @param item アイテムへの参照
61 static std::string describe_artifact_mark_ja(const ItemEntity &item, const describe_option_type &opt)
63 if (!opt.known || any_bits(opt.mode, OD_BASE_NAME)) {
67 if (item.is_fixed_artifact()) {
69 } else if (item.is_random_artifact()) {
77 * @brief アイテムの固有名称(アイテム本体の手前に表記するもの)を記述する
80 * 固定/ランダムアーティファクト: ★リリアのダガー → "リリアの"
81 * エゴアイテム: (聖戦者)ロング・ソード → "(聖戦者)"
83 * @param item アイテムへの参照
87 static std::string describe_unique_name_before_body_ja(const ItemEntity &item, const describe_option_type &opt)
89 if (!opt.known || any_bits(opt.mode, OD_BASE_NAME)) {
93 if (item.is_random_artifact()) {
94 const std::string_view name_sv = *item.randart_name;
96 /* '『' から始まらない伝説のアイテムの名前は最初に付加する */
97 /* 英語版のセーブファイルから来た 'of XXX' は,「XXXの」と表示する */
98 if (name_sv.starts_with("of ")) {
100 ss << name_sv.substr(3) << "の";
102 } else if (!name_sv.starts_with("『") && !name_sv.starts_with("《") && !name_sv.starts_with('\'')) {
103 return *item.randart_name;
107 if (item.is_fixed_artifact() && item.get_flags().has_not(TR_FULL_NAME)) {
108 const auto &artifact = item.get_fixed_artifact();
109 /* '『' から始まらない伝説のアイテムの名前は最初に付加する */
110 if (artifact.name.find("『", 0, 2) != 0) {
111 return artifact.name;
118 return item.get_ego().name;
124 static std::optional<std::string> describe_random_artifact_name_after_body_ja(const ItemEntity &item)
126 if (!item.is_random_artifact()) {
130 const std::string_view name_sv = *item.randart_name;
131 if (name_sv.starts_with("『") || name_sv.starts_with("《")) {
132 return item.randart_name;
135 if (!name_sv.starts_with('\'')) {
139 // "'foobar'" の foobar の部分を取り出し『foobar』と表記する
140 // (英語版のセーブファイルのランダムアーティファクトを考慮)
141 return format("『%s』", name_sv.substr(1, name_sv.length() - 2).data());
144 static std::string describe_fake_artifact_name_after_body_ja(const ItemEntity &item)
146 if (!item.is_inscribed()) {
150 auto str = item.inscription->data();
168 auto str_aux = angband_strchr(item.inscription->data(), '#');
169 return format("『%s』", str_aux + 1);
173 * @brief アイテムの固有名称(アイテム本体の後に表記するもの)を記述する。
176 * 固定/ランダムアーティファクト: ★ロング・ソード『リンギル』 → "『リンギル』"
177 * 銘刻みによる疑似アーティファクト: ロング・ソード『AIR』 → "『AIR』"
178 * エゴアイテムはアイテム本体の後に記述されるタイプのものは存在しない。
180 * 銘刻みによる疑似アーティファクトは、銘の最後に #foobar と刻むことにより
181 * ゲーム上の表記が「アイテム名『foobar』」のようになる機能。
183 * @param item アイテムへの参照
187 static std::string describe_unique_name_after_body_ja(const ItemEntity &item, const describe_option_type &opt)
189 if (!opt.known || any_bits(opt.mode, OD_BASE_NAME)) {
193 if (auto body = describe_random_artifact_name_after_body_ja(item); body) {
197 if (item.is_fixed_artifact()) {
198 const auto &artifact = item.get_fixed_artifact();
199 if (artifact.name.find("『", 0, 2) == 0) {
200 return artifact.name;
206 return describe_fake_artifact_name_after_body_ja(item);
210 static std::string describe_vowel(const ItemEntity &item, std::string_view basename, std::string_view modstr)
213 switch (basename[0]) {
215 vowel = is_a_vowel(modstr[0]);
218 vowel = is_a_vowel(item.get_baseitem().name[0]);
221 vowel = is_a_vowel(basename[0]);
225 return (vowel) ? "an " : "a ";
228 static std::string describe_prefix_en(const ItemEntity &item)
230 if (item.number <= 0) {
234 if (item.number == 1) {
238 return std::to_string(item.number) + ' ';
241 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)
243 if (any_bits(opt.mode, OD_OMIT_PREFIX)) {
247 if (auto prefix = describe_prefix_en(item); !prefix.empty()) {
251 auto is_unique_item = opt.known && item.is_fixed_or_random_artifact();
252 is_unique_item |= (item.bi_key.tval() == ItemKindType::CORPSE) && item.get_monrace().kind_flags.has(MonsterKindType::UNIQUE);
253 if (is_unique_item) {
257 return describe_vowel(item, basename, modstr);
260 static std::string describe_item_count_or_definite_article_en(const ItemEntity &item, const describe_option_type &opt)
262 if (any_bits(opt.mode, OD_OMIT_PREFIX)) {
266 if (auto prefix = describe_prefix_en(item); !prefix.empty()) {
270 if (opt.known && item.is_fixed_or_random_artifact()) {
277 static std::string describe_unique_name_after_body_en(const ItemEntity &item, const describe_option_type &opt)
279 if (!opt.known || item.get_flags().has(TR_FULL_NAME) || any_bits(opt.mode, OD_BASE_NAME)) {
283 std::stringstream ss;
285 if (item.is_random_artifact()) {
286 ss << ' ' << *item.randart_name;
290 if (item.is_fixed_artifact()) {
291 const auto &artifact = ArtifactList::get_instance().get_artifact(item.fa_id);
292 ss << ' ' << artifact.name;
297 ss << ' ' << egos_info[item.ego_idx].name;
300 if (item.is_inscribed()) {
301 if (auto str = angband_strchr(item.inscription->data(), '#'); str != nullptr) {
302 ss << ' ' << str + 1;
311 * @brief アイテム本体の名称を記述する
313 * 基本的には basename の内容がそのまま記述されるが、basename 上の特定の文字に対して以下の修正が行われる。
315 * - '#' が modstr の内容で置き換えられる:
316 * 主に未鑑定名やモンスターボール・像・死体などのモンスター名の部分に使用される
317 * - '%' が item のベースアイテム名で置き換えられる:
318 * BaseitemDefinition.txt でカテゴリ内における名称のみが記述されているものに使用される。
319 * たとえば薬の場合 basename は「%の薬」となっており、BaseitemDefinition.txt 内で記述されている
320 * "体力回復" により '%' が置き換えられ「体力回復の薬」と表記されるといった具合。
321 * - '~' がアイテムが複数の場合複数形表現で置き換えられる(英語版のみ)
323 * @param item アイテムへの参照
325 * @param basename アイテム本体のベースとなる文字列
326 * @param modstr ベースとなる文字列上の特定の文字を置き換える文字列
329 static std::string describe_body(const ItemEntity &item, [[maybe_unused]] const describe_option_type &opt, std::string_view basename, std::string_view modstr)
332 auto pluralize = [&opt, &item](auto &ss, auto it) {
333 if (none_bits(opt.mode, OD_NO_PLURAL) && (item.number != 1)) {
334 char k = *std::next(it, -1);
335 if ((k == 's') || (k == 'h')) {
342 std::stringstream ss;
344 for (auto it = basename.begin(), it_end = basename.end(); it != it_end; ++it) {
351 const auto &baseitem = item.get_baseitem();
355 for (auto ib = baseitem.name.begin(), ib_end = baseitem.name.end(); ib != ib_end; ++ib) {
390 * @param item アイテムへの参照
392 * @return std::string 記述したアイテム名
394 std::string describe_named_item(PlayerType *player_ptr, const ItemEntity &item, const describe_option_type &opt)
396 auto [basename, modstr] = switch_tval_description(item, opt);
397 if (auto name = get_fullname_if_set(item, opt); !name.empty()) {
398 basename = std::move(name);
400 std::string_view basename_sv = basename;
401 std::stringstream ss;
404 if (basename_sv[0] == '&') {
405 basename_sv.remove_prefix(2);
407 ss << describe_item_count_ja(item, opt)
408 << describe_artifact_mark_ja(item, opt);
410 if (basename_sv[0] == '&') {
411 basename_sv.remove_prefix(2);
412 ss << describe_item_count_or_article_en(item, opt, basename_sv, modstr);
414 ss << describe_item_count_or_definite_article_en(item, opt);
419 if (item.is_smith() && none_bits(opt.mode, OD_BASE_NAME)) {
420 ss << format("鍛冶師%sの", player_ptr->name);
423 ss << describe_unique_name_before_body_ja(item, opt);
425 if (item.is_spell_book()) {
426 // svalは0から数えているので表示用に+1している
427 ss << format("Lv%d ", *item.bi_key.sval() + 1);
430 ss << describe_body(item, opt, basename_sv, modstr);
433 ss << describe_unique_name_after_body_ja(item, opt);
435 if (item.is_smith() && none_bits(opt.mode, OD_BASE_NAME)) {
436 ss << format(" of %s the Smith", player_ptr->name);
439 ss << describe_unique_name_after_body_en(item, opt);