diff --git a/docs/maa_tasks_schema.json b/docs/maa_tasks_schema.json index a514ce44b4..fc8deba3a1 100644 --- a/docs/maa_tasks_schema.json +++ b/docs/maa_tasks_schema.json @@ -47,10 +47,14 @@ }, "action": { "type": "string", - "pattern": "ClickSelf|ClickRand|DoNothing|Stop|ClickRect|Swipe", + "pattern": "ClickSelf|ClickRand|DoNothing|Stop|ClickRect|Swipe|Input", "default": "DoNothing", "description": "可选项,表示识别到后的动作\n不填写时默认为 DoNothing" }, + "inputText": { + "type": "string", + "description": "可选项,当 action 为 Input 时有效,表示要输入的文本" + }, "sub": { "$ref": "#/definitions/TaskNameList", "description": "可选项,子任务。会在执行完当前任务后,依次执行每一个子任务\n可以套娃,子任务再套子任务。但要注意不要写出了死循环" diff --git a/docs/zh-cn/protocol/task-schema.md b/docs/zh-cn/protocol/task-schema.md index 82c98fcf83..735b0cb3c3 100644 --- a/docs/zh-cn/protocol/task-schema.md +++ b/docs/zh-cn/protocol/task-schema.md @@ -32,6 +32,7 @@ icon: material-symbols:task // - DoNothing: 什么都不做 // - Stop: 停止当前任务 // - Swipe: 滑动,对应 specificRect 与 rectMove 字段 + // - Input: 输入文本,要求 algorithm 为 JustReturn,对应 inputText 字段 "sub": ["SubTaskName1", "SubTaskName2"], // 可选项,子任务,不推荐使用。会在执行完当前任务后,依次执行每一个子任务 @@ -138,9 +139,13 @@ icon: material-symbols:task "isAscii": false, // 可选项,要识别的文字内容是否为 ASCII 码字符 // 不填写默认 false - "withoutDet": false // 可选项,是否不使用检测模型 + "withoutDet": false, // 可选项,是否不使用检测模型 // 不填写默认 false + /* 以下字段仅当 algorithm 为 JustReturn,action 为 Input 时有效 */ + + "inputText": "A string text." // 必选项,要输入的文字内容,以字符串的形式 + } } ``` diff --git a/resource/config.json b/resource/config.json index a7a5059704..0498a6e158 100644 --- a/resource/config.json +++ b/resource/config.json @@ -84,6 +84,7 @@ "screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p", "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "input": "[Adb] -s [AdbSerial] shell input text [text]", "start": "[Adb] -s [AdbSerial] shell am start -n [PackageName]/com.u8.sdk.U8UnityContext", "stop": "[Adb] -s [AdbSerial] shell am force-stop [PackageName]", "back_to_home": "[Adb] -s [AdbSerial] shell input keyevent HOME", diff --git a/resource/tasks.json b/resource/tasks.json index 0da93e1e5a..464e316f32 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -6653,6 +6653,7 @@ "Roguelike@Begin": { "algorithm": "JustReturn", "next": [ + "Roguelike@StartExploreWithSeed", "Roguelike@ChooseDifficulty", "Roguelike@StartExplore", "Roguelike@Stages#next", @@ -6777,7 +6778,13 @@ "Roguelike@ClickToStartPointAfterFailed": { "doc": "因为是从肉鸽失败界面进入,删除了对下个stage的判断", "baseTask": "Roguelike@ClickToStartPoint", - "next": ["Roguelike@StartExplore", "Roguelike@StartUpThemes#next", "Roguelike@ClickToStartPointAfterFailed"] + "next": [ + "Roguelike@StartExploreWithSeed", + "Roguelike@StartExplore", + "Roguelike@StartExploreCD", + "Roguelike@StartUpThemes#next", + "Roguelike@ClickToStartPointAfterFailed" + ] }, "Roguelike@CloseCollection": { "Doc": "base_task", @@ -7737,6 +7744,62 @@ "postDelay": 1000, "next": ["Roguelike@StartExplore", "Roguelike@StartExploreCD"] }, + "Roguelike@StartExploreWithSeed": { + "Doc": "萨卡兹点刺刷钱,由 cpp 内启用", + "maxTimes": 0, + "exceededNext": ["Roguelike@StartExplore"], + "algorithm": "OcrDetect", + "action": "ClickSelf", + "text": ["启用参数"], + "roi": [851, 567, 93, 33], + "next": ["Roguelike@StartExploreWithSeed-ClickPen"] + }, + "Roguelike@StartExploreWithSeed-ClickPen": { + "template": "empty.png", + "action": "ClickSelf", + "roi": [640, 163, 145, 141], + "next": ["Roguelike@StartExploreWithSeed-ClickInputBox"] + }, + "Roguelike@StartExploreWithSeed-ClickInputBox": { + "algorithm": "OcrDetect", + "action": "ClickSelf", + "text": ["参数"], + "roi": [323, 267, 200, 147], + "next": ["Roguelike@StartExploreWithSeed-InputSeed"] + }, + "Roguelike@StartExploreWithSeed-InputSeed": { + "algorithm": "JustReturn", + "action": "Input", + "inputText": "", + "next": ["Roguelike@StartExploreWithSeed-Confirm"] + }, + "Roguelike@StartExploreWithSeed-Confirm": { + "Doc": "找到一个 “RecruitConfirm.png” 可能复用,但是分数太低,放弃", + "template": "empty.png", + "action": "ClickSelf", + "roi": [645, 393, 585, 173], + "postDelay": 1000, + "next": [ + "Roguelike@StartExploreWithSeed-Confirm", + "Roguelike@StartExploreWithSeed-SetDifficulty", + "Roguelike@StartExploreWithSeed-BackToMainPage" + ] + }, + "Roguelike@StartExploreWithSeed-SetDifficulty": { + "template": "empty.png", + "action": "ClickSelf", + "roi": [847, 246, 276, 205], + "next": ["Roguelike@StartExploreWithSeed-BackToMainPage"] + }, + "Roguelike@StartExploreWithSeed-BackToMainPage": { + "Doc": "识别到 ‘停用参数’ 就行了,不做难度解锁的检查了", + "algorithm": "OcrDetect", + "text": ["停用参数"], + "action": "ClickRect", + "roi": [631, 209, 118, 49], + "specificRect": [600, 5, 300, 70], + "next": ["Roguelike@StartExplore", "Roguelike@StartExploreWithSeed-BackToMainPage"] + }, "Roguelike@StrategyChange": { "baseTask": "Roguelike@StrategyChange_default" }, @@ -10944,6 +11007,12 @@ "Sarkaz@Roguelike@StartExplore": { "roi": [1014, 481, 266, 219] }, + "Sarkaz@Roguelike@StartExploreWithSeed-ClickPen": {}, + "Sarkaz@Roguelike@StartExploreWithSeed-Confirm": {}, + "Sarkaz@Roguelike@StartExploreWithSeed-SetDifficulty": {}, + "Sarkaz@Roguelike@StartExploreWithSeed-InputSeed": { + "inputText": "U1eIA1I29844Ae925i,rogue_4,6" + }, "Sarkaz@Roguelike@StrategyChange-FastInvestment": { "baseTask": "Sarkaz@Roguelike@StrategyChange_mode1", "text": ["_fastInvestment", "_exit"], diff --git a/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-ClickPen.png b/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-ClickPen.png new file mode 100644 index 0000000000..35cafb29cc Binary files /dev/null and b/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-ClickPen.png differ diff --git a/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-Confirm.png b/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-Confirm.png new file mode 100644 index 0000000000..6d203489cc Binary files /dev/null and b/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-Confirm.png differ diff --git a/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-SetDifficulty.png b/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-SetDifficulty.png new file mode 100644 index 0000000000..74f769752b Binary files /dev/null and b/resource/template/Sarkaz@Roguelike@StartExploreWithSeed-SetDifficulty.png differ diff --git a/src/MaaCore/Common/AsstTypes.h b/src/MaaCore/Common/AsstTypes.h index 672d780507..82b83ae049 100644 --- a/src/MaaCore/Common/AsstTypes.h +++ b/src/MaaCore/Common/AsstTypes.h @@ -336,6 +336,7 @@ enum class ProcessTaskAction DoNothing = 0x200, // 什么都不做 Stop = 0x400, // 停止当前Task Swipe = 0x1000, // 滑动 + Input = 0x2000, // 输入文本 }; inline ProcessTaskAction get_action_type(std::string action_str) @@ -345,6 +346,7 @@ inline ProcessTaskAction get_action_type(std::string action_str) { "", ProcessTaskAction::DoNothing }, { "donothing", ProcessTaskAction::DoNothing }, { "clickself", ProcessTaskAction::ClickSelf }, { "clickrect", ProcessTaskAction::ClickRect }, { "stop", ProcessTaskAction::Stop }, { "swipe", ProcessTaskAction::Swipe }, + { "input", ProcessTaskAction::Input }, }; if (auto it = action_map.find(action_str); it != action_map.end()) { return it->second; @@ -358,7 +360,7 @@ inline std::string enum_to_string(ProcessTaskAction action) { ProcessTaskAction::Invalid, "Invalid" }, { ProcessTaskAction::DoNothing, "DoNothing" }, { ProcessTaskAction::BasicClick, "BasicClick" }, { ProcessTaskAction::ClickSelf, "ClickSelf" }, { ProcessTaskAction::ClickRect, "ClickRect" }, { ProcessTaskAction::Stop, "Stop" }, - { ProcessTaskAction::Swipe, "Swipe" }, + { ProcessTaskAction::Swipe, "Swipe" }, { ProcessTaskAction::Input, "Input" }, }; if (auto it = action_map.find(action); it != action_map.end()) { return it->second; @@ -490,6 +492,7 @@ struct TaskInfo : public TaskPipelineInfo // 即识别到了res,点击res + result_move的位置 bool cache = false; // 是否使用缓存区域 std::vector special_params; // 某些任务会用到的特殊参数 + std::string input_text; // 输入任务的文字,目前希望仅针对 Input 任务有效, algorithm 为 JustReturn }; using TaskPtr = std::shared_ptr; diff --git a/src/MaaCore/Config/GeneralConfig.cpp b/src/MaaCore/Config/GeneralConfig.cpp index 93d5bb376b..9508fb7bd6 100644 --- a/src/MaaCore/Config/GeneralConfig.cpp +++ b/src/MaaCore/Config/GeneralConfig.cpp @@ -83,6 +83,7 @@ bool asst::GeneralConfig::parse(const json::value& json) adb.display_id = cfg_json.get("displayId", base_cfg.display_id); adb.uuid = cfg_json.get("uuid", base_cfg.uuid); adb.click = cfg_json.get("click", base_cfg.click); + adb.input = cfg_json.get("input", base_cfg.input); adb.swipe = cfg_json.get("swipe", base_cfg.swipe); adb.press_esc = cfg_json.get("pressEsc", base_cfg.press_esc); adb.display = cfg_json.get("display", base_cfg.display); diff --git a/src/MaaCore/Config/GeneralConfig.h b/src/MaaCore/Config/GeneralConfig.h index 1b239590d7..a0fd0e3af9 100644 --- a/src/MaaCore/Config/GeneralConfig.h +++ b/src/MaaCore/Config/GeneralConfig.h @@ -70,6 +70,7 @@ struct AdbCfg std::string display_id; std::string uuid; std::string click; + std::string input; std::string swipe; std::string press_esc; std::string display; diff --git a/src/MaaCore/Config/Roguelike/RoguelikeStageEncounterConfig.h b/src/MaaCore/Config/Roguelike/RoguelikeStageEncounterConfig.h index 1659d3c8cd..ea55c42249 100644 --- a/src/MaaCore/Config/Roguelike/RoguelikeStageEncounterConfig.h +++ b/src/MaaCore/Config/Roguelike/RoguelikeStageEncounterConfig.h @@ -46,6 +46,29 @@ public: const auto& get_event_names(const std::string& theme) const noexcept { return m_event_names.at(theme); } + bool set_event(std::string theme, RoguelikeMode mode, std::string event_name, int choose, int option_num) + { + std::pair key = std::make_pair(theme, static_cast(mode)); + if (theme == "Sarkaz" || theme == "Sami") { + // 在调试器里发现 m_events 中,Sami 和 Sarkaz 的 mode 只有 -1 + key.second = -1; + } + // 边界检查 + auto outerIt = m_events.find(key); + if (outerIt == m_events.end()) { + return false; + } + auto& innerMap = outerIt->second; + auto innerIt = innerMap.find(event_name); + if (innerIt == innerMap.end()) { + return false; + } + // 修改事件选择 + m_events[key][event_name].default_choose = choose; + m_events[key][event_name].option_num = option_num; + return true; + } + enum class ComparisonType { GreaterThan, diff --git a/src/MaaCore/Config/TaskData.cpp b/src/MaaCore/Config/TaskData.cpp index 26cbcb9304..78992f0303 100644 --- a/src/MaaCore/Config/TaskData.cpp +++ b/src/MaaCore/Config/TaskData.cpp @@ -431,6 +431,7 @@ asst::TaskPtr asst::TaskData::generate_task_info(std::string_view name) ASST_TASKDATA_GET_VALUE_OR("rectMove", rect_move); ASST_TASKDATA_GET_VALUE_OR("specificRect", specific_rect); ASST_TASKDATA_GET_VALUE_OR("specialParams", special_params); + ASST_TASKDATA_GET_VALUE_OR("inputText", input_text); // 展开五个任务列表中的虚任务 ASST_TASKDATA_GET_VALUE_OR_LAZY("next", next, false); @@ -933,6 +934,7 @@ bool asst::TaskData::syntax_check(std::string_view task_name, const json::value& "specialParams", "sub", "subErrorIgnored", // specific + "inputText" } }, }; // clang-format on @@ -940,6 +942,7 @@ bool asst::TaskData::syntax_check(std::string_view task_name, const json::value& static const std::unordered_map> allowed_key_under_action = { { ProcessTaskAction::ClickRect, { "specificRect" } }, { ProcessTaskAction::Swipe, { "specificRect", "rectMove" } }, + { ProcessTaskAction::Input, { "inputText" } }, }; auto is_doc = [&](std::string_view key) { diff --git a/src/MaaCore/Controller/AdbController.cpp b/src/MaaCore/Controller/AdbController.cpp index 554fee0d5e..628ce07554 100644 --- a/src/MaaCore/Controller/AdbController.cpp +++ b/src/MaaCore/Controller/AdbController.cpp @@ -336,6 +336,16 @@ bool asst::AdbController::click(const Point& p) return call_command(cur_cmd).has_value(); } +bool asst::AdbController::input(const std::string& text) +{ + if (text == "") { + Log.error("empty text"); + } + + std::string cur_cmd = utils::string_replace_all(m_adb.input, { { "[text]", text } }); + return call_command(cur_cmd).has_value(); +} + bool asst::AdbController::swipe( const Point& p1, const Point& p2, @@ -996,6 +1006,7 @@ bool asst::AdbController::connect(const std::string& adb_path, const std::string } m_adb.click = cmd_replace(adb_cfg.click); + m_adb.input = cmd_replace(adb_cfg.input); m_adb.swipe = cmd_replace(adb_cfg.swipe); m_adb.press_esc = cmd_replace(adb_cfg.press_esc); m_adb.screencap_raw_with_gzip = cmd_replace(adb_cfg.screencap_raw_with_gzip); diff --git a/src/MaaCore/Controller/AdbController.h b/src/MaaCore/Controller/AdbController.h index a0081bf147..7808b465e5 100644 --- a/src/MaaCore/Controller/AdbController.h +++ b/src/MaaCore/Controller/AdbController.h @@ -44,6 +44,8 @@ public: virtual bool click(const Point& p) override; + virtual bool input(const std::string& text) override; + virtual bool swipe( const Point& p1, const Point& p2, @@ -117,6 +119,7 @@ protected: std::string call_minitouch; std::string call_maatouch; std::string click; + std::string input; std::string swipe; std::string press_esc; diff --git a/src/MaaCore/Controller/ControlScaleProxy.h b/src/MaaCore/Controller/ControlScaleProxy.h index 32923849ee..5cf2c90d5d 100644 --- a/src/MaaCore/Controller/ControlScaleProxy.h +++ b/src/MaaCore/Controller/ControlScaleProxy.h @@ -27,6 +27,8 @@ public: bool click(const Point& p); bool click(const Rect& rect); + bool input(const std::string text); + bool swipe( const Point& p1, const Point& p2, diff --git a/src/MaaCore/Controller/Controller.cpp b/src/MaaCore/Controller/Controller.cpp index 9274898c8d..6f15aa7061 100644 --- a/src/MaaCore/Controller/Controller.cpp +++ b/src/MaaCore/Controller/Controller.cpp @@ -173,6 +173,12 @@ bool asst::Controller::click(const Rect& rect) return m_scale_proxy->click(rect); } +bool asst::Controller::input(const std::string& text) +{ + CHECK_EXIST(m_controller, false); + return m_controller->input(text); +} + bool asst::Controller::swipe( const Point& p1, const Point& p2, diff --git a/src/MaaCore/Controller/Controller.h b/src/MaaCore/Controller/Controller.h index 4ce09a2ee8..a32c20ddaa 100644 --- a/src/MaaCore/Controller/Controller.h +++ b/src/MaaCore/Controller/Controller.h @@ -68,6 +68,7 @@ public: bool click(const Point& p); bool click(const Rect& rect); + bool input(const std::string& text); bool swipe( const Point& p1, diff --git a/src/MaaCore/Controller/ControllerAPI.h b/src/MaaCore/Controller/ControllerAPI.h index 93dadcdd5a..b543e2e38c 100644 --- a/src/MaaCore/Controller/ControllerAPI.h +++ b/src/MaaCore/Controller/ControllerAPI.h @@ -42,6 +42,8 @@ public: virtual bool click(const Point& p) = 0; + virtual bool input(const std::string& text) = 0; + virtual bool swipe( const Point& p1, const Point& p2, diff --git a/src/MaaCore/Controller/PlayToolsController.cpp b/src/MaaCore/Controller/PlayToolsController.cpp index e8db7ae617..2735a7dcb3 100644 --- a/src/MaaCore/Controller/PlayToolsController.cpp +++ b/src/MaaCore/Controller/PlayToolsController.cpp @@ -120,6 +120,11 @@ bool asst::PlayToolsController::click(const Point& p) return toucher_down(p) && toucher_up(p); } +bool asst::PlayToolsController::input([[maybe_unused]] const std::string& text) +{ + return true; +} + bool asst::PlayToolsController::swipe( const Point& p1, const Point& p2, diff --git a/src/MaaCore/Controller/PlayToolsController.h b/src/MaaCore/Controller/PlayToolsController.h index 6dfb3ca344..ccb55dabd4 100644 --- a/src/MaaCore/Controller/PlayToolsController.h +++ b/src/MaaCore/Controller/PlayToolsController.h @@ -37,6 +37,8 @@ public: virtual bool click(const Point& p) override; + virtual bool input(const std::string& text) override; + virtual bool swipe( const Point& p1, const Point& p2, diff --git a/src/MaaCore/Task/Interface/RoguelikeTask.cpp b/src/MaaCore/Task/Interface/RoguelikeTask.cpp index 9bb8e273a2..30b5ecdb14 100644 --- a/src/MaaCore/Task/Interface/RoguelikeTask.cpp +++ b/src/MaaCore/Task/Interface/RoguelikeTask.cpp @@ -123,6 +123,11 @@ bool asst::RoguelikeTask::set_params(const json::value& params) m_roguelike_task_ptr->set_times_limit("StageTraderInvestCancel", 0); m_roguelike_task_ptr->set_times_limit("StageTraderLeaveConfirm", INT_MAX); } + // 萨卡兹种子刷钱 + if (theme == RoguelikeTheme::Sarkaz && params.get("start_with_seed", false)) { + m_roguelike_task_ptr->set_times_limit("Roguelike@StartExploreWithSeed", INT_MAX); + RoguelikeStageEncounter.set_event(theme, mode, "相遇", 3, 4); + } } else { // 重置战斗后奖励next diff --git a/src/MaaCore/Task/ProcessTask.cpp b/src/MaaCore/Task/ProcessTask.cpp index 94c0a6e27c..fbf9fa17e1 100644 --- a/src/MaaCore/Task/ProcessTask.cpp +++ b/src/MaaCore/Task/ProcessTask.cpp @@ -195,6 +195,10 @@ ProcessTask::NodeStatus ProcessTask::run_action(const HitDetail& hits) const exec_click_task(rect); return NodeStatus::Success; } + case ProcessTaskAction::Input: { + exec_input_task(task->input_text); + return NodeStatus::Success; + } case ProcessTaskAction::Swipe: { size_t param_size = task->special_params.size(); // Warning: 这里的后两个参数 slope_in 和 slope_out 是 double 类型,但是在 json 中是 int 类型 @@ -404,6 +408,11 @@ void ProcessTask::exec_click_task(const Rect& matched_rect) const ctrler()->click(matched_rect); } +void ProcessTask::exec_input_task(const std::string& text) const +{ + ctrler()->input(text); +} + void ProcessTask::exec_swipe_task( const Rect& r1, const Rect& r2, diff --git a/src/MaaCore/Task/ProcessTask.h b/src/MaaCore/Task/ProcessTask.h index c37e2cb1b0..351cf1fe7c 100644 --- a/src/MaaCore/Task/ProcessTask.h +++ b/src/MaaCore/Task/ProcessTask.h @@ -68,6 +68,7 @@ protected: int calc_post_delay(TaskConstPtr task) const; void exec_click_task(const Rect& matched_rect) const; + void exec_input_task(const std::string& text) const; void exec_swipe_task( const Rect& r1, const Rect& r2, diff --git a/src/MaaCore/Task/Roguelike/Map/RoguelikeRoutingTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/Map/RoguelikeRoutingTaskPlugin.cpp index ddbf2082fc..509b36249c 100644 --- a/src/MaaCore/Task/Roguelike/Map/RoguelikeRoutingTaskPlugin.cpp +++ b/src/MaaCore/Task/Roguelike/Map/RoguelikeRoutingTaskPlugin.cpp @@ -12,7 +12,7 @@ #include "Vision/Miscellaneous/BrightPointAnalyzer.h" #include "Vision/MultiMatcher.h" -bool asst::RoguelikeRoutingTaskPlugin::load_params([[maybe_unused]] const json::value& params) +bool asst::RoguelikeRoutingTaskPlugin::load_params(const json::value& params) { LogTraceFunction; const std::string& theme = m_config->get_theme(); @@ -37,7 +37,8 @@ bool asst::RoguelikeRoutingTaskPlugin::load_params([[maybe_unused]] const json:: const RoguelikeMode& mode = m_config->get_mode(); const std::string squad = params.get("squad", ""); - if (mode == RoguelikeMode::Investment && squad == "点刺成锭分队") { + if (mode == RoguelikeMode::Investment && + (squad == "点刺成锭分队" || (squad == "后勤分队" && params.get("start_with_seed", false)))) { m_routing_strategy = RoutingStrategy::FastInvestment; return true; } diff --git a/src/MaaCore/Task/Roguelike/RoguelikeConfig.cpp b/src/MaaCore/Task/Roguelike/RoguelikeConfig.cpp index 84c2372641..62dbffeb21 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeConfig.cpp +++ b/src/MaaCore/Task/Roguelike/RoguelikeConfig.cpp @@ -28,6 +28,7 @@ bool asst::RoguelikeConfig::verify_and_load_params(const json::value& params) return false; } + m_start_with_seed = params.get("start_with_seed", false); m_start_with_elite_two = params.get("start_with_elite_two", false); m_only_start_with_elite_two = params.get("only_start_with_elite_two", false); if (mode != RoguelikeMode::Collectible && (m_start_with_elite_two || m_only_start_with_elite_two)) { @@ -52,9 +53,13 @@ bool asst::RoguelikeConfig::verify_and_load_params(const json::value& params) // 点刺成锭分队特殊策略 if (m_theme == "Sarkaz") { - if (m_mode == RoguelikeMode::Investment && params.get("squad", "") == "点刺成锭分队") { - // 启用特殊策略,联动 RoguelikeRoutingTaskPlugin - Task.set_task_base(strategy_task, "Sarkaz@Roguelike@StrategyChange-FastInvestment"); + auto squad = params.get("squad", ""); + if (m_mode == RoguelikeMode::Investment && + (squad == "点刺成锭分队" || (squad == "后勤分队" && m_start_with_seed))) { + if (squad == "点刺成锭分队") { + // 启用特殊策略,联动 RoguelikeRoutingTaskPlugin + Task.set_task_base(strategy_task, "Sarkaz@Roguelike@StrategyChange-FastInvestment"); + } // 禁用前 2 层的 <思维负荷干员编队> 功能 Task.set_task_base( "Sarkaz@Roguelike@StageBurdenOperation", diff --git a/src/MaaCore/Task/Roguelike/RoguelikeConfig.h b/src/MaaCore/Task/Roguelike/RoguelikeConfig.h index 4a79a1477a..a0db1c0b18 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeConfig.h +++ b/src/MaaCore/Task/Roguelike/RoguelikeConfig.h @@ -155,6 +155,8 @@ public: int get_floor() const { return m_floor; } + bool get_start_with_seed() const { return m_start_with_seed; } + private: // ------------------ 招募 ------------------ bool m_team_full_without_rookie = false; // 编队内没有预干员 @@ -173,6 +175,7 @@ private: int m_hp = 0; // 当前生命值 int m_floor = 0; // 当前到达层数 int m_formation_upper_limit = 6; // 当前编队上限 + bool m_start_with_seed = false; // 种子刷钱 public: // ------------------ 密文板 ------------------ diff --git a/src/MaaCore/Task/Roguelike/RoguelikeCustomStartTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/RoguelikeCustomStartTaskPlugin.cpp index 6e66992173..8a1a026345 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeCustomStartTaskPlugin.cpp +++ b/src/MaaCore/Task/Roguelike/RoguelikeCustomStartTaskPlugin.cpp @@ -47,8 +47,16 @@ bool asst::RoguelikeCustomStartTaskPlugin::verify(AsstMsg msg, const json::value bool asst::RoguelikeCustomStartTaskPlugin::load_params(const json::value& params) { - set_custom(RoguelikeCustomType::Squad, params.get("squad", "")); // 开局分队 - set_custom(RoguelikeCustomType::Roles, params.get("roles", "")); // 开局职业组 + LogTraceFunction; + + set_custom(RoguelikeCustomType::Squad, params.get("squad", "")); // 开局分队 + if (params.get("start_with_seed", false)) { // 种子刷钱,强制随心所欲 + set_custom(RoguelikeCustomType::Roles, "随心所欲"); + } + else { + set_custom(RoguelikeCustomType::Roles, params.get("roles", "")); // 开局职业组 + } + set_custom(RoguelikeCustomType::CoreChar, params.get("core_char", "")); // 开局干员名 set_custom(RoguelikeCustomType::UseSupport, params.get("use_support", false) ? "1" : "0"); // 开局干员是否为助战干员 set_custom( @@ -88,6 +96,8 @@ bool asst::RoguelikeCustomStartTaskPlugin::_run() bool asst::RoguelikeCustomStartTaskPlugin::hijack_squad() { + LogTraceFunction; + constexpr size_t SwipeTimes = 7; for (size_t i = 0; i != SwipeTimes; ++i) { auto image = ctrler()->get_image(); @@ -112,6 +122,8 @@ bool asst::RoguelikeCustomStartTaskPlugin::hijack_squad() bool asst::RoguelikeCustomStartTaskPlugin::hijack_roles() { + LogTraceFunction; + auto image = ctrler()->get_image(); OCRer analyzer(image); analyzer.set_task_info("RoguelikeCustom-HijackRoles"); @@ -127,6 +139,27 @@ bool asst::RoguelikeCustomStartTaskPlugin::hijack_roles() bool asst::RoguelikeCustomStartTaskPlugin::hijack_core_char() { + LogTraceFunction; + + if (m_config->get_start_with_seed()) { + Log.trace("Start with seed"); + auto image = ctrler()->get_image(); + OCRer analyzer(image); + analyzer.set_task_info("RoguelikeCustom-HijackCoChar"); + analyzer.set_roi({ 186, 500, 913, 200 }); + analyzer.set_required({ "招募" }); + if (!analyzer.analyze()) { + return false; + } + const auto& role_rect = analyzer.get_result().front().rect; + ctrler()->click(role_rect); + + m_config->set_use_support(m_customs[RoguelikeCustomType::UseSupport] == "1"); + m_config->set_use_nonfriend_support(m_customs[RoguelikeCustomType::UseNonfriendSupport] == "1"); + m_config->set_core_char(m_customs[RoguelikeCustomType::CoreChar]); + return true; + } + const std::string& char_name = m_customs[RoguelikeCustomType::CoreChar]; static const std::unordered_map RoleOcrNameMap = { diff --git a/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp index ccf331a895..57d8ee4924 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp +++ b/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp @@ -73,7 +73,8 @@ bool asst::RoguelikeRecruitTaskPlugin::_run() ++m_recruit_count; if (m_config->get_theme() == "Sarkaz" && m_config->get_mode() == RoguelikeMode::Investment && - m_config->get_squad() == "点刺成锭分队") { + (m_config->get_squad() == "点刺成锭分队" || + (m_config->get_squad() == "后勤分队" && m_config->get_start_with_seed()))) { ProcessTask(*this, { "Sarkaz@RoguelikeRecruit-GiveUp" }).run(); return true; } diff --git a/src/MaaWpfGui/Constants/ConfigurationKeys.cs b/src/MaaWpfGui/Constants/ConfigurationKeys.cs index 7d628e956b..7bfdd4f975 100755 --- a/src/MaaWpfGui/Constants/ConfigurationKeys.cs +++ b/src/MaaWpfGui/Constants/ConfigurationKeys.cs @@ -142,6 +142,8 @@ namespace MaaWpfGui.Constants public const string RoguelikeDeploymentWithPause = "Roguelike.DeploymentWithPause"; public const string RoguelikeStopAtFinalBoss = "Roguelike.ExitAtFinalBoss"; public const string RoguelikeStopAtMaxLevel = "Roguelike.StopAtMaxLevel"; + public const string RoguelikeStartWithSeed = "Roguelike.StartWithSeed"; + public const string ReclamationTheme = "Reclamation.Theme"; public const string ReclamationMode = "Reclamation.Mode"; public const string ReclamationToolToCraft = "Reclamation.ToolToCraft"; diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index 88feea6344..9f4818dacd 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -2434,6 +2434,7 @@ namespace MaaWpfGui.Main /// 是否用骰子刷新商店购买特殊商品,目前支持水月肉鸽的指路鳞 /// 是否在五层BOSS前停下来 /// 是否在满级时停止任务 + /// 是否使用刷钱种子 /// 是否成功。 public bool AsstAppendRoguelike( int mode, @@ -2458,7 +2459,8 @@ namespace MaaWpfGui.Main string theme, bool refreshTraderWithDice, bool stopAtFinalBoss, - bool stopAtMaxLevel) + bool stopAtMaxLevel, + bool startWithSeed) { var taskParams = new JObject { @@ -2518,9 +2520,10 @@ namespace MaaWpfGui.Main taskParams["refresh_trader_with_dice"] = theme == "Mizuki" && refreshTraderWithDice; taskParams["stop_at_final_boss"] = mode == 0 && stopAtFinalBoss; - taskParams["stop_at_max_level"] = mode == 0 && stopAtMaxLevel; + taskParams["start_with_seed"] = startWithSeed; + AsstTaskId id = AsstAppendTaskWithEncoding("Roguelike", taskParams); _latestTaskId[TaskType.Roguelike] = id; return id != 0; diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 0d1733db2a..e919ff6f9f 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -144,6 +144,7 @@ Don't abort Auto IS until the end of battle Stop before final boss Stop when max level is achieved + 启用刷钱种子 Pause Deployment (Pause Trick) (Experimental) Use ADB Lite (Experimental) Kill ADB On Exit diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index c5cc84048f..e9ed0d8740 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -144,6 +144,7 @@ 自動ローグ 戦闘終了まえに「停止」を遅らせる 第5階層のボスの手前で一時停止する 最大レベル達成時に停止 + 启用刷钱种子 統合戦略/保全駐在でポーズトリックを使用します(ベータ機能のため非推奨です) 軽量ADBを使用(実験機能) 終了時にADBを終了する diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index 27ea1634a9..61bd168e89 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -144,6 +144,7 @@ 통합전략 중지 시 전투 클리어 후 작업 종료 5스테이지 보스 앞에서 일시정지 최대 레벨 달성 시 중지 + 启用刷钱种子 일시정지 상태로 배치하기 - 통합전략, 자동 작전, 보안 파견에 적용 (비권장) ADB Lite 사용 (실험적) 종료 시 ADB도 종료 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index efdbc8c9ad..b4efc5e34f 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -144,6 +144,7 @@ 自动肉鸽在战斗结束前延迟「停止」动作 在第五层 BOSS 前暂停 满级后自动停止 + 启用刷钱种子(美愿) 划火柴模式(自动战斗) 使用 ADB Lite(实验性功能) 退出时释放 ADB diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index 036a4df158..3128f80506 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -144,6 +144,7 @@ 肉鴿在戰鬥結束前延遲「停止」動作 在第五層 BOSS 前暫停 滿級後自動停止 + 启用刷钱种子 暫停下幹員(同時影響肉鴿、自動戰鬥、保全)(不穩定,暫不推薦開啟) 使用 ADB Lite(實驗性功能) 退出時釋放 ADB diff --git a/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs b/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs index f9a5571071..1119fbde48 100644 --- a/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs @@ -1790,7 +1790,8 @@ namespace MaaWpfGui.ViewModels.UI SettingsViewModel.RoguelikeTask.RoguelikeTheme, SettingsViewModel.RoguelikeTask.RoguelikeRefreshTraderWithDice, SettingsViewModel.RoguelikeTask.RoguelikeStopAtFinalBoss, - SettingsViewModel.RoguelikeTask.RoguelikeStopAtMaxLevel); + SettingsViewModel.RoguelikeTask.RoguelikeStopAtMaxLevel, + SettingsViewModel.RoguelikeTask.RoguelikeStartWithSeed); } private static bool AppendReclamation() diff --git a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/RoguelikeSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/RoguelikeSettingsUserControlModel.cs index 21d22a7e5a..594d747d86 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/RoguelikeSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/RoguelikeSettingsUserControlModel.cs @@ -704,4 +704,21 @@ public class RoguelikeSettingsUserControlModel : PropertyChangedBase ConfigurationHelper.SetValue(ConfigurationKeys.RoguelikeStopAtMaxLevel, value.ToString()); } } + + private bool _roguelikeStartWithSeedRaw = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.RoguelikeStartWithSeed, bool.FalseString)); + + /// + /// Gets or sets a value indicating whether start with seed when investing in Sarkaz. + /// + public bool RoguelikeStartWithSeedRaw + { + get => _roguelikeStartWithSeedRaw; + set + { + SetAndNotify(ref _roguelikeStartWithSeedRaw, value); + ConfigurationHelper.SetValue(ConfigurationKeys.RoguelikeStartWithSeed, value.ToString()); + } + } + + public bool RoguelikeStartWithSeed => _roguelikeStartWithSeedRaw && RoguelikeTheme == "Sarkaz" && RoguelikeMode == "1" && RoguelikeSquad is "点刺成锭分队" or "后勤分队"; } diff --git a/src/MaaWpfGui/Views/UserControl/TaskQueue/RoguelikeSettingsUserControl.xaml b/src/MaaWpfGui/Views/UserControl/TaskQueue/RoguelikeSettingsUserControl.xaml index 9e2c9440d0..56b55f6ccc 100644 --- a/src/MaaWpfGui/Views/UserControl/TaskQueue/RoguelikeSettingsUserControl.xaml +++ b/src/MaaWpfGui/Views/UserControl/TaskQueue/RoguelikeSettingsUserControl.xaml @@ -217,6 +217,15 @@ Text="{DynamicResource RoguelikeStopAtMaxLevel}" TextWrapping="Wrap" /> + + +