feat: 自动战斗引入干员头像缓存机制

This commit is contained in:
MistEO
2022-12-23 23:44:34 +08:00
parent 35b93b765e
commit 5e08e1e138
13 changed files with 287 additions and 39 deletions

View File

@@ -30,7 +30,7 @@ namespace json
using array_ptr = std::unique_ptr<array>;
using object_ptr = std::unique_ptr<object>;
public:
enum class value_type : char
enum class value_type: char
{
Invalid,
Null,
@@ -87,6 +87,9 @@ namespace json
bool is_string() const noexcept { return _type == value_type::String; }
bool is_array() const noexcept { return _type == value_type::Array; }
bool is_object() const noexcept { return _type == value_type::Object; }
template<typename Type>
bool is() const noexcept;
bool contains(const std::string& key) const;
bool contains(size_t pos) const;
bool exists(const std::string& key) const { return contains(key); }
@@ -117,6 +120,8 @@ namespace json
const std::string as_string() const;
const array& as_array() const;
const object& as_object() const;
template<typename Type>
Type as() const;
array& as_array();
object& as_object();
@@ -394,11 +399,11 @@ namespace json
// *************************
// * exception declare *
// *************************
class exception : public std::exception
class exception: public std::exception
{
public:
exception() = default;
exception(const std::string& msg) : _what(msg) {}
exception(const std::string& msg): _what(msg) {}
exception(const exception&) = default;
exception& operator=(const exception&) = default;
@@ -525,6 +530,40 @@ namespace json
// for Pimpl
MEOJSON_INLINE value::~value() = default;
template<typename Type>
MEOJSON_INLINE bool value::is() const noexcept
{
if constexpr (std::is_same_v<Type, value>) {
return true;
}
else if constexpr (std::is_same_v<Type, bool>) {
return _type == value_type::Boolean;
}
else if constexpr (std::is_same_v<Type, int> ||
std::is_same_v<Type, unsigned> ||
std::is_same_v<Type, long> ||
std::is_same_v<Type, unsigned long> ||
std::is_same_v<Type, long long> ||
std::is_same_v<Type, unsigned long long> ||
std::is_same_v<Type, float> ||
std::is_same_v<Type, double> ||
std::is_same_v<Type, long double>) {
return _type == value_type::Number;
}
else if constexpr (std::is_same_v<Type, std::string>) {
return _type == value_type::String;
}
else if constexpr (std::is_same_v<Type, array>) {
return _type == value_type::Array;
}
else if constexpr (std::is_same_v<Type, object>) {
return _type == value_type::Object;
}
else {
static_assert(false, "Unsupported type");
}
}
MEOJSON_INLINE bool value::contains(const std::string& key) const
{
return is_object() && as_object().contains(key);
@@ -782,6 +821,12 @@ namespace json
throw exception("Wrong Type or data empty");
}
template<typename Type>
MEOJSON_INLINE Type value::as() const
{
return static_cast<Type>(*this);
}
MEOJSON_INLINE const std::string& value::as_basic_type_str() const
{
return std::get<std::string>(_raw_data);
@@ -1308,7 +1353,8 @@ namespace json
if (!contains(pos)) {
return std::nullopt;
}
return static_cast<Type>(_array_data.at(pos));
const auto& val = _array_data.at(pos);
return val.is<Type>() ? std::optional<Type>(val.as<Type>()) : std::nullopt;
}
MEOJSON_INLINE array::iterator array::begin() noexcept
@@ -1765,7 +1811,8 @@ namespace json
if (iter == _object_data.end()) {
return std::nullopt;
}
return static_cast<Type>(iter->second);
const auto& val = iter->second;
return val.is<Type>() ? std::optional<Type>(val.as<Type>()) : std::nullopt;
}
MEOJSON_INLINE object::iterator object::begin() noexcept

View File

@@ -20,7 +20,10 @@
"3-8",
"3-10"
],
"rarity": 6
"rarity": 6,
"tokens": [
"Mon3tr"
]
},
"char_009_12fce": {
"name": "12F",
@@ -86,7 +89,10 @@
"x-1",
"x-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"斯卡蒂的海嗣"
]
},
"char_1013_chen2": {
"name": "假日威龙陈",
@@ -108,7 +114,10 @@
"1-1",
"1-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"“耀阳”"
]
},
"char_101_sora": {
"name": "空",
@@ -251,7 +260,10 @@
"3-3",
"3-3"
],
"rarity": 5
"rarity": 5,
"tokens": [
"医疗探机"
]
},
"char_109_fmout": {
"name": "远山",
@@ -273,7 +285,10 @@
"3-1",
"3-1"
],
"rarity": 4
"rarity": 4,
"tokens": [
"触手"
]
},
"char_112_siege": {
"name": "推进之王",
@@ -295,7 +310,10 @@
"3-8",
"3-10"
],
"rarity": 6
"rarity": 6,
"tokens": [
"此面向敌"
]
},
"char_115_headbr": {
"name": "凛冬",
@@ -746,7 +764,10 @@
"y-2",
"y-2"
],
"rarity": 6
"rarity": 6,
"tokens": [
"幻影"
]
},
"char_180_amgoat": {
"name": "艾雅法拉",
@@ -933,7 +954,10 @@
"3-6",
"3-6"
],
"rarity": 6
"rarity": 6,
"tokens": [
"\"小自在\""
]
},
"char_201_moeshd": {
"name": "可颂",
@@ -955,7 +979,12 @@
"3-1",
"3-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"“清平”",
"“逍遥”",
"“弦惊”"
]
},
"char_202_demkni": {
"name": "塞雷娅",
@@ -1219,7 +1248,10 @@
"3-1",
"3-1"
],
"rarity": 5
"rarity": 5,
"tokens": [
"机械水獭"
]
},
"char_243_waaifu": {
"name": "槐琥",
@@ -1241,7 +1273,12 @@
"3-1",
"3-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"龙腾.F",
"龙腾.L",
"龙腾.A"
]
},
"char_250_phatom": {
"name": "傀影",
@@ -1252,7 +1289,10 @@
"1-1",
"1-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"镜中虚影"
]
},
"char_252_bibeak": {
"name": "柏喙",
@@ -1285,7 +1325,10 @@
"y-6",
"y-6"
],
"rarity": 5
"rarity": 5,
"tokens": [
"诅咒娃娃"
]
},
"char_258_podego": {
"name": "波登可",
@@ -1725,7 +1768,10 @@
"3-1",
"3-1"
],
"rarity": 5
"rarity": 5,
"tokens": [
"移动摄影器"
]
},
"char_337_utage": {
"name": "宴",
@@ -1780,7 +1826,10 @@
"x-1",
"x-1"
],
"rarity": 5
"rarity": 5,
"tokens": [
"沙之碑"
]
},
"char_345_folnic": {
"name": "亚叶",
@@ -2055,7 +2104,10 @@
"3-9",
"3-9"
],
"rarity": 6
"rarity": 6,
"tokens": [
"迷迭香的战术装备"
]
},
"char_4000_jnight": {
"name": "正义骑士号",
@@ -2099,7 +2151,10 @@
"1-1",
"1-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"工程蓄水炮"
]
},
"char_4013_kjera": {
"name": "耶拉",
@@ -2132,7 +2187,10 @@
"1-1",
"1-1"
],
"rarity": 5
"rarity": 5,
"tokens": [
"纸偶"
]
},
"char_4017_puzzle": {
"name": "谜图",
@@ -2286,7 +2344,10 @@
"3-14",
"3-14"
],
"rarity": 6
"rarity": 6,
"tokens": [
"旧日残影"
]
},
"char_4047_pianst": {
"name": "车尔尼",
@@ -2308,7 +2369,10 @@
"3-3",
"3-3"
],
"rarity": 6
"rarity": 6,
"tokens": [
"共振装置"
]
},
"char_4054_malist": {
"name": "至简",
@@ -2330,7 +2394,10 @@
"3-6",
"3-6"
],
"rarity": 6
"rarity": 6,
"tokens": [
"“打字机”"
]
},
"char_405_absin": {
"name": "苦艾",
@@ -2429,7 +2496,12 @@
"1-1",
"1-1"
],
"rarity": 6
"rarity": 6,
"tokens": [
"白铁™多功能平台",
"白铁™多功能平台",
"铁钳号·原型机"
]
},
"char_411_tomimi": {
"name": "特米米",
@@ -2528,7 +2600,10 @@
"3-3",
"3-3"
],
"rarity": 6
"rarity": 6,
"tokens": [
"狼群"
]
},
"char_430_fartth": {
"name": "远牙",
@@ -2561,7 +2636,10 @@
"1-1",
"1-1"
],
"rarity": 5
"rarity": 5,
"tokens": [
"可靠电池"
]
},
"char_436_whispr": {
"name": "絮雨",
@@ -2616,7 +2694,10 @@
"3-3",
"3-3"
],
"rarity": 5
"rarity": 5,
"tokens": [
"\"夹子\""
]
},
"char_452_bstalk": {
"name": "豆苗",
@@ -2627,7 +2708,10 @@
"3-3",
"3-3"
],
"rarity": 4
"rarity": 4,
"tokens": [
"磐蟹护卫队"
]
},
"char_455_nothin": {
"name": "乌有",
@@ -2671,7 +2755,10 @@
"3-3",
"3-3"
],
"rarity": 5
"rarity": 5,
"tokens": [
"迎宾踏垫"
]
},
"char_459_tachak": {
"name": "战车",
@@ -2759,7 +2846,10 @@
"3-3",
"3-3"
],
"rarity": 5
"rarity": 5,
"tokens": [
"眠兽"
]
},
"char_478_kirara": {
"name": "绮良",
@@ -2792,7 +2882,10 @@
"1-1",
"1-1"
],
"rarity": 4
"rarity": 4,
"tokens": [
"全自动造型仪"
]
},
"char_485_pallas": {
"name": "帕拉斯",

View File

@@ -129,6 +129,7 @@ namespace asst::battle
std::array<std::string, 3> ranges;
int rarity = 0;
LocationType location_type = LocationType::None;
std::vector<std::string> tokens; // 召唤物名字
};
using AttackRange = std::vector<Point>;

