OSDN Git Service

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