OSDN Git Service

[Refactor] #3406 message_str() の戻り値をconcptr からstring に変えた
[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 "mind/mind-explanations-table.h"
15 #include "mind/mind-info.h"
16 #include "mind/mind-sniper.h"
17 #include "mind/mind-types.h"
18 #include "monster-race/monster-race.h"
19 #include "monster-race/race-flags1.h"
20 #include "monster/monster-describer.h"
21 #include "monster/monster-description-types.h"
22 #include "monster/monster-flag-types.h"
23 #include "monster/monster-info.h"
24 #include "monster/monster-status.h"
25 #include "object/item-tester-hooker.h"
26 #include "object/object-info.h"
27 #include "object/object-mark-types.h"
28 #include "player-base/player-class.h"
29 #include "player-info/class-info.h"
30 #include "player/player-status-flags.h"
31 #include "player/player-status-table.h"
32 #include "player/player-status.h"
33 #include "realm/realm-names-table.h"
34 #include "spell-kind/magic-item-recharger.h"
35 #include "spell/spells-execution.h"
36 #include "spell/technic-info-table.h"
37 #include "system/baseitem-info.h"
38 #include "system/floor-type-definition.h"
39 #include "system/grid-type-definition.h"
40 #include "system/item-entity.h"
41 #include "system/monster-entity.h"
42 #include "system/monster-race-info.h"
43 #include "system/player-type-definition.h"
44 #include "system/redrawing-flags-updater.h"
45 #include "system/terrain-type-definition.h"
46 #include "target/target-describer.h"
47 #include "target/target-preparation.h"
48 #include "target/target-setter.h"
49 #include "target/target-types.h"
50 #include "term/gameterm.h"
51 #include "term/screen-processor.h"
52 #include "term/term-color-types.h"
53 #include "term/z-form.h"
54 #include "timed-effect/player-hallucination.h"
55 #include "timed-effect/player-stun.h"
56 #include "timed-effect/timed-effects.h"
57 #include "util/bit-flags-calculator.h"
58 #include "util/int-char-converter.h"
59 #include "view/display-lore.h"
60 #include "view/display-map.h"
61 #include "view/display-messages.h"
62 #include "view/display-player.h"
63 #include "view/object-describer.h"
64 #include "window/main-window-equipments.h"
65 #include "window/main-window-util.h"
66 #include "world/world.h"
67 #include <algorithm>
68 #include <concepts>
69 #include <mutex>
70 #include <sstream>
71 #include <string>
72 #include <util/object-sort.h>
73
74 /*! サブウィンドウ表示用の ItemTester オブジェクト */
75 static std::unique_ptr<ItemTester> fix_item_tester = std::make_unique<AllMatchItemTester>();
76
77 FixItemTesterSetter::FixItemTesterSetter(const ItemTester &item_tester)
78 {
79     fix_item_tester = item_tester.clone();
80 }
81
82 FixItemTesterSetter::~FixItemTesterSetter()
83 {
84     fix_item_tester = std::make_unique<AllMatchItemTester>();
85 }
86
87 /*!
88  * @brief サブウィンドウの描画を行う
89  *
90  * pw_flag で指定したウィンドウフラグが設定されているサブウィンドウに対し描画を行う。
91  * 描画は display_func で指定したコールバック関数で行う。
92  *
93  * @param pw_flag 描画を行うフラグ
94  * @param display_func 描画を行う関数
95  */
96 static void display_sub_windows(SubWindowRedrawingFlag pw_flag, std::invocable auto display_func)
97 {
98     auto current_term = game_term;
99
100     for (auto i = 0U; i < angband_terms.size(); ++i) {
101         auto term = angband_terms[i];
102         if (term == nullptr) {
103             continue;
104         }
105
106         if (!g_window_flags[i].has(pw_flag)) {
107             continue;
108         }
109
110         term_activate(term);
111         display_func();
112         term_fresh();
113     }
114
115     term_activate(current_term);
116 }
117
118 /*!
119  * @brief サブウィンドウに所持品一覧を表示する / Hack -- display inventory in sub-windows
120  * @param player_ptr プレイヤーへの参照ポインタ
121  */
122 void fix_inventory(PlayerType *player_ptr)
123 {
124     display_sub_windows(SubWindowRedrawingFlag::INVENTORY,
125         [player_ptr] {
126             display_inventory(player_ptr, *fix_item_tester);
127         });
128 }
129
130 /*!
131  * @brief モンスターの現在数を一行で表現する / Print monster info in line
132  * @param x 表示列
133  * @param y 表示行
134  * @param m_ptr 思い出を表示するモンスター情報の参照ポインタ
135  * @param n_same モンスター数の現在数
136  * @param n_awake 起きている数
137  * @details
138  * <pre>
139  * nnn X LV name
140  *  nnn : number or unique(U) or wanted unique(W)
141  *  X   : symbol of monster
142  *  LV  : monster lv if known
143  *  name: name of monster
144  * </pre>
145  */
146 static void print_monster_line(TERM_LEN x, TERM_LEN y, MonsterEntity *m_ptr, int n_same, int n_awake)
147 {
148     std::string buf;
149     MonsterRaceId r_idx = m_ptr->ap_r_idx;
150     auto *r_ptr = &monraces_info[r_idx];
151
152     term_erase(0, y, 255);
153     term_gotoxy(x, y);
154     if (!r_ptr) {
155         return;
156     }
157     if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
158         buf = format(_("%3s(覚%2d)", "%3s(%2d)"), MonsterRace(r_idx).is_bounty(true) ? "  W" : "  U", n_awake);
159     } else {
160         buf = format(_("%3d(覚%2d)", "%3d(%2d)"), n_same, n_awake);
161     }
162     term_addstr(-1, TERM_WHITE, buf);
163
164     term_addstr(-1, TERM_WHITE, " ");
165     term_add_bigch(r_ptr->x_attr, r_ptr->x_char);
166
167     if (r_ptr->r_tkills && m_ptr->mflag2.has_not(MonsterConstantFlagType::KAGE)) {
168         buf = format(" %2d", (int)r_ptr->level);
169     } else {
170         buf = " ??";
171     }
172
173     term_addstr(-1, TERM_WHITE, buf);
174
175     term_addstr(-1, TERM_WHITE, format(" %s ", r_ptr->name.data()));
176 }
177
178 /*!
179  * @brief モンスターの出現リストを表示する / Print monster info in line
180  * @param x 表示列
181  * @param y 表示行
182  * @param max_lines 最大何行描画するか
183  */
184 void print_monster_list(FloorType *floor_ptr, const std::vector<MONSTER_IDX> &monster_list, TERM_LEN x, TERM_LEN y, TERM_LEN max_lines)
185 {
186     TERM_LEN line = y;
187     struct info {
188         MonsterEntity *monster_entity;
189         int visible_count; // 現在数
190         int awake_count; // 起きている数
191     };
192
193     // 出現リスト表示用のデータ
194     std::vector<info> monster_list_info;
195
196     // 描画に必要なデータを集める
197     for (auto monster_index : monster_list) {
198         auto m_ptr = &floor_ptr->m_list[monster_index];
199
200         if (m_ptr->is_pet()) {
201             continue;
202         } // pet
203         if (!MonsterRace(m_ptr->r_idx).is_valid()) {
204             continue;
205         } // dead?
206
207         // ソート済みなので同じモンスターは連続する.これを利用して同じモンスターをカウント,まとめて表示する.
208         if (monster_list_info.empty() || (monster_list_info.back().monster_entity->ap_r_idx != m_ptr->ap_r_idx)) {
209             monster_list_info.push_back({ m_ptr, 0, 0 });
210         }
211
212         // 出現数をカウント
213         monster_list_info.back().visible_count++;
214
215         // 起きているモンスターをカウント
216         if (!m_ptr->is_asleep()) {
217             monster_list_info.back().awake_count++;
218         }
219     }
220
221     // 集めたデータを元にリストを表示する
222     for (const auto &info : monster_list_info) {
223         print_monster_line(x, line++, info.monster_entity, info.visible_count, info.awake_count);
224
225         // 行数が足りなくなったら中断。
226         if (line - y == max_lines) {
227             // 行数が足りなかった場合、最終行にその旨表示。
228             term_addstr(-1, TERM_WHITE, "-- and more --");
229             break;
230         }
231     }
232
233     for (; line < max_lines; line++) {
234         term_erase(0, line, 255);
235     }
236 }
237
238 static void print_pet_list_oneline(PlayerType *player_ptr, const MonsterEntity &monster, TERM_LEN x, TERM_LEN y, TERM_LEN width)
239 {
240     const auto &monrace = monraces_info[monster.ap_r_idx];
241     const auto name = monster_desc(player_ptr, &monster, MD_ASSUME_VISIBLE | MD_INDEF_VISIBLE | MD_NO_OWNER);
242     const auto [bar_color, bar_len] = monster.get_hp_bar_data();
243     const auto is_visible = monster.ml && !player_ptr->effects()->hallucination()->is_hallucinated();
244
245     term_erase(0, y, 255);
246     if (is_visible) {
247         term_putstr(x, y, -1, TERM_WHITE, "[----------]");
248         term_putstr(x + 1, y, bar_len, bar_color, "**********");
249     }
250
251     term_gotoxy(x + 13, y);
252     term_add_bigch(monrace.x_attr, monrace.x_char);
253     term_addstr(-1, TERM_WHITE, " ");
254     term_addstr(-1, TERM_WHITE, name);
255
256     if (width >= 50) {
257         const auto location = format(" (X:%3d Y:%3d)", monster.fx, monster.fy);
258         prt(is_visible ? location : "", y, width - location.length());
259     }
260 }
261
262 static void print_pet_list(PlayerType *player_ptr, const std::vector<MONSTER_IDX> &pets, TERM_LEN x, TERM_LEN y, TERM_LEN width, TERM_LEN height)
263 {
264     for (auto n = 0U; n < pets.size(); ++n) {
265         const auto &monster = player_ptr->current_floor_ptr->m_list[pets[n]];
266         const int line = y + n;
267
268         print_pet_list_oneline(player_ptr, monster, x, line, width);
269
270         if ((line == height - 2) && (n < pets.size() - 2)) {
271             term_erase(0, line + 1, 255);
272             term_putstr(x, line + 1, -1, TERM_WHITE, "-- and more --");
273             break;
274         }
275     }
276
277     for (int n = pets.size(); n < height; ++n) {
278         term_erase(0, y + n, 255);
279     }
280 }
281
282 /*!
283  * @brief 出現中モンスターのリストをサブウィンドウに表示する / Hack -- display monster list in sub-windows
284  * @param player_ptr プレイヤーへの参照ポインタ
285  */
286 void fix_monster_list(PlayerType *player_ptr)
287 {
288     static std::vector<MONSTER_IDX> monster_list;
289     std::once_flag once;
290
291     display_sub_windows(SubWindowRedrawingFlag::SIGHT_MONSTERS,
292         [player_ptr, &once] {
293             int w, h;
294             term_get_size(&w, &h);
295             std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
296             print_monster_list(player_ptr->current_floor_ptr, monster_list, 0, 0, h);
297         });
298
299     if (use_music && has_monster_music) {
300         std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
301         select_monster_music(player_ptr, monster_list);
302     }
303 }
304
305 /*!
306  * @brief 視界内のペットのリストをサブウィンドウに表示する
307  */
308 void fix_pet_list(PlayerType *player_ptr)
309 {
310     display_sub_windows(SubWindowRedrawingFlag::PETS,
311         [player_ptr] {
312             int w, h;
313             term_get_size(&w, &h);
314             const auto pets = target_pets_prepare(player_ptr);
315             print_pet_list(player_ptr, pets, 0, 0, w, h);
316         });
317 }
318
319 /*!
320  * @brief 装備アイテム一覧を表示する /
321  * Choice window "shadow" of the "show_equip()" function
322  */
323 static void display_equipment(PlayerType *player_ptr, const ItemTester &item_tester)
324 {
325     if (!player_ptr || !player_ptr->inventory_list) {
326         return;
327     }
328
329     TERM_LEN wid, hgt;
330     term_get_size(&wid, &hgt);
331     byte attr = TERM_WHITE;
332     for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
333         int cur_row = i - INVEN_MAIN_HAND;
334         if (cur_row >= hgt) {
335             break;
336         }
337
338         auto o_ptr = &player_ptr->inventory_list[i];
339         auto do_disp = player_ptr->select_ring_slot ? is_ring_slot(i) : item_tester.okay(o_ptr);
340         std::string tmp_val = "   ";
341
342         if (do_disp) {
343             tmp_val[0] = index_to_label(i);
344             tmp_val[1] = ')';
345         }
346
347         int cur_col = 3;
348         term_erase(cur_col, cur_row, 255);
349         term_putstr(0, cur_row, cur_col, TERM_WHITE, tmp_val);
350
351         std::string item_name;
352         auto is_two_handed = (i == INVEN_MAIN_HAND) && can_attack_with_sub_hand(player_ptr);
353         is_two_handed |= (i == INVEN_SUB_HAND) && can_attack_with_main_hand(player_ptr);
354         if (is_two_handed && has_two_handed_weapons(player_ptr)) {
355             item_name = _("(武器を両手持ち)", "(wielding with two-hands)");
356             attr = TERM_WHITE;
357         } else {
358             item_name = describe_flavor(player_ptr, o_ptr, 0);
359             attr = tval_to_attr[enum2i(o_ptr->bi_key.tval()) % 128];
360         }
361
362         int n = item_name.length();
363         if (o_ptr->timeout) {
364             attr = TERM_L_DARK;
365         }
366
367         if (show_item_graph) {
368             const auto a = o_ptr->get_color();
369             const auto c = o_ptr->get_symbol();
370             term_queue_bigchar(cur_col, cur_row, a, c, 0, 0);
371             if (use_bigtile) {
372                 cur_col++;
373             }
374
375             cur_col += 2;
376         }
377
378         term_putstr(cur_col, cur_row, n, attr, item_name);
379         if (show_weights) {
380             int wgt = o_ptr->weight * o_ptr->number;
381             tmp_val = format(_("%3d.%1d kg", "%3d.%1d lb"), _(lb_to_kg_integer(wgt), wgt / 10), _(lb_to_kg_fraction(wgt), wgt % 10));
382             prt(tmp_val, cur_row, wid - (show_labels ? 28 : 9));
383         }
384
385         if (show_labels) {
386             term_putstr(wid - 20, cur_row, -1, TERM_WHITE, " <-- ");
387             prt(mention_use(player_ptr, i), cur_row, wid - 15);
388         }
389     }
390
391     for (int i = INVEN_TOTAL - INVEN_MAIN_HAND; i < hgt; i++) {
392         term_erase(0, i, 255);
393     }
394 }
395
396 /*!
397  * @brief 現在の装備品をサブウィンドウに表示する /
398  * Hack -- display equipment in sub-windows
399  * @param player_ptr プレイヤーへの参照ポインタ
400  */
401 void fix_equip(PlayerType *player_ptr)
402 {
403     display_sub_windows(SubWindowRedrawingFlag::EQUIPMENT,
404         [player_ptr] {
405             display_equipment(player_ptr, *fix_item_tester);
406         });
407 }
408
409 /*!
410  * @brief 現在のプレイヤーステータスをサブウィンドウに表示する /
411  * @param player_ptr プレイヤーへの参照ポインタ
412  * Hack -- display character in sub-windows
413  */
414 void fix_player(PlayerType *player_ptr)
415 {
416     update_playtime();
417     display_sub_windows(SubWindowRedrawingFlag::PLAYER,
418         [player_ptr] {
419             display_player(player_ptr, 0);
420         });
421 }
422
423 /*!
424  * @brief ゲームメッセージ履歴をサブウィンドウに表示する /
425  * Hack -- display recent messages in sub-windows
426  * Adjust for width and split messages
427  */
428 void fix_message(void)
429 {
430     display_sub_windows(SubWindowRedrawingFlag::MESSAGE,
431         [] {
432             TERM_LEN w, h;
433             term_get_size(&w, &h);
434             for (short i = 0; i < h; i++) {
435                 term_putstr(0, (h - 1) - i, -1, (byte)((i < now_message) ? TERM_WHITE : TERM_SLATE), message_str(i));
436                 TERM_LEN x, y;
437                 term_locate(&x, &y);
438                 term_erase(x, y, 255);
439             }
440         });
441 }
442
443 /*!
444  * @brief 簡易マップをサブウィンドウに表示する /
445  * Hack -- display overhead view in sub-windows
446  * Adjust for width and split messages
447  * @param player_ptr プレイヤーへの参照ポインタ
448  * @details
449  * Note that the "player" symbol does NOT appear on the map.
450  */
451 void fix_overhead(PlayerType *player_ptr)
452 {
453     display_sub_windows(SubWindowRedrawingFlag::OVERHEAD,
454         [player_ptr] {
455             TERM_LEN wid, hgt;
456             term_get_size(&wid, &hgt);
457             if (wid > COL_MAP + 2 && hgt > ROW_MAP + 2) {
458                 int cy, cx;
459                 display_map(player_ptr, &cy, &cx);
460             }
461         });
462 }
463
464 /*!
465  * @brief 自分の周辺の地形をTermに表示する
466  * @param プレイヤー情報への参照ポインタ
467  */
468 static void display_dungeon(PlayerType *player_ptr)
469 {
470     TERM_COLOR ta = 0;
471     auto tc = '\0';
472
473     for (TERM_LEN x = player_ptr->x - game_term->wid / 2 + 1; x <= player_ptr->x + game_term->wid / 2; x++) {
474         for (TERM_LEN y = player_ptr->y - game_term->hgt / 2 + 1; y <= player_ptr->y + game_term->hgt / 2; y++) {
475             TERM_COLOR a;
476             char c;
477             if (!in_bounds2(player_ptr->current_floor_ptr, y, x)) {
478                 auto *f_ptr = &terrains_info[feat_none];
479                 a = f_ptr->x_attr[F_LIT_STANDARD];
480                 c = f_ptr->x_char[F_LIT_STANDARD];
481                 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);
482                 continue;
483             }
484
485             map_info(player_ptr, y, x, &a, &c, &ta, &tc);
486
487             if (!use_graphics) {
488                 if (w_ptr->timewalk_m_idx) {
489                     a = TERM_DARK;
490                 } else if (is_invuln(player_ptr) || player_ptr->timewalk) {
491                     a = TERM_WHITE;
492                 } else if (player_ptr->wraith_form) {
493                     a = TERM_L_DARK;
494                 }
495             }
496
497             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);
498         }
499     }
500 }
501
502 /*!
503  * @brief 自分の周辺のダンジョンの地形をサブウィンドウに表示する / display dungeon view around player in a sub window
504  * @param player_ptr プレイヤーへの参照ポインタ
505  */
506 void fix_dungeon(PlayerType *player_ptr)
507 {
508     display_sub_windows(SubWindowRedrawingFlag::DUNGEON,
509         [player_ptr] {
510             display_dungeon(player_ptr);
511         });
512 }
513
514 /*!
515  * @brief モンスターの思い出をサブウィンドウに表示する /
516  * Hack -- display dungeon view in sub-windows
517  * @param player_ptr プレイヤーへの参照ポインタ
518  */
519 void fix_monster(PlayerType *player_ptr)
520 {
521     if (!MonsterRace(player_ptr->monster_race_idx).is_valid()) {
522         return;
523     }
524     display_sub_windows(SubWindowRedrawingFlag::MONSTER_LORE,
525         [player_ptr] {
526             display_roff(player_ptr);
527         });
528 }
529
530 /*!
531  * @brief ベースアイテム情報をサブウィンドウに表示する /
532  * Hack -- display object recall in sub-windows
533  * @param player_ptr プレイヤーへの参照ポインタ
534  */
535 void fix_object(PlayerType *player_ptr)
536 {
537     display_sub_windows(SubWindowRedrawingFlag::ITEM_KNOWLEDGE,
538         [player_ptr] {
539             display_koff(player_ptr);
540         });
541 }
542
543 /*!
544  * @brief 床上のモンスター情報を返す
545  * @param floor_ptr 階の情報への参照ポインタ
546  * @param grid_prt 座標グリッドの情報への参照ポインタ
547  * @return モンスターが見える場合にはモンスター情報への参照ポインタ、それ以外はnullptr
548  * @details
549  * Lookコマンドでカーソルを合わせた場合に合わせてミミックは考慮しない。
550  */
551 static const MonsterEntity *monster_on_floor_items(FloorType *floor_ptr, const grid_type *g_ptr)
552 {
553     if (g_ptr->m_idx == 0) {
554         return nullptr;
555     }
556
557     auto m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
558     if (!m_ptr->is_valid() || !m_ptr->ml) {
559         return nullptr;
560     }
561
562     return m_ptr;
563 }
564
565 /*!
566  * @brief 床上のアイテム一覧を作成し、表示する
567  * @param プレイヤー情報への参照ポインタ
568  * @param y 参照する座標グリッドのy座標
569  * @param x 参照する座標グリッドのx座標
570  */
571 static void display_floor_item_list(PlayerType *player_ptr, const int y, const int x)
572 {
573     // Term の行数を取得。
574     TERM_LEN term_h;
575     {
576         TERM_LEN term_w;
577         term_get_size(&term_w, &term_h);
578     }
579     if (term_h <= 0) {
580         return;
581     }
582
583     term_clear();
584     term_gotoxy(0, 0);
585
586     auto *floor_ptr = player_ptr->current_floor_ptr;
587     const auto *g_ptr = &floor_ptr->grid_array[y][x];
588     std::string line;
589
590     // 先頭行を書く。
591     auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
592     if (player_bold(player_ptr, y, x)) {
593         line = format(_("(X:%03d Y:%03d) あなたの足元のアイテム一覧", "Items at (%03d,%03d) under you"), x, y);
594     } else if (const auto *m_ptr = monster_on_floor_items(floor_ptr, g_ptr); m_ptr != nullptr) {
595         if (is_hallucinated) {
596             line = format(_("(X:%03d Y:%03d) 何か奇妙な物の足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under something strange"), x, y);
597         } else {
598             const MonsterRaceInfo *const r_ptr = &monraces_info[m_ptr->ap_r_idx];
599             line = format(_("(X:%03d Y:%03d) %sの足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under %s"), x, y, r_ptr->name.data());
600         }
601     } else {
602         const TerrainType *const f_ptr = &terrains_info[g_ptr->feat];
603         concptr fn = f_ptr->name.data();
604         std::string buf;
605
606         if (f_ptr->flags.has(TerrainCharacteristics::STORE) || (f_ptr->flags.has(TerrainCharacteristics::BLDG) && !floor_ptr->inside_arena)) {
607             buf = format(_("%sの入口", "on the entrance of %s"), fn);
608         } else if (f_ptr->flags.has(TerrainCharacteristics::WALL)) {
609             buf = format(_("%sの中", "in %s"), fn);
610         } else {
611             buf = format(_("%s", "on %s"), fn);
612         }
613         line = format(_("(X:%03d Y:%03d) %sの上の発見済みアイテム一覧", "Found items at (X:%03d Y:%03d) %s"), x, y, buf.data());
614     }
615     term_addstr(-1, TERM_WHITE, line);
616
617     // (y,x) のアイテムを1行に1個ずつ書く。
618     TERM_LEN term_y = 1;
619     for (const auto o_idx : g_ptr->o_idx_list) {
620         auto *const o_ptr = &floor_ptr->o_list[o_idx];
621         const auto tval = o_ptr->bi_key.tval();
622         if (o_ptr->marked.has_not(OmType::FOUND) || tval == ItemKindType::GOLD) {
623             continue;
624         }
625
626         // 途中で行数が足りなくなったら最終行にその旨追記して終了。
627         if (term_y >= term_h) {
628             term_addstr(-1, TERM_WHITE, "-- more --");
629             break;
630         }
631
632         term_gotoxy(0, term_y);
633
634         if (is_hallucinated) {
635             term_addstr(-1, TERM_WHITE, _("何か奇妙な物", "something strange"));
636         } else {
637             const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
638             TERM_COLOR attr = tval_to_attr[enum2i(tval) % 128];
639             term_addstr(-1, attr, item_name);
640         }
641
642         ++term_y;
643     }
644 }
645
646 /*!
647  * @brief (y,x) のアイテム一覧をサブウィンドウに表示する / display item at (y,x) in sub-windows
648  */
649 void fix_floor_item_list(PlayerType *player_ptr, const int y, const int x)
650 {
651     display_sub_windows(SubWindowRedrawingFlag::FLOOR_ITEMS,
652         [player_ptr, y, x] {
653             display_floor_item_list(player_ptr, y, x);
654         });
655 }
656
657 /*!
658  * @brief 発見済みのアイテム一覧を作成し、表示する
659  * @param プレイヤー情報への参照ポインタ
660  */
661 static void display_found_item_list(PlayerType *player_ptr)
662 {
663     // Term の行数を取得。
664     TERM_LEN term_h;
665     TERM_LEN term_w;
666     term_get_size(&term_w, &term_h);
667
668     if (term_h <= 0) {
669         return;
670     }
671
672     auto *floor_ptr = player_ptr->current_floor_ptr;
673
674     // 所持品一覧と同じ順にソートする
675     // あらかじめfloor_ptr->o_list から↓項目を取り除く
676     // bi_idが0
677     // OM_FOUNDフラグが立っていない
678     // ItemKindTypeがGOLD
679     std::vector<ItemEntity *> found_item_list;
680     for (auto &item : floor_ptr->o_list) {
681         const auto is_item_to_display =
682             item.is_valid() && (item.number > 0) &&
683             item.marked.has(OmType::FOUND) && (item.bi_key.tval() != ItemKindType::GOLD);
684
685         if (is_item_to_display) {
686             found_item_list.push_back(&item);
687         }
688     }
689
690     std::sort(
691         found_item_list.begin(), found_item_list.end(),
692         [player_ptr](ItemEntity *left, ItemEntity *right) -> bool {
693             return object_sort_comp(player_ptr, left, left->get_price(), right);
694         });
695
696     term_clear();
697     term_gotoxy(0, 0);
698
699     // 先頭行を書く。
700     term_addstr(-1, TERM_WHITE, _("発見済みのアイテム一覧", "Found items"));
701
702     // 発見済みのアイテムを表示
703     TERM_LEN term_y = 1;
704     for (auto item : found_item_list) {
705         // 途中で行数が足りなくなったら終了。
706         if (term_y >= term_h) {
707             break;
708         }
709
710         term_gotoxy(0, term_y);
711
712         // アイテムシンボル表示
713         const auto symbol_code = item->get_symbol();
714         const auto symbol = format(" %c ", symbol_code);
715         const auto color_code_for_symbol = item->get_color();
716         term_addstr(-1, color_code_for_symbol, symbol);
717
718         const auto item_name = describe_flavor(player_ptr, item, 0);
719         const auto color_code_for_item = tval_to_attr[enum2i(item->bi_key.tval()) % 128];
720         term_addstr(-1, color_code_for_item, item_name);
721
722         // アイテム座標表示
723         const auto item_location = format("(X:%3d Y:%3d)", item->ix, item->iy);
724         prt(item_location, term_y, term_w - item_location.length() - 1);
725
726         ++term_y;
727     }
728 }
729
730 /*!
731  * @brief 発見済みのアイテム一覧をサブウィンドウに表示する
732  */
733 void fix_found_item_list(PlayerType *player_ptr)
734 {
735     display_sub_windows(SubWindowRedrawingFlag::FOUND_ITEMS,
736         [player_ptr] {
737             display_found_item_list(player_ptr);
738         });
739 }
740
741 /*!
742  * @brief プレイヤーの全既知呪文を表示する / Display all known spells in a window
743  * @param player_ptr プレイヤーへの参照ポインタ
744  * @details
745  * Need to analyze size of the window.
746  * Need more color coding.
747  */
748 static void display_spell_list(PlayerType *player_ptr)
749 {
750     TERM_LEN y, x;
751     int m[9];
752     const magic_type *s_ptr;
753     GAME_TEXT name[MAX_NLEN];
754
755     clear_from(0);
756
757     PlayerClass pc(player_ptr);
758     if (pc.is_every_magic()) {
759         return;
760     }
761
762     if (pc.equals(PlayerClassType::SNIPER)) {
763         display_snipe_list(player_ptr);
764         return;
765     }
766
767     if (pc.has_listed_magics()) {
768         PERCENTAGE minfail = 0;
769         PLAYER_LEVEL plev = player_ptr->lev;
770         PERCENTAGE chance = 0;
771         mind_type spell;
772         MindKindType use_mind;
773         bool use_hp = false;
774
775         y = 1;
776         x = 1;
777
778         prt("", y, x);
779         put_str(_("名前", "Name"), y, x + 5);
780         put_str(_("Lv   MP 失率 効果", "Lv Mana Fail Info"), y, x + 35);
781
782         switch (player_ptr->pclass) {
783         case PlayerClassType::MINDCRAFTER:
784             use_mind = MindKindType::MINDCRAFTER;
785             break;
786         case PlayerClassType::FORCETRAINER:
787             use_mind = MindKindType::KI;
788             break;
789         case PlayerClassType::BERSERKER:
790             use_mind = MindKindType::BERSERKER;
791             use_hp = true;
792             break;
793         case PlayerClassType::MIRROR_MASTER:
794             use_mind = MindKindType::MIRROR_MASTER;
795             break;
796         case PlayerClassType::NINJA:
797             use_mind = MindKindType::NINJUTSU;
798             use_hp = true;
799             break;
800         case PlayerClassType::ELEMENTALIST:
801             use_mind = MindKindType::ELEMENTAL;
802             break;
803         default:
804             use_mind = MindKindType::MINDCRAFTER;
805             break;
806         }
807
808         for (int i = 0; i < MAX_MIND_POWERS; i++) {
809             byte a = TERM_WHITE;
810             spell = mind_powers[static_cast<int>(use_mind)].info[i];
811             if (spell.min_lev > plev) {
812                 break;
813             }
814
815             chance = spell.fail;
816             chance -= 3 * (player_ptr->lev - spell.min_lev);
817             chance -= 3 * (adj_mag_stat[player_ptr->stat_index[mp_ptr->spell_stat]] - 1);
818             if (!use_hp) {
819                 if (spell.mana_cost > player_ptr->csp) {
820                     chance += 5 * (spell.mana_cost - player_ptr->csp);
821                     a = TERM_ORANGE;
822                 }
823             } else {
824                 if (spell.mana_cost > player_ptr->chp) {
825                     chance += 100;
826                     a = TERM_RED;
827                 }
828             }
829
830             minfail = adj_mag_fail[player_ptr->stat_index[mp_ptr->spell_stat]];
831             if (chance < minfail) {
832                 chance = minfail;
833             }
834
835             auto player_stun = player_ptr->effects()->stun();
836             chance += player_stun->get_magic_chance_penalty();
837             if (chance > 95) {
838                 chance = 95;
839             }
840
841             const auto comment = mindcraft_info(player_ptr, use_mind, i);
842             constexpr auto fmt = "  %c) %-30s%2d %4d %3d%%%s";
843             term_putstr(x, y + i + 1, -1, a, format(fmt, I2A(i), spell.name, spell.min_lev, spell.mana_cost, chance, comment.data()));
844         }
845
846         return;
847     }
848
849     if (REALM_NONE == player_ptr->realm1) {
850         return;
851     }
852
853     for (int j = 0; j < ((player_ptr->realm2 > REALM_NONE) ? 2 : 1); j++) {
854         m[j] = 0;
855         y = (j < 3) ? 0 : (m[j - 3] + 2);
856         x = 27 * (j % 3);
857         int n = 0;
858         for (int i = 0; i < 32; i++) {
859             byte a = TERM_WHITE;
860
861             if (!is_magic((j < 1) ? player_ptr->realm1 : player_ptr->realm2)) {
862                 s_ptr = &technic_info[((j < 1) ? player_ptr->realm1 : player_ptr->realm2) - MIN_TECHNIC][i % 32];
863             } else {
864                 s_ptr = &mp_ptr->info[((j < 1) ? player_ptr->realm1 : player_ptr->realm2) - 1][i % 32];
865             }
866
867             const auto realm = (j < 1) ? player_ptr->realm1 : player_ptr->realm2;
868             const auto spell_name = exe_spell(player_ptr, realm, i % 32, SpellProcessType::NAME);
869             strcpy(name, spell_name->data());
870
871             if (s_ptr->slevel >= 99) {
872                 strcpy(name, _("(判読不能)", "(illegible)"));
873                 a = TERM_L_DARK;
874             } else if ((j < 1) ? ((player_ptr->spell_forgotten1 & (1UL << i))) : ((player_ptr->spell_forgotten2 & (1UL << (i % 32))))) {
875                 a = TERM_ORANGE;
876             } else if (!((j < 1) ? (player_ptr->spell_learned1 & (1UL << i)) : (player_ptr->spell_learned2 & (1UL << (i % 32))))) {
877                 a = TERM_RED;
878             } else if (!((j < 1) ? (player_ptr->spell_worked1 & (1UL << i)) : (player_ptr->spell_worked2 & (1UL << (i % 32))))) {
879                 a = TERM_YELLOW;
880             }
881
882             m[j] = y + n;
883             term_putstr(x, m[j], -1, a, format("%c/%c) %-20.20s", I2A(n / 8), I2A(n % 8), name));
884             n++;
885         }
886     }
887 }
888
889 /*!
890  * @brief 現在の習得済魔法をサブウィンドウに表示する /
891  * @param player_ptr プレイヤーへの参照ポインタ
892  * Hack -- display spells in sub-windows
893  */
894 void fix_spell(PlayerType *player_ptr)
895 {
896     display_sub_windows(SubWindowRedrawingFlag::SPELL,
897         [player_ptr] {
898             display_spell_list(player_ptr);
899         });
900 }
901
902 /*!
903  * @brief サブウィンドウに所持品、装備品リストの表示を行う
904  */
905 void toggle_inventory_equipment()
906 {
907     auto &rfu = RedrawingFlagsUpdater::get_instance();
908     for (auto i = 0U; i < angband_terms.size(); ++i) {
909         if (!angband_terms[i]) {
910             continue;
911         }
912
913         if (g_window_flags[i].has(SubWindowRedrawingFlag::INVENTORY)) {
914             g_window_flags[i].reset(SubWindowRedrawingFlag::INVENTORY);
915             g_window_flags[i].set(SubWindowRedrawingFlag::EQUIPMENT);
916             rfu.set_flag(SubWindowRedrawingFlag::EQUIPMENT);
917             continue;
918         }
919
920         if (g_window_flags[i].has(SubWindowRedrawingFlag::EQUIPMENT)) {
921             g_window_flags[i].reset(SubWindowRedrawingFlag::EQUIPMENT);
922             g_window_flags[i].set(SubWindowRedrawingFlag::INVENTORY);
923             rfu.set_flag(SubWindowRedrawingFlag::INVENTORY);
924         }
925     }
926 }