OSDN Git Service

[Refactor] #40014 Separated display_monster_collective() from from process_monster_lo...
[hengband/hengband.git] / src / view / display-lore.c
1 /*!
2  * @brief モンスターの思い出を表示する処理
3  * @date 2020/06/09
4  * @author Hourier
5  */
6
7 #include "view/display-lore.h"
8 #include "lore/monster-lore.h"
9 #include "monster-race/race-flags1.h"
10 #include "monster-race/race-flags2.h"
11 #include "monster-race/race-flags3.h"
12 #include "monster-race/race-indice-types.h"
13 #include "term/term-color-types.h"
14 #include "world/world.h"
15
16 /*!
17  * @brief モンスター情報のヘッダを記述する
18  * Hack -- Display the "name" and "attr/chars" of a monster race
19  * @param r_idx モンスターの種族ID
20  * @return なし
21  */
22 void roff_top(MONRACE_IDX r_idx)
23 {
24     monster_race *r_ptr = &r_info[r_idx];
25     char c1 = r_ptr->d_char;
26     char c2 = r_ptr->x_char;
27
28     TERM_COLOR a1 = r_ptr->d_attr;
29     TERM_COLOR a2 = r_ptr->x_attr;
30
31     Term_erase(0, 0, 255);
32     Term_gotoxy(0, 0);
33
34 #ifdef JP
35 #else
36     if (!(r_ptr->flags1 & RF1_UNIQUE)) {
37         Term_addstr(-1, TERM_WHITE, "The ");
38     }
39 #endif
40
41     Term_addstr(-1, TERM_WHITE, (r_name + r_ptr->name));
42
43     Term_addstr(-1, TERM_WHITE, " ('");
44     Term_add_bigch(a1, c1);
45     Term_addstr(-1, TERM_WHITE, "')");
46
47     Term_addstr(-1, TERM_WHITE, "/('");
48     Term_add_bigch(a2, c2);
49     Term_addstr(-1, TERM_WHITE, "'):");
50
51     if (!current_world_ptr->wizard)
52         return;
53
54     char buf[16];
55     sprintf(buf, "%d", r_idx);
56     Term_addstr(-1, TERM_WHITE, " (");
57     Term_addstr(-1, TERM_L_BLUE, buf);
58     Term_addch(TERM_WHITE, ')');
59 }
60
61 /*!
62  * @brief  モンスター情報の表示と共に画面を一時消去するサブルーチン /
63  * Hack -- describe the given monster race at the top of the screen
64  * @param r_idx モンスターの種族ID
65  * @param mode 表示オプション
66  * @return なし
67  */
68 void screen_roff(player_type *player_ptr, MONRACE_IDX r_idx, BIT_FLAGS mode)
69 {
70     msg_erase();
71     Term_erase(0, 1, 255);
72     hook_c_roff = c_roff;
73     process_monster_lore(player_ptr, r_idx, mode);
74     roff_top(r_idx);
75 }
76
77 /*!
78  * @brief モンスター情報の現在のウィンドウに表示する /
79  * Hack -- describe the given monster race in the current "term" window
80  * @param r_idx モンスターの種族ID
81  * @return なし
82  */
83 void display_roff(player_type *player_ptr)
84 {
85     for (int y = 0; y < Term->hgt; y++) {
86         Term_erase(0, y, 255);
87     }
88
89     Term_gotoxy(0, 1);
90     hook_c_roff = c_roff;
91     MONRACE_IDX r_idx = player_ptr->monster_race_idx;
92     process_monster_lore(player_ptr, r_idx, 0);
93     roff_top(r_idx);
94 }
95
96 /*!
97  * @brief モンスター詳細情報を自動スポイラー向けに出力する /
98  * Hack -- output description of the given monster race
99  * @param r_idx モンスターの種族ID
100  * @param roff_func 出力処理を行う関数ポインタ
101  * @return なし
102  */
103 void output_monster_spoiler(player_type *player_ptr, MONRACE_IDX r_idx, void (*roff_func)(TERM_COLOR attr, concptr str))
104 {
105     hook_c_roff = roff_func;
106     process_monster_lore(player_ptr, r_idx, 0x03);
107 }
108
109 static bool display_kill_unique(lore_type *lore_ptr)
110 {
111     if ((lore_ptr->flags1 & RF1_UNIQUE) == 0)
112         return FALSE;
113
114     bool dead = (lore_ptr->r_ptr->max_num == 0);
115     if (lore_ptr->r_ptr->r_deaths) {
116         hooked_roff(format(_("%^sはあなたの先祖を %d 人葬っている", "%^s has slain %d of your ancestors"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->r_deaths));
117
118         if (dead) {
119             hooked_roff(
120                 _(format("が、すでに仇討ちは果たしている!"), format(", but you have avenged %s!  ", plural(lore_ptr->r_ptr->r_deaths, "him", "them"))));
121         } else {
122             hooked_roff(
123                 _(format("のに、まだ仇討ちを果たしていない。"), format(", who %s unavenged.  ", plural(lore_ptr->r_ptr->r_deaths, "remains", "remain"))));
124         }
125
126         hooked_roff("\n");
127     } else if (dead) {
128         hooked_roff(_("あなたはこの仇敵をすでに葬り去っている。", "You have slain this foe.  "));
129         hooked_roff("\n");
130     }
131
132     return TRUE;
133 }
134
135 static bool display_killed(lore_type *lore_ptr)
136 {
137     if (lore_ptr->r_ptr->r_deaths == 0)
138         return FALSE;
139
140     hooked_roff(_(format("このモンスターはあなたの先祖を %d 人葬っている", lore_ptr->r_ptr->r_deaths),
141         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"))));
142
143     if (lore_ptr->r_ptr->r_pkills) {
144         hooked_roff(format(_("が、あなたはこのモンスターを少なくとも %d 体は倒している。", "and you have exterminated at least %d of the creatures.  "),
145             lore_ptr->r_ptr->r_pkills));
146     } else if (lore_ptr->r_ptr->r_tkills) {
147         hooked_roff(format(
148             _("が、あなたの先祖はこのモンスターを少なくとも %d 体は倒している。", "and your ancestors have exterminated at least %d of the creatures.  "),
149             lore_ptr->r_ptr->r_tkills));
150     } else {
151         hooked_roff(format(_("が、まだ%sを倒したことはない。", "and %s is not ever known to have been defeated.  "), wd_he[lore_ptr->msex]));
152     }
153
154     hooked_roff("\n");
155     return TRUE;
156 }
157
158 void display_kill_numbers(lore_type *lore_ptr)
159 {
160     if ((lore_ptr->mode & 0x02) != 0)
161         return;
162
163     if (display_kill_unique(lore_ptr))
164         return;
165
166     if (display_killed(lore_ptr))
167         return;
168
169     if (lore_ptr->r_ptr->r_pkills) {
170         hooked_roff(format(
171             _("あなたはこのモンスターを少なくとも %d 体は殺している。", "You have killed at least %d of these creatures.  "), lore_ptr->r_ptr->r_pkills));
172     } else if (lore_ptr->r_ptr->r_tkills) {
173         hooked_roff(format(_("あなたの先祖はこのモンスターを少なくとも %d 体は殺している。", "Your ancestors have killed at least %d of these creatures.  "),
174             lore_ptr->r_ptr->r_tkills));
175     } else {
176         hooked_roff(_("このモンスターを倒したことはない。", "No battles to the death are recalled.  "));
177     }
178
179     hooked_roff("\n");
180 }
181
182 /*!
183  * @brief どこに出没するかを表示する
184  * @param lore_ptr モンスターの思い出構造体への参照ポインタ
185  * @return たぬきならFALSE、それ以外はTRUE
186  */
187 bool display_where_to_appear(lore_type *lore_ptr)
188 {
189     lore_ptr->old = FALSE;
190     if (lore_ptr->r_ptr->level == 0) {
191         hooked_roff(format(_("%^sは町に住み", "%^s lives in the town"), wd_he[lore_ptr->msex]));
192         lore_ptr->old = TRUE;
193     } else if (lore_ptr->r_ptr->r_tkills || lore_ptr->know_everything) {
194         if (depth_in_feet) {
195             hooked_roff(format(
196                 _("%^sは通常地下 %d フィートで出現し", "%^s is normally found at depths of %d feet"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->level * 50));
197         } else {
198             hooked_roff(format(_("%^sは通常地下 %d 階で出現し", "%^s is normally found on dungeon level %d"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->level));
199         }
200
201         lore_ptr->old = TRUE;
202     }
203
204     if (lore_ptr->r_idx == MON_CHAMELEON) {
205         hooked_roff(_("、他のモンスターに化ける。", "and can take the shape of other monster."));
206         return FALSE;
207     }
208
209     if (lore_ptr->old) {
210         hooked_roff(_("、", ", and "));
211     } else {
212         hooked_roff(format(_("%^sは", "%^s "), wd_he[lore_ptr->msex]));
213         lore_ptr->old = TRUE;
214     }
215
216     return TRUE;
217 }
218
219 void display_monster_move(lore_type *lore_ptr)
220 {
221 #ifdef JP
222 #else
223     hooked_roff("moves");
224 #endif
225
226     display_random_move(lore_ptr);
227     if (lore_ptr->speed > 110) {
228         if (lore_ptr->speed > 139)
229             hook_c_roff(TERM_RED, _("信じ難いほど", " incredibly"));
230         else if (lore_ptr->speed > 134)
231             hook_c_roff(TERM_ORANGE, _("猛烈に", " extremely"));
232         else if (lore_ptr->speed > 129)
233             hook_c_roff(TERM_ORANGE, _("非常に", " very"));
234         else if (lore_ptr->speed > 124)
235             hook_c_roff(TERM_UMBER, _("かなり", " fairly"));
236         else if (lore_ptr->speed < 120)
237             hook_c_roff(TERM_L_UMBER, _("やや", " somewhat"));
238         hook_c_roff(TERM_L_RED, _("素早く", " quickly"));
239     } else if (lore_ptr->speed < 110) {
240         if (lore_ptr->speed < 90)
241             hook_c_roff(TERM_L_GREEN, _("信じ難いほど", " incredibly"));
242         else if (lore_ptr->speed < 95)
243             hook_c_roff(TERM_BLUE, _("非常に", " very"));
244         else if (lore_ptr->speed < 100)
245             hook_c_roff(TERM_BLUE, _("かなり", " fairly"));
246         else if (lore_ptr->speed > 104)
247             hook_c_roff(TERM_GREEN, _("やや", " somewhat"));
248         hook_c_roff(TERM_L_BLUE, _("ゆっくりと", " slowly"));
249     } else {
250         hooked_roff(_("普通の速さで", " at normal speed"));
251     }
252
253 #ifdef JP
254     hooked_roff("動いている");
255 #endif
256 }
257
258 void display_random_move(lore_type *lore_ptr)
259 {
260     if (((lore_ptr->flags1 & RF1_RAND_50) == 0) && ((lore_ptr->flags1 & RF1_RAND_25) == 0))
261         return;
262
263     if ((lore_ptr->flags1 & RF1_RAND_50) && (lore_ptr->flags1 & RF1_RAND_25)) {
264         hooked_roff(_("かなり", " extremely"));
265     } else if (lore_ptr->flags1 & RF1_RAND_50) {
266         hooked_roff(_("幾分", " somewhat"));
267     } else if (lore_ptr->flags1 & RF1_RAND_25) {
268         hooked_roff(_("少々", " a bit"));
269     }
270
271     hooked_roff(_("不規則に", " erratically"));
272     if (lore_ptr->speed != 110)
273         hooked_roff(_("、かつ", ", and"));
274 }
275
276 void display_monster_never_move(lore_type *lore_ptr)
277 {
278     if ((lore_ptr->flags1 & RF1_NEVER_MOVE) == 0)
279         return;
280
281     if (lore_ptr->old) {
282         hooked_roff(_("、しかし", ", but "));
283     } else {
284         hooked_roff(format(_("%^sは", "%^s "), wd_he[lore_ptr->msex]));
285         lore_ptr->old = TRUE;
286     }
287
288     hooked_roff(_("侵入者を追跡しない", "does not deign to chase intruders"));
289 }
290
291 void display_monster_kind(lore_type *lore_ptr)
292 {
293     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)) {
294         hooked_roff(_("モンスター", " creature"));
295         return;
296     }
297
298     if (lore_ptr->flags3 & RF3_DRAGON)
299         hook_c_roff(TERM_ORANGE, _("ドラゴン", " dragon"));
300
301     if (lore_ptr->flags3 & RF3_DEMON)
302         hook_c_roff(TERM_VIOLET, _("デーモン", " demon"));
303
304     if (lore_ptr->flags3 & RF3_GIANT)
305         hook_c_roff(TERM_L_UMBER, _("巨人", " giant"));
306
307     if (lore_ptr->flags3 & RF3_TROLL)
308         hook_c_roff(TERM_BLUE, _("トロル", " troll"));
309
310     if (lore_ptr->flags3 & RF3_ORC)
311         hook_c_roff(TERM_UMBER, _("オーク", " orc"));
312
313     if (lore_ptr->flags2 & RF2_HUMAN)
314         hook_c_roff(TERM_L_WHITE, _("人間", " human"));
315
316     if (lore_ptr->flags2 & RF2_QUANTUM)
317         hook_c_roff(TERM_VIOLET, _("量子生物", " quantum creature"));
318
319     if (lore_ptr->flags3 & RF3_ANGEL)
320         hook_c_roff(TERM_YELLOW, _("天使", " angel"));
321 }
322
323 void display_monster_alignment(lore_type *lore_ptr)
324 {
325     if (lore_ptr->flags2 & RF2_ELDRITCH_HORROR)
326         hook_c_roff(TERM_VIOLET, _("狂気を誘う", " sanity-blasting"));
327
328     if (lore_ptr->flags3 & RF3_ANIMAL)
329         hook_c_roff(TERM_L_GREEN, _("自然界の", " natural"));
330
331     if (lore_ptr->flags3 & RF3_EVIL)
332         hook_c_roff(TERM_L_DARK, _("邪悪なる", " evil"));
333
334     if (lore_ptr->flags3 & RF3_GOOD)
335         hook_c_roff(TERM_YELLOW, _("善良な", " good"));
336
337     if (lore_ptr->flags3 & RF3_UNDEAD)
338         hook_c_roff(TERM_VIOLET, _("アンデッドの", " undead"));
339
340     if (lore_ptr->flags3 & RF3_AMBERITE)
341         hook_c_roff(TERM_VIOLET, _("アンバーの王族の", " Amberite"));
342 }
343
344 void display_monster_exp(player_type *player_ptr, lore_type *lore_ptr)
345 {
346 #ifdef JP
347     hooked_roff("を倒すことは");
348 #endif
349     long exp_integer = (long)lore_ptr->r_ptr->mexp * lore_ptr->r_ptr->level / (player_ptr->max_plv + 2) * 3 / 2;
350     long exp_decimal
351         = ((((long)lore_ptr->r_ptr->mexp * lore_ptr->r_ptr->level % (player_ptr->max_plv + 2) * 3 / 2) * (long)1000 / (player_ptr->max_plv + 2) + 5) / 10);
352
353 #ifdef JP
354     hooked_roff(format(" %d レベルのキャラクタにとって 約%ld.%02ld ポイントの経験となる。", player_ptr->lev, (long)exp_integer, (long)exp_decimal));
355 #else
356     hooked_roff(format(" is worth about %ld.%02ld point%s", (long)exp_integer, (long)exp_decimal, ((exp_integer == 1) && (exp_decimal == 0)) ? "" : "s"));
357
358     char *ordinal;
359     ordinal = "th";
360     exp_integer = player_ptr->lev % 10;
361     if ((player_ptr->lev / 10) != 1) {
362         if (exp_integer == 1)
363             ordinal = "st";
364         else if (exp_integer == 2)
365             ordinal = "nd";
366         else if (exp_integer == 3)
367             ordinal = "rd";
368     }
369
370     char *vowel;
371     vowel = "";
372     exp_integer = player_ptr->lev;
373     if ((exp_integer == 8) || (exp_integer == 11) || (exp_integer == 18))
374         vowel = "n";
375
376     hooked_roff(format(" for a%s %lu%s level character.  ", vowel, (long)exp_integer, ordinal));
377 #endif
378 }
379
380 void display_monster_aura(lore_type *lore_ptr)
381 {
382     if ((lore_ptr->flags2 & RF2_AURA_FIRE) && (lore_ptr->flags2 & RF2_AURA_ELEC) && (lore_ptr->flags3 & RF3_AURA_COLD))
383         hook_c_roff(
384             TERM_VIOLET, format(_("%^sは炎と氷とスパークに包まれている。", "%^s is surrounded by flames, ice and electricity.  "), wd_he[lore_ptr->msex]));
385     else if ((lore_ptr->flags2 & RF2_AURA_FIRE) && (lore_ptr->flags2 & RF2_AURA_ELEC))
386         hook_c_roff(TERM_L_RED, format(_("%^sは炎とスパークに包まれている。", "%^s is surrounded by flames and electricity.  "), wd_he[lore_ptr->msex]));
387     else if ((lore_ptr->flags2 & RF2_AURA_FIRE) && (lore_ptr->flags3 & RF3_AURA_COLD))
388         hook_c_roff(TERM_BLUE, format(_("%^sは炎と氷に包まれている。", "%^s is surrounded by flames and ice.  "), wd_he[lore_ptr->msex]));
389     else if ((lore_ptr->flags3 & RF3_AURA_COLD) && (lore_ptr->flags2 & RF2_AURA_ELEC))
390         hook_c_roff(TERM_L_GREEN, format(_("%^sは氷とスパークに包まれている。", "%^s is surrounded by ice and electricity.  "), wd_he[lore_ptr->msex]));
391     else if (lore_ptr->flags2 & RF2_AURA_FIRE)
392         hook_c_roff(TERM_RED, format(_("%^sは炎に包まれている。", "%^s is surrounded by flames.  "), wd_he[lore_ptr->msex]));
393     else if (lore_ptr->flags3 & RF3_AURA_COLD)
394         hook_c_roff(TERM_BLUE, format(_("%^sは氷に包まれている。", "%^s is surrounded by ice.  "), wd_he[lore_ptr->msex]));
395     else if (lore_ptr->flags2 & RF2_AURA_ELEC)
396         hook_c_roff(TERM_L_BLUE, format(_("%^sはスパークに包まれている。", "%^s is surrounded by electricity.  "), wd_he[lore_ptr->msex]));
397 }
398
399 void display_lore_this(player_type *player_ptr, lore_type *lore_ptr)
400 {
401     if ((lore_ptr->r_ptr->r_tkills == 0) && !lore_ptr->know_everything)
402         return;
403
404 #ifdef JP
405     hooked_roff("この");
406 #else
407     if (lore_ptr->flags1 & RF1_UNIQUE) {
408         hooked_roff("Killing this");
409     } else {
410         hooked_roff("A kill of this");
411     }
412 #endif
413
414     display_monster_alignment(lore_ptr);
415     display_monster_kind(lore_ptr);
416     display_monster_exp(player_ptr, lore_ptr);
417 }
418
419 static void display_monster_escort_contents(lore_type *lore_ptr)
420 {
421     if (!lore_ptr->reinforce)
422         return;
423
424     hooked_roff(_("護衛の構成は", "These escorts"));
425     if ((lore_ptr->flags1 & RF1_ESCORT) || (lore_ptr->flags1 & RF1_ESCORTS)) {
426         hooked_roff(_("少なくとも", " at the least"));
427     }
428
429 #ifdef JP
430 #else
431     hooked_roff(" contain ");
432 #endif
433
434     for (int n = 0; n < A_MAX; n++) {
435         bool is_reinforced = lore_ptr->r_ptr->reinforce_id[n] > 0;
436         is_reinforced &= lore_ptr->r_ptr->reinforce_dd[n] > 0;
437         is_reinforced &= lore_ptr->r_ptr->reinforce_ds[n] > 0;
438         if (!is_reinforced)
439             continue;
440
441         monster_race *rf_ptr = &r_info[lore_ptr->r_ptr->reinforce_id[n]];
442         if (rf_ptr->flags1 & RF1_UNIQUE) {
443             hooked_roff(format(_("、%s", ", %s"), r_name + rf_ptr->name));
444             continue;
445         }
446
447 #ifdef JP
448         hooked_roff(format("、 %dd%d 体の%s", lore_ptr->r_ptr->reinforce_dd[n], lore_ptr->r_ptr->reinforce_ds[n], r_name + rf_ptr->name));
449 #else
450         bool plural = (lore_ptr->r_ptr->reinforce_dd[n] * lore_ptr->r_ptr->reinforce_ds[n] > 1);
451         GAME_TEXT name[MAX_NLEN];
452         strcpy(name, r_name + rf_ptr->name);
453         if (plural)
454             plural_aux(name);
455         hooked_roff(format(",%dd%d %s", lore_ptr->r_ptr->reinforce_dd[n], lore_ptr->r_ptr->reinforce_ds[n], name));
456 #endif
457     }
458
459     hooked_roff(_("で成り立っている。", "."));
460 }
461
462 void display_monster_collective(lore_type *lore_ptr)
463 {
464     if ((lore_ptr->flags1 & RF1_ESCORT) || (lore_ptr->flags1 & RF1_ESCORTS) || lore_ptr->reinforce) {
465         hooked_roff(format(_("%^sは通常護衛を伴って現れる。", "%^s usually appears with escorts.  "), wd_he[lore_ptr->msex]));
466         display_monster_escort_contents(lore_ptr);
467     }
468     else if (lore_ptr->flags1 & RF1_FRIENDS) {
469         hooked_roff(format(_("%^sは通常集団で現れる。", "%^s usually appears in groups.  "), wd_he[lore_ptr->msex]));
470     }
471 }