feat: 新增沙盘推演作业支持 (#16578)

新增沙盘推演相关配置字段与识别逻辑,添加沙盘选项弹窗、确认等任务配置,支持设置两个沙盘选项参数,适配单多编队作业的沙盘模式调用
This commit is contained in:
Evoltsuki
2026-05-15 12:19:01 +08:00
committed by status102
parent 204542620c
commit d7250628e1
10 changed files with 133 additions and 9 deletions

View File

@@ -389,6 +389,59 @@
"template": ["RaidDifficulty.png", "RaidDifficulty-Chapter15.png"],
"next": []
},
"SandboxOptionToggle": {
"doc": "沙盘推演选项弹窗按钮",
"template": "SandboxOptionToggle.png",
"action": "ClickSelf",
"roi": [1131, 123, 149, 148],
"preDelay": 500,
"next": []
},
"SandboxButton": {
"doc": "沙盘推演按钮",
"algorithm": "OcrDetect",
"action": "ClickSelf",
"text": ["沙盘推演"],
"roi": [910, 128, 244, 138],
"maxTimes": 3,
"next": ["SandboxOptionToggle", "#self"]
},
"SandboxOption1_1": {
"doc": "沙盘选项1-1",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [63, 263, 574, 128],
"preDelay": 1000
},
"SandboxOption1_2": {
"doc": "沙盘选项1-2",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [643, 264, 580, 132],
"preDelay": 1000
},
"SandboxOption2_1": {
"doc": "沙盘选项2-1",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [58, 463, 582, 130],
"preDelay": 500
},
"SandboxOption2_2": {
"doc": "沙盘选项2-2",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [641, 466, 580, 126],
"preDelay": 500
},
"SandboxConfirm": {
"doc": "沙盘推演确定按钮",
"algorithm": "OcrDetect",
"action": "ClickSelf",
"text": ["确定"],
"roi": [939, 570, 333, 150],
"preDelay": 500
},
"EpisodeNew": {
"Doc": "base_task",
"algorithm": "JustReturn",

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -441,6 +441,9 @@ struct BasicInfo
std::string title_color;
std::string details;
std::string details_color;
bool is_sandbox = false;
int sandbox_option1 = 1;
int sandbox_option2 = 1;
};
struct CombatData // 作业 JSON 数据

View File

@@ -40,6 +40,10 @@ asst::battle::copilot::BasicInfo asst::CopilotConfig::parse_basic_info(const jso
info.details = json.get("doc", "details", std::string());
info.details_color = json.get("doc", "details_color", std::string());
info.is_sandbox = json.get("is_sandbox", false);
info.sandbox_option1 = json.get("sandbox_option1", 1);
info.sandbox_option2 = json.get("sandbox_option2", 1);
return info;
}

View File

