diff --git a/resource/tasks.json b/resource/tasks.json
index dee2ea7071..522607cfb2 100644
--- a/resource/tasks.json
+++ b/resource/tasks.json
@@ -871,6 +871,83 @@
]
]
},
+ "SideStoryReopen": {
+ "action": "ClickSelf",
+ "algorithm": "OcrDetect",
+ "text": [
+ "复刻"
+ ],
+ "roi": [
+ 0,
+ 500,
+ 500,
+ 150
+ ],
+ "postDelay": 3000
+ },
+ "StageQueue@CheckPrts": {
+ "algorithm": "JustReturn",
+ "action": "DoNothing",
+ "maxTimes": 3,
+ "next": [
+ "StageQueue@UsePrtsSuccess",
+ "StageQueue@UsePrts"
+ ]
+ },
+ "StageQueue@UsePrts": {
+ "Doc": "执行Prts激活",
+ "template": "UsePrts.png",
+ "roi": [
+ 1000,
+ 550,
+ 280,
+ 90
+ ],
+ "next": ["StageQueue@CheckPrts"]
+ },
+ "StageQueue@UsePrtsSuccess": {
+ "Doc": "检查代理激活没有,roi引用UnableToAgent2",
+ "template": "UsePrtsSuccess.png",
+ "action": "DoNothing",
+ "roi": [
+ 1000,
+ 550,
+ 280,
+ 90
+ ]
+ },
+ "StageQueue": {
+ "baseTask": "Fight",
+ "template": "Terminal.png",
+ "maxTimes": 3,
+ "exceededNext": ["Stop"],
+ "next": [
+ "StageQueue@GoLastBattle",
+ "StageQueue"
+ ],
+ "postDelay": 3000
+ },
+ "StageQueue@GoLastBattle": {
+ "next": ["StageQueue@StartButton1"]
+ },
+ "StageQueue@CloseStonePage": {
+ "next": []
+ },
+ "StageQueue@StartButton1":{
+ "Doc": "开始战斗1",
+ "action": "DoNothing",
+ "maxTimes": 1,
+ "exceededNext": [],
+ "next": [
+ "StageQueue@StartButton2",
+ "StageQueue@StartButton2Adverse",
+ "StageQueue@UseMedicine",
+ "StageQueue@UseStone",
+ "StageQueue@NoStone",
+ "StageQueue@OfflineConfirm",
+ "StageQueue@StartButton1TryAgain"
+ ]
+ },
"SL-8": {
"algorithm": "JustReturn",
"sub": [
@@ -3556,6 +3633,7 @@
"SanityMatch": {
"algorithm": "OcrDetect",
"text": [],
+ "isAscii": true,
"roi": [
1120,
20,
diff --git a/resource/template/UsePrtsSuccess.png b/resource/template/UsePrtsSuccess.png
new file mode 100644
index 0000000000..2affaf2929
Binary files /dev/null and b/resource/template/UsePrtsSuccess.png differ
diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj
index 06a32c91bb..1d51318735 100644
--- a/src/MaaCore/MaaCore.vcxproj
+++ b/src/MaaCore/MaaCore.vcxproj
@@ -67,6 +67,8 @@
+
+
@@ -225,6 +227,8 @@
+
+
diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters
index e364b613c5..ee86cc2261 100644
--- a/src/MaaCore/MaaCore.vcxproj.filters
+++ b/src/MaaCore/MaaCore.vcxproj.filters
@@ -621,6 +621,12 @@
源文件\Task\Fight
+
+ 源文件\Task\Fight
+
+
+ 源文件\Task\Fight
+
@@ -1025,5 +1031,11 @@
源文件\Task\Fight
+
+ 源文件\Task\Fight
+
+
+ 源文件\Task\Fight
+
\ No newline at end of file
diff --git a/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.cpp b/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.cpp
index 00ec13e612..c0470a050a 100644
--- a/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.cpp
+++ b/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.cpp
@@ -16,7 +16,7 @@ bool asst::SanityBeforeStagePlugin::verify(AsstMsg msg, const json::value& detai
}
const std::string task = details.at("details").at("task").as_string();
- if (task == "StartButton1" || task == "Fight@StartButton1") {
+ if (task.ends_with("StartButton1")) {
return true;
}
else {
diff --git a/src/MaaCore/Task/Fight/SideStoryReopenTask.cpp b/src/MaaCore/Task/Fight/SideStoryReopenTask.cpp
new file mode 100644
index 0000000000..0a873111c4
--- /dev/null
+++ b/src/MaaCore/Task/Fight/SideStoryReopenTask.cpp
@@ -0,0 +1,201 @@
+#include "SideStoryReopenTask.h"
+
+#include "Config/TaskData.h"
+#include "Task/Fight/StageQueueMissionCompletedPlugin.h"
+#include "Task/ProcessTask.h"
+#include "Utils/Logger.hpp"
+
+void asst::SideStoryReopenTask::set_sidestory_name(std::string sidestory_name)
+{
+ std::string upper_prefix = std::move(sidestory_name);
+ ranges::transform(upper_prefix, upper_prefix.begin(),
+ [](char ch) -> char { return static_cast(::toupper(ch)); });
+ m_sidestory_name = upper_prefix;
+}
+
+void asst::SideStoryReopenTask::set_medicine(int medicine)
+{
+ m_medicine = medicine;
+}
+
+void asst::SideStoryReopenTask::set_expiring_medicine(int expiring_medicine)
+{
+ m_expiring_medicine = expiring_medicine;
+}
+
+void asst::SideStoryReopenTask::set_stone(int stone)
+{
+ m_stone = stone;
+}
+
+bool asst::SideStoryReopenTask::set_server(std::string server)
+{
+ m_server = std::move(server);
+ return true;
+}
+bool asst::SideStoryReopenTask::set_enable_penguid(bool enable)
+{
+ m_enable_penguid = enable;
+ return true;
+}
+bool asst::SideStoryReopenTask::set_penguin_id(std::string id)
+{
+ m_penguin_id = std::move(id);
+ return true;
+}
+bool asst::SideStoryReopenTask::_run()
+{
+ LogTraceFunction;
+
+ if (need_exit()) {
+ return false;
+ }
+ clear();
+
+ if (m_sidestory_name.length() != 2) {
+ Log.error(__FUNCTION__, "SideStory_name.len != 2");
+ return false;
+ }
+
+ std::string m_sidestory_reopen_task = m_sidestory_name + "ChapterTo" + m_sidestory_name;
+ if (!Task.get(m_sidestory_reopen_task)) {
+ Log.error(__FUNCTION__, m_sidestory_reopen_task, "task not exists");
+
+ json::value task_not_exists = basic_info_with_what("SideStoryReopenTaskNotExists");
+ callback(AsstMsg::SubTaskExtraInfo, task_not_exists);
+ return false;
+ }
+
+ Task.get("SideStoryReopen")->next = { m_sidestory_name + "ChapterTo" + m_sidestory_name };
+
+ if (!navigate_to_normal_page()) {
+ Log.error(__FUNCTION__, "cound not navigate to normal page");
+ return false;
+ }
+
+ // 选择关卡并依次配置Task信息
+ for (int stage_index = 1; stage_index < 10; stage_index++) {
+ if (need_exit()) {
+ return false;
+ }
+
+ // 如果无法选中则结束任务,如果不是第一关则认为已完成
+ if (!select_stage(stage_index)) {
+ if (stage_index == 1) {
+ json::value battle_info = basic_info_with_what("StageQueueStageNotFound");
+ battle_info["details"]["stage_code"] = m_sidestory_name + "-" + (char)(stage_index + '0');
+
+ callback(AsstMsg::SubTaskExtraInfo, battle_info);
+ return false;
+ }
+ else {
+ // 已打至index-1关
+ return true;
+ }
+ }
+ // 检查 / 激活代理,代不了就下一关
+ if (!activate_prts()) {
+ json::value battle_info = basic_info_with_what("StageQueueUnableToAgent");
+ battle_info["details"]["stage_code"] = m_sidestory_name + "-" + (char)(stage_index + '0');
+ callback(AsstMsg::SubTaskExtraInfo, battle_info);
+ continue;
+ }
+
+ auto result = fight(false, false);
+ if (!result) {
+ if (m_sanity_not_enough) {
+ if (m_medicine > m_cur_medicine) {
+ m_cur_medicine++;
+ result = fight(true, false);
+ }
+ else if (m_stone > m_cur_stone) {
+ m_cur_stone++;
+ result = fight(false, true);
+ }
+ else {
+ // 理智真不够了
+ return true;
+ }
+ }
+ }
+ if (!result) {
+ // 遇到掉线、意外情况,结束任务
+ return false;
+ }
+ }
+ return true;
+}
+///
+/// 从首页导航至普通关页面
+///
+bool asst::SideStoryReopenTask::navigate_to_normal_page()
+{
+ LogTraceFunction;
+
+ return ProcessTask(*this, { "StageBegin" }).set_times_limit("GoLastBattle", 0).run() &&
+ ProcessTask(*this, { "SideStoryReopen" }).run();
+}
+///
+/// 点选关卡
+///
+/// 第几关,从1开始
+bool asst::SideStoryReopenTask::select_stage(int stage_index)
+{
+ LogTraceFunction;
+
+ std::string m_stage_code = m_sidestory_name + "-" + std::to_string(stage_index);
+
+ Task.get(m_stage_code + "@ClickStageName")->text = { m_stage_code };
+ Task.get(m_stage_code + "@ClickedCorrectStage")->text = { m_stage_code };
+ // 防止在关卡名展开的情况下无法滑动,调整滑动区域
+ Task.get(m_stage_code + "@FullStageNavigation")->specific_rect = Rect(600, 100, 20, 20);
+ return ProcessTask(*this, { m_stage_code + "@StageNavigationBegin" }).run();
+}
+///
+/// 检查 / 激活代理
+///
+bool asst::SideStoryReopenTask::activate_prts()
+{
+ LogTraceFunction;
+ // 复制一下UnableToAgent2的roi
+ Task.get("StageQueue@UsePrtsSuccess")->roi = Task.get("UnableToAgent2")->roi;
+ return ProcessTask(*this, { "StageQueue@CheckPrts" }).run();
+}
+
+bool asst::SideStoryReopenTask::fight(bool use_medicine, bool use_stone)
+{
+ LogTraceFunction;
+ m_sanity_not_enough = false;
+
+ auto fight_task = ProcessTask(*this, { "StageQueue@StartButton1" });
+
+ // 配置药+源石
+ fight_task.set_times_limit("StageQueue@MedicineConfirm", use_medicine ? 1 : 0)
+ .set_times_limit("StageQueue@StoneConfirm", use_stone ? 1 : 0)
+ .set_times_limit("StageQueue@ExpiringMedicineConfirm", m_expiring_medicine)
+ .set_times_limit("StageQueue@StartButton1", 1)
+ .set_times_limit("StageQueue@StartButton2", 1);
+
+ auto plugin = fight_task.register_plugin();
+ plugin->set_drop_stats(&m_drop_stats);
+ plugin->set_enable_penguid(m_enable_penguid);
+ plugin->set_server(m_server);
+ plugin->set_penguin_id(m_penguin_id);
+ auto result = fight_task.run();
+ if (!result) {
+ return false;
+ }
+ if (fight_task.get_last_task_name() == "StageQueue@CloseStonePage") {
+ m_sanity_not_enough = true;
+ }
+ else {
+ return true;
+ }
+ return false;
+}
+
+void asst::SideStoryReopenTask::clear()
+{
+ m_cur_medicine = 0;
+ m_cur_stone = 0;
+}
diff --git a/src/MaaCore/Task/Fight/SideStoryReopenTask.h b/src/MaaCore/Task/Fight/SideStoryReopenTask.h
new file mode 100644
index 0000000000..8f3faa09cb
--- /dev/null
+++ b/src/MaaCore/Task/Fight/SideStoryReopenTask.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "Task/AbstractTask.h"
+
+namespace asst
+{
+ class SideStoryReopenTask final : public AbstractTask
+ {
+ public:
+ using AbstractTask::AbstractTask;
+ //SideStoryReopenTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain);
+ virtual ~SideStoryReopenTask() noexcept override = default;
+
+ void set_sidestory_name(std::string sidestory_name);
+ void set_medicine(int medicine);
+ void set_expiring_medicine(int expiring_medicine);
+ void set_stone(int stone);
+
+ bool set_enable_penguid(bool enable);
+ bool set_penguin_id(std::string id);
+ bool set_server(std::string server);
+
+ private:
+ virtual bool _run() override;
+ bool navigate_to_normal_page();
+ bool select_stage(int stage_index);
+ bool activate_prts();
+ bool fight(bool use_medicine, bool use_stone);
+ void clear();
+
+ // 活动名简称:IC、CW等
+ std::string m_sidestory_name;
+ int m_medicine = 0;
+ int m_expiring_medicine = 0;
+ int m_stone = 0;
+ // 已使用的理智药数量,不计算临期药品。在每次任务开始前,通过clear()清空
+ int m_cur_medicine;
+ // 已碎石数量。在每次任务开始前,通过clear()清空
+ int m_cur_stone;
+ // 理智不够,需要吃药 / 碎石
+ bool m_sanity_not_enough = false;
+ std::unordered_map m_drop_stats;
+ // 企鹅物流上报用
+ bool m_enable_penguid = false;
+ std::string m_penguin_id;
+ std::string m_server = "CN";
+ };
+
+}
diff --git a/src/MaaCore/Task/Fight/StageNavigationTask.h b/src/MaaCore/Task/Fight/StageNavigationTask.h
index 62f48ed8de..e7d423a273 100644
--- a/src/MaaCore/Task/Fight/StageNavigationTask.h
+++ b/src/MaaCore/Task/Fight/StageNavigationTask.h
@@ -18,6 +18,7 @@ namespace asst
bool chapter_wayfinding();
bool swipe_and_find_stage();
+ // 是否有定义任务名的Task
bool m_is_directly = false;
std::string m_directly_task;
// Not directly
diff --git a/src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.cpp b/src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.cpp
new file mode 100644
index 0000000000..66539d105c
--- /dev/null
+++ b/src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.cpp
@@ -0,0 +1,233 @@
+#include "StageQueueMissionCompletedPlugin.h"
+
+#include "Config/Miscellaneous/ItemConfig.h"
+#include "Config/TaskData.h"
+#include "Controller/Controller.h"
+#include "StageDropsTaskPlugin.h"
+#include "Utils/ImageIo.hpp"
+#include "Utils/Logger.hpp"
+#include "Vision/Matcher.h"
+#include "Vision/Miscellaneous/StageDropsImageAnalyzer.h"
+#include "Vision/RegionOCRer.h"
+
+bool asst::StageQueueMissionCompletedPlugin::verify(AsstMsg msg, const json::value& details) const
+{
+ if (msg != AsstMsg::SubTaskStart || details.get("subtask", std::string()) != "ProcessTask") {
+ return false;
+ }
+
+ const std::string task = details.at("details").at("task").as_string();
+ if (task == ("StageQueue@EndOfAction")) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+void asst::StageQueueMissionCompletedPlugin::set_drop_stats(std::unordered_map* drop_stats)
+{
+ m_drop_stats = drop_stats;
+}
+
+bool asst::StageQueueMissionCompletedPlugin::_run()
+{
+ LogTraceFunction;
+
+ mission_completed();
+ return true;
+}
+
+///
+/// 识别关卡是否为三星完成
+///
+void asst::StageQueueMissionCompletedPlugin::mission_completed()
+{
+ LogTraceFunction;
+
+ sleep(Task.get("PRTS")->post_delay);
+ if (need_exit()) {
+ return;
+ }
+
+ StageDropsImageAnalyzer analyzer(ctrler()->get_image());
+ if (!analyzer.analyze()) {
+ Log.error(__FUNCTION__, "StageDropsImage Analyze failed");
+ return;
+ }
+ auto&& [code, difficulty] = analyzer.get_stage_key();
+
+ std::string stage_code = std::move(code);
+ ranges::transform(stage_code, stage_code.begin(), [](char ch) -> char { return static_cast(::toupper(ch)); });
+
+ Log.info(__FUNCTION__, "Stage Code:", stage_code, "Stars:", analyzer.get_stars());
+
+ json::value battle_info = basic_info_with_what("StageQueueMissionCompleted");
+ battle_info["details"]["stage_code"] = stage_code;
+ battle_info["details"]["stars"] = analyzer.get_stars();
+ callback(AsstMsg::SubTaskExtraInfo, battle_info);
+
+ drop_info_callback(stage_code, difficulty, analyzer);
+}
+// 抄自StageDropsTaskPlugin
+void asst::StageQueueMissionCompletedPlugin::drop_info_callback(std::string stage_code,
+ StageDifficulty stage_difficulty,
+ StageDropsImageAnalyzer analyzer)
+{
+ LogTraceFunction;
+
+ if (m_drop_stats == nullptr) {
+ Log.error(__FUNCTION__, "drop stats is not initialized");
+ return;
+ }
+
+ std::unordered_map cur_drops_count;
+ std::vector drops_vec;
+ for (const auto& drop : analyzer.get_drops()) {
+ (*m_drop_stats)[drop.item_id] += drop.quantity;
+ cur_drops_count.emplace(drop.item_id, drop.quantity);
+ json::value info;
+ info["itemId"] = drop.item_id;
+ info["quantity"] = drop.quantity;
+ info["itemName"] = drop.item_name;
+ info["dropType"] = drop.drop_type_name;
+ drops_vec.emplace_back(std::move(info));
+ }
+
+ std::vector stats_vec;
+ for (auto&& [id, count] : *m_drop_stats) {
+ json::value info;
+ info["itemId"] = id;
+ const std::string& name = ItemData.get_item_name(id);
+ info["itemName"] = name.empty() ? id : name;
+ info["quantity"] = count;
+ if (auto iter = cur_drops_count.find(id); iter != cur_drops_count.end()) {
+ info["addQuantity"] = iter->second;
+ }
+ else {
+ info["addQuantity"] = 0;
+ }
+ stats_vec.emplace_back(std::move(info));
+ }
+
+ json::value info = basic_info_with_what("StageDrops");
+ json::value& details = info["details"];
+ details["stars"] = analyzer.get_stars();
+ details["stats"] = json::array(std::move(stats_vec));
+ details["drops"] = json::array(std::move(drops_vec));
+ json::value& stage = details["stage"];
+ stage["stageCode"] = stage_code;
+ if (!stage_code.empty()) {
+ stage["stageId"] = StageDrops.get_stage_info(stage_code, stage_difficulty).stage_id;
+ }
+
+ callback(AsstMsg::SubTaskExtraInfo, info);
+ m_cur_info_json = std::move(details);
+
+ if (m_enable_penguid) {
+ upload_to_penguin(stage_code, analyzer.get_stars());
+ }
+}
+bool asst::StageQueueMissionCompletedPlugin::set_server(std::string server)
+{
+ m_server = std::move(server);
+ return true;
+}
+bool asst::StageQueueMissionCompletedPlugin::set_enable_penguid(bool enable)
+{
+ m_enable_penguid = enable;
+ return true;
+}
+bool asst::StageQueueMissionCompletedPlugin::set_penguin_id(std::string id)
+{
+ m_penguin_id = std::move(id);
+ return true;
+}
+void asst::StageQueueMissionCompletedPlugin::upload_to_penguin(std::string stage_code, int stars)
+{
+ LogTraceFunction;
+
+ if (m_server != "CN" && m_server != "US" && m_server != "JP" && m_server != "KR") {
+ return;
+ }
+
+ json::value cb_info = basic_info();
+ cb_info["subtask"] = "ReportToPenguinStats";
+
+ // Doc: https://developer.penguin-stats_vec.io/public-api/api-v2-instruction/report-api
+ std::string stage_id = m_cur_info_json.get("stage", "stageId", std::string());
+ if (stage_id.empty()) {
+ cb_info["why"] = "UnknownStage";
+ cb_info["details"] = json::object { { "stage_code", stage_code } };
+ callback(AsstMsg::SubTaskError, cb_info);
+ return;
+ }
+ if (stars != 3) {
+ cb_info["why"] = "NotThreeStars";
+ callback(AsstMsg::SubTaskError, cb_info);
+ return;
+ }
+ json::value body;
+ body["server"] = m_server;
+ body["stageId"] = stage_id;
+ auto& all_drops = body["drops"];
+ for (const auto& drop : m_cur_info_json["drops"].as_array()) {
+ static const std::array filter = {
+ "NORMAL_DROP",
+ "EXTRA_DROP",
+ "FURNITURE",
+ "SPECIAL_DROP",
+ };
+ std::string drop_type = drop.at("dropType").as_string();
+ if (drop_type == "UNKNOWN_DROP") {
+ cb_info["why"] = "UnknownDropType";
+ callback(AsstMsg::SubTaskError, cb_info);
+ return;
+ }
+ if (ranges::find(filter, drop_type) == filter.cend()) {
+ continue;
+ }
+ if (drop.at("itemId").as_string().empty()) {
+ cb_info["why"] = "UnknownDrops";
+ callback(AsstMsg::SubTaskError, cb_info);
+ return;
+ }
+ json::value format_drop = drop;
+ format_drop.as_object().erase("itemName");
+ all_drops.array_emplace(std::move(format_drop));
+ }
+ body["source"] = UploadDataSource;
+ body["version"] = Version;
+
+ std::unordered_map extra_headers;
+ if (!m_penguin_id.empty()) {
+ extra_headers.insert({ "authorization", "PenguinID " + m_penguin_id });
+ }
+
+ if (!m_report_penguin_task_ptr) {
+ m_report_penguin_task_ptr = std::make_shared(report_penguin_callback, this);
+ }
+
+ m_report_penguin_task_ptr->set_report_type(ReportType::PenguinStats)
+ .set_body(body.to_string())
+ .set_extra_headers(extra_headers)
+ .set_retry_times(5)
+ .run();
+}
+void asst::StageQueueMissionCompletedPlugin::report_penguin_callback(AsstMsg msg, const json::value& detail,
+ AbstractTask* task_ptr)
+{
+ LogTraceFunction;
+
+ auto p_this = dynamic_cast(task_ptr);
+ if (!p_this) {
+ return;
+ }
+
+ if (msg == AsstMsg::SubTaskExtraInfo && detail.get("what", std::string()) == "PenguinId") {
+ std::string id = detail.get("details", "id", std::string());
+ p_this->m_penguin_id = id;
+ }
+
+ p_this->callback(msg, detail);
+}
diff --git a/src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.h b/src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.h
new file mode 100644
index 0000000000..a4d664b571
--- /dev/null
+++ b/src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.h
@@ -0,0 +1,45 @@
+#pragma once
+#include "Task/AbstractTaskPlugin.h"
+
+#include
+
+#include "Config/Miscellaneous/StageDropsConfig.h"
+#include "Config/TaskData.h"
+#include "Controller/Controller.h"
+#include "Task/ReportDataTask.h"
+#include "Utils/ImageIo.hpp"
+#include "Utils/Logger.hpp"
+#include "Vision/Miscellaneous/StageDropsImageAnalyzer.h"
+
+namespace asst
+{
+ class StageQueueMissionCompletedPlugin : public AbstractTaskPlugin
+ {
+ public:
+ using AbstractTaskPlugin::AbstractTaskPlugin;
+ virtual ~StageQueueMissionCompletedPlugin() override = default;
+ virtual bool verify(AsstMsg msg, const json::value& details) const override;
+ void set_drop_stats(std::unordered_map* m_drop_stats);
+
+ bool set_enable_penguid(bool enable);
+ bool set_penguin_id(std::string id);
+ bool set_server(std::string server);
+
+ private:
+ virtual bool _run() override;
+
+ void mission_completed();
+ void drop_info_callback(std::string stage_code, StageDifficulty stage_difficulty,
+ StageDropsImageAnalyzer analyzer);
+ void upload_to_penguin(std::string stage_code, int stars);
+ static void report_penguin_callback(AsstMsg msg, const json::value& detail, AbstractTask* task_ptr);
+
+ std::unordered_map* m_drop_stats = nullptr;
+ json::value m_cur_info_json;
+ // 企鹅物流上报用
+ bool m_enable_penguid = false;
+ std::string m_penguin_id;
+ std::string m_server = "CN";
+ std::shared_ptr m_report_penguin_task_ptr = nullptr;
+ };
+}
diff --git a/src/MaaCore/Task/Interface/FightTask.cpp b/src/MaaCore/Task/Interface/FightTask.cpp
index d4480bacb2..ab468f5d87 100644
--- a/src/MaaCore/Task/Interface/FightTask.cpp
+++ b/src/MaaCore/Task/Interface/FightTask.cpp
@@ -3,11 +3,11 @@
#include
#include "Config/TaskData.h"
-
#include "Task/Fight/DrGrandetTaskPlugin.h"
+#include "Task/Fight/SanityBeforeStagePlugin.h"
+#include "Task/Fight/SideStoryReopenTask.h"
#include "Task/Fight/StageDropsTaskPlugin.h"
#include "Task/Fight/StageNavigationTask.h"
-#include "Task/Fight/SanityBeforeStagePlugin.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
#include "Utils/Ranges.hpp"
@@ -16,7 +16,8 @@ asst::FightTask::FightTask(const AsstCallback& callback, Assistant* inst)
: InterfaceTask(callback, inst, TaskType),
m_start_up_task_ptr(std::make_shared(m_callback, m_inst, TaskType)),
m_stage_navigation_task_ptr(std::make_shared(m_callback, m_inst, TaskType)),
- m_fight_task_ptr(std::make_shared(m_callback, m_inst, TaskType))
+ m_fight_task_ptr(std::make_shared(m_callback, m_inst, TaskType)),
+ m_sidestory_reopen_task_ptr(std::make_shared(m_callback, m_inst, TaskType))
{
LogTraceFunction;
@@ -35,6 +36,7 @@ asst::FightTask::FightTask(const AsstCallback& callback, Assistant* inst)
.set_retry_times(5);
m_stage_navigation_task_ptr->set_enable(false).set_retry_times(0);
+ m_sidestory_reopen_task_ptr->set_enable(false);
// 开始战斗任务
m_fight_task_ptr->set_tasks({ "FightBegin" })
@@ -52,6 +54,7 @@ asst::FightTask::FightTask(const AsstCallback& callback, Assistant* inst)
m_subtasks.emplace_back(m_start_up_task_ptr);
m_subtasks.emplace_back(m_stage_navigation_task_ptr);
m_subtasks.emplace_back(m_fight_task_ptr);
+ m_subtasks.emplace_back(m_sidestory_reopen_task_ptr);
}
bool asst::FightTask::set_params(const json::value& params)
@@ -81,15 +84,28 @@ bool asst::FightTask::set_params(const json::value& params)
if (stage.empty()) {
m_start_up_task_ptr->set_tasks({ "LastOrCurBattleBegin" }).set_times_limit("GoLastBattle", INT_MAX);
m_stage_navigation_task_ptr->set_enable(false);
+ m_sidestory_reopen_task_ptr->set_enable(false);
}
else {
m_start_up_task_ptr->set_tasks({ "StageBegin" }).set_times_limit("GoLastBattle", 0);
- if (!m_stage_navigation_task_ptr->set_stage_name(stage)) {
+ if (stage.starts_with("SSReopen-") && stage.length() == 11) {
+ m_sidestory_reopen_task_ptr->set_sidestory_name(stage.substr(9));
+ m_sidestory_reopen_task_ptr->set_enable(true);
+ m_stage_navigation_task_ptr->set_enable(false);
+ }
+ else if (m_stage_navigation_task_ptr->set_stage_name(stage)) {
+ m_sidestory_reopen_task_ptr->set_enable(false);
+ m_stage_navigation_task_ptr->set_enable(true);
+ }
+ else {
+ m_stage_navigation_task_ptr->set_enable(false);
+ m_sidestory_reopen_task_ptr->set_enable(false);
Log.error("Cannot set stage", stage);
return false;
}
- m_stage_navigation_task_ptr->set_enable(true);
}
+ m_start_up_task_ptr->set_enable(!m_sidestory_reopen_task_ptr->get_enable());
+ m_fight_task_ptr->set_enable(!m_sidestory_reopen_task_ptr->get_enable());
m_stage_drops_plugin_ptr->set_server(server);
}
@@ -102,5 +118,12 @@ bool asst::FightTask::set_params(const json::value& params)
m_stage_drops_plugin_ptr->set_enable_penguid(enable_penguid);
m_stage_drops_plugin_ptr->set_penguin_id(std::move(penguin_id));
+ m_sidestory_reopen_task_ptr->set_medicine(medicine);
+ m_sidestory_reopen_task_ptr->set_expiring_medicine(expiring_medicine);
+ m_sidestory_reopen_task_ptr->set_stone(stone);
+ m_sidestory_reopen_task_ptr->set_enable_penguid(enable_penguid);
+ m_sidestory_reopen_task_ptr->set_penguin_id(std::move(penguin_id));
+ m_sidestory_reopen_task_ptr->set_server(server);
+
return true;
}
diff --git a/src/MaaCore/Task/Interface/FightTask.h b/src/MaaCore/Task/Interface/FightTask.h
index 4d3730f55e..80f2727bc1 100644
--- a/src/MaaCore/Task/Interface/FightTask.h
+++ b/src/MaaCore/Task/Interface/FightTask.h
@@ -9,6 +9,7 @@ namespace asst
class StageDropsTaskPlugin;
class StageNavigationTask;
class DrGrandetTaskPlugin;
+ class SideStoryReopenTask;
class FightTask final : public InterfaceTask
{
@@ -26,5 +27,6 @@ namespace asst
std::shared_ptr m_fight_task_ptr = nullptr;
std::shared_ptr m_stage_drops_plugin_ptr = nullptr;
std::shared_ptr m_dr_grandet_task_plugin_ptr = nullptr;
+ std::shared_ptr m_sidestory_reopen_task_ptr = nullptr;
};
}
diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs
index 69af67e45b..887079723b 100644
--- a/src/MaaWpfGui/Main/AsstProxy.cs
+++ b/src/MaaWpfGui/Main/AsstProxy.cs
@@ -15,7 +15,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
@@ -1105,6 +1104,13 @@ namespace MaaWpfGui.Main
case "SanityBeforeStage":
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("CurrentSanity") + $" {subTaskDetails["sanity"]} ");
break;
+
+ case "StageQueueUnableToAgent":
+ Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("StageQueue") + $"{subTaskDetails["stage_code"]}" + LocalizationHelper.GetString("UnableToAgent"), UiLogColor.Info);
+ break;
+ case "StageQueueMissionCompleted":
+ Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("StageQueue") + $"{subTaskDetails["stage_code"]} {subTaskDetails["stars"]}★", UiLogColor.Info);
+ break;
}
}
diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml
index 5bcb8140b4..9f4722bf90 100644
--- a/src/MaaWpfGui/Res/Localizations/en-us.xaml
+++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml
@@ -504,6 +504,8 @@ The video aspect ratio needs to be 16:9 without interference factors such as bla
Drops recognition error
Abort upload to Penguin Statistics
No bonus stage, stopped
+ Stage Queue:
+ Unable to use PRTS
Current Sanity:
Mission started
times
diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml
index 1bbb1e5b26..0850c69c77 100644
--- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml
+++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml
@@ -495,6 +495,8 @@
ドロップ認識エラー
Penguin-Statsへのアップロードをあきらめました
ステージ報酬がないため停止します
+ レベルキュー:
+ 自動指揮利用不可
現在理性:
行動開始しました
回
diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml
index 0c709aae35..b31a0c6a6e 100644
--- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml
+++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml
@@ -508,6 +508,8 @@ OF-1을 해금하지 않았다면 선택하지 말아 주세요.
드롭 인식 오류
펭귄 물류에 업로드 불가
EX 스테이지가 없어 중단되었습니다
+ 레벨 대기열:
+ 프록시 명령을 사용할 수 없습니다
현재 이성:
행동 개시
회
diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml
index 787022559e..b1c4c90347 100644
--- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml
+++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml
@@ -508,6 +508,8 @@
掉落识别错误
放弃上传企鹅物流
无奖励关卡,已停止
+ 关卡队列:
+ 无法使用代理指挥
当前理智:
已开始行动
次
diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml
index 6dc9e96064..efcc170fb1 100644
--- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml
+++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml
@@ -507,6 +507,8 @@
掉落辨識錯誤
放棄上傳企鵝物流
無獎勵關卡,已停止
+ 關卡隊列:
+ 無法使用代理指揮
當前理智:
已開始行動
次