OSDN Git Service

Merge pull request #3721 from daradarach/feature/nodequest
[hengbandforosx/hengbandosx.git] / src / cmd-visual / cmd-draw.cpp
1 #include "cmd-visual/cmd-draw.h"
2 #include "core/asking-player.h"
3 #include "core/stuff-handler.h"
4 #include "core/window-redrawer.h"
5 #include "io/files-util.h"
6 #include "io/input-key-acceptor.h"
7 #include "main/sound-of-music.h"
8 #include "player-base/player-race.h"
9 #include "player-info/race-types.h"
10 #include "player/process-name.h"
11 #include "racial/racial-android.h"
12 #include "system/player-type-definition.h"
13 #include "system/redrawing-flags-updater.h"
14 #include "term/gameterm.h"
15 #include "term/screen-processor.h"
16 #include "term/term-color-types.h"
17 #include "term/z-form.h"
18 #include "util/int-char-converter.h"
19 #include "util/string-processor.h"
20 #include "view/display-messages.h"
21 #include "view/display-player.h"
22 #include "world/world.h"
23 #include <optional>
24
25 /*!
26  * @brief 画面を再描画するコマンドのメインルーチン
27  * Hack -- redraw the screen
28  * @param player_ptr プレイヤーへの参照ポインタ
29  * @details
30  * <pre>
31  * This command performs various low level updates, clears all the "extra"
32  * windows, does a total redraw of the main window, and requests all of the
33  * interesting updates and redraws that I can think of.
34  *
35  * This command is also used to "instantiate" the results of the user
36  * selecting various things, such as graphics mode, so it must call
37  * the "TERM_XTRA_REACT" hook before redrawing the windows.
38  * </pre>
39  */
40 void do_cmd_redraw(PlayerType *player_ptr)
41 {
42     term_xtra(TERM_XTRA_REACT, 0);
43
44     auto &rfu = RedrawingFlagsUpdater::get_instance();
45     static constexpr auto flags_srf = {
46         StatusRecalculatingFlag::COMBINATION,
47         StatusRecalculatingFlag::REORDER,
48         StatusRecalculatingFlag::TORCH,
49         StatusRecalculatingFlag::BONUS,
50         StatusRecalculatingFlag::HP,
51         StatusRecalculatingFlag::MP,
52         StatusRecalculatingFlag::SPELLS,
53         StatusRecalculatingFlag::UN_VIEW,
54         StatusRecalculatingFlag::UN_LITE,
55         StatusRecalculatingFlag::VIEW,
56         StatusRecalculatingFlag::LITE,
57         StatusRecalculatingFlag::MONSTER_LITE,
58         StatusRecalculatingFlag::MONSTER_STATUSES,
59     };
60     rfu.set_flags(flags_srf);
61     static constexpr auto flags_mwrf = {
62         MainWindowRedrawingFlag::WIPE,
63         MainWindowRedrawingFlag::BASIC,
64         MainWindowRedrawingFlag::EXTRA,
65         MainWindowRedrawingFlag::EQUIPPY,
66         MainWindowRedrawingFlag::MAP,
67     };
68     rfu.set_flags(flags_mwrf);
69     static constexpr auto flags_swrf = {
70         SubWindowRedrawingFlag::INVENTORY,
71         SubWindowRedrawingFlag::EQUIPMENT,
72         SubWindowRedrawingFlag::SPELL,
73         SubWindowRedrawingFlag::PLAYER,
74         SubWindowRedrawingFlag::MESSAGE,
75         SubWindowRedrawingFlag::OVERHEAD,
76         SubWindowRedrawingFlag::DUNGEON,
77         SubWindowRedrawingFlag::MONSTER_LORE,
78         SubWindowRedrawingFlag::ITEM_KNOWLEDGE,
79     };
80     rfu.set_flags(flags_swrf);
81     update_playtime();
82     handle_stuff(player_ptr);
83     if (PlayerRace(player_ptr).equals(PlayerRaceType::ANDROID)) {
84         calc_android_exp(player_ptr);
85     }
86
87     term_type *old = game_term;
88     for (auto i = 0U; i < angband_terms.size(); ++i) {
89         if (!angband_terms[i]) {
90             continue;
91         }
92
93         term_activate(angband_terms[i]);
94         term_redraw();
95         term_fresh();
96         term_activate(old);
97     }
98 }
99
100 static std::optional<int> input_status_command(PlayerType *player_ptr, int page)
101 {
102     auto c = inkey();
103     switch (c) {
104     case 'c':
105         get_name(player_ptr);
106         process_player_name(player_ptr);
107         return page;
108     case 'f': {
109         const auto initial_filename = format("%s.txt", player_ptr->base_name);
110         const auto input_filename = input_string(_("ファイル名: ", "File name: "), 80, initial_filename);
111         if (!input_filename) {
112             return page;
113         }
114
115         const auto &filename = str_ltrim(input_filename.value());
116         if (!filename.empty()) {
117             update_playtime();
118             file_character(player_ptr, filename);
119         }
120
121         return page;
122     }
123     case 'h':
124         return page + 1;
125     case ESCAPE:
126         return std::nullopt;
127     default:
128         bell();
129         return page;
130     }
131 }
132
133 /*!
134  * @brief プレイヤーのステータス表示
135  */
136 void do_cmd_player_status(PlayerType *player_ptr)
137 {
138     auto page = 0;
139     screen_save();
140     constexpr auto prompt = _("['c'で名前変更, 'f'でファイルへ書出, 'h'でモード変更, ESCで終了]", "['c' to change name, 'f' to file, 'h' to change mode, or ESC]");
141     while (true) {
142         TermCenteredOffsetSetter tcos(MAIN_TERM_MIN_COLS, MAIN_TERM_MIN_ROWS);
143
144         update_playtime();
145         (void)display_player(player_ptr, page);
146         if (page == 5) {
147             page = 0;
148             (void)display_player(player_ptr, page);
149         }
150
151         term_putstr(2, 23, -1, TERM_WHITE, prompt);
152         auto next_page = input_status_command(player_ptr, page);
153         if (!next_page) {
154             break;
155         }
156
157         page = next_page.value();
158         msg_erase();
159     }
160
161     screen_load();
162     auto &rfu = RedrawingFlagsUpdater::get_instance();
163     static constexpr auto flags_mwrf = {
164         MainWindowRedrawingFlag::WIPE,
165         MainWindowRedrawingFlag::BASIC,
166         MainWindowRedrawingFlag::EXTRA,
167         MainWindowRedrawingFlag::EQUIPPY,
168         MainWindowRedrawingFlag::MAP,
169     };
170     rfu.set_flags(flags_mwrf);
171     handle_stuff(player_ptr);
172 }
173
174 /*!
175  * @brief 最近表示されたメッセージを再表示するコマンドのメインルーチン
176  * Recall the most recent message
177  */
178 void do_cmd_message_one(void)
179 {
180     prt(format("> %s", message_str(0)->data()), 0, 0);
181 }
182
183 /*!
184  * @brief メッセージのログを表示するコマンドのメインルーチン
185  * Recall the most recent message
186  * @details
187  * <pre>
188  * Show previous messages to the user   -BEN-
189  *
190  * The screen format uses line 0 and 23 for headers and prompts,
191  * skips line 1 and 22, and uses line 2 thru 21 for old messages.
192  *
193  * This command shows you which commands you are viewing, and allows
194  * you to "search" for strings in the recall.
195  *
196  * Note that messages may be longer than 80 characters, but they are
197  * displayed using "infinite" length, with a special sub-command to
198  * "slide" the virtual display to the left or right.
199  *
200  * Attempt to only hilite the matching portions of the string.
201  * </pre>
202  */
203 void do_cmd_messages(int num_now)
204 {
205     std::string shower_str("");
206     std::string finder_str("");
207     std::string shower("");
208     const auto &[wid, hgt] = term_get_size();
209     auto num_lines = hgt - 4;
210     auto n = message_num();
211     auto i = 0;
212     screen_save();
213     term_clear();
214     while (true) {
215         int j;
216         int skey;
217         for (j = 0; (j < num_lines) && (i + j < n); j++) {
218             const auto msg_str = message_str(i + j);
219             const auto *msg = msg_str->data();
220             c_prt((i + j < num_now ? TERM_WHITE : TERM_SLATE), msg, num_lines + 1 - j, 0);
221             if (shower.empty()) {
222                 continue;
223             }
224
225             // @details ダメ文字対策でstringを使わない.
226             const auto *str = msg;
227             while (true) {
228                 str = angband_strstr(str, shower);
229                 if (str == nullptr) {
230                     break;
231                 }
232
233                 const auto len = shower.length();
234                 term_putstr(str - msg, num_lines + 1 - j, len, TERM_YELLOW, shower);
235                 str += len;
236             }
237         }
238
239         for (; j < num_lines; j++) {
240             term_erase(0, num_lines + 1 - j);
241         }
242
243         prt(format(_("以前のメッセージ %d-%d 全部で(%d)", "Message Recall (%d-%d of %d)"), i, i + j - 1, n), 0, 0);
244         prt(_("[ 'p' で更に古いもの, 'n' で更に新しいもの, '/' で検索, ESC で中断 ]", "[Press 'p' for older, 'n' for newer, ..., or ESCAPE]"), hgt - 1, 0);
245         skey = inkey_special(true);
246         if (skey == ESCAPE) {
247             break;
248         }
249
250         j = i;
251         switch (skey) {
252         case '=': {
253             prt(_("強調: ", "Show: "), hgt - 1, 0);
254             const auto ask_result = askfor(80, shower_str);
255             if (ask_result) {
256                 shower = *ask_result;
257                 shower_str = *ask_result;
258             }
259
260             continue;
261         }
262         case '/':
263         case KTRL('s'): {
264             prt(_("検索: ", "Find: "), hgt - 1, 0);
265             const auto ask_result = askfor(80, finder_str);
266             if (!ask_result) {
267                 continue;
268             }
269
270             finder_str = *ask_result;
271             if (finder_str.empty()) {
272                 shower = "";
273                 continue;
274             }
275
276             shower = finder_str;
277             for (int z = i + 1; z < n; z++) {
278                 // @details ダメ文字対策でstringを使わない.
279                 const auto msg = message_str(z);
280                 if (str_find(*msg, finder_str)) {
281                     i = z;
282                     break;
283                 }
284             }
285
286             break;
287         }
288         case SKEY_TOP:
289             i = n - num_lines;
290             break;
291         case SKEY_BOTTOM:
292             i = 0;
293             break;
294         case '8':
295         case SKEY_UP:
296         case '\n':
297         case '\r':
298             i = std::min(i + 1, n - num_lines);
299             break;
300         case '+':
301             i = std::min(i + 10, n - num_lines);
302             break;
303         case 'p':
304         case KTRL('P'):
305         case ' ':
306         case SKEY_PGUP:
307             i = std::min(i + num_lines, n - num_lines);
308             break;
309         case 'n':
310         case KTRL('N'):
311         case SKEY_PGDOWN:
312             i = std::max(0, i - num_lines);
313             break;
314         case '-':
315             i = std::max(0, i - 10);
316             break;
317         case '2':
318         case SKEY_DOWN:
319             i = std::max(0, i - 1);
320             break;
321         }
322
323         if (i == j) {
324             bell();
325         }
326     }
327
328     screen_load();
329 }