From 4e0c1d0f6c5f23190885d8e60515ff292ff2954f Mon Sep 17 00:00:00 2001 From: status102 <102887808+status102@users.noreply.github.com> Date: Sat, 3 Feb 2024 10:12:36 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=82=89=E9=B8=BD?= =?UTF-8?q?=E6=8A=95=E8=B5=84=E6=93=8D=E4=BD=9C=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/tasks.json | 46 ++++++- .../Roguelike@StageTraderInvest-Arrow.png | Bin 0 -> 6369 bytes src/MaaCore/MaaCore.vcxproj | 2 + src/MaaCore/MaaCore.vcxproj.filters | 6 + src/MaaCore/Task/Interface/RoguelikeTask.cpp | 5 +- src/MaaCore/Task/Roguelike/RoguelikeConfig.h | 6 + .../Roguelike/RoguelikeInvestTaskPlugin.cpp | 129 ++++++++++++++++++ .../Roguelike/RoguelikeInvestTaskPlugin.h | 21 +++ src/MaaWpfGui/Main/AsstProxy.cs | 5 + src/MaaWpfGui/Res/Localizations/en-us.xaml | 1 + src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 1 + src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 1 + src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 1 + src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 1 + 14 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 resource/template/Roguelike@StageTraderInvest-Arrow.png create mode 100644 src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.cpp create mode 100644 src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.h diff --git a/resource/tasks.json b/resource/tasks.json index c63b12a0b5..c0cfc5cab8 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -12727,24 +12727,62 @@ ] }, "Roguelike@StageTraderInvestConfirm": { - "action": "ClickSelf", + "doc": "绑定 RoguelikeInvestTaskPlugin 插件进行投资,本任务仅作为进入投资系统时的初始化确认", "roi": [ 852, 422, 278, 144 ], - "postDelay": 500, "exceededNext": [ "RoguelikeControlTaskPlugin-ExitThenStop" ], "next": [ "Roguelike@StageTraderInvestSystemError", - "Roguelike@StageTraderInvestSystemFull", - "Roguelike@StageTraderInvestConfirm", "Roguelike@StageTraderInvestCancel" ] }, + "Roguelike@StageTraderInvest-Arrow": { + "doc": "商店投资界面的箭头,99 >> 100", + "roi": [ + 750, + 320, + 100, + 100 + ] + }, + "Roguelike@StageTraderInvest-Confirm": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 850, + 470, + 300, + 45 + ] + }, + "Roguelike@StageTraderInvest-Count": { + "doc": "商店投资界面的余额显示,99 >> 100", + "algorithm": "OcrDetect", + "isAscii": true, + "roi": [ + 570, + 335, + 150, + 60 + ] + }, + "Roguelike@StageTraderInvest-Count-Error": { + "doc": "商店投资界面系统出错时余额显示", + "algorithm": "OcrDetect", + "isAscii": true, + "roi": [ + 635, + 205, + 70, + 40 + ] + }, "Roguelike@StageTraderInvestSystem": { "templThreshold": 0.75, "action": "ClickSelf", diff --git a/resource/template/Roguelike@StageTraderInvest-Arrow.png b/resource/template/Roguelike@StageTraderInvest-Arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8e331039db9e6afc36e69eb3e1c089a00c87b9e6 GIT binary patch literal 6369 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 literal 0 HcmV?d00001 diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj index ba260c16cf..2a423e8619 100644 --- a/src/MaaCore/MaaCore.vcxproj +++ b/src/MaaCore/MaaCore.vcxproj @@ -96,6 +96,7 @@ + @@ -273,6 +274,7 @@ + diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters index b12b9bc4cc..90b08de49c 100644 --- a/src/MaaCore/MaaCore.vcxproj.filters +++ b/src/MaaCore/MaaCore.vcxproj.filters @@ -693,6 +693,9 @@ Source\Resource\TaskData + + Source\Task\Roguelike + @@ -1154,5 +1157,8 @@ Source\Task\Infrast + + Source\Task\Roguelike + \ No newline at end of file diff --git a/src/MaaCore/Task/Interface/RoguelikeTask.cpp b/src/MaaCore/Task/Interface/RoguelikeTask.cpp index d223624480..f73922fd3b 100644 --- a/src/MaaCore/Task/Interface/RoguelikeTask.cpp +++ b/src/MaaCore/Task/Interface/RoguelikeTask.cpp @@ -22,6 +22,7 @@ #include "Task/Roguelike/RoguelikeSkillSelectionTaskPlugin.h" #include "Task/Roguelike/RoguelikeStageEncounterTaskPlugin.h" #include "Task/Roguelike/RoguelikeStrategyChangeTaskPlugin.h" +#include "Task/Roguelike/RoguelikeInvestTaskPlugin.h" #include "Utils/Logger.hpp" @@ -40,6 +41,7 @@ asst::RoguelikeTask::RoguelikeTask(const AsstCallback& callback, Assistant* inst m_roguelike_task_ptr->register_plugin(m_roguelike_config_ptr); m_debug_plugin_ptr = m_roguelike_task_ptr->register_plugin(m_roguelike_config_ptr); m_roguelike_task_ptr->register_plugin(m_roguelike_config_ptr)->set_retry_times(0); + m_roguelike_task_ptr->register_plugin(m_roguelike_config_ptr); m_custom_start_plugin_ptr = m_roguelike_task_ptr->register_plugin(m_roguelike_config_ptr); @@ -96,6 +98,8 @@ bool asst::RoguelikeTask::set_params(const json::value& params) // 是否凹指定干员开局直升 m_roguelike_config_ptr->set_start_with_elite_two(params.get("start_with_elite_two", false)); m_roguelike_config_ptr->set_only_start_with_elite_two(params.get("only_start_with_elite_two", false)); + m_roguelike_config_ptr->set_invest_maximum(params.get("investments_count", INT_MAX)); + m_roguelike_config_ptr->set_invest_stop_when_full(params.get("stop_when_investment_full", false)); // 设置层数选点策略,相关逻辑在 RoguelikeStrategyChangeTaskPlugin { @@ -144,7 +148,6 @@ bool asst::RoguelikeTask::set_params(const json::value& params) params.get("investment_enabled", true) ? INT_MAX : 0); m_roguelike_task_ptr->set_times_limit("StageTraderRefreshWithDice", params.get("refresh_trader_with_dice", false) ? INT_MAX : 0); - m_roguelike_task_ptr->set_times_limit("StageTraderInvestConfirm", params.get("investments_count", INT_MAX)); if (params.get("stop_when_investment_full", false)) { constexpr int InvestLimit = 999; diff --git a/src/MaaCore/Task/Roguelike/RoguelikeConfig.h b/src/MaaCore/Task/Roguelike/RoguelikeConfig.h index 8b111c23b5..b2a623c002 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeConfig.h +++ b/src/MaaCore/Task/Roguelike/RoguelikeConfig.h @@ -55,6 +55,10 @@ namespace asst bool get_start_with_elite_two() const { return m_start_with_elite_two; } void set_only_start_with_elite_two(bool value) { m_only_start_with_elite_two = value; } bool get_only_start_with_elite_two() const { return m_only_start_with_elite_two; } + void set_invest_maximum(int value) { m_invest_maximum = value; } + int get_invest_maximum() const { return m_invest_maximum; } + void set_invest_stop_when_full(bool value) { m_invest_stop_when_full = value; } + bool get_invest_stop_when_full() const { return m_invest_stop_when_full; } private: std::string m_theme; // 主题 @@ -62,6 +66,8 @@ namespace asst int m_difficulty = 0; // 难度 bool m_start_with_elite_two = false; // 在刷开局模式下凹开局干员精二直升 bool m_only_start_with_elite_two = false; // 只凹开局干员精二直升且不进行作战 + int m_invest_maximum = 0; // 投资次数上限 + bool m_invest_stop_when_full = false; // 存款满了就停止 /* 以下为每次重置 */ public: diff --git a/src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.cpp new file mode 100644 index 0000000000..18bf66e0b8 --- /dev/null +++ b/src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.cpp @@ -0,0 +1,129 @@ +#include "RoguelikeInvestTaskPlugin.h" + +#include "Config/TaskData.h" +#include "Controller/Controller.h" +#include "Task/ProcessTask.h" +#include "Utils/Logger.hpp" +#include "Vision/RegionOCRer.h" + +bool asst::RoguelikeInvestTaskPlugin::verify(AsstMsg msg, const json::value& details) const +{ + if (msg != AsstMsg::SubTaskStart || details.get("subtask", std::string()) != "ProcessTask") { + return false; + } + + const auto task_name = details.get("details", "task", ""); + if (task_name.ends_with("Roguelike@StageTraderInvestConfirm")) { + return m_invest_count < m_config->get_invest_maximum(); + } + else { + return false; + } +} + +bool asst::RoguelikeInvestTaskPlugin::_run() +{ + LogTraceFunction; + + auto image = ctrler()->get_image(); + + // 进入投资页面成功 + // 999 >> 1000 + // 取消 | 确认投资 + + int count = 0; // 当次已投资的个数 + int retry = 0; // 重试次数 + const auto ocr_current_count = [](const auto& img, const auto& task_name) -> std::optional { + RegionOCRer ocr(img); + ocr.set_task_info(task_name); + if (!ocr.analyze()) { + Log.error(__FUNCTION__, "unable to analyze current investment count."); + return std::nullopt; + }; + int count = 0; + if (!utils::chars_to_number(ocr.get_result().text, count)) { + Log.error(__FUNCTION__, "unable to convert current investment count<", ocr.get_result().text, + "> to number."); + return std::nullopt; + } + return count; + }; + // 当前的存款 + auto deposit = ocr_current_count(image, "Roguelike@StageTraderInvest-Count"); + // 可用投资次数 + int count_limit = m_config->get_invest_maximum() - m_invest_count; + // 投资确认按钮 + const auto& click_rect = Task.get("Roguelike@StageTraderInvest-Confirm")->specific_rect; + Log.info(__FUNCTION__, "开始投资, 存款", deposit.value_or(-1), ", 可投资次数", count_limit); + while (!need_exit() && deposit && *deposit < 999 && count_limit > 0 && retry < 3) { + int times = std::min(15, count_limit - count); + while (!need_exit() && times > 0) { + ctrler()->click(click_rect); + sleep(100); + times--; + } + image = ctrler()->get_image(); + if (!is_investment_available(image)) { // 检查是否处于可投资状态 + break; // 投资系统错误 / 没钱了 + } + else if (auto ocr = ocr_current_count(image, "Roguelike@StageTraderInvest-Count"); ocr) { + // 可继续投资 / 到达投资上限999 + if (*ocr == *deposit || *ocr > 999 || *ocr < 1) { + retry++; // 可能是出错了,重试三次放弃 + } + else { + count += *ocr - *deposit; + deposit = *ocr; + retry = 0; + } + } + else { + Log.error(__FUNCTION__, "未知状态"); + return true; + } + } + + if (auto ocr = ocr_current_count(ctrler()->get_image(), "Roguelike@StageTrader-InvestCount"); ocr) { + // 可继续投资 / 到达投资上限999 + count += *ocr - deposit.value_or(0); + deposit = *ocr; + } + + const auto total = count + m_invest_count; + + auto info = basic_info_with_what("RoguelikeInvestment"); + info["details"]["count"] = count; + info["details"]["total"] = total; + info["details"]["deposit"] = deposit ? *deposit : -1; + callback(AsstMsg::SubTaskExtraInfo, info); + + Log.info(__FUNCTION__, "本轮投资结束, 投资", count, ", 共投资", total, "; 系统余额", deposit ? *deposit : -1); + m_invest_count = total; + + if (count_limit - count <= 0) { + Log.info(__FUNCTION__, "投资达到设置上限,", m_config->get_invest_maximum()); + stop_roguelike(); + m_task_ptr->set_enable(false); + return true; + } + if (deposit.value_or(0) == 999 && m_config->get_invest_stop_when_full()) { + Log.info(__FUNCTION__, "存款已满"); + stop_roguelike(); + m_task_ptr->set_enable(false); + } + + return true; +} + +bool asst::RoguelikeInvestTaskPlugin::is_investment_available(const cv::Mat& image) const +{ + auto task = ProcessTask(*this, { "Roguelike@StageTraderInvest-Arrow", "Roguelike@StageTraderInvestSystemError" }); + task.set_reusable_image(image).set_retry_times(3); + task.set_times_limit("Roguelike@StageTraderInvestCancel", 0); + return task.run() && task.get_last_task_name() == "Roguelike@StageTraderInvest-Arrow"; +} + +void asst::RoguelikeInvestTaskPlugin::stop_roguelike() +{ + dynamic_cast(m_task_ptr)->set_times_limit("Roguelike@StageTraderInvestConfirm", 0); +} diff --git a/src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.h b/src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.h new file mode 100644 index 0000000000..2819617567 --- /dev/null +++ b/src/MaaCore/Task/Roguelike/RoguelikeInvestTaskPlugin.h @@ -0,0 +1,21 @@ +#pragma once +#include "AbstractRoguelikeTaskPlugin.h" + +namespace asst +{ + // 肉鸽投资插件 + class RoguelikeInvestTaskPlugin : public AbstractRoguelikeTaskPlugin + { + public: + using AbstractRoguelikeTaskPlugin::AbstractRoguelikeTaskPlugin; + virtual ~RoguelikeInvestTaskPlugin() override = default; + virtual bool verify(AsstMsg msg, const json::value& details) const override; + + private: + virtual bool _run() override; + bool is_investment_available(const cv::Mat& image) const; + void stop_roguelike(); + + int m_invest_count = 0; + }; +} // namespace asst diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index e6becac79d..8f14c7e91b 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -1175,6 +1175,11 @@ namespace MaaWpfGui.Main break; } + case "RoguelikeInvestment": + Instances.TaskQueueViewModel.AddLog(string.Format(LocalizationHelper.GetString("RoguelikeInvestment"), subTaskDetails["count"], subTaskDetails["total"], subTaskDetails["deposit"]), UiLogColor.Info); + break; + + case "RoguelikeSettlement": // 肉鸽结算 bool roguelikeGamePass = (bool)subTaskDetails["game_pass"]; diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 656e441f21..4788cc822b 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -634,6 +634,7 @@ The video aspect ratio needs to be 16:9 without interference factors such as bla Node: Emergency Operation Node: Dreadful Foe Event: + Invested {1}(+{0}), deposit: {2} Roguelike Settlement: {0}\nExplore: {1} Floor {2} Step\nCombat: {3} / {4} Emergency / {5} BOSS\nRecruit: {6}\nCollectibles: {7}\nDifficulty: {8}\nScore: {9}\nExp: {10}, Skill: {11} Current times Nothing diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index b7f5f2bea0..a185164368 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -634,6 +634,7 @@ Bilibili: ログイン インターフェイスに表示されるアカウント ステージ: 緊急作戦 ステージ: 悪路凶敵 イベント: + 投資済み: {1}(+{0})、デポジット: {2} ローグライク決済: {0}\n探検する: {1} 床 {2} ステップ\n戦闘: {3} / {4} 緊急 / {5} BOSS\nリクルート: {6}\nコレクション: {7}\n困難: {8}\nスコア: {9}\n経験値: {10}, スキル: {11} 現在の回数 ドロップなし diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index b00c9392cf..f5eb44cdf4 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -634,6 +634,7 @@ Bilibili: 로그인 인터페이스에 표시되는 계정 이름(예: 장산) 레벨: 긴급 작전 레벨: 험난한 길 이벤트: + 투자: {1}(+{0}), 예금: {2} 로그라이크 종착점: {0}\n클리어 : {1} 구역 {2} 노드\n전투 횟수: {3} 일반 / {4} 정예 / {5} 리더\n모집 횟수 : {6} 회\n수집: {7}\n난이도: {8}\n점수: {9}\n경험치: {10}, 스킬: {11} 현재 횟수 없음 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 1aff3233bc..dcd328bec0 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -634,6 +634,7 @@ 关卡: 紧急作战 关卡: 险路恶敌 事件: + 已投资 {1}(+{0}), 存款: {2} 肉鸽结算: {0}\n探索: {1} 层 {2} 步\n战斗: {3}/{4} 紧急/{5} BOSS\n招募: {6} 收藏品: {7}\n难度: {8} 得分: {9}\nExp: {10} Skill: {11} 当前次数 diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index f2f2ac27eb..75bfcc6bda 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -632,6 +632,7 @@ 關卡: 緊急作戰 關卡: 險路惡敵 事件: + 已投資 {1}(+{0}), 存款: {2} 肉鴿結算: {0}\n探索: {1} 层 {2} 步\n戰鬥: {3}/{4} 緊急/{5} BOSS\n招募: {6} 收藏品: {7}\n難度: {8} 得分: {9}\nExp: {10} Skill: {11} 目前次數