OSDN Git Service

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