diff --git a/resource/global/YoStarEN/resource/tasks.json b/resource/global/YoStarEN/resource/tasks.json
index d768ea164e..9b8892a94f 100644
--- a/resource/global/YoStarEN/resource/tasks.json
+++ b/resource/global/YoStarEN/resource/tasks.json
@@ -2204,7 +2204,7 @@
208
]
},
- "ExpiringMedicineCheck": {
+ "MedicineExpiringTime": {
"text": [
"min",
"hr",
diff --git a/resource/global/YoStarJP/resource/tasks.json b/resource/global/YoStarJP/resource/tasks.json
index 0f12bfbdc5..ef7b8cd5d4 100644
--- a/resource/global/YoStarJP/resource/tasks.json
+++ b/resource/global/YoStarJP/resource/tasks.json
@@ -2332,7 +2332,7 @@
"残り配置可能数"
]
},
- "ExpiringMedicineCheck": {
+ "MedicineExpiringTime": {
"text": [
"分",
"時間",
@@ -5777,4 +5777,4 @@
"Stop"
]
}
-}
\ No newline at end of file
+}
diff --git a/resource/global/YoStarKR/resource/tasks.json b/resource/global/YoStarKR/resource/tasks.json
index 35a87ae6d3..d233527261 100644
--- a/resource/global/YoStarKR/resource/tasks.json
+++ b/resource/global/YoStarKR/resource/tasks.json
@@ -2297,7 +2297,7 @@
"인원"
]
},
- "ExpiringMedicineCheck": {
+ "MedicineExpiringTime": {
"text": [
"1일",
"시간",
@@ -5892,4 +5892,4 @@
]
]
}
-}
\ No newline at end of file
+}
diff --git a/resource/global/txwy/resource/tasks.json b/resource/global/txwy/resource/tasks.json
index d638d80671..1bee21c3e5 100644
--- a/resource/global/txwy/resource/tasks.json
+++ b/resource/global/txwy/resource/tasks.json
@@ -1441,7 +1441,7 @@
"行動結束"
]
},
- "ExpiringMedicineCheck": {
+ "MedicineExpiringTime": {
"text": [
"分鐘",
"小時",
@@ -4615,4 +4615,4 @@
]
]
}
-}
\ No newline at end of file
+}
diff --git a/resource/tasks.json b/resource/tasks.json
index 8b7b096bba..149616d1f2 100644
--- a/resource/tasks.json
+++ b/resource/tasks.json
@@ -4109,51 +4109,205 @@
400,
100
],
+ "reduceOtherTimes": [
+ "StartButton1"
+ ],
"next": [
- "MedicineConfirm"
+ "StartButton1",
+ "OfflineConfirm",
+ "CloseStonePage"
]
},
"MedicineConfirm": {
"action": "ClickSelf",
- "maxTimes": 0,
"roi": [
1000,
500,
200,
150
],
- "reduceOtherTimes": [
- "StartButton1"
- ],
- "exceededNext": [
- "ExpiringMedicineCheck",
- "CloseStonePage"
- ],
"next": [
- "StartButton1",
- "OfflineConfirm",
- "MedicineConfirmAgain@LoadingText"
+ "#self",
+ "Stop"
]
},
- "MedicineConfirmAgain": {
- "Doc": "Refer to Task `StoneConfirmAgain`",
- "baseTask": "MedicineConfirm",
- "template": "MedicineConfirm.png",
- "maxTimes": 2147483647,
- "reduceOtherTimes": []
- },
- "UseStone": {
- "action": "DoNothing",
- "roi": [
- 600,
- 130,
- 420,
- 420
- ],
- "next": [
- "StoneConfirm"
+ "MedicineReduceIcon":{
+ "preDelay": 500,
+ "roi": [
+ 750,
+ 200,
+ 500,
+ 50
+ ],
+ "maskRange": [
+ 1,
+ 255
+ ]
+ },
+ "MedicineUsingCount":{
+ "algorithm": "OcrDetect",
+ "text": [],
+ "isAscii": true,
+ "rectMove": [
+ -90,
+ 28,
+ 100,
+ 60
+ ],
+ "ocrReplace": [
+ [
+ "\\|",
+ "/"
+ ],
+ [
+ "o",
+ "0"
+ ],
+ [
+ "O",
+ "0"
+ ],
+ [
+ "I",
+ "1"
+ ],
+ [
+ "l",
+ "1"
+ ]
]
- },
+ },
+ "MedicineInventory":{
+ "algorithm": "OcrDetect",
+ "text": [],
+ "isAscii": true,
+ "rectMove": [
+ -29,
+ 87,
+ 40,
+ 33
+ ],
+ "ocrReplace": [
+ [
+ "\\|",
+ "/"
+ ],
+ [
+ "o",
+ "0"
+ ],
+ [
+ "O",
+ "0"
+ ],
+ [
+ "I",
+ "1"
+ ],
+ [
+ "l",
+ "1"
+ ]
+ ]
+ },
+ "MedicineExpiringTime":{
+ "algorithm": "OcrDetect",
+ "text": [
+ "分钟",
+ "小时",
+ "1天"
+ ],
+ "rectMove": [
+ -60,
+ 128,
+ 75,
+ 30
+ ],
+ "ocrReplace": [
+ [
+ ".+小时",
+ "小时"
+ ],
+ [
+ ".+分钟",
+ "分钟"
+ ],
+ [
+ "^\\d+小$",
+ "小时"
+ ],
+ [
+ "^\\d+分$",
+ "分钟"
+ ]
+ ]
+ },
+ "UsingMedicine-Target":{
+ "algorithm": "OcrDetect",
+ "roi": [
+ 255,
+ 390,
+ 315,
+ 140
+ ],
+ "text": [],
+ "isAscii": true,
+ "ocrReplace": [
+ [
+ "\\|",
+ "/"
+ ],
+ [
+ "o",
+ "0"
+ ],
+ [
+ "O",
+ "0"
+ ],
+ [
+ "I",
+ "1"
+ ],
+ [
+ "l",
+ "1"
+ ]
+ ]
+ },
+ "UsingMedicine-SanityMax":{
+ "algorithm": "OcrDetect",
+ "roi": [
+ 440,
+ 330,
+ 150,
+ 55
+ ],
+ "text": [],
+ "isAscii": true,
+ "ocrReplace": [
+ [
+ "\\|",
+ "/"
+ ],
+ [
+ "o",
+ "0"
+ ],
+ [
+ "O",
+ "0"
+ ],
+ [
+ "I",
+ "1"
+ ],
+ [
+ "l",
+ "1"
+ ]
+ ]
+ },
"DrGrandetUseOriginiums": {
"algorithm": "OcrDetect",
"text": [],
@@ -4227,6 +4381,18 @@
"ExpiringMedicineConfirm@LoadingText"
]
},
+ "UseStone": {
+ "action": "DoNothing",
+ "roi": [
+ 600,
+ 130,
+ 420,
+ 420
+ ],
+ "next": [
+ "StoneConfirm"
+ ]
+ },
"StoneConfirm": {
"template": "MedicineConfirm.png",
"action": "ClickSelf",
diff --git a/resource/template/MedicineReduceIcon.png b/resource/template/MedicineReduceIcon.png
new file mode 100644
index 0000000000..66844cae89
Binary files /dev/null and b/resource/template/MedicineReduceIcon.png differ
diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj
index 6650b2f15f..9ab909a0d6 100644
--- a/src/MaaCore/MaaCore.vcxproj
+++ b/src/MaaCore/MaaCore.vcxproj
@@ -67,6 +67,7 @@
+
@@ -236,6 +237,7 @@
+
diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters
index 109baf578b..d1c257a0bb 100644
--- a/src/MaaCore/MaaCore.vcxproj.filters
+++ b/src/MaaCore/MaaCore.vcxproj.filters
@@ -657,6 +657,9 @@
Source\Task\Roguelike
+
+ Source\Task\Fight
+
Source\Task\Miscellaneous
@@ -1094,5 +1097,8 @@
Source\Task\Miscellaneous
+
+ Source\Task\Fight
+
\ No newline at end of file
diff --git a/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.cpp b/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.cpp
index 8f08bdfc61..9d14c6b47b 100644
--- a/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.cpp
+++ b/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.cpp
@@ -14,7 +14,7 @@ bool asst::DrGrandetTaskPlugin::verify(AsstMsg msg, const json::value& details)
}
const std::string task = details.at("details").at("task").as_string();
- if (task == "Fight@StoneConfirm" || task == "Fight@MedicineConfirm") {
+ if (task == "Fight@StoneConfirm") {
return true;
}
else {
@@ -26,7 +26,7 @@ bool asst::DrGrandetTaskPlugin::_run()
{
LogTraceFunction;
- int cur_ms = analyze_time_left();
+ int cur_ms = analyze_time_left(ctrler()->get_image());
if (cur_ms < 0) {
return false;
}
@@ -45,11 +45,11 @@ bool asst::DrGrandetTaskPlugin::_run()
return false;
}
-int asst::DrGrandetTaskPlugin::analyze_time_left()
+int asst::DrGrandetTaskPlugin::analyze_time_left(const cv::Mat& image)
{
LogTraceFunction;
- OCRer analyzer(ctrler()->get_image());
+ OCRer analyzer(image);
analyzer.set_task_info("DrGrandetUseOriginiums");
analyzer.set_replace(Task.get("NumberOcrReplace")->replace_map);
// 这里是汉字和数字混合的,用不了单独的en模型
diff --git a/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.h b/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.h
index 3ae661bed7..12ed948020 100644
--- a/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.h
+++ b/src/MaaCore/Task/Fight/DrGrandetTaskPlugin.h
@@ -14,11 +14,11 @@ namespace asst
virtual bool verify(AsstMsg msg, const json::value& details) const override;
- private:
- virtual bool _run() override;
-
// 识别“理智将在x:xx后恢复”,返回 x:xx 对应的 毫秒 数
// 若识别失败返回 < 0
- int analyze_time_left();
+ static int analyze_time_left(const cv::Mat& img);
+
+ private:
+ virtual bool _run() override;
};
}
diff --git a/src/MaaCore/Task/Fight/MedicineCounterPlugin.cpp b/src/MaaCore/Task/Fight/MedicineCounterPlugin.cpp
new file mode 100644
index 0000000000..39725d4920
--- /dev/null
+++ b/src/MaaCore/Task/Fight/MedicineCounterPlugin.cpp
@@ -0,0 +1,208 @@
+#include "MedicineCounterPlugin.h"
+
+#include
+
+#include "Config/TaskData.h"
+#include "Controller/Controller.h"
+#include "InstHelper.h"
+#include "Task/Fight/DrGrandetTaskPlugin.h"
+#include "Task/ProcessTask.h"
+#include "Utils/Logger.hpp"
+#include "Utils/NoWarningCV.h"
+#include "Vision/MultiMatcher.h"
+#include "Vision/RegionOCRer.h"
+
+bool asst::MedicineCounterPlugin::verify(AsstMsg msg, const json::value& details) const
+{
+ if (msg != AsstMsg::SubTaskStart || details.get("subtask", std::string()) != "ProcessTask") {
+ return false;
+ }
+
+ return details.at("details").at("task").as_string().ends_with("UseMedicine");
+}
+
+bool asst::MedicineCounterPlugin::_run()
+{
+ LogTraceFunction;
+
+ if (m_using_count >= m_max_count && !m_use_expiring) {
+ return true;
+ }
+
+ // 现在葛朗台插件在前,暂时不需要延迟
+ // sleep(Task.get("MedicineReduceIcon")->pre_delay);
+ auto image = ctrler()->get_image();
+
+ // 预计最大使用药品情况不会超过2种,不考虑滑动
+ // 10+10+N
+ auto initialCount = initial_count(image);
+ if (!initialCount) [[unlikely]] {
+ return false;
+ }
+
+ if (m_using_count < m_max_count) {
+ if (initialCount->using_count + m_using_count > m_max_count) {
+ reduce_excess(*initialCount);
+ }
+
+ image = ctrler()->get_image();
+ auto count = initial_count(image);
+ if (!count || count->using_count + m_using_count > m_max_count) [[unlikely]] {
+ Log.error(__FUNCTION__, "use medicine over limit:", count ? count->using_count : -1);
+ return false;
+ }
+ initialCount = count;
+ }
+ else if (m_use_expiring) {
+ for (auto& [use, inventory, rect, is_expiring] : initialCount->medicines | std::ranges::views::reverse) {
+ while (use > 0 && !is_expiring) {
+ ctrler()->click(rect);
+ sleep(Config.get_options().task_delay);
+ use--;
+ }
+ }
+
+ image = ctrler()->get_image();
+ auto count = initial_count(image);
+ if (!count || count->using_count + m_using_count > m_max_count) [[unlikely]] {
+ Log.error(__FUNCTION__, "use medicine over limit:", count ? count->using_count : -1);
+ return false;
+ }
+ initialCount = count;
+ }
+ m_using_count += initialCount->using_count;
+
+ // 博朗台:如果溢出则等待
+ if (m_dr_grandet) {
+ auto sanity_target = get_target_of_sanity(image);
+ auto sanity_max = get_maximun_of_sanity(image);
+ if (!sanity_target || !sanity_max) [[unlikely]] {
+ return false;
+ }
+
+ if (*sanity_target >= *sanity_max) [[unlikely]] {
+ auto waitTime = DrGrandetTaskPlugin::analyze_time_left(image);
+ sleep(waitTime);
+ }
+ }
+
+ ProcessTask(*this, { "MedicineConfirm" }).run();
+
+ auto info = basic_info_with_what("UseMedicine");
+ info["detail"]["is_expiring"] = m_using_count > m_max_count;
+ info["detail"]["count"] = initialCount->using_count;
+ callback(AsstMsg::SubTaskExtraInfo, info);
+ return true;
+}
+
+std::optional asst::MedicineCounterPlugin::initial_count(
+ cv::Mat image)
+{
+ int use = 0;
+ MultiMatcher multiMatcher(image);
+ multiMatcher.set_task_info("MedicineReduceIcon");
+ if (!multiMatcher.analyze()) {
+ Log.error(__FUNCTION__, "medicine reduce icon analyze failed");
+ return std::nullopt;
+ }
+
+ std::vector medicines;
+ for (const auto& result : multiMatcher.get_result()) {
+ auto using_rect = result.rect.move(Task.get("MedicineUsingCount")->rect_move);
+ auto inventory_rect = result.rect.move(Task.get("MedicineInventory")->rect_move);
+ auto expiring_rect = result.rect.move(Task.get("MedicineExpiringTime")->rect_move);
+ cv::rectangle(image, make_rect(using_rect), cv::Scalar(0, 0, 255), 1);
+ cv::rectangle(image, make_rect(inventory_rect), cv::Scalar(0, 0, 255), 1);
+ cv::rectangle(image, make_rect(expiring_rect), cv::Scalar(0, 0, 255), 1);
+
+ RegionOCRer using_ocr(image);
+ using_ocr.set_task_info("MedicineUsingCount");
+ using_ocr.set_bin_threshold(100, 255);
+ using_ocr.set_roi(using_rect);
+ if (!using_ocr.analyze()) {
+ Log.error(__FUNCTION__, "medicine using count analyze failed");
+ return std::nullopt;
+ }
+
+ RegionOCRer inventry_ocr(image);
+ inventry_ocr.set_task_info("MedicineUsingCount");
+ inventry_ocr.set_bin_threshold(100, 255);
+ inventry_ocr.set_roi(inventory_rect);
+ if (!inventry_ocr.analyze()) {
+ Log.error(__FUNCTION__, "medicine using count analyze failed");
+ return std::nullopt;
+ }
+
+ RegionOCRer expiring_ocr(image);
+ expiring_ocr.set_task_info("MedicineExpiringTime");
+ expiring_ocr.set_bin_threshold(100, 255);
+ expiring_ocr.set_roi(expiring_rect);
+ if (!expiring_ocr.analyze()) {
+ Log.error(__FUNCTION__, "medicine expiring time analyze failed");
+ return std::nullopt;
+ }
+ bool is_expiring =
+ ranges::any_of(Task.get("MedicineExpiringTime")->text,
+ [&](const std::string_view& sv) { return sv == expiring_ocr.get_result().text; });
+
+ int using_count = 0, inventory_count = 0;
+ if (!utils::chars_to_number(using_ocr.get_result().text, using_count) ||
+ !utils::chars_to_number(inventry_ocr.get_result().text, inventory_count)) {
+ return std::nullopt;
+ }
+ use += using_count;
+ medicines.emplace_back(Medicine { .use = using_count,
+ .inventry = inventory_count,
+ .reduce_button_position = result.rect,
+ .is_expiring = is_expiring });
+ }
+ return InitialMedicineResult { .using_count = use, .medicines = medicines };
+}
+
+void asst::MedicineCounterPlugin::reduce_excess(InitialMedicineResult& using_medicine)
+{
+ auto reduce = m_using_count + using_medicine.using_count - m_max_count;
+ for (auto& [use, inventory, rect, is_expiring] : using_medicine.medicines | std::ranges::views::reverse) {
+ while (use > 0 && reduce > 0) {
+ ctrler()->click(rect);
+ sleep(Config.get_options().task_delay);
+ use--;
+ reduce--;
+ using_medicine.using_count--;
+ }
+ }
+}
+
+std::optional asst::MedicineCounterPlugin::get_target_of_sanity(const cv::Mat& image)
+{
+ RegionOCRer ocr(image);
+ ocr.set_bin_threshold(100, 255);
+ ocr.set_task_info("UsingMedicine-Target");
+ if (!ocr.analyze()) [[unlikely]] {
+ Log.error(__FUNCTION__, "unable to ocr");
+ return std::nullopt;
+ }
+ int num = 0;
+ if (!utils::chars_to_number(ocr.get_result().text, num)) {
+ Log.error(__FUNCTION__, "unable to change [", ocr.get_result().text, "] into int");
+ return false;
+ }
+ return num;
+}
+
+std::optional asst::MedicineCounterPlugin::get_maximun_of_sanity(const cv::Mat& image)
+{
+ RegionOCRer ocr(image);
+ ocr.set_bin_threshold(100, 255);
+ ocr.set_task_info("UsingMedicine-SanityMax");
+ if (!ocr.analyze()) [[unlikely]] {
+ Log.error(__FUNCTION__, "unable to ocr");
+ return std::nullopt;
+ }
+ int num = 0;
+ if (!utils::chars_to_number(ocr.get_result().text, num)) {
+ Log.error(__FUNCTION__, "unable to change [", ocr.get_result().text, "] into int");
+ return false;
+ }
+ return num;
+}
diff --git a/src/MaaCore/Task/Fight/MedicineCounterPlugin.h b/src/MaaCore/Task/Fight/MedicineCounterPlugin.h
new file mode 100644
index 0000000000..a0741a6d7a
--- /dev/null
+++ b/src/MaaCore/Task/Fight/MedicineCounterPlugin.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "Task/AbstractTaskPlugin.h"
+
+#include "Common/AsstTypes.h"
+
+namespace asst
+{
+ class MedicineCounterPlugin final : public AbstractTaskPlugin
+ {
+ public:
+
+ using AbstractTaskPlugin::AbstractTaskPlugin;
+ virtual ~MedicineCounterPlugin() override = default;
+ virtual bool verify(AsstMsg msg, const json::value& details) const override;
+ void set_dr_grandet(bool dr_grandet) { m_dr_grandet = dr_grandet; };
+ void set_count(int count) { m_max_count = count; }
+ void set_use_expiring(bool use_expiring) { m_use_expiring = use_expiring; }
+
+ private:
+ virtual bool _run() override;
+
+ struct Medicine
+ {
+ int use;
+ int inventry;
+ asst::Rect reduce_button_position;
+ bool is_expiring;
+ };
+
+ // 库存量, 移除按钮的位置
+ struct InitialMedicineResult
+ {
+ int using_count;
+ std::vector medicines;
+ };
+
+ // 识别初始使用的药量
+ std::optional initial_count(cv::Mat image);
+ // 减少药品使用
+ void reduce_excess(InitialMedicineResult& using_medicine);
+ std::optional get_target_of_sanity(const cv::Mat& image);
+ std::optional get_maximun_of_sanity(const cv::Mat& image);
+
+ bool m_use_expiring,m_dr_grandet = false;
+ int m_using_count = 0, m_max_count = 0;
+ };
+}
diff --git a/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.h b/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.h
index 03e75d9953..a6c8a24260 100644
--- a/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.h
+++ b/src/MaaCore/Task/Fight/SanityBeforeStagePlugin.h
@@ -4,7 +4,6 @@
#include
namespace asst
{
-
class SanityBeforeStagePlugin final : public AbstractTaskPlugin
{
public:
diff --git a/src/MaaCore/Task/Interface/FightTask.cpp b/src/MaaCore/Task/Interface/FightTask.cpp
index 01537e6754..fd0c50e229 100644
--- a/src/MaaCore/Task/Interface/FightTask.cpp
+++ b/src/MaaCore/Task/Interface/FightTask.cpp
@@ -8,6 +8,7 @@
#include "Task/Fight/SideStoryReopenTask.h"
#include "Task/Fight/StageDropsTaskPlugin.h"
#include "Task/Fight/StageNavigationTask.h"
+#include "Task/Fight/MedicineCounterPlugin.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
#include "Utils/Ranges.hpp"
@@ -26,7 +27,6 @@ asst::FightTask::FightTask(const AsstCallback& callback, Assistant* inst)
// 对于当前/上次,就是点到 蓝色开始行动 为止。
m_start_up_task_ptr->set_times_limit("StartButton1", 0)
.set_times_limit("StartButton2", 0)
- .set_times_limit("MedicineConfirm", 0)
.set_times_limit("StoneConfirm", 0)
.set_times_limit("StageSNReturnFlag", 0)
.set_times_limit("PRTS1", 0)
@@ -50,6 +50,7 @@ asst::FightTask::FightTask(const AsstCallback& callback, Assistant* inst)
m_dr_grandet_task_plugin_ptr = m_fight_task_ptr->register_plugin();
m_dr_grandet_task_plugin_ptr->set_enable(false);
m_fight_task_ptr->register_plugin();
+ m_medicine_plugin = m_fight_task_ptr->register_plugin();
m_subtasks.emplace_back(m_start_up_task_ptr);
m_subtasks.emplace_back(m_stage_navigation_task_ptr);
@@ -114,6 +115,9 @@ bool asst::FightTask::set_params(const json::value& params)
.set_times_limit("StoneConfirm", stone)
.set_times_limit("StartButton1", times)
.set_times_limit("StartButton2", times);
+ m_medicine_plugin->set_count(medicine);
+ m_medicine_plugin->set_use_expiring(expiring_medicine != 0);
+ m_medicine_plugin->set_dr_grandet(is_dr_grandet);
m_dr_grandet_task_plugin_ptr->set_enable(is_dr_grandet);
m_stage_drops_plugin_ptr->set_enable_penguid(enable_penguid);
m_stage_drops_plugin_ptr->set_penguin_id(penguin_id);
diff --git a/src/MaaCore/Task/Interface/FightTask.h b/src/MaaCore/Task/Interface/FightTask.h
index 80f2727bc1..d3f11c1fbc 100644
--- a/src/MaaCore/Task/Interface/FightTask.h
+++ b/src/MaaCore/Task/Interface/FightTask.h
@@ -10,6 +10,7 @@ namespace asst
class StageNavigationTask;
class DrGrandetTaskPlugin;
class SideStoryReopenTask;
+ class MedicineCounterPlugin;
class FightTask final : public InterfaceTask
{
@@ -25,6 +26,7 @@ namespace asst
std::shared_ptr m_start_up_task_ptr = nullptr;
std::shared_ptr m_stage_navigation_task_ptr = nullptr;
std::shared_ptr m_fight_task_ptr = nullptr;
+ std::shared_ptr m_medicine_plugin = 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 f979086c85..27f95eab1f 100644
--- a/src/MaaWpfGui/Main/AsstProxy.cs
+++ b/src/MaaWpfGui/Main/AsstProxy.cs
@@ -816,16 +816,6 @@ namespace MaaWpfGui.Main
Instances.TaskQueueViewModel.AddLog(log.TrimEnd('\n').TrimEnd(' '), UiLogColor.Info);
break;
- case "MedicineConfirm":
- Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("MedicineUsed") + $" {execTimes} " + LocalizationHelper.GetString("UnitTime"), UiLogColor.Info);
- MedicineUsedTimes++;
- break;
-
- case "ExpiringMedicineConfirm":
- Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("ExpiringMedicineUsed") + $" {execTimes} " + LocalizationHelper.GetString("UnitTime"), UiLogColor.Info);
- ExpiringMedicineUsedTimes++;
- break;
-
case "StoneConfirm":
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("StoneUsed") + $" {execTimes} " + LocalizationHelper.GetString("UnitTime"), UiLogColor.Info);
StoneUsedTimes++;
@@ -1265,8 +1255,8 @@ namespace MaaWpfGui.Main
break;
}
- var sanityCur = sanityReport.TryGetValue("current_sanity", out var sanityCurToken) ? (int)sanityCurToken : -1;
- var sanityMax = sanityReport.TryGetValue("max_sanity", out var sanityMaxToken) ? (int)sanityMaxToken : -1;
+ int sanityCur = sanityReport.TryGetValue("current_sanity", out var sanityCurToken) ? (int)sanityCurToken : -1;
+ int sanityMax = sanityReport.TryGetValue("max_sanity", out var sanityMaxToken) ? (int)sanityMaxToken : -1;
var reportTime = sanityReport.TryGetValue("report_time", out var reportTimeToken) ? (string)reportTimeToken : string.Empty;
if (sanityCur < 0 || sanityMax < 1 || reportTime?.Length < 12)
{
@@ -1288,6 +1278,38 @@ namespace MaaWpfGui.Main
SanityReport.HasSanityReport = true;
break;
+ case "UseMedicine":
+ var medicineReport = (JObject)subTaskDetails;
+ if (medicineReport is null || !medicineReport.ContainsKey("is_expiring") || !medicineReport.ContainsKey("count"))
+ {
+ // todo addlog
+ break;
+ }
+
+ bool isExpiringMedicine = medicineReport.TryGetValue("is_expiring", out var isExpiringMedicineToken) ? (bool)isExpiringMedicineToken : false;
+ int medicineCount = medicineReport.TryGetValue("count", out var medicineCountToken) ? (int)medicineCountToken : -1;
+
+ if (medicineCount == -1)
+ {
+ Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("MedicineUsed") + $" Unknown times", UiLogColor.Error);
+ break;
+ }
+
+ var medicineLog = string.Empty;
+ if (!isExpiringMedicine)
+ {
+ MedicineUsedTimes += medicineCount;
+ medicineLog = LocalizationHelper.GetString("MedicineUsed") + $" {MedicineUsedTimes}(+{medicineCount})";
+ }
+ else
+ {
+ ExpiringMedicineUsedTimes += medicineCount;
+ medicineLog = LocalizationHelper.GetString("ExpiringMedicineUsed") + $" {ExpiringMedicineUsedTimes}(+{medicineCount})";
+ }
+
+ Instances.TaskQueueViewModel.AddLog(medicineLog, UiLogColor.Info);
+ break;
+
case "StageQueueUnableToAgent":
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("StageQueue") + $" {subTaskDetails["stage_code"]} " + LocalizationHelper.GetString("UnableToAgent"), UiLogColor.Info);
break;