1 #include "view/display-messages.h"
2 #include "core/window-redrawer.h"
3 #include "game-option/cheat-options.h"
4 #include "game-option/input-options.h"
5 #include "game-option/map-screen-options.h"
6 #include "game-option/option-flags.h"
7 #include "io/input-key-acceptor.h"
8 #include "main/sound-of-music.h"
9 #include "system/player-type-definition.h"
10 #include "term/gameterm.h"
11 #include "term/term-color-types.h"
12 #include "util/int-char-converter.h"
13 #include "world/world.h"
20 /* Used in msg_print() for "buffering" */
23 COMMAND_CODE now_message;
26 using msg_sp = std::shared_ptr<const std::string>;
27 using msg_wp = std::weak_ptr<const std::string>;
29 /** メッセージが同一かどうかを比較するためのラムダ式 */
30 auto string_ptr_cmp = [](const std::string *a, const std::string *b) { return *a < *b; };
32 /** 同一メッセージの検索に使用するmapオブジェクト。
33 * 同一メッセージがあるかどうかを検索し、ヒットしたらweak_ptrからshared_ptrを作成しメッセージを共有する。
34 * message_historyのカスタムデリータの中で参照するので、message_historyより先に宣言しなければならない事に注意。
36 std::map<const std::string *, msg_wp, decltype(string_ptr_cmp)> message_map(string_ptr_cmp);
39 std::deque<msg_sp> message_history;
42 * @brief メッセージを保持する msg_sp オブジェクトを生成する
44 * @tparam T メッセージの型。std::string / std::string_view / const char* 等
46 * @return 生成した msg_sp オブジェクト
49 msg_sp make_message(T &&str)
51 /** std::stringオブジェクトと同時にmessage_mapのエントリも削除するカスタムデリータ */
52 auto deleter = [](std::string *s) {
57 // 新たにメッセージを保持する msg_sp オブジェクトを生成し、検索用mapオブジェクトにも追加する
58 auto new_msg = msg_sp(new std::string(std::forward<T>(str)), std::move(deleter));
59 message_map.emplace(new_msg.get(), msg_wp(new_msg));
66 * @brief 保存中の過去ゲームメッセージの数を返す。 / How many messages are "available"?
67 * @return 残っているメッセージの数
69 int32_t message_num(void)
71 return message_history.size();
75 * @brief 過去のゲームメッセージを返す。 / Recall the "text" of a saved message
77 * @return メッセージの文字列ポインタ
79 concptr message_str(int age)
81 if ((age < 0) || (age >= message_num()))
84 return message_history[age]->c_str();
87 static void message_add_aux(std::string str)
94 // 80桁を超えるメッセージは80桁ずつ分割する
95 if (str.length() > 80) {
98 for (n = 0; n < 80; n++) {
99 if (iskanji(str[n])) {
108 for (n = 80; n > 60; n--)
114 splitted = str.substr(n);
115 str = str.substr(0, n);
118 // 直前と同じメッセージの場合、「~ <xNN>」と表示する
119 if (!message_history.empty()) {
121 std::string_view last_message = *message_history.front();
123 for (t = last_message.data(); *t && (*t != '<' || (*(t + 1) != 'x')); t++)
127 for (t = last_message.data(); *t && (*t != '<'); t++)
131 if (*t && t != last_message.data()) {
132 if (last_message.length() >= sizeof(" <xN>") - 1) {
133 last_message = last_message.substr(0, t - last_message.data() - 1);
138 if (str == last_message && (j < 1000)) {
139 str = format("%s <x%d>", str.c_str(), j + 1);
140 message_history.pop_front();
152 // メッセージ履歴から同一のメッセージを探す
153 if (const auto &it = message_map.find(&str); it != message_map.end()) {
154 // 同一のメッセージが見つかったならそのメッセージの msg_sp オブジェクトを複製
155 add_msg = it->second.lock();
157 // 見つからなかった場合は新たに msg_sp オブジェクトを作成
158 add_msg = make_message(std::move(str));
162 message_history.push_front(std::move(add_msg));
164 if (message_history.size() == MESSAGE_MAX)
165 message_history.pop_back();
167 if (!splitted.empty()) {
168 message_add_aux(std::move(splitted));
173 * @brief ゲームメッセージをログに追加する。 / Add a new message, with great efficiency
174 * @param msg 保存したいメッセージ
176 void message_add(concptr msg)
178 message_add_aux(msg);
181 bool is_msg_window_flowed(void)
184 for (i = 0; i < 8; i++) {
185 if (angband_term[i] && (window_flag[i] & PW_MESSAGE))
189 if (num_more < angband_term[i]->hgt)
192 return (num_more >= 0);
194 return (num_more >= 0);
200 static void msg_flush(player_type *player_ptr, int x)
202 byte a = TERM_L_BLUE;
203 bool show_more = (num_more >= 0);
205 if (auto_more && !player_ptr->now_damaged)
206 show_more = is_msg_window_flowed();
211 player_ptr->now_damaged = false;
212 if (!player_ptr->playing || show_more) {
213 term_putstr(x, 0, -1, a, _("-続く-", "-more-"));
217 /* auto_moreのとき、全て流す */
220 } else if (cmd == ' ') {
224 } else if ((cmd == '\n') || (cmd == '\r')) {
236 term_erase(0, 0, 255);
245 * @briefOutput a message to the top line of the screen.
247 * Break long messages into multiple pieces (40-72 chars).
249 * Allow multiple short messages to "share" the top line.
251 * Prompt the user to make sure he has a chance to read them.
253 * These messages are memorized for later reference (see above).
255 * We could do "term_fresh()" to provide "flicker" if needed.
257 * The global "msg_flag" variable can be cleared to tell us to
258 * "erase" any "pending" messages still on the screen.
260 * Note that we must be very careful about using the
261 * "msg_print()" functions without explicitly calling the special
262 * "msg_print(nullptr)" function, since this may result in the loss
263 * of information if the screen is cleared, or if anything is
264 * displayed on the top line.
266 * Note that "msg_print(nullptr)" will clear the top line
267 * even if no messages are pending. This is probably a hack.
268 * @todo ここのp_ptrを削除するのは破滅的に作業が増えるので保留
270 void msg_print(concptr msg)
276 if (w_ptr->timewalk_m_idx)
280 term_erase(0, 0, 255);
284 int n = (msg ? strlen(msg) : 0);
285 if (p && (!msg || ((p + n) > 72))) {
299 sprintf(buf, ("T:%d - %s"), (int)w_ptr->game_turn, msg);
303 if (w_ptr->character_generated)
308 int check, split = 72;
312 for (check = 0; check < 72; check++) {
318 if (iskanji(t[check])) {
321 } else if (t[check] == ' ') {
332 for (check = 40; check < 72; check++) {
338 char oops = t[split];
340 term_putstr(0, 0, split, TERM_WHITE, t);
341 msg_flush(p_ptr, split + 1);
348 term_putstr(p, 0, n, TERM_WHITE, t);
349 p_ptr->window_flags |= (PW_MESSAGE);
364 * Display a formatted message, using "vstrnfmt()" and "msg_print()".
366 void msg_format(concptr fmt, ...)
371 (void)vstrnfmt(buf, 1024, fmt, vp);