From 3f331caa1db8c3bd1cf3f727f90edd1230f591c9 Mon Sep 17 00:00:00 2001 From: MistEO Date: Tue, 25 Jan 2022 00:54:07 +0800 Subject: [PATCH] =?UTF-8?q?feat.=E5=88=9D=E6=AD=A5=E9=9B=86=E6=88=90?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E6=A0=BC=E5=AD=90=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../include/Arknights-Tile-Pos/TileCalc.hpp | 230 ++++++++++++++++++ README.md | 1 + resource/tasks.json | 12 + src/MeoAssistant/Controller.cpp | 16 +- src/MeoAssistant/MeoAssistant.vcxproj | 2 + src/MeoAssistant/MeoAssistant.vcxproj.filters | 6 + src/MeoAssistant/Resource.cpp | 11 +- src/MeoAssistant/Resource.h | 10 + .../RoguelikeBattleTaskPlugin.cpp | 49 +++- src/MeoAssistant/RoguelikeBattleTaskPlugin.h | 6 + src/MeoAssistant/TilePack.cpp | 57 +++++ src/MeoAssistant/TilePack.h | 49 ++++ 12 files changed, 435 insertions(+), 14 deletions(-) create mode 100644 3rdparty/include/Arknights-Tile-Pos/TileCalc.hpp create mode 100644 src/MeoAssistant/TilePack.cpp create mode 100644 src/MeoAssistant/TilePack.h diff --git a/3rdparty/include/Arknights-Tile-Pos/TileCalc.hpp b/3rdparty/include/Arknights-Tile-Pos/TileCalc.hpp new file mode 100644 index 0000000000..2ccb23360b --- /dev/null +++ b/3rdparty/include/Arknights-Tile-Pos/TileCalc.hpp @@ -0,0 +1,230 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace Map +{ + struct Tile + { + int heightType = 0; + int buildableType = 0; + }; + + class Level + { + public: + Level(const json::value& data); + int get_width() const { return width; } + int get_height() const { return height; } + Tile get_item(int y, int x) const { return tiles[y][x]; } + int view = 0; + std::string stageId; + std::string code; + std::string levelId; + std::string name; + private: + int height = 0; + int width = 0; + std::vector> tiles; + }; + class TileCalc + { + public: + TileCalc(int width, int height, const std::string& dir); + bool run(const std::string& code_or_name, bool side, std::vector>& out_pos, std::vector>& out_tiles) const; + private: + int width = 0; + int height = 0; + const double degree = atan(1.0) * 4 / 180; + std::vector levels; + cv::Mat MatrixP = cv::Mat(4, 4, CV_64F); + cv::Mat MatrixX = cv::Mat(4, 4, CV_64F); + cv::Mat MatrixY = cv::Mat(4, 4, CV_64F); + bool adapter(double& x, double& y) const; + }; + + inline void InitMat4x4(cv::Mat& m, double(*num)[4]) + { + for (int i = 0; i < m.rows; i++) + for (int j = 0; j < m.cols; j++) + m.at(i, j) = num[i][j]; + } + + Level::Level(const json::value& data) + { + Level::stageId = data.at("stageId").as_string(); + Level::code = data.at("code").as_string(); + Level::levelId = data.at("levelId").as_string(); + Level::name = data.get("name", "null"); + Level::height = data.at("height").as_integer(); + Level::width = data.at("width").as_integer(); + Level::view = data.at("view").as_integer(); + for (const json::value& row : data.at("tiles").as_array()) { + std::vector tmp(Level::width); + for (const json::value& tile : row.as_array()) { + tmp.emplace_back(Tile{ tile.at("heightType").as_integer(), tile.at("buildableType").as_integer() }); + } + tiles.emplace_back(std::move(tmp)); + } + } + + TileCalc::TileCalc(int width, int height, const std::string& dir) + { + TileCalc::width = width; + TileCalc::height = height; + double ratio = static_cast(height) / width; + double matrixP[4][4]{ + { ratio / tan(20 * degree), 0, 0, 0}, + { 0, 1 / tan(20 * degree), 0, 0}, + { 0, 0, -(1000 + 0.3) / (1000 - 0.3), -(1000 * 0.3 * 2) / (1000 - 0.3)}, + { 0, 0, -1, 0 } + }; + InitMat4x4(TileCalc::MatrixP, matrixP); + double matrixX[4][4]{ + { 1, 0, 0, 0}, + { 0, cos(30 * degree), -sin(30 * degree), 0}, + { 0, -sin(30 * degree), -cos(30 * degree), 0}, + { 0, 0, 0, 1} + }; + InitMat4x4(TileCalc::MatrixX, matrixX); + double matrixY[4][4]{ + { cos(10 * degree), 0, sin(10 * degree), 0}, + { 0, 1, 0, 0}, + { -sin(10 * degree), 0, cos(10 * degree), 0}, + { 0, 0, 0, 1} + }; + InitMat4x4(TileCalc::MatrixY, matrixY); + std::ifstream ifs(dir, std::ios::in); + if (!ifs.is_open()) { + std::cerr << "Read resource failed" << std::endl; + throw "Read resource failed"; + } + std::stringstream iss; + iss << ifs.rdbuf(); + ifs.close(); + std::string content = iss.str(); + auto ret = json::parse(content); + if (!ret) { + std::cerr << "Parsing failed" << std::endl; + throw "Parsing failed"; + } + for (const json::value& item : ret.value().as_array()) { + TileCalc::levels.emplace_back(item); + } + } + + bool TileCalc::adapter(double& x, double& y) const + { + const double fromRatio = 9.0 / 16; + const double toRatio = 3.0 / 4; + double ratio = static_cast(height) / width; + if (ratio < fromRatio - 0.00001) { + x = 0; + y = 0; + return false; + } + double t = (ratio - fromRatio) / (toRatio - fromRatio); + x = -1.4 * t; + y = -2.8 * t; + return true; + } + + bool TileCalc::run(const std::string& code_or_name, bool side, std::vector>& out_pos, std::vector>& out_tiles) const + { + bool runned = false; + double x = 0, y = 0, z = 0; + for (const Map::Level& level : TileCalc::levels) { + if (level.code == code_or_name || level.name == code_or_name) { + switch (level.view) { + case 0: + x = 0; + y = -4.81; + z = -7.76; + if (side) { + x += 0.5975104570388794; + y -= 0.5; + z -= 0.882108688354492; + } + break; + case 1: + x = 0; + y = -5.60; + z = -8.92; + if (side) { + x += 0.7989424467086792; + y -= 0.5; + z -= 0.86448486328125; + } + break; + case 2: + x = 0; + y = -5.08; + z = -8.04; + if (side) { + x += 0.6461319923400879; + y -= 0.5; + z -= 0.877854309082031; + } + break; + default: + x = 0; + y = -6.1; + z = -9.78; + if (side) { + x += 0.948279857635498; + y -= 0.5; + z -= 0.85141918182373; + } + break; + } + double adapter_y = 0, adapter_z = 0; + TileCalc::adapter(adapter_y, adapter_z); + double matrix[4][4]{ + { 1, 0, 0, -x}, + { 0, 1, 0, -y - adapter_y}, + { 0, 0, 1, -z - adapter_z}, + { 0, 0, 0, 1} + }; + auto raw = cv::Mat(cv::Size(4, 4), CV_64F); + auto Finall_Matrix = cv::Mat(cv::Size(4, 4), CV_64F); + InitMat4x4(raw, matrix); + if (side) { + Finall_Matrix = TileCalc::MatrixP * TileCalc::MatrixX * TileCalc::MatrixY * raw; + } + else { + Finall_Matrix = TileCalc::MatrixP * TileCalc::MatrixX * raw; + } + int h = level.get_height(); + int w = level.get_width(); + auto map_point = cv::Mat(cv::Size(1, 4), CV_64F); + map_point.at(3, 0) = 1; + auto tmp_pos = std::vector(w); + auto tmp_tiles = std::vector(w); + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + tmp_tiles[j] = level.get_item(i, j); + map_point.at(0, 0) = j - (w - 1) / 2.0; + map_point.at(1, 0) = (h - 1) / 2.0 - i; + map_point.at(2, 0) = tmp_tiles[j].heightType * -0.4; + cv::Mat view_point = Finall_Matrix * map_point; + view_point = view_point / view_point.at(3, 0); + view_point = (view_point + 1) / 2; + tmp_pos[j] = cv::Point2d(view_point.at(0, 0) * TileCalc::width, (1 - view_point.at(1, 0)) * TileCalc::height); + } + out_pos.emplace_back(tmp_pos); + out_tiles.emplace_back(tmp_tiles); + } + runned = true; + break; + } + } + return runned; + } +} diff --git a/README.md b/README.md index 6e09de044c..2eab138683 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ Yes, but there are few supported features, please refer to [this link](resource/ - WPF控件库:[HandyControl](https://github.com/HandyOrg/HandyControl) - C# JSON库: [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) - 下载器:[aria2](https://github.com/aria2/aria2) +- 明日方舟地图格子识别: [Arknights-Tile-Pos](https://github.com/yuanyan3060/Arknights-Tile-Pos) ### 数据源 diff --git a/resource/tasks.json b/resource/tasks.json index 317066c548..7f64e1e116 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -3127,6 +3127,18 @@ "ClueGiveTo1stConfirm" ] }, + "BattleStageName": { + "algorithm": "OcrDetect", + "text": [ + + ], + "roi": [ + 255, + 263, + 785, + 328 + ] + }, "BattleUseOper": { "algorithm": "JustReturn", "preDelay": 500, diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index fe87849cf4..2af4694582 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -697,9 +697,9 @@ bool asst::Controller::screencap(const std::string & cmd, DecodeFunc decode_func } return false; } -} - -cv::Mat asst::Controller::get_resized_image() const +} + +cv::Mat asst::Controller::get_resized_image() const { const static cv::Size dsize(m_scale_size.first, m_scale_size.second); @@ -710,7 +710,7 @@ cv::Mat asst::Controller::get_resized_image() const } cv::Mat resized_mat; cv::resize(m_cache_image, resized_mat, dsize); - return resized_mat; + return resized_mat; } int asst::Controller::click(const Point & p, bool block) @@ -831,13 +831,13 @@ cv::Mat asst::Controller::get_image() } return get_resized_image(); -} - -std::vector asst::Controller::get_image_encode() +} + +std::vector asst::Controller::get_image_encode() { cv::Mat img = get_resized_image(); std::vector buf; cv::imencode(".png", img, buf); - return buf; + return buf; } diff --git a/src/MeoAssistant/MeoAssistant.vcxproj b/src/MeoAssistant/MeoAssistant.vcxproj index 543b4b0099..4c918f0d72 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj +++ b/src/MeoAssistant/MeoAssistant.vcxproj @@ -68,6 +68,7 @@ + @@ -124,6 +125,7 @@ + diff --git a/src/MeoAssistant/MeoAssistant.vcxproj.filters b/src/MeoAssistant/MeoAssistant.vcxproj.filters index 8575753e92..397c7be3a8 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj.filters +++ b/src/MeoAssistant/MeoAssistant.vcxproj.filters @@ -252,6 +252,9 @@ 头文件\TaskPlugin + + 头文件\Resource + @@ -413,6 +416,9 @@ 源文件\TaskPlugin + + 源文件\Resource + diff --git a/src/MeoAssistant/Resource.cpp b/src/MeoAssistant/Resource.cpp index d0ebf85f97..8907427858 100644 --- a/src/MeoAssistant/Resource.cpp +++ b/src/MeoAssistant/Resource.cpp @@ -16,9 +16,10 @@ bool asst::Resource::load(const std::string& dir) constexpr static const char* RecruitCfgFilename = "recruit.json"; constexpr static const char* ItemCfgFilename = "item_index.json"; constexpr static const char* InfrastCfgFilename = "infrast.json"; - constexpr static const char* UserCfgFilename = "..\\user.json"; + constexpr static const char* UserCfgFilename = "../user.json"; constexpr static const char* OcrResourceFilename = "PaddleOCR"; constexpr static const char* PenguinResourceFilename = "penguin-stats-recognize"; + constexpr static const char* TilesCalcResourceFilename = "Arknights-Tile-Pos"; /* 加载各个Json配置文件 */ if (!m_general_cfg_unique_ins.load(dir + GeneralCfgFilename)) { @@ -77,5 +78,11 @@ bool asst::Resource::load(const std::string& dir) return false; } + /* 加载地图格子识别库所需要的资源 */ + if (!m_tile_pack_unique_ins.load(dir + TilesCalcResourceFilename)) { + m_last_error = std::string(TilesCalcResourceFilename) + ": " + m_tile_pack_unique_ins.get_last_error(); + return false; + } + return true; -} \ No newline at end of file +} diff --git a/src/MeoAssistant/Resource.h b/src/MeoAssistant/Resource.h index 5b1d3b7ca5..b803056ba2 100644 --- a/src/MeoAssistant/Resource.h +++ b/src/MeoAssistant/Resource.h @@ -14,6 +14,7 @@ #include "TaskData.h" #include "TemplResource.h" #include "UserConfiger.h" +#include "TilePack.h" namespace asst { @@ -62,6 +63,10 @@ namespace asst { return m_penguin_pack_unique_ins; } + TilePack& tile() noexcept + { + return m_tile_pack_unique_ins; + } const TemplResource& templ() const noexcept { @@ -95,6 +100,10 @@ namespace asst { return m_penguin_pack_unique_ins; } + const TilePack& tile() const noexcept + { + return m_tile_pack_unique_ins; + } Resource& operator=(const Resource&) = delete; Resource& operator=(Resource&&) noexcept = delete; @@ -110,6 +119,7 @@ namespace asst UserConfiger m_user_cfg_unique_ins; OcrPack m_ocr_pack_unique_ins; PenguinPack m_penguin_pack_unique_ins; + TilePack m_tile_pack_unique_ins; }; //static auto& resource = Resource::get_instance(); diff --git a/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp b/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp index 3184616459..feb61606b4 100644 --- a/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp +++ b/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp @@ -5,6 +5,8 @@ #include "Controller.h" #include "TaskData.h" #include "ProcessTask.h" +#include "OcrImageAnalyzer.h" +#include "Resource.h" bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& details) const { @@ -23,11 +25,9 @@ bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& det bool asst::RoguelikeBattleTaskPlugin::_run() { - m_used_opers = false; - m_pre_hp = 0; - m_home_cache.clear(); + clear(); - speed_up(); + get_stage_info(); while (!need_exit()) { // 不在战斗场景,且已使用过了干员,说明已经打完了,就结束循环 @@ -39,6 +39,38 @@ bool asst::RoguelikeBattleTaskPlugin::_run() return true; } +bool asst::RoguelikeBattleTaskPlugin::get_stage_info() +{ + const auto& tile = Resrc.tile(); + bool calced = false; + for (int i = 0; i != m_retry_times; ++i) { + OcrImageAnalyzer name_analyzer(Ctrler.get_image()); + name_analyzer.set_task_info("BattleStageName"); + name_analyzer.analyze(); + + for (const auto& tr : name_analyzer.get_result()) { + auto normal_info = tile.calc(tr.text, false); + if (normal_info.empty()) { + continue; + } + auto side_info = tile.calc(tr.text, true); + if (side_info.empty()) { + continue; + } + + m_tile_info = std::move(normal_info); + m_side_tile_info = std::move(side_info); + calced = true; + break; + } + if (calced) { + break; + } + } + + return calced; +} + bool asst::RoguelikeBattleTaskPlugin::auto_battle() { using Role = asst::BattleImageAnalyzer::Role; @@ -191,3 +223,12 @@ bool asst::RoguelikeBattleTaskPlugin::use_skill(const asst::Rect& rect) ProcessTask task(*this, { "BattleUseSkill" }); return task.run(); } + +void asst::RoguelikeBattleTaskPlugin::clear() +{ + m_used_opers = false; + m_pre_hp = 0; + m_home_cache.clear(); + m_tile_info.clear(); + m_side_tile_info.clear(); +} diff --git a/src/MeoAssistant/RoguelikeBattleTaskPlugin.h b/src/MeoAssistant/RoguelikeBattleTaskPlugin.h index d5fdb0dd76..031c0b89f8 100644 --- a/src/MeoAssistant/RoguelikeBattleTaskPlugin.h +++ b/src/MeoAssistant/RoguelikeBattleTaskPlugin.h @@ -2,6 +2,7 @@ #include "AbstractTaskPlugin.h" #include "AsstDef.h" +#include "TilePack.h" namespace asst { @@ -16,12 +17,17 @@ namespace asst protected: virtual bool _run() override; + bool get_stage_info(); bool auto_battle(); bool speed_up(); bool use_skill(const Rect& rect); + void clear(); std::vector m_home_cache; bool m_used_opers = false; int m_pre_hp = 0; + + std::vector m_tile_info; + std::vector m_side_tile_info; }; } diff --git a/src/MeoAssistant/TilePack.cpp b/src/MeoAssistant/TilePack.cpp new file mode 100644 index 0000000000..d2260c95ad --- /dev/null +++ b/src/MeoAssistant/TilePack.cpp @@ -0,0 +1,57 @@ +#include "TilePack.h" + +#include + +#include "Logger.hpp" + +asst::TilePack::~TilePack() = default; + +bool asst::TilePack::load(const std::string & dir) +{ + LogTraceFunction; + + constexpr static const char* filename = "/levels.json"; + + try { + m_tile_calculator = std::make_unique( + WindowWidthDefault, WindowHeightDefault, dir + filename); + } + catch (std::exception& e) { + m_last_error = e.what(); + return false; + } + return m_tile_calculator != nullptr; +} + +std::vector asst::TilePack::calc(const std::string & stage_code, bool side) const +{ + LogTraceFunction; + + std::vector> pos; + std::vector> tiles; + + bool ret = m_tile_calculator->run(stage_code, side, pos, tiles); + + Log.trace("After tiles cacl run"); + if (!ret) { + Log.info("Tiles calc error!"); + return std::vector(); + } + + std::vector dst; + + for (size_t y = 0; y < pos.size(); ++y) { + for (size_t x = 0; x < pos[y].size(); ++x) { + const auto& cv_p = pos[y][x]; + const auto& tile = tiles[y][x]; + + dst.emplace_back( + TileInfo{ + static_cast(tile.buildableType), + static_cast(tile.heightType), + Point(static_cast(cv_p.x), static_cast(cv_p.y)) + }); + } + } + return dst; +} diff --git a/src/MeoAssistant/TilePack.h b/src/MeoAssistant/TilePack.h new file mode 100644 index 0000000000..6e92c069e0 --- /dev/null +++ b/src/MeoAssistant/TilePack.h @@ -0,0 +1,49 @@ +#pragma once + +#include "AbstractResource.h" +#include "AsstDef.h" + +#include + +namespace Map +{ + class TileCalc; +} + +namespace asst +{ + class TilePack final : public AbstractResource + { + public: + enum class BuildableType + { + Invaild = -1, + None = 0, + Melee = 1, + Ranged = 2, + All = 3 + }; + enum class HeightType + { + Invaild = -1, + Highland = 0, + Floor = 1 + }; + struct TileInfo + { + BuildableType buildable = BuildableType::Invaild; + HeightType height = HeightType::Invaild; + Point pos; + }; + public: + using AbstractResource::AbstractResource; + virtual ~TilePack(); + + virtual bool load(const std::string& dir) override; + + std::vector calc(const std::string& stage_code, bool side) const; + + private: + std::shared_ptr m_tile_calculator = nullptr; + }; +}