OSDN Git Service

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