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-flags1.h"
6 #include "monster/monster-description-types.h"
7 #include "monster/monster-flag-types.h"
8 #include "monster/monster-info.h"
9 #include "system/floor-type-definition.h"
10 #include "system/monster-entity.h"
11 #include "system/monster-race-info.h"
12 #include "system/player-type-definition.h"
13 #include "timed-effect/player-hallucination.h"
14 #include "timed-effect/timed-effects.h"
15 #include "util/bit-flags-calculator.h"
16 #include "util/quarks.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)
31 if (any_bits(monrace.flags1, RF1_FEMALE)) {
35 if (any_bits(monrace.flags1, RF1_MALE)) {
42 static std::string get_monster_personal_pronoun(const int kind, const BIT_FLAGS mode)
44 switch (kind + (mode & (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE))) {
47 case 0x00 + (MD_OBJECTIVE):
49 case 0x00 + (MD_POSSESSIVE):
50 return _("何かの", "its");
51 case 0x00 + (MD_POSSESSIVE | MD_OBJECTIVE):
52 return _("何か自身", "itself");
53 case 0x00 + (MD_INDEF_HIDDEN):
54 return _("何か", "something");
55 case 0x00 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
56 return _("何か", "something");
57 case 0x00 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
58 return _("何かの", "something's");
59 case 0x00 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
60 return _("それ自身", "itself");
63 case 0x10 + (MD_OBJECTIVE):
65 case 0x10 + (MD_POSSESSIVE):
66 return _("彼の", "his");
67 case 0x10 + (MD_POSSESSIVE | MD_OBJECTIVE):
68 return _("彼自身", "himself");
69 case 0x10 + (MD_INDEF_HIDDEN):
70 return _("誰か", "someone");
71 case 0x10 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
72 return _("誰か", "someone");
73 case 0x10 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
74 return _("誰かの", "someone's");
75 case 0x10 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
76 return _("彼自身", "himself");
78 return _("彼女", "she");
79 case 0x20 + (MD_OBJECTIVE):
80 return _("彼女", "her");
81 case 0x20 + (MD_POSSESSIVE):
82 return _("彼女の", "her");
83 case 0x20 + (MD_POSSESSIVE | MD_OBJECTIVE):
84 return _("彼女自身", "herself");
85 case 0x20 + (MD_INDEF_HIDDEN):
86 return _("誰か", "someone");
87 case 0x20 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
88 return _("誰か", "someone");
89 case 0x20 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
90 return _("誰かの", "someone's");
91 case 0x20 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
92 return _("彼女自身", "herself");
98 static std::optional<std::string> decide_monster_personal_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
100 const auto seen = any_bits(mode, MD_ASSUME_VISIBLE) || (none_bits(mode, MD_ASSUME_HIDDEN) && monster.ml);
101 const auto pron = (seen && any_bits(mode, MD_PRON_VISIBLE)) || (!seen && any_bits(mode, MD_PRON_HIDDEN));
106 const auto &monrace = monraces_info[monster.ap_r_idx];
107 const auto kind = get_monster_pronoun_kind(monrace, pron);
108 return get_monster_personal_pronoun(kind, mode);
111 static std::optional<std::string> get_monster_self_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
113 const auto &monrace = monraces_info[monster.ap_r_idx];
114 constexpr BIT_FLAGS self = MD_POSSESSIVE | MD_OBJECTIVE;
115 if (!match_bits(mode, self, self)) {
119 if (any_bits(monrace.flags1, RF1_FEMALE)) {
120 return _("彼女自身", "herself");
123 if (any_bits(monrace.flags1, RF1_MALE)) {
124 return _("彼自身", "himself");
127 return _("それ自身", "itself");
130 static std::string get_describing_monster_name(const MonsterEntity &monster, const bool is_hallucinated, const BIT_FLAGS mode)
132 const auto &monrace = monraces_info[monster.ap_r_idx];
133 if (!is_hallucinated || any_bits(mode, MD_IGNORE_HALLU)) {
134 return any_bits(mode, MD_TRUE_NAME) ? monster.get_real_r_ref().name : monrace.name;
138 constexpr auto filename = _("silly_j.txt", "silly.txt");
139 const auto silly_name = get_random_line(filename, enum2i(monster.r_idx));
140 if (silly_name.has_value()) {
141 return silly_name.value();
145 MonsterRaceInfo *hallu_race;
147 auto r_idx = MonsterRace::pick_one_at_random();
148 hallu_race = &monraces_info[r_idx];
149 } while (hallu_race->name.empty() || hallu_race->kind_flags.has(MonsterKindType::UNIQUE));
150 return hallu_race->name;
155 * @brief モンスターの名前末尾に「?」を付ける
156 * @param name モンスターの名前
157 * @return ユニークの時は「『ユニーク?』」、非ユニークの時は「非ユニーク?」
158 * @details 幻覚時のペット、カメレオンが該当する
160 static std::string replace_monster_name_undefined(std::string_view name)
162 if (name.starts_with("』")) {
163 constexpr auto ja_char_length = 2;
164 const auto name_without_brackets = name.substr(0, name.length() - ja_char_length);
165 return format("%s?』", name_without_brackets.data());
168 return format("%s?", name.data());
172 static std::optional<std::string> get_fake_monster_name(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
174 const auto &monrace = monraces_info[monster.ap_r_idx];
175 const auto is_hallucinated = player.effects()->hallucination()->is_hallucinated();
176 if (monrace.kind_flags.has_not(MonsterKindType::UNIQUE) || (is_hallucinated && none_bits(mode, MD_IGNORE_HALLU))) {
180 if (monster.mflag2.has(MonsterConstantFlagType::CHAMELEON) && none_bits(mode, MD_TRUE_NAME)) {
181 return _(replace_monster_name_undefined(name), format("%s?", name.data()));
184 if (player.phase_out && !(player.riding && (&player.current_floor_ptr->m_list[player.riding] == &monster))) {
185 return format(_("%sもどき", "fake %s"), name.data());
191 static std::string describe_non_pet(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
193 const auto fake_name = get_fake_monster_name(player, monster, name, mode);
194 if (fake_name.has_value()) {
195 return fake_name.value();
198 if (any_bits(mode, MD_INDEF_VISIBLE)) {
202 std::stringstream ss;
203 const auto first_char = name[0];
204 const auto article = is_a_vowel(first_char) ? "an " : "a ";
211 std::stringstream ss;
212 if (monster.is_pet()) {
213 ss << _("あなたの", "your ");
222 static std::string add_cameleon_name(const MonsterEntity &monster, const BIT_FLAGS mode)
224 if (none_bits(mode, MD_IGNORE_HALLU) || monster.mflag2.has_not(MonsterConstantFlagType::CHAMELEON)) {
228 const auto &monrace = monraces_info[monster.ap_r_idx];
229 if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
230 return _("(カメレオンの王)", "(Chameleon Lord)");
233 return _("(カメレオン)", "(Chameleon)");
237 * @brief モンスターの呼称を作成する / Build a string describing a monster in some way.
238 * @param m_ptr モンスターの参照ポインタ
239 * @param mode 呼称オプション
240 * @return std::string 要求されたモンスターの説明を含む文字列
242 std::string monster_desc(PlayerType *player_ptr, const MonsterEntity *m_ptr, BIT_FLAGS mode)
244 const auto pronoun = decide_monster_personal_pronoun(*m_ptr, mode);
245 if (pronoun.has_value()) {
246 return pronoun.value();
249 const auto pronoun_self = get_monster_self_pronoun(*m_ptr, mode);
250 if (pronoun_self.has_value()) {
251 return pronoun_self.value();
254 const auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
255 const auto name = get_describing_monster_name(*m_ptr, is_hallucinated, mode);
256 std::stringstream ss;
257 if (m_ptr->is_pet() && !m_ptr->is_original_ap()) {
258 ss << _(replace_monster_name_undefined(name), format("%s?", name.data()));
260 ss << describe_non_pet(*player_ptr, *m_ptr, name, mode);
263 if (m_ptr->is_named()) {
264 ss << _("「", " called ") << m_ptr->nickname << _("」", "");
267 if (player_ptr->riding && (&player_ptr->current_floor_ptr->m_list[player_ptr->riding] == m_ptr)) {
268 ss << _("(乗馬中)", "(riding)");
271 ss << add_cameleon_name(*m_ptr, mode);
272 if (any_bits(mode, MD_IGNORE_HALLU) && !m_ptr->is_original_ap()) {
273 ss << "(" << monraces_info[m_ptr->r_idx].name << ")";
276 if (any_bits(mode, MD_POSSESSIVE)) {