feat: 分离不同肉鸽的招募、购物配置 (#2562)

This commit is contained in:
MistEO
2022-11-02 21:30:51 +08:00
committed by GitHub
parent d07b5e8b05
commit f989f5d84d
12 changed files with 3542 additions and 1797 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -54,7 +54,6 @@ std::string asst::RoguelikeSkillSelectionImageAnalyzer::name_analyze(const Rect&
analyzer.set_task_info(name_task_ptr);
analyzer.set_image(m_image);
analyzer.set_roi(roi.move(name_task_ptr->roi));
analyzer.set_required(RoguelikeRecruit.get_oper_order());
analyzer.set_replace(std::dynamic_pointer_cast<OcrTaskInfo>(Task.get("CharsNameOcrReplace"))->replace_map);
if (!analyzer.analyze()) {

View File

@@ -4,10 +4,11 @@
#include <meojson/json.hpp>
const asst::RoguelikeOperInfo& asst::RoguelikeRecruitConfiger::get_oper_info(const std::string& name) const noexcept
const asst::RoguelikeOperInfo& asst::RoguelikeRecruitConfiger::get_oper_info(const std::string& theme,
const std::string& name) const noexcept
{
if (auto find_iter = m_all_opers.find(name); find_iter != m_all_opers.cend()) {
auto& opers = m_all_opers.at(theme);
if (auto find_iter = opers.find(name); find_iter != opers.cend()) {
return find_iter->second;
}
else {
@@ -16,10 +17,14 @@ const asst::RoguelikeOperInfo& asst::RoguelikeRecruitConfiger::get_oper_info(con
}
}
const std::vector<std::pair<int, int>> asst::RoguelikeRecruitConfiger::get_role_info(const BattleRole& role) const noexcept
const std::vector<std::pair<int, int>> asst::RoguelikeRecruitConfiger::get_role_info(
const std::string& theme, const BattleRole& role) const noexcept
{
if (role == BattleRole::Unknown) return std::vector<std::pair<int, int>>();
if (auto iter = m_role_offset_map.find(role); iter != m_role_offset_map.end()) {
if (role == BattleRole::Unknown) {
return std::vector<std::pair<int, int>>();
}
auto& map = m_role_offset_map.at(theme);
if (auto iter = map.find(role); iter != map.end()) {
return iter->second;
}
return std::vector<std::pair<int, int>>();
@@ -29,42 +34,46 @@ bool asst::RoguelikeRecruitConfiger::parse(const json::value& json)
{
clear();
for (const auto& role_name : json.at("roles").as_array()) {
std::string str_role = role_name.as_string();
const auto& role_json = json.at(str_role);
std::vector<std::pair<int, int>> role_offset;
if (auto opt = role_json.find<json::array>("role_priority_offset")) {
for (const auto& offset : opt.value()) {
std::pair<int, int> offset_pair = std::make_pair(offset[0].as_integer(), offset[1].as_integer());
role_offset.emplace_back(offset_pair);
}
}
m_role_offset_map.emplace(get_role_type(str_role), std::move(role_offset));
for (const auto& oper_info : role_json.at("opers").as_array()) {
std::string name = oper_info.at("name").as_string();
RoguelikeOperInfo info;
info.name = name;
info.recruit_priority = oper_info.get("recruit_priority", 0);
info.promote_priority = oper_info.get("promote_priority", 0);
info.recruit_priority_when_team_full =
oper_info.get("recruit_priority_when_team_full", info.recruit_priority - 100);
info.promote_priority_when_team_full =
oper_info.get("promote_priority_when_team_full", info.promote_priority + 100);
info.is_alternate = oper_info.get("is_alternate", false);
info.skill = oper_info.at("skill").as_integer();
info.alternate_skill = oper_info.get("alternate_skill", 0);
info.skill_usage = static_cast<BattleSkillUsage>(oper_info.get("skill_usage", 1));
info.alternate_skill_usage = static_cast<BattleSkillUsage>(oper_info.get("alternate_skill_usage", 1));
if (auto opt = oper_info.find<json::array>("recruit_priority_offset")) {
for (const auto& theme_view : { RoguelikePhantomThemeName, RoguelikeMizukiThemeName }) {
const std::string theme(theme_view);
const auto& theme_json = json.at(theme);
for (const auto& role_name : theme_json.at("roles").as_array()) {
std::string str_role = role_name.as_string();
const auto& role_json = theme_json.at(str_role);
std::vector<std::pair<int, int>> role_offset;
if (auto opt = role_json.find<json::array>("role_priority_offset")) {
for (const auto& offset : opt.value()) {
std::pair<int, int> offset_pair = std::make_pair(offset[0].as_integer(), offset[1].as_integer());
info.recruit_priority_offset.emplace_back(offset_pair);
role_offset.emplace_back(offset_pair);
}
}
info.offset_melee = oper_info.get("offset_melee", false);
m_role_offset_map[theme].emplace(get_role_type(str_role), std::move(role_offset));
for (const auto& oper_info : role_json.at("opers").as_array()) {
std::string name = oper_info.at("name").as_string();
RoguelikeOperInfo info;
info.name = name;
info.recruit_priority = oper_info.get("recruit_priority", 0);
info.promote_priority = oper_info.get("promote_priority", 0);
info.recruit_priority_when_team_full =
oper_info.get("recruit_priority_when_team_full", info.recruit_priority - 100);
info.promote_priority_when_team_full =
oper_info.get("promote_priority_when_team_full", info.promote_priority + 100);
info.is_alternate = oper_info.get("is_alternate", false);
info.skill = oper_info.at("skill").as_integer();
info.alternate_skill = oper_info.get("alternate_skill", 0);
info.skill_usage = static_cast<BattleSkillUsage>(oper_info.get("skill_usage", 1));
info.alternate_skill_usage = static_cast<BattleSkillUsage>(oper_info.get("alternate_skill_usage", 1));
if (auto opt = oper_info.find<json::array>("recruit_priority_offset")) {
for (const auto& offset : opt.value()) {
std::pair<int, int> offset_pair =
std::make_pair(offset[0].as_integer(), offset[1].as_integer());
info.recruit_priority_offset.emplace_back(offset_pair);
}
}
info.offset_melee = oper_info.get("offset_melee", false);
m_all_opers.emplace(name, std::move(info));
m_ordered_all_opers_name.emplace_back(name);
m_all_opers[theme].emplace(name, std::move(info));
}
}
}
@@ -74,5 +83,5 @@ bool asst::RoguelikeRecruitConfiger::parse(const json::value& json)
void asst::RoguelikeRecruitConfiger::clear()
{
m_all_opers.clear();
m_ordered_all_opers_name.clear();
m_role_offset_map.clear();
}

View File

@@ -27,23 +27,24 @@ namespace asst
BattleSkillUsage skill_usage = BattleSkillUsage::Possibly;
BattleSkillUsage alternate_skill_usage = BattleSkillUsage::Possibly;
};
class RoguelikeRecruitConfiger final : public SingletonHolder<RoguelikeRecruitConfiger>, public AbstractConfiger
{
public:
virtual ~RoguelikeRecruitConfiger() override = default;
const RoguelikeOperInfo& get_oper_info(const std::string& name) const noexcept;
const std::vector<std::pair<int, int>> get_role_info(const BattleRole& role) const noexcept;
const auto& get_oper_order() const noexcept { return m_ordered_all_opers_name; }
const RoguelikeOperInfo& get_oper_info(const std::string& theme, const std::string& name) const noexcept;
const std::vector<std::pair<int, int>> get_role_info(const std::string& theme,
const BattleRole& role) const noexcept;
protected:
virtual bool parse(const json::value& json) override;
void clear();
std::unordered_map<std::string, RoguelikeOperInfo> m_all_opers;
std::unordered_map<BattleRole, std::vector<std::pair<int, int>>> m_role_offset_map;
std::vector<std::string> m_ordered_all_opers_name;
std::unordered_map<std::string, std::unordered_map<std::string, RoguelikeOperInfo>> m_all_opers;
std::unordered_map<std::string, std::unordered_map<BattleRole, std::vector<std::pair<int, int>>>>
m_role_offset_map;
};
inline static auto& RoguelikeRecruit = RoguelikeRecruitConfiger::get_instance();

View File

@@ -6,37 +6,41 @@ bool asst::RoguelikeShoppingConfiger::parse(const json::value& json)
{
clear();
for (const auto& goods_json : json.as_array()) {
std::string name = goods_json.at("name").as_string();
for (const auto& theme_view : { RoguelikePhantomThemeName, RoguelikeMizukiThemeName }) {
const std::string theme(theme_view);
const auto& theme_json = json.at(theme);
for (const auto& goods_json : theme_json.as_array()) {
std::string name = goods_json.at("name").as_string();
std::vector<BattleRole> roles;
if (auto roles_opt = goods_json.find<json::array>("roles")) {
for (const auto& role_json : roles_opt.value()) {
static const std::unordered_map<std::string, BattleRole> RoleMap = {
{ "CASTER", BattleRole::Caster }, { "MEDIC", BattleRole::Medic },
{ "PIONEER", BattleRole::Pioneer }, { "SNIPER", BattleRole::Sniper },
{ "SPECIAL", BattleRole::Special }, { "SUPPORT", BattleRole::Support },
{ "TANK", BattleRole::Tank }, { "WARRIOR", BattleRole::Warrior },
};
roles.emplace_back(RoleMap.at(role_json.as_string()));
std::vector<BattleRole> roles;
if (auto roles_opt = goods_json.find<json::array>("roles")) {
for (const auto& role_json : roles_opt.value()) {
static const std::unordered_map<std::string, BattleRole> RoleMap = {
{ "CASTER", BattleRole::Caster }, { "MEDIC", BattleRole::Medic },
{ "PIONEER", BattleRole::Pioneer }, { "SNIPER", BattleRole::Sniper },
{ "SPECIAL", BattleRole::Special }, { "SUPPORT", BattleRole::Support },
{ "TANK", BattleRole::Tank }, { "WARRIOR", BattleRole::Warrior },
};
roles.emplace_back(RoleMap.at(role_json.as_string()));
}
}
}
std::vector<std::string> chars;
if (auto chars_opt = goods_json.find<json::array>("chars")) {
for (const auto& char_json : chars_opt.value()) {
chars.emplace_back(char_json.as_string());
std::vector<std::string> chars;
if (auto chars_opt = goods_json.find<json::array>("chars")) {
for (const auto& char_json : chars_opt.value()) {
chars.emplace_back(char_json.as_string());
}
}
RoguelikeGoods goods;
goods.name = std::move(name);
goods.roles = std::move(roles);
goods.chars = std::move(chars);
goods.promotion = goods_json.get("promotion", 0);
goods.no_longer_buy = goods_json.get("no_longer_buy", false);
goods.ignore_no_longer_buy = goods_json.get("ignore_no_longer_buy", false);
m_goods.at(theme).emplace_back(std::move(goods));
}
RoguelikeGoods goods;
goods.name = std::move(name);
goods.roles = std::move(roles);
goods.chars = std::move(chars);
goods.promotion = goods_json.get("promotion", 0);
goods.no_longer_buy = goods_json.get("no_longer_buy", false);
goods.ignore_no_longer_buy = goods_json.get("ignore_no_longer_buy", false);
m_goods.emplace_back(std::move(goods));
}
return true;
}

View File

@@ -23,14 +23,14 @@ namespace asst
public:
virtual ~RoguelikeShoppingConfiger() override = default;
const auto& get_goods() const noexcept { return m_goods; }
const auto& get_goods(const std::string& theme) const noexcept {return m_goods.at(theme); }
private:
virtual bool parse(const json::value& json) override;
void clear();
std::vector<RoguelikeGoods> m_goods;
std::unordered_map<std::string, std::vector<RoguelikeGoods>> m_goods;
};
inline static auto& RoguelikeShopping = RoguelikeShoppingConfiger::get_instance();

View File

@@ -132,7 +132,8 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
}
// 查询招募配置
auto& recruit_info = RoguelikeRecruit.get_oper_info(oper_info.name);
auto& recruit_info = RoguelikeRecruit.get_oper_info(
m_status->get_properties(RuntimeStatus::RoguelikeTheme).value(), oper_info.name);
if (recruit_info.name.empty()) {
continue;
}
@@ -184,7 +185,8 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
}
}
role_num = team_roles[oper_role];
const auto role_info = RoguelikeRecruit.get_role_info(oper_role);
const auto role_info = RoguelikeRecruit.get_role_info(
m_status->get_properties(RuntimeStatus::RoguelikeTheme).value(), oper_role);
for (const auto& offset_pair : ranges::reverse_view(role_info)) {
if (role_num >= offset_pair.first) {
priority += offset_pair.second;

View File

@@ -93,7 +93,8 @@ bool asst::RoguelikeShoppingTaskPlugin::_run()
const auto& result = analyzer.get_result();
bool bought = false;
for (const auto& goods : RoguelikeShopping.get_goods()) {
auto& all_goods = RoguelikeShopping.get_goods(m_status->get_properties(RuntimeStatus::RoguelikeTheme).value());
for (const auto& goods : all_goods) {
if (need_exit()) {
return false;
}
@@ -166,4 +167,4 @@ bool asst::RoguelikeShoppingTaskPlugin::_run()
}
return true;
}
}

View File

@@ -47,10 +47,12 @@ bool asst::RoguelikeSkillSelectionTaskPlugin::_run()
int delay = Task.get("RoguelikeSkillSelectionMove1")->post_delay;
bool has_rookie = false;
for (const auto& [name, skill_vec] : analyzer.get_result()) {
if (name.empty()) {
const auto& oper_info =
RoguelikeRecruit.get_oper_info(m_status->get_properties(RuntimeStatus::RoguelikeTheme).value(), name);
if (oper_info.name.empty()) {
Log.warn("Unknown oper", name);
continue;
}
const auto& oper_info = RoguelikeRecruit.get_oper_info(name);
if (oper_info.alternate_skill > 0) {
Log.info(__FUNCTION__, name, " select alternate skill:", oper_info.alternate_skill);
@@ -89,4 +91,4 @@ bool asst::RoguelikeSkillSelectionTaskPlugin::_run()
m_status->set_number(RuntimeStatus::RoguelikeTeamFullWithoutRookie, 0);
}
return true;
}
}

View File

@@ -12,6 +12,7 @@
#include "RuntimeStatus.h"
#include "Sub/ProcessTask.h"
#include "Utils/Logger.hpp"
#include "Utils/AsstBattleDef.h"
asst::RoguelikeTask::RoguelikeTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
@@ -38,21 +39,24 @@ asst::RoguelikeTask::RoguelikeTask(const AsstCallback& callback, void* callback_
bool asst::RoguelikeTask::set_params(const json::value& params)
{
std::string theme = params.get("theme", std::string(RoguelikePhantomThemeName));
if (theme != RoguelikePhantomThemeName && theme != RoguelikeMizukiThemeName) {
Log.error("Unknown roguelike theme", theme);
return false;
}
if (m_status == nullptr) {
m_roguelike_task_ptr->set_tasks({ "Stop" });
Log.error(__FUNCTION__, "m_status is null");
return false;
}
m_status->set_properties(RuntimeStatus::RoguelikeTheme, theme);
m_roguelike_task_ptr->set_tasks({ theme + "@Roguelike@Begin" });
// 0 - 刷蜡烛,尽可能稳定地打更多层数
// 1 - 刷源石锭,第一层投资完就退出
// 2 - 【弃用】两者兼顾,投资过后再退出,没有投资就继续往后打
// 3 - 尝试通关激进策略TODO
std::string theme = params.get("theme", "Phantom");
if (m_status == nullptr) {
m_roguelike_task_ptr->set_tasks({ "Stop" });
Log.error(__FUNCTION__, "| Cannot set roguelike name!");
return false;
}
m_status->set_properties(RuntimeStatus::RoguelikeTheme, theme);
theme += "@";
m_roguelike_task_ptr->set_tasks({ theme + "Roguelike@Begin" });
int mode = params.get("mode", 0);
switch (mode) {
case 0:
@@ -63,8 +67,9 @@ bool asst::RoguelikeTask::set_params(const json::value& params)
m_roguelike_task_ptr->set_times_limit("StageTraderLeaveConfirm", 0, ProcessTask::TimesLimitType::Post);
break;
case 2:
[[unlikely]]
m_debug_task_ptr->set_enable(true);
[[unlikely]] m_roguelike_task_ptr->set_times_limit("StageTraderInvestCancel", 0);
m_roguelike_task_ptr->set_times_limit("StageTraderInvestCancel", 0);
break;
default:
Log.error(__FUNCTION__, "| Unknown mode", mode);
@@ -72,7 +77,7 @@ bool asst::RoguelikeTask::set_params(const json::value& params)
}
int number_of_starts = params.get("starts_count", INT_MAX);
m_roguelike_task_ptr->set_times_limit(theme + "Roguelike@StartExplore", number_of_starts);
m_roguelike_task_ptr->set_times_limit(theme + "@Roguelike@StartExplore", number_of_starts);
bool investment_enabled = params.get("investment_enabled", true);
if (!investment_enabled) {

View File

@@ -222,4 +222,7 @@ namespace asst
};
using BattleAttackRange = std::vector<Point>;
static constexpr std::string_view RoguelikePhantomThemeName = "Phantom";
static constexpr std::string_view RoguelikeMizukiThemeName = "Mizuki";
} // namespace asst