fix: 修复肉鸽关卡识别受截图速度影响的问题

This commit is contained in:
MistEO
2024-05-04 16:14:37 +08:00
parent e36b185505
commit afbe5e2e03

View File

@@ -23,15 +23,20 @@
using namespace asst::battle; using namespace asst::battle;
using namespace asst::battle::roguelike; using namespace asst::battle::roguelike;
asst::RoguelikeBattleTaskPlugin::RoguelikeBattleTaskPlugin(const AsstCallback& callback, Assistant* inst, asst::RoguelikeBattleTaskPlugin::RoguelikeBattleTaskPlugin(
std::string_view task_chain, const AsstCallback& callback,
std::shared_ptr<RoguelikeConfig> roguelike_config_ptr) Assistant* inst,
: AbstractRoguelikeTaskPlugin(callback, inst, task_chain, roguelike_config_ptr), BattleHelper(inst) std::string_view task_chain,
{} std::shared_ptr<RoguelikeConfig> roguelike_config_ptr)
: AbstractRoguelikeTaskPlugin(callback, inst, task_chain, roguelike_config_ptr)
, BattleHelper(inst)
{
}
bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& details) const bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& details) const
{ {
if (msg != AsstMsg::SubTaskCompleted || details.get("subtask", std::string()) != "ProcessTask") { if (msg != AsstMsg::SubTaskCompleted
|| details.get("subtask", std::string()) != "ProcessTask") {
return false; return false;
} }
@@ -101,7 +106,8 @@ void asst::RoguelikeBattleTaskPlugin::wait_until_start_button_clicked()
.run(); .run();
} }
std::string asst::RoguelikeBattleTaskPlugin::oper_name_in_config(const battle::DeploymentOper& oper) const std::string
asst::RoguelikeBattleTaskPlugin::oper_name_in_config(const battle::DeploymentOper& oper) const
{ {
if (oper.role == Role::Warrior && oper.name == "阿米娅") { if (oper.role == Role::Warrior && oper.name == "阿米娅") {
return "阿米娅-WARRIOR"; // 在 BattleData.json 中有 return "阿米娅-WARRIOR"; // 在 BattleData.json 中有
@@ -123,8 +129,10 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
const auto stage_name_task_ptr = Task.get("BattleStageName"); const auto stage_name_task_ptr = Task.get("BattleStageName");
sleep(stage_name_task_ptr->pre_delay); sleep(stage_name_task_ptr->pre_delay);
constexpr int StageNameRetryTimes = 100; auto start = std::chrono::steady_clock::now();
for (int i = 0; i != StageNameRetryTimes; ++i) { constexpr auto kTimeout = std::chrono::seconds(20);
while (std::chrono::steady_clock::now() - start < kTimeout) {
if (need_exit()) { if (need_exit()) {
return false; return false;
} }
@@ -138,7 +146,10 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
continue; continue;
} }
static const std::vector<std::string> RoguelikeStageCode = { "ISW-NO", "ISW-DF", "ISW-DU", "ISW-SP", static const std::vector<std::string> RoguelikeStageCode = { "ISW-NO",
"ISW-DF",
"ISW-DU",
"ISW-SP",
std::string() }; std::string() };
TilePack::LevelKey stage_key; TilePack::LevelKey stage_key;
stage_key.name = text; stage_key.name = text;
@@ -176,9 +187,10 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
m_blacklist_location = opt->blacklist_location; m_blacklist_location = opt->blacklist_location;
m_role_order = opt->role_order; m_role_order = opt->role_order;
m_force_deploy_direction = opt->force_deploy_direction; m_force_deploy_direction = opt->force_deploy_direction;
m_force_air_defense = AirDefenseData { .stop_blocking_deploy_num = opt->stop_deploy_blocking_num, m_force_air_defense =
.deploy_air_defense_num = opt->force_deploy_air_defense_num, AirDefenseData { .stop_blocking_deploy_num = opt->stop_deploy_blocking_num,
.ban_medic = opt->force_ban_medic }; .deploy_air_defense_num = opt->force_deploy_air_defense_num,
.ban_medic = opt->force_ban_medic };
m_deploy_plan = opt->deploy_plan; m_deploy_plan = opt->deploy_plan;
m_retreat_plan = opt->retreat_plan; m_retreat_plan = opt->retreat_plan;
} }
@@ -197,9 +209,10 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
} }
else { else {
auto homes_pos = m_homes | views::transform(&ReplacementHome::location); auto homes_pos = m_homes | views::transform(&ReplacementHome::location);
auto invalid_homes_pos = auto invalid_homes_pos = homes_pos | views::filter([&](const auto& home_pos) {
homes_pos | views::filter([&](const auto& home_pos) { return !m_normal_tile_info.contains(home_pos); }) | return !m_normal_tile_info.contains(home_pos);
views::transform(&Point::to_string); })
| views::transform(&Point::to_string);
if (!invalid_homes_pos.empty()) { if (!invalid_homes_pos.empty()) {
Log.error("No replacement homes point:", invalid_homes_pos); Log.error("No replacement homes point:", invalid_homes_pos);
} }
@@ -227,7 +240,8 @@ asst::battle::LocationType asst::RoguelikeBattleTaskPlugin::get_oper_location_ty
return BattleData.get_location_type(oper_name_in_config(oper)); return BattleData.get_location_type(oper_name_in_config(oper));
} }
asst::battle::OperPosition asst::RoguelikeBattleTaskPlugin::get_role_position(const battle::Role& role) const asst::battle::OperPosition
asst::RoguelikeBattleTaskPlugin::get_role_position(const battle::Role& role) const
{ {
switch (role) { switch (role) {
case battle::Role::Support: case battle::Role::Support:
@@ -241,7 +255,8 @@ asst::battle::OperPosition asst::RoguelikeBattleTaskPlugin::get_role_position(co
return battle::OperPosition::Blocking; return battle::OperPosition::Blocking;
break; break;
case battle::Role::Medic: case battle::Role::Medic:
return m_force_air_defense.ban_medic ? battle::OperPosition::None : battle::OperPosition::AirDefense; return m_force_air_defense.ban_medic ? battle::OperPosition::None
: battle::OperPosition::AirDefense;
break; break;
case battle::Role::Special: case battle::Role::Special:
case battle::Role::Drone: case battle::Role::Drone:
@@ -255,7 +270,9 @@ void asst::RoguelikeBattleTaskPlugin::cache_oper_elite_status()
{ {
const auto& oper_list = m_config->get_oper(); const auto& oper_list = m_config->get_oper();
for (const auto& oper : m_cur_deployment_opers) { for (const auto& oper : m_cur_deployment_opers) {
m_oper_elite.emplace(oper.name, oper_list.contains(oper.name) ? oper_list.at(oper.name).elite : 0); m_oper_elite.emplace(
oper.name,
oper_list.contains(oper.name) ? oper_list.at(oper.name).elite : 0);
} }
} }
@@ -286,7 +303,9 @@ void asst::RoguelikeBattleTaskPlugin::set_position_full(const Point& loc, bool f
} }
} }
void asst::RoguelikeBattleTaskPlugin::set_position_full(const battle::DeploymentOper& oper, bool full) void asst::RoguelikeBattleTaskPlugin::set_position_full(
const battle::DeploymentOper& oper,
bool full)
{ {
set_position_full(get_oper_location_type(oper), full); set_position_full(get_oper_location_type(oper), full);
} }
@@ -349,7 +368,8 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
// 获取招募信息 // 获取招募信息
const auto& recruit_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), oper_name); const auto& recruit_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), oper_name);
// 获取会用到该干员的干员组[干员组1序号,干员组2序号,...] // 获取会用到该干员的干员组[干员组1序号,干员组2序号,...]
std::vector<int> group_ids = RoguelikeRecruit.get_group_id(m_config->get_theme(), oper_name); std::vector<int> group_ids =
RoguelikeRecruit.get_group_id(m_config->get_theme(), oper_name);
for (const auto& group_id : group_ids) { for (const auto& group_id : group_ids) {
// 当前干员组名,string类型 // 当前干员组名,string类型
@@ -358,7 +378,12 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
if (m_deploy_plan.contains(group_name)) { if (m_deploy_plan.contains(group_name)) {
for (const auto& info : m_deploy_plan[group_name]) { for (const auto& info : m_deploy_plan[group_name]) {
if (m_kills < info.kill_lower_bound || m_kills > info.kill_upper_bound) { if (m_kills < info.kill_lower_bound || m_kills > info.kill_upper_bound) {
Log.debug(" deploy info", oper.name, "in group", group_name, "is waiting."); Log.debug(
" deploy info",
oper.name,
"in group",
group_name,
"is waiting.");
is_success = true; // 如果发现了待命干员此函数最终返回true is_success = true; // 如果发现了待命干员此函数最终返回true
continue; continue;
} }
@@ -369,13 +394,22 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
// deploy_plan.oper_order_in_group = recruit_info.order_in_group.at(group_id); // deploy_plan.oper_order_in_group = recruit_info.order_in_group.at(group_id);
deploy_plan.oper_order_in_group = deploy_plan.oper_order_in_group =
recruit_info.order_in_group.empty() ? INT_MAX : recruit_info.order_in_group.at(group_id); recruit_info.order_in_group.empty()
? INT_MAX
: recruit_info.order_in_group.at(group_id);
deploy_plan.rank = info.rank; deploy_plan.rank = info.rank;
deploy_plan.placed = info.location; deploy_plan.placed = info.location;
deploy_plan.direction = info.direction; deploy_plan.direction = info.direction;
deploy_plan_list.emplace_back(deploy_plan); deploy_plan_list.emplace_back(deploy_plan);
Log.debug(" deploy info", deploy_plan.oper_name, "is No. ", deploy_plan.oper_order_in_group + 1, Log.debug(
"in group", group_name, ", with deploy command rank", deploy_plan.rank); " deploy info",
deploy_plan.oper_name,
"is No. ",
deploy_plan.oper_order_in_group + 1,
"in group",
group_name,
", with deploy command rank",
deploy_plan.rank);
} }
} }
else { else {
@@ -385,14 +419,18 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
} }
std::sort(deploy_plan_list.begin(), deploy_plan_list.end()); std::sort(deploy_plan_list.begin(), deploy_plan_list.end());
for (const auto& deploy_plan : deploy_plan_list) { for (const auto& deploy_plan : deploy_plan_list) {
if (!m_used_tiles.contains(deploy_plan.placed) && if (!m_used_tiles.contains(deploy_plan.placed)
!m_blacklist_location.contains(deploy_plan.placed) /* 判断该位置是否已被占据 */) { && !m_blacklist_location.contains(deploy_plan.placed) /* 判断该位置是否已被占据 */) {
if (auto oper_it = ranges::find_if(
if (auto oper_it = ranges::find_if(m_cur_deployment_opers, m_cur_deployment_opers,
[&](const auto& oper) { return oper.name == deploy_plan.oper_name; }); [&](const auto& oper) { return oper.name == deploy_plan.oper_name; });
oper_it == m_cur_deployment_opers.end() || !oper_it->available) { // 等费 oper_it == m_cur_deployment_opers.end() || !oper_it->available) { // 等费
Log.trace(" best deploy is", deploy_plan.oper_name, "with rank", deploy_plan.rank, Log.trace(
"but now waiting for cost."); " best deploy is",
deploy_plan.oper_name,
"with rank",
deploy_plan.rank,
"but now waiting for cost.");
return true; return true;
} }
deploy_oper(deploy_plan.oper_name, deploy_plan.placed, deploy_plan.direction); deploy_oper(deploy_plan.oper_name, deploy_plan.placed, deploy_plan.direction);
@@ -400,7 +438,8 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
auto deployed_time = std::chrono::steady_clock::now(); auto deployed_time = std::chrono::steady_clock::now();
m_deployed_time.insert_or_assign(deploy_plan.oper_name, deployed_time); m_deployed_time.insert_or_assign(deploy_plan.oper_name, deployed_time);
// 获取技能用法和使用次数 // 获取技能用法和使用次数
const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), deploy_plan.oper_name); const auto& oper_info =
RoguelikeRecruit.get_oper_info(m_config->get_theme(), deploy_plan.oper_name);
m_skill_usage[deploy_plan.oper_name] = oper_info.skill_usage; m_skill_usage[deploy_plan.oper_name] = oper_info.skill_usage;
m_skill_times[deploy_plan.oper_name] = oper_info.skill_times; m_skill_times[deploy_plan.oper_name] = oper_info.skill_times;
Log.trace(" best deploy is", deploy_plan.oper_name, "with rank", deploy_plan.rank); Log.trace(" best deploy is", deploy_plan.oper_name, "with rank", deploy_plan.rank);
@@ -432,8 +471,8 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), name); const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), name);
auto iter = m_deployed_time.find(name); auto iter = m_deployed_time.find(name);
if (iter != m_deployed_time.end() && oper_info.auto_retreat > 0) { if (iter != m_deployed_time.end() && oper_info.auto_retreat > 0) {
if (std::chrono::steady_clock::now() - m_deployed_time.at(name) >= if (std::chrono::steady_clock::now() - m_deployed_time.at(name)
oper_info.auto_retreat * std::chrono::seconds(1)) { >= oper_info.auto_retreat * std::chrono::seconds(1)) {
// 时间到了就撤退 // 时间到了就撤退
asst::BattleHelper::retreat_oper(name); asst::BattleHelper::retreat_oper(name);
m_deployed_time.erase(name); m_deployed_time.erase(name);
@@ -452,14 +491,18 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
size_t cur_available_count = 0; // without drones size_t cur_available_count = 0; // without drones
size_t cur_deployments_count = 0; // without drones size_t cur_deployments_count = 0; // without drones
for (const auto& oper : m_cur_deployment_opers) { for (const auto& oper : m_cur_deployment_opers) {
if (oper.role == Role::Drone) continue; if (oper.role == Role::Drone) {
continue;
}
++cur_deployments_count; ++cur_deployments_count;
if (oper.cooling) { if (oper.cooling) {
cur_cooling.emplace(oper.name); cur_cooling.emplace(oper.name);
m_deployed_time.erase(oper.name); m_deployed_time.erase(oper.name);
} }
if (oper.available) ++cur_available_count; if (oper.available) {
++cur_available_count;
}
} }
if (do_best_deploy()) { // 这是新的部署逻辑,更加精确 if (do_best_deploy()) { // 这是新的部署逻辑,更加精确
@@ -489,8 +532,9 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
m_cur_home_index = *urgent_home_opt; m_cur_home_index = *urgent_home_opt;
if (m_allow_to_use_dice) { if (m_allow_to_use_dice) {
auto dice_key_iter = ranges::find_if(m_cur_deployment_opers, auto dice_key_iter = ranges::find_if(m_cur_deployment_opers, [&](const auto& oper) {
[&](const auto& oper) { return DiceSet.contains(oper.name); }); return DiceSet.contains(oper.name);
});
if (dice_key_iter != m_cur_deployment_opers.end()) { if (dice_key_iter != m_cur_deployment_opers.end()) {
best_oper_is_dice = true; best_oper_is_dice = true;
best_oper = *dice_key_iter; best_oper = *dice_key_iter;
@@ -526,7 +570,8 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
auto deployed_time = std::chrono::steady_clock::now(); auto deployed_time = std::chrono::steady_clock::now();
m_deployed_time.insert_or_assign(best_oper.name, deployed_time); m_deployed_time.insert_or_assign(best_oper.name, deployed_time);
// 获取技能用法和使用次数 // 获取技能用法和使用次数
const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), best_oper.name); const auto& oper_info =
RoguelikeRecruit.get_oper_info(m_config->get_theme(), best_oper.name);
m_skill_usage[best_oper.name] = oper_info.skill_usage; m_skill_usage[best_oper.name] = oper_info.skill_usage;
m_skill_times[best_oper.name] = oper_info.skill_times; m_skill_times[best_oper.name] = oper_info.skill_times;
@@ -543,16 +588,18 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
return true; return true;
} }
void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(const battle::DeploymentOper& oper, void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(
const Point& placed_loc, const battle::DeploymentOper& oper,
battle::DeployDirection direction) const Point& placed_loc,
battle::DeployDirection direction)
{ {
// 如果是医疗干员,判断覆盖范围内有无第一次放置的干员 // 如果是医疗干员,判断覆盖范围内有无第一次放置的干员
if (oper.role == battle::Role::Medic) { if (oper.role == battle::Role::Medic) {
std::vector<size_t> contain_index; std::vector<size_t> contain_index;
for (const Point& relative_pos : get_attack_range(oper, direction)) { for (const Point& relative_pos : get_attack_range(oper, direction)) {
Point absolute_pos = placed_loc + relative_pos; Point absolute_pos = placed_loc + relative_pos;
if (auto iter = m_blocking_for_home_index.find(absolute_pos); iter != m_blocking_for_home_index.end()) { if (auto iter = m_blocking_for_home_index.find(absolute_pos);
iter != m_blocking_for_home_index.end()) {
m_homes_status[iter->second].wait_medic = false; m_homes_status[iter->second].wait_medic = false;
contain_index.emplace_back(iter->second); contain_index.emplace_back(iter->second);
} }
@@ -577,7 +624,8 @@ void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(const ba
case OperPosition::AirDefense: case OperPosition::AirDefense:
if (!m_force_air_defense.has_finished_deploy_air_defense) { if (!m_force_air_defense.has_finished_deploy_air_defense) {
m_force_air_defense.has_deployed_air_defense_num++; m_force_air_defense.has_deployed_air_defense_num++;
if (m_force_air_defense.has_deployed_air_defense_num >= m_force_air_defense.deploy_air_defense_num) { if (m_force_air_defense.has_deployed_air_defense_num
>= m_force_air_defense.deploy_air_defense_num) {
m_force_air_defense.has_finished_deploy_air_defense = true; m_force_air_defense.has_finished_deploy_air_defense = true;
Log.info("FORCE RANGED OPER DEPLOY END"); Log.info("FORCE RANGED OPER DEPLOY END");
} }
@@ -587,7 +635,8 @@ void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(const ba
break; break;
} }
if (m_force_air_defense.has_finished_deploy_air_defense && position != OperPosition::AirDefense) { if (m_force_air_defense.has_finished_deploy_air_defense
&& position != OperPosition::AirDefense) {
Log.info("FORCE RANGED OPER DEPLOY END"); Log.info("FORCE RANGED OPER DEPLOY END");
m_force_air_defense.has_finished_deploy_air_defense = true; m_force_air_defense.has_finished_deploy_air_defense = true;
} }
@@ -600,7 +649,12 @@ void asst::RoguelikeBattleTaskPlugin::check_drone_tiles()
while ((!m_need_clear_tiles.empty()) && m_need_clear_tiles.top().placed_time < now_time) { while ((!m_need_clear_tiles.empty()) && m_need_clear_tiles.top().placed_time < now_time) {
const auto& placed_loc = m_need_clear_tiles.top().placed_loc; const auto& placed_loc = m_need_clear_tiles.top().placed_loc;
if (auto iter = m_used_tiles.find(placed_loc); iter != m_used_tiles.end()) { if (auto iter = m_used_tiles.find(placed_loc); iter != m_used_tiles.end()) {
Log.info("Drone at location (", placed_loc.x, ",", placed_loc.y, ") is recognized as retreated"); Log.info(
"Drone at location (",
placed_loc.x,
",",
placed_loc.y,
") is recognized as retreated");
set_position_full(placed_loc, false); set_position_full(placed_loc, false);
m_battlefield_opers.erase(iter->second); m_battlefield_opers.erase(iter->second);
m_used_tiles.erase(iter); m_used_tiles.erase(iter);
@@ -609,9 +663,10 @@ void asst::RoguelikeBattleTaskPlugin::check_drone_tiles()
} }
} }
std::optional<size_t> asst::RoguelikeBattleTaskPlugin::check_urgent(const std::unordered_set<std::string>& pre_cooling, std::optional<size_t> asst::RoguelikeBattleTaskPlugin::check_urgent(
const std::unordered_set<std::string>& cur_cooling, const std::unordered_set<std::string>& pre_cooling,
const std::map<std::string, Point>& pre_battlefield) const std::unordered_set<std::string>& cur_cooling,
const std::map<std::string, Point>& pre_battlefield)
{ {
std::vector<size_t> new_urgent; std::vector<size_t> new_urgent;
for (const auto& name : cur_cooling) { for (const auto& name : cur_cooling) {
@@ -693,19 +748,31 @@ std::optional<asst::battle::DeploymentOper> asst::RoguelikeBattleTaskPlugin::cal
} }
} }
bool use_air_defense = bool use_air_defense = has_air_defense && !m_force_air_defense.has_finished_deploy_air_defense
has_air_defense && !m_force_air_defense.has_finished_deploy_air_defense && && m_force_air_defense.has_deployed_blocking_num
m_force_air_defense.has_deployed_blocking_num >= m_force_air_defense.stop_blocking_deploy_num && !m_ranged_full; >= m_force_air_defense.stop_blocking_deploy_num
bool use_blocking = has_blocking && m_homes_status[m_cur_home_index].wait_blocking && !m_melee_full; && !m_ranged_full;
bool use_blocking =
has_blocking && m_homes_status[m_cur_home_index].wait_blocking && !m_melee_full;
bool use_medic = has_medic && m_homes_status[m_cur_home_index].wait_medic && !m_ranged_full; bool use_medic = has_medic && m_homes_status[m_cur_home_index].wait_medic && !m_ranged_full;
Log.trace("use_air_defense", use_air_defense, ", use_blocking", use_blocking, ", use_medic", use_medic); Log.trace(
"use_air_defense",
use_air_defense,
", use_blocking",
use_blocking,
", use_medic",
use_medic);
std::vector<DeploymentOper> cur_available; std::vector<DeploymentOper> cur_available;
for (const auto& oper : m_cur_deployment_opers) { for (const auto& oper : m_cur_deployment_opers) {
if (oper.available) cur_available.emplace_back(oper); if (oper.available) {
cur_available.emplace_back(oper);
}
} }
// 费用高的优先用,放前面 // 费用高的优先用,放前面
ranges::sort(cur_available, [](const auto& lhs, const auto& rhs) { return lhs.cost > rhs.cost; }); ranges::sort(cur_available, [](const auto& lhs, const auto& rhs) {
return lhs.cost > rhs.cost;
});
DeploymentOper best_oper; DeploymentOper best_oper;
@@ -787,7 +854,8 @@ void asst::RoguelikeBattleTaskPlugin::clear()
m_oper_elite.clear(); m_oper_elite.clear();
} }
std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(const DeploymentOper& oper) const std::vector<asst::Point>
asst::RoguelikeBattleTaskPlugin::available_locations(const DeploymentOper& oper) const
{ {
auto type = get_oper_location_type(oper); auto type = get_oper_location_type(oper);
if (type == LocationType::Invalid || type == LocationType::None) { if (type == LocationType::Invalid || type == LocationType::None) {
@@ -796,15 +864,18 @@ std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(co
return available_locations(type); return available_locations(type);
} }
std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(battle::LocationType type) const std::vector<asst::Point>
asst::RoguelikeBattleTaskPlugin::available_locations(battle::LocationType type) const
{ {
std::vector<Point> result; std::vector<Point> result;
for (const auto& [loc, tile] : m_normal_tile_info) { for (const auto& [loc, tile] : m_normal_tile_info) {
bool position_matched = tile.buildable == type || tile.buildable == battle::LocationType::All; bool position_matched =
position_matched |= (type == battle::LocationType::All) && (tile.buildable == battle::LocationType::Melee || tile.buildable == type || tile.buildable == battle::LocationType::All;
tile.buildable == battle::LocationType::Ranged); position_matched |= (type == battle::LocationType::All)
if (position_matched && && (tile.buildable == battle::LocationType::Melee
tile.key != TilePack::TileKey::DeepSea && // 水上要先放板子才能放人,肉鸽里也没板子,那就当作不可放置 || tile.buildable == battle::LocationType::Ranged);
if (position_matched && tile.key != TilePack::TileKey::DeepSea
&& // 水上要先放板子才能放人,肉鸽里也没板子,那就当作不可放置
!m_used_tiles.contains(loc) && !m_blacklist_location.contains(loc)) { !m_used_tiles.contains(loc) && !m_blacklist_location.contains(loc)) {
result.emplace_back(loc); result.emplace_back(loc);
} }
@@ -812,8 +883,9 @@ std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(ba
return result; return result;
} }
asst::battle::AttackRange asst::RoguelikeBattleTaskPlugin::get_attack_range(const battle::DeploymentOper& oper, asst::battle::AttackRange asst::RoguelikeBattleTaskPlugin::get_attack_range(
DeployDirection direction) const const battle::DeploymentOper& oper,
DeployDirection direction) const
{ {
int64_t elite = 0; int64_t elite = 0;
if (m_oper_elite.contains(oper.name)) { if (m_oper_elite.contains(oper.name)) {
@@ -859,20 +931,25 @@ asst::battle::AttackRange asst::RoguelikeBattleTaskPlugin::get_attack_range(cons
} }
} }
for (auto cur_direction : for (auto cur_direction : { DeployDirection::Right,
{ DeployDirection::Right, DeployDirection::Up, DeployDirection::Left, DeployDirection::Down }) { DeployDirection::Up,
if (cur_direction == direction) break; DeployDirection::Left,
DeployDirection::Down }) {
if (cur_direction == direction) {
break;
}
// rotate relative attack range counterclockwise // rotate relative attack range counterclockwise
for (Point& point : right_attack_range) for (Point& point : right_attack_range) {
point = { point.y, -point.x }; point = { point.y, -point.x };
}
} }
return right_attack_range; return right_attack_range;
} }
std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo> asst::RoguelikeBattleTaskPlugin::calc_best_loc( std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo>
const battle::DeploymentOper& oper) const asst::RoguelikeBattleTaskPlugin::calc_best_loc(const battle::DeploymentOper& oper) const
{ {
size_t home_index = m_cur_home_index; size_t home_index = m_cur_home_index;
if (home_index >= m_homes.size()) { if (home_index >= m_homes.size()) {
@@ -911,10 +988,12 @@ std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo> asst::RoguelikeBattle
// 取距离最近的N个点计算分数。然后使用得分最高的点 // 取距离最近的N个点计算分数。然后使用得分最高的点
constexpr int CalcPointCount = 4; constexpr int CalcPointCount = 4;
for (const auto& loc : available_loc | views::take(CalcPointCount)) { for (const auto& loc : available_loc | views::take(CalcPointCount)) {
const auto& [cur_direction, cur_socre] = calc_best_direction_and_score(loc, oper, home.direction); const auto& [cur_direction, cur_socre] =
calc_best_direction_and_score(loc, oper, home.direction);
// 离得远的要扣分 // 离得远的要扣分
constexpr int DistWeights = -1050; constexpr int DistWeights = -1050;
int extra_dist = std::abs(loc.x - home.location.x) + std::abs(loc.y - home.location.y) - min_dist; int extra_dist =
std::abs(loc.x - home.location.x) + std::abs(loc.y - home.location.y) - min_dist;
int extra_dist_score = DistWeights * extra_dist; int extra_dist_score = DistWeights * extra_dist;
if (oper.role == battle::Role::Medic) { // 医疗干员离得远无所谓 if (oper.role == battle::Role::Medic) { // 医疗干员离得远无所谓
extra_dist_score = 0; extra_dist_score = 0;
@@ -928,7 +1007,8 @@ std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo> asst::RoguelikeBattle
} }
// 强制变化为确定的攻击方向 // 强制变化为确定的攻击方向
if (auto iter = m_force_deploy_direction.find(best_location); iter != m_force_deploy_direction.end()) { if (auto iter = m_force_deploy_direction.find(best_location);
iter != m_force_deploy_direction.end()) {
if (iter->second.role.contains(oper.role)) { if (iter->second.role.contains(oper.role)) {
best_direction = iter->second.direction; best_direction = iter->second.direction;
} }
@@ -937,8 +1017,11 @@ std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo> asst::RoguelikeBattle
return DeployInfo { best_location, best_direction }; return DeployInfo { best_location, best_direction };
} }
asst::RoguelikeBattleTaskPlugin::DirectionAndScore asst::RoguelikeBattleTaskPlugin::calc_best_direction_and_score( asst::RoguelikeBattleTaskPlugin::DirectionAndScore
Point loc, const battle::DeploymentOper& oper, DeployDirection recommended_direction) const asst::RoguelikeBattleTaskPlugin::calc_best_direction_and_score(
Point loc,
const battle::DeploymentOper& oper,
DeployDirection recommended_direction) const
{ {
LogTraceFunction; LogTraceFunction;
@@ -994,8 +1077,10 @@ asst::RoguelikeBattleTaskPlugin::DirectionAndScore asst::RoguelikeBattleTaskPlug
int max_score = 0; int max_score = 0;
DeployDirection best_direction = DeployDirection::None; DeployDirection best_direction = DeployDirection::None;
for (auto direction : for (auto direction : { DeployDirection::Right,
{ DeployDirection::Right, DeployDirection::Up, DeployDirection::Left, DeployDirection::Down }) { DeployDirection::Up,
DeployDirection::Left,
DeployDirection::Down }) {
int score = 0; int score = 0;
// 按朝右算,后面根据方向做转换 // 按朝右算,后面根据方向做转换
for (const Point& relative_pos : get_attack_range(oper, direction)) { for (const Point& relative_pos : get_attack_range(oper, direction)) {
@@ -1004,12 +1089,14 @@ asst::RoguelikeBattleTaskPlugin::DirectionAndScore asst::RoguelikeBattleTaskPlug
using TileKey = TilePack::TileKey; using TileKey = TilePack::TileKey;
// 战斗干员朝向的权重 // 战斗干员朝向的权重
static const std::unordered_map<TileKey, int> TileKeyFightWeights = { static const std::unordered_map<TileKey, int> TileKeyFightWeights = {
{ TileKey::Invalid, 0 }, { TileKey::Forbidden, 0 }, { TileKey::Wall, 500 }, { TileKey::Invalid, 0 }, { TileKey::Forbidden, 0 },
{ TileKey::Road, 1000 }, { TileKey::Home, 500 }, { TileKey::EnemyHome, 1000 }, { TileKey::Wall, 500 }, { TileKey::Road, 1000 },
{ TileKey::Airport, 1000 }, { TileKey::Floor, 1000 }, { TileKey::Hole, 0 }, { TileKey::Home, 500 }, { TileKey::EnemyHome, 1000 },
{ TileKey::Telin, 700 }, { TileKey::Telout, 800 }, { TileKey::Grass, 500 }, { TileKey::Airport, 1000 }, { TileKey::Floor, 1000 },
{ TileKey::DeepSea, 1000 }, { TileKey::Volcano, 1000 }, { TileKey::Healing, 1000 }, { TileKey::Hole, 0 }, { TileKey::Telin, 700 },
{ TileKey::Fence, 800 }, { TileKey::Telout, 800 }, { TileKey::Grass, 500 },
{ TileKey::DeepSea, 1000 }, { TileKey::Volcano, 1000 },
{ TileKey::Healing, 1000 }, { TileKey::Fence, 800 },
}; };
// 治疗干员朝向的权重 // 治疗干员朝向的权重
static const std::unordered_map<TileKey, int> TileKeyMedicWeights = { static const std::unordered_map<TileKey, int> TileKeyMedicWeights = {
@@ -1024,15 +1111,21 @@ asst::RoguelikeBattleTaskPlugin::DirectionAndScore asst::RoguelikeBattleTaskPlug
switch (oper.role) { switch (oper.role) {
case battle::Role::Medic: case battle::Role::Medic:
if (auto iter = m_used_tiles.find(absolute_pos); if (auto iter = m_used_tiles.find(absolute_pos);
iter != m_used_tiles.cend() && iter != m_used_tiles.cend()
BattleData.get_role(iter->second) != battle::Role::Drone) // 根据哪个方向上人多决定朝向哪 && BattleData.get_role(iter->second)
!= battle::Role::Drone) { // 根据哪个方向上人多决定朝向哪
score += 10000; score += 10000;
if (auto iter = m_side_tile_info.find(absolute_pos); iter != m_side_tile_info.end()) }
if (auto iter = m_side_tile_info.find(absolute_pos);
iter != m_side_tile_info.end()) {
score += TileKeyMedicWeights.at(iter->second.key); score += TileKeyMedicWeights.at(iter->second.key);
}
break; break;
default: default:
if (auto iter = m_side_tile_info.find(absolute_pos); iter != m_side_tile_info.end()) if (auto iter = m_side_tile_info.find(absolute_pos);
iter != m_side_tile_info.end()) {
score += TileKeyFightWeights.at(iter->second.key); score += TileKeyFightWeights.at(iter->second.key);
}
break; break;
} }
} }