From 1e4eb67470e32044e70f53364aef016789c60b18 Mon Sep 17 00:00:00 2001 From: status102 Date: Tue, 5 Sep 2023 20:36:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=A4=8D=E5=88=BB?= =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E6=97=B6=EF=BC=8C=E8=BF=9E=E7=BB=AD=E5=88=B7?= =?UTF-8?q?=E6=99=AE=E9=80=9A1-9=E5=85=B3=20(=E5=BC=82=E5=BD=A2UI=E6=9A=82?= =?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BE=8B=E5=A6=82=E6=84=9A?= =?UTF-8?q?=E4=BA=BA=E5=8F=B7)=EF=BC=8C=E6=94=AF=E6=8C=81=E4=B8=8A?= =?UTF-8?q?=E6=8A=A5=E8=87=B3=E4=BC=81=E9=B9=85=E7=89=A9=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/tasks.json | 78 ++++++ resource/template/UsePrtsSuccess.png | Bin 0 -> 2352 bytes src/MaaCore/MaaCore.vcxproj | 4 + src/MaaCore/MaaCore.vcxproj.filters | 12 + .../Task/Fight/SanityBeforeStagePlugin.cpp | 2 +- .../Task/Fight/SideStoryReopenTask.cpp | 201 +++++++++++++++ src/MaaCore/Task/Fight/SideStoryReopenTask.h | 48 ++++ src/MaaCore/Task/Fight/StageNavigationTask.h | 1 + .../StageQueueMissionCompletedPlugin.cpp | 233 ++++++++++++++++++ .../Fight/StageQueueMissionCompletedPlugin.h | 45 ++++ src/MaaCore/Task/Interface/FightTask.cpp | 33 ++- src/MaaCore/Task/Interface/FightTask.h | 2 + src/MaaWpfGui/Main/AsstProxy.cs | 8 +- src/MaaWpfGui/Res/Localizations/en-us.xaml | 2 + src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 2 + src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 2 + src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 2 + src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 2 + 18 files changed, 670 insertions(+), 7 deletions(-) create mode 100644 resource/template/UsePrtsSuccess.png create mode 100644 src/MaaCore/Task/Fight/SideStoryReopenTask.cpp create mode 100644 src/MaaCore/Task/Fight/SideStoryReopenTask.h create mode 100644 src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.cpp create mode 100644 src/MaaCore/Task/Fight/StageQueueMissionCompletedPlugin.h 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 0000000000000000000000000000000000000000..2affaf292987941459a55f0fdea7d7da10ae7a49 GIT binary patch literal 2352 zcmV-03D5S4P)pFA#_DpbVG7wVRUJ4ZXi@?ZDjy5FfubbFgZFfG9WQ9Ix;dkG&Ww_zvch{2#HBV zK~!i%#hJ^F6vq;VySlpP!ogl3_BMC{bHvtu03?LGzze)&gLn&g6EFNYmTV!P!-kF7 zgJj0yA~VdTyUSnve^ypjb@yBtM*gYJij0hm{4+ALDr=~|`R4jan2oAMH5xZf-858H z)T7a4G+I>EtWiCqswzq=2TcXShIW<7tg6N#OPQM0o*cHufmRCmrN*P!xBGFiltLG$Lwkr5hs@?v#~bRT@}sHS(Fd#3gtf-{Q)evXDi06<+07 zaM%k)AVMG|eKI3?P1D z)ZWF>7nOQE=9<(A$yYnyR9ixI{kpabJ0c=f#de`eZU^;xR86&Aj>01B!k=#x2xH{=V!komke+TG*Gz?h$wJGZ)Qh@j-puEoMrN5F z-3eA-yLN4Vf1hc=W-_TKlS!;%8$54DXicZFHAz!zHa9n87i&Aam*H06sya^b<|_Pv z+S&H8L~;VFG#sRQ6qymll`B_V8^GRErKoCvdc*Z4Y;SLyP1tONmAlA?ojCSu(WeOd zd9V!ei6|m7U4mxX(g(`OK|)Q z+XF0l=&*0`bLrA0ToFXgFqYm9SEBm*>#v|iP<5oOu>Jq<|NHINU*Erf&$E4Fx(UTv z&qW-CU&ml$k1PW=^h?_27ZT!u?S|ySxmaMh}{vFZZ7-i?Jx85GMy7x}9YyPN0~9cbs|{W^Co(p9DG*LP|1H-omZ?twy!8vtud!@oqHw?U!Gk zK7IP;%^MD{M3D~xT6}h*^sA4LDN0T&EC=UP%aqH93`YwRSbq5M!Aj)4y^qqf;+Vvb z1v_FJRTYWlZAt>ba2Uld1LAZTf3Wft!;Lc1RX&dP_xJbq_J{<+{{8_n29kTg9%eRk zcK|<<5Y0Ri9?vn2*-C66Yb&QHeLt3yKJ*NsB6$egpVITO6~zAO|XHbyP%|O z9}eB$%GRNa;;N02V5c6y^Fs#2G9q_yz*_+lE29WAKo?4JW-+6X!|p+=a^>diC`K%0 zaa=^9Mvx+Y1PtW>L8^_VSw;tS!y$DA$Jc7C+tHPyMWz6fBU}pBBcNF{ zO$1UcwxkM7%dpbj?U1g~L5SB^PIubkhYg0suJ8>gB~MPuAgG1LL2N)=D<~$kj2v2Q z9L4D3oRJ4>jzOmAP&b@$-oAa?G669o%`PSg>=7j!kSdAj_!27F2ZHhv%u;16acsHR z!s%}fYZI&riXtc13U2fK`SYt+ud1C|(6Na%K-ozAv=U9cZ2_Y70|b4+HioyzDN!dj zOkFg{!PkHO`QX6=UIV+kf7$!EuE>zckp6Xc>5?76xyWBylC%=^T4{l= zXlmsF&}WL(tV;iT6)fxVojZ5#-u?c&@4l7g$vAMKO-Vlys4&9->bnXc0nY(WOYu(X(A2d(jTix7E(hfAysj z6dlDnJOtp-G#m?1r(G=XK~b$~T|SLsz*^nB2#DE5rWju1JHKs0Ef=mO^ff_AX6Hh3 z1{irwl33e4x<$0ZGEsNw`wn=zpNiGwN^}%^qZe2rdNGJhSsl!h;tHIW-^ER1RQ&^g WO7sndW;o*j0000QCGJB literal 0 HcmV?d00001 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 @@ 掉落辨識錯誤 放棄上傳企鵝物流 無獎勵關卡,已停止 + 關卡隊列: + 無法使用代理指揮 當前理智: 已開始行動