View File

@@ -47,6 +47,12 @@ bool asst::BattleDataConfig::parse(const json::value& json)
const auto& rarity = char_data_json.at("rarity").as_integer();
data.rarity = rarity;
if (auto tokens_opt = char_data_json.find<json::array>("tokens")) {
for (const auto& token : *tokens_opt) {
data.tokens.emplace_back(token.as_string());
}
}
m_chars.emplace(std::move(name), std::move(data));
}
for (const auto& [id, points_json] : json.at("ranges").as_object()) {

View File

@@ -64,6 +64,16 @@ namespace asst
return range_iter->second;
}
const std::vector<std::string>& get_tokens(const std::string& name) const
{
auto char_iter = m_chars.find(name);
if (char_iter == m_chars.cend()) {
static const std::vector<std::string> Empty;
return Empty;
}
return char_iter->second.tokens;
}
protected:
virtual bool parse(const json::value& json) override;

View File

@@ -459,6 +459,9 @@
<ClInclude Include="Task\BattleHelper.h">
<Filter>源文件\Task</Filter>
</ClInclude>
<ClInclude Include="Utils\Algorithm.hpp">
<Filter>源文件\Utils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Vision\AbstractImageAnalyzer.cpp">

View File

@@ -44,6 +44,41 @@ bool asst::BattleHelper::calc_tiles_info(const std::string& stage_name)
return true;
}
bool asst::BattleHelper::load_avatar_cache(const std::string& name)
{
LogTraceFunction;
if (name.empty()) {
return false;
}
auto path = AvatarCacheDir / utils::path(name + CacheExtension);
Log.info(path);
if (!std::filesystem::exists(path)) {
Log.info(path, "not exists");
return false;
}
cv::Mat avatar = asst::imread(path);
if (avatar.empty()) {
Log.info(path, "image is empty");
return false;
}
m_all_deployment_avatars.emplace(name, std::move(avatar));
Log.info(path, "loaded");
return true;
}
void asst::BattleHelper::save_avatar_cache(const std::string& name, const cv::Mat& avatar)
{
LogTraceFunction;
auto path = AvatarCacheDir / utils::path(name + CacheExtension);
Log.info(path);
std::filesystem::create_directories(AvatarCacheDir);
asst::imwrite(path, avatar);
}
bool asst::BattleHelper::pause()
{
LogTraceFunction;
@@ -151,6 +186,7 @@ bool asst::BattleHelper::update_deployment(bool init)
oper.name = name;
m_cur_deployment_opers.insert_or_assign(name, oper);
m_all_deployment_avatars.insert_or_assign(name, oper.avatar);
save_avatar_cache(name, oper.avatar);
}
pause();
cancel_oper_selection();

