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/string-processor.h"
17 #include "view/display-messages.h"
21 #include <string_view>
23 // @todo 性別をEnumFlags に切り替えたら引数の型も変えること.
24 static int get_monster_pronoun_kind(const MonsterRaceInfo &monrace, const bool pron)
30 if (any_bits(monrace.flags1, RF1_FEMALE)) {
34 if (any_bits(monrace.flags1, RF1_MALE)) {
41 static std::string get_monster_personal_pronoun(const int kind, const BIT_FLAGS mode)
43 switch (kind + (mode & (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE))) {
46 case 0x00 + (MD_OBJECTIVE):
48 case 0x00 + (MD_POSSESSIVE):
49 return _("何かの", "its");
50 case 0x00 + (MD_POSSESSIVE | MD_OBJECTIVE):
51 return _("何か自身", "itself");
52 case 0x00 + (MD_INDEF_HIDDEN):
53 return _("何か", "something");
54 case 0x00 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
55 return _("何か", "something");
56 case 0x00 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
57 return _("何かの", "something's");
58 case 0x00 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
59 return _("それ自身", "itself");
62 case 0x10 + (MD_OBJECTIVE):
64 case 0x10 + (MD_POSSESSIVE):
65 return _("彼の", "his");
66 case 0x10 + (MD_POSSESSIVE | MD_OBJECTIVE):
67 return _("彼自身", "himself");
68 case 0x10 + (MD_INDEF_HIDDEN):
69 return _("誰か", "someone");
70 case 0x10 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
71 return _("誰か", "someone");
72 case 0x10 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
73 return _("誰かの", "someone's");
74 case 0x10 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
75 return _("彼自身", "himself");
77 return _("彼女", "she");
78 case 0x20 + (MD_OBJECTIVE):
79 return _("彼女", "her");
80 case 0x20 + (MD_POSSESSIVE):
81 return _("彼女の", "her");
82 case 0x20 + (MD_POSSESSIVE | MD_OBJECTIVE):
83 return _("彼女自身", "herself");
84 case 0x20 + (MD_INDEF_HIDDEN):
85 return _("誰か", "someone");
86 case 0x20 + (MD_INDEF_HIDDEN | MD_OBJECTIVE):
87 return _("誰か", "someone");
88 case 0x20 + (MD_INDEF_HIDDEN | MD_POSSESSIVE):
89 return _("誰かの", "someone's");
90 case 0x20 + (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE):
91 return _("彼女自身", "herself");
97 static std::optional<std::string> decide_monster_personal_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
99 const auto seen = any_bits(mode, MD_ASSUME_VISIBLE) || (none_bits(mode, MD_ASSUME_HIDDEN) && monster.ml);
100 const auto pron = (seen && any_bits(mode, MD_PRON_VISIBLE)) || (!seen && any_bits(mode, MD_PRON_HIDDEN));
105 const auto &monrace = monraces_info[monster.ap_r_idx];
106 const auto kind = get_monster_pronoun_kind(monrace, pron);
107 return get_monster_personal_pronoun(kind, mode);
110 static std::optional<std::string> get_monster_self_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
112 const auto &monrace = monraces_info[monster.ap_r_idx];
113 constexpr BIT_FLAGS self = MD_POSSESSIVE | MD_OBJECTIVE;
114 if (!match_bits(mode, self, self)) {
118 if (any_bits(monrace.flags1, RF1_FEMALE)) {
119 return _("彼女自身", "herself");
122 if (any_bits(monrace.flags1, RF1_MALE)) {
123 return _("彼自身", "himself");
126 return _("それ自身", "itself");
129 static std::string get_describing_monster_name(const MonsterEntity &monster, const bool is_hallucinated, const BIT_FLAGS mode)
131 const auto &monrace = monraces_info[monster.ap_r_idx];
132 if (!is_hallucinated || any_bits(mode, MD_IGNORE_HALLU)) {
133 return any_bits(mode, MD_TRUE_NAME) ? monster.get_real_r_ref().name : monrace.name;
137 constexpr auto filename = _("silly_j.txt", "silly.txt");
138 const auto silly_name = get_random_line(filename, enum2i(monster.r_idx));
139 if (silly_name.has_value()) {
140 return silly_name.value();
144 MonsterRaceInfo *hallu_race;
146 auto r_idx = MonsterRace::pick_one_at_random();
147 hallu_race = &monraces_info[r_idx];
148 } while (hallu_race->kind_flags.has(MonsterKindType::UNIQUE));
149 return hallu_race->name;
154 * @brief モンスターの名前末尾に「?」を付ける
155 * @param name モンスターの名前
156 * @return ユニークの時は「『ユニーク?』」、非ユニークの時は「非ユニーク?」
157 * @details 幻覚時のペット、カメレオンが該当する
159 static std::string replace_monster_name_undefined(std::string_view name)
161 if (name.starts_with("』")) {
162 constexpr auto ja_char_length = 2;
163 const auto name_without_brackets = name.substr(0, name.length() - ja_char_length);
164 return format("%s?』", name_without_brackets.data());
167 return format("%s?", name.data());
171 static std::optional<std::string> get_fake_monster_name(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
173 const auto &monrace = monraces_info[monster.ap_r_idx];
174 const auto is_hallucinated = player.effects()->hallucination()->is_hallucinated();
175 if (monrace.kind_flags.has_not(MonsterKindType::UNIQUE) || (is_hallucinated && none_bits(mode, MD_IGNORE_HALLU))) {
179 if (monster.mflag2.has(MonsterConstantFlagType::CHAMELEON) && none_bits(mode, MD_TRUE_NAME)) {
180 return _(replace_monster_name_undefined(name), format("%s?", name.data()));
183 if (player.phase_out && !(player.riding && (&player.current_floor_ptr->m_list[player.riding] == &monster))) {
184 return format(_("%sもどき", "fake %s"), name.data());
190 static std::string describe_non_pet(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
192 const auto fake_name = get_fake_monster_name(player, monster, name, mode);
193 if (fake_name.has_value()) {
194 return fake_name.value();
197 if (any_bits(mode, MD_INDEF_VISIBLE)) {
201 std::stringstream ss;
202 const auto first_char = name[0];
203 const auto article = is_a_vowel(first_char) ? "an " : "a ";
210 std::stringstream ss;
211 if (monster.is_pet() && none_bits(mode, MD_NO_OWNER)) {
212 ss << _("あなたの", "your ");
221 static std::string add_cameleon_name(const MonsterEntity &monster, const BIT_FLAGS mode)
223 if (none_bits(mode, MD_IGNORE_HALLU) || monster.mflag2.has_not(MonsterConstantFlagType::CHAMELEON)) {
227 const auto &monrace = monraces_info[monster.ap_r_idx];
228 if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
229 return _("(カメレオンの王)", "(Chameleon Lord)");
232 return _("(カメレオン)", "(Chameleon)");
236 * @brief モンスターの呼称を作成する / Build a string describing a monster in some way.
237 * @param m_ptr モンスターの参照ポインタ
238 * @param mode 呼称オプション
239 * @return std::string 要求されたモンスターの説明を含む文字列
241 std::string monster_desc(PlayerType *player_ptr, const MonsterEntity *m_ptr, BIT_FLAGS mode)
243 const auto pronoun = decide_monster_personal_pronoun(*m_ptr, mode);
244 if (pronoun.has_value()) {
245 return pronoun.value();
248 const auto pronoun_self = get_monster_self_pronoun(*m_ptr, mode);
249 if (pronoun_self.has_value()) {
250 return pronoun_self.value();
253 const auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
254 const auto name = get_describing_monster_name(*m_ptr, is_hallucinated, mode);
255 std::stringstream ss;
256 if (m_ptr->is_pet() && !m_ptr->is_original_ap()) {
257 ss << _(replace_monster_name_undefined(name), format("%s?", name.data()));
259 ss << describe_non_pet(*player_ptr, *m_ptr, name, mode);
262 if (m_ptr->is_named()) {
263 ss << _("「", " called ") << m_ptr->nickname << _("」", "");
266 if (player_ptr->riding && (&player_ptr->current_floor_ptr->m_list[player_ptr->riding] == m_ptr)) {
267 ss << _("(乗馬中)", "(riding)");
270 ss << add_cameleon_name(*m_ptr, mode);
271 if (any_bits(mode, MD_IGNORE_HALLU) && !m_ptr->is_original_ap()) {
272 ss << "(" << monraces_info[m_ptr->r_idx].name << ")";
275 if (any_bits(mode, MD_POSSESSIVE)) {