OSDN Git Service

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