OSDN Git Service

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