mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 01:40:46 +08:00
feat.完成自动抄作业-部署干员全部逻辑
This commit is contained in:
@@ -45,6 +45,9 @@
|
||||
],
|
||||
"direction": 1
|
||||
},
|
||||
{
|
||||
"type": 100
|
||||
},
|
||||
{
|
||||
"name": "临光",
|
||||
"location": [
|
||||
|
||||
@@ -3265,6 +3265,16 @@
|
||||
60
|
||||
]
|
||||
},
|
||||
"BattleSpeedUp": {
|
||||
"algorithm": "JustReturn",
|
||||
"action": "ClickRect",
|
||||
"specificRect": [
|
||||
1070,
|
||||
20,
|
||||
60,
|
||||
60
|
||||
]
|
||||
},
|
||||
"BattlePause": {
|
||||
"algorithm": "JustReturn",
|
||||
"action": "ClickRect",
|
||||
@@ -3307,6 +3317,16 @@
|
||||
255
|
||||
]
|
||||
},
|
||||
"BattleCostData": {
|
||||
"algorithm": "OcrDetect",
|
||||
"text": [],
|
||||
"roi": [
|
||||
1197,
|
||||
492,
|
||||
83,
|
||||
48
|
||||
]
|
||||
},
|
||||
"BattleUseOper": {
|
||||
"algorithm": "JustReturn",
|
||||
"preDelay": 500,
|
||||
|
||||
@@ -42,7 +42,15 @@ bool asst::BattleImageAnalyzer::analyze()
|
||||
ret &= kills_analyze();
|
||||
}
|
||||
|
||||
return true;
|
||||
if (m_target & Target::Cost) {
|
||||
ret &= cost_analyze();
|
||||
}
|
||||
|
||||
if (m_target & Target::Vacancies) {
|
||||
ret &= vacancies_analyze();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<asst::BattleRealTimeOper>& asst::BattleImageAnalyzer::get_opers() const noexcept
|
||||
@@ -108,7 +116,7 @@ bool asst::BattleImageAnalyzer::opers_analyze()
|
||||
oper.role = oper_role_analyze(role_rect);
|
||||
Rect cost_rect = flag_mrect.rect.move(cost_move);
|
||||
|
||||
oper.cost = oper_cost_analyze(cost_rect);
|
||||
//oper.cost = oper_cost_analyze(cost_rect);
|
||||
oper.index = index++;
|
||||
|
||||
m_opers.emplace_back(std::move(oper));
|
||||
@@ -381,7 +389,7 @@ bool asst::BattleImageAnalyzer::kills_analyze()
|
||||
|
||||
// 这里的结果应该是 "击杀数/总的敌人数",例如 "0/41"
|
||||
std::string kills_res = kills_analyzer.get_result().front().text;
|
||||
int pos = kills_res.find('/');
|
||||
size_t pos = kills_res.find('/');
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
@@ -400,7 +408,24 @@ bool asst::BattleImageAnalyzer::kills_analyze()
|
||||
|
||||
bool asst::BattleImageAnalyzer::cost_analyze()
|
||||
{
|
||||
return false;
|
||||
OcrImageAnalyzer cost_analyzer(m_image);
|
||||
cost_analyzer.set_task_info("BattleKillsFlag");
|
||||
|
||||
if (!cost_analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cost_analyzer.sort_result_by_score();
|
||||
std::string cost_str = cost_analyzer.get_result().front().text;
|
||||
|
||||
if (cost_str.empty() ||
|
||||
!std::all_of(cost_str.cbegin(), cost_str.cend(),
|
||||
[](char c) -> bool {return std::isdigit(c);})) {
|
||||
return false;
|
||||
}
|
||||
m_cost = std::stoi(cost_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleImageAnalyzer::vacancies_analyze()
|
||||
|
||||
@@ -8,15 +8,16 @@ namespace asst
|
||||
class BattleImageAnalyzer : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
enum Target // 需要识别的目标
|
||||
enum Target // 需要识别的目标
|
||||
{
|
||||
HP, // 剩余生命值
|
||||
Home, // 蓝色的家门
|
||||
Oper, // 下方的干员信息
|
||||
Skill, // cd 转好了可以使用的技能
|
||||
Kills, // 击杀数
|
||||
Cost, // 费用
|
||||
Vacancies, // 剩余可部署干员数
|
||||
None = 0,
|
||||
HP = 1, // 剩余生命值
|
||||
Home = 2, // 蓝色的家门
|
||||
Oper = 4, // 下方的干员信息
|
||||
Skill = 8, // cd 转好了可以使用的技能
|
||||
Kills = 16, // 击杀数
|
||||
Cost = 32, // 费用
|
||||
Vacancies = 64, // 剩余可部署干员数
|
||||
// 肉鸽模式需要用到的识别
|
||||
Roguelike = Home | HP | Oper | Skill
|
||||
};
|
||||
@@ -55,6 +56,7 @@ namespace asst
|
||||
std::vector<Rect> m_ready_skills; // 可以释放的技能(Rect实际是干员的位置)
|
||||
int m_hp = 0; // 剩余生命值
|
||||
int m_kills = 0; // 击杀数
|
||||
int m_cost = 0; // 部署费用
|
||||
|
||||
protected:
|
||||
// 该分析器不支持外部设置ROI
|
||||
|
||||
@@ -28,7 +28,7 @@ bool asst::BattleProcessTask::_run()
|
||||
;
|
||||
}
|
||||
|
||||
for (const BattleAction& action : m_actions.actions) {
|
||||
for (const auto& action : m_actions.actions) {
|
||||
do_action(action);
|
||||
}
|
||||
|
||||
@@ -59,18 +59,18 @@ bool asst::BattleProcessTask::get_stage_info()
|
||||
|
||||
bool asst::BattleProcessTask::analyze_opers_preview()
|
||||
{
|
||||
BattleImageAnalyzer analyzer(m_ctrler->get_image());
|
||||
analyzer.set_target(BattleImageAnalyzer::Target::Oper);
|
||||
if (!analyzer.analyze()) {
|
||||
BattleImageAnalyzer oper_analyzer(m_ctrler->get_image());
|
||||
oper_analyzer.set_target(BattleImageAnalyzer::Target::Oper);
|
||||
if (!oper_analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
// TODO: 干员头像出来之后,还要过 2 秒左右才可以点击,这里可能还要加个延时
|
||||
|
||||
battle_pause();
|
||||
auto opers = analyzer.get_opers();
|
||||
auto opers = oper_analyzer.get_opers();
|
||||
|
||||
for (size_t i = 0; i != opers.size(); ++i) {
|
||||
const auto& cur_oper = analyzer.get_opers();
|
||||
const auto& cur_oper = oper_analyzer.get_opers();
|
||||
size_t offset = opers.size() > cur_oper.size() ? opers.size() - cur_oper.size() : 0;
|
||||
m_ctrler->click(cur_oper.at(i - offset).rect);
|
||||
|
||||
@@ -95,8 +95,8 @@ bool asst::BattleProcessTask::analyze_opers_preview()
|
||||
m_opers_info.emplace(std::move(oper_name), std::move(opers.at(i)));
|
||||
|
||||
// 干员特别多的时候,任意干员被点开,都会导致下方的干员图标被裁剪和移动。所以这里需要重新识别一下
|
||||
analyzer.set_image(image);
|
||||
analyzer.analyze();
|
||||
oper_analyzer.set_image(image);
|
||||
oper_analyzer.analyze();
|
||||
}
|
||||
battle_pause();
|
||||
cancel_selection();
|
||||
@@ -112,10 +112,6 @@ bool asst::BattleProcessTask::update_opers_info()
|
||||
return false;
|
||||
}
|
||||
const auto& cur_opers_info = analyzer.get_opers();
|
||||
// 干员数量没有变化,无需更新
|
||||
if (cur_opers_info.size() == m_opers_info.size()) {
|
||||
return true;
|
||||
}
|
||||
decltype(m_opers_info) pre_opers_info;
|
||||
m_opers_info.swap(pre_opers_info);
|
||||
|
||||
@@ -124,12 +120,12 @@ bool asst::BattleProcessTask::update_opers_info()
|
||||
for (const auto& cur_oper : cur_opers_info) {
|
||||
int left_index = 0;
|
||||
int right_index = 0;
|
||||
// 干员数变多了,干员可能位于 [之前的位置, 之前的位置 + |size_change|] 之间
|
||||
// 干员数变多了,之前的干员可能位于 [当前位置 - |size_change|, 当前位置 ] 之间
|
||||
if (is_increased) {
|
||||
left_index = static_cast<int>(cur_oper.index);
|
||||
right_index = static_cast<int>(cur_oper.index) + size_change;
|
||||
left_index = static_cast<int>(cur_oper.index) - size_change;
|
||||
right_index = static_cast<int>(cur_oper.index);
|
||||
}
|
||||
// 干员数减少了,干员可能位于 [之前的位置 - |size_change|, 之前的位置] 之间
|
||||
// 干员数减少了,之前的干员可能位于 [当前位置 , 当前位置 + |size_change|] 之间
|
||||
else {
|
||||
left_index = static_cast<int>(cur_oper.index);
|
||||
right_index = static_cast<int>(cur_oper.index) - size_change; // size_change 是 负值
|
||||
@@ -143,25 +139,34 @@ bool asst::BattleProcessTask::update_opers_info()
|
||||
ranged_iters.emplace_back(iter);
|
||||
}
|
||||
}
|
||||
MatchImageAnalyzer avatar_analyzer(cur_oper.avatar);
|
||||
avatar_analyzer.set_task_info("BattleAvatarData");
|
||||
decltype(ranged_iters)::value_type matched_iter;
|
||||
MatchRect matched_result;
|
||||
|
||||
// 遍历比较,得分最高的那个就说明是对应的那个
|
||||
for (const auto& iter : ranged_iters) {
|
||||
avatar_analyzer.set_templ(iter->second.avatar);
|
||||
if (!avatar_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
if (matched_result.score < avatar_analyzer.get_result().score) {
|
||||
matched_result = avatar_analyzer.get_result();
|
||||
matched_iter = iter;
|
||||
decltype(ranged_iters)::value_type matched_iter = ranged_iters.front();
|
||||
|
||||
if (ranged_iters.empty()) {
|
||||
Log.error("ranged_iters empty", left_index, right_index);
|
||||
return false;
|
||||
}
|
||||
else if (ranged_iters.size() > 1) {
|
||||
MatchImageAnalyzer avatar_analyzer(cur_oper.avatar);
|
||||
avatar_analyzer.set_task_info("BattleAvatarData");
|
||||
MatchRect matched_result;
|
||||
|
||||
// 遍历比较,得分最高的那个就说明是对应的那个
|
||||
for (const auto& iter : ranged_iters) {
|
||||
avatar_analyzer.set_templ(iter->second.avatar);
|
||||
if (!avatar_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
if (matched_result.score < avatar_analyzer.get_result().score) {
|
||||
matched_result = avatar_analyzer.get_result();
|
||||
matched_iter = iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matched_result.score == 0) {
|
||||
continue;
|
||||
else {
|
||||
; // 只有一个干员的时候就不用计算,就是唯一的那个
|
||||
}
|
||||
|
||||
auto temp_oper = cur_oper;
|
||||
temp_oper.name = matched_iter->first;
|
||||
// 保存当前干员信息
|
||||
@@ -175,19 +180,65 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
|
||||
if (!wait_condition(action)) {
|
||||
return false;
|
||||
}
|
||||
while (!update_opers_info()) {
|
||||
;
|
||||
|
||||
switch (action.type) {
|
||||
case BattleActionType::Deploy:
|
||||
return oper_deploy(action);
|
||||
break;
|
||||
case BattleActionType::Retreat:
|
||||
break;
|
||||
case BattleActionType::UseSkill:
|
||||
break;
|
||||
case BattleActionType::SwitchSpeed:
|
||||
return battle_speedup();
|
||||
break;
|
||||
case BattleActionType::SlowMode:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::wait_condition(const BattleAction& action)
|
||||
{
|
||||
if (action.kills_condition <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr int analyze_target = BattleImageAnalyzer::Target::Kills;
|
||||
|
||||
while (true) {
|
||||
auto image = m_ctrler->get_image();
|
||||
BattleImageAnalyzer analyzer(image);
|
||||
analyzer.set_target(analyze_target);
|
||||
|
||||
if (!analyzer.analyze()) {
|
||||
return true;
|
||||
}
|
||||
if (action.kills_condition <= analyzer.get_kills()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::oper_deploy(const BattleAction& action)
|
||||
{
|
||||
const auto use_oper_task_ptr = Task.get("BattleUseOper");
|
||||
const auto swipe_oper_task_ptr = Task.get("BattleSwipeOper");
|
||||
|
||||
// TODO,临时调试方案。正式版本这里需要对 group_name -> oper_name 做一个转换
|
||||
auto iter = m_opers_info.find(action.group_name);
|
||||
if (iter == m_opers_info.cend()) {
|
||||
Log.error("battle opers group", action.group_name, "not found");
|
||||
return false;
|
||||
}
|
||||
decltype(m_opers_info)::iterator iter;
|
||||
do {
|
||||
update_opers_info();
|
||||
|
||||
// TODO,临时调试方案。正式版本这里需要对 group_name -> oper_name 做一个转换
|
||||
iter = m_opers_info.find(action.group_name);
|
||||
if (iter == m_opers_info.cend()) {
|
||||
Log.error("battle opers group", action.group_name, "not found");
|
||||
return false;
|
||||
}
|
||||
} while (!iter->second.available);
|
||||
|
||||
// 点击干员
|
||||
Rect oper_rect = iter->second.rect;
|
||||
@@ -201,6 +252,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
|
||||
|
||||
sleep(use_oper_task_ptr->rear_delay);
|
||||
|
||||
// 拖动干员朝向
|
||||
if (action.direction != BattleDeployDirection::No) {
|
||||
static const std::unordered_map<BattleDeployDirection, Point> DirectionMapping = {
|
||||
{ BattleDeployDirection::Right, Point(1, 0)},
|
||||
@@ -210,7 +262,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
|
||||
{ BattleDeployDirection::No, Point(0, 0)},
|
||||
};
|
||||
|
||||
// 计算往哪边拖动(干员朝向)
|
||||
// 计算往哪边拖动
|
||||
Point direction = DirectionMapping.at(action.direction);
|
||||
|
||||
// 将方向转换为实际的 swipe end 坐标点
|
||||
@@ -231,31 +283,16 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::wait_condition(const BattleAction& action)
|
||||
{
|
||||
constexpr int analyze_target = BattleImageAnalyzer::Target::Kills | BattleImageAnalyzer::Target::Vacancies | BattleImageAnalyzer::Target::Cost;
|
||||
|
||||
while (true) {
|
||||
auto image = m_ctrler->get_image();
|
||||
BattleImageAnalyzer analyzer(image);
|
||||
analyzer.set_target(analyze_target);
|
||||
|
||||
if (!analyzer.analyze()) {
|
||||
return true;
|
||||
}
|
||||
if (action.kills_condition <= analyzer.get_kills()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::battle_pause()
|
||||
{
|
||||
return ProcessTask(*this, { "BattlePause" }).run();
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::battle_speedup()
|
||||
{
|
||||
return ProcessTask(*this, { "BattleSpeedUp" }).run();
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::cancel_selection()
|
||||
{
|
||||
return ProcessTask(*this, { "BattleCancelSelection" }).run();
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace asst
|
||||
|
||||
bool get_stage_info();
|
||||
bool battle_pause();
|
||||
bool battle_speedup();
|
||||
bool cancel_selection(); // 取消选择干员
|
||||
bool analyze_opers_preview();
|
||||
bool update_opers_info();
|
||||
@@ -28,12 +29,15 @@ namespace asst
|
||||
bool do_action(const BattleAction& action);
|
||||
bool wait_condition(const BattleAction& action);
|
||||
|
||||
bool oper_deploy(const BattleAction& action);
|
||||
|
||||
std::string m_stage_name;
|
||||
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_side_tile_info;
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_normal_tile_info;
|
||||
BattleActionsGroup m_actions;
|
||||
|
||||
/* 实时更新的数据 */
|
||||
std::unordered_map<std::string, BattleRealTimeOper> m_opers_info;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user