OSDN Git Service

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