OSDN Git Service

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