1 #include "monster/monster-describer.h"
2 #include "io/files-util.h"
3 #include "locale/english.h"
4 #include "monster-race/monster-race.h"
5 #include "monster-race/race-sex-const.h"
6 #include "monster/monster-description-types.h"
7 #include "monster/monster-flag-types.h"
8 #include "monster/monster-info.h"
9 #include "system/angband-system.h"
10 #include "system/floor-type-definition.h"
11 #include "system/monster-entity.h"
12 #include "system/monster-race-info.h"
13 #include "system/player-type-definition.h"
14 #include "timed-effect/player-hallucination.h"
15 #include "timed-effect/timed-effects.h"
16 #include "util/bit-flags-calculator.h"
17 #include "util/string-processor.h"
18 #include "view/display-messages.h"
22 #include <string_view>
24 // @todo 性別をEnumFlags に切り替えたら引数の型も変えること.
25 static int get_monster_pronoun_kind(const MonsterRaceInfo &monrace, const bool pron)
30 if (monrace.sex == MonsterSex::FEMALE) {
33 if (monrace.sex == MonsterSex::MALE) {
39 static std::string get_monster_personal_pronoun(const int kind, const BIT_FLAGS mode)
41 switch (kind + (mode & (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE))) {
44 case 0x00 + (MD_OBJECTIVE):
46 case 0x00 + (MD_POSSESSIVE):
47 return _("何かの", "its");
48 case 0x00 + (MD_POSSESSIVE | MD_OBJECTIVE):
49 return _("何か自身", "itself");
50 case 0x00 + (MD_INDEF_HIDDEN):
51 return _("何か", "something");
52 case 0x00 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
53 return _("何か", "something");
54 case 0x00 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
55 return _("何かの", "something's");
56 case 0x00 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
57 return _("それ自身", "itself");
60 case 0x10 + (MD_OBJECTIVE):
62 case 0x10 + (MD_POSSESSIVE):
63 return _("彼の", "his");
64 case 0x10 + (MD_POSSESSIVE | MD_OBJECTIVE):
65 return _("彼自身", "himself");
66 case 0x10 + (MD_INDEF_HIDDEN):
67 return _("誰か", "someone");
68 case 0x10 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
69 return _("誰か", "someone");
70 case 0x10 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
71 return _("誰かの", "someone's");
72 case 0x10 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
73 return _("彼自身", "himself");
75 return _("彼女", "she");
76 case 0x20 + (MD_OBJECTIVE):
77 return _("彼女", "her");
78 case 0x20 + (MD_POSSESSIVE):
79 return _("彼女の", "her");
80 case 0x20 + (MD_POSSESSIVE | MD_OBJECTIVE):
81 return _("彼女自身", "herself");
82 case 0x20 + (MD_INDEF_HIDDEN):
83 return _("誰か", "someone");
84 case 0x20 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
85 return _("誰か", "someone");
86 case 0x20 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
87 return _("誰かの", "someone's");
88 case 0x20 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
89 return _("彼女自身", "herself");
95 static std::optional<std::string> decide_monster_personal_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
97 const auto seen = any_bits(mode, MD_ASSUME_VISIBLE) || (none_bits(mode, MD_ASSUME_HIDDEN) && monster.ml);
98 const auto pron = (seen && any_bits(mode, MD_PRON_VISIBLE)) || (!seen && any_bits(mode, MD_PRON_HIDDEN));
103 const auto &monrace = monster.get_appearance_monrace();
104 const auto kind = get_monster_pronoun_kind(monrace, pron);
105 return get_monster_personal_pronoun(kind, mode);
108 static std::optional<std::string> get_monster_self_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
110 const auto &monrace = monster.get_appearance_monrace();
111 constexpr BIT_FLAGS self = MD_POSSESSIVE | MD_OBJECTIVE;
112 if (!match_bits(mode, self, self)) {
116 if (monrace.sex == MonsterSex::FEMALE) {
117 return _("彼女自身", "herself");
120 if (monrace.sex == MonsterSex::MALE) {
121 return _("彼自身", "himself");
124 return _("それ自身", "itself");
127 static std::string get_describing_monster_name(const MonsterEntity &monster, const bool is_hallucinated, const BIT_FLAGS mode)
129 const auto &monrace = monster.get_appearance_monrace();
130 if (!is_hallucinated || any_bits(mode, MD_IGNORE_HALLU)) {
131 return any_bits(mode, MD_TRUE_NAME) ? monster.get_real_monrace().name : monrace.name;
135 constexpr auto filename = _("silly_j.txt", "silly.txt");
136 const auto silly_name = get_random_line(filename, enum2i(monster.r_idx));
142 MonsterRaceInfo *hallu_race;
144 auto r_idx = MonsterRace::pick_one_at_random();
145 hallu_race = &monraces_info[r_idx];
146 } while (hallu_race->kind_flags.has(MonsterKindType::UNIQUE));
147 return hallu_race->name;
152 * @brief モンスターの名前末尾に「?」を付ける
153 * @param name モンスターの名前
154 * @return ユニークの時は「『ユニーク?』」、非ユニークの時は「非ユニーク?」
155 * @details 幻覚時のペット、カメレオンが該当する
157 static std::string replace_monster_name_undefined(std::string_view name)
159 if (name.starts_with("』")) {
160 constexpr auto ja_char_length = 2;
161 const auto name_without_brackets = name.substr(0, name.length() - ja_char_length);
162 return format("%s?』", name_without_brackets.data());
165 return format("%s?", name.data());
169 static std::optional<std::string> get_fake_monster_name(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
171 const auto &monrace = monster.get_appearance_monrace();
172 const auto is_hallucinated = player.effects()->hallucination()->is_hallucinated();
173 if (monrace.kind_flags.has_not(MonsterKindType::UNIQUE) || (is_hallucinated && none_bits(mode, MD_IGNORE_HALLU))) {
177 if (monster.mflag2.has(MonsterConstantFlagType::CHAMELEON) && none_bits(mode, MD_TRUE_NAME)) {
178 return _(replace_monster_name_undefined(name), format("%s?", name.data()));
181 if (AngbandSystem::get_instance().is_phase_out() && !(player.riding && (&player.current_floor_ptr->m_list[player.riding] == &monster))) {
182 return format(_("%sもどき", "fake %s"), name.data());
188 static std::string describe_non_pet(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
190 const auto fake_name = get_fake_monster_name(player, monster, name, mode);
195 if (any_bits(mode, MD_INDEF_VISIBLE)) {
199 std::stringstream ss;
200 const auto first_char = name[0];
201 const auto article = is_a_vowel(first_char) ? "an " : "a ";
208 std::stringstream ss;
209 if (monster.is_pet() && none_bits(mode, MD_NO_OWNER)) {
210 ss << _("あなたの", "your ");
219 static std::string add_cameleon_name(const MonsterEntity &monster, const BIT_FLAGS mode)
221 if (none_bits(mode, MD_IGNORE_HALLU) || monster.mflag2.has_not(MonsterConstantFlagType::CHAMELEON)) {
225 const auto &monrace = monster.get_appearance_monrace();
226 if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
227 return _("(カメレオンの王)", "(Chameleon Lord)");
230 return _("(カメレオン)", "(Chameleon)");
234 * @brief モンスターの呼称を作成する / Build a string describing a monster in some way.
235 * @param m_ptr モンスターの参照ポインタ
236 * @param mode 呼称オプション
237 * @return std::string 要求されたモンスターの説明を含む文字列
239 std::string monster_desc(PlayerType *player_ptr, const MonsterEntity *m_ptr, BIT_FLAGS mode)
241 const auto pronoun = decide_monster_personal_pronoun(*m_ptr, mode);
246 const auto pronoun_self = get_monster_self_pronoun(*m_ptr, mode);
248 return *pronoun_self;
251 const auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
252 const auto name = get_describing_monster_name(*m_ptr, is_hallucinated, mode);
253 std::stringstream ss;
254 if (m_ptr->is_pet() && !m_ptr->is_original_ap()) {
255 ss << _(replace_monster_name_undefined(name), format("%s?", name.data()));
257 ss << describe_non_pet(*player_ptr, *m_ptr, name, mode);
260 if (m_ptr->is_named()) {
261 ss << _("「", " called ") << m_ptr->nickname << _("」", "");
264 if (player_ptr->riding && (&player_ptr->current_floor_ptr->m_list[player_ptr->riding] == m_ptr)) {
265 ss << _("(乗馬中)", "(riding)");
268 ss << add_cameleon_name(*m_ptr, mode);
269 if (any_bits(mode, MD_IGNORE_HALLU) && !m_ptr->is_original_ap()) {
270 ss << "(" << m_ptr->get_monrace().name << ")";
273 if (any_bits(mode, MD_POSSESSIVE)) {