OSDN Git Service

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