Files
MaaAssistantArknights/src/MaaCore/Task/SSS/SSSBattleProcessTask.cpp
status102 730b1733d0 Revert "feat: 保全作战保留core不过牌且放置core后回到待部署则重放工具人和core" (#10300)
* Revert "feat: 保全作战保留core不过牌且放置core后回到待部署则重放工具人和core"

This reverts commit 970d2cfddc.

* Revert "feat: 保全作战相同location以json写的策略顺序执行"

This reverts commit 29db7fabd9.
2024-08-20 08:00:50 +08:00

318 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "SSSBattleProcessTask.h"
#include "Config/GeneralConfig.h"
#include "Config/Miscellaneous/SSSCopilotConfig.h"
#include "Config/TaskData.h"
#include "Controller/Controller.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
using namespace asst::battle;
using namespace asst::battle::sss;
bool asst::SSSBattleProcessTask::set_stage_name(const std::string& stage_name)
{
Log.info(__FUNCTION__, stage_name);
if (!SSSCopilot.contains(stage_name)) {
Log.error("SSS SSSBattleProcessTask: unknown name", stage_name);
return false;
}
m_sss_combat_data = SSSCopilot.get_data(stage_name);
ranges::transform(m_sss_combat_data.strategies, std::inserter(m_all_cores, m_all_cores.begin()),
[](const auto& strategy) { return strategy.core; });
for (const auto& action : m_sss_combat_data.actions) {
if (action.type == battle::copilot::ActionType::Deploy) {
m_all_action_opers.emplace(action.name);
}
}
if (!BattleHelper::set_stage_name(m_sss_combat_data.info.stage_name)) {
json::value info = basic_info_with_what("UnsupportedLevel");
auto& details = info["details"];
details["level"] = m_sss_combat_data.info.stage_name;
callback(AsstMsg::SubTaskExtraInfo, info);
return false;
}
return true;
}
bool asst::SSSBattleProcessTask::update_deployment_with_skip(const cv::Mat& reusable)
{
const auto now = std::chrono::steady_clock::now();
const std::vector<DeploymentOper> old_deployment_opers = m_cur_deployment_opers;
static auto last_same_time = now;
static auto last_skip_time = now;
static auto interval_time = 0;
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_skip_time).count() < interval_time) {
Log.trace("Passed without update deployment");
sleep(interval_time);
return true;
}
last_skip_time = now;
if (!update_deployment(false, reusable)) {
return false;
}
if (ranges::equal(
m_cur_deployment_opers, old_deployment_opers,
[](const DeploymentOper& oper1, const DeploymentOper& oper2) { return oper1.name == oper2.name; })) {
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_same_time).count() > 30000) {
// 30s 能回 60 费,基本上已经到了挂机的时候,放缓检查的速度
Log.trace("30s is unchanged and the waiting time is extended to 1s");
interval_time = 1000;
}
}
else {
last_same_time = now;
Log.trace("Changed, the waiting time is reset to 0s");
interval_time = 0;
}
return true;
}
bool asst::SSSBattleProcessTask::do_derived_action(const battle::copilot::Action& action, size_t index)
{
LogTraceFunction;
std::ignore = index;
switch (action.type) {
case battle::copilot::ActionType::DrawCard:
return draw_card() && update_deployment();
case battle::copilot::ActionType::CheckIfStartOver:
return check_if_start_over(action);
default:
Log.error("unknown action type", static_cast<int>(action.type));
return false;
}
}
bool asst::SSSBattleProcessTask::do_strategic_action(const cv::Mat& reusable)
{
LogTraceFunction;
thread_local auto prev_frame_time = std::chrono::steady_clock::time_point {};
static const auto min_frame_interval = std::chrono::milliseconds(Config.get_options().sss_fight_screencap_interval);
// prevent our program from consuming too much CPU
if (const auto now = std::chrono::steady_clock::now();
prev_frame_time > now - min_frame_interval) [[unlikely]] {
Log.debug("Sleeping for framerate limit");
std::this_thread::sleep_for(min_frame_interval - (now - prev_frame_time));
}
cv::Mat image = reusable.empty() ? ctrler()->get_image() : reusable;
prev_frame_time = std::chrono::steady_clock::now();
if (check_and_get_drops(image)) {
sleep(1000);
return true;
}
if (check_and_do_strategy(image)) {
image = ctrler()->get_image();
}
if (use_all_ready_skill(image)) {
// image = ctrler()->get_image();
}
if (m_sss_combat_data.draw_as_possible && draw_card(false, image)) {
// image = ctrler()->get_image();
}
return true;
}
bool asst::SSSBattleProcessTask::wait_until_start(bool weak)
{
LogTraceFunction;
if (!ProcessTask(*this, { "SSSFightStart-PreSelect-Match" }).set_retry_times(300).run()) {
return false;
}
update_deployment();
ProcessTask(*this, { "SSSFightStart-PreSelect-Clear" }).run();
int replace_count; // 替换干员数量,装置不计数
int replace_limit = 4; // 替换数量限制最多替换4个
int cost_limit = 29; // 费用阈值,低于该费用的干员不替换
if (m_all_action_opers.contains("超重绝缘水泥")) {
replace_count = 1; // 只换水泥+1个以防换出水泥
}
else {
replace_count = 4;
if (ranges::count_if(m_cur_deployment_opers,
[](const auto& oper) { return oper.role == Role::Pioneer; }) /* 先锋数量 */
< 2) {
cost_limit = 25; // 先锋低于2个时降低费用阈值以试图换出先锋
}
}
for (const auto& oper : m_cur_deployment_opers | views::reverse) {
if (replace_limit <= 0 || oper.cost < cost_limit) {
break;
}
if (oper.role == Role::Drone) {
// 直接抛弃水泥
ctrler()->click(oper.rect);
Log.info(__FUNCTION__, "replace Drone, name:", oper.name);
--replace_limit;
}
else if (replace_count > 0 && !m_all_cores.contains(oper.name)) {
ctrler()->click(oper.rect);
Log.info(__FUNCTION__, "replace oper, name:", oper.name);
--replace_count;
--replace_limit;
}
}
return ProcessTask(*this, { "SSSFightStart-PreSelect", "SSSFightStart-PreSelect-Confirm" }).run() &&
BattleProcessTask::wait_until_start(weak);
}
bool asst::SSSBattleProcessTask::check_and_do_strategy(const cv::Mat& reusable)
{
if (m_all_cores.empty()) {
return false;
}
cv::Mat image = reusable.empty() ? ctrler()->get_image() : reusable;
if (!update_deployment_with_skip(image)) {
return false;
}
std::unordered_map<std::string, DeploymentOper> exist_core;
std::vector<DeploymentOper> tool_men;
for (const auto& oper : m_cur_deployment_opers) {
if (m_all_cores.contains(oper.name)) {
exist_core.emplace(oper.name, oper);
}
else if (oper.is_unusual_location && !m_all_action_opers.contains(oper.name)) {
tool_men.emplace_back(oper);
// 工具人的技能一概好了就用
m_skill_usage.try_emplace(oper.name, SkillUsage::Possibly);
}
}
for (auto& strategy : m_sss_combat_data.strategies) {
bool use_the_core = ranges::all_of(strategy.tool_men, [](const auto& pair) { return pair.second <= 0; }) &&
!strategy.core.empty() && exist_core.contains(strategy.core);
if (use_the_core) {
const auto& core = exist_core.at(strategy.core);
if (!core.available) {
// 直接返回,等费用,等下次循环处理部署逻辑
break;
}
m_all_cores.erase(strategy.core);
// 部署完,画面会发生变化,所以直接返回,后续逻辑交给下次循环处理
return deploy_oper(strategy.core, strategy.location, strategy.direction) && update_deployment();
}
bool skip = false;
for (auto& [role, quantity] : strategy.tool_men) {
if (quantity <= 0) {
continue;
}
// for apple-clang build error
Role role_for_lambda = role;
// 如果有可用的干员,直接使用
auto available_iter = ranges::find_if(
tool_men, [&](const DeploymentOper& oper) { return oper.available && oper.role == role_for_lambda; });
if (available_iter != tool_men.cend()) {
--quantity;
// 部署完,画面会发生变化,所以直接返回,后续逻辑交给下次循环处理
return deploy_oper(available_iter->name, strategy.location, strategy.direction) && update_deployment();
}
auto not_available_iter =
ranges::find_if(tool_men, [&](const DeploymentOper& oper) { return oper.role == role_for_lambda; });
if (not_available_iter == tool_men.cend()) {
continue;
}
// 如果有对应职业干员,但费用没转好,就等他转好,而不是部署下一个策略中的 tool_men
// 直接返回出去,后续逻辑交给下次循环处理
skip = true;
break;
}
if (skip) {
break;
}
}
return false;
}
bool asst::SSSBattleProcessTask::check_if_start_over(const battle::copilot::Action& action)
{
LogTraceFunction;
update_deployment();
bool to_abandon = false;
if (!action.name.empty() &&
!ranges::any_of(m_cur_deployment_opers, [&](const auto& oper) { return oper.name == action.name; }) &&
!m_battlefield_opers.contains(action.name)) {
to_abandon = true;
}
else if (!action.role_counts.empty()) {
std::unordered_map<Role, size_t> cur_counts;
for (const auto& oper : m_cur_deployment_opers) {
cur_counts[oper.role] += 1;
}
for (const auto& [role, number] : action.role_counts) {
if (cur_counts[role] < static_cast<size_t>(number)) {
to_abandon = true;
break;
}
}
}
if (to_abandon) {
abandon();
}
return true;
}
bool asst::SSSBattleProcessTask::draw_card(bool with_retry, const cv::Mat& reusable)
{
cv::Mat image = reusable.empty() ? ctrler()->get_image() : reusable;
ProcessTask task(*this, { "SSSDrawCard" });
if (!with_retry) {
task.set_task_delay(0).set_retry_times(0);
}
task.set_reusable_image(image);
return task.run();
}
bool asst::SSSBattleProcessTask::check_and_get_drops(const cv::Mat& reusable)
{
cv::Mat image = reusable.empty() ? ctrler()->get_image() : reusable;
if (!ProcessTask(*this, { "SSSHalfTimeDropsBegin" })
.set_reusable_image(image)
.set_times_limit("SSSHalfTimeDropsBegin", 0)
.set_task_delay(0)
.set_retry_times(0)
.run()) {
return false;
}
const auto& drops = m_sss_combat_data.order_of_drops;
std::string task_name;
if (drops.empty()) {
task_name = inst_string() + "@SSSHalfTimeDropsCancel";
}
else {
Task.get<OcrTaskInfo>(inst_string() + "@SSSHalfTimeDrops")->text = { drops };
task_name = inst_string() + "@SSSHalfTimeDropsBegin";
}
Log.info("Get drops", drops);
return ProcessTask(*this, { task_name }).set_reusable_image(image).set_retry_times(3).run();
}