OSDN Git Service

Merge pull request #2810 from Hourier/Remove-KindObjectIndex
[hengbandforosx/hengbandosx.git] / src / knowledge / knowledge-items.cpp
1 /*!
2  * @brief 既知のアイテムとアーティファクトを表示する
3  * @date 2020/04/23
4  * @author Hourier
5  */
6
7 #include "knowledge/knowledge-items.h"
8 #include "core/show-file.h"
9 #include "core/stuff-handler.h"
10 #include "flavor/flavor-describer.h"
11 #include "flavor/object-flavor-types.h"
12 #include "flavor/object-flavor.h"
13 #include "game-option/special-options.h"
14 #include "inventory/inventory-slot-types.h"
15 #include "io-dump/dump-util.h"
16 #include "io/input-key-acceptor.h"
17 #include "knowledge/object-group-table.h"
18 #include "object-enchant/special-object-flags.h"
19 #include "object/object-kind-hook.h"
20 #include "object/tval-types.h"
21 #include "perception/identification.h"
22 #include "perception/object-perception.h"
23 #include "system/artifact-type-definition.h"
24 #include "system/baseitem-info.h"
25 #include "system/floor-type-definition.h"
26 #include "system/grid-type-definition.h"
27 #include "system/item-entity.h"
28 #include "system/player-type-definition.h"
29 #include "term/screen-processor.h"
30 #include "term/term-color-types.h"
31 #include "util/angband-files.h"
32 #include "util/int-char-converter.h"
33 #include "util/sort.h"
34 #include "view/display-messages.h"
35 #include "world/world.h"
36 #include <numeric>
37 #include <set>
38
39 /*!
40  * @brief Check the status of "artifacts"
41  * @param player_ptr プレイヤーへの参照ポインタ
42  */
43 void do_cmd_knowledge_artifacts(PlayerType *player_ptr)
44 {
45     FILE *fff = nullptr;
46     GAME_TEXT file_name[FILE_NAME_SIZE];
47     if (!open_temporary_file(&fff, file_name)) {
48         return;
49     }
50
51     std::set<FixedArtifactId> known_list;
52
53     for (const auto &[a_idx, a_ref] : artifacts_info) {
54         if (a_ref.name.empty()) {
55             continue;
56         }
57         if (!a_ref.is_generated) {
58             continue;
59         }
60
61         known_list.insert(known_list.end(), a_idx);
62     }
63
64     for (POSITION y = 0; y < player_ptr->current_floor_ptr->height; y++) {
65         for (POSITION x = 0; x < player_ptr->current_floor_ptr->width; x++) {
66             auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
67             for (const auto this_o_idx : g_ptr->o_idx_list) {
68                 ItemEntity *o_ptr;
69                 o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
70                 if (!o_ptr->is_fixed_artifact()) {
71                     continue;
72                 }
73                 if (o_ptr->is_known()) {
74                     continue;
75                 }
76
77                 known_list.erase(o_ptr->fixed_artifact_idx);
78             }
79         }
80     }
81
82     for (auto i = 0; i < INVEN_TOTAL; i++) {
83         auto *o_ptr = &player_ptr->inventory_list[i];
84         if (!o_ptr->bi_id) {
85             continue;
86         }
87         if (!o_ptr->is_fixed_artifact()) {
88             continue;
89         }
90         if (o_ptr->is_known()) {
91             continue;
92         }
93
94         known_list.erase(o_ptr->fixed_artifact_idx);
95     }
96
97     std::vector<FixedArtifactId> whats(known_list.begin(), known_list.end());
98
99     uint16_t why = 3;
100     ang_sort(player_ptr, whats.data(), &why, whats.size(), ang_sort_art_comp, ang_sort_art_swap);
101     for (auto a_idx : whats) {
102         const auto &a_ref = artifacts_info.at(a_idx);
103         GAME_TEXT base_name[MAX_NLEN];
104         strcpy(base_name, _("未知の伝説のアイテム", "Unknown Artifact"));
105         const auto bi_id = lookup_baseitem_id(a_ref.bi_key);
106         constexpr auto template_basename = _("     %s\n", "     The %s\n");
107         if (bi_id == 0) {
108             fprintf(fff, template_basename, base_name);
109             continue;
110         }
111
112         ItemEntity item;
113         item.prep(bi_id);
114         item.fixed_artifact_idx = a_idx;
115         item.ident |= IDENT_STORE;
116         describe_flavor(player_ptr, base_name, &item, (OD_OMIT_PREFIX | OD_NAME_ONLY));
117         fprintf(fff, template_basename, base_name);
118     }
119
120     angband_fclose(fff);
121     (void)show_file(player_ptr, true, file_name, _("既知の伝説のアイテム", "Artifacts Seen"), 0, 0);
122     fd_kill(file_name);
123 }
124
125 /*
126  * Build a list of object indexes in the given group. Return the number
127  * of objects in the group.
128  *
129  * mode & 0x01 : check for non-empty group
130  * mode & 0x02 : visual operation only
131  */
132 static short collect_objects(int grp_cur, short object_idx[], BIT_FLAGS8 mode)
133 {
134     short object_cnt = 0;
135     auto group_tval = object_group_tval[grp_cur];
136     for (const auto &k_ref : baseitems_info) {
137         if (k_ref.name.empty()) {
138             continue;
139         }
140
141         if (!(mode & 0x02)) {
142             if (!w_ptr->wizard) {
143                 if (!k_ref.flavor) {
144                     continue;
145                 }
146                 if (!k_ref.aware) {
147                     continue;
148                 }
149             }
150
151             auto k = std::reduce(std::begin(k_ref.chance), std::end(k_ref.chance), 0);
152             if (!k) {
153                 continue;
154             }
155         }
156
157         const auto tval = k_ref.bi_key.tval();
158         if (group_tval == ItemKindType::LIFE_BOOK) {
159             if (ItemKindType::LIFE_BOOK <= tval && tval <= ItemKindType::HEX_BOOK) {
160                 object_idx[object_cnt++] = k_ref.idx;
161             } else {
162                 continue;
163             }
164         } else if (tval == group_tval) {
165             object_idx[object_cnt++] = k_ref.idx;
166         } else {
167             continue;
168         }
169
170         if (mode & 0x01) {
171             break;
172         }
173     }
174
175     object_idx[object_cnt] = -1;
176     return object_cnt;
177 }
178
179 /*
180  * Display the objects in a group.
181  */
182 static void display_object_list(int col, int row, int per_page, IDX object_idx[], int object_cur, int object_top, bool visual_only)
183 {
184     int i;
185     for (i = 0; i < per_page && (object_idx[object_top + i] >= 0); i++) {
186         TERM_COLOR a;
187         BaseitemInfo *flavor_k_ptr;
188         short bi_id = object_idx[object_top + i];
189         auto *k_ptr = &baseitems_info[bi_id];
190         TERM_COLOR attr = ((k_ptr->aware || visual_only) ? TERM_WHITE : TERM_SLATE);
191         byte cursor = ((k_ptr->aware || visual_only) ? TERM_L_BLUE : TERM_BLUE);
192         if (!visual_only && k_ptr->flavor) {
193             flavor_k_ptr = &baseitems_info[k_ptr->flavor];
194         } else {
195             flavor_k_ptr = k_ptr;
196         }
197
198         attr = ((i + object_top == object_cur) ? cursor : attr);
199         const auto is_flavor_only = (k_ptr->flavor != 0) && (visual_only || !k_ptr->aware);
200         const auto o_name = is_flavor_only ? flavor_k_ptr->flavor_name : strip_name(bi_id);
201         c_prt(attr, o_name.data(), row + i, col);
202         if (per_page == 1) {
203             c_prt(attr, format("%02x/%02x", flavor_k_ptr->x_attr, flavor_k_ptr->x_char), row + i, (w_ptr->wizard || visual_only) ? 64 : 68);
204         }
205
206         if (w_ptr->wizard || visual_only) {
207             c_prt(attr, format("%d", bi_id), row + i, 70);
208         }
209
210         a = flavor_k_ptr->x_attr;
211         auto c = flavor_k_ptr->x_char;
212
213         term_queue_bigchar(use_bigtile ? 76 : 77, row + i, a, c, 0, 0);
214     }
215
216     for (; i < per_page; i++) {
217         term_erase(col, row + i, 255);
218     }
219 }
220
221 /*
222  * Describe fake object
223  */
224 static void desc_obj_fake(PlayerType *player_ptr, short bi_id)
225 {
226     ItemEntity *o_ptr;
227     ItemEntity ObjectType_body;
228     o_ptr = &ObjectType_body;
229     o_ptr->wipe();
230     o_ptr->prep(bi_id);
231
232     o_ptr->ident |= IDENT_KNOWN;
233     handle_stuff(player_ptr);
234
235     if (screen_object(player_ptr, o_ptr, SCROBJ_FAKE_OBJECT | SCROBJ_FORCE_DETAIL)) {
236         return;
237     }
238
239     msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
240     msg_print(nullptr);
241 }
242
243 /**
244  * @brief Display known objects
245  */
246 void do_cmd_knowledge_objects(PlayerType *player_ptr, bool *need_redraw, bool visual_only, short direct_k_idx)
247 {
248     short object_old, object_top;
249     short grp_idx[100];
250     int object_cnt;
251
252     bool visual_list = false;
253     TERM_COLOR attr_top = 0;
254     byte char_left = 0;
255     byte mode;
256
257     TERM_LEN wid, hgt;
258     term_get_size(&wid, &hgt);
259
260     int browser_rows = hgt - 8;
261     std::vector<short> object_idx(baseitems_info.size());
262
263     int len;
264     int max = 0;
265     int grp_cnt = 0;
266     if (direct_k_idx < 0) {
267         mode = visual_only ? 0x03 : 0x01;
268         for (IDX i = 0; object_group_text[i] != nullptr; i++) {
269             len = strlen(object_group_text[i]);
270             if (len > max) {
271                 max = len;
272             }
273
274             if (collect_objects(i, object_idx.data(), mode)) {
275                 grp_idx[grp_cnt++] = i;
276             }
277         }
278
279         object_old = -1;
280         object_cnt = 0;
281     } else {
282         auto *k_ptr = &baseitems_info[direct_k_idx];
283         BaseitemInfo *flavor_k_ptr;
284
285         if (!visual_only && k_ptr->flavor) {
286             flavor_k_ptr = &baseitems_info[k_ptr->flavor];
287         } else {
288             flavor_k_ptr = k_ptr;
289         }
290
291         object_idx[0] = direct_k_idx;
292         object_old = direct_k_idx;
293         object_cnt = 1;
294         object_idx[1] = -1;
295         (void)visual_mode_command(
296             'v', &visual_list, browser_rows - 1, wid - (max + 3), &attr_top, &char_left, &flavor_k_ptr->x_attr, &flavor_k_ptr->x_char, need_redraw);
297     }
298
299     grp_idx[grp_cnt] = -1;
300     mode = visual_only ? 0x02 : 0x00;
301     IDX old_grp_cur = -1;
302     IDX grp_cur = 0;
303     IDX grp_top = 0;
304     IDX object_cur = object_top = 0;
305     bool flag = false;
306     bool redraw = true;
307     int column = 0;
308     while (!flag) {
309         BaseitemInfo *k_ptr, *flavor_k_ptr;
310
311         if (redraw) {
312             clear_from(0);
313
314 #ifdef JP
315             prt(format("%s - アイテム", !visual_only ? "知識" : "表示"), 2, 0);
316             if (direct_k_idx < 0) {
317                 prt("グループ", 4, 0);
318             }
319             prt("名前", 4, max + 3);
320             if (w_ptr->wizard || visual_only) {
321                 prt("Idx", 4, 70);
322             }
323             prt("文字", 4, 74);
324 #else
325             prt(format("%s - objects", !visual_only ? "Knowledge" : "Visuals"), 2, 0);
326             if (direct_k_idx < 0) {
327                 prt("Group", 4, 0);
328             }
329             prt("Name", 4, max + 3);
330             if (w_ptr->wizard || visual_only) {
331                 prt("Idx", 4, 70);
332             }
333             prt("Sym", 4, 75);
334 #endif
335
336             for (IDX i = 0; i < 78; i++) {
337                 term_putch(i, 5, TERM_WHITE, '=');
338             }
339
340             if (direct_k_idx < 0) {
341                 for (IDX i = 0; i < browser_rows; i++) {
342                     term_putch(max + 1, 6 + i, TERM_WHITE, '|');
343                 }
344             }
345
346             redraw = false;
347         }
348
349         if (direct_k_idx < 0) {
350             if (grp_cur < grp_top) {
351                 grp_top = grp_cur;
352             }
353             if (grp_cur >= grp_top + browser_rows) {
354                 grp_top = grp_cur - browser_rows + 1;
355             }
356
357             std::vector<concptr> tmp_texts;
358             for (auto &text : object_group_text) {
359                 tmp_texts.push_back(text);
360             }
361
362             display_group_list(0, 6, max, browser_rows, grp_idx, tmp_texts.data(), grp_cur, grp_top);
363             if (old_grp_cur != grp_cur) {
364                 old_grp_cur = grp_cur;
365                 object_cnt = collect_objects(grp_idx[grp_cur], object_idx.data(), mode);
366             }
367
368             while (object_cur < object_top) {
369                 object_top = std::max<short>(0, object_top - browser_rows / 2);
370             }
371
372             while (object_cur >= object_top + browser_rows) {
373                 object_top = std::min<short>(object_cnt - browser_rows, object_top + browser_rows / 2);
374             }
375         }
376
377         if (!visual_list) {
378             display_object_list(max + 3, 6, browser_rows, object_idx.data(), object_cur, object_top, visual_only);
379         } else {
380             object_top = object_cur;
381             display_object_list(max + 3, 6, 1, object_idx.data(), object_cur, object_top, visual_only);
382             display_visual_list(max + 3, 7, browser_rows - 1, wid - (max + 3), attr_top, char_left);
383         }
384
385         k_ptr = &baseitems_info[object_idx[object_cur]];
386
387         if (!visual_only && k_ptr->flavor) {
388             flavor_k_ptr = &baseitems_info[k_ptr->flavor];
389         } else {
390             flavor_k_ptr = k_ptr;
391         }
392
393 #ifdef JP
394         prt(format("<方向>%s%s%s, ESC", (!visual_list && !visual_only) ? ", 'r'で詳細を見る" : "", visual_list ? ", ENTERで決定" : ", 'v'でシンボル変更",
395                 (attr_idx || char_idx) ? ", 'c', 'p'でペースト" : ", 'c'でコピー"),
396             hgt - 1, 0);
397 #else
398         prt(format("<dir>%s%s%s, ESC", (!visual_list && !visual_only) ? ", 'r' to recall" : "", visual_list ? ", ENTER to accept" : ", 'v' for visuals",
399                 (attr_idx || char_idx) ? ", 'c', 'p' to paste" : ", 'c' to copy"),
400             hgt - 1, 0);
401 #endif
402
403         if (!visual_only) {
404             if (object_cnt) {
405                 object_kind_track(player_ptr, object_idx[object_cur]);
406             }
407
408             if (object_old != object_idx[object_cur]) {
409                 handle_stuff(player_ptr);
410                 object_old = object_idx[object_cur];
411             }
412         }
413
414         if (visual_list) {
415             place_visual_list_cursor(max + 3, 7, flavor_k_ptr->x_attr, flavor_k_ptr->x_char, attr_top, char_left);
416         } else if (!column) {
417             term_gotoxy(0, 6 + (grp_cur - grp_top));
418         } else {
419             term_gotoxy(max + 3, 6 + (object_cur - object_top));
420         }
421
422         char ch = inkey();
423         if (visual_mode_command(
424                 ch, &visual_list, browser_rows - 1, wid - (max + 3), &attr_top, &char_left, &flavor_k_ptr->x_attr, &flavor_k_ptr->x_char, need_redraw)) {
425             if (direct_k_idx >= 0) {
426                 switch (ch) {
427                 case '\n':
428                 case '\r':
429                 case ESCAPE:
430                     flag = true;
431                     break;
432                 }
433             }
434             continue;
435         }
436
437         switch (ch) {
438         case ESCAPE: {
439             flag = true;
440             break;
441         }
442
443         case 'R':
444         case 'r': {
445             if (!visual_list && !visual_only && (grp_cnt > 0)) {
446                 desc_obj_fake(player_ptr, object_idx[object_cur]);
447                 redraw = true;
448             }
449
450             break;
451         }
452
453         default: {
454             browser_cursor(ch, &column, &grp_cur, grp_cnt, &object_cur, object_cnt);
455             break;
456         }
457         }
458     }
459 }