OSDN Git Service

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