diff --git a/.github/workflows/markdown-checker.yml b/.github/workflows/markdown-checker.yml index abd6d09ec9..eb3896ee69 100644 --- a/.github/workflows/markdown-checker.yml +++ b/.github/workflows/markdown-checker.yml @@ -47,6 +47,7 @@ jobs: with: # 仅检查内部链接,排除所有外部链接 args: > + --root-dir ./docs/.vuepress/public --verbose --no-progress --cache --max-cache-age 1d --cache-exclude-status '429, 500..599' --exclude 'https?://.*' diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7143d5b7..95f8d14f8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v6.3.6 +## v6.3.7 ### 拉电线不知天地为何物 | Highlight @@ -65,6 +65,27 @@ We warmly welcome community developers to assist with adaptation and submit impr 以下是详细内容: +## v6.3.7 + +### 改进 | Improved + +* 重构战斗中击杀识别, 击杀图标匹配使用高斯模糊预处理 (#15868) @status102 + +### 修复 | Fix + +* 自动战斗最右侧单位无法识别 @status102 +* 自动战斗击杀数识别便宜 @status102 +* correct_rect在x y负方向超限不完全的情况下, 校正后width height错误 @status102 + +### 文档 | Docs + +* 修族谱 (#15856) @JasonHuang79 +* Add dedicated "MAA GUI download and installation guide" (#15138) @JasonHuang79 @Rbqwow + +### 其他 | Other + +* 繁中服「墟」活動導航 (#15849) @momomochi987 + ## v6.3.6 ### 改进 | Improved diff --git a/docs/zh-cn/manual/cli/install.md b/docs/zh-cn/manual/cli/install.md index 405ef291d1..804550767e 100644 --- a/docs/zh-cn/manual/cli/install.md +++ b/docs/zh-cn/manual/cli/install.md @@ -1,6 +1,6 @@ --- order: 1 -icon: material-symbols:download +icon: material-symbols:download-2-rounded --- # 安装及编译 diff --git a/docs/zh-cn/manual/connection.md b/docs/zh-cn/manual/connection.md index 0d32aa6e5f..87566aa81e 100644 --- a/docs/zh-cn/manual/connection.md +++ b/docs/zh-cn/manual/connection.md @@ -1,5 +1,5 @@ --- -order: 3 +order: 4 icon: mdi:plug --- diff --git a/docs/zh-cn/manual/faq.md b/docs/zh-cn/manual/faq.md index 044fb3cf63..63a7e26588 100644 --- a/docs/zh-cn/manual/faq.md +++ b/docs/zh-cn/manual/faq.md @@ -1,5 +1,5 @@ --- -order: 2 +order: 3 icon: ph:question-fill --- diff --git a/docs/zh-cn/manual/install.md b/docs/zh-cn/manual/install.md new file mode 100644 index 0000000000..ff2ee22648 --- /dev/null +++ b/docs/zh-cn/manual/install.md @@ -0,0 +1,93 @@ +--- +order: 2 +icon: material-symbols:download-2-rounded +--- + + + +# 下载与安装 + +::: tip + +你正在查阅 MAA GUI 的下载与安装说明文档。若你需要查阅 maa-cli 的下载与安装说明,请访问 maa-cli 的 [安装及编译](./cli/install.md) 文档。目前安卓牛牛(MAA 安卓版)已开放测试,请前往 [MAA-Meow](https://github.com/Aliothmoon/MAA-Meow) 了解更多情况。 + +::: + +## 下载 MAA + +MAA 提供多种下载方式,包括官网下载,从包管理器安装,群文件下载等方式。请选择适合你的方式进行下载。 + +### 通过 [官网](https://maa.plus) 下载最新的 MAA 软件包 + +官网一般会自动选择正确的版本架构,对于大多数阅读本文的用户来说,应为 Windows x64。对于阅读本文的 macOS 用户来说,应下载 macOS 通用版。 + +### 通过 [Mirror酱](https://mirrorchyan.com/zh/projects?rid=MAA&source=maadocs-install) 下载最新的 MAA 软件包 + +请确认系统架构并下载对应的软件包。对于大多数阅读本文的 Windows 用户来说,应为 Windows x64。对于阅读本文的 Mac 用户,Mirror酱 不提供通用软件包,请确认您的芯片架构(arm/x86)后下载对应的软件包。 + +::: tip + +[Mirror酱](https://mirrorchyan.com/zh/projects?rid=MAA&source=maadocs-install) 是独立的第三方下载加速服务,需要付费使用,而非 MAA 收费。其运营成本由订阅收入支撑,部分收益将回馈项目开发者。欢迎订阅 CDK 享受高速下载,同时支持项目持续开发。 + +::: + +### 使用 Windows 包管理器(Winget)安装 + +::: tip + +本方法仅适用于使用 Windows 的用户 + +::: + +请在终端中运行以下命令: + +```bash +winget install maa +``` + +通过此方式安装的默认安装路径为 `C:\Users\用户名\AppData\Local\Microsoft\WinGet\Packages`。 + +### 通过 QQ 群文件下载最新的 MAA 软件包 + +1. 加入 [MAA 官方 QQ 群](https://api.maa.plus/MaaAssistantArknights/api/qqgroup/index.html) +2. 在群文件中找到最新的 MAA 软件包进行下载。 + +### 通过 [GitHub Releases](https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases) 下载最新的 MAA 软件包 + +请确认系统架构并下载对应的软件包。对于大多数阅读本文的 Windows 用户来说,应为 `MAA-<版本号>-win-x64.zip`。对于阅读本文的 macOS 用户来说,应选择 `MAA-<版本号>-macos-universal.dmg`。 + +## Linux 和其他操作系统 + +MAA GUI **暂未支持** Linux 和其他操作系统。你可以使用 **maa-cli** 来在这些系统上使用 MAA 的功能。请访问 maa-cli 的 [安装及编译](./cli/install.md) 文档了解更多信息。 + +## 安装 MAA + +### Windows + +下载完成后,你会得到一个 `.zip` 文件。用解压缩软件将其完全解压后,会得到一个包含 MAA 所有文件的文件夹。 + +::: warning + +1.请不要将 MAA 解压到如 `C:\`、`C:\Program Files\` 等需要 UAC 权限的路径。 + +2.MAA 需要 Visual C++ Redistributable x64(VCRedist x64)和 .NET 10 。请在解压后的 MAA 目录中以管理员身份运行 `DependencySetup_依赖库安装.bat` 来安装这些依赖。安装完成后再运行 `maa.exe`。 + +更多信息请参考[常见问题](./faq.md)置顶。 + +::: + +双击 `MAA.exe` 即可启动 MAA。 + +::: tip + +通过 Windows 包管理器(Winget)安装的用户,可直接在命令行中输入 `maa` 来启动 MAA 而无需解压缩、安装运行库等额外操作。若 PATH 中有 `maa-cli`,可能需要额外步骤以区分二者。 + +::: + +### macOS + +下载完成后,你会得到一个 `.dmg` 文件。双击打开该 `.dmg`,将 `MAA.app` 拖入 `/Applications` 以完成安装。 + +## 后续步骤 + +安装完成后,请返回 [新手上路](./newbie.md) 继续配置,或前往 [功能介绍](./introduction/) 查看 MAA 支持的各项功能吧!若您在安装中遇到问题,可以尝试查阅 [常见问题](./faq.md) 来解决问题。 diff --git a/docs/zh-cn/manual/newbie.md b/docs/zh-cn/manual/newbie.md index 7de9e5af7e..1a3715501a 100644 --- a/docs/zh-cn/manual/newbie.md +++ b/docs/zh-cn/manual/newbie.md @@ -17,28 +17,19 @@ icon: ri:guide-fill 非 Windows 用户请参阅[模拟器及设备支持](./device/)。 -2. 下载正确的版本 - - [MAA 官网](https://maa.plus/)一般会自动选择正确的版本架构,对于大多数阅读本文的用户来说,应为 Windows x64。 - -3. 正确解压 - - 确认解压完整,并确保将 MAA 解压到一个独立的文件夹中。请勿将 MAA 解压到如 `C:\`、`C:\Program Files\` 等需要 UAC 权限的路径。 - -4. 安装运行库 - - MAA 需要 VCRedist x64 和 .NET 10,请运行 MAA 目录下的 `DependencySetup_依赖库安装.bat` 以安装。 - - 更多信息请参考[常见问题](./faq.md)置顶。 - -5. 确认模拟器支持 +2. 确认模拟器支持 查阅[模拟器和设备支持](./device/),确认正在使用的模拟器支持情况。 -6. 正确设置模拟器分辨率 +3. 下载与安装 + + 查阅[下载与安装](./install.md)。 + +4. 正确设置模拟器分辨率 模拟器分辨率应为横屏的 `1280x720` 或 `1920x1080`;对于美服(YosterEN)玩家,必须为 `1920x1080`。 - :::: + +:::: ## 初始配置 @@ -64,12 +55,10 @@ icon: ri:guide-fill **遇到问题多看文档多看文档多看文档**    **遇到问题多看文档多看文档多看文档**    **遇到问题多看文档多看文档多看文档** ::: -1. 运行日志位于 MAA 文件夹下的 `debug` 文件夹内。日志很重要,非常重要。向他人寻求帮助时,请务必带着 `asst.log` 和 `gui.log` 一同前去。 +1. 使用 `设置` - `问题反馈` - `生成日志压缩包` 可生成便于共享的日志压缩包。日志很重要,非常重要。向他人寻求帮助时,请务必带着日志压缩包一同前去。 2. MAA 为了尽可能满足所有用户的各种需求,提供了大量的自定义选项。在认为 MAA 缺少了某项功能时,有可能只是没找到而不是 MAA 没有,如 `手动输入关卡名` 和 `不将已进驻的干员放入宿舍`。 -3. 将鼠标指针放在某些选项上会给出详细说明,如 `不将已进驻的干员放入宿舍`。 +3. 对于部分复选框,在其上右键单击可以将其变为半选状态,这种状态下的复选框会在 MAA 下一次启动时自动清除,可以理解为 `仅一次`。 -4. 对于部分复选框来说,在其上右键单击可以将其变为半选状态,这种状态下的复选框会在 MAA 下一次启动时自动清除,可以理解为 `仅一次`。 - -5. ~~如果不知道自己需要什么,不方便解决有可能会出现的问题,请勿使用内测版。~~ +4. ~~如果不知道自己需要什么,不方便解决有可能会出现的问题,请勿使用内测版。~~ diff --git a/resource/global/txwy/resource/tasks/Stages/AT.json b/resource/global/txwy/resource/tasks/Stages/AT.json new file mode 100644 index 0000000000..460594aba5 --- /dev/null +++ b/resource/global/txwy/resource/tasks/Stages/AT.json @@ -0,0 +1,8 @@ +{ + "AT-OpenOcr": { + "text": ["墟", "鍛冶", "舊跡", "絕緣", "石階"] + }, + "ATChapterToAT": { + "text": ["不夜街區"] + } +} diff --git a/resource/tasks/tasks.json b/resource/tasks/tasks.json index ec5e904b78..ebae48b27f 100644 --- a/resource/tasks/tasks.json +++ b/resource/tasks/tasks.json @@ -3771,7 +3771,7 @@ "algorithm": "OcrDetect", "isAscii": true, "text": [], - "roi": [50, -20, 100, 40], + "roi": [50, 0, 100, 40], "useRaw": false, "specialParams_doc": "Kill区域变化阈值, 模板匹配, 100=1.0; 正常在 0.997-1 之间波动, 少有0.995; _5->_6 的分数最高, 可达0.94", "specialParams": [98] @@ -3816,7 +3816,7 @@ }, "BattleOpersFlag": { "templThreshold": 0.65, - "roi": [35, 588, 1210, 18], + "roi": [35, 588, 1245, 18], "maskRange": [1, 255] }, "BattleAvatarReMatch": { @@ -3934,6 +3934,12 @@ "text": [], "roi": [73, 378, 221, 31] }, + "StageDrops-NoDropCheck": { + "template": "empty.png", + "roi": [60, 510, 150, 160], + "specialParams_Doc": "灰度过滤阈值", + "specialParams": [120] + }, "StageDrops-BaseLine": { "template": "empty.png", "roi": [0, 636, 1280, 2], diff --git a/resource/template/Battle/BattleFlag/BattleKillsFlag.png b/resource/template/Battle/BattleFlag/BattleKillsFlag.png index 79b82c37ca..87889a22b3 100644 Binary files a/resource/template/Battle/BattleFlag/BattleKillsFlag.png and b/resource/template/Battle/BattleFlag/BattleKillsFlag.png differ diff --git a/src/MaaCore/Task/Interface/DebugTask.cpp b/src/MaaCore/Task/Interface/DebugTask.cpp index e723ae7a73..a300b967c2 100644 --- a/src/MaaCore/Task/Interface/DebugTask.cpp +++ b/src/MaaCore/Task/Interface/DebugTask.cpp @@ -1,6 +1,9 @@ #include "DebugTask.h" +#include "MaaUtils/Encoding.h" +#include #include +#include #include "Common/AsstTypes.h" #include "Config/TaskData.h" @@ -9,7 +12,10 @@ #include "Utils/Logger.hpp" #include "Vision/Battle/BattlefieldClassifier.h" #include "Vision/Battle/BattlefieldMatcher.h" +#include "Vision/BestMatcher.h" +#include "Vision/FeatureMatcher.h" #include "Vision/Matcher.h" +#include "Config/TemplResource.h" #include "Vision/Miscellaneous/DepotImageAnalyzer.h" #include "Vision/Miscellaneous/StageDropsImageAnalyzer.h" #include "Vision/MultiMatcher.h" @@ -23,6 +29,42 @@ asst::DebugTask::DebugTask(const AsstCallback& callback, Assistant* inst) : bool asst::DebugTask::run() { + auto image_dep = MaaNS::imread(utils::path("C:\\Users\\status102\\Desktop\\deploy.png")); + static const std::unordered_map RoleMap = { + { "Caster", battle::Role::Caster }, { "Medic", battle::Role::Medic }, { "Pioneer", battle::Role::Pioneer }, + { "Sniper", battle::Role::Sniper }, { "Special", battle::Role::Special }, { "Support", battle::Role::Support }, + { "Tank", battle::Role::Tank }, { "Warrior", battle::Role::Warrior }, { "Drone", battle::Role::Drone }, + }; + + auto image = MaaNS::imread(utils::path("C:\\Users\\status102\\Desktop\\kill_1080p_Vec.png")); + + cv::Mat template_image = TemplResource::get_instance().get_templ("BattleKillsFlag.png"); + auto task = Task.get("BattleKillsFlag"); + + // 对模板进行高斯模糊预处理(解决小尺寸模板的细线条缩放问题) + cv::Mat template_blurred; + cv::GaussianBlur(template_image, template_blurred, cv::Size(3, 3), 0); + + // 同时对输入图像应用相同的模糊处理 + cv::Mat image_blurred; + cv::GaussianBlur(make_roi(image, task->roi), image_blurred, cv::Size(3, 3), 0); + + // 使用模糊后的图像进行匹配 + Matcher analyzer(image_blurred); + analyzer.set_task_info("BattleKillsFlag"); + analyzer.set_roi({ 0, 0, task->roi.width, task->roi.height }); + if (!analyzer.analyze()) { + Log.info("flag not found"); + } + // 使用模糊后的图像进行匹配 + Matcher analyzer2(image_blurred); + analyzer2.set_task_info("BattleKillsFlag"); + analyzer2.set_templ(template_blurred); + analyzer2.set_roi({ 0, 0, task->roi.width, task->roi.height }); + if (!analyzer2.analyze()) { + Log.info("flag not found"); + } + return true; } diff --git a/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp b/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp index fc2b0ba976..b95f04fa9d 100644 --- a/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp +++ b/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp @@ -273,17 +273,48 @@ bool BattlefieldMatcher::kills_flag_analyze() const BattlefieldMatcher::MatchResult> BattlefieldMatcher::kills_analyze() const { - if (hit_kills_cache()) { + const auto& flag_task = Task.get("BattleKillsFlag"); + cv::Mat template_image = TemplResource::get_instance().get_templ("BattleKillsFlag.png"); + cv::Mat match_image = make_roi(m_image, flag_task->roi); + + // 对模板图和截图都进行一次模糊,减少yj缩放对文字及细节的影响,提升命中率 + cv::Mat template_blur, image_blur; + cv::GaussianBlur(template_image, template_blur, cv::Size(3, 3), 0); + cv::GaussianBlur(match_image, image_blur, cv::Size(3, 3), 0); + + Matcher flag_match(image_blur); + flag_match.set_mask_ranges(flag_task->mask_ranges); + flag_match.set_roi({ 0, 0, flag_task->roi.width, flag_task->roi.height }); + flag_match.set_templ(template_blur); + flag_match.set_method(MatchMethod::Ccoeff); + flag_match.set_threshold(flag_task->templ_thresholds); + if (!flag_match.analyze()) { + LogWarn << __FUNCTION__ << "kill flag not found"; + return {}; + } + + auto flag_rect = flag_match.get_result().rect; + flag_rect.x += flag_task->roi.x; + flag_rect.y += flag_task->roi.y; + + if (hit_kills_cache(flag_rect)) { return { .status = MatchStatus::HitCache }; } - TemplDetOCRer kills_analyzer(m_image); - kills_analyzer.set_task_info("BattleKillsFlag", "BattleKills"); - kills_analyzer.set_replace(Task.get("NumberOcrReplace")->replace_map); - auto kills_opt = kills_analyzer.analyze(); + + const auto& ocr_task = Task.get("BattleKills"); + RegionOCRer flag_analyzer(m_image); + flag_analyzer.set_roi(flag_rect.move(ocr_task->roi)); + flag_analyzer.set_use_char_model(ocr_task->is_ascii); + flag_analyzer.set_use_raw(ocr_task->use_raw); + flag_analyzer.set_replace(Task.get("NumberOcrReplace")->replace_map); + auto kills_opt = flag_analyzer.analyze(); if (!kills_opt) { return {}; } - const std::string& kills_text = kills_opt->front().text; + const std::string& kills_text = kills_opt->text; + if (kills_text.empty()) { + return {}; + } size_t pos = kills_text.find('/'); if (pos == std::string::npos) { @@ -338,22 +369,17 @@ BattlefieldMatcher::MatchResult> BattlefieldMatcher::kills_a return { .value = std::make_pair(kills, total_kills), .status = MatchStatus::Success }; } -bool asst::BattlefieldMatcher::hit_kills_cache() const +bool asst::BattlefieldMatcher::hit_kills_cache(Rect flag_rect) const { - if (m_image_prev.empty() || m_image.cols != m_image_prev.cols || m_image.rows != m_image_prev.rows) { + if (m_image_prev.empty() || m_image.size != m_image_prev.size) { return false; } - Matcher flag_match(m_image); - flag_match.set_task_info("BattleKillsFlag"); - if (!flag_match.analyze()) { - return false; - } - const auto& flag_rect = flag_match.get_result().rect; - const auto& task = Task.get("BattleKills"); - const auto& roi = flag_rect.move(task->roi); - cv::Mat kills_image_cache = make_roi(m_image_prev, roi); - cv::Mat kills_image = make_roi(m_image, roi); + const auto& ocr_task = Task.get("BattleKills"); + const auto& roi = flag_rect.move(ocr_task->roi); + + cv::Mat kills_image_cache = make_roi(m_image_prev, correct_rect(roi, m_image_prev)); + cv::Mat kills_image = make_roi(m_image, correct_rect(roi, m_image)); cv::cvtColor(kills_image_cache, kills_image_cache, cv::COLOR_BGR2GRAY); cv::cvtColor(kills_image, kills_image, cv::COLOR_BGR2GRAY); cv::Mat match; @@ -362,7 +388,7 @@ bool asst::BattlefieldMatcher::hit_kills_cache() const cv::minMaxLoc(match, nullptr, &mark); // 正常在 0.997-1 之间波动, 少有0.995 // _5->_6 的分数最高, 可达0.94 - const double threshold = static_cast(task->special_params[0]) / 100; + const double threshold = static_cast(ocr_task->special_params[0]) / 100; return mark > threshold; } @@ -394,7 +420,7 @@ BattlefieldMatcher::MatchResult BattlefieldMatcher::costs_analyze() const bool asst::BattlefieldMatcher::hit_costs_cache() const { - if (m_image_prev.empty() || m_image.cols != m_image_prev.cols || m_image.rows != m_image_prev.rows) { + if (m_image_prev.empty() || m_image.size != m_image_prev.size) { return false; } const auto& task = Task.get("BattleCostData"); diff --git a/src/MaaCore/Vision/Battle/BattlefieldMatcher.h b/src/MaaCore/Vision/Battle/BattlefieldMatcher.h index 9298803ca9..330473fb7d 100644 --- a/src/MaaCore/Vision/Battle/BattlefieldMatcher.h +++ b/src/MaaCore/Vision/Battle/BattlefieldMatcher.h @@ -68,11 +68,11 @@ protected: bool oper_available_analyze(const Rect& roi) const; MatchResult> kills_analyze() const; // 识别击杀数 - // 识别是否持有费用是否命中缓存 - bool hit_kills_cache() const; + // 识别击杀数是否命中缓存 + bool hit_kills_cache(Rect flag_rect) const; bool cost_symbol_analyze() const; // 识别费用左侧图标 MatchResult costs_analyze() const; // 识别费用 - // 识别是否持有费用是否命中缓存 + // 识别持有费用是否命中缓存 bool hit_costs_cache() const; bool in_detail_analyze() const; // 识别是否在详情页 bool speed_button_analyze() const; // 识别是否有加速按钮(在详情页就没有) diff --git a/src/MaaCore/Vision/VisionHelper.cpp b/src/MaaCore/Vision/VisionHelper.cpp index 79804adeb7..3514f9ca10 100644 --- a/src/MaaCore/Vision/VisionHelper.cpp +++ b/src/MaaCore/Vision/VisionHelper.cpp @@ -14,12 +14,18 @@ using namespace asst; VisionHelper::VisionHelper(const cv::Mat& image, const Rect& roi, Assistant* inst) : InstHelper(inst), - m_image(image), + m_image(image) #ifdef ASST_DEBUG - m_image_draw(image.clone()), + , + m_image_draw(image.clone()) #endif - m_roi(correct_rect(roi, image)) { + if (roi.empty()) { + m_roi = { 0, 0, image.cols, image.rows }; + } + else { + m_roi = correct_rect(roi, image); + } } void VisionHelper::set_image(const cv::Mat& image) @@ -90,6 +96,14 @@ Rect VisionHelper::correct_rect(const Rect& rect, const cv::Mat& image) } Rect res = rect; + if (res.x < 0) { + res.x = 0; + res.width += rect.x; + } + if (res.y < 0) { + res.y = 0; + res.height += rect.y; + } res.x = std::clamp(res.x, 0, image.cols - 1); res.y = std::clamp(res.y, 0, image.rows - 1); res.width = std::clamp(res.width, 1, image.cols - res.x); diff --git a/tools/OptimizeTemplates/optimize_templates.json b/tools/OptimizeTemplates/optimize_templates.json index 41582ab88c..d17e410de9 100644 --- a/tools/OptimizeTemplates/optimize_templates.json +++ b/tools/OptimizeTemplates/optimize_templates.json @@ -2653,7 +2653,7 @@ "resource/template/Battle/BattleFlag/EndOfAction": "3a4e90ae88d0ff83304c793bd05695335888d67d0b3224066a22b465c5f53f0b", "resource/template/Battle/BattleFlag/BattleSpeedUp": "f81a6ea54970ec3fdfbea14c2887e067e60ca3ba7d3c7168da7b19c5a62c91ca", "resource/template/Battle/BattleFlag/BattleSkillReadyOnClick-TopView2": "9de4d366eab581c30dfde35346a94197727a6397e7c8f99da07a828f70b89607", - "resource/template/Battle/BattleFlag/BattleKillsFlag": "e167dd5a8ef789c7626eb463110d1899d1987a84003ff9d15fa9a4ddf34103f5", + "resource/template/Battle/BattleFlag/BattleKillsFlag": "7b5db70e0fd69ea0f09db643d6e3c31999fe521a6439fe3ab47aa85a014f3b59", "resource/template/Battle/BattleFlag/AbandonAction": "052a60fec1eb98ff3ab12814e40426ff682377b89c9ee844d3cff210c662559e", "resource/template/Battle/BattleFlag/BattleSkillReady": "6dcb381619811d91419ffda02fa4764908e3d61107abe2ecd0c8e92b631a6746", "resource/template/Battle/BattleFlag/PrtsErrorConfirm": "57ce7a88d0a231a8b77ea24c7f69e4cce65038095dda68d6782afddbcd37566e",