OSDN Git Service

[Refactor] #40413 Separated input-key-acceptor.c/h from util.c/h
[hengband/hengband.git] / src / knowledge / knowledge-monsters.c
1 /*!
2  * todo サブルーチン分割を行うと行数が膨れ上がりそう、再分割も検討すべし
3  * @brief 既知のモンスターに関する情報を表示する
4  * @date 2020/04/24
5  * @author Hourier
6  */
7
8 #include "knowledge/knowledge-monsters.h"
9 #include "core/show-file.h"
10 #include "core/sort.h"
11 #include "core/stuff-handler.h"
12 #include "floor/floor.h"
13 #include "game-option/cheat-options.h"
14 #include "game-option/special-options.h"
15 #include "io-dump/dump-util.h"
16 #include "io/input-key-acceptor.h"
17 #include "knowledge/monster-group-table.h"
18 #include "locale/english.h"
19 #include "monster-race/race-flags1.h"
20 #include "monster-race/race-flags3.h"
21 #include "monster-race/race-flags7.h"
22 #include "monster/monster-describer.h"
23 #include "monster/monster-description-types.h"
24 #include "monster/monster-info.h"
25 #include "monster/monster-status.h"
26 #include "monster/smart-learn-types.h"
27 #include "term/term-color-types.h"
28 #include "util/angband-files.h"
29 #include "view/display-main-window.h" // 暫定、後で消す.
30 #include "view/display-lore.h"
31 #include "view/display-monster-status.h"
32 #include "world/world.h"
33
34  /*
35   * todo 引数と戻り値について追記求む
36   * Build a list of monster indexes in the given group.
37   *
38   * mode & 0x01 : check for non-empty group
39   * mode & 0x02 : visual operation only
40
41   * @param creature_ptr プレーヤーへの参照ポインタ
42   * @param grp_cur ???
43   * @param mon_idx[] ???
44   * @param mode ???
45   * @return The number of monsters in the group
46   */
47 static IDX collect_monsters(player_type *creature_ptr, IDX grp_cur, IDX mon_idx[], BIT_FLAGS8 mode)
48 {
49         concptr group_char = monster_group_char[grp_cur];
50         bool grp_unique = (monster_group_char[grp_cur] == (char *)-1L);
51         bool grp_riding = (monster_group_char[grp_cur] == (char *)-2L);
52         bool grp_wanted = (monster_group_char[grp_cur] == (char *)-3L);
53         bool grp_amberite = (monster_group_char[grp_cur] == (char *)-4L);
54
55         IDX mon_cnt = 0;
56         for (IDX i = 0; i < max_r_idx; i++)
57         {
58                 monster_race *r_ptr = &r_info[i];
59                 if (!r_ptr->name) continue;
60                 if (!(mode & 0x02) && !cheat_know && !r_ptr->r_sights) continue;
61
62                 if (grp_unique)
63                 {
64                         if (!(r_ptr->flags1 & RF1_UNIQUE)) continue;
65                 }
66                 else if (grp_riding)
67                 {
68                         if (!(r_ptr->flags7 & RF7_RIDING)) continue;
69                 }
70                 else if (grp_wanted)
71                 {
72                         bool wanted = FALSE;
73                         for (int j = 0; j < MAX_BOUNTY; j++)
74                         {
75                                 if (current_world_ptr->bounty_r_idx[j] == i || current_world_ptr->bounty_r_idx[j] - 10000 == i ||
76                                         (creature_ptr->today_mon && creature_ptr->today_mon == i))
77                                 {
78                                         wanted = TRUE;
79                                         break;
80                                 }
81                         }
82
83                         if (!wanted) continue;
84                 }
85                 else if (grp_amberite)
86                 {
87                         if (!(r_ptr->flags3 & RF3_AMBERITE)) continue;
88                 }
89                 else
90                 {
91                         if (!angband_strchr(group_char, r_ptr->d_char)) continue;
92                 }
93
94                 mon_idx[mon_cnt++] = i;
95                 if (mode & 0x01) break;
96         }
97
98         mon_idx[mon_cnt] = -1;
99         int dummy_why;
100         ang_sort(mon_idx, &dummy_why, mon_cnt, ang_sort_comp_monster_level, ang_sort_swap_hook);
101         return mon_cnt;
102 }
103
104
105 /*!
106  * @brief 現在のペットを表示するコマンドのメインルーチン /
107  * Display current pets
108  * @param creature_ptr プレーヤーへの参照ポインタ
109  * @return なし
110  */
111 void do_cmd_knowledge_pets(player_type *creature_ptr)
112 {
113         FILE *fff = NULL;
114         GAME_TEXT file_name[FILE_NAME_SIZE];
115         if (!open_temporary_file(&fff, file_name)) return;
116
117         monster_type *m_ptr;
118         GAME_TEXT pet_name[MAX_NLEN];
119         int t_friends = 0;
120         for (int i = creature_ptr->current_floor_ptr->m_max - 1; i >= 1; i--)
121         {
122                 m_ptr = &creature_ptr->current_floor_ptr->m_list[i];
123                 if (!monster_is_valid(m_ptr)) continue;
124                 if (!is_pet(m_ptr)) continue;
125
126                 t_friends++;
127                 monster_desc(creature_ptr, pet_name, m_ptr, MD_ASSUME_VISIBLE | MD_INDEF_VISIBLE);
128                 fprintf(fff, "%s (%s)\n", pet_name, look_mon_desc(m_ptr, 0x00));
129         }
130
131         int show_upkeep = calculate_upkeep(creature_ptr);
132
133         fprintf(fff, "----------------------------------------------\n");
134 #ifdef JP
135         fprintf(fff, "    合計: %d 体のペット\n", t_friends);
136 #else
137         fprintf(fff, "   Total: %d pet%s.\n", t_friends, (t_friends == 1 ? "" : "s"));
138 #endif
139         fprintf(fff, _(" 維持コスト: %d%% MP\n", "   Upkeep: %d%% mana.\n"), show_upkeep);
140
141         angband_fclose(fff);
142         (void)show_file(creature_ptr, TRUE, file_name, _("現在のペット", "Current Pets"), 0, 0);
143         fd_kill(file_name);
144 }
145
146
147 /*!
148  * @brief 現在までに倒したモンスターを表示するコマンドのメインルーチン /
149  * @param creature_ptr プレーヤーへの参照ポインタ
150  * Total kill count
151  * @return なし
152  * @note the player ghosts are ignored.
153  */
154 void do_cmd_knowledge_kill_count(player_type *creature_ptr)
155 {
156         FILE *fff = NULL;
157         GAME_TEXT file_name[FILE_NAME_SIZE];
158         if (!open_temporary_file(&fff, file_name)) return;
159
160         MONRACE_IDX *who;
161         C_MAKE(who, max_r_idx, MONRACE_IDX);
162         s32b total = 0;
163         for (int kk = 1; kk < max_r_idx; kk++)
164         {
165                 monster_race *r_ptr = &r_info[kk];
166
167                 if (r_ptr->flags1 & (RF1_UNIQUE))
168                 {
169                         bool dead = (r_ptr->max_num == 0);
170
171                         if (dead)
172                         {
173                                 total++;
174                         }
175                 }
176                 else
177                 {
178                         MONSTER_NUMBER this_monster = r_ptr->r_pkills;
179
180                         if (this_monster > 0)
181                         {
182                                 total += this_monster;
183                         }
184                 }
185         }
186
187         if (total < 1)
188                 fprintf(fff, _("あなたはまだ敵を倒していない。\n\n", "You have defeated no enemies yet.\n\n"));
189         else
190 #ifdef JP
191                 fprintf(fff, "あなたは%ld体の敵を倒している。\n\n", (long int)total);
192 #else
193                 fprintf(fff, "You have defeated %ld %s.\n\n", (long int)total, (total == 1) ? "enemy" : "enemies");
194 #endif
195
196         total = 0;
197         int n = 0;
198         for (MONRACE_IDX i = 1; i < max_r_idx; i++)
199         {
200                 monster_race *r_ptr = &r_info[i];
201                 if (r_ptr->name) who[n++] = i;
202         }
203
204         u16b why = 2;
205         ang_sort(who, &why, n, ang_sort_comp_hook, ang_sort_swap_hook);
206         for (int k = 0; k < n; k++)
207         {
208                 monster_race *r_ptr = &r_info[who[k]];
209                 if (r_ptr->flags1 & (RF1_UNIQUE))
210                 {
211                         bool dead = (r_ptr->max_num == 0);
212                         if (dead)
213                         {
214                                 fprintf(fff, "     %s\n", (r_name + r_ptr->name));
215                                 total++;
216                         }
217
218                         continue;
219                 }
220
221                 MONSTER_NUMBER this_monster = r_ptr->r_pkills;
222                 if (this_monster <= 0) continue;
223
224 #ifdef JP
225                 if (angband_strchr("pt", r_ptr->d_char))
226                         fprintf(fff, "     %3d 人の %s\n", (int)this_monster, r_name + r_ptr->name);
227                 else
228                         fprintf(fff, "     %3d 体の %s\n", (int)this_monster, r_name + r_ptr->name);
229 #else
230                 if (this_monster < 2)
231                 {
232                         if (angband_strstr(r_name + r_ptr->name, "coins"))
233                         {
234                                 fprintf(fff, "     1 pile of %s\n", (r_name + r_ptr->name));
235                         }
236                         else
237                         {
238                                 fprintf(fff, "     1 %s\n", (r_name + r_ptr->name));
239                         }
240                 }
241                 else
242                 {
243                         char ToPlural[80];
244                         strcpy(ToPlural, (r_name + r_ptr->name));
245                         plural_aux(ToPlural);
246                         fprintf(fff, "     %d %s\n", this_monster, ToPlural);
247                 }
248 #endif
249                 total += this_monster;
250         }
251
252         fprintf(fff, "----------------------------------------------\n");
253 #ifdef JP
254         fprintf(fff, "    合計: %lu 体を倒した。\n", (unsigned long int)total);
255 #else
256         fprintf(fff, "   Total: %lu creature%s killed.\n", (unsigned long int)total, (total == 1 ? "" : "s"));
257 #endif
258
259         C_KILL(who, max_r_idx, s16b);
260         angband_fclose(fff);
261         (void)show_file(creature_ptr, TRUE, file_name, _("倒した敵の数", "Kill Count"), 0, 0);
262         fd_kill(file_name);
263 }
264
265
266 /*
267  * Display the monsters in a group.
268  */
269 static void display_monster_list(int col, int row, int per_page, s16b mon_idx[], int mon_cur, int mon_top, bool visual_only)
270 {
271         int i;
272         for (i = 0; i < per_page && (mon_idx[mon_top + i] >= 0); i++)
273         {
274                 TERM_COLOR attr;
275                 MONRACE_IDX r_idx = mon_idx[mon_top + i];
276                 monster_race *r_ptr = &r_info[r_idx];
277                 attr = ((i + mon_top == mon_cur) ? TERM_L_BLUE : TERM_WHITE);
278                 c_prt(attr, (r_name + r_ptr->name), row + i, col);
279                 if (per_page == 1)
280                 {
281                         c_prt(attr, format("%02x/%02x", r_ptr->x_attr, r_ptr->x_char), row + i, (current_world_ptr->wizard || visual_only) ? 56 : 61);
282                 }
283
284                 if (current_world_ptr->wizard || visual_only)
285                 {
286                         c_prt(attr, format("%d", r_idx), row + i, 62);
287                 }
288
289                 Term_erase(69, row + i, 255);
290                 Term_queue_bigchar(use_bigtile ? 69 : 70, row + i, r_ptr->x_attr, r_ptr->x_char, 0, 0);
291                 if (!visual_only)
292                 {
293                         if (!(r_ptr->flags1 & RF1_UNIQUE))
294                                 put_str(format("%5d", r_ptr->r_pkills), row + i, 73);
295                         else
296                                 c_put_str((r_ptr->max_num == 0 ? TERM_L_DARK : TERM_WHITE),
297                                 (r_ptr->max_num == 0 ? _("死亡", " dead") : _("生存", "alive")), row + i, 74);
298                 }
299         }
300
301         for (; i < per_page; i++)
302         {
303                 Term_erase(col, row + i, 255);
304         }
305 }
306
307
308 /*
309  * todo 引数の詳細について加筆求む
310  * Display known monsters.
311  * @param creature_ptr プレーヤーへの参照ポインタ
312  * @param need_redraw 画面の再描画が必要な時TRUE
313  * @param visual_only ???
314  * @param direct_r_idx モンスターID
315  * @return なし
316  */
317 void do_cmd_knowledge_monsters(player_type *creature_ptr, bool *need_redraw, bool visual_only, IDX direct_r_idx)
318 {
319         TERM_LEN wid, hgt;
320         Term_get_size(&wid, &hgt);
321         IDX *mon_idx;
322         C_MAKE(mon_idx, max_r_idx, MONRACE_IDX);
323
324         int max = 0;
325         IDX grp_cnt = 0;
326         IDX grp_idx[100];
327         IDX mon_cnt;
328         bool visual_list = FALSE;
329         TERM_COLOR attr_top = 0;
330         byte char_left = 0;
331         BIT_FLAGS8 mode;
332         int browser_rows = hgt - 8;
333         if (direct_r_idx < 0)
334         {
335                 mode = visual_only ? 0x03 : 0x01;
336                 int len;
337                 for (IDX i = 0; monster_group_text[i] != NULL; i++)
338                 {
339                         len = strlen(monster_group_text[i]);
340                         if (len > max) max = len;
341
342                         if ((monster_group_char[i] == ((char *)-1L)) || collect_monsters(creature_ptr, i, mon_idx, mode))
343                         {
344                                 grp_idx[grp_cnt++] = i;
345                         }
346                 }
347
348                 mon_cnt = 0;
349         }
350         else
351         {
352                 mon_idx[0] = direct_r_idx;
353                 mon_cnt = 1;
354                 mon_idx[1] = -1;
355
356                 (void)visual_mode_command('v', &visual_list, browser_rows - 1, wid - (max + 3),
357                         &attr_top, &char_left, &r_info[direct_r_idx].x_attr, &r_info[direct_r_idx].x_char, need_redraw);
358         }
359
360         grp_idx[grp_cnt] = -1;
361         mode = visual_only ? 0x02 : 0x00;
362         IDX old_grp_cur = -1;
363         IDX grp_cur = 0;
364         IDX grp_top = 0;
365         IDX mon_cur = 0;
366         IDX mon_top = 0;
367         int column = 0;
368         bool flag = FALSE;
369         bool redraw = TRUE;
370         while (!flag)
371         {
372                 if (redraw)
373                 {
374                         clear_from(0);
375                         prt(format(_("%s - モンスター", "%s - monsters"), !visual_only ? _("知識", "Knowledge") : _("表示", "Visuals")), 2, 0);
376                         if (direct_r_idx < 0) prt(_("グループ", "Group"), 4, 0);
377                         prt(_("名前", "Name"), 4, max + 3);
378                         if (current_world_ptr->wizard || visual_only) prt("Idx", 4, 62);
379                         prt(_("文字", "Sym"), 4, 67);
380                         if (!visual_only) prt(_("殺害数", "Kills"), 4, 72);
381
382                         for (IDX i = 0; i < 78; i++)
383                         {
384                                 Term_putch(i, 5, TERM_WHITE, '=');
385                         }
386
387                         if (direct_r_idx < 0)
388                         {
389                                 for (IDX i = 0; i < browser_rows; i++)
390                                 {
391                                         Term_putch(max + 1, 6 + i, TERM_WHITE, '|');
392                                 }
393                         }
394
395                         redraw = FALSE;
396                 }
397
398                 if (direct_r_idx < 0)
399                 {
400                         if (grp_cur < grp_top) grp_top = grp_cur;
401                         if (grp_cur >= grp_top + browser_rows) grp_top = grp_cur - browser_rows + 1;
402
403                         display_group_list(0, 6, max, browser_rows, grp_idx, monster_group_text, grp_cur, grp_top);
404                         if (old_grp_cur != grp_cur)
405                         {
406                                 old_grp_cur = grp_cur;
407                                 mon_cnt = collect_monsters(creature_ptr, grp_idx[grp_cur], mon_idx, mode);
408                         }
409
410                         while (mon_cur < mon_top)
411                                 mon_top = MAX(0, mon_top - browser_rows / 2);
412                         while (mon_cur >= mon_top + browser_rows)
413                                 mon_top = MIN(mon_cnt - browser_rows, mon_top + browser_rows / 2);
414                 }
415
416                 if (!visual_list)
417                 {
418                         display_monster_list(max + 3, 6, browser_rows, mon_idx, mon_cur, mon_top, visual_only);
419                 }
420                 else
421                 {
422                         mon_top = mon_cur;
423                         display_monster_list(max + 3, 6, 1, mon_idx, mon_cur, mon_top, visual_only);
424                         display_visual_list(max + 3, 7, browser_rows - 1, wid - (max + 3), attr_top, char_left);
425                 }
426
427                 prt(format(_("<方向>%s%s%s, ESC", "<dir>%s%s%s, ESC"),
428                         (!visual_list && !visual_only) ? _(", 'r'で思い出を見る", ", 'r' to recall") : "",
429                         visual_list ? _(", ENTERで決定", ", ENTER to accept") : _(", 'v'でシンボル変更", ", 'v' for visuals"),
430                         (attr_idx || char_idx) ? _(", 'c', 'p'でペースト", ", 'c', 'p' to paste") : _(", 'c'でコピー", ", 'c' to copy")),
431                         hgt - 1, 0);
432
433                 monster_race *r_ptr;
434                 r_ptr = &r_info[mon_idx[mon_cur]];
435
436                 if (!visual_only)
437                 {
438                         if (mon_cnt) monster_race_track(creature_ptr, mon_idx[mon_cur]);
439                         handle_stuff(creature_ptr);
440                 }
441
442                 if (visual_list)
443                 {
444                         place_visual_list_cursor(max + 3, 7, r_ptr->x_attr, r_ptr->x_char, attr_top, char_left);
445                 }
446                 else if (!column)
447                 {
448                         Term_gotoxy(0, 6 + (grp_cur - grp_top));
449                 }
450                 else
451                 {
452                         Term_gotoxy(max + 3, 6 + (mon_cur - mon_top));
453                 }
454
455                 char ch = inkey();
456                 if (visual_mode_command(ch, &visual_list, browser_rows - 1, wid - (max + 3), &attr_top, &char_left, &r_ptr->x_attr, &r_ptr->x_char, need_redraw))
457                 {
458                         if (direct_r_idx >= 0)
459                         {
460                                 switch (ch)
461                                 {
462                                 case '\n':
463                                 case '\r':
464                                 case ESCAPE:
465                                         flag = TRUE;
466                                         break;
467                                 }
468                         }
469
470                         continue;
471                 }
472
473                 switch (ch)
474                 {
475                 case ESCAPE:
476                 {
477                         flag = TRUE;
478                         break;
479                 }
480
481                 case 'R':
482                 case 'r':
483                 {
484                         if (!visual_list && !visual_only && (mon_idx[mon_cur] > 0))
485                         {
486                                 screen_roff(creature_ptr, mon_idx[mon_cur], 0);
487
488                                 (void)inkey();
489
490                                 redraw = TRUE;
491                         }
492
493                         break;
494                 }
495
496                 default:
497                 {
498                         browser_cursor(ch, &column, &grp_cur, grp_cnt, &mon_cur, mon_cnt);
499
500                         break;
501                 }
502                 }
503         }
504
505         C_KILL(mon_idx, max_r_idx, MONRACE_IDX);
506 }
507
508
509 /*
510  * List wanted monsters
511  * @param creature_ptr プレーヤーへの参照ポインタ
512  * @return なし
513  */
514 void do_cmd_knowledge_bounty(player_type *creature_ptr)
515 {
516         FILE *fff = NULL;
517         GAME_TEXT file_name[FILE_NAME_SIZE];
518         if (!open_temporary_file(&fff, file_name)) return;
519
520         fprintf(fff, _("今日のターゲット : %s\n", "Today's target : %s\n"),
521                 (creature_ptr->today_mon ? r_name + r_info[creature_ptr->today_mon].name : _("不明", "unknown")));
522         fprintf(fff, "\n");
523         fprintf(fff, _("賞金首リスト\n", "List of wanted monsters\n"));
524         fprintf(fff, "----------------------------------------------\n");
525
526         bool listed = FALSE;
527         for (int i = 0; i < MAX_BOUNTY; i++)
528         {
529                 if (current_world_ptr->bounty_r_idx[i] <= 10000)
530                 {
531                         fprintf(fff, "%s\n", r_name + r_info[current_world_ptr->bounty_r_idx[i]].name);
532                         listed = TRUE;
533                 }
534         }
535
536         if (!listed)
537         {
538                 fprintf(fff, "\n%s\n", _("賞金首はもう残っていません。", "There are no more wanted monster."));
539         }
540
541         angband_fclose(fff);
542         (void)show_file(creature_ptr, TRUE, file_name, _("賞金首の一覧", "Wanted monsters"), 0, 0);
543         fd_kill(file_name);
544 }