OSDN Git Service

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