OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / monster / monster-describer.cpp
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"
18 #include <optional>
19 #include <sstream>
20 #include <string>
21 #include <string_view>
22
23 // @todo 性別をEnumFlags に切り替えたら引数の型も変えること.
24 static int get_monster_pronoun_kind(const MonsterRaceInfo &monrace, const bool pron)
25 {
26     if (!pron) {
27         return 0x00;
28     }
29
30     if (any_bits(monrace.flags1, RF1_FEMALE)) {
31         return 0x20;
32     }
33
34     if (any_bits(monrace.flags1, RF1_MALE)) {
35         return 0x10;
36     }
37
38     return 0x00;
39 }
40
41 static std::string get_monster_personal_pronoun(const int kind, const BIT_FLAGS mode)
42 {
43     switch (kind + (mode & (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE))) {
44     case 0x00:
45         return _("何か", "it");
46     case 0x00 + (MD_OBJECTIVE):
47         return _("何か", "it");
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");
60     case 0x10:
61         return _("彼", "he");
62     case 0x10 + (MD_OBJECTIVE):
63         return _("彼", "him");
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");
76     case 0x20:
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");
92     default:
93         return _("何か", "it");
94     }
95 }
96
97 static std::optional<std::string> decide_monster_personal_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
98 {
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));
101     if (seen && !pron) {
102         return std::nullopt;
103     }
104
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);
108 }
109
110 static std::optional<std::string> get_monster_self_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
111 {
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)) {
115         return std::nullopt;
116     }
117
118     if (any_bits(monrace.flags1, RF1_FEMALE)) {
119         return _("彼女自身", "herself");
120     }
121
122     if (any_bits(monrace.flags1, RF1_MALE)) {
123         return _("彼自身", "himself");
124     }
125
126     return _("それ自身", "itself");
127 }
128
129 static std::string get_describing_monster_name(const MonsterEntity &monster, const bool is_hallucinated, const BIT_FLAGS mode)
130 {
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;
134     }
135
136     if (one_in_(2)) {
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();
141         }
142     }
143
144     MonsterRaceInfo *hallu_race;
145     do {
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;
150 }
151
152 #ifdef JP
153 /*!
154  * @brief モンスターの名前末尾に「?」を付ける
155  * @param name モンスターの名前
156  * @return ユニークの時は「『ユニーク?』」、非ユニークの時は「非ユニーク?」
157  * @details 幻覚時のペット、カメレオンが該当する
158  */
159 static std::string replace_monster_name_undefined(std::string_view name)
160 {
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());
165     }
166
167     return format("%s?", name.data());
168 }
169 #endif
170
171 static std::optional<std::string> get_fake_monster_name(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
172 {
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))) {
176         return std::nullopt;
177     }
178
179     if (monster.mflag2.has(MonsterConstantFlagType::CHAMELEON) && none_bits(mode, MD_TRUE_NAME)) {
180         return _(replace_monster_name_undefined(name), format("%s?", name.data()));
181     }
182
183     if (player.phase_out && !(player.riding && (&player.current_floor_ptr->m_list[player.riding] == &monster))) {
184         return format(_("%sもどき", "fake %s"), name.data());
185     }
186
187     return name;
188 }
189
190 static std::string describe_non_pet(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
191 {
192     const auto fake_name = get_fake_monster_name(player, monster, name, mode);
193     if (fake_name.has_value()) {
194         return fake_name.value();
195     }
196
197     if (any_bits(mode, MD_INDEF_VISIBLE)) {
198 #ifdef JP
199         return name;
200 #else
201         std::stringstream ss;
202         const auto first_char = name[0];
203         const auto article = is_a_vowel(first_char) ? "an " : "a ";
204         ss << article;
205         ss << name;
206         return ss.str();
207 #endif
208     }
209
210     std::stringstream ss;
211     if (monster.is_pet() && none_bits(mode, MD_NO_OWNER)) {
212         ss << _("あなたの", "your ");
213     } else {
214         ss << _("", "the ");
215     }
216
217     ss << name;
218     return ss.str();
219 }
220
221 static std::string add_cameleon_name(const MonsterEntity &monster, const BIT_FLAGS mode)
222 {
223     if (none_bits(mode, MD_IGNORE_HALLU) || monster.mflag2.has_not(MonsterConstantFlagType::CHAMELEON)) {
224         return "";
225     }
226
227     const auto &monrace = monraces_info[monster.ap_r_idx];
228     if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
229         return _("(カメレオンの王)", "(Chameleon Lord)");
230     }
231
232     return _("(カメレオン)", "(Chameleon)");
233 }
234
235 /*!
236  * @brief モンスターの呼称を作成する / Build a string describing a monster in some way.
237  * @param m_ptr モンスターの参照ポインタ
238  * @param mode 呼称オプション
239  * @return std::string 要求されたモンスターの説明を含む文字列
240  */
241 std::string monster_desc(PlayerType *player_ptr, const MonsterEntity *m_ptr, BIT_FLAGS mode)
242 {
243     const auto pronoun = decide_monster_personal_pronoun(*m_ptr, mode);
244     if (pronoun.has_value()) {
245         return pronoun.value();
246     }
247
248     const auto pronoun_self = get_monster_self_pronoun(*m_ptr, mode);
249     if (pronoun_self.has_value()) {
250         return pronoun_self.value();
251     }
252
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()));
258     } else {
259         ss << describe_non_pet(*player_ptr, *m_ptr, name, mode);
260     }
261
262     if (m_ptr->is_named()) {
263         ss << _("「", " called ") << m_ptr->nickname << _("」", "");
264     }
265
266     if (player_ptr->riding && (&player_ptr->current_floor_ptr->m_list[player_ptr->riding] == m_ptr)) {
267         ss << _("(乗馬中)", "(riding)");
268     }
269
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 << ")";
273     }
274
275     if (any_bits(mode, MD_POSSESSIVE)) {
276         ss << _("の", "'s");
277     }
278
279     return ss.str();
280 }