@@ -1,5 +1,7 @@
#include "CopilotTask.h"
#include <algorithm>
#include "Arknights-Tile-Pos/TileCalc2.hpp"
#include "Config/Miscellaneous/BattleDataConfig.h"
@@ -90,13 +92,37 @@ bool asst::CopilotTask::set_params(const json::value& params)
Log.error("Not support stage");
return false;
}
if (Copilot.get_data().info.is_sandbox) {
int opt1 = std::clamp(Copilot.get_data().info.sandbox_option1, 1, 2);
int opt2 = std::clamp(Copilot.get_data().info.sandbox_option2, 1, 2);
m_sandbox_tasks.clear();
auto raid_tp = std::make_shared<ProcessTask>(m_callback, inst(), TaskType);
raid_tp->set_tasks({ "RaidConfirm", "ChangeToRaidDifficulty" }).set_retry_times(20);
m_sandbox_tasks.emplace_back(raid_tp);
auto sandbox_entry_tp = std::make_shared<ProcessTask>(m_callback, inst(), TaskType);
sandbox_entry_tp->set_tasks({ "SandboxOptionToggle", "SandboxButton" }).set_retry_times(20);
m_sandbox_tasks.emplace_back(sandbox_entry_tp);
std::vector<std::string> sandbox_steps = { "SandboxOption1_" + std::to_string(opt1),
"SandboxOption2_" + std::to_string(opt2),
"SandboxConfirm" };
for (const auto& task_name : sandbox_steps) {
auto tp = std::make_shared<ProcessTask>(m_callback, inst(), TaskType);
tp->set_tasks({ task_name }).set_retry_times(20);
m_sandbox_tasks.emplace_back(tp);
}
size_t insert_pos = 1;
for (const auto& tp : m_sandbox_tasks) {
m_subtasks.insert(m_subtasks.begin() + insert_pos, tp);
++insert_pos;
}
}
}
else if (multi_tasks_opt) {
m_multi_copilot_plugin_ptr->set_enable(true); // 启用多任务插件, 自动覆盖Copilot中的配置
m_battle_task_ptr->set_wait_until_end(true);
auto configs = static_cast<std::vector<MultiCopilotConfig>>(*multi_tasks_opt);
std::vector<MultiCopilotTaskPlugin::MultiCopilotConfig> configs_cvt;
for (const auto& [id, filename, stage_name, is_raid] : configs) {
for (const auto& [id, filename, stage_name, is_raid, is_sandbox, sandbox_option1, sandbox_option2] : configs) {
MultiCopilotTaskPlugin::MultiCopilotConfig config_cvt;
auto copilot_opt = parse_copilot_filename(filename);
if (!copilot_opt) {
@@ -110,6 +136,9 @@ bool asst::CopilotTask::set_params(const json::value& params)
config_cvt.nav_name = stage_name;
config_cvt.is_raid = is_raid;
config_cvt.id = id; // ID 从0开始
config_cvt.is_sandbox = is_sandbox;
config_cvt.sandbox_option1 = sandbox_option1;
config_cvt.sandbox_option2 = sandbox_option2;
configs_cvt.emplace_back(std::move(config_cvt));
}

View File

@@ -19,11 +19,21 @@ public:
struct MultiCopilotConfig
{
int id = -1;
std::string filename; // 文件名
std::string stage_name; // 关卡名
bool is_raid = false; // 是否是突袭
std::string filename; // 文件名
std::string stage_name; // 关卡名
bool is_raid = false; // 是否是突袭
bool is_sandbox = false; // 是否是沙盘推演
int sandbox_option1 = 1; // 沙盘选项1 (1 or 2)
int sandbox_option2 = 1; // 沙盘选项2 (1 or 2)
MEO_JSONIZATION(MEO_OPT id, filename, stage_name, MEO_OPT is_raid);
MEO_JSONIZATION(
MEO_OPT id,
filename,
stage_name,
MEO_OPT is_raid,
MEO_OPT is_sandbox,
MEO_OPT sandbox_option1,
MEO_OPT sandbox_option2);
};
public:
@@ -40,6 +50,7 @@ private:
std::optional<std::filesystem::path> parse_copilot_filename(const std::string& name);
std::shared_ptr<MultiCopilotTaskPlugin> m_multi_copilot_plugin_ptr = nullptr;
std::vector<std::shared_ptr<ProcessTask>> m_sandbox_tasks;
std::shared_ptr<ProcessTask> m_medicine_task_ptr = nullptr;
std::shared_ptr<BattleFormationTask> m_formation_task_ptr = nullptr;
std::shared_ptr<BattleProcessTask> m_battle_task_ptr = nullptr;

View File

@@ -1,6 +1,7 @@
#include "MultiCopilotTaskPlugin.h"
#include <ranges>
#include <algorithm>
#include "Config/GeneralConfig.h"
#include "Config/Miscellaneous/CopilotConfig.h"
@@ -47,10 +48,24 @@ bool asst::MultiCopilotTaskPlugin::_run()
ret = ret && navigate_to_stage(config.nav_name);
ProcessTask(*this, { "NotUsePrts" }).set_ignore_error(true).set_retry_times(0).run();
if (config.is_raid) {
// 选择突袭模式
bool is_sandbox = config.is_sandbox || Copilot.get_data().info.is_sandbox;
int sandbox_option1 =
std::clamp(config.is_sandbox ? config.sandbox_option1 : Copilot.get_data().info.sandbox_option1, 1, 2);
int sandbox_option2 =
std::clamp(config.is_sandbox ? config.sandbox_option2 : Copilot.get_data().info.sandbox_option2, 1, 2);
if (config.is_raid || is_sandbox) {
ret = ret && ProcessTask(*this, { "RaidConfirm", "ChangeToRaidDifficulty" }).set_retry_times(20).run();
}
if (ret && is_sandbox) {
ret = ret && ProcessTask(*this, { "SandboxOptionToggle", "SandboxButton" }).set_retry_times(20).run();
std::string option1_task = "SandboxOption1_" + std::to_string(sandbox_option1);
ret = ret && ProcessTask(*this, { option1_task }).set_retry_times(3).run();
std::string option2_task = "SandboxOption2_" + std::to_string(sandbox_option2);
ret = ret && ProcessTask(*this, { option2_task }).set_retry_times(3).run();
ret = ret && ProcessTask(*this, { "SandboxConfirm" }).set_retry_times(20).run();
}
return ret;
}

View File

@@ -19,6 +19,9 @@ public:
std::string nav_name; // 关卡名
bool is_raid = false; // 是否是突袭
int id;
bool is_sandbox = false; // 是否是沙盘推演
int sandbox_option1 = 1; // 沙盘选项1 (1 or 2)
int sandbox_option2 = 1; // 沙盘选项2 (1 or 2)
};
public:

View File

@@ -47,6 +47,12 @@ public class CopilotModel : CopilotBase
[JsonProperty("difficulty")]
public DifficultyFlags Difficulty { get; set; }
/// <summary>
/// Gets or sets a value indicating whether 沙盘推演模式
/// </summary>
[JsonProperty("is_sandbox")]
public bool IsSandbox { get; set; }
public List<(string Output, string? Color)> Output()
{
var output = new List<(string, string?)>();

View File

@@ -1721,13 +1721,13 @@ public partial class CopilotViewModel : Screen
}
else
{
if (flags.HasFlag(CopilotModel.DifficultyFlags.Normal))
if (flags.HasFlag(CopilotModel.DifficultyFlags.Normal) && !copilot.IsSandbox)
{
var item = new CopilotItemViewModel(stageCode, cachePath, false, copilotId) { Index = CopilotItemViewModels.Count, };
CopilotItemViewModels.Add(item);
}
if (flags.HasFlag(CopilotModel.DifficultyFlags.Raid))
if (flags.HasFlag(CopilotModel.DifficultyFlags.Raid) || copilot.IsSandbox)
{
var item = new CopilotItemViewModel(stageCode, cachePath, true, copilotId) { Index = CopilotItemViewModels.Count, };
CopilotItemViewModels.Add(item);