From b7ae83e9e965d75f375f43d6ff31befafff3ca19 Mon Sep 17 00:00:00 2001 From: Horror Proton <107091537+horror-proton@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:38:27 +0800 Subject: [PATCH] feat: use `cv::warpPerspective` in `use_skill` --- .../include/Arknights-Tile-Pos/TileCalc2.hpp | 16 +- resource/tasks.json | 29 +-- resource/template/BattleSkillReady.png | Bin 1323 -> 0 bytes .../BattleSkillReadyOnClick-SquareMap.png | Bin 386 -> 0 bytes .../BattleSkillReadyOnClick-TopView.png | Bin 0 -> 3466 bytes resource/template/BattleSkillReadyOnClick.png | Bin 493 -> 0 bytes .../BattleSkillStopOnClick-MiddleMap.png | Bin 563 -> 0 bytes .../BattleSkillStopOnClick-SquareMap.png | Bin 458 -> 0 bytes .../BattleSkillStopOnClick-TopView.png | Bin 0 -> 925 bytes resource/template/BattleSkillStopOnClick.png | Bin 761 -> 0 bytes src/MaaCore/Task/BattleHelper.cpp | 175 +++++++++--------- src/MaaCore/Task/BattleHelper.h | 2 + 12 files changed, 105 insertions(+), 117 deletions(-) delete mode 100644 resource/template/BattleSkillReady.png delete mode 100644 resource/template/BattleSkillReadyOnClick-SquareMap.png create mode 100644 resource/template/BattleSkillReadyOnClick-TopView.png delete mode 100644 resource/template/BattleSkillReadyOnClick.png delete mode 100644 resource/template/BattleSkillStopOnClick-MiddleMap.png delete mode 100644 resource/template/BattleSkillStopOnClick-SquareMap.png create mode 100644 resource/template/BattleSkillStopOnClick-TopView.png delete mode 100644 resource/template/BattleSkillStopOnClick.png diff --git a/3rdparty/include/Arknights-Tile-Pos/TileCalc2.hpp b/3rdparty/include/Arknights-Tile-Pos/TileCalc2.hpp index 3be61f8fe5b..a6e0c6737ff 100644 --- a/3rdparty/include/Arknights-Tile-Pos/TileCalc2.hpp +++ b/3rdparty/include/Arknights-Tile-Pos/TileCalc2.hpp @@ -66,11 +66,13 @@ inline matrix4x4 camera_matrix_from_trans( 0, -sin_x, -cos_x, 0, // 0, 0, 0, 1, }; - const matrix4x4 proj = { + const double elem1 = -(far + near) / (far - near); // unable to inline, MSVC bug? + const double elem2 = -(far * near * 2) / (far - near); + const matrix4x4 proj = matrix4x4 { // clang-format off ratio / tan_f, 0, 0, 0, 0, 1 / tan_f, 0, 0, - 0, 0, -(far + near) / (far - near), -(far * near * 2) / (far - near), + 0, 0, elem1, elem2, 0, 0, -1, 0, // clang-format on }; @@ -78,8 +80,7 @@ inline matrix4x4 camera_matrix_from_trans( return proj * matrixX * matrixY * translate; } -inline cv::Point - world_to_screen(const Level& level, const vec3d& world_pos, bool side, const vec3d& offset = {}) +inline cv::Point world_to_screen(const Level& level, const vec3d& world_pos, bool side, const vec3d& offset = {}) { static constexpr double width = 1280; static constexpr double height = 720; @@ -107,12 +108,7 @@ inline vec3d get_tile_world_pos(const Level& level, int tile_y, int tile_x) }; } -inline auto get_tile_screen_pos( - const Level& level, - int tile_y, - int tile_x, - bool side = false, - const vec3d& offset = {}) +inline auto get_tile_screen_pos(const Level& level, int tile_y, int tile_x, bool side = false, const vec3d& offset = {}) { return world_to_screen(level, get_tile_world_pos(level, tile_y, tile_x), side, offset); } diff --git a/resource/tasks.json b/resource/tasks.json index 243d0104520..6b3f0c6c5ca 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -4627,29 +4627,12 @@ "BattleSkillReady": { "rectMove": [-28, -140, 64, 64] }, - "BattleSkillReadyOnClick": { - "roi": [748, 414, 210, 118], - "templThreshold": 0.7, - "action": "ClickSelf", - "rectMove": [0, -110, 90, 90], - "postDelay": 300, - "next": ["#self", "Stop"] - }, - "BattleSkillReadyOnClick-SquareMap": { - "baseTask": "BattleSkillReadyOnClick", - "template": "BattleSkillReadyOnClick-SquareMap.png" - }, - "BattleSkillStopOnClick": { - "baseTask": "BattleSkillReadyOnClick", - "template": "BattleSkillStopOnClick.png" - }, - "BattleSkillStopOnClick-MiddleMap": { - "baseTask": "BattleSkillReadyOnClick", - "template": "BattleSkillStopOnClick-MiddleMap.png" - }, - "BattleSkillStopOnClick-SquareMap": { - "baseTask": "BattleSkillReadyOnClick", - "template": "BattleSkillStopOnClick-SquareMap.png" + "BattleSkillReadyOnClick-TopView": { + "template": [ + "BattleSkillReadyOnClick-TopView.png", + "BattleSkillStopOnClick-TopView.png" + ], + "templThreshold": [ 0.8, 0.8 ] }, "BattleUseOper": { "algorithm": "JustReturn", diff --git a/resource/template/BattleSkillReady.png b/resource/template/BattleSkillReady.png deleted file mode 100644 index 46f95b7d047e454917d7859f7fa7cf698af308f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1323 zcmV+`1=RY9P)y)Pg=ikaG0gRC_KC6mk@zoo`G)u5MR7 zod5$`5oNxz?cPDzl`SfFhUV}rX)Q-h`oc(OhPw103RzjvO6(h}*YXgqg(K_qqLuUJ z1~?W4KWZjc4v~r$4u62Rd-?LUT`5<#3Rz^XEUA4~cnwF@SY5}U98E>u8Dy2?ET38t zaGwcCfMIPUcd`}PBaHioN2VzV%M2|l3h)X%-o`axXSY-!M_H}X-dyRC8L~=8IsNj& z6mJn#ttFYuyLnd$d2>h5%V`7_Je5k=*GN0Q8#MM?ZE6tf*%M=(tMz6^?jINQW-Mu6 z6@^O51-Q-KIi@2sQlJ73hsSAAW;n7`7jU-u%Ee7&ck`0IdqpJ&2$aM47AB*Xc_m#D zs$(tFVh4PoDOwU5u7L^PD3LV{S#?#v=jpF6O~@MaC{#*>DaoEJ+_p-LF8zpi=IHvr z!ENmpKKn~G7T^?PWGDi78gv@E(fisJ%?CVb6~pGTNk zU*%XTX4xYC?t1lsArAxm{x~R_Zoaoa5ETcWUdLHxaU&ZVFpB|r=ds`)UI~uS!bgv& z6eL8eSiOWC*DC~Bl6|!*aPD0B_07t?gN*=njpTw+5zgH~cQd##uTlNpmqcm_gM|9v zQO>$l)9zj6y8dAgg|Hc&Vm^vx?aM-3dAN75C|`SRS@@3r4*>W#7ZnfZ?qqPqi=&Mi zS|cN8G|>0|QtHXoUD>R=xXFBWP0Y{+hKB-S$lK(JReDiioLlJZQQ-jfnN71s$GP@| zF76c)Ac2mbQnxcCqK(E?fww!E`ko)~574l2KnSv?oL`i%vKwyI`mYBiLnK#M$25FF z+3|gF74l_sk#7_9HxESY9In5xO6}UM+159y{_8=dV_M?PO$jHKNpL;jX{SP+ki@VRLosk61ijGLw{dfROuKj6Za^bo48$rV*&n|*wisD hdP8qa|35|7zW_Kjgp62Q(Z2uy002ovPDHLkV1j`HcFF(% diff --git a/resource/template/BattleSkillReadyOnClick-SquareMap.png b/resource/template/BattleSkillReadyOnClick-SquareMap.png deleted file mode 100644 index 05ad1fa9c400f2faa7367037b1fed32326d2a860..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 386 zcmV-|0e$|7P)cI6V#x?wR23Zt*J$RVI5L1i36+c>{!Rvl6>lqM zFNC!3uV?rx^g@DrF~Vhrenc}2|8Uh?=71Guj@9Nhf8G`@+0zblD1~jv2<&o}i zX>+%e0w)YJPZt1!mH+?%07*qoM6N<$f?pk|LI3~& diff --git a/resource/template/BattleSkillReadyOnClick-TopView.png b/resource/template/BattleSkillReadyOnClick-TopView.png new file mode 100644 index 0000000000000000000000000000000000000000..942ea7cac8cb300a7e5484dd27e8a4d74c5c3062 GIT binary patch literal 3466 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 literal 0 HcmV?d00001 diff --git a/resource/template/BattleSkillReadyOnClick.png b/resource/template/BattleSkillReadyOnClick.png deleted file mode 100644 index 0aab185168086e7fced1ad72ca6c4448b0b52e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 493 zcmVa|R0005BNkl0U2;t1heHdT-C44nA+Ga%C>^PkhY<1a}6qPd$p9DO7LmSoa zewkdApvzOznvA?Tr|d4MhdQ-gvGU^2lZ)!G$H_`+Kf`Z$uISR(Tjq4=eAE<^mY}v! zO#V7iUiWF@TLx6O2a7qP-xi?w6CP~8-&=k9`TAz2K#v%1KdK|^UOFjlu47e`bctfW z23$;q04#^6bMlTbZ)>y9`7|-VsN4i}Kvc7wSIs|N4?z2oIhb~;FiX6wwC@~k9T&Ag6Z j?W*xES>6~``RaBbKbD^a@;++E00000NkvXXu0mjfx^C>& diff --git a/resource/template/BattleSkillStopOnClick-MiddleMap.png b/resource/template/BattleSkillStopOnClick-MiddleMap.png deleted file mode 100644 index e88755b44a80129395c040083082d66505bf77c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 563 zcmV-30?hr1P) zr2^ZtdI2DA60V>PlT;=tRT8P?Ed&HjIAVqqn=-oTh^m>>OdV;>to7#a-2ri-#sZrZ zMnn_IvuEx+1#Kn-`5%NlLOqY{te{B@6yVJ!N-7E0Mu%vYQv$mP@$-Zo5=>Jv$%H1SI|t(Jbxb|?Ep6Iz%=ID3jiUDC-`0d%H{lrU)D3v}OyR{<-- zD@$L$5Oq-$KxTSiizZ0Utc?%3S1&7RL(XbS%8zJ2mK2+~oqu=~dW`3AKm3>@R;1prV07#;iqVLm+_95|hi2Sps%&`-k1S+TLU z_afyPz<2=sUtmT2rSR&$Cw=+~-lG%BZF-WOK?H-COq8lt{tkPk5={$*#H^qX& zWWjITSx&xKXf31KiP9xkCJn}0kbMi<|BWhvCQIn<77+p+;Wd%YM8Mjt16Yktdq7?& zWupO@5s3?DP8<+OTo8vG5Rwl>Lb3>(#P)h;XJ@9ni|WxjnYHXSy{nc+Q~kQ$ zt5-F|Un#7Y29E>n&*?dXjj=`>O{6&}-%wjI5GaA&l{mEjg8juo81z;kwHl5V2cK5e z0@|o_sPkz$zMT%Ib}+K(z`$!>;`kYU{!XB-MB!S6<-Ly%o;(Fvc8x1fWd}QeptUlo zN)w_5p5Mh!-}bNXql6V#RYC#*SJOYgSzSrm@?*Ba>tG6+9ANw|><8Ff=F)RA7$daq z#nIUhPYXL&2Berna7}R8zIpV8e(*V#uWvxHg2KViXYC)qa{V_WNREnqCNk{Y#o^-^ zv^?F0LQ)z*QHFxgQzWa^Mr>Uoc=#68WfZmfASi`!KwIf#eF1}*lxJO*m1BkAFn_Tv z7cI}lolfXlP~>n>DQu>qJf@zXC&>^Ybc_hSQg5Pg5IXEzg2TN>?*1og=N*#Zrcq6U zU%n?}!Hi}Jr6Rj4QH;E4Yi?^{pznOB4j-$-hh(i>%k@$p{jQAaQedP*RP*Z?@v1J3 zcS;3L3YG1V-CL{ELfv{#1QYuq9m8b6{AzWDYa`w#P1><{+{+pD!c2DHRzewrNf&!f zjY2xAt8>ztj1H@c`|r5bqfu-KsbIc?=%Z^;yyQ@xkV&CaUoM!am!?x$CfxwSf)bdMd=k_5#O#&L-lAyl>r-g}h**o+|CkJ7 ze9tN$qdJ3XMXFff*GJ*xSWiEej=!;|yU8F;(0a6slW(5HCYls_Jk<$@a%D%iqVcPn zCW976Vwi&m`Ild-Wf|&Dc$-4mY^70{j}g zWC5&%b>OlM9IkMOzsqRTnYKv|$-LVB>hg=72YUj5fo4-(&TB+D!D=jihFwA{ZWPyw zcS@I~r7V8vRWX=ib`OpTjxKs<)B=+`?77b`fAn^y&Y^A4*8beojWwdUF}(2^FArvY zIf60O5`Zw*)5|{6UwSwu$A}@%5?GldRjeS|RyQ7@s zV}e1m#aZHliH_ua(wAcxqx?6@Mu0|Lb>wJBL{_cf7)&986S*HUhUV?MBL^wM;7HD= zuJew(581iHkP&NJE<3VJf}-%UX_1bc-;o1weeJLPT}E-pswanX46YS)10p|iuN))U zn$@Mf>_cYD4U7gcYy;Dv-3V#5T6j6mkxx$KZpfsQNBX-@$^OHvS(mOuyRGzv-!Zz! zC)6>ztt0P4wm2NNxaE=RuZ{WZip+*0{~slH21OR${gN2pgmeJM4fl0q2dTLWnMjK8 z$v2A!-;h#51P~I^pZ)G3I3~CEW!JL{S(FqM9mwbaXR!vZcy;L$S}QEh_2ofZ(iiU{ rjIq9q%@zN5Q~F=)Gv~GfGxq!kl@nT|Uc?DH00000NkvXXu0mjf&G2V( diff --git a/src/MaaCore/Task/BattleHelper.cpp b/src/MaaCore/Task/BattleHelper.cpp index c5a62cf07ea..9c4407f450c 100644 --- a/src/MaaCore/Task/BattleHelper.cpp +++ b/src/MaaCore/Task/BattleHelper.cpp @@ -3,6 +3,7 @@ #include #include +#include "Config/GeneralConfig.h" #include "Config/Miscellaneous/AvatarCacheManager.h" #include "Config/Miscellaneous/BattleDataConfig.h" #include "Config/TaskData.h" @@ -11,16 +12,19 @@ #include "Utils/ImageIo.hpp" #include "Utils/Logger.hpp" #include "Utils/NoWarningCV.h" +#include "Utils/Time.hpp" #include "Vision/Battle/BattlefieldClassifier.h" #include "Vision/Battle/BattlefieldMatcher.h" #include "Vision/BestMatcher.h" #include "Vision/Matcher.h" #include "Vision/RegionOCRer.h" +#include "Arknights-Tile-Pos/TileCalc2.hpp" + using namespace asst::battle; -asst::BattleHelper::BattleHelper(Assistant* inst) - : m_inst_helper(inst) +asst::BattleHelper::BattleHelper(Assistant* inst) : + m_inst_helper(inst) { } @@ -54,10 +58,7 @@ void asst::BattleHelper::clear() m_used_tiles.clear(); } -bool asst::BattleHelper::calc_tiles_info( - const std::string& stage_name, - double shift_x, - double shift_y) +bool asst::BattleHelper::calc_tiles_info(const std::string& stage_name, double shift_x, double shift_y) { LogTraceFunction; @@ -99,7 +100,7 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable, b LogTraceFunction; if (init) { - AvatarCache.remove_avatars(Role::Drone);// 移除小龙等不同技能很像的召唤物,防止错误识别 + AvatarCache.remove_avatars(Role::Drone); // 移除小龙等不同技能很像的召唤物,防止错误识别 wait_until_start(false); } @@ -157,14 +158,12 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable, b Log.trace("start matching cooling", oper.index); static const auto cooling_threshold = Task.get("BattleAvatarCoolingData")->templ_thresholds.front(); - static const auto cooling_mask_range = - Task.get("BattleAvatarCoolingData")->mask_ranges; + static const auto cooling_mask_range = Task.get("BattleAvatarCoolingData")->mask_ranges; avatar_analyzer.set_threshold(cooling_threshold); avatar_analyzer.set_mask_ranges(cooling_mask_range, true, true); } else { - static const auto threshold = - Task.get("BattleAvatarData")->templ_thresholds.front(); + static const auto threshold = Task.get("BattleAvatarData")->templ_thresholds.front(); static const auto drone_threshold = Task.get("BattleDroneAvatarData")->templ_thresholds.front(); avatar_analyzer.set_threshold(oper.role == Role::Drone ? drone_threshold : threshold); @@ -208,8 +207,7 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable, b } } - if (ranges::count_if(unknown_opers, [](const DeploymentOper& it) { return !it.cooling; }) > 0 - || init) { + if (ranges::count_if(unknown_opers, [](const DeploymentOper& it) { return !it.cooling; }) > 0 || init) { // 一个都没匹配上的,挨个点开来看一下 LogTraceScope("rec unknown opers"); @@ -285,6 +283,38 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable, b return check_in_battle(image); } +// if side = true, get top view of the selected operator, tile size is 5x5 +cv::Mat asst::BattleHelper::get_top_view(const cv::Mat& cam_img, bool side) +{ + if (!side) { + return cv::Mat {}; // TODO + } + + const std::vector world_points { + { -2.5, -2.5 }, + { 2.5, -2.5 }, + { -2.5, 2.5 }, + { 2.5, 2.5 }, + }; + const std::vector dst_points { + { 0, 0 }, + { 500, 0 }, + { 0, 500 }, + { 500, 500 }, + }; + std::vector screen_points; + for (const auto& point : world_points) { + cv::Vec3d temp { point.x, -point.y, -0.3967874050140381 }; + auto screen_pt = Map::TileCalc2::world_to_screen(m_map_data, temp, true); + screen_points.push_back(screen_pt); + } + + const auto xform = cv::getPerspectiveTransform(screen_points, dst_points); + cv::Mat result; + cv::warpPerspective(cam_img, result, xform, cv::Size(500, 500)); + return result; +} + bool asst::BattleHelper::update_kills(const cv::Mat& reusable) { cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable; @@ -314,10 +344,7 @@ bool asst::BattleHelper::update_cost(const cv::Mat& reusable) return true; } -bool asst::BattleHelper::deploy_oper( - const std::string& name, - const Point& loc, - DeployDirection direction) +bool asst::BattleHelper::deploy_oper(const std::string& name, const Point& loc, DeployDirection direction) { LogTraceFunction; @@ -337,9 +364,8 @@ bool asst::BattleHelper::deploy_oper( } Point target_point = target_iter->second.pos; - int dist = static_cast(Point::distance( - target_point, - { oper_rect.x + oper_rect.width / 2, oper_rect.y + oper_rect.height / 2 })); + int dist = static_cast( + Point::distance(target_point, { oper_rect.x + oper_rect.width / 2, oper_rect.y + oper_rect.height / 2 })); // 1000 是随便取的一个系数,把整数的 pre_delay 转成小数用的 int duration = static_cast(dist / 1000.0 * swipe_oper_task_ptr->pre_delay); @@ -347,9 +373,8 @@ bool asst::BattleHelper::deploy_oper( if (int min_duration = swipe_oper_task_ptr->special_params.at(4); duration < min_duration) { duration = min_duration; } - bool deploy_with_pause = ControlFeat::support( - m_inst_helper.ctrler()->support_features(), - ControlFeat::SWIPE_WITH_PAUSE); + bool deploy_with_pause = + ControlFeat::support(m_inst_helper.ctrler()->support_features(), ControlFeat::SWIPE_WITH_PAUSE); Point oper_point(oper_rect.x + oper_rect.width / 2, oper_rect.y + oper_rect.height / 2); m_inst_helper.ctrler()->swipe( oper_point, @@ -447,9 +472,7 @@ bool asst::BattleHelper::retreat_oper(const Point& loc, bool manually) m_used_tiles.erase(loc); if (manually) { - std::erase_if(m_battlefield_opers, [&loc](const auto& pair) -> bool { - return pair.second == loc; - }); + std::erase_if(m_battlefield_opers, [&loc](const auto& pair) -> bool { return pair.second == loc; }); } return true; } @@ -611,10 +634,7 @@ bool asst::BattleHelper::use_all_ready_skill(const cv::Mat& reusable) return used; } -bool asst::BattleHelper::check_and_use_skill( - const std::string& name, - bool& has_error, - const cv::Mat& reusable) +bool asst::BattleHelper::check_and_use_skill(const std::string& name, bool& has_error, const cv::Mat& reusable) { auto oper_iter = m_battlefield_opers.find(name); if (oper_iter == m_battlefield_opers.cend()) { @@ -624,10 +644,7 @@ bool asst::BattleHelper::check_and_use_skill( return check_and_use_skill(oper_iter->second, has_error, reusable); } -bool asst::BattleHelper::check_and_use_skill( - const Point& loc, - bool& has_error, - const cv::Mat& reusable) +bool asst::BattleHelper::check_and_use_skill(const Point& loc, bool& has_error, const cv::Mat& reusable) { cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable; BattlefieldClassifier skill_analyzer(image); @@ -658,14 +675,7 @@ void asst::BattleHelper::save_map(const cv::Mat& image) auto draw = image.clone(); for (const auto& [loc, info] : m_normal_tile_info) { cv::circle(draw, cv::Point(info.pos.x, info.pos.y), 5, cv::Scalar(0, 255, 0), -1); - cv::putText( - draw, - loc.to_string(), - cv::Point(info.pos.x - 30, info.pos.y), - 1, - 1.2, - cv::Scalar(0, 0, 255), - 2); + cv::putText(draw, loc.to_string(), cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2); } std::string suffix; @@ -732,9 +742,8 @@ bool asst::BattleHelper::click_oper_on_battlefield(const Point& loc) bool asst::BattleHelper::click_retreat() { LogTraceFunction; - bool deploy_with_pause = ControlFeat::support( - m_inst_helper.ctrler()->support_features(), - ControlFeat::SWIPE_WITH_PAUSE); + bool deploy_with_pause = + ControlFeat::support(m_inst_helper.ctrler()->support_features(), ControlFeat::SWIPE_WITH_PAUSE); if (deploy_with_pause) { ProcessTask(this_task(), { "BattlePause" }).run(); @@ -748,39 +757,48 @@ bool asst::BattleHelper::click_retreat() return ret; } -// TODO: use m_skill_button_pos bool asst::BattleHelper::click_skill(bool keep_waiting) { LogTraceFunction; - bool deploy_with_pause = ControlFeat::support( - m_inst_helper.ctrler()->support_features(), - ControlFeat::SWIPE_WITH_PAUSE); + bool deploy_with_pause = + ControlFeat::support(m_inst_helper.ctrler()->support_features(), ControlFeat::SWIPE_WITH_PAUSE); + bool pausing = false; if (!keep_waiting && deploy_with_pause) { - ProcessTask(this_task(), { "BattlePause" }).run(); + pausing = ProcessTask(this_task(), { "BattlePause" }).run(); + } + + cv::Mat top_view; + for (int retry = 0; retry < (keep_waiting ? 1000 : 5); ++retry) { + top_view = get_top_view(m_inst_helper.ctrler()->get_image(), true); + Matcher skill_analyzer { top_view }; + skill_analyzer.set_task_info("BattleSkillReadyOnClick-TopView"); + skill_analyzer.set_roi({ 250, 250, 250, 250 }); + if (skill_analyzer.analyze()) { + m_inst_helper.ctrler()->click(m_skill_button_pos); + if (pausing) { + ProcessTask(this_task(), { "BattlePauseCancel" }).run(); + } + return true; + } + m_inst_helper.sleep(Config.get_options().task_delay); } - ProcessTask skill_task( - this_task(), - { "BattleSkillReadyOnClick", - "BattleSkillReadyOnClick-SquareMap", - "BattleSkillStopOnClick", - "BattleSkillStopOnClick-MiddleMap", - "BattleSkillStopOnClick-SquareMap" }); - skill_task.set_task_delay(0); - if (keep_waiting) { - return skill_task.set_retry_times(1000).run(); + // this means false positive in skill ready detection + +#ifdef ASST_DEBUG + if (!top_view.empty()) { + using namespace asst::utils::path_literals; + asst::imwrite( + "debug"_p / "skill"_p / asst::utils::path(m_stage_name + '_' + utils::get_time_filestem() + ".png"), + top_view); } - else { - bool ret = skill_task.set_retry_times(5).run(); - if (!ret) { - cancel_oper_selection(); - } - if (deploy_with_pause) { - ProcessTask(this_task(), { "BattlePauseCancel" }).run(); - } - return ret; +#endif + cancel_oper_selection(); + if (pausing) { + ProcessTask(this_task(), { "BattlePauseCancel" }).run(); } + return false; } bool asst::BattleHelper::cancel_oper_selection() @@ -828,8 +846,7 @@ void asst::BattleHelper::fix_swipe_out_of_limit( }; // 旋转后偏移值会不够,计算补偿比例 - double adjust_more = - std::get<0>(adjust_scale) * direct.x + std::get<1>(adjust_scale) * direct.y; + double adjust_more = std::get<0>(adjust_scale) * direct.x + std::get<1>(adjust_scale) * direct.y; Point adjust = { static_cast(std::get<0>(adjust_scale) / adjust_more * distance), @@ -843,14 +860,7 @@ void asst::BattleHelper::fix_swipe_out_of_limit( }; } - Log.info( - __FUNCTION__, - "swipe end_point out of limit, start:", - p1, - ", end:", - p2, - ", adjust:", - adjust); + Log.info(__FUNCTION__, "swipe end_point out of limit, start:", p1, ", end:", p2, ", adjust:", adjust); p1 += adjust; p2 += adjust; } @@ -905,14 +915,11 @@ std::string asst::BattleHelper::analyze_detail_page_oper_name(const cv::Mat& ima return BattleData.is_name_invalid(det_name) ? std::string() : det_name; } -std::optional - asst::BattleHelper::get_oper_rect_on_deployment(const std::string& name) const +std::optional asst::BattleHelper::get_oper_rect_on_deployment(const std::string& name) const { LogTraceFunction; - auto oper_iter = ranges::find_if(m_cur_deployment_opers, [&](const auto& oper) { - return oper.name == name; - }); + auto oper_iter = ranges::find_if(m_cur_deployment_opers, [&](const auto& oper) { return oper.name == name; }); if (oper_iter == m_cur_deployment_opers.end()) { Log.error("No oper", name); return std::nullopt; diff --git a/src/MaaCore/Task/BattleHelper.h b/src/MaaCore/Task/BattleHelper.h index f597e302456..b39e17125f0 100644 --- a/src/MaaCore/Task/BattleHelper.h +++ b/src/MaaCore/Task/BattleHelper.h @@ -39,6 +39,8 @@ namespace asst bool update_kills(const cv::Mat& reusable = cv::Mat()); bool update_cost(const cv::Mat& reusable = cv::Mat()); + cv::Mat get_top_view(const cv::Mat& cam_img, bool side = true); + bool deploy_oper(const std::string& name, const Point& loc, battle::DeployDirection direction); bool retreat_oper(const std::string& name); bool retreat_oper(const Point& loc, bool manually = true);