OSDN Git Service

Merge pull request #3814 from Slimebreath6078/feature/Add_Laffey_II
[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-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"
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     if (monrace.sex == MonsterSex::FEMALE) {
31         return 0x20;
32     }
33     if (monrace.sex == MonsterSex::MALE) {
34         return 0x10;
35     }
36     return 0x00;
37 }
38
39 static std::string get_monster_personal_pronoun(const int kind, const BIT_FLAGS mode)
40 {
41     switch (kind + (mode & (MD_INDEF_HIDDEN | MD_POSSESSIVE | MD_OBJECTIVE))) {
42     case 0x00:
43         return _("何か", "it");
44     case 0x00 + (MD_OBJECTIVE):
45         return _("何か", "it");
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");
58     case 0x10:
59         return _("彼", "he");
60     case 0x10 + (MD_OBJECTIVE):
61         return _("彼", "him");
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");
74     case 0x20:
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");
90     default:
91         return _("何か", "it");
92     }
93 }
94
95 static std::optional<std::string> decide_monster_personal_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
96 {
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));
99     if (seen && !pron) {
100         return std::nullopt;
101     }
102
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);
106 }
107
108 static std::optional<std::string> get_monster_self_pronoun(const MonsterEntity &monster, const BIT_FLAGS mode)
109 {
110     const auto &monrace = monster.get_appearance_monrace();
111     constexpr BIT_FLAGS self = MD_POSSESSIVE | MD_OBJECTIVE;
112     if (!match_bits(mode, self, self)) {
113         return std::nullopt;
114     }
115
116     if (monrace.sex == MonsterSex::FEMALE) {
117         return _("彼女自身", "herself");
118     }
119
120     if (monrace.sex == MonsterSex::MALE) {
121         return _("彼自身", "himself");
122     }
123
124     return _("それ自身", "itself");
125 }
126
127 static std::string get_describing_monster_name(const MonsterEntity &monster, const bool is_hallucinated, const BIT_FLAGS mode)
128 {
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;
132     }
133
134     if (one_in_(2)) {
135         constexpr auto filename = _("silly_j.txt", "silly.txt");
136         const auto silly_name = get_random_line(filename, enum2i(monster.r_idx));
137         if (silly_name) {
138             return *silly_name;
139         }
140     }
141
142     MonsterRaceInfo *hallu_race;
143     do {
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;
148 }
149
150 #ifdef JP
151 /*!
152  * @brief モンスターの名前末尾に「?」を付ける
153  * @param name モンスターの名前
154  * @return ユニークの時は「『ユニーク?』」、非ユニークの時は「非ユニーク?」
155  * @details 幻覚時のペット、カメレオンが該当する
156  */
157 static std::string replace_monster_name_undefined(std::string_view name)
158 {
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());
163     }
164
165     return format("%s?", name.data());
166 }
167 #endif
168
169 static std::optional<std::string> get_fake_monster_name(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
170 {
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))) {
174         return std::nullopt;
175     }
176
177     if (monster.mflag2.has(MonsterConstantFlagType::CHAMELEON) && none_bits(mode, MD_TRUE_NAME)) {
178         return _(replace_monster_name_undefined(name), format("%s?", name.data()));
179     }
180
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());
183     }
184
185     return name;
186 }
187
188 static std::string describe_non_pet(const PlayerType &player, const MonsterEntity &monster, const std::string &name, const BIT_FLAGS mode)
189 {
190     const auto fake_name = get_fake_monster_name(player, monster, name, mode);
191     if (fake_name) {
192         return *fake_name;
193     }
194
195     if (any_bits(mode, MD_INDEF_VISIBLE)) {
196 #ifdef JP
197         return name;
198 #else
199         std::stringstream ss;
200         const auto first_char = name[0];
201         const auto article = is_a_vowel(first_char) ? "an " : "a ";
202         ss << article;
203         ss << name;
204         return ss.str();
205 #endif
206     }
207
208     std::stringstream ss;
209     if (monster.is_pet() && none_bits(mode, MD_NO_OWNER)) {
210         ss << _("あなたの", "your ");
211     } else {
212         ss << _("", "the ");
213     }
214
215     ss << name;
216     return ss.str();
217 }
218
219 static std::string add_cameleon_name(const MonsterEntity &monster, const BIT_FLAGS mode)
220 {
221     if (none_bits(mode, MD_IGNORE_HALLU) || monster.mflag2.has_not(MonsterConstantFlagType::CHAMELEON)) {
222         return "";
223     }
224
225     const auto &monrace = monster.get_appearance_monrace();
226     if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
227         return _("(カメレオンの王)", "(Chameleon Lord)");
228     }
229
230     return _("(カメレオン)", "(Chameleon)");
231 }
232
233 /*!
234  * @brief モンスターの呼称を作成する / Build a string describing a monster in some way.
235  * @param m_ptr モンスターの参照ポインタ
236  * @param mode 呼称オプション
237  * @return std::string 要求されたモンスターの説明を含む文字列
238  */
239 std::string monster_desc(PlayerType *player_ptr, const MonsterEntity *m_ptr, BIT_FLAGS mode)
240 {
241     const auto pronoun = decide_monster_personal_pronoun(*m_ptr, mode);
242     if (pronoun) {
243         return *pronoun;
244     }
245
246     const auto pronoun_self = get_monster_self_pronoun(*m_ptr, mode);
247     if (pronoun_self) {
248         return *pronoun_self;
249     }
250
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()));
256     } else {
257         ss << describe_non_pet(*player_ptr, *m_ptr, name, mode);
258     }
259
260     if (m_ptr->is_named()) {
261         ss << _("「", " called ") << m_ptr->nickname << _("」", "");
262     }
263
264     if (player_ptr->riding && (&player_ptr->current_floor_ptr->m_list[player_ptr->riding] == m_ptr)) {
265         ss << _("(乗馬中)", "(riding)");
266     }
267
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 << ")";
271     }
272
273     if (any_bits(mode, MD_POSSESSIVE)) {
274         ss << _("の", "'s");
275     }
276
277     return ss.str();
278 }