OSDN Git Service

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