OSDN Git Service

[Refactor] #2807 Renamed monster-race-definition.h to monster-race-info.h
[hengbandforosx/hengbandosx.git] / src / view / display-lore.cpp
1 /*!
2  * @brief モンスターの思い出を表示する処理
3  * @date 2020/06/09
4  * @author Hourier
5  */
6
7 #include "view/display-lore.h"
8 #include "game-option/cheat-options.h"
9 #include "game-option/text-display-options.h"
10 #include "locale/english.h"
11 #include "locale/japanese.h"
12 #include "lore/lore-calculator.h"
13 #include "lore/lore-util.h"
14 #include "lore/monster-lore.h"
15 #include "monster-attack/monster-attack-table.h"
16 #include "monster-race/monster-race.h"
17 #include "monster-race/race-ability-flags.h"
18 #include "monster-race/race-flags1.h"
19 #include "monster-race/race-flags2.h"
20 #include "monster-race/race-flags3.h"
21 #include "monster-race/race-flags7.h"
22 #include "monster-race/race-indice-types.h"
23 #include "system/monster-race-info.h"
24 #include "system/player-type-definition.h"
25 #include "term/screen-processor.h"
26 #include "term/term-color-types.h"
27 #include "util/bit-flags-calculator.h"
28 #include "view/display-messages.h"
29 #include "world/world.h"
30
31 /*!
32  * 英語の複数系記述用マクロ / Pluralizer.  Args(count, singular, plural)
33  */
34 #define plural(c, s, p) (((c) == 1) ? (s) : (p))
35
36 /*!
37  * @brief モンスター情報のヘッダを記述する
38  * Hack -- Display the "name" and "attr/chars" of a monster race
39  * @param r_idx モンスターの種族ID
40  */
41 void roff_top(MonsterRaceId r_idx)
42 {
43     auto *r_ptr = &monraces_info[r_idx];
44     char c1 = r_ptr->d_char;
45     char c2 = r_ptr->x_char;
46
47     TERM_COLOR a1 = r_ptr->d_attr;
48     TERM_COLOR a2 = r_ptr->x_attr;
49
50     term_erase(0, 0, 255);
51     term_gotoxy(0, 0);
52
53 #ifdef JP
54 #else
55     if (r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE)) {
56         term_addstr(-1, TERM_WHITE, "The ");
57     }
58 #endif
59
60     if (w_ptr->wizard || cheat_know) {
61         term_addstr(-1, TERM_WHITE, "[");
62         term_addstr(-1, TERM_L_BLUE, format("%d", r_idx));
63         term_addstr(-1, TERM_WHITE, "] ");
64     }
65
66     term_addstr(-1, TERM_WHITE, (r_ptr->name.data()));
67
68     term_addstr(-1, TERM_WHITE, " ('");
69     term_add_bigch(a1, c1);
70     term_addstr(-1, TERM_WHITE, "')");
71
72     term_addstr(-1, TERM_WHITE, "/('");
73     term_add_bigch(a2, c2);
74     term_addstr(-1, TERM_WHITE, "'):");
75 }
76
77 /*!
78  * @brief  モンスター情報の表示と共に画面を一時消去するサブルーチン /
79  * Hack -- describe the given monster race at the top of the screen
80  * @param r_idx モンスターの種族ID
81  * @param mode 表示オプション
82  */
83 void screen_roff(PlayerType *player_ptr, MonsterRaceId r_idx, monster_lore_mode mode)
84 {
85     msg_erase();
86     term_erase(0, 1, 255);
87     hook_c_roff = c_roff;
88     process_monster_lore(player_ptr, r_idx, mode);
89     roff_top(r_idx);
90 }
91
92 /*!
93  * @brief モンスター情報の現在のウィンドウに表示する /
94  * Hack -- describe the given monster race in the current "term" window
95  * @param r_idx モンスターの種族ID
96  */
97 void display_roff(PlayerType *player_ptr)
98 {
99     for (int y = 0; y < game_term->hgt; y++) {
100         term_erase(0, y, 255);
101     }
102
103     term_gotoxy(0, 1);
104     hook_c_roff = c_roff;
105     MonsterRaceId r_idx = player_ptr->monster_race_idx;
106     process_monster_lore(player_ptr, r_idx, MONSTER_LORE_NORMAL);
107     roff_top(r_idx);
108 }
109
110 /*!
111  * @brief モンスター詳細情報を自動スポイラー向けに出力する /
112  * Hack -- output description of the given monster race
113  * @param r_idx モンスターの種族ID
114  * @param roff_func 出力処理を行う関数ポインタ
115  * @todo ここのroff_funcの引数にFILE* を追加しないとspoiler_file をローカル関数化することができないと判明した、保留.
116  */
117 void output_monster_spoiler(MonsterRaceId r_idx, void (*roff_func)(TERM_COLOR attr, concptr str))
118 {
119     hook_c_roff = roff_func;
120     PlayerType dummy;
121
122     dummy.lev = 1;
123     dummy.max_plv = 1;
124     process_monster_lore(&dummy, r_idx, MONSTER_LORE_DEBUG);
125 }
126
127 static bool display_kill_unique(lore_type *lore_ptr)
128 {
129     if (lore_ptr->kind_flags.has_not(MonsterKindType::UNIQUE)) {
130         return false;
131     }
132
133     bool dead = (lore_ptr->r_ptr->max_num == 0);
134     if (lore_ptr->r_ptr->r_deaths) {
135         hooked_roff(format(_("%^sはあなたの先祖を %d 人葬っている", "%^s has slain %d of your ancestors"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->r_deaths));
136
137         if (dead) {
138             hooked_roff(
139                 _(format("が、すでに仇討ちは果たしている!"), format(", but you have avenged %s!  ", plural(lore_ptr->r_ptr->r_deaths, "him", "them"))));
140         } else {
141             hooked_roff(
142                 _(format("のに、まだ仇討ちを果たしていない。"), format(", who %s unavenged.  ", plural(lore_ptr->r_ptr->r_deaths, "remains", "remain"))));
143         }
144
145         hooked_roff("\n");
146     } else {
147         if (dead) {
148             hooked_roff(_("あなたはこの仇敵をすでに葬り去っている。", "You have slain this foe.  "));
149         } else {
150             hooked_roff(_("この仇敵はまだ生きている!", "This foe is still alive!  "));
151         }
152
153         hooked_roff("\n");
154     }
155
156     return true;
157 }
158
159 static void display_killed(lore_type *lore_ptr)
160 {
161     hooked_roff(_(format("このモンスターはあなたの先祖を %d 人葬っている", lore_ptr->r_ptr->r_deaths),
162         format("%d of your ancestors %s been killed by this creature, ", lore_ptr->r_ptr->r_deaths, plural(lore_ptr->r_ptr->r_deaths, "has", "have"))));
163
164     if (lore_ptr->r_ptr->r_pkills) {
165         hooked_roff(format(_("が、あなたはこのモンスターを少なくとも %d 体は倒している。", "and you have exterminated at least %d of the creatures.  "),
166             lore_ptr->r_ptr->r_pkills));
167     } else if (lore_ptr->r_ptr->r_tkills) {
168         hooked_roff(format(
169             _("が、あなたの先祖はこのモンスターを少なくとも %d 体は倒している。", "and your ancestors have exterminated at least %d of the creatures.  "),
170             lore_ptr->r_ptr->r_tkills));
171     } else {
172         hooked_roff(format(_("が、まだ%sを倒したことはない。", "and %s is not ever known to have been defeated.  "), Who::who(lore_ptr->msex)));
173     }
174 }
175
176 static void display_no_killed(lore_type *lore_ptr)
177 {
178     if (lore_ptr->r_ptr->r_pkills) {
179         hooked_roff(format(
180             _("あなたはこのモンスターを少なくとも %d 体は殺している。", "You have killed at least %d of these creatures.  "), lore_ptr->r_ptr->r_pkills));
181     } else if (lore_ptr->r_ptr->r_tkills) {
182         hooked_roff(format(_("あなたの先祖はこのモンスターを少なくとも %d 体は殺している。", "Your ancestors have killed at least %d of these creatures.  "),
183             lore_ptr->r_ptr->r_tkills));
184     } else {
185         hooked_roff(_("このモンスターを倒したことはない。", "No battles to the death are recalled.  "));
186     }
187 }
188
189 /*!
190  * @brief 生存数制限のあるモンスターの最大生存数を表示する
191  * @param lore_ptr モンスターの思い出構造体への参照ポインタ
192  * @details
193  * 一度も倒したことのないモンスターの情報は不明。
194  */
195 static void display_number_of_nazguls(lore_type *lore_ptr)
196 {
197     if (lore_ptr->mode != MONSTER_LORE_DEBUG && lore_ptr->r_ptr->r_tkills == 0) {
198         return;
199     }
200     if (!lore_ptr->r_ptr->population_flags.has(MonsterPopulationType::NAZGUL)) {
201         return;
202     }
203
204     int remain = lore_ptr->r_ptr->max_num;
205     int killed = lore_ptr->r_ptr->r_akills;
206     if (remain == 0) {
207 #ifdef JP
208         hooked_roff(format("%sはかつて %ld 体存在した。", Who::who(lore_ptr->msex, (killed > 1)), killed));
209 #else
210         hooked_roff(format("You already killed all %ld of %s.  ", killed, Who::whom(lore_ptr->msex, (killed > 1))));
211 #endif
212     } else {
213 #ifdef JP
214         hooked_roff(format("%sはまだ %ld 体生きている。", Who::who(lore_ptr->msex, (remain + killed > 1)), remain));
215 #else
216         concptr be = (remain > 1) ? "are" : "is";
217         hooked_roff(format("%ld of %s %s still alive.  ", remain, Who::whom(lore_ptr->msex, (remain + killed > 1)), be));
218 #endif
219     }
220 }
221
222 void display_kill_numbers(lore_type *lore_ptr)
223 {
224     if ((lore_ptr->mode & 0x02) != 0) {
225         return;
226     }
227
228     if (display_kill_unique(lore_ptr)) {
229         return;
230     }
231
232     if (lore_ptr->r_ptr->r_deaths == 0) {
233         display_no_killed(lore_ptr);
234     } else {
235         display_killed(lore_ptr);
236     }
237
238     display_number_of_nazguls(lore_ptr);
239
240     hooked_roff("\n");
241 }
242
243 /*!
244  * @brief どこに出没するかを表示する
245  * @param lore_ptr モンスターの思い出構造体への参照ポインタ
246  * @return たぬきならFALSE、それ以外はTRUE
247  */
248 bool display_where_to_appear(lore_type *lore_ptr)
249 {
250     lore_ptr->old = false;
251     if (lore_ptr->r_ptr->level == 0) {
252         hooked_roff(format(_("%^sは町に住み", "%^s lives in the town"), Who::who(lore_ptr->msex)));
253         lore_ptr->old = true;
254     } else if (lore_ptr->r_ptr->r_tkills || lore_ptr->know_everything) {
255         if (depth_in_feet) {
256             hooked_roff(format(
257                 _("%^sは通常地下 %d フィートで出現し", "%^s is normally found at depths of %d feet"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->level * 50));
258         } else {
259             hooked_roff(format(_("%^sは通常地下 %d 階で出現し", "%^s is normally found on dungeon level %d"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->level));
260         }
261
262         lore_ptr->old = true;
263     }
264
265     if (lore_ptr->r_idx == MonsterRaceId::CHAMELEON) {
266         hooked_roff(_("、他のモンスターに化ける。", "and can take the shape of other monster."));
267         return false;
268     }
269
270     if (lore_ptr->old) {
271         hooked_roff(_("、", ", and "));
272     } else {
273         hooked_roff(format(_("%^sは", "%^s "), Who::who(lore_ptr->msex)));
274         lore_ptr->old = true;
275     }
276
277     return true;
278 }
279
280 // @todo モンスターの速度表記はmonster_typeのオブジェクトメソッドにした方がベター
281 void display_monster_move(lore_type *lore_ptr)
282 {
283 #ifdef JP
284 #else
285     hooked_roff("moves");
286 #endif
287
288     display_random_move(lore_ptr);
289     if (lore_ptr->speed > STANDARD_SPEED) {
290         if (lore_ptr->speed > 139) {
291             hook_c_roff(TERM_RED, _("信じ難いほど", " incredibly"));
292         } else if (lore_ptr->speed > 134) {
293             hook_c_roff(TERM_ORANGE, _("猛烈に", " extremely"));
294         } else if (lore_ptr->speed > 129) {
295             hook_c_roff(TERM_ORANGE, _("非常に", " very"));
296         } else if (lore_ptr->speed > 124) {
297             hook_c_roff(TERM_UMBER, _("かなり", " fairly"));
298         } else if (lore_ptr->speed < 120) {
299             hook_c_roff(TERM_L_UMBER, _("やや", " somewhat"));
300         }
301         hook_c_roff(TERM_L_RED, _("素早く", " quickly"));
302     } else if (lore_ptr->speed < STANDARD_SPEED) {
303         if (lore_ptr->speed < 90) {
304             hook_c_roff(TERM_L_GREEN, _("信じ難いほど", " incredibly"));
305         } else if (lore_ptr->speed < 95) {
306             hook_c_roff(TERM_BLUE, _("非常に", " very"));
307         } else if (lore_ptr->speed < 100) {
308             hook_c_roff(TERM_BLUE, _("かなり", " fairly"));
309         } else if (lore_ptr->speed > 104) {
310             hook_c_roff(TERM_GREEN, _("やや", " somewhat"));
311         }
312         hook_c_roff(TERM_L_BLUE, _("ゆっくりと", " slowly"));
313     } else {
314         hooked_roff(_("普通の速さで", " at normal speed"));
315     }
316
317 #ifdef JP
318     hooked_roff("動いている");
319 #endif
320 }
321
322 void display_random_move(lore_type *lore_ptr)
323 {
324     if (lore_ptr->behavior_flags.has_none_of({ MonsterBehaviorType::RAND_MOVE_50, MonsterBehaviorType::RAND_MOVE_25 })) {
325         return;
326     }
327
328     if (lore_ptr->behavior_flags.has(MonsterBehaviorType::RAND_MOVE_50) && lore_ptr->behavior_flags.has(MonsterBehaviorType::RAND_MOVE_25)) {
329         hooked_roff(_("かなり", " extremely"));
330     } else if (lore_ptr->behavior_flags.has(MonsterBehaviorType::RAND_MOVE_50)) {
331         hooked_roff(_("幾分", " somewhat"));
332     } else if (lore_ptr->behavior_flags.has(MonsterBehaviorType::RAND_MOVE_25)) {
333         hooked_roff(_("少々", " a bit"));
334     }
335
336     hooked_roff(_("不規則に", " erratically"));
337     if (lore_ptr->speed != STANDARD_SPEED) {
338         hooked_roff(_("、かつ", ", and"));
339     }
340 }
341
342 void display_monster_never_move(lore_type *lore_ptr)
343 {
344     if (lore_ptr->behavior_flags.has_not(MonsterBehaviorType::NEVER_MOVE)) {
345         return;
346     }
347
348     if (lore_ptr->old) {
349         hooked_roff(_("、しかし", ", but "));
350     } else {
351         hooked_roff(format(_("%^sは", "%^s "), Who::who(lore_ptr->msex)));
352         lore_ptr->old = true;
353     }
354
355     hooked_roff(_("侵入者を追跡しない", "does not deign to chase intruders"));
356 }
357
358 void display_monster_kind(lore_type *lore_ptr)
359 {
360     if (lore_ptr->kind_flags.has_none_of({ MonsterKindType::DRAGON, MonsterKindType::DEMON, MonsterKindType::GIANT, MonsterKindType::TROLL, MonsterKindType::ORC, MonsterKindType::ANGEL, MonsterKindType::QUANTUM, MonsterKindType::HUMAN })) {
361         hooked_roff(_("モンスター", " creature"));
362         return;
363     }
364
365     if (lore_ptr->kind_flags.has(MonsterKindType::DRAGON)) {
366         hook_c_roff(TERM_ORANGE, _("ドラゴン", " dragon"));
367     }
368
369     if (lore_ptr->kind_flags.has(MonsterKindType::DEMON)) {
370         hook_c_roff(TERM_VIOLET, _("デーモン", " demon"));
371     }
372
373     if (lore_ptr->kind_flags.has(MonsterKindType::GIANT)) {
374         hook_c_roff(TERM_L_UMBER, _("巨人", " giant"));
375     }
376
377     if (lore_ptr->kind_flags.has(MonsterKindType::TROLL)) {
378         hook_c_roff(TERM_BLUE, _("トロル", " troll"));
379     }
380
381     if (lore_ptr->kind_flags.has(MonsterKindType::ORC)) {
382         hook_c_roff(TERM_UMBER, _("オーク", " orc"));
383     }
384
385     if (lore_ptr->kind_flags.has(MonsterKindType::HUMAN)) {
386         hook_c_roff(TERM_L_WHITE, _("人間", " human"));
387     }
388
389     if (lore_ptr->kind_flags.has(MonsterKindType::QUANTUM)) {
390         hook_c_roff(TERM_VIOLET, _("量子生物", " quantum creature"));
391     }
392
393     if (lore_ptr->kind_flags.has(MonsterKindType::ANGEL)) {
394         hook_c_roff(TERM_YELLOW, _("天使", " angel"));
395     }
396 }
397
398 void display_monster_alignment(lore_type *lore_ptr)
399 {
400     if (lore_ptr->flags2 & RF2_ELDRITCH_HORROR) {
401         hook_c_roff(TERM_VIOLET, _("狂気を誘う", " sanity-blasting"));
402     }
403
404     if (lore_ptr->kind_flags.has(MonsterKindType::ANIMAL)) {
405         hook_c_roff(TERM_L_GREEN, _("自然界の", " natural"));
406     }
407
408     if (lore_ptr->kind_flags.has(MonsterKindType::EVIL)) {
409         hook_c_roff(TERM_L_DARK, _("邪悪なる", " evil"));
410     }
411
412     if (lore_ptr->kind_flags.has(MonsterKindType::GOOD)) {
413         hook_c_roff(TERM_YELLOW, _("善良な", " good"));
414     }
415
416     if (lore_ptr->kind_flags.has(MonsterKindType::UNDEAD)) {
417         hook_c_roff(TERM_VIOLET, _("アンデッドの", " undead"));
418     }
419
420     if (lore_ptr->kind_flags.has(MonsterKindType::AMBERITE)) {
421         hook_c_roff(TERM_VIOLET, _("アンバーの王族の", " Amberite"));
422     }
423 }
424
425 /*!
426  * @brief モンスターの経験値の思い出を表示する
427  * @param player_ptr プレイヤーの情報へのポインター
428  * @param lore_ptr モンスターの思い出の情報へのポインター
429  */
430 void display_monster_exp(PlayerType *player_ptr, lore_type *lore_ptr)
431 {
432 #ifdef JP
433     hooked_roff("を倒すことは");
434 #endif
435
436     int64_t base_exp = lore_ptr->r_ptr->mexp * lore_ptr->r_ptr->level * 3 / 2;
437     int64_t player_factor = (int64_t)player_ptr->max_plv + 2;
438
439     int64_t exp_integer = base_exp / player_factor;
440     int64_t exp_decimal = ((base_exp % player_factor * 1000 / player_factor) + 5) / 10;
441
442 #ifdef JP
443     hooked_roff(format(" %d レベルのキャラクタにとって 約%Ld.%02Ld ポイントの経験となる。", player_ptr->lev, exp_integer, exp_decimal));
444 #else
445     hooked_roff(format(" is worth about %Ld.%02Ld point%s", exp_integer, exp_decimal, ((exp_integer == 1) && (exp_decimal == 0)) ? "" : "s"));
446
447     concptr ordinal;
448     switch (player_ptr->lev % 10) {
449     case 1:
450         ordinal = "st";
451         break;
452     case 2:
453         ordinal = "nd";
454         break;
455     case 3:
456         ordinal = "rd";
457         break;
458     default:
459         ordinal = "th";
460         break;
461     }
462
463     concptr vowel;
464     switch (player_ptr->lev) {
465     case 8:
466     case 11:
467     case 18:
468         vowel = "n";
469         break;
470     default:
471         vowel = "";
472         break;
473     }
474
475     hooked_roff(format(" for a%s %d%s level character.  ", vowel, player_ptr->lev, ordinal));
476 #endif
477 }
478
479 void display_monster_aura(lore_type *lore_ptr)
480 {
481     auto has_fire_aura = lore_ptr->aura_flags.has(MonsterAuraType::FIRE);
482     auto has_elec_aura = lore_ptr->aura_flags.has(MonsterAuraType::ELEC);
483     auto has_cold_aura = lore_ptr->aura_flags.has(MonsterAuraType::COLD);
484     if (has_fire_aura && has_elec_aura && has_cold_aura) {
485         hook_c_roff(
486             TERM_VIOLET, format(_("%^sは炎と氷とスパークに包まれている。", "%^s is surrounded by flames, ice and electricity.  "), Who::who(lore_ptr->msex)));
487     } else if (has_fire_aura && has_elec_aura) {
488         hook_c_roff(TERM_L_RED, format(_("%^sは炎とスパークに包まれている。", "%^s is surrounded by flames and electricity.  "), Who::who(lore_ptr->msex)));
489     } else if (has_fire_aura && has_cold_aura) {
490         hook_c_roff(TERM_BLUE, format(_("%^sは炎と氷に包まれている。", "%^s is surrounded by flames and ice.  "), Who::who(lore_ptr->msex)));
491     } else if (has_cold_aura && has_elec_aura) {
492         hook_c_roff(TERM_L_GREEN, format(_("%^sは氷とスパークに包まれている。", "%^s is surrounded by ice and electricity.  "), Who::who(lore_ptr->msex)));
493     } else if (has_fire_aura) {
494         hook_c_roff(TERM_RED, format(_("%^sは炎に包まれている。", "%^s is surrounded by flames.  "), Who::who(lore_ptr->msex)));
495     } else if (has_cold_aura) {
496         hook_c_roff(TERM_BLUE, format(_("%^sは氷に包まれている。", "%^s is surrounded by ice.  "), Who::who(lore_ptr->msex)));
497     } else if (has_elec_aura) {
498         hook_c_roff(TERM_L_BLUE, format(_("%^sはスパークに包まれている。", "%^s is surrounded by electricity.  "), Who::who(lore_ptr->msex)));
499     }
500 }
501
502 void display_lore_this(PlayerType *player_ptr, lore_type *lore_ptr)
503 {
504     if ((lore_ptr->r_ptr->r_tkills == 0) && !lore_ptr->know_everything) {
505         return;
506     }
507
508 #ifdef JP
509     hooked_roff("この");
510 #else
511     if (lore_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
512         hooked_roff("Killing this");
513     } else {
514         hooked_roff("A kill of this");
515     }
516 #endif
517
518     display_monster_alignment(lore_ptr);
519     display_monster_kind(lore_ptr);
520     display_monster_exp(player_ptr, lore_ptr);
521 }
522
523 static void display_monster_escort_contents(lore_type *lore_ptr)
524 {
525     if (!lore_ptr->reinforce) {
526         return;
527     }
528
529     hooked_roff(_("護衛の構成は", "These escorts"));
530     if ((lore_ptr->flags1 & RF1_ESCORT) || (lore_ptr->flags1 & RF1_ESCORTS)) {
531         hooked_roff(_("少なくとも", " at the least"));
532     }
533
534 #ifdef JP
535 #else
536     hooked_roff(" contain ");
537 #endif
538
539     for (auto [r_idx, dd, ds] : lore_ptr->r_ptr->reinforces) {
540         auto is_reinforced = MonsterRace(r_idx).is_valid();
541         is_reinforced &= dd > 0;
542         is_reinforced &= ds > 0;
543         if (!is_reinforced) {
544             continue;
545         }
546
547         const auto *rf_ptr = &monraces_info[r_idx];
548         if (rf_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
549             hooked_roff(format(_("、%s", ", %s"), rf_ptr->name.data()));
550             continue;
551         }
552
553 #ifdef JP
554         hooked_roff(format("、 %dd%d 体の%s", dd, ds, rf_ptr->name.data()));
555 #else
556         auto plural = (dd * ds > 1);
557         GAME_TEXT name[MAX_NLEN];
558         strcpy(name, rf_ptr->name.data());
559         if (plural) {
560             plural_aux(name);
561         }
562         hooked_roff(format(",%dd%d %s", dd, ds, name));
563 #endif
564     }
565
566     hooked_roff(_("で成り立っている。", "."));
567 }
568
569 void display_monster_collective(lore_type *lore_ptr)
570 {
571     if ((lore_ptr->flags1 & RF1_ESCORT) || (lore_ptr->flags1 & RF1_ESCORTS) || lore_ptr->reinforce) {
572         hooked_roff(format(_("%^sは通常護衛を伴って現れる。", "%^s usually appears with escorts.  "), Who::who(lore_ptr->msex)));
573         display_monster_escort_contents(lore_ptr);
574     } else if (lore_ptr->flags1 & RF1_FRIENDS) {
575         hooked_roff(format(_("%^sは通常集団で現れる。", "%^s usually appears in groups.  "), Who::who(lore_ptr->msex)));
576     }
577 }
578
579 /*!
580  * @brief モンスターの発射に関する情報を表示するルーチン /
581  * Display monster launching information
582  * @param player_ptr プレイヤーへの参照ポインタ
583  * @param lore_ptr モンスターの思い出構造体への参照ポインタ
584  * @details
585  * This function should only be called when display/dump a recall of
586  * a monster.
587  * @todo max_blows はゲームの中核的なパラメータの1つなのでどこかのヘッダに定数宣言しておきたい
588  */
589 void display_monster_launching(PlayerType *player_ptr, lore_type *lore_ptr)
590 {
591     if (lore_ptr->ability_flags.has(MonsterAbilityType::ROCKET)) {
592         set_damage(player_ptr, lore_ptr, MonsterAbilityType::ROCKET, _("ロケット%sを発射する", "shoot a rocket%s"));
593         lore_ptr->vp[lore_ptr->vn] = lore_ptr->tmp_msg[lore_ptr->vn];
594         lore_ptr->color[lore_ptr->vn++] = TERM_UMBER;
595         lore_ptr->rocket = true;
596     }
597
598     if (lore_ptr->ability_flags.has_not(MonsterAbilityType::SHOOT)) {
599         return;
600     }
601
602     int p = -1; /* Position of SHOOT */
603     int n = 0; /* Number of blows */
604     const int max_blows = 4;
605     for (int m = 0; m < max_blows; m++) {
606         if (lore_ptr->r_ptr->blow[m].method != RaceBlowMethodType::NONE) {
607             n++;
608         } /* Count blows */
609
610         if (lore_ptr->r_ptr->blow[m].method == RaceBlowMethodType::SHOOT) {
611             p = m; /* Remember position */
612             break;
613         }
614     }
615
616     /* When full blows, use a first damage */
617     if (n == max_blows) {
618         p = 0;
619     }
620
621     if (p < 0) {
622         return;
623     }
624
625     if (know_armour(lore_ptr->r_idx, lore_ptr->know_everything)) {
626         sprintf(lore_ptr->tmp_msg[lore_ptr->vn], _("威力 %dd%d の射撃をする", "fire an arrow (Power:%dd%d)"), lore_ptr->r_ptr->blow[p].d_dice,
627             lore_ptr->r_ptr->blow[p].d_side);
628     } else {
629         sprintf(lore_ptr->tmp_msg[lore_ptr->vn], _("射撃をする", "fire an arrow"));
630     }
631
632     lore_ptr->vp[lore_ptr->vn] = lore_ptr->tmp_msg[lore_ptr->vn];
633     lore_ptr->color[lore_ptr->vn++] = TERM_UMBER;
634     lore_ptr->shoot = true;
635 }
636
637 void display_monster_sometimes(lore_type *lore_ptr)
638 {
639     if (lore_ptr->vn <= 0) {
640         return;
641     }
642
643     hooked_roff(format(_("%^sは", "%^s"), Who::who(lore_ptr->msex)));
644     for (int n = 0; n < lore_ptr->vn; n++) {
645 #ifdef JP
646         if (n != lore_ptr->vn - 1) {
647             jverb(lore_ptr->vp[n], lore_ptr->jverb_buf, JVERB_OR);
648             hook_c_roff(lore_ptr->color[n], lore_ptr->jverb_buf);
649             hook_c_roff(lore_ptr->color[n], "り");
650             hooked_roff("、");
651         } else {
652             hook_c_roff(lore_ptr->color[n], lore_ptr->vp[n]);
653         }
654 #else
655         if (n == 0) {
656             hooked_roff(" may ");
657         } else if (n < lore_ptr->vn - 1) {
658             hooked_roff(", ");
659         } else {
660             hooked_roff(" or ");
661         }
662
663         hook_c_roff(lore_ptr->color[n], lore_ptr->vp[n]);
664 #endif
665     }
666
667     hooked_roff(_("ことがある。", ".  "));
668 }
669
670 void display_monster_guardian(lore_type *lore_ptr)
671 {
672     bool is_kingpin = (lore_ptr->flags1 & RF1_QUESTOR) != 0;
673     is_kingpin &= lore_ptr->r_ptr->r_sights > 0;
674     is_kingpin &= lore_ptr->r_ptr->max_num > 0;
675     is_kingpin &= (lore_ptr->r_idx == MonsterRaceId::OBERON) || (lore_ptr->r_idx == MonsterRaceId::SERPENT);
676     if (is_kingpin) {
677         hook_c_roff(TERM_VIOLET, _("あなたはこのモンスターを殺したいという強い欲望を感じている...", "You feel an intense desire to kill this monster...  "));
678     } else if (lore_ptr->flags7 & RF7_GUARDIAN) {
679         hook_c_roff(TERM_L_RED, _("このモンスターはダンジョンの主である。", "This monster is the master of a dungeon."));
680     }
681
682     hooked_roff("\n");
683 }