View File

@@ -6,6 +6,8 @@
#include "Config/Miscellaneous/TilePack.h"
#include "InstHelper.h"
#include "Utils/NoWarningCVMat.h"
#include "Utils/Platform.hpp"
#include "Utils/WorkingDir.hpp"
#include <map>
@@ -13,6 +15,9 @@ namespace asst
{
class BattleHelper
{
inline static const auto AvatarCacheDir = UserDir.get() / utils::path("cache") / utils::path("avatars");
inline static const std::string CacheExtension = ".png";
public:
~BattleHelper() = default;
@@ -22,6 +27,8 @@ namespace asst
virtual bool set_stage_name(const std::string& name);
bool calc_tiles_info(const std::string& stage_name);
bool load_avatar_cache(const std::string& name);
void save_avatar_cache(const std::string& name, const cv::Mat& avatar);
bool pause();
bool speed_up();

View File

@@ -7,6 +7,7 @@
#include "Utils/NoWarningCV.h"
#include "Config/Miscellaneous/BattleDataConfig.h"
#include "Config/Miscellaneous/CopilotConfig.h"
#include "Config/Miscellaneous/TilePack.h"
#include "Config/TaskData.h"
@@ -35,6 +36,7 @@ bool asst::BattleProcessTask::_run()
return false;
}
load_cache();
update_deployment(true);
to_group();
@@ -63,6 +65,23 @@ bool asst::BattleProcessTask::set_stage_name(const std::string& stage_name)
return true;
}
void asst::BattleProcessTask::load_cache()
{
LogTraceFunction;
for (const auto& oper_list : m_combat_data.groups | views::values) {
for (const auto& oper : oper_list) {
load_avatar_cache(oper.name);
if (auto tokens = BattleData.get_tokens(oper.name); !tokens.empty()) {
for (const std::string& token_name : tokens) {
load_avatar_cache(token_name);
}
}
}
}
// TODO: 识别编队,额外加载编队中有的干员的缓存
}
bool asst::BattleProcessTask::to_group()
{
std::unordered_map<std::string, std::vector<std::string>> groups;

View File

@@ -21,6 +21,7 @@ namespace asst
virtual bool _run() override;
virtual AbstractTask& this_task() override { return *this; }
void load_cache();
bool to_group();
bool do_action(size_t action_index);
void notify_action(const battle::copilot::Action& action);

View File

@@ -7,7 +7,7 @@
namespace asst::algorithm
{
std::optional<std::unordered_map<std::string, std::string>> get_char_allocation_for_each_group(
static std::optional<std::unordered_map<std::string, std::string>> get_char_allocation_for_each_group(
const std::unordered_map<std::string, std::vector<std::string>>& group_list,
const std::unordered_set<std::string>& char_set)
{

View File

@@ -129,7 +129,10 @@ bool asst::BattleImageAnalyzer::opers_analyze()
if (oper.rect.x + oper.rect.width >= m_image.cols) {
oper.rect.width = m_image.cols - oper.rect.x;
}
oper.avatar = m_image(make_rect<cv::Rect>(oper.rect));
auto avatar_rect = make_rect<cv::Rect>(oper.rect);
avatar_rect.y += 30;
avatar_rect.height = 70;
oper.avatar = m_image(avatar_rect);
Rect available_rect = flag_mrect.rect.move(avlb_move);
oper.available = oper_available_analyze(available_rect);

View File

@@ -646,7 +646,8 @@ bool update_battle_chars_info(const std::filesystem::path& input_dir, const std:
range.emplace(id, std::move(points));
}
auto& chars = result["chars"].as_object();
auto& chars = result["chars"];
std::map<std::string, std::vector<std::string>> tokens;
for (auto& [id, char_data] : chars_json.as_object()) {
json::value char_new_data;
std::string name = char_data["name"].as_string();
@@ -662,15 +663,36 @@ bool update_battle_chars_info(const std::filesystem::path& input_dir, const std:
char_new_data["rarity"] = static_cast<int>(char_data["rarity"]) + 1;
char_new_data["position"] = char_data["position"];
chars.emplace(id, std::move(char_new_data));
if (auto token_opt = char_data.find<std::string>("tokenKey")) {
tokens[id].emplace_back(*token_opt);
}
if (auto skill_opt = char_data.find<json::array>("skills")) {
for (const auto& skill_obj : *skill_opt) {
if (auto token_opt = skill_obj.find<std::string>("overrideTokenKey")) {
tokens[id].emplace_back(*token_opt);
}
}
}
chars.object_emplace(id, std::move(char_new_data));
}
for (const auto& [oper_id, token_id_list] : tokens) {
std::vector<std::string> token_names_list;
for (const auto& token_id : token_id_list) {
std::string token_name = chars.get(token_id, "name", std::string());
if (!token_name.empty()) {
token_names_list.emplace_back(token_name);
}
}
chars[oper_id]["tokens"] = json::array(token_names_list);
}
json::value Amiya_data;
Amiya_data["name"] = "阿米娅-WARRIOR";
Amiya_data["profession"] = "WARRIOR";
Amiya_data["rangeId"] = json::array { "1-1", "1-1", "1-1" };
Amiya_data["rarity"] = 5;
Amiya_data["position"] = "MELEE";
chars.emplace("char_1001_amiya2", std::move(Amiya_data));
chars.object_emplace("char_1001_amiya2", std::move(Amiya_data));
const auto& out_file = output_dir / "battle_data.json";
std::ofstream ofs(out_file, std::ios::out);