OSDN Git Service

Replace sprintf() with std::string and/or format(). Does part of the work of resolvi...
[hengbandforosx/hengbandosx.git] / src / window / display-sub-windows.cpp
1 #include "window/display-sub-windows.h"
2 #include "core/window-redrawer.h"
3 #include "flavor/flavor-describer.h"
4 #include "floor/cave.h"
5 #include "game-option/option-flags.h"
6 #include "game-option/special-options.h"
7 #include "game-option/text-display-options.h"
8 #include "grid/feature.h"
9 #include "inventory/inventory-describer.h"
10 #include "inventory/inventory-slot-types.h"
11 #include "inventory/inventory-util.h"
12 #include "locale/japanese.h"
13 #include "main/sound-of-music.h"
14 #include "monster-race/monster-race.h"
15 #include "monster-race/race-flags1.h"
16 #include "monster/monster-flag-types.h"
17 #include "monster/monster-info.h"
18 #include "monster/monster-status.h"
19 #include "object/item-tester-hooker.h"
20 #include "object/object-info.h"
21 #include "object/object-mark-types.h"
22 #include "player/player-status-flags.h"
23 #include "player/player-status.h"
24 #include "spell-kind/magic-item-recharger.h"
25 #include "system/baseitem-info.h"
26 #include "system/floor-type-definition.h"
27 #include "system/grid-type-definition.h"
28 #include "system/item-entity.h"
29 #include "system/monster-entity.h"
30 #include "system/monster-race-info.h"
31 #include "system/player-type-definition.h"
32 #include "system/terrain-type-definition.h"
33 #include "target/target-describer.h"
34 #include "target/target-preparation.h"
35 #include "target/target-setter.h"
36 #include "target/target-types.h"
37 #include "term/gameterm.h"
38 #include "term/screen-processor.h"
39 #include "term/term-color-types.h"
40 #include "term/z-form.h"
41 #include "timed-effect/player-hallucination.h"
42 #include "timed-effect/timed-effects.h"
43 #include "util/bit-flags-calculator.h"
44 #include "view/display-lore.h"
45 #include "view/display-map.h"
46 #include "view/display-messages.h"
47 #include "view/display-player.h"
48 #include "view/object-describer.h"
49 #include "window/main-window-equipments.h"
50 #include "window/main-window-util.h"
51 #include "world/world.h"
52 #include <algorithm>
53 #include <mutex>
54 #include <sstream>
55 #include <string>
56 #include <util/object-sort.h>
57
58 /*! サブウィンドウ表示用の ItemTester オブジェクト */
59 static std::unique_ptr<ItemTester> fix_item_tester = std::make_unique<AllMatchItemTester>();
60
61 FixItemTesterSetter::FixItemTesterSetter(const ItemTester &item_tester)
62 {
63     fix_item_tester = item_tester.clone();
64 }
65
66 FixItemTesterSetter::~FixItemTesterSetter()
67 {
68     fix_item_tester = std::make_unique<AllMatchItemTester>();
69 }
70
71 /*!
72  * @brief サブウィンドウに所持品一覧を表示する / Hack -- display inventory in sub-windows
73  * @param player_ptr プレイヤーへの参照ポインタ
74  */
75 void fix_inventory(PlayerType *player_ptr)
76 {
77     for (int j = 0; j < 8; j++) {
78         term_type *old = game_term;
79         if (!angband_terms[j]) {
80             continue;
81         }
82
83         if (!(window_flag[j] & (PW_INVEN))) {
84             continue;
85         }
86
87         term_activate(angband_terms[j]);
88         display_inventory(player_ptr, *fix_item_tester);
89         term_fresh();
90         term_activate(old);
91     }
92 }
93
94 /*!
95  * @brief モンスターの現在数を一行で表現する / Print monster info in line
96  * @param x 表示列
97  * @param y 表示行
98  * @param m_ptr 思い出を表示するモンスター情報の参照ポインタ
99  * @param n_same モンスター数の現在数
100  * @param n_awake 起きている数
101  * @details
102  * <pre>
103  * nnn X LV name
104  *  nnn : number or unique(U) or wanted unique(W)
105  *  X   : symbol of monster
106  *  LV  : monster lv if known
107  *  name: name of monster
108  * </pre>
109  */
110 static void print_monster_line(TERM_LEN x, TERM_LEN y, MonsterEntity *m_ptr, int n_same, int n_awake)
111 {
112     std::string buf;
113     MonsterRaceId r_idx = m_ptr->ap_r_idx;
114     auto *r_ptr = &monraces_info[r_idx];
115
116     term_erase(0, y, 255);
117     term_gotoxy(x, y);
118     if (!r_ptr) {
119         return;
120     }
121     if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
122         buf = format(_("%3s(覚%2d)", "%3s(%2d)"), MonsterRace(r_idx).is_bounty(true) ? "  W" : "  U", n_awake);
123     } else {
124         buf = format(_("%3d(覚%2d)", "%3d(%2d)"), n_same, n_awake);
125     }
126     term_addstr(-1, TERM_WHITE, buf);
127
128     term_addstr(-1, TERM_WHITE, " ");
129     term_add_bigch(r_ptr->x_attr, r_ptr->x_char);
130
131     if (r_ptr->r_tkills && m_ptr->mflag2.has_not(MonsterConstantFlagType::KAGE)) {
132         buf = format(" %2d", (int)r_ptr->level);
133     } else {
134         buf = " ??";
135     }
136
137     term_addstr(-1, TERM_WHITE, buf);
138
139     term_addstr(-1, TERM_WHITE, format(" %s ", r_ptr->name.data()));
140 }
141
142 /*!
143  * @brief モンスターの出現リストを表示する / Print monster info in line
144  * @param x 表示列
145  * @param y 表示行
146  * @param max_lines 最大何行描画するか
147  */
148 void print_monster_list(FloorType *floor_ptr, const std::vector<MONSTER_IDX> &monster_list, TERM_LEN x, TERM_LEN y, TERM_LEN max_lines)
149 {
150     TERM_LEN line = y;
151     struct info {
152         MonsterEntity *monster_entity;
153         int visible_count; // 現在数
154         int awake_count; // 起きている数
155     };
156
157     // 出現リスト表示用のデータ
158     std::vector<info> monster_list_info;
159
160     // 描画に必要なデータを集める
161     for (auto monster_index : monster_list) {
162         auto m_ptr = &floor_ptr->m_list[monster_index];
163
164         if (m_ptr->is_pet()) {
165             continue;
166         } // pet
167         if (!MonsterRace(m_ptr->r_idx).is_valid()) {
168             continue;
169         } // dead?
170
171         // ソート済みなので同じモンスターは連続する.これを利用して同じモンスターをカウント,まとめて表示する.
172         if (monster_list_info.empty() || (monster_list_info.back().monster_entity->ap_r_idx != m_ptr->ap_r_idx)) {
173             monster_list_info.push_back({ m_ptr, 0, 0 });
174         }
175
176         // 出現数をカウント
177         monster_list_info.back().visible_count++;
178
179         // 起きているモンスターをカウント
180         if (!m_ptr->is_asleep()) {
181             monster_list_info.back().awake_count++;
182         }
183     }
184
185     // 集めたデータを元にリストを表示する
186     for (const auto &info : monster_list_info) {
187         print_monster_line(x, line++, info.monster_entity, info.visible_count, info.awake_count);
188
189         // 行数が足りなくなったら中断。
190         if (line - y == max_lines) {
191             // 行数が足りなかった場合、最終行にその旨表示。
192             term_addstr(-1, TERM_WHITE, "-- and more --");
193             break;
194         }
195     }
196
197     for (; line < max_lines; line++) {
198         term_erase(0, line, 255);
199     }
200 }
201
202 /*!
203  * @brief 出現中モンスターのリストをサブウィンドウに表示する / Hack -- display monster list in sub-windows
204  * @param player_ptr プレイヤーへの参照ポインタ
205  */
206 void fix_monster_list(PlayerType *player_ptr)
207 {
208     static std::vector<MONSTER_IDX> monster_list;
209     std::once_flag once;
210
211     for (int j = 0; j < 8; j++) {
212         term_type *old = game_term;
213         if (!angband_terms[j]) {
214             continue;
215         }
216         if (!(window_flag[j] & PW_MONSTER_LIST)) {
217             continue;
218         }
219         if (angband_terms[j]->never_fresh) {
220             continue;
221         }
222
223         term_activate(angband_terms[j]);
224         int w, h;
225         term_get_size(&w, &h);
226         std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
227         print_monster_list(player_ptr->current_floor_ptr, monster_list, 0, 0, h);
228         term_fresh();
229         term_activate(old);
230     }
231
232     if (use_music && has_monster_music) {
233         std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
234         select_monster_music(player_ptr, monster_list);
235     }
236 }
237
238 /*!
239  * @brief 装備アイテム一覧を表示する /
240  * Choice window "shadow" of the "show_equip()" function
241  */
242 static void display_equipment(PlayerType *player_ptr, const ItemTester &item_tester)
243 {
244     if (!player_ptr || !player_ptr->inventory_list) {
245         return;
246     }
247
248     TERM_LEN wid, hgt;
249     term_get_size(&wid, &hgt);
250
251     TERM_COLOR attr = TERM_WHITE;
252     GAME_TEXT o_name[MAX_NLEN];
253     for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
254         int cur_row = i - INVEN_MAIN_HAND;
255         if (cur_row >= hgt) {
256             break;
257         }
258
259         auto o_ptr = &player_ptr->inventory_list[i];
260         auto do_disp = player_ptr->select_ring_slot ? is_ring_slot(i) : item_tester.okay(o_ptr);
261         std::string tmp_val = "   ";
262
263         if (do_disp) {
264             tmp_val[0] = index_to_label(i);
265             tmp_val[1] = ')';
266         }
267
268         int cur_col = 3;
269         term_erase(cur_col, cur_row, 255);
270         term_putstr(0, cur_row, cur_col, TERM_WHITE, tmp_val);
271
272         if ((((i == INVEN_MAIN_HAND) && can_attack_with_sub_hand(player_ptr)) || ((i == INVEN_SUB_HAND) && can_attack_with_main_hand(player_ptr))) && has_two_handed_weapons(player_ptr)) {
273             strcpy(o_name, _("(武器を両手持ち)", "(wielding with two-hands)"));
274             attr = TERM_WHITE;
275         } else {
276             describe_flavor(player_ptr, o_name, o_ptr, 0);
277             attr = tval_to_attr[enum2i(o_ptr->bi_key.tval()) % 128];
278         }
279
280         int n = strlen(o_name);
281         if (o_ptr->timeout) {
282             attr = TERM_L_DARK;
283         }
284
285         if (show_item_graph) {
286             const auto a = o_ptr->get_color();
287             const auto c = o_ptr->get_symbol();
288             term_queue_bigchar(cur_col, cur_row, a, c, 0, 0);
289             if (use_bigtile) {
290                 cur_col++;
291             }
292
293             cur_col += 2;
294         }
295
296         term_putstr(cur_col, cur_row, n, attr, o_name);
297         if (show_weights) {
298             int wgt = o_ptr->weight * o_ptr->number;
299             tmp_val = format(_("%3d.%1d kg", "%3d.%1d lb"), _(lb_to_kg_integer(wgt), wgt / 10), _(lb_to_kg_fraction(wgt), wgt % 10));
300             prt(tmp_val, cur_row, wid - (show_labels ? 28 : 9));
301         }
302
303         if (show_labels) {
304             term_putstr(wid - 20, cur_row, -1, TERM_WHITE, " <-- ");
305             prt(mention_use(player_ptr, i), cur_row, wid - 15);
306         }
307     }
308
309     for (int i = INVEN_TOTAL - INVEN_MAIN_HAND; i < hgt; i++) {
310         term_erase(0, i, 255);
311     }
312 }
313
314 /*!
315  * @brief 現在の装備品をサブウィンドウに表示する /
316  * Hack -- display equipment in sub-windows
317  * @param player_ptr プレイヤーへの参照ポインタ
318  */
319 void fix_equip(PlayerType *player_ptr)
320 {
321     for (int j = 0; j < 8; j++) {
322         term_type *old = game_term;
323         if (!angband_terms[j]) {
324             continue;
325         }
326         if (!(window_flag[j] & (PW_EQUIP))) {
327             continue;
328         }
329
330         term_activate(angband_terms[j]);
331         display_equipment(player_ptr, *fix_item_tester);
332         term_fresh();
333         term_activate(old);
334     }
335 }
336
337 /*!
338  * @brief 現在のプレイヤーステータスをサブウィンドウに表示する /
339  * @param player_ptr プレイヤーへの参照ポインタ
340  * Hack -- display character in sub-windows
341  */
342 void fix_player(PlayerType *player_ptr)
343 {
344     for (int j = 0; j < 8; j++) {
345         term_type *old = game_term;
346         if (!angband_terms[j]) {
347             continue;
348         }
349
350         if (!(window_flag[j] & (PW_PLAYER))) {
351             continue;
352         }
353
354         term_activate(angband_terms[j]);
355         update_playtime();
356         (void)display_player(player_ptr, 0);
357         term_fresh();
358         term_activate(old);
359     }
360 }
361
362 /*!
363  * @brief ゲームメッセージ履歴をサブウィンドウに表示する /
364  * Hack -- display recent messages in sub-windows
365  * Adjust for width and split messages
366  */
367 void fix_message(void)
368 {
369     for (int j = 0; j < 8; j++) {
370         term_type *old = game_term;
371         if (!angband_terms[j]) {
372             continue;
373         }
374
375         if (!(window_flag[j] & (PW_MESSAGE))) {
376             continue;
377         }
378
379         term_activate(angband_terms[j]);
380         TERM_LEN w, h;
381         term_get_size(&w, &h);
382         for (int i = 0; i < h; i++) {
383             term_putstr(0, (h - 1) - i, -1, (byte)((i < now_message) ? TERM_WHITE : TERM_SLATE), message_str((int16_t)i));
384             TERM_LEN x, y;
385             term_locate(&x, &y);
386             term_erase(x, y, 255);
387         }
388
389         term_fresh();
390         term_activate(old);
391     }
392 }
393
394 /*!
395  * @brief 簡易マップをサブウィンドウに表示する /
396  * Hack -- display overhead view in sub-windows
397  * Adjust for width and split messages
398  * @param player_ptr プレイヤーへの参照ポインタ
399  * @details
400  * Note that the "player" symbol does NOT appear on the map.
401  */
402 void fix_overhead(PlayerType *player_ptr)
403 {
404     for (int j = 0; j < 8; j++) {
405         term_type *old = game_term;
406         TERM_LEN wid, hgt;
407         if (!angband_terms[j]) {
408             continue;
409         }
410
411         if (!(window_flag[j] & (PW_OVERHEAD))) {
412             continue;
413         }
414
415         term_activate(angband_terms[j]);
416         term_get_size(&wid, &hgt);
417         if (wid > COL_MAP + 2 && hgt > ROW_MAP + 2) {
418             int cy, cx;
419             display_map(player_ptr, &cy, &cx);
420             term_fresh();
421         }
422
423         term_activate(old);
424     }
425 }
426
427 /*!
428  * @brief 自分の周辺の地形をTermに表示する
429  * @param プレイヤー情報への参照ポインタ
430  */
431 static void display_dungeon(PlayerType *player_ptr)
432 {
433     TERM_COLOR ta = 0;
434     auto tc = '\0';
435
436     for (TERM_LEN x = player_ptr->x - game_term->wid / 2 + 1; x <= player_ptr->x + game_term->wid / 2; x++) {
437         for (TERM_LEN y = player_ptr->y - game_term->hgt / 2 + 1; y <= player_ptr->y + game_term->hgt / 2; y++) {
438             TERM_COLOR a;
439             char c;
440             if (!in_bounds2(player_ptr->current_floor_ptr, y, x)) {
441                 auto *f_ptr = &terrains_info[feat_none];
442                 a = f_ptr->x_attr[F_LIT_STANDARD];
443                 c = f_ptr->x_char[F_LIT_STANDARD];
444                 term_queue_char(x - player_ptr->x + game_term->wid / 2 - 1, y - player_ptr->y + game_term->hgt / 2 - 1, a, c, ta, tc);
445                 continue;
446             }
447
448             map_info(player_ptr, y, x, &a, &c, &ta, &tc);
449
450             if (!use_graphics) {
451                 if (w_ptr->timewalk_m_idx) {
452                     a = TERM_DARK;
453                 } else if (is_invuln(player_ptr) || player_ptr->timewalk) {
454                     a = TERM_WHITE;
455                 } else if (player_ptr->wraith_form) {
456                     a = TERM_L_DARK;
457                 }
458             }
459
460             term_queue_char(x - player_ptr->x + game_term->wid / 2 - 1, y - player_ptr->y + game_term->hgt / 2 - 1, a, c, ta, tc);
461         }
462     }
463 }
464
465 /*!
466  * @brief 自分の周辺のダンジョンの地形をサブウィンドウに表示する / display dungeon view around player in a sub window
467  * @param player_ptr プレイヤーへの参照ポインタ
468  */
469 void fix_dungeon(PlayerType *player_ptr)
470 {
471     for (int j = 0; j < 8; j++) {
472         term_type *old = game_term;
473         if (!angband_terms[j]) {
474             continue;
475         }
476
477         if (!(window_flag[j] & (PW_DUNGEON))) {
478             continue;
479         }
480
481         term_activate(angband_terms[j]);
482         display_dungeon(player_ptr);
483         term_fresh();
484         term_activate(old);
485     }
486 }
487
488 /*!
489  * @brief モンスターの思い出をサブウィンドウに表示する /
490  * Hack -- display dungeon view in sub-windows
491  * @param player_ptr プレイヤーへの参照ポインタ
492  */
493 void fix_monster(PlayerType *player_ptr)
494 {
495     for (int j = 0; j < 8; j++) {
496         term_type *old = game_term;
497         if (!angband_terms[j]) {
498             continue;
499         }
500
501         if (!(window_flag[j] & (PW_MONSTER))) {
502             continue;
503         }
504
505         term_activate(angband_terms[j]);
506         if (MonsterRace(player_ptr->monster_race_idx).is_valid()) {
507             display_roff(player_ptr);
508         }
509
510         term_fresh();
511         term_activate(old);
512     }
513 }
514
515 /*!
516  * @brief ベースアイテム情報をサブウィンドウに表示する /
517  * Hack -- display object recall in sub-windows
518  * @param player_ptr プレイヤーへの参照ポインタ
519  */
520 void fix_object(PlayerType *player_ptr)
521 {
522     for (auto i = 0; i < 8; i++) {
523         auto *old = game_term;
524         if (angband_terms[i] == nullptr) {
525             continue;
526         }
527
528         if (none_bits(window_flag[i], PW_OBJECT)) {
529             continue;
530         }
531
532         term_activate(angband_terms[i]);
533         display_koff(player_ptr);
534         term_fresh();
535         term_activate(old);
536     }
537 }
538
539 /*!
540  * @brief 床上のモンスター情報を返す
541  * @param floor_ptr 階の情報への参照ポインタ
542  * @param grid_prt 座標グリッドの情報への参照ポインタ
543  * @return モンスターが見える場合にはモンスター情報への参照ポインタ、それ以外はnullptr
544  * @details
545  * Lookコマンドでカーソルを合わせた場合に合わせてミミックは考慮しない。
546  */
547 static const MonsterEntity *monster_on_floor_items(FloorType *floor_ptr, const grid_type *g_ptr)
548 {
549     if (g_ptr->m_idx == 0) {
550         return nullptr;
551     }
552
553     auto m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
554     if (!m_ptr->is_valid() || !m_ptr->ml) {
555         return nullptr;
556     }
557
558     return m_ptr;
559 }
560
561 /*!
562  * @brief 床上のアイテム一覧を作成し、表示する
563  * @param プレイヤー情報への参照ポインタ
564  * @param y 参照する座標グリッドのy座標
565  * @param x 参照する座標グリッドのx座標
566  */
567 static void display_floor_item_list(PlayerType *player_ptr, const int y, const int x)
568 {
569     // Term の行数を取得。
570     TERM_LEN term_h;
571     {
572         TERM_LEN term_w;
573         term_get_size(&term_w, &term_h);
574     }
575     if (term_h <= 0) {
576         return;
577     }
578
579     term_clear();
580     term_gotoxy(0, 0);
581
582     auto *floor_ptr = player_ptr->current_floor_ptr;
583     const auto *g_ptr = &floor_ptr->grid_array[y][x];
584     std::string line;
585
586     // 先頭行を書く。
587     auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
588     if (player_bold(player_ptr, y, x)) {
589         line = format(_("(X:%03d Y:%03d) あなたの足元のアイテム一覧", "Items at (%03d,%03d) under you"), x, y);
590     } else if (const auto *m_ptr = monster_on_floor_items(floor_ptr, g_ptr); m_ptr != nullptr) {
591         if (is_hallucinated) {
592             line = format(_("(X:%03d Y:%03d) 何か奇妙な物の足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under something strange"), x, y);
593         } else {
594             const MonsterRaceInfo *const r_ptr = &monraces_info[m_ptr->ap_r_idx];
595             line = format(_("(X:%03d Y:%03d) %sの足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under %s"), x, y, r_ptr->name.data());
596         }
597     } else {
598         const TerrainType *const f_ptr = &terrains_info[g_ptr->feat];
599         concptr fn = f_ptr->name.data();
600         std::string buf;
601
602         if (f_ptr->flags.has(TerrainCharacteristics::STORE) || (f_ptr->flags.has(TerrainCharacteristics::BLDG) && !floor_ptr->inside_arena)) {
603             buf = format(_("%sの入口", "on the entrance of %s"), fn);
604         } else if (f_ptr->flags.has(TerrainCharacteristics::WALL)) {
605             buf = format(_("%sの中", "in %s"), fn);
606         } else {
607             buf = format(_("%s", "on %s"), fn);
608         }
609         line = format(_("(X:%03d Y:%03d) %sの上の発見済みアイテム一覧", "Found items at (X:%03d Y:%03d) %s"), x, y, buf.data());
610     }
611     term_addstr(-1, TERM_WHITE, line);
612
613     // (y,x) のアイテムを1行に1個ずつ書く。
614     TERM_LEN term_y = 1;
615     for (const auto o_idx : g_ptr->o_idx_list) {
616         auto *const o_ptr = &floor_ptr->o_list[o_idx];
617         const auto tval = o_ptr->bi_key.tval();
618         if (o_ptr->marked.has_not(OmType::FOUND) || tval == ItemKindType::GOLD) {
619             continue;
620         }
621
622         // 途中で行数が足りなくなったら最終行にその旨追記して終了。
623         if (term_y >= term_h) {
624             term_addstr(-1, TERM_WHITE, "-- more --");
625             break;
626         }
627
628         term_gotoxy(0, term_y);
629
630         if (is_hallucinated) {
631             term_addstr(-1, TERM_WHITE, _("何か奇妙な物", "something strange"));
632         } else {
633             char buf[1024];
634             describe_flavor(player_ptr, buf, o_ptr, 0);
635             TERM_COLOR attr = tval_to_attr[enum2i(tval) % 128];
636             term_addstr(-1, attr, buf);
637         }
638
639         ++term_y;
640     }
641 }
642
643 /*!
644  * @brief (y,x) のアイテム一覧をサブウィンドウに表示する / display item at (y,x) in sub-windows
645  */
646 void fix_floor_item_list(PlayerType *player_ptr, const int y, const int x)
647 {
648     for (int j = 0; j < 8; j++) {
649         if (!angband_terms[j]) {
650             continue;
651         }
652         if (angband_terms[j]->never_fresh) {
653             continue;
654         }
655         if (!(window_flag[j] & PW_FLOOR_ITEM_LIST)) {
656             continue;
657         }
658
659         term_type *old = game_term;
660         term_activate(angband_terms[j]);
661
662         display_floor_item_list(player_ptr, y, x);
663         term_fresh();
664
665         term_activate(old);
666     }
667 }
668
669 /*!
670  * @brief 発見済みのアイテム一覧を作成し、表示する
671  * @param プレイヤー情報への参照ポインタ
672  */
673 static void display_found_item_list(PlayerType *player_ptr)
674 {
675     // Term の行数を取得。
676     TERM_LEN term_h;
677     TERM_LEN term_w;
678     term_get_size(&term_w, &term_h);
679
680     if (term_h <= 0) {
681         return;
682     }
683
684     auto *floor_ptr = player_ptr->current_floor_ptr;
685
686     // 所持品一覧と同じ順にソートする
687     // あらかじめfloor_ptr->o_list から↓項目を取り除く
688     // bi_idが0
689     // OM_FOUNDフラグが立っていない
690     // ItemKindTypeがGOLD
691     std::vector<ItemEntity *> found_item_list;
692     for (auto &item : floor_ptr->o_list) {
693         auto item_entity_ptr = &item;
694         if (item_entity_ptr->bi_id > 0 && item_entity_ptr->marked.has(OmType::FOUND) && item_entity_ptr->bi_key.tval() != ItemKindType::GOLD) {
695             found_item_list.push_back(item_entity_ptr);
696         }
697     }
698
699     std::sort(
700         found_item_list.begin(), found_item_list.end(),
701         [player_ptr](ItemEntity *left, ItemEntity *right) -> bool {
702             return object_sort_comp(player_ptr, left, left->get_price(), right);
703         });
704
705     term_clear();
706     term_gotoxy(0, 0);
707
708     // 先頭行を書く。
709     term_addstr(-1, TERM_WHITE, _("発見済みのアイテム一覧", "Found items"));
710
711     // 発見済みのアイテムを表示
712     TERM_LEN term_y = 1;
713     for (auto item : found_item_list) {
714         // 途中で行数が足りなくなったら終了。
715         if (term_y >= term_h) {
716             break;
717         }
718
719         term_gotoxy(0, term_y);
720
721         // アイテムシンボル表示
722         const auto symbol_code = item->get_symbol();
723         const std::string symbol = format(" %c ", symbol_code);
724         const auto color_code_for_symbol = item->get_color();
725         term_addstr(-1, color_code_for_symbol, symbol.data());
726
727         // アイテム名表示
728         char temp[512];
729         describe_flavor(player_ptr, temp, item, 0);
730         const std::string item_description(temp);
731         const auto color_code_for_item = tval_to_attr[enum2i(item->bi_key.tval()) % 128];
732         term_addstr(-1, color_code_for_item, item_description.data());
733
734         // アイテム座標表示
735         const std::string item_location = format("(X:%3d Y:%3d)", item->ix, item->iy);
736         prt(item_location.data(), term_y, term_w - item_location.length() - 1);
737
738         ++term_y;
739     }
740 }
741
742 /*!
743  * @brief 発見済みのアイテム一覧をサブウィンドウに表示する
744  */
745 void fix_found_item_list(PlayerType *player_ptr)
746 {
747     for (int j = 0; j < 8; j++) {
748         if (!angband_terms[j]) {
749             continue;
750         }
751         if (angband_terms[j]->never_fresh) {
752             continue;
753         }
754         if (none_bits(window_flag[j], PW_FOUND_ITEM_LIST)) {
755             continue;
756         }
757
758         term_type *old = game_term;
759         term_activate(angband_terms[j]);
760
761         display_found_item_list(player_ptr);
762         term_fresh();
763
764         term_activate(old);
765     }
766 }
767
768 /*!
769  * @brief サブウィンドウに所持品、装備品リストの表示を行う /
770  * Flip "inven" and "equip" in any sub-windows
771  */
772 void toggle_inventory_equipment(PlayerType *player_ptr)
773 {
774     for (auto i = 0U; i < angband_terms.size(); ++i) {
775         if (!angband_terms[i]) {
776             continue;
777         }
778
779         if (window_flag[i] & (PW_INVEN)) {
780             window_flag[i] &= ~(PW_INVEN);
781             window_flag[i] |= (PW_EQUIP);
782             player_ptr->window_flags |= (PW_EQUIP);
783             continue;
784         }
785
786         if (window_flag[i] & PW_EQUIP) {
787             window_flag[i] &= ~(PW_EQUIP);
788             window_flag[i] |= PW_INVEN;
789             player_ptr->window_flags |= PW_INVEN;
790         }
791     }
792 }