Files
MaaAssistantArknights/src/MaaCore/Controller/ControlScaleProxy.cpp

210 lines
6.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "ControlScaleProxy.h"
#include "Config/GeneralConfig.h"
#include "Utils/Logger.hpp"
asst::ControlScaleProxy::ControlScaleProxy(
std::shared_ptr<ControllerAPI> controller,
ControllerType controller_type,
ProxyCallback proxy_callback) :
m_controller(controller),
m_controller_type(controller_type),
m_callback(proxy_callback),
m_rand_engine(std::random_device {}())
{
auto screen_res = m_controller->get_screen_res();
int width = screen_res.first;
int height = screen_res.second;
auto info = json::object { {
"details",
json::object {
{ "width", width },
{ "height", height },
},
} };
if (width < WindowWidthDefault || height < WindowHeightDefault) {
info["what"] = "UnsupportedResolution";
info["why"] = "Low screen resolution";
callback(info);
throw std::runtime_error("Unsupported resolution");
}
else if (
std::fabs(
static_cast<double>(WindowWidthDefault) / static_cast<double>(WindowHeightDefault) -
static_cast<double>(width) / static_cast<double>(height)) > 1e-7) {
info["what"] = "UnsupportedResolution";
info["why"] = "Not 16:9";
callback(info);
throw std::runtime_error("Unsupported resolution");
}
/* calc ratio */
constexpr double DefaultRatio = static_cast<double>(WindowWidthDefault) / static_cast<double>(WindowHeightDefault);
double cur_ratio = static_cast<double>(width) / static_cast<double>(height);
if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9按照高度计算缩放
|| std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) {
int scale_width = static_cast<int>(cur_ratio * WindowHeightDefault);
m_scale_size = std::make_pair(scale_width, WindowHeightDefault);
m_control_scale = static_cast<double>(height) / static_cast<double>(WindowHeightDefault);
}
else { // 否则可能是偏正方形的屏幕,按宽度计算
int scale_height = static_cast<int>(WindowWidthDefault / cur_ratio);
m_scale_size = std::make_pair(WindowWidthDefault, scale_height);
m_control_scale = static_cast<double>(width) / static_cast<double>(WindowWidthDefault);
}
info["what"] = "ResolutionInfo";
info["why"] = "Normal";
callback(info);
}
bool asst::ControlScaleProxy::click(const Point& p)
{
int x = static_cast<int>(p.x * m_control_scale);
int y = static_cast<int>(p.y * m_control_scale);
Log.trace("Click with scaled coordinates", p, m_control_scale);
return m_controller->click(Point(x, y));
}
bool asst::ControlScaleProxy::click(const Rect& rect)
{
return click(rand_point_in_rect(rect));
}
bool asst::ControlScaleProxy::swipe(
const Point& p1,
const Point& p2,
int duration,
bool extra_swipe,
double slope_in,
double slope_out,
bool with_pause,
bool high_resolution_swipe_fix)
{
int x1 = static_cast<int>(p1.x * m_control_scale);
int y1 = static_cast<int>(p1.y * m_control_scale);
int x2, y2;
if (high_resolution_swipe_fix) {
// 保持滑动距离一致:先计算原始的相对偏移,再加到缩放后的起点上
int dx = p2.x - p1.x; // 不缩放
int dy = p2.y - p1.y;
x2 = x1 + dx;
y2 = y1 + dy;
Log.trace("High-resolution swipe fix, offset", Point(dx, dy));
}
else {
x2 = static_cast<int>(p2.x * m_control_scale);
y2 = static_cast<int>(p2.y * m_control_scale);
}
Log.trace("Swipe with scaled coordinates", p1, p2, m_control_scale);
return m_controller->swipe(Point(x1, y1), Point(x2, y2), duration, extra_swipe, slope_in, slope_out, with_pause);
}
bool asst::ControlScaleProxy::swipe(
const Rect& r1,
const Rect& r2,
int duration,
bool extra_swipe,
double slope_in,
double slope_out,
bool with_pause,
bool high_resolution_swipe_fix)
{
Point rand_p1, rand_p2;
bool precise1 = (r1.width == 1 && r1.height == 1);
bool precise2 = (r2.width == 1 && r2.height == 1);
if (precise1) {
rand_p1 = Point(r1.x, r1.y);
}
else {
rand_p1 = rand_point_in_rect(r1);
}
if (precise2) {
rand_p2 = Point(r2.x, r2.y);
}
else {
rand_p2 = rand_point_in_rect(r2);
}
if (m_controller_type == ControllerType::Adb && !(precise1 && precise2)) {
// 只有不是精确点时才做ADB修正
// 同样的参数 ADB 总是划过头,糊点屎进来
// 外部调用 swipe(Point, Point) 时,说明是精确要求位置的,不能做这个调整
// 所以屎没法糊在下面一层,只能糊在这里了(
const auto& opt = Config.get_options();
auto x_dist = rand_p1.x - rand_p2.x;
rand_p2.x = rand_p1.x - static_cast<int>(x_dist * opt.adb_swipe_x_distance_multiplier);
}
return swipe(rand_p1, rand_p2, duration, extra_swipe, slope_in, slope_out, with_pause, high_resolution_swipe_fix);
}
bool asst::ControlScaleProxy::inject_input_event(InputEvent event)
{
switch (event.type) {
case InputEvent::Type::TOUCH_DOWN:
case InputEvent::Type::TOUCH_MOVE:
event.point.x = static_cast<int>(event.point.x * m_control_scale);
event.point.y = static_cast<int>(event.point.y * m_control_scale);
break;
default:
break;
}
return m_controller->inject_input_event(event);
}
std::pair<int, int> asst::ControlScaleProxy::get_scale_size() const noexcept
{
return m_scale_size;
}
asst::Point asst::ControlScaleProxy::rand_point_in_rect(const Rect& r)
{
// 过小矩形直接返回中心点,避免死循环
if (r.width <= 2 || r.height <= 2) {
return { r.x + r.width / 2, r.y + r.height / 2 };
}
constexpr double kStdDevFactor = 3.0;
const double std_dev_x = r.width / kStdDevFactor;
const double std_dev_y = r.height / kStdDevFactor;
std::normal_distribution<double> dist_x(r.x + r.width / 2.0, std_dev_x);
std::normal_distribution<double> dist_y(r.y + r.height / 2.0, std_dev_y);
// 优先进行有限次拒绝采样
constexpr int kMaxAttempts = 8;
for (int i = 0; i < kMaxAttempts; ++i) {
const int x = static_cast<int>(std::round(dist_x(m_rand_engine)));
const int y = static_cast<int>(std::round(dist_y(m_rand_engine)));
if (x < r.x || x >= r.x + r.width || y < r.y || y >= r.y + r.height) {
continue;
}
return { x, y };
}
LogWarn << "Too many sampling attempts";
// 返回中心点
return { r.x + r.width / 2, r.y + r.height / 2 };
}
void asst::ControlScaleProxy::callback(const json::object& details)
{
if (m_callback) {
m_callback(details);
}
}