Merge pull request #3368 from MaaAssistantArknights/dev

Release v4.10.0-beta.2
This commit is contained in:
MistEO
2023-01-15 03:54:36 +08:00
committed by GitHub
15 changed files with 199 additions and 82 deletions

View File

@@ -1,3 +1,9 @@
## v4.10.0-beta.2
- 修复 `引航者试炼` 部分已知问题 @MistEO
- 更新 `保全派驻` 协议,新增部分功能支持 @MistEO
- 开放 掉落识别上传企鹅物流 @MistEO
## v4.10.0-beta.1
- 修复 `引航者试炼` 地图格子错误 @horror-proton

View File

@@ -28,18 +28,19 @@
],
"tool_men": { // 剩余所需各职业人数,按费用排序随便拿,可选
"Pioneer": 13,
"Warrior": 2, // 中英文均可
"近卫": 2, // 中英文均可
"Medic": 2
},
"drop_buffs": [ // 战斗后拿buff的优先级。好像只有第四层有可选
"定向部署支援系统"
],
"drop_tool_men": [ // 战斗结束后招募干员职业的优先级,可选
"drops": [ // 战斗开始时和战斗中途,招募干员、获取装备优先级
"空弦",
"能天使",
"先锋", // 中英文均可
"能天使", // 支持干员名、职业名
"先锋", // 职业名中英文均可
"Support",
"无需增调干员" // 不招人
"无需增调干员", // 不招人
"重整导能组件", // 支持装备名,全写一起.jpg
"反制导能组件",
"战备激活阀", // 关卡中途的可选装备,也也放这里。需要配合 actions 中 type: GetDrops 使用
"改派发讯器",
],
"stages": [
{
@@ -88,7 +89,6 @@
}
],
"draw_as_possible": true, // “调配干员”按钮,是否好了就用,可选,默认 true
"actions": [ // 可选
// 基本复用抄作业的逻辑,可参考 3.3-战斗流程协议.md
// 符合 action 的条件就执行 action否则执行上面的 strategies 的逻辑
@@ -100,10 +100,16 @@
"name": "棘刺"
},
{
"type": "CheckIfStartOver", // 检查干员在不在,不在就退出重开
"tool_men": {
"Pioneer": 2
}
"type": "CheckIfStartOver", // 新 type检查战利品并获取
"name": "棘刺"
},
{
"type": "GetDrops", // 检查获取战斗中途的战利品,将按照 drops 字段的排序进行拿去
// 若 drops 字段为空,或 drops 中的物品一个都没识别到,将会选择放弃战利品
// 若执行动作时未识别到战利品获取,将会直接跳过该 action
"kills": 20,
"pre_delay": 3000 // 小技巧:一般来说都是打死了某个怪才会出,所以可以基于击杀数来触发
// 但是从击杀数达到到选择界面跳出来可能有个延迟,所以建议价格 pre_delay
},
{
"name": "桃金娘",

View File

@@ -33,15 +33,16 @@
"近卫": 6,
"医疗": 2
},
"drop_buffs": [
"定向部署支援系统"
],
"drop_tool_men": [
"drops": [
"空弦",
"能天使",
"先锋",
"辅助",
"无需增调干员"
"Support",
"无需增调干员",
"重整导能组件",
"反制导能组件",
"战备激活阀",
"改派发讯器"
],
"stages": [
{

View File

@@ -6154,12 +6154,14 @@
"specialParams": [
400,
2,
0
0,
100
],
"specialParamsDoc": [
"设置朝向时的滑动距离",
"滑动初速度(平均速度是固定的,初速度快末速度慢相当于一直减速)",
"滑动末速度"
"滑动末速度",
"最小滑动时长"
]
},
"BattleOpersFlag": {
@@ -9437,14 +9439,19 @@
]
},
"SSSDropRecruitmentFlag": {
"algorithm": "OcrDetect",
"action": "DoNothing",
"roi": [
0,
39,
250,
203
"text": [
"请选择",
"请进行"
],
"postDelay": 2000,
"roi": [
163,
78,
900,
112
],
"postDelay": 5000,
"next": [
"SSSStartFighting2",
"SSSDropRecruitmentFlag"
@@ -9470,9 +9477,9 @@
"cache": false,
"roi": [
0,
434,
352,
1280,
169
263
]
},
"SSSDropRecruitmentConfrim": {
@@ -9480,13 +9487,64 @@
"action": "ClickSelf",
"text": [
"确认调用",
"放弃调用"
"放弃调用",
"确认选择"
],
"roi": [
1105,
525,
175,
132
],
"preDelay": 1000,
"postDelay": 1000
},
"SSSHalfTimeDropsBegin": {
"algorithm": "JustReturn",
"action": "DoNothing",
"next": [
"SSSHalfTimeDrops",
"SSSHalfTimeDropsCancel"
]
},
"SSSHalfTimeDrops": {
"algorithm": "OcrDetect",
"action": "ClickSelf",
"text": [],
"roi": [
206,
452,
897,
88
],
"next": [
"SSSHalfTimeDropsConfirm"
]
},
"SSSHalfTimeDropsConfirm": {
"algorithm": "OcrDetect",
"action": "ClickSelf",
"text": [
"确认"
],
"roi": [
1073,
281,
155,
151
]
},
"SSSHalfTimeDropsCancel": {
"algorithm": "OcrDetect",
"action": "ClickSelf",
"text": [
"放弃"
],
"roi": [
51,
251,
146,
199
]
},
"ClickCornerUntilStartButton": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -157,6 +157,7 @@ namespace asst::battle
/* for SSS */
DrawCard, // “调配干员”
CheckIfStartOver, // 检查如果没有某干员则退出重开
GetDrops, // 识别并获取战斗中途的掉落物
};
struct Action
@@ -212,6 +213,7 @@ namespace asst::battle
std::vector<Strategy> strategies;
bool draw_as_possible = false;
int retry_times = 0;
std::vector<std::string> order_of_drops;
};
enum class EquipmentType
@@ -231,8 +233,7 @@ namespace asst::battle
copilot::OperUsageGroups groups;
RoleCounts tool_men;
std::vector<std::string> drop_buffs;
std::vector<std::string> drop_tool_men;
std::vector<std::string> order_of_drops;
std::unordered_map<std::string, CombatData> stages_data;
};

View File

@@ -155,6 +155,14 @@ std::vector<asst::battle::copilot::Action> asst::CopilotConfig::parse_actions(co
{ "CHECKIFSTARTOVER", ActionType::CheckIfStartOver },
{ "checkifstartover", ActionType::CheckIfStartOver },
{ "检查重开", ActionType::CheckIfStartOver },
{ "GetDrops", ActionType::GetDrops },
{ "Getdrops", ActionType::GetDrops },
{ "GETDROPS", ActionType::GetDrops },
{ "getdrops", ActionType::GetDrops },
{ "获取战利品", ActionType::GetDrops },
{ "获取掉落", ActionType::GetDrops },
{ "获取装备", ActionType::GetDrops },
};
std::string type_str = action_info.get("type", "Deploy");

View File

@@ -42,29 +42,13 @@ bool asst::SSSCopilotConfig::parse(const json::value& json)
}
}
if (auto drop_buffs = json.find<json::array>("drop_buffs")) {
for (const auto& drop_buff : drop_buffs.value()) {
m_data.drop_buffs.emplace_back(drop_buff.as_string());
if (auto drops_opt = json.find<json::array>("drops")) {
m_data.order_of_drops.reserve(drops_opt->size());
for (const auto& drop : *drops_opt) {
m_data.order_of_drops.emplace_back(drop.as_string());
}
}
if (auto drop_tool_men = json.find<json::array>("drop_tool_men")) {
for (const auto& man : drop_tool_men.value()) {
std::string name = man.as_string();
if (auto role = get_role_type(name); role != Role::Unknown) {
m_data.drop_tool_men.emplace_back(name);
}
else if (BattleData.get_rarity(name) != 0) {
m_data.drop_tool_men.emplace_back(name);
}
else if (name == "None") {
m_data.drop_tool_men.emplace_back(name);
}
else {
Log.warn("Unknown drop tool man", name);
}
}
}
for (const auto& stage : json.at("stages").as_array()) {
CombatData stage_data;
stage_data.info = CopilotConfig::parse_basic_info(stage);
@@ -72,6 +56,7 @@ bool asst::SSSCopilotConfig::parse(const json::value& json)
stage_data.actions = CopilotConfig::parse_actions(stage);
stage_data.groups = m_data.groups;
stage_data.order_of_drops = m_data.order_of_drops;
for (const auto& strategy_info : stage.at("strategies").as_array()) {
Strategy strategy;

View File

@@ -86,14 +86,13 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable)
LogTraceFunction;
if (init) {
wait_until_start();
wait_until_start(false);
}
cv::Mat image = init || reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable;
if (init) {
auto draw_future = std::async(std::launch::async, [&]() { save_map(image); });
update_kills(image);
}
BattleImageAnalyzer oper_analyzer(image);
@@ -219,11 +218,16 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable)
AvatarCache.set_avatar(name, oper.role, oper.avatar);
}
}
pause();
if (!unknown_opers.empty()) {
cancel_oper_selection();
}
image = m_inst_helper.ctrler()->get_image();
}
if (init) {
update_kills(image);
}
check_in_battle(image);
@@ -283,6 +287,10 @@ bool asst::BattleHelper::deploy_oper(const std::string& name, const Point& loc,
// 1000 是随便取的一个系数,把整数的 pre_delay 转成小数用的
int duration = static_cast<int>(dist / 1000.0 * swipe_oper_task_ptr->pre_delay);
// 时间太短了的压根放不上去,故意加长一点
if (int min_duration = swipe_oper_task_ptr->special_params.at(3); duration < min_duration) {
duration = min_duration;
}
bool deploy_with_pause = m_inst_helper.ctrler()->support_swipe_with_pause();
m_inst_helper.ctrler()->swipe(oper_rect, Rect(target_point.x, target_point.y, 1, 1), duration, false,
swipe_oper_task_ptr->special_params.at(1), swipe_oper_task_ptr->special_params.at(2),
@@ -380,38 +388,51 @@ bool asst::BattleHelper::check_pause_button(const cv::Mat& reusable)
return ret;
}
bool asst::BattleHelper::check_in_battle(const cv::Mat& reusable)
bool asst::BattleHelper::check_in_battle(const cv::Mat& reusable, bool weak)
{
cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable;
BattleImageAnalyzer analyzer(image);
m_in_battle = analyzer.analyze();
if (weak) {
BattleImageAnalyzer analyzer(image);
m_in_battle = analyzer.analyze();
}
else {
m_in_battle = check_pause_button(image);
}
return m_in_battle;
}
bool asst::BattleHelper::wait_until_start()
bool asst::BattleHelper::wait_until_start(bool weak)
{
LogTraceFunction;
while (!m_inst_helper.need_exit() && !check_in_battle()) {
cv::Mat image = m_inst_helper.ctrler()->get_image();
while (!m_inst_helper.need_exit() && !check_in_battle(image, weak)) {
do_strategic_action(image);
std::this_thread::yield();
image = m_inst_helper.ctrler()->get_image();
}
return true;
}
bool asst::BattleHelper::wait_until_end()
bool asst::BattleHelper::wait_until_end(bool weak)
{
LogTraceFunction;
while (!m_inst_helper.need_exit() && check_in_battle()) {
do_strategic_action();
cv::Mat image = m_inst_helper.ctrler()->get_image();
while (!m_inst_helper.need_exit() && check_in_battle(image, weak)) {
do_strategic_action(image);
std::this_thread::yield();
image = m_inst_helper.ctrler()->get_image();
}
return true;
}
bool asst::BattleHelper::do_strategic_action(const cv::Mat& reusable)
{
return check_in_battle(reusable) && use_all_ready_skill(reusable);
cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable;
return check_in_battle(image) && use_all_ready_skill(image);
}
bool asst::BattleHelper::use_all_ready_skill(const cv::Mat& reusable)
@@ -474,8 +495,8 @@ void asst::BattleHelper::save_map(const cv::Mat& image)
auto draw = image.clone();
for (const auto& [loc, info] : m_normal_tile_info) {
std::string text = "( " + std::to_string(loc.x) + ", " + std::to_string(loc.y) + " )";
cv::putText(draw, text, cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2);
cv::circle(draw, cv::Point(info.pos.x, info.pos.y), 5, cv::Scalar(0, 255, 0), -1);
cv::putText(draw, loc.to_string(), cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2);
}
std::string suffix;
@@ -579,7 +600,7 @@ bool asst::BattleHelper::move_camera(const std::pair<double, double>& delta)
// 还没转场的时候
if (m_kills != 0) {
wait_until_end();
wait_until_end(false);
}
m_kills = 0;

View File

@@ -45,9 +45,9 @@ namespace asst
bool use_skill(const std::string& name, bool keep_waiting = true);
bool use_skill(const Point& loc, bool keep_waiting = true);
bool check_pause_button(const cv::Mat& reusable = cv::Mat());
bool check_in_battle(const cv::Mat& reusable = cv::Mat());
virtual bool wait_until_start();
bool wait_until_end();
bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = false);
virtual bool wait_until_start(bool weak = true);
bool wait_until_end(bool weak = true);
bool use_all_ready_skill(const cv::Mat& reusable = cv::Mat());
bool check_and_use_skill(const std::string& name, const cv::Mat& reusable = cv::Mat());
bool check_and_use_skill(const Point& loc, const cv::Mat& reusable = cv::Mat());

View File

@@ -209,13 +209,7 @@ void asst::StageDropsTaskPlugin::upload_to_penguin()
{
LogTraceFunction;
Log.warn("debug version, not upload to penguin");
return;
// https://github.com/MaaAssistantArknights/MaaAssistantArknights/pull/3290
// 新的掉落识别算法,在确认识别结果完全准确前,暂时禁用上传功能
#if 0
if (m_server != "CN" && m_server != "US" && m_server != "JP") {
if (m_server != "CN" && m_server != "US" && m_server != "JP" && m_server != "KR") {
return;
}
@@ -276,7 +270,6 @@ void asst::StageDropsTaskPlugin::upload_to_penguin()
.set_extra_param(extra_param)
.set_retry_times(5)
.run();
#endif
}
void asst::StageDropsTaskPlugin::report_penguin_callback(AsstMsg msg, const json::value& detail, AbstractTask* task_ptr)

View File

@@ -262,6 +262,9 @@ bool asst::BattleProcessTask::wait_condition(const Action& action)
break;
}
}
if (!check_in_battle(image)) {
return false;
}
do_strategy_and_update_image();
}
}
@@ -273,6 +276,9 @@ bool asst::BattleProcessTask::wait_condition(const Action& action)
if (m_kills >= action.kills) {
break;
}
if (!check_in_battle(image)) {
return false;
}
do_strategy_and_update_image();
}
}
@@ -284,6 +290,9 @@ bool asst::BattleProcessTask::wait_condition(const Action& action)
if (m_cost >= action.costs) {
break;
}
if (!check_in_battle(image)) {
return false;
}
do_strategy_and_update_image();
}
}
@@ -298,6 +307,9 @@ bool asst::BattleProcessTask::wait_condition(const Action& action)
if (cooling_count == action.cooling) {
break;
}
if (!check_in_battle(image)) {
return false;
}
do_strategy_and_update_image();
}
}
@@ -312,6 +324,9 @@ bool asst::BattleProcessTask::wait_condition(const Action& action)
iter != m_cur_deployment_opers.cend() && iter->second.available) {
break;
}
if (!check_in_battle(image)) {
return false;
}
do_strategy_and_update_image();
}
}

