From e3899c720803c63b90a5d7edde4eceb7add6a28a Mon Sep 17 00:00:00 2001 From: MistEO Date: Wed, 28 Dec 2022 23:56:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E4=BF=9D=E5=85=A8json=E8=A7=A3=E6=9E=90=E5=8F=8A=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E4=BD=9C=E6=88=98=E5=89=8D=E9=83=A8=E5=88=86=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/copilot/SSS_Test.json | 119 +++++++++++++++ resource/tasks.json | 141 +++++++++++++++++- .../template/BattleQuickFormationFilter.png | Bin 0 -> 850 bytes resource/template/SSSCloseTip.png | Bin 0 -> 3154 bytes src/MaaCore/Assistant.cpp | 2 + src/MaaCore/Common/AsstBattleDef.h | 3 +- .../Config/Miscellaneous/SSSCopilotConfig.cpp | 11 +- .../Config/Miscellaneous/SSSCopilotConfig.h | 2 + src/MaaCore/InstHelper.cpp | 9 ++ src/MaaCore/InstHelper.h | 3 + src/MaaCore/MaaCore.vcxproj | 2 + src/MaaCore/MaaCore.vcxproj.filters | 6 + src/MaaCore/Task/Interface/SSSCopilotTask.cpp | 66 ++++++++ src/MaaCore/Task/Interface/SSSCopilotTask.h | 28 ++++ .../Miscellaneous/BattleFormationTask.cpp | 93 ++++++++++-- .../Task/Miscellaneous/BattleFormationTask.h | 27 ++++ src/MaaWpfGui/Main/AsstProxy.cs | 7 +- src/MaaWpfGui/Main/CopilotViewModel.cs | 16 +- 18 files changed, 514 insertions(+), 21 deletions(-) create mode 100644 resource/copilot/SSS_Test.json create mode 100644 resource/template/BattleQuickFormationFilter.png create mode 100644 resource/template/SSSCloseTip.png create mode 100644 src/MaaCore/Task/Interface/SSSCopilotTask.cpp create mode 100644 src/MaaCore/Task/Interface/SSSCopilotTask.h diff --git a/resource/copilot/SSS_Test.json b/resource/copilot/SSS_Test.json new file mode 100644 index 0000000000..32a450ea3d --- /dev/null +++ b/resource/copilot/SSS_Test.json @@ -0,0 +1,119 @@ +{ + "type": "SSS", + "stage_name": "多索雷斯在建地块", + "minimum_required": "v4.9.0", + "doc": { + "title": "保全测试作业", + }, + "buff": "镀膜装置导能阀", + "equipment": [ + "A", + "A", + "A", + "A", + "A", + "A", + "A", + "A" + ], + "strategy": "优选策略", + "opers": [ + { + "name": "泥岩", + "skill": 2 + }, + { + "name": "水月", + "skill": 2, + "skill_usage": 1 + } + ], + "tool_men": { + "Pioneer": 3, + "Warrior": 6, + "Medic": 2 + }, + "drop_buffs": [ + "定向部署支援系统" + ], + "drop_tool_men": [ + "空弦", + "能天使", + "Pioneer", + "Support", + "None" + ], + "stages": [ + { + "stage_name": "蜂拥而上", + "strategies": [ + { + "core": "棘刺", + "tool_men": { + "Pioneer": 1, + "Warrior": 1, + "Medic": 1 + }, + "location": [ + 10, + 1 + ], + "direction": "Left" + }, + { + "core": "泥岩", + "tool_men": { + "Pioneer": 1, + "Warrior": 1, + "Medic": 1 + }, + "location": [ + 2, + 8 + ], + "direction": "Left" + }, + { + "tool_men": { + "Support": 100 + }, + "location": [ + 2, + 8 + ], + "direction": "Left" + } + ], + "draw_as_possible": true, + "actions": [ + { + "type": "调配干员" + }, + { + "type": "CheckIfStartOver", + "name": "棘刺" + }, + { + "type": "CheckIfStartOver", + "tool_men": { + "Pioneer": 2 + } + }, + { + "name": "桃金娘", + "location": [ + 4, + 5 + ], + "direction": "左" + }, + { + "kills": 10, + "type": "撤退", + "name": "桃金娘" + } + ], + "retry_times": 3 + } + ] +} \ No newline at end of file diff --git a/resource/tasks.json b/resource/tasks.json index 6051f28931..f2597ebc12 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -5737,6 +5737,41 @@ ], "postDelay": 500 }, + "BattleQuickFormationFilter": { + "action": "ClickSelf", + "roi": [ + 1045, + 0, + 134, + 123 + ], + "postDelay": 500 + }, + "BattleQuickFormationFilter-Cost": { + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "部署费用" + ], + "roi": [ + 989, + 433, + 196, + 142 + ], + "postDelay": 500 + }, + "BattleQuickFormationFilterClose": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 37, + 104, + 291, + 185 + ], + "postDelay": 500 + }, "BattleQuickFormationRole-All": { "baseTask": "BattleQuickFormationRole", "roi": [ @@ -6084,7 +6119,7 @@ "BattleSwipeOper": { "algorithm": "JustReturn", "preDelay": 400, - "postDelay": 150, + "postDelay": 100, "Doc": "pre 是将干员滑动到场上的 duration 系数;post 是设置干员朝向滑动的 duration", "specialParams": [ 400, @@ -9355,5 +9390,109 @@ 172, 47 ] + }, + "SSSBegin": { + "Doc": "保全派驻", + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "开始保全作战" + ], + "roi": [ + 992, + 576, + 288, + 144 + ], + "next": [ + "SSSBuffChoose" + ] + }, + "SSSBuffChoose": { + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "自适应补给元件" + ], + "roi": [ + 0, + 136, + 1280, + 162 + ], + "next": [ + "SSSBuffChooseConfirm" + ] + }, + "SSSBuffChooseConfirm": { + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "确认选择" + ], + "roi": [ + 992, + 576, + 288, + 144 + ], + "next": [ + "SSSEquipmentConfirm" + ] + }, + "SSSEquipmentConfirm": { + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "装备确认" + ], + "roi": [ + 992, + 576, + 288, + 144 + ] + }, + "SSSTeamConfirm": { + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "阵容确认" + ], + "roi": [ + 992, + 576, + 288, + 144 + ], + "next": [ + "SSSStartFighting", + "SSSCloseTip" + ] + }, + "SSSCloseTip": { + "action": "ClickSelf", + "roi": [ + 54, + 40, + 201, + 136 + ], + "next": [ + "SSSStartFighting" + ] + }, + "SSSStartFighting": { + "action": "ClickSelf", + "algorithm": "OcrDetect", + "text": [ + "开始部署" + ], + "roi": [ + 992, + 576, + 288, + 144 + ] } } diff --git a/resource/template/BattleQuickFormationFilter.png b/resource/template/BattleQuickFormationFilter.png new file mode 100644 index 0000000000000000000000000000000000000000..ded1d29648cd44379824fe8a56b6c2258410ed73 GIT binary patch literal 850 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 literal 0 HcmV?d00001 diff --git a/src/MaaCore/Assistant.cpp b/src/MaaCore/Assistant.cpp index 91cd79c506..21d1086b86 100644 --- a/src/MaaCore/Assistant.cpp +++ b/src/MaaCore/Assistant.cpp @@ -17,6 +17,7 @@ #include "Task/Interface/MallTask.h" #include "Task/Interface/RecruitTask.h" #include "Task/Interface/RoguelikeTask.h" +#include "Task/Interface/SSSCopilotTask.h" #include "Task/Interface/StartUpTask.h" #include "Utils/Logger.hpp" #ifdef ASST_DEBUG @@ -141,6 +142,7 @@ asst::Assistant::TaskId asst::Assistant::append_task(const std::string& type, co ASST_ASSISTANT_APPEND_TASK_FROM_STRING_IF_BRANCH(RecruitTask) ASST_ASSISTANT_APPEND_TASK_FROM_STRING_IF_BRANCH(RoguelikeTask) ASST_ASSISTANT_APPEND_TASK_FROM_STRING_IF_BRANCH(CopilotTask) + ASST_ASSISTANT_APPEND_TASK_FROM_STRING_IF_BRANCH(SSSCopilotTask) ASST_ASSISTANT_APPEND_TASK_FROM_STRING_IF_BRANCH(DepotTask) #ifdef ASST_DEBUG ASST_ASSISTANT_APPEND_TASK_FROM_STRING_IF_BRANCH(DebugTask) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index ada19b6cd6..2cf989c31a 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -223,8 +223,7 @@ namespace asst::battle RoleCounts tool_men; std::vector drop_buffs; - std::vector drop_opers; - std::vector drop_tool_men; + std::vector drop_tool_men; std::vector stages_data; }; diff --git a/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.cpp index b5a05eaeef..8e169b9317 100644 --- a/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.cpp @@ -25,7 +25,7 @@ bool asst::SSSCopilotConfig::parse(const json::value& json) m_data.buff = json.get("buff", std::string()); m_data.strategy = json.get("strategy", std::string()); - m_data.tool_men = CopilotConfig::parse_role_counts(json); + m_data.tool_men = CopilotConfig::parse_role_counts(json.at("tool_men")); if (auto equipment_opt = json.find("equipment")) { static const std::unordered_map Equipment { @@ -52,10 +52,13 @@ bool asst::SSSCopilotConfig::parse(const json::value& json) std::string name = man.as_string(); if (auto role = get_role_type(name); role != Role::Unknown) { - m_data.drop_tool_men.emplace_back(std::move(name)); + m_data.drop_tool_men.emplace_back(name); } else if (BattleData.get_rarity(name) != 0) { - m_data.drop_opers.emplace_back(std::move(name)); + m_data.drop_tool_men.emplace_back(name); + } + else if (name == "None") { + m_data.drop_tool_men.emplace_back(name); } else { Log.error("Unknown drop tool man", name); @@ -72,7 +75,7 @@ bool asst::SSSCopilotConfig::parse(const json::value& json) for (const auto& strategy_info : stage.at("strategies").as_array()) { Strategy strategy; - strategy.core = strategy_info.get("core").as_string(); + strategy.core = strategy_info.get("core", std::string()); strategy.location.x = strategy_info.at("location").at(0).as_integer(); strategy.location.y = strategy_info.at("location").at(1).as_integer(); strategy.direction = CopilotConfig::string_to_direction(strategy_info.get("direction", "Right")); diff --git a/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.h b/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.h index 58bc8128a9..017b2d12b9 100644 --- a/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.h +++ b/src/MaaCore/Config/Miscellaneous/SSSCopilotConfig.h @@ -19,4 +19,6 @@ namespace asst battle::sss::CompleteData m_data; }; + + inline static auto& SSSCopilot = SSSCopilotConfig::get_instance(); } diff --git a/src/MaaCore/InstHelper.cpp b/src/MaaCore/InstHelper.cpp index 253dd60012..317a90a5ee 100644 --- a/src/MaaCore/InstHelper.cpp +++ b/src/MaaCore/InstHelper.cpp @@ -1,5 +1,7 @@ #include "InstHelper.h" +#include + #include "Assistant.h" #include "Utils/Logger.hpp" @@ -47,3 +49,10 @@ asst::Assistant* asst::InstHelper::inst() noexcept { return m_inst; } + +std::string asst::InstHelper::inst_string() const +{ + std::stringstream ss; + ss << m_inst; + return ss.str(); +} diff --git a/src/MaaCore/InstHelper.h b/src/MaaCore/InstHelper.h index f75e1c6176..d12361556a 100644 --- a/src/MaaCore/InstHelper.h +++ b/src/MaaCore/InstHelper.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace asst { @@ -21,7 +22,9 @@ namespace asst std::shared_ptr status() const; bool need_exit() const; bool sleep(unsigned millisecond) const; + Assistant* inst() noexcept; + std::string inst_string() const; InstHelper& operator=(const InstHelper&) = default; InstHelper& operator=(InstHelper&&) noexcept = default; diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj index e8b49c2708..53ea06b7f1 100644 --- a/src/MaaCore/MaaCore.vcxproj +++ b/src/MaaCore/MaaCore.vcxproj @@ -32,6 +32,7 @@ + @@ -150,6 +151,7 @@ + diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters index f84851060d..36d98add0c 100644 --- a/src/MaaCore/MaaCore.vcxproj.filters +++ b/src/MaaCore/MaaCore.vcxproj.filters @@ -462,6 +462,9 @@ 源文件\Config\Miscellaneous + + 源文件\Task\Interface + @@ -755,5 +758,8 @@ 源文件\Config\Miscellaneous + + 源文件\Task\Interface + \ No newline at end of file diff --git a/src/MaaCore/Task/Interface/SSSCopilotTask.cpp b/src/MaaCore/Task/Interface/SSSCopilotTask.cpp new file mode 100644 index 0000000000..a6783ed44e --- /dev/null +++ b/src/MaaCore/Task/Interface/SSSCopilotTask.cpp @@ -0,0 +1,66 @@ +#include "SSSCopilotTask.h" + +#include "Config/Miscellaneous/SSSCopilotConfig.h" +#include "Config/TaskData.h" +#include "Task/Miscellaneous/BattleFormationTask.h" +#include "Task/Miscellaneous/BattleProcessTask.h" +#include "Task/ProcessTask.h" +#include "Utils/Logger.hpp" +#include "Utils/Platform.hpp" + +asst::SSSCopilotTask::SSSCopilotTask(const AsstCallback& callback, Assistant* inst) + : InterfaceTask(callback, inst, TaskType), + m_begin_task_ptr(std::make_shared(callback, inst, TaskType)), + m_formation_task_ptr(std::make_shared(callback, inst, TaskType)), + m_battle_task_ptr(std::make_shared(callback, inst, TaskType)) +{ + m_begin_task_ptr->set_tasks({ inst_string() + "@SSSBegin" }).set_ignore_error(false); + m_subtasks.emplace_back(m_begin_task_ptr); + + m_formation_task_ptr->set_data_resource(BattleFormationTask::DataResource::SSSCopilot); + m_subtasks.emplace_back(m_formation_task_ptr); + + auto start_2_tp = std::make_shared(callback, inst, TaskType); + start_2_tp->set_tasks({ inst_string() + "@SSSTeamConfirm" }).set_ignore_error(false); + m_subtasks.emplace_back(start_2_tp); + + m_subtasks.emplace_back(m_battle_task_ptr)->set_retry_times(0); +} + +bool asst::SSSCopilotTask::set_params(const json::value& params) +{ + if (m_running) { + Log.error("SSSCopilotTask not support set_params when running"); + return false; + } + + auto filename_opt = params.find("filename"); + if (!filename_opt) { + Log.error("SSSCopilotTask set_params failed, stage_name or filename not found"); + return false; + } + + if (!SSSCopilot.load(utils::path(*filename_opt))) { + Log.error("SSSCopilotConfig parse failed"); + return false; + } + + if (const auto& buff = SSSCopilot.get_data().buff; !buff.empty()) { + Task.get(inst_string() + "@SSSBuffChoose")->text = { buff }; + } + + bool with_formation = params.get("formation", false); + m_formation_task_ptr->set_enable(with_formation); + + std::string support_unit_name = params.get("support_unit_name", std::string()); + m_formation_task_ptr->set_support_unit_name(std::move(support_unit_name)); + + BattleFormationTask::AdditionalFormation additional_formation { + .filter = BattleFormationTask::Filter::Cost, + .double_click_filter = true, + .role_counts = SSSCopilot.get_data().tool_men, + }; + m_formation_task_ptr->append_additional_formation(std::move(additional_formation)); + + return true; +} diff --git a/src/MaaCore/Task/Interface/SSSCopilotTask.h b/src/MaaCore/Task/Interface/SSSCopilotTask.h new file mode 100644 index 0000000000..14e305b1c8 --- /dev/null +++ b/src/MaaCore/Task/Interface/SSSCopilotTask.h @@ -0,0 +1,28 @@ +#pragma once +#include "Task/InterfaceTask.h" + +#include + +namespace asst +{ + class BattleProcessTask; + class BattleFormationTask; + class ProcessTask; + + // 保全派驻抄作业任务 + class SSSCopilotTask final : public InterfaceTask + { + public: + inline static constexpr std::string_view TaskType = "SSSCopilot"; + + SSSCopilotTask(const AsstCallback& callback, Assistant* inst); + virtual ~SSSCopilotTask() override = default; + + virtual bool set_params(const json::value& params) override; + + private: + std::shared_ptr m_begin_task_ptr = nullptr; + std::shared_ptr m_formation_task_ptr = nullptr; + std::shared_ptr m_battle_task_ptr = nullptr; + }; +} diff --git a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp index 2fdbf81dd9..ef9376c90f 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp @@ -4,17 +4,28 @@ #include "Config/Miscellaneous/BattleDataConfig.h" #include "Config/Miscellaneous/CopilotConfig.h" +#include "Config/Miscellaneous/SSSCopilotConfig.h" #include "Config/TaskData.h" #include "Controller.h" #include "Task/ProcessTask.h" #include "Utils/Logger.hpp" #include "Vision/OcrWithFlagTemplImageAnalyzer.h" +void asst::BattleFormationTask::append_additional_formation(AdditionalFormation formation) +{ + m_additional.emplace_back(std::move(formation)); +} + void asst::BattleFormationTask::set_support_unit_name(std::string name) { m_support_unit_name = std::move(name); } +void asst::BattleFormationTask::set_data_resource(DataResource resource) +{ + m_data_resource = resource; +} + bool asst::BattleFormationTask::_run() { LogTraceFunction; @@ -51,6 +62,9 @@ bool asst::BattleFormationTask::_run() } } } + + add_additional(); + confirm_selection(); // 借一个随机助战 @@ -63,17 +77,55 @@ bool asst::BattleFormationTask::_run() return true; } +bool asst::BattleFormationTask::add_additional() +{ + LogTraceFunction; + + if (m_additional.empty()) { + return false; + } + + for (const auto& addition : m_additional) { + std::string filter_name; + switch (addition.filter) { + case Filter::Cost: + filter_name = "BattleQuickFormationFilter-Cost"; + break; + case Filter::Trust: + // TODO + break; + } + if (!filter_name.empty()) { + ProcessTask(*this, { "BattleQuickFormationFilter" }).run(); + ProcessTask(*this, { filter_name }).run(); + if (addition.double_click_filter) { + ProcessTask(*this, { filter_name }).run(); + } + ProcessTask(*this, { "BattleQuickFormationFilterClose" }).run(); + } + for (const auto& [role, number] : addition.role_counts) { + // unknown role means "all" + click_role_table(role); + + auto opers_result = analyzer_opers(); + + // TODO 这里要识别一下干员之前有没有被选中过 + for (int i = 0; i < number && i < opers_result.size(); ++i) { + const auto& oper = opers_result.at(i); + ctrler()->click(oper.rect); + } + } + } + + return true; +} + bool asst::BattleFormationTask::select_random_support_unit() { return ProcessTask(*this, { "BattleSupportUnitFormation" }).run(); } -bool asst::BattleFormationTask::enter_selection_page() -{ - return ProcessTask(*this, { "BattleQuickFormation" }).run(); -} - -bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& groups) +std::vector asst::BattleFormationTask::analyzer_opers() { auto formation_task_ptr = Task.get("BattleQuickFormationOCR"); auto image = ctrler()->get_image(); @@ -94,7 +146,7 @@ bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& opers_result.insert(opers_result.end(), special_focus_res.cbegin(), special_focus_res.cend()); if (opers_result.empty()) { - return false; + return {}; } // 按位置排个序 @@ -106,17 +158,32 @@ bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& return lhs.rect.x < rhs.rect.x; // 否则按x排序 } }); + if (m_the_right_name == opers_result.back().text) { - return false; + return {}; } m_the_right_name = opers_result.back().text; + return opers_result; +} + +bool asst::BattleFormationTask::enter_selection_page() +{ + return ProcessTask(*this, { "BattleQuickFormation" }).run(); +} + +bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& groups) +{ + auto opers_result = analyzer_opers(); + static const std::array SkillRectArray = { Task.get("BattleQuickFormationSkill1")->specific_rect, Task.get("BattleQuickFormationSkill2")->specific_rect, Task.get("BattleQuickFormationSkill3")->specific_rect, }; + int delay = Task.get("BattleQuickFormationOCR")->post_delay; + int skill = 1; for (const auto& res : opers_result) { const std::string& name = res.text; @@ -139,7 +206,7 @@ bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& continue; } ctrler()->click(res.rect); - sleep(formation_task_ptr->post_delay); + sleep(delay); if (1 <= skill && skill <= 3) { ctrler()->click(SkillRectArray.at(skill - 1ULL)); } @@ -171,6 +238,7 @@ bool asst::BattleFormationTask::click_role_table(battle::Role role) { battle::Role::Sniper, "Sniper" }, { battle::Role::Special, "Special" }, { battle::Role::Support, "Support" }, { battle::Role::Tank, "Tank" }, { battle::Role::Warrior, "Warrior" }, }; + m_the_right_name.clear(); auto role_iter = RoleNameType.find(role); if (role_iter == RoleNameType.cend()) { @@ -187,7 +255,12 @@ bool asst::BattleFormationTask::parse_formation() auto& details = info["details"]; auto& formation = details["formation"]; - for (const auto& [name, opers_vec] : Copilot.get_data().groups) { + auto* groups = &Copilot.get_data().groups; + if (m_data_resource == DataResource::SSSCopilot) { + groups = &SSSCopilot.get_data().groups; + } + + for (const auto& [name, opers_vec] : *groups) { if (opers_vec.empty()) { continue; } diff --git a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h index 5dbcad4c4f..256757b166 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h @@ -10,13 +10,36 @@ namespace asst using AbstractTask::AbstractTask; virtual ~BattleFormationTask() override = default; + enum class Filter + { + None, + Trust, + Cost, + }; + struct AdditionalFormation + { + Filter filter = Filter::None; + bool double_click_filter = true; + battle::RoleCounts role_counts; + }; + void append_additional_formation(AdditionalFormation formation); + void set_support_unit_name(std::string name); + enum class DataResource + { + Copilot, + SSSCopilot, + }; + void set_data_resource(DataResource resource); + protected: using OperGroup = std::vector; virtual bool _run() override; + bool add_additional(); + bool enter_selection_page(); bool select_opers_in_cur_page(std::vector& groups); void swipe_page(); @@ -25,9 +48,13 @@ namespace asst bool parse_formation(); bool select_random_support_unit(); + std::vector analyzer_opers(); + std::string m_stage_name; std::unordered_map> m_formation; std::string m_the_right_name; std::string m_support_unit_name; + DataResource m_data_resource = DataResource::Copilot; + std::vector m_additional; }; } diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index f8ad2a77c7..09da002494 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -1525,15 +1525,16 @@ namespace MaaWpfGui /// /// 作业 JSON 的文件路径,绝对、相对路径均可。 /// 是否进行 “快捷编队”。 + /// 任务类型 /// 是否成功。 - public bool AsstStartCopilot(string filename, bool formation) + public bool AsstStartCopilot(string filename, bool formation, string type) { var task_params = new JObject { ["filename"] = filename, ["formation"] = formation, }; - AsstTaskId id = AsstAppendTaskWithEncoding("Copilot", task_params); + AsstTaskId id = AsstAppendTaskWithEncoding(type, task_params); _latestTaskId[TaskType.Copilot] = id; return id != 0 && AsstStart(); } @@ -1663,4 +1664,4 @@ namespace MaaWpfGui /// DeploymentWithPause = 3, } -} +} \ No newline at end of file diff --git a/src/MaaWpfGui/Main/CopilotViewModel.cs b/src/MaaWpfGui/Main/CopilotViewModel.cs index f8c8b948fc..e28bf28a03 100644 --- a/src/MaaWpfGui/Main/CopilotViewModel.cs +++ b/src/MaaWpfGui/Main/CopilotViewModel.cs @@ -214,6 +214,7 @@ namespace MaaWpfGui private bool _isDataFromWeb = false; private const string TempCopilotFile = "resource/_temp_copilot.json"; + private string TaskType = "General"; private void ParseJsonAndShowInfo(string jsonStr) { @@ -308,6 +309,19 @@ namespace MaaWpfGui AddLog(string.Format("共 {0} 名干员", count), UILogColor.Message); + if (json.ContainsKey("type")) + { + var type = json["type"].ToString(); + if (type == "SSS") + { + TaskType = "SSSCopilot"; + } + } + else + { + TaskType = "Copilot"; + } + if (_isDataFromWeb) { File.Delete(TempCopilotFile); @@ -420,7 +434,7 @@ namespace MaaWpfGui AddLog(errMsg, UILogColor.Error); } - bool ret = asstProxy.AsstStartCopilot(_isDataFromWeb ? TempCopilotFile : Filename, Form); + bool ret = asstProxy.AsstStartCopilot(_isDataFromWeb ? TempCopilotFile : Filename, Form, TaskType); if (ret) { AddLog(Localization.GetString("Running"));