OSDN Git Service

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