OSDN Git Service

Merge pull request #4184 from habu1010/feature/improve-message-history
authorHabu <habu1010+github@gmail.com>
Sun, 2 Jun 2024 10:36:56 +0000 (19:36 +0900)
committerGitHub <noreply@github.com>
Sun, 2 Jun 2024 10:36:56 +0000 (19:36 +0900)
[Refactor] メッセージ履歴の繰り返しの保持方法の変更

src/load/info-loader.cpp
src/save/save.cpp
src/system/angband-version.h
src/view/display-messages.cpp
src/view/display-messages.h

index 519c2c6..64f62d3 100644 (file)
@@ -88,6 +88,11 @@ void rd_randomizer(void)
  */
 void rd_messages(void)
 {
+    if (!loading_savefile_version_is_older_than(22)) {
+        rd_message_history();
+        return;
+    }
+
     const auto message_max = static_cast<int>(h_older_than(2, 2, 0, 75) ? rd_u16b() : rd_u32b());
     for (int i = 0; i < message_max; i++) {
         message_add(rd_string());
index 9b981af..9dc8c43 100644 (file)
@@ -16,7 +16,6 @@
 #include "dungeon/quest.h"
 #include "floor/floor-town.h"
 #include "floor/wild.h"
-#include "game-option/text-display-options.h"
 #include "inventory/inventory-slot-types.h"
 #include "io/files-util.h"
 #include "io/report.h"
@@ -103,15 +102,7 @@ static bool wr_savefile_new(PlayerType *player_ptr)
 
     wr_randomizer();
     wr_options();
-    uint32_t tmp32u = message_num();
-    if (compress_savefile && (tmp32u > 40)) {
-        tmp32u = 40;
-    }
-
-    wr_u32b(tmp32u);
-    for (int i = tmp32u - 1; i >= 0; i--) {
-        wr_string(*message_str(i));
-    }
+    wr_message_history();
 
     uint16_t tmp16u = static_cast<uint16_t>(monraces_info.size());
     wr_u16b(tmp16u);
index 9c3913d..22cfed9 100644 (file)
@@ -29,7 +29,7 @@ constexpr std::string_view ROOT_VARIANT_NAME("Hengband");
 /*!
  * @brief セーブファイルのバージョン(3.0.0から導入)
  */
-constexpr uint32_t SAVEFILE_VERSION = 21;
+constexpr uint32_t SAVEFILE_VERSION = 22;
 
 /*!
  * @brief バージョンが開発版が安定版かを返す(廃止予定)
index c8ced50..5e16de3 100644 (file)
@@ -4,8 +4,11 @@
 #include "game-option/input-options.h"
 #include "game-option/map-screen-options.h"
 #include "game-option/option-flags.h"
+#include "game-option/text-display-options.h"
 #include "io/input-key-acceptor.h"
+#include "load/load-util.h"
 #include "main/sound-of-music.h"
+#include "save/save-util.h"
 #include "system/player-type-definition.h"
 #include "system/redrawing-flags-updater.h"
 #include "term/gameterm.h"
@@ -39,19 +42,34 @@ auto string_ptr_cmp = [](const std::string *a, const std::string *b) { return *a
  */
 std::map<const std::string *, msg_wp, decltype(string_ptr_cmp)> message_map(string_ptr_cmp);
 
+/** メッセージ行 */
+struct msg_record {
+    msg_record(msg_sp msg, short repeat_count = 1)
+        : msg(std::move(msg))
+        , repeat_count(repeat_count)
+    {
+    }
+    msg_sp msg; //< メッセージ
+    short repeat_count; //< 繰り返し回数
+};
+
 /** メッセージ履歴 */
-std::deque<msg_sp> message_history;
+std::deque<msg_record> message_history;
 
 /**
  * @brief メッセージを保持する msg_sp オブジェクトを生成する
  *
- * @tparam T メッセージの型。std::string / std::string_view / const char* 等
- * @param str メッセージ
+ * @param msg メッセージ
  * @return 生成した msg_sp オブジェクト
  */
-template <typename T>
-msg_sp make_message(T &&str)
+msg_sp make_message(std::string &&msg)
 {
+    // メッセージ履歴に同一のメッセージがあれば、そのメッセージの msg_sp オブジェクトを複製して返す
+    if (const auto &it = message_map.find(&msg); it != message_map.end()) {
+        return it->second.lock();
+    }
+
+    // 見つからなければ新たに msg_sp オブジェクトを作成する
     /** std::stringオブジェクトと同時にmessage_mapのエントリも削除するカスタムデリータ */
     auto deleter = [](std::string *s) {
         message_map.erase(s);
@@ -59,7 +77,7 @@ msg_sp make_message(T &&str)
     };
 
     // 新たにメッセージを保持する msg_sp オブジェクトを生成し、検索用mapオブジェクトにも追加する
-    auto new_msg = msg_sp(new std::string(std::forward<T>(str)), std::move(deleter));
+    auto new_msg = msg_sp(new std::string(std::move(msg)), std::move(deleter));
     message_map.emplace(new_msg.get(), msg_wp(new_msg));
 
     return new_msg;
@@ -86,79 +104,49 @@ std::shared_ptr<const std::string> message_str(int age)
         return std::make_shared<const std::string>("");
     }
 
-    return message_history[age];
+    const auto &[msg, repeat_count] = message_history[age];
+    if (repeat_count > 1) {
+        return std::make_shared<const std::string>(*msg + format(" <x%d>", repeat_count));
+    }
+
+    return msg;
 }
 
-static void message_add_aux(std::string str)
+/*!
+ * @brief メッセージ履歴にメッセージを追加する
+ * @param msg 保存するメッセージ
+ */
+void message_add(std::string_view msg)
 {
-    if (str.empty()) {
+    if (msg.empty()) {
         return;
     }
 
-    // 直前と同じメッセージの場合、「~ <xNN>」と表示する
     if (!message_history.empty()) {
-        const char *t;
-        std::string_view last_message = *message_history.front();
-#ifdef JP
-        for (t = last_message.data(); *t && (*t != '<' || (*(t + 1) != 'x')); t++) {
-            if (iskanji(*t)) {
-                t++;
-            }
-        }
-#else
-        for (t = last_message.data(); *t && (*t != '<'); t++) {
-            ;
-        }
-#endif
-        int j = 1;
-        if (*t && t != last_message.data()) {
-            if (last_message.length() >= sizeof(" <xN>") - 1) {
-                last_message = last_message.substr(0, t - last_message.data() - 1);
-                j = atoi(t + 2);
-            }
-        }
+        auto &last_msg = message_history.front();
 
-        if (str == last_message && (j < 1000)) {
-            str = format("%s <x%d>", str.data(), j + 1);
-            message_history.pop_front();
+        // 直前と同じメッセージの場合、繰り返し回数を増やして終了
+        if ((msg == *last_msg.msg) && (last_msg.repeat_count < 9999)) {
+            last_msg.repeat_count++;
             if (!now_message) {
                 now_message++;
             }
-        } else {
-            /*流れた行の数を数えておく */
-            num_more++;
-            now_message++;
+            return;
         }
-    }
 
-    msg_sp add_msg;
-
-    // メッセージ履歴から同一のメッセージを探す
-    if (const auto &it = message_map.find(&str); it != message_map.end()) {
-        // 同一のメッセージが見つかったならそのメッセージの msg_sp オブジェクトを複製
-        add_msg = it->second.lock();
-    } else {
-        // 見つからなかった場合は新たに msg_sp オブジェクトを作成
-        add_msg = make_message(std::move(str));
+        /*流れた行の数を数えておく */
+        num_more++;
+        now_message++;
     }
 
     // メッセージ履歴に追加
-    message_history.push_front(std::move(add_msg));
+    message_history.emplace_front(make_message(std::string(msg)));
 
-    if (message_history.size() == MESSAGE_MAX) {
+    while (message_history.size() > MESSAGE_MAX) {
         message_history.pop_back();
     }
 }
 
-/*!
- * @brief ゲームメッセージをログに追加する。 / Add a new message, with great efficiency
- * @param msg 保存したいメッセージ
- */
-void message_add(std::string_view msg)
-{
-    message_add_aux(std::string(msg));
-}
-
 bool is_msg_window_flowed(void)
 {
     auto i = 0U;
@@ -369,3 +357,38 @@ void msg_format(const char *fmt, ...)
     va_end(vp);
     msg_print(buf);
 }
+
+/*!
+ * @brief セーブファイルにメッセージ履歴を保存する
+ */
+void wr_message_history()
+{
+    auto num = message_num();
+    if (compress_savefile && (num > 40)) {
+        num = 40;
+    }
+
+    wr_s32b(num);
+    for (auto i = 0; i < num; ++i) {
+        const auto &[msg, repeat_count] = message_history[i];
+        wr_string(*msg);
+        wr_s16b(repeat_count);
+    }
+}
+
+/*!
+ * @brief セーブファイルからメッセージ履歴を読み込む
+ */
+void rd_message_history()
+{
+    message_history.clear();
+
+    const auto message_hisotry_num = rd_s32b();
+    for (auto i = 0; i < message_hisotry_num; i++) {
+        auto msg = rd_string();
+        const auto repeat_count = rd_s16b();
+        if (message_history.size() < MESSAGE_MAX) {
+            message_history.emplace_back(make_message(std::move(msg)), repeat_count);
+        }
+    }
+}
index 754e70b..0341929 100644 (file)
@@ -22,3 +22,5 @@ void msg_erase();
 void msg_print(std::string_view msg);
 void msg_print(std::nullptr_t);
 void msg_format(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void wr_message_history();
+void rd_message_history();