OSDN Git Service

Merge pull request #1755 from Thujopsis/feature/add-monster-Rabbit-of-Caerbannog
[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 "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-kind.h"
22 #include "object/object-mark-types.h"
23 #include "player/player-status-flags.h"
24 #include "player/player-status.h"
25 #include "spell-kind/magic-item-recharger.h"
26 #include "system/floor-type-definition.h"
27 #include "system/grid-type-definition.h"
28 #include "system/monster-race-definition.h"
29 #include "system/monster-type-definition.h"
30 #include "system/object-type-definition.h"
31 #include "system/player-type-definition.h"
32 #include "target/target-describer.h"
33 #include "target/target-preparation.h"
34 #include "target/target-setter.h"
35 #include "target/target-types.h"
36 #include "term/gameterm.h"
37 #include "term/screen-processor.h"
38 #include "term/term-color-types.h"
39 #include "util/bit-flags-calculator.h"
40 #include "view/display-lore.h"
41 #include "view/display-map.h"
42 #include "view/display-messages.h"
43 #include "view/display-player.h"
44 #include "view/object-describer.h"
45 #include "window/main-window-equipments.h"
46 #include "window/main-window-util.h"
47 #include "world/world.h"
48 #include <mutex>
49 #include <sstream>
50 #include <string>
51
52 /*! サブウィンドウ表示用の ItemTester オブジェクト */
53 static std::unique_ptr<ItemTester> fix_item_tester = std::make_unique<AllMatchItemTester>();
54
55 FixItemTesterSetter::FixItemTesterSetter(const ItemTester &item_tester)
56 {
57     fix_item_tester = item_tester.clone();
58 }
59
60 FixItemTesterSetter::~FixItemTesterSetter()
61 {
62     fix_item_tester = std::make_unique<AllMatchItemTester>();
63 }
64
65 /*!
66  * @brief サブウィンドウに所持品一覧を表示する / Hack -- display inventory in sub-windows
67  * @param player_ptr プレイヤーへの参照ポインタ
68  */
69 void fix_inventory(player_type *player_ptr)
70 {
71     for (int j = 0; j < 8; j++) {
72         term_type *old = Term;
73         if (!angband_term[j])
74             continue;
75
76         if (!(window_flag[j] & (PW_INVEN)))
77             continue;
78
79         term_activate(angband_term[j]);
80         display_inventory(player_ptr, *fix_item_tester);
81         term_fresh();
82         term_activate(old);
83     }
84 }
85
86 /*!
87  * @brief モンスターの現在数を一行で表現する / Print monster info in line
88  * @param x 表示列
89  * @param y 表示行
90  * @param m_ptr 思い出を表示するモンスター情報の参照ポインタ
91  * @param n_same モンスターの数の現在数
92  * @details
93  * <pre>
94  * nnn X LV name
95  *  nnn : number or unique(U) or wanted unique(W)
96  *  X   : symbol of monster
97  *  LV  : monster lv if known
98  *  name: name of monster
99  * </pre>
100  */
101 static void print_monster_line(TERM_LEN x, TERM_LEN y, monster_type *m_ptr, int n_same)
102 {
103     char buf[256];
104     MONRACE_IDX r_idx = m_ptr->ap_r_idx;
105     monster_race *r_ptr = &r_info[r_idx];
106
107     term_erase(0, y, 255);
108     term_gotoxy(x, y);
109     if (!r_ptr)
110         return;
111     if (r_ptr->flags1 & RF1_UNIQUE) {
112         bool is_bounty = false;
113         for (int i = 0; i < MAX_BOUNTY; i++) {
114             if (w_ptr->bounty_r_idx[i] == r_idx) {
115                 is_bounty = true;
116                 break;
117             }
118         }
119
120         term_addstr(-1, TERM_WHITE, is_bounty ? "  W" : "  U");
121     } else {
122         sprintf(buf, "%3d", n_same);
123         term_addstr(-1, TERM_WHITE, buf);
124     }
125
126     term_addstr(-1, TERM_WHITE, " ");
127     term_add_bigch(r_ptr->x_attr, r_ptr->x_char);
128
129     if (r_ptr->r_tkills && m_ptr->mflag2.has_not(MFLAG2::KAGE)) {
130         sprintf(buf, " %2d", (int)r_ptr->level);
131     } else {
132         strcpy(buf, " ??");
133     }
134
135     term_addstr(-1, TERM_WHITE, buf);
136
137     sprintf(buf, " %s ", r_ptr->name.c_str());
138     term_addstr(-1, TERM_WHITE, buf);
139 }
140
141 /*!
142  * @brief モンスターの出現リストを表示する / Print monster info in line
143  * @param x 表示列
144  * @param y 表示行
145  * @param max_lines 最大何行描画するか
146  */
147 void print_monster_list(floor_type *floor_ptr, const std::vector<MONSTER_IDX> &monster_list, TERM_LEN x, TERM_LEN y, TERM_LEN max_lines)
148 {
149     TERM_LEN line = y;
150     monster_type *last_mons = nullptr;
151     int n_same = 0;
152     size_t i;
153     for (i = 0; i < monster_list.size(); i++) {
154         auto m_ptr = &floor_ptr->m_list[monster_list[i]];
155         if (is_pet(m_ptr))
156             continue; // pet
157         if (!m_ptr->r_idx)
158             continue; // dead?
159
160         //ソート済みなので同じモンスターは連続する.これを利用して同じモンスターをカウント,まとめて表示する.
161         //先頭モンスター
162         if (!last_mons) {
163             last_mons = m_ptr;
164             n_same = 1;
165             continue;
166         }
167
168         // same race?
169         if (last_mons->ap_r_idx == m_ptr->ap_r_idx) {
170             n_same++;
171             continue; //表示処理を次に回す
172         }
173
174         // last_mons と m_ptr が(見た目が)異なるなら、last_mons とその数を出力。
175         // m_ptr を新たな last_mons とする。
176         print_monster_line(x, line++, last_mons, n_same);
177         n_same = 1;
178         last_mons = m_ptr;
179
180         // 行数が足りなくなったら中断。
181         if (line - y == max_lines)
182             break;
183     }
184
185     if (i != monster_list.size()) {
186         // 行数が足りなかった場合、最終行にその旨表示。
187         term_addstr(-1, TERM_WHITE, "-- and more --");
188     } else {
189         // 行数が足りていれば last_mons とその数を出力し、残りの行をクリア。
190         if (last_mons)
191             print_monster_line(x, line++, last_mons, n_same);
192         for (; line < max_lines; line++)
193             term_erase(0, line, 255);
194     }
195 }
196
197 /*!
198  * @brief 出現中モンスターのリストをサブウィンドウに表示する / Hack -- display monster list in sub-windows
199  * @param player_ptr プレイヤーへの参照ポインタ
200  */
201 void fix_monster_list(player_type *player_ptr)
202 {
203     static std::vector<MONSTER_IDX> monster_list;
204     std::once_flag once;
205
206     for (int j = 0; j < 8; j++) {
207         term_type *old = Term;
208         if (!angband_term[j])
209             continue;
210         if (!(window_flag[j] & PW_MONSTER_LIST))
211             continue;
212         if (angband_term[j]->never_fresh)
213             continue;
214
215         term_activate(angband_term[j]);
216         int w, h;
217         term_get_size(&w, &h);
218         std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
219         print_monster_list(player_ptr->current_floor_ptr, monster_list, 0, 0, h);
220         term_fresh();
221         term_activate(old);
222     }
223
224     if (use_music && has_monster_music) {
225         std::call_once(once, target_sensing_monsters_prepare, player_ptr, monster_list);
226         select_monster_music(player_ptr, monster_list);
227     }
228 }
229
230 /*!
231  * @brief 装備アイテム一覧を表示する /
232  * Choice window "shadow" of the "show_equip()" function
233  */
234 static void display_equipment(player_type *player_ptr, const ItemTester &item_tester)
235 {
236     if (!player_ptr || !player_ptr->inventory_list)
237         return;
238
239     TERM_LEN wid, hgt;
240     term_get_size(&wid, &hgt);
241
242     TERM_COLOR attr = TERM_WHITE;
243     char tmp_val[80];
244     GAME_TEXT o_name[MAX_NLEN];
245     for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
246         int cur_row = i - INVEN_MAIN_HAND;
247         if (cur_row >= hgt)
248             break;
249
250         auto o_ptr = &player_ptr->inventory_list[i];
251         auto do_disp = player_ptr->select_ring_slot ? is_ring_slot(i) : item_tester.okay(o_ptr);
252         strcpy(tmp_val, "   ");
253
254         if (do_disp) {
255             tmp_val[0] = index_to_label(i);
256             tmp_val[1] = ')';
257         }
258
259         int cur_col = 3;
260         term_erase(cur_col, cur_row, 255);
261         term_putstr(0, cur_row, cur_col, TERM_WHITE, tmp_val);
262
263         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)) {
264             strcpy(o_name, _("(武器を両手持ち)", "(wielding with two-hands)"));
265             attr = TERM_WHITE;
266         } else {
267             describe_flavor(player_ptr, o_name, o_ptr, 0);
268             attr = tval_to_attr[enum2i(o_ptr->tval) % 128];
269         }
270
271         int n = strlen(o_name);
272         if (o_ptr->timeout)
273             attr = TERM_L_DARK;
274
275         if (show_item_graph) {
276             TERM_COLOR a = object_attr(o_ptr);
277             SYMBOL_CODE c = object_char(o_ptr);
278             term_queue_bigchar(cur_col, cur_row, a, c, 0, 0);
279             if (use_bigtile)
280                 cur_col++;
281
282             cur_col += 2;
283         }
284
285         term_putstr(cur_col, cur_row, n, attr, o_name);
286         if (show_weights) {
287             int wgt = o_ptr->weight * o_ptr->number;
288             sprintf(tmp_val, _("%3d.%1d kg", "%3d.%1d lb"), _(lb_to_kg_integer(wgt), wgt / 10), _(lb_to_kg_fraction(wgt), wgt % 10));
289             prt(tmp_val, cur_row, wid - (show_labels ? 28 : 9));
290         }
291
292         if (show_labels) {
293             term_putstr(wid - 20, cur_row, -1, TERM_WHITE, " <-- ");
294             prt(mention_use(player_ptr, i), cur_row, wid - 15);
295         }
296     }
297
298     for (int i = INVEN_TOTAL - INVEN_MAIN_HAND; i < hgt; i++)
299         term_erase(0, i, 255);
300 }
301
302 /*!
303  * @brief 現在の装備品をサブウィンドウに表示する /
304  * Hack -- display equipment in sub-windows
305  * @param player_ptr プレイヤーへの参照ポインタ
306  */
307 void fix_equip(player_type *player_ptr)
308 {
309     for (int j = 0; j < 8; j++) {
310         term_type *old = Term;
311         if (!angband_term[j])
312             continue;
313         if (!(window_flag[j] & (PW_EQUIP)))
314             continue;
315
316         term_activate(angband_term[j]);
317         display_equipment(player_ptr, *fix_item_tester);
318         term_fresh();
319         term_activate(old);
320     }
321 }
322
323 /*!
324  * @brief 現在のプレイヤーステータスをサブウィンドウに表示する /
325  * @param player_ptr プレイヤーへの参照ポインタ
326  * Hack -- display character in sub-windows
327  */
328 void fix_player(player_type *player_ptr)
329 {
330     for (int j = 0; j < 8; j++) {
331         term_type *old = Term;
332         if (!angband_term[j])
333             continue;
334
335         if (!(window_flag[j] & (PW_PLAYER)))
336             continue;
337
338         term_activate(angband_term[j]);
339         update_playtime();
340         display_player(player_ptr, 0);
341         term_fresh();
342         term_activate(old);
343     }
344 }
345
346 /*!
347  * @brief ゲームメッセージ履歴をサブウィンドウに表示する /
348  * Hack -- display recent messages in sub-windows
349  * Adjust for width and split messages
350  */
351 void fix_message(void)
352 {
353     for (int j = 0; j < 8; j++) {
354         term_type *old = Term;
355         if (!angband_term[j])
356             continue;
357
358         if (!(window_flag[j] & (PW_MESSAGE)))
359             continue;
360
361         term_activate(angband_term[j]);
362         TERM_LEN w, h;
363         term_get_size(&w, &h);
364         for (int i = 0; i < h; i++) {
365             term_putstr(0, (h - 1) - i, -1, (byte)((i < now_message) ? TERM_WHITE : TERM_SLATE), message_str((int16_t)i));
366             TERM_LEN x, y;
367             term_locate(&x, &y);
368             term_erase(x, y, 255);
369         }
370
371         term_fresh();
372         term_activate(old);
373     }
374 }
375
376 /*!
377  * @brief 簡易マップをサブウィンドウに表示する /
378  * Hack -- display overhead view in sub-windows
379  * Adjust for width and split messages
380  * @param player_ptr プレイヤーへの参照ポインタ
381  * @details
382  * Note that the "player" symbol does NOT appear on the map.
383  */
384 void fix_overhead(player_type *player_ptr)
385 {
386     for (int j = 0; j < 8; j++) {
387         term_type *old = Term;
388         TERM_LEN wid, hgt;
389         if (!angband_term[j])
390             continue;
391
392         if (!(window_flag[j] & (PW_OVERHEAD)))
393             continue;
394
395         term_activate(angband_term[j]);
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             term_fresh();
401         }
402
403         term_activate(old);
404     }
405 }
406
407 /*!
408  * @brief 自分の周辺の地形をTermに表示する
409  * @param プレイヤー情報への参照ポインタ
410  */
411 static void display_dungeon(player_type *player_ptr)
412 {
413     TERM_COLOR ta = 0;
414     SYMBOL_CODE tc = '\0';
415
416     for (TERM_LEN x = player_ptr->x - Term->wid / 2 + 1; x <= player_ptr->x + Term->wid / 2; x++) {
417         for (TERM_LEN y = player_ptr->y - Term->hgt / 2 + 1; y <= player_ptr->y + Term->hgt / 2; y++) {
418             TERM_COLOR a;
419             SYMBOL_CODE c;
420             if (!in_bounds2(player_ptr->current_floor_ptr, y, x)) {
421                 feature_type *f_ptr = &f_info[feat_none];
422                 a = f_ptr->x_attr[F_LIT_STANDARD];
423                 c = f_ptr->x_char[F_LIT_STANDARD];
424                 term_queue_char(x - player_ptr->x + Term->wid / 2 - 1, y - player_ptr->y + Term->hgt / 2 - 1, a, c, ta, tc);
425                 continue;
426             }
427
428             map_info(player_ptr, y, x, &a, &c, &ta, &tc);
429
430             if (!use_graphics) {
431                 if (w_ptr->timewalk_m_idx)
432                     a = TERM_DARK;
433                 else if (is_invuln(player_ptr) || player_ptr->timewalk)
434                     a = TERM_WHITE;
435                 else if (player_ptr->wraith_form)
436                     a = TERM_L_DARK;
437             }
438
439             term_queue_char(x - player_ptr->x + Term->wid / 2 - 1, y - player_ptr->y + Term->hgt / 2 - 1, a, c, ta, tc);
440         }
441     }
442 }
443
444 /*!
445  * @brief 自分の周辺のダンジョンの地形をサブウィンドウに表示する / display dungeon view around player in a sub window
446  * @param player_ptr プレイヤーへの参照ポインタ
447  */
448 void fix_dungeon(player_type *player_ptr)
449 {
450     for (int j = 0; j < 8; j++) {
451         term_type *old = Term;
452         if (!angband_term[j])
453             continue;
454
455         if (!(window_flag[j] & (PW_DUNGEON)))
456             continue;
457
458         term_activate(angband_term[j]);
459         display_dungeon(player_ptr);
460         term_fresh();
461         term_activate(old);
462     }
463 }
464
465 /*!
466  * @brief モンスターの思い出をサブウィンドウに表示する /
467  * Hack -- display dungeon view in sub-windows
468  * @param player_ptr プレイヤーへの参照ポインタ
469  */
470 void fix_monster(player_type *player_ptr)
471 {
472     for (int j = 0; j < 8; j++) {
473         term_type *old = Term;
474         if (!angband_term[j])
475             continue;
476
477         if (!(window_flag[j] & (PW_MONSTER)))
478             continue;
479
480         term_activate(angband_term[j]);
481         if (player_ptr->monster_race_idx)
482             display_roff(player_ptr);
483
484         term_fresh();
485         term_activate(old);
486     }
487 }
488
489 /*!
490  * @brief ベースアイテム情報をサブウィンドウに表示する /
491  * Hack -- display object recall in sub-windows
492  * @param player_ptr プレイヤーへの参照ポインタ
493  */
494 void fix_object(player_type *player_ptr)
495 {
496     for (int j = 0; j < 8; j++) {
497         term_type *old = Term;
498         if (!angband_term[j])
499             continue;
500
501         if (!(window_flag[j] & (PW_OBJECT)))
502             continue;
503
504         term_activate(angband_term[j]);
505         if (player_ptr->object_kind_idx)
506             display_koff(player_ptr, player_ptr->object_kind_idx);
507
508         term_fresh();
509         term_activate(old);
510     }
511 }
512
513 /*!
514  * @brief 床上のモンスター情報を返す
515  * @param floor_ptr 階の情報への参照ポインタ
516  * @param grid_prt 座標グリッドの情報への参照ポインタ
517  * @return モンスターが見える場合にはモンスター情報への参照ポインタ、それ以外はnullptr
518  * @details
519  * Lookコマンドでカーソルを合わせた場合に合わせてミミックは考慮しない。
520  */
521 static const monster_type *monster_on_floor_items(floor_type *floor_ptr, const grid_type *g_ptr)
522 {
523     if (g_ptr->m_idx == 0)
524         return nullptr;
525
526     auto m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
527     if (!monster_is_valid(m_ptr) || !m_ptr->ml)
528         return nullptr;
529
530     return m_ptr;
531 }
532
533 /*!
534  * @brief 床上のアイテム一覧を作成し、表示する
535  * @param プレイヤー情報への参照ポインタ
536  * @param y 参照する座標グリッドのy座標
537  * @param x 参照する座標グリッドのx座標
538  */
539 static void display_floor_item_list(player_type *player_ptr, const int y, const int x)
540 {
541     // Term の行数を取得。
542     TERM_LEN term_h;
543     {
544         TERM_LEN term_w;
545         term_get_size(&term_w, &term_h);
546     }
547     if (term_h <= 0)
548         return;
549
550     term_clear();
551     term_gotoxy(0, 0);
552
553     auto *floor_ptr = player_ptr->current_floor_ptr;
554     const auto *g_ptr = &floor_ptr->grid_array[y][x];
555     char line[1024];
556
557     // 先頭行を書く。
558     if (player_bold(player_ptr, y, x))
559         sprintf(line, _("(X:%03d Y:%03d) あなたの足元のアイテム一覧", "Items at (%03d,%03d) under you"), x, y);
560     else if (const auto *m_ptr = monster_on_floor_items(floor_ptr, g_ptr); m_ptr != nullptr) {
561         if (player_ptr->hallucinated) {
562             sprintf(line, _("(X:%03d Y:%03d) 何か奇妙な物の足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under something strange"), x, y);
563         } else {
564             const monster_race *const r_ptr = &r_info[m_ptr->ap_r_idx];
565             sprintf(line, _("(X:%03d Y:%03d) %sの足元の発見済みアイテム一覧", "Found items at (%03d,%03d) under %s"), x, y, r_ptr->name.c_str());
566         }
567     } else {
568         const feature_type *const f_ptr = &f_info[g_ptr->feat];
569         concptr fn = f_ptr->name.c_str();
570         char buf[512];
571
572         if (f_ptr->flags.has(FF::STORE) || (f_ptr->flags.has(FF::BLDG) && !floor_ptr->inside_arena))
573             sprintf(buf, _("%sの入口", "on the entrance of %s"), fn);
574         else if (f_ptr->flags.has(FF::WALL))
575             sprintf(buf, _("%sの中", "in %s"), fn);
576         else
577             sprintf(buf, _("%s", "on %s"), fn);
578         sprintf(line, _("(X:%03d Y:%03d) %sの上の発見済みアイテム一覧", "Found items at (X:%03d Y:%03d) %s"), x, y, buf);
579     }
580     term_addstr(-1, TERM_WHITE, line);
581
582     // (y,x) のアイテムを1行に1個ずつ書く。
583     TERM_LEN term_y = 1;
584     for (const auto o_idx : g_ptr->o_idx_list) {
585         object_type *const o_ptr = &floor_ptr->o_list[o_idx];
586
587         // 未発見アイテムおよび金は対象外。
588         if (none_bits(o_ptr->marked, OM_FOUND) || o_ptr->tval == ItemKindType::GOLD) {
589             continue;
590         }
591
592         // 途中で行数が足りなくなったら最終行にその旨追記して終了。
593         if (term_y >= term_h) {
594             term_addstr(-1, TERM_WHITE, "-- more --");
595             break;
596         }
597
598         term_gotoxy(0, term_y);
599
600         if (player_ptr->hallucinated) {
601             term_addstr(-1, TERM_WHITE, _("何か奇妙な物", "something strange"));
602         } else {
603             describe_flavor(player_ptr, line, o_ptr, 0);
604             TERM_COLOR attr = tval_to_attr[enum2i(o_ptr->tval) % 128];
605             term_addstr(-1, attr, line);
606         }
607
608         ++term_y;
609     }
610 }
611
612 /*!
613  * @brief (y,x) のアイテム一覧をサブウィンドウに表示する / display item at (y,x) in sub-windows
614  */
615 void fix_floor_item_list(player_type *player_ptr, const int y, const int x)
616 {
617     for (int j = 0; j < 8; j++) {
618         if (!angband_term[j])
619             continue;
620         if (angband_term[j]->never_fresh)
621             continue;
622         if (!(window_flag[j] & PW_FLOOR_ITEM_LIST))
623             continue;
624
625         term_type *old = Term;
626         term_activate(angband_term[j]);
627
628         display_floor_item_list(player_ptr, y, x);
629         term_fresh();
630
631         term_activate(old);
632     }
633 }
634
635 /*!
636  * @brief サブウィンドウに所持品、装備品リストの表示を行う /
637  * Flip "inven" and "equip" in any sub-windows
638  */
639 void toggle_inventory_equipment(player_type *player_ptr)
640 {
641     for (int j = 0; j < 8; j++) {
642         if (!angband_term[j])
643             continue;
644
645         if (window_flag[j] & (PW_INVEN)) {
646             window_flag[j] &= ~(PW_INVEN);
647             window_flag[j] |= (PW_EQUIP);
648             player_ptr->window_flags |= (PW_EQUIP);
649             continue;
650         }
651
652         if (window_flag[j] & PW_EQUIP) {
653             window_flag[j] &= ~(PW_EQUIP);
654             window_flag[j] |= PW_INVEN;
655             player_ptr->window_flags |= PW_INVEN;
656         }
657     }
658 }