OSDN Git Service

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