View File

@@ -1,6 +1,7 @@
#include "SSSBattleProcessTask.h"
#include "Config/Miscellaneous/SSSCopilotConfig.h"
#include "Config/TaskData.h"
#include "Controller.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
@@ -47,6 +48,8 @@ bool asst::SSSBattleProcessTask::do_derived_action(size_t action_index)
return draw_card();
case battle::copilot::ActionType::CheckIfStartOver:
return check_if_start_over(action);
case battle::copilot::ActionType::GetDrops:
return get_drops();
default:
Log.error("unknown action type", static_cast<int>(action.type));
return false;
@@ -121,11 +124,12 @@ bool asst::SSSBattleProcessTask::do_strategic_action(const cv::Mat& reusable)
return true;
}
bool asst::SSSBattleProcessTask::wait_until_start()
bool asst::SSSBattleProcessTask::wait_until_start(bool weak)
{
LogTraceFunction;
return ProcessTask(*this, { "SSSFightDirectly" }).set_retry_times(300).run();
return ProcessTask(*this, { "SSSFightDirectly" }).set_retry_times(300).run() &&
BattleProcessTask::wait_until_start(weak);
}
bool asst::SSSBattleProcessTask::check_if_start_over(const battle::copilot::Action& action)
@@ -161,9 +165,27 @@ bool asst::SSSBattleProcessTask::check_if_start_over(const battle::copilot::Acti
bool asst::SSSBattleProcessTask::draw_card(bool with_retry)
{
LogTraceFunction;
ProcessTask task(*this, { "SSSDrawCard" });
if (!with_retry) {
task.set_retry_times(0);
}
return task.run();
}
bool asst::SSSBattleProcessTask::get_drops()
{
LogTraceFunction;
const auto& drops = m_sss_combat_data.order_of_drops;
// 不一定就有掉落界面,有可能宝箱怪压根没被打掉。所以不重试
if (drops.empty()) {
return ProcessTask(*this, { inst_string() + "@SSSHalfTimeDropsCancel" }).set_retry_times(0).run();
}
else {
Task.get<OcrTaskInfo>(inst_string() + "@SSSHalfTimeDrops")->text = { drops };
return ProcessTask(*this, { inst_string() + "@SSSHalfTimeDropsBegin" }).set_retry_times(0).run();
}
}

View File

@@ -17,10 +17,11 @@ namespace asst
virtual battle::copilot::CombatData& get_combat_data() override { return m_sss_combat_data; }
virtual const std::string oper_name_ocr_task_name() const noexcept override { return "SSSBattleOperName"; }
virtual bool need_to_wait_until_end() const noexcept override { return true; }
virtual bool wait_until_start() override;
virtual bool wait_until_start(bool weak = true) override;
bool check_if_start_over(const battle::copilot::Action& action);
bool draw_card(bool with_retry = true);
bool get_drops();
battle::sss::CombatData m_sss_combat_data;
std::unordered_set<std::string> m_all_cores;

View File

@@ -44,7 +44,7 @@ bool asst::SSSDropRewardsTaskPlugin::_run()
.ocr_res = result, .role = role == Role::Unknown ? std::nullopt : std::optional<Role>(role) });
}
for (const std::string& name : SSSCopilot.get_data().drop_tool_men) {
for (const std::string& name : SSSCopilot.get_data().order_of_drops) {
auto role = get_role_type(name);
bool is_role = role != Role::Unknown;
auto iter = ranges::find_if(opers, [&](const DropRecruitment& props) {