1 #include "window/display-sub-windows.h"
2 #include "core/window-redrawer.h"
3 #include "flavor/flavor-describer.h"
4 #include "floor/cave.h"
5 #include "game-option/option-flags.h"
6 #include "game-option/special-options.h"
7 #include "game-option/text-display-options.h"
8 #include "grid/feature.h"
9 #include "inventory/inventory-describer.h"
10 #include "inventory/inventory-slot-types.h"
11 #include "inventory/inventory-util.h"
12 #include "locale/japanese.h"
13 #include "main/sound-of-music.h"
14 #include "monster-race/monster-race.h"
15 #include "monster-race/race-flags1.h"
16 #include "monster/monster-flag-types.h"
17 #include "monster/monster-info.h"
18 #include "monster/monster-status.h"
19 #include "object/item-tester-hooker.h"
20 #include "object/object-info.h"
21 #include "object/object-mark-types.h"
22 #include "player/player-status-flags.h"
23 #include "player/player-status.h"
24 #include "spell-kind/magic-item-recharger.h"
25 #include "system/baseitem-info-definition.h"
26 #include "system/floor-type-definition.h"
27 #include "system/grid-type-definition.h"
28 #include "system/item-entity.h"
29 #include "system/monster-entity.h"
30 #include "system/monster-race-info.h"
31 #include "system/player-type-definition.h"
32 #include "system/terrain-type-definition.h"
33 #include "target/target-describer.h"
34 #include "target/target-preparation.h"
35 #include "target/target-setter.h"
36 #include "target/target-types.h"
37 #include "term/gameterm.h"
38 #include "term/screen-processor.h"
39 #include "term/term-color-types.h"
40 #include "timed-effect/player-hallucination.h"
41 #include "timed-effect/timed-effects.h"
42 #include "util/bit-flags-calculator.h"
43 #include "view/display-lore.h"
44 #include "view/display-map.h"
45 #include "view/display-messages.h"
46 #include "view/display-player.h"
47 #include "view/object-describer.h"
48 #include "window/main-window-equipments.h"
49 #include "window/main-window-util.h"
50 #include "world/world.h"
55 /*! サブウィンドウ表示用の ItemTester オブジェクト */
56 static std::unique_ptr<ItemTester> fix_item_tester = std::make_unique<AllMatchItemTester>();
58 FixItemTesterSetter::FixItemTesterSetter(const ItemTester &item_tester)
60 fix_item_tester = item_tester.clone();
63 FixItemTesterSetter::~FixItemTesterSetter()
65 fix_item_tester = std::make_unique<AllMatchItemTester>();
69 * @brief サブウィンドウに所持品一覧を表示する / Hack -- display inventory in sub-windows
70 * @param player_ptr プレイヤーへの参照ポインタ
72 void fix_inventory(PlayerType *player_ptr)
74 for (int j = 0; j < 8; j++) {
75 term_type *old = game_term;
76 if (!angband_term[j]) {
80 if (!(window_flag[j] & (PW_INVEN))) {
84 term_activate(angband_term[j]);
85 display_inventory(player_ptr, *fix_item_tester);
92 * @brief モンスターの現在数を一行で表現する / Print monster info in line
95 * @param m_ptr 思い出を表示するモンスター情報の参照ポインタ
96 * @param n_same モンスターの数の現在数
100 * nnn : number or unique(U) or wanted unique(W)
101 * X : symbol of monster
102 * LV : monster lv if known
103 * name: name of monster
106 static void print_monster_line(TERM_LEN x, TERM_LEN y, MonsterEntity *m_ptr, int n_same)
109 MonsterRaceId r_idx = m_ptr->ap_r_idx;
110 auto *r_ptr = &monraces_info[r_idx];
112 term_erase(0, y, 255);
117 if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
118 term_addstr(-1, TERM_WHITE, MonsterRace(r_idx).is_bounty(true) ? " W" : " U");
120 sprintf(buf, "%3d", n_same);
121 term_addstr(-1, TERM_WHITE, buf);
124 term_addstr(-1, TERM_WHITE, " ");
125 term_add_bigch(r_ptr->x_attr, r_ptr->x_char);
127 if (r_ptr->r_tkills && m_ptr->mflag2.has_not(MonsterConstantFlagType::KAGE)) {
128 sprintf(buf, " %2d", (int)r_ptr->level);
133 term_addstr(-1, TERM_WHITE, buf);
135 sprintf(buf, " %s ", r_ptr->name.data());
136 term_addstr(-1, TERM_WHITE, buf);
140 * @brief モンスターの出現リストを表示する / Print monster info in line
143 * @param max_lines 最大何行描画するか
145 void print_monster_list(FloorType *floor_ptr, const std::vector<MONSTER_IDX> &monster_list, TERM_LEN x, TERM_LEN y, TERM_LEN max_lines)
148 MonsterEntity *last_mons = nullptr;
151 for (i = 0; i < monster_list.size(); i++) {
152 auto m_ptr = &floor_ptr->m_list[monster_list[i]];
153 if (m_ptr->is_pet()) {
156 if (!MonsterRace(m_ptr->r_idx).is_valid()) {
160 // ソート済みなので同じモンスターは連続する.これを利用して同じモンスターをカウント,まとめて表示する.
169 if (last_mons->ap_r_idx == m_ptr->ap_r_idx) {
171 continue; // 表示処理を次に回す
174 // last_mons と m_ptr が(見た目が)異なるなら、last_mons とその数を出力。
175 // m_ptr を新たな last_mons とする。
176 print_monster_line(x, line++, last_mons, n_same);
181 if (line - y == max_lines) {
186 if (i != monster_list.size()) {
187 // 行数が足りなかった場合、最終行にその旨表示。
188 term_addstr(-1, TERM_WHITE, "-- and more --");
190 // 行数が足りていれば last_mons とその数を出力し、残りの行をクリア。
192 print_monster_line(x, line++, last_mons, n_same);
194 for (; line < max_lines; line++) {
195 term_erase(0, line, 255);
201 * @brief 出現中モンスターのリストをサブウィンドウに表示する / Hack -- display monster list in sub-windows
202 * @param player_ptr プレイヤーへの参照ポインタ
204 void fix_monster_list(PlayerType *player_ptr)
206 static std::vector<MONSTER_IDX> monster_list;
209 for (int j = 0; j < 8; j++) {
210 term_type *old = game_term;
211 if (!angband_term[j]) {
214 if (!(window_flag[j] & PW_MONSTER_LIST)) {
217 if (angband_term[j]->never_fresh) {
221 term_activate(angband_term[j]);
223 term_get_size(&w, &h);
224 std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
225 print_monster_list(player_ptr->current_floor_ptr, monster_list, 0, 0, h);
230 if (use_music && has_monster_music) {
231 std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
232 select_monster_music(player_ptr, monster_list);
237 * @brief 装備アイテム一覧を表示する /
238 * Choice window "shadow" of the "show_equip()" function
240 static void display_equipment(PlayerType *player_ptr, const ItemTester &item_tester)
242 if (!player_ptr || !player_ptr->inventory_list) {
247 term_get_size(&wid, &hgt);
249 TERM_COLOR attr = TERM_WHITE;
251 GAME_TEXT o_name[MAX_NLEN];
252 for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
253 int cur_row = i - INVEN_MAIN_HAND;
254 if (cur_row >= hgt) {
258 auto o_ptr = &player_ptr->inventory_list[i];
259 auto do_disp = player_ptr->select_ring_slot ? is_ring_slot(i) : item_tester.okay(o_ptr);
260 strcpy(tmp_val, " ");
263 tmp_val[0] = index_to_label(i);
268 term_erase(cur_col, cur_row, 255);
269 term_putstr(0, cur_row, cur_col, TERM_WHITE, tmp_val);
271 if ((((i == INVEN_MAIN_HAND) && can_attack_with_sub_hand(player_ptr)) || ((i == INVEN_SUB_HAND) && can_attack_with_main_hand(player_ptr))) && has_two_handed_weapons(player_ptr)) {
272 strcpy(o_name, _("(武器を両手持ち)", "(wielding with two-hands)"));
275 describe_flavor(player_ptr, o_name, o_ptr, 0);
276 attr = tval_to_attr[enum2i(o_ptr->tval) % 128];
279 int n = strlen(o_name);
280 if (o_ptr->timeout) {
284 if (show_item_graph) {
285 const auto a = o_ptr->get_color();
286 const auto c = o_ptr->get_symbol();
287 term_queue_bigchar(cur_col, cur_row, a, c, 0, 0);
295 term_putstr(cur_col, cur_row, n, attr, o_name);
297 int wgt = o_ptr->weight * o_ptr->number;
298 sprintf(tmp_val, _("%3d.%1d kg", "%3d.%1d lb"), _(lb_to_kg_integer(wgt), wgt / 10), _(lb_to_kg_fraction(wgt), wgt % 10));
299 prt(tmp_val, cur_row, wid - (show_labels ? 28 : 9));
303 term_putstr(wid - 20, cur_row, -1, TERM_WHITE, " <-- ");
304 prt(mention_use(player_ptr, i), cur_row, wid - 15);
308 for (int i = INVEN_TOTAL - INVEN_MAIN_HAND; i < hgt; i++) {
309 term_erase(0, i, 255);
314 * @brief 現在の装備品をサブウィンドウに表示する /
315 * Hack -- display equipment in sub-windows
316 * @param player_ptr プレイヤーへの参照ポインタ
318 void fix_equip(PlayerType *player_ptr)
320 for (int j = 0; j < 8; j++) {
321 term_type *old = game_term;
322 if (!angband_term[j]) {
325 if (!(window_flag[j] & (PW_EQUIP))) {
329 term_activate(angband_term[j]);
330 display_equipment(player_ptr, *fix_item_tester);
337 * @brief 現在のプレイヤーステータスをサブウィンドウに表示する /
338 * @param player_ptr プレイヤーへの参照ポインタ
339 * Hack -- display character in sub-windows
341 void fix_player(PlayerType *player_ptr)
343 for (int j = 0; j < 8; j++) {
344 term_type *old = game_term;
345 if (!angband_term[j]) {
349 if (!(window_flag[j] & (PW_PLAYER))) {
353 term_activate(angband_term[j]);
355 (void)display_player(player_ptr, 0);
362 * @brief ゲームメッセージ履歴をサブウィンドウに表示する /
363 * Hack -- display recent messages in sub-windows
364 * Adjust for width and split messages
366 void fix_message(void)
368 for (int j = 0; j < 8; j++) {
369 term_type *old = game_term;
370 if (!angband_term[j]) {
374 if (!(window_flag[j] & (PW_MESSAGE))) {
378 term_activate(angband_term[j]);
380 term_get_size(&w, &h);
381 for (int i = 0; i < h; i++) {
382 term_putstr(0, (h - 1) - i, -1, (byte)((i < now_message) ? TERM_WHITE : TERM_SLATE), message_str((int16_t)i));
385 term_erase(x, y, 255);
394 * @brief 簡易マップをサブウィンドウに表示する /
395 * Hack -- display overhead view in sub-windows
396 * Adjust for width and split messages
397 * @param player_ptr プレイヤーへの参照ポインタ
399 * Note that the "player" symbol does NOT appear on the map.
401 void fix_overhead(PlayerType *player_ptr)
403 for (int j = 0; j < 8; j++) {
404 term_type *old = game_term;
406 if (!angband_term[j]) {
410 if (!(window_flag[j] & (PW_OVERHEAD))) {
414 term_activate(angband_term[j]);
415 term_get_size(&wid, &hgt);
416 if (wid > COL_MAP + 2 && hgt > ROW_MAP + 2) {
418 display_map(player_ptr, &cy, &cx);
427 * @brief 自分の周辺の地形をTermに表示する
428 * @param プレイヤー情報への参照ポインタ
430 static void display_dungeon(PlayerType *player_ptr)
435 for (TERM_LEN x = player_ptr->x - game_term->wid / 2 + 1; x <= player_ptr->x + game_term->wid / 2; x++) {
436 for (TERM_LEN y = player_ptr->y - game_term->hgt / 2 + 1; y <= player_ptr->y + game_term->hgt / 2; y++) {
439 if (!in_bounds2(player_ptr->current_floor_ptr, y, x)) {
440 auto *f_ptr = &terrains_info[feat_none];
441 a = f_ptr->x_attr[F_LIT_STANDARD];
442 c = f_ptr->x_char[F_LIT_STANDARD];
443 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);
447 map_info(player_ptr, y, x, &a, &c, &ta, &tc);
450 if (w_ptr->timewalk_m_idx) {
452 } else if (is_invuln(player_ptr) || player_ptr->timewalk) {
454 } else if (player_ptr->wraith_form) {
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);
465 * @brief 自分の周辺のダンジョンの地形をサブウィンドウに表示する / display dungeon view around player in a sub window
466 * @param player_ptr プレイヤーへの参照ポインタ
468 void fix_dungeon(PlayerType *player_ptr)
470 for (int j = 0; j < 8; j++) {
471 term_type *old = game_term;
472 if (!angband_term[j]) {
476 if (!(window_flag[j] & (PW_DUNGEON))) {
480 term_activate(angband_term[j]);
481 display_dungeon(player_ptr);
488 * @brief モンスターの思い出をサブウィンドウに表示する /
489 * Hack -- display dungeon view in sub-windows
490 * @param player_ptr プレイヤーへの参照ポインタ
492 void fix_monster(PlayerType *player_ptr)
494 for (int j = 0; j < 8; j++) {
495 term_type *old = game_term;
496 if (!angband_term[j]) {
500 if (!(window_flag[j] & (PW_MONSTER))) {
504 term_activate(angband_term[j]);
505 if (MonsterRace(player_ptr->monster_race_idx).is_valid()) {
506 display_roff(player_ptr);
515 * @brief ベースアイテム情報をサブウィンドウに表示する /
516 * Hack -- display object recall in sub-windows
517 * @param player_ptr プレイヤーへの参照ポインタ
519 void fix_object(PlayerType *player_ptr)
521 for (int j = 0; j < 8; j++) {
522 term_type *old = game_term;
523 if (!angband_term[j]) {
527 if (!(window_flag[j] & (PW_OBJECT))) {
531 term_activate(angband_term[j]);
532 if (player_ptr->baseitem_info_idx) {
533 display_koff(player_ptr, player_ptr->baseitem_info_idx);
542 * @brief 床上のモンスター情報を返す
543 * @param floor_ptr 階の情報への参照ポインタ
544 * @param grid_prt 座標グリッドの情報への参照ポインタ
545 * @return モンスターが見える場合にはモンスター情報への参照ポインタ、それ以外はnullptr
547 * Lookコマンドでカーソルを合わせた場合に合わせてミミックは考慮しない。
549 static const MonsterEntity *monster_on_floor_items(FloorType *floor_ptr, const grid_type *g_ptr)
551 if (g_ptr->m_idx == 0) {
555 auto m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
556 if (!m_ptr->is_valid() || !m_ptr->ml) {
564 * @brief 床上のアイテム一覧を作成し、表示する
565 * @param プレイヤー情報への参照ポインタ
566 * @param y 参照する座標グリッドのy座標
567 * @param x 参照する座標グリッドのx座標
569 static void display_floor_item_list(PlayerType *player_ptr, const int y, const int x)
575 term_get_size(&term_w, &term_h);
584 auto *floor_ptr = player_ptr->current_floor_ptr;
585 const auto *g_ptr = &floor_ptr->grid_array[y][x];
589 auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
590 if (player_bold(player_ptr, y, x)) {
591 sprintf(line, _("(X:%03d Y:%03d) あなたの足元のアイテム一覧", "Items at (%03d,%03d) under you"), x, y);
592 } else if (const auto *m_ptr = monster_on_floor_items(floor_ptr, g_ptr); m_ptr != nullptr) {
593 if (is_hallucinated) {
594 sprintf(line, _("(X:%03d Y:%03d) 何か奇妙な物の足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under something strange"), x, y);
596 const MonsterRaceInfo *const r_ptr = &monraces_info[m_ptr->ap_r_idx];
597 sprintf(line, _("(X:%03d Y:%03d) %sの足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under %s"), x, y, r_ptr->name.data());
600 const TerrainType *const f_ptr = &terrains_info[g_ptr->feat];
601 concptr fn = f_ptr->name.data();
604 if (f_ptr->flags.has(TerrainCharacteristics::STORE) || (f_ptr->flags.has(TerrainCharacteristics::BLDG) && !floor_ptr->inside_arena)) {
605 sprintf(buf, _("%sの入口", "on the entrance of %s"), fn);
606 } else if (f_ptr->flags.has(TerrainCharacteristics::WALL)) {
607 sprintf(buf, _("%sの中", "in %s"), fn);
609 sprintf(buf, _("%s", "on %s"), fn);
611 sprintf(line, _("(X:%03d Y:%03d) %sの上の発見済みアイテム一覧", "Found items at (X:%03d Y:%03d) %s"), x, y, buf);
613 term_addstr(-1, TERM_WHITE, line);
615 // (y,x) のアイテムを1行に1個ずつ書く。
617 for (const auto o_idx : g_ptr->o_idx_list) {
618 ItemEntity *const o_ptr = &floor_ptr->o_list[o_idx];
621 if (none_bits(o_ptr->marked, OM_FOUND) || o_ptr->tval == ItemKindType::GOLD) {
625 // 途中で行数が足りなくなったら最終行にその旨追記して終了。
626 if (term_y >= term_h) {
627 term_addstr(-1, TERM_WHITE, "-- more --");
631 term_gotoxy(0, term_y);
633 if (is_hallucinated) {
634 term_addstr(-1, TERM_WHITE, _("何か奇妙な物", "something strange"));
636 describe_flavor(player_ptr, line, o_ptr, 0);
637 TERM_COLOR attr = tval_to_attr[enum2i(o_ptr->tval) % 128];
638 term_addstr(-1, attr, line);
646 * @brief (y,x) のアイテム一覧をサブウィンドウに表示する / display item at (y,x) in sub-windows
648 void fix_floor_item_list(PlayerType *player_ptr, const int y, const int x)
650 for (int j = 0; j < 8; j++) {
651 if (!angband_term[j]) {
654 if (angband_term[j]->never_fresh) {
657 if (!(window_flag[j] & PW_FLOOR_ITEM_LIST)) {
661 term_type *old = game_term;
662 term_activate(angband_term[j]);
664 display_floor_item_list(player_ptr, y, x);
672 * @brief サブウィンドウに所持品、装備品リストの表示を行う /
673 * Flip "inven" and "equip" in any sub-windows
675 void toggle_inventory_equipment(PlayerType *player_ptr)
677 for (int j = 0; j < 8; j++) {
678 if (!angband_term[j]) {
682 if (window_flag[j] & (PW_INVEN)) {
683 window_flag[j] &= ~(PW_INVEN);
684 window_flag[j] |= (PW_EQUIP);
685 player_ptr->window_flags |= (PW_EQUIP);
689 if (window_flag[j] & PW_EQUIP) {
690 window_flag[j] &= ~(PW_EQUIP);
691 window_flag[j] |= PW_INVEN;
692 player_ptr->window_flags |= PW_INVEN;