Compare commits
65 Commits
v1.3-alpha
...
v1.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8143501c1 | ||
|
|
3c16fccca9 | ||
|
|
c79124f7c4 | ||
|
|
d434d423e3 | ||
|
|
f66c1e1c59 | ||
|
|
3b03f50a4a | ||
|
|
0ca13ae16a | ||
|
|
ae020bda5d | ||
|
|
9cf5c351f3 | ||
|
|
574ec830a0 | ||
|
|
6e8e5e80d2 | ||
|
|
27c228e355 | ||
|
|
273c2b19ad | ||
|
|
29d14f5a7b | ||
|
|
94cccfe424 | ||
|
|
ad1704df0d | ||
|
|
bf6d929e80 | ||
|
|
67036398bd | ||
|
|
4a8c20c604 | ||
|
|
b6c6d46362 | ||
|
|
c36c2c74ff | ||
|
|
ed9e017ebb | ||
|
|
762f701593 | ||
|
|
254f34a50d | ||
|
|
9f5564522d | ||
|
|
6bbc2c5615 | ||
|
|
34c50898d5 | ||
|
|
736244162c | ||
|
|
dbec3ae7ed | ||
|
|
1395ec46b6 | ||
|
|
27fc4a6bf6 | ||
|
|
33f7e677d7 | ||
|
|
716d1971c9 | ||
|
|
bbaf8cdf80 | ||
|
|
09cb5b271a | ||
|
|
7f1eff2188 | ||
|
|
70a1428fa3 | ||
|
|
0dd6524da9 | ||
|
|
92c53f4a8b | ||
|
|
51f51f4d3b | ||
|
|
5722ca6191 | ||
|
|
08d80e34b0 | ||
|
|
99f837ad7b | ||
|
|
35de8f5796 | ||
|
|
805b7d9d78 | ||
|
|
96f0d45680 | ||
|
|
32bfed1826 | ||
|
|
5b12678dcd | ||
|
|
7aec6eb49c | ||
|
|
c3fb83b241 | ||
|
|
808ae2307b | ||
|
|
1bbc1b41cb | ||
|
|
908cd9fe72 | ||
|
|
7e935aaaea | ||
|
|
c334b323aa | ||
|
|
08230c2731 | ||
|
|
8d30393596 | ||
|
|
ab62f28a15 | ||
|
|
7df3cdc8c8 | ||
|
|
929a8fb1e1 | ||
|
|
ff91fc1d70 | ||
|
|
d6d0af43b4 | ||
|
|
909d123f8f | ||
|
|
10283bf72a | ||
|
|
c8a2fe3099 |
4
.gitignore
vendored
@@ -427,4 +427,6 @@ FodyWeavers.xsd
|
||||
*.sln.iml
|
||||
|
||||
screen.png
|
||||
adb_screen.png
|
||||
adb_screen.png
|
||||
tools/*.png
|
||||
resource/infrast
|
||||
|
||||
168
3rdparty/include/meojson/json_aux.h
vendored
@@ -5,93 +5,93 @@
|
||||
|
||||
namespace json
|
||||
{
|
||||
static std::string unescape_string(std::string&& str)
|
||||
{
|
||||
std::string replace_str;
|
||||
std::string escape_str = std::move(str);
|
||||
static std::string unescape_string(std::string&& str)
|
||||
{
|
||||
std::string replace_str;
|
||||
std::string escape_str = std::move(str);
|
||||
|
||||
for (size_t pos = 0; pos < escape_str.size(); ++pos)
|
||||
{
|
||||
switch (escape_str[pos]) {
|
||||
case '\"':
|
||||
replace_str = R"(\")";
|
||||
break;
|
||||
case '\\':
|
||||
replace_str = R"(\\)";
|
||||
break;
|
||||
case '\b':
|
||||
replace_str = R"(\b)";
|
||||
break;
|
||||
case '\f':
|
||||
replace_str = R"(\f)";
|
||||
break;
|
||||
case '\n':
|
||||
replace_str = R"(\n)";
|
||||
break;
|
||||
case '\r':
|
||||
replace_str = R"(\r)";
|
||||
break;
|
||||
case '\t':
|
||||
replace_str = R"(\t)";
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
escape_str.replace(pos, 1, replace_str);
|
||||
++pos;
|
||||
}
|
||||
return escape_str;
|
||||
}
|
||||
for (size_t pos = 0; pos < escape_str.size(); ++pos)
|
||||
{
|
||||
switch (escape_str[pos]) {
|
||||
case '\"':
|
||||
replace_str = R"(\")";
|
||||
break;
|
||||
case '\\':
|
||||
replace_str = R"(\\)";
|
||||
break;
|
||||
case '\b':
|
||||
replace_str = R"(\b)";
|
||||
break;
|
||||
case '\f':
|
||||
replace_str = R"(\f)";
|
||||
break;
|
||||
case '\n':
|
||||
replace_str = R"(\n)";
|
||||
break;
|
||||
case '\r':
|
||||
replace_str = R"(\r)";
|
||||
break;
|
||||
case '\t':
|
||||
replace_str = R"(\t)";
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
escape_str.replace(pos, 1, replace_str);
|
||||
++pos;
|
||||
}
|
||||
return escape_str;
|
||||
}
|
||||
|
||||
static std::string unescape_string(const std::string& str)
|
||||
{
|
||||
return unescape_string(std::string(str));
|
||||
}
|
||||
static std::string unescape_string(const std::string& str)
|
||||
{
|
||||
return unescape_string(std::string(str));
|
||||
}
|
||||
|
||||
static std::string escape_string(std::string&& str)
|
||||
{
|
||||
std::string escape_str = std::move(str);
|
||||
static std::string escape_string(std::string&& str)
|
||||
{
|
||||
std::string escape_str = std::move(str);
|
||||
|
||||
for (size_t pos = 0; pos + 1 < escape_str.size(); ++pos)
|
||||
{
|
||||
if (escape_str[pos] != '\\') {
|
||||
continue;
|
||||
}
|
||||
std::string replace_str;
|
||||
switch (escape_str[pos+1]) {
|
||||
case '"':
|
||||
replace_str = "\"";
|
||||
break;
|
||||
case '\\':
|
||||
replace_str = "\\";
|
||||
break;
|
||||
case 'b':
|
||||
replace_str = "\b";
|
||||
break;
|
||||
case 'f':
|
||||
replace_str = "\f";
|
||||
break;
|
||||
case 'n':
|
||||
replace_str = "\n";
|
||||
break;
|
||||
case 'r':
|
||||
replace_str = "\r";
|
||||
break;
|
||||
case 't':
|
||||
replace_str = "\r";
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
break;
|
||||
}
|
||||
escape_str.replace(pos, 2, replace_str);
|
||||
}
|
||||
return escape_str;
|
||||
}
|
||||
for (size_t pos = 0; pos + 1 < escape_str.size(); ++pos)
|
||||
{
|
||||
if (escape_str[pos] != '\\') {
|
||||
continue;
|
||||
}
|
||||
std::string replace_str;
|
||||
switch (escape_str[pos + 1]) {
|
||||
case '"':
|
||||
replace_str = "\"";
|
||||
break;
|
||||
case '\\':
|
||||
replace_str = "\\";
|
||||
break;
|
||||
case 'b':
|
||||
replace_str = "\b";
|
||||
break;
|
||||
case 'f':
|
||||
replace_str = "\f";
|
||||
break;
|
||||
case 'n':
|
||||
replace_str = "\n";
|
||||
break;
|
||||
case 'r':
|
||||
replace_str = "\r";
|
||||
break;
|
||||
case 't':
|
||||
replace_str = "\t";
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
break;
|
||||
}
|
||||
escape_str.replace(pos, 2, replace_str);
|
||||
}
|
||||
return escape_str;
|
||||
}
|
||||
|
||||
static std::string escape_string(const std::string& str)
|
||||
{
|
||||
return escape_string(std::string(str));
|
||||
}
|
||||
static std::string escape_string(const std::string& str)
|
||||
{
|
||||
return escape_string(std::string(str));
|
||||
}
|
||||
}
|
||||
BIN
3rdparty/lib/libmeojson.lib
vendored
43529
3rdparty/resource/penguin-stats-recognize/json/stages.json
vendored
143
LICENSE
@@ -1,5 +1,5 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
@@ -7,17 +7,15 @@
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@@ -72,7 +60,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
82
README.md
@@ -16,7 +16,7 @@
|
||||
|
||||
A game assistance for Arknights
|
||||
|
||||
一款明日方舟的游戏辅助,供PC端安卓模拟器使用,也勉强兼容安卓手机,绝赞开发中……
|
||||
一款明日方舟的游戏助手,自动刷理智、智能基建换班、公招识别,纯图像识别,全图形化界面,兼容模拟器和安卓设备,开罐即食,绝赞开发中!✿✿ヽ(°▽°)ノ✿
|
||||
<br>
|
||||
|
||||

|
||||
@@ -37,6 +37,7 @@ A game assistance for Arknights
|
||||
- 单设施内最优解,会计算单设施内所有特殊干员组合
|
||||
- 可识别赤金、经验书,分别使用不同的干员组合
|
||||
- `迷迭香`等跨设施体系正在开发中!
|
||||
- 详细换班功能介绍见[基建换班说明](#基建换班)
|
||||
- 自动刷理智
|
||||
- 支持刷完自动上传[企鹅物流数据统计](https://penguin-stats.cn/)
|
||||
- 界面支持统计掉落数量
|
||||
@@ -57,7 +58,7 @@ A game assistance for Arknights
|
||||
- 自动帮你点击时间9小时
|
||||
- 出5、6星干员弹窗提示
|
||||
- 最新版本已支持夏活新增的`煌`、`灰喉`等干员
|
||||
- 不会帮你点击确定按钮!!!请自行检查辅助选择的是否正确,若出现识别错误,遗漏了高星干员,作者概不负责哦__(:з」∠)_
|
||||
- 不会帮你点击确定按钮!!!请自行检查软件选择的是否正确,若出现识别错误,遗漏了高星干员,作者概不负责哦__(:з」∠)_
|
||||
- 自动访问好友
|
||||
- 访问完了还会贴心的帮你点进信用商店~
|
||||
- 可设置访问完自动买信用商店的材料
|
||||
@@ -65,10 +66,11 @@ A game assistance for Arknights
|
||||
- 所有点击操作,都是点击按钮内随机位置,并模拟泊松分布(按钮偏中间位置点的概率大,越往旁边点到的概率越小)
|
||||
- 刷理智及访问好友的点击操作,支持设置随机延时,没有封号风险~
|
||||
- 底层算法纯C++开发,并设计了多重的缓存技术,最大限度降低CPU和内存占用
|
||||
- 模拟器窗口可以被遮挡、可以最小化、甚至可以老板键隐藏!即使全屏看视频、玩游戏,也完全不影响辅助运行
|
||||
- 模拟器窗口可以被遮挡、可以最小化、甚至可以老板键隐藏!即使全屏看视频、玩游戏,也完全不影响软件运行
|
||||
- 软件支持自动更新✿✿ヽ(°▽°)ノ✿
|
||||
- 支持多款主流模拟器
|
||||
- 勉强兼容安卓手机(USB调试、无线调试)
|
||||
- 兼容安卓手机(USB调试、无线调试)
|
||||
- 兼容非16:9分辨率:包括宽屏、方屏,也兼容在游戏设置中的异形屏适配(基建换班功能除外,正在适配中)。但仍推荐使用16:9分辨率
|
||||
- 自适应分辨率及屏幕缩放
|
||||
- 未来更多功能见[Todo](#Todo)
|
||||
|
||||
@@ -88,11 +90,11 @@ A game assistance for Arknights
|
||||
|
||||
#### MuMu模拟器
|
||||
|
||||
支持
|
||||
完美支持
|
||||
|
||||
#### 雷电模拟器
|
||||
|
||||
勉强支持,偶尔能用偶尔用不了,莫名其妙的,有空再修,暂时不建议用_(:з」∠)_
|
||||
支持
|
||||
|
||||
#### 逍遥模拟器
|
||||
|
||||
@@ -121,7 +123,7 @@ A game assistance for Arknights
|
||||
|
||||
1. 根据上面模拟器支持情况,进行对应的`ADB`相关操作
|
||||
2. 解压压缩包,到**没有中文或特殊符号**的文件夹路径
|
||||
3. 第一次运行辅助,**请使用管理员权限**打开`MeoAsstGui.exe`。运行过一次后,后续不再需要管理员权限(之后的版本会尝试完全去掉管理员权限)
|
||||
3. 第一次运行软件,**请使用管理员权限**打开`MeoAsstGui.exe`。运行过一次后,后续不再需要管理员权限(之后的版本会尝试完全去掉管理员权限)
|
||||
4. 运行期间,模拟器窗口可以最小化,全屏玩游戏、看视频等,完全不影响
|
||||
|
||||
### 刷理智
|
||||
@@ -131,13 +133,39 @@ A game assistance for Arknights
|
||||
3. 点击"开始刷理智",开始后上面的选项也可以随时修改
|
||||
4. 刷完了会自动停止的
|
||||
|
||||
### 基建换班
|
||||
|
||||
1. 明日方舟处于主界面,或刚进入基建的界面(基建界面没有进行过缩放、干员排序调整等)
|
||||
2. 根据你的需要勾选左侧需要进行换班的设施(也可以拖动调整换班顺序);设置换班模式、无人机、心情阈值等
|
||||
3. 点击开始换班,开始执行后上述设置不可修改
|
||||
|
||||
#### 换班策略
|
||||
|
||||
自动计算并选择单设施内的最优解,支持所有通用类技能和特殊技能组合(但并非跨设施的最优解,如`迷迭香`这类跨设施间联动的体系,暂不支持)
|
||||
|
||||
#### 温和换班模式
|
||||
|
||||
会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班
|
||||
|
||||
#### 激进换班模式
|
||||
|
||||
会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员
|
||||
|
||||
#### 偏激换班模式
|
||||
|
||||
会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作(放心,辅助不会傻到把某个干员在几个设施之间来回倒腾的_(:з」∠)_
|
||||
|
||||
#### 宿舍入驻心情阈值
|
||||
|
||||
计算心情进度条的百分比;心情小于该阈值的干员,无论如何也不上班(不管上面的模式你选择了哪一种),直接进驻宿舍
|
||||
|
||||
### 公开招募识别
|
||||
|
||||
1. 明日方舟打开公开招募,有Tag选择的界面
|
||||
2. 辅助勾选你需要的选项,点击"开始识别"
|
||||
2. 软件勾选你需要的选项,点击"开始识别"
|
||||
3. 请检查识别结果是否正确,自行判断是否确定开始招募
|
||||
|
||||
再次强调,本辅助仅会帮你选择最优Tags和时间,不会帮你点击确定按钮!!!请自行检查辅助选择的是否正确,若出现识别错误,遗漏了高星干员,作者概不负责哦__(:з」∠)_
|
||||
再次强调,本软件仅会帮你选择最优Tags和时间,不会帮你点击确定按钮!!!请自行检查软件选择的是否正确,若出现识别错误,遗漏了高星干员,作者概不负责哦__(:з」∠)_
|
||||
|
||||
### 访问好友
|
||||
|
||||
@@ -147,24 +175,16 @@ A game assistance for Arknights
|
||||
4. 然后会贴心的帮你跳转到信用商店,顺便收了当天信用~
|
||||
5. 信用商店随缘买,就是从左到右依次买,但不会买`碳`和`家具零件`
|
||||
|
||||
### 不使用模拟器,而是USB连接安卓手机
|
||||
### 连接自定义模拟器端口,或安卓手机/平板
|
||||
|
||||
1. 下载[ADB程序](https://dl.google.com/android/repository/platform-tools-latest-windows.zip),将`platform-tools`文件夹解压到`MeoAsstGui.exe`的同级目录
|
||||
2. 使用USB有线连接安卓手机和电脑
|
||||
3. 请在手机`设置`-`开发者选项`中打开`USB调试`、`USB调试(安全设置)`两个选项。具体操作方式不同品牌手机各不相同,请自行百度查询
|
||||
4. 请手动修改`resource\config.json`文件中,`options`.`connectType`为`1`。文件保存后请重新打开程序
|
||||
5. 当前仅兼容刷理智、公招计算功能,且公招计算暂时无法设置时间
|
||||
4. 请手动修改`resource\config.json`文件中:
|
||||
1. `options`.`connectType`字段值为`1`
|
||||
2. `emulator`.`Custom`.`adb`.`addresses`字段填写为要连接的地址,请注意这是个数组,会以此尝试所有的(若不填写,或`addresses`中所有的都没连上,则会使用`adb devices`自动查找)
|
||||
|
||||
### 不使用模拟器,而是无线连接安卓手机
|
||||
|
||||
1. 下载[ADB程序](https://dl.google.com/android/repository/platform-tools-latest-windows.zip),将`platform-tools`文件夹解压到`MeoAsstGui.exe`的同级目录
|
||||
2. 请保证安卓手机和电脑处于同一局域网中
|
||||
3. 请在手机`设置`-`开发者选项`中打开`USB调试`、`USB调试(安全设置)`、`无线调试`三个选项。具体操作方式不同品牌手机各不相同,请自行百度查询
|
||||
4. 请手动修改`resource\config.json`文件中,`options`.`connectType`为`2`
|
||||
5. 请手动修改`resource\config.json`文件中,`options`.`connectRemoteAddress`为无线调试的IP地址和端口号。文件保存后请重新打开程序
|
||||
6. 当前仅兼容刷理智、公招计算功能,且公招计算暂时无法设置时间
|
||||
|
||||
**警告:`无线调试`是非常危险的选项,若局域网中存在其他攻击性设备或程序,可能导致手机数据泄露或丢失,请勿在公共的WLAN中使用该功能!**
|
||||
目前非16:9分辨率下,刷理智、访问好友、公招识别已初步可用,基建功能暂不可用,后期将逐步优化。但仍推荐使用16:9分辨率,经过的测试验证最多,也最稳定。
|
||||
|
||||
### 设置操作延时
|
||||
|
||||
@@ -179,11 +199,11 @@ A game assistance for Arknights
|
||||
- [x] 支持设置吃理智药数量
|
||||
- [x] 信用商店`看着买`功能
|
||||
- [x] 支持所有主流模拟器
|
||||
- [ ] adb连接逻辑重构
|
||||
- [x] adb连接逻辑重构
|
||||
- [ ] 任务队列功能
|
||||
- [ ] 常用关卡选关
|
||||
- [ ] 自动收任务功能
|
||||
- [ ] 基建智能换班功能
|
||||
- [x] 基建智能换班功能
|
||||
- [x] 图形化界面
|
||||
- [x] 干员技能识别
|
||||
- [x] 干员识别准确率提高到100%
|
||||
@@ -192,10 +212,10 @@ A game assistance for Arknights
|
||||
- [x] 发电站、办公室换班
|
||||
- [x] 使用无人机
|
||||
- [ ] 控制中枢智能换班
|
||||
- [ ] 会客室智能换班
|
||||
- [ ] 会客室智能线索交流
|
||||
- [x] 会客室智能换班
|
||||
- [x] 会客室智能线索交流
|
||||
- [ ] 支持`迷迭香`等复杂基建体系
|
||||
- [ ] `激进换班模式`
|
||||
- [x] `激进换班模式`
|
||||
- [ ] 自定义换班(手动修改配置文件)
|
||||
- [ ] 使用GPU进行识别的版本
|
||||
- [x] 关卡掉落识别
|
||||
@@ -221,6 +241,7 @@ A game assistance for Arknights
|
||||
- 文字识别库:[chineseocr_lite](https://github.com/DayBreak-u/chineseocr_lite.git)
|
||||
- 关卡掉落识别:[企鹅物流识别](https://github.com/KumoSiunaus/penguin-stats-recognize-v3)
|
||||
- C++ JSON库:[meojson](https://github.com/MistEO/meojson.git)
|
||||
- C++ 运算符解析器:[calculator](https://github.com/kimwalisch/calculator)
|
||||
- WPF MVVW框架:[Stylet](https://github.com/canton7/Stylet)
|
||||
- WPF控件库:[HandyControl](https://github.com/HandyOrg/HandyControl)
|
||||
- C# JSON库: [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
|
||||
@@ -236,10 +257,13 @@ A game assistance for Arknights
|
||||
- 非常感谢 [tcyh035](https://github.com/tcyh035) 帮忙设计重构图形界面
|
||||
- 非常感谢 [GengGode](https://github.com/GengGode) 和 [DbgDebug](https://github.com/DbgDebug) 提供图像算法思路并协助验证
|
||||
- 非常感谢 [LoveLoliii](https://github.com/LoveLoliii) 提供公开招募算法及数据、部分功能逻辑思路
|
||||
- 感谢[AAH](https://github.com/ninthDevilHAUNSTER/ArknightsAutoHelper)的大佬们协助提供部分图像、操作思路
|
||||
- 感谢参与软件测试、提bug的小伙伴们~
|
||||
- ~~感谢[B站直播间](https://live.bilibili.com/2808861)的小伙伴们陪我弹幕吹水~~
|
||||
|
||||
## 广告
|
||||
|
||||
[B站直播间](https://live.bilibili.com/2808861):每晚直播敲代码,近期很长一段时间应该都是在写本辅助软件
|
||||
[QQ群](https://jq.qq.com/?_wv=1027&k=ypbzXcA2):欢迎加入~
|
||||
[B站直播间](https://live.bilibili.com/2808861):每晚直播敲代码,近期很长一段时间应该都是在写本助手软件
|
||||
[QQ群](https://jq.qq.com/?_wv=1027&k=ypbzXcA2):欢迎加入~
|
||||
|
||||
另:作者前端苦手,寻大佬帮忙一起写界面,继续现在的WPF也行,用electron或者别的重写也行,球球了QAQ
|
||||
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 60 KiB |
@@ -13,15 +13,15 @@ extern "C" {
|
||||
|
||||
bool MEOAPI AsstCatchDefault(void* p_asst);
|
||||
bool MEOAPI AsstCatchEmulator(void* p_asst);
|
||||
bool MEOAPI AsstCatchUSB(void* p_asst);
|
||||
bool MEOAPI AsstCatchRemote(void* p_asst, const char* address);
|
||||
bool MEOAPI AsstCatchCustom(void* p_asst);
|
||||
bool MEOAPI AsstCatchSpecific(void* p_asst, const char* address);
|
||||
bool MEOAPI AsstCatchFake(void* p_asst);
|
||||
|
||||
bool MEOAPI AsstStartSanity(void* p_asst);
|
||||
bool MEOAPI AsstStartVisit(void* p_asst, bool with_shopping);
|
||||
bool MEOAPI AsstStartProcessTask(void* p_asst, const char* task);
|
||||
bool MEOAPI AsstStartRecruiting(void* p_asst, const int required_level[], int required_len, bool set_time);
|
||||
bool MEOAPI AsstStartInfrastShift(void* p_asst, const char** order, int order_size, int uses_of_drones, double dorm_threshold);
|
||||
bool MEOAPI AsstStartInfrastShift(void* p_asst, int work_mode, const char** order, int order_size, int uses_of_drones, double dorm_threshold);
|
||||
bool MEOAPI AsstStartDebugTask(void* p_asst);
|
||||
|
||||
void MEOAPI AsstStop(void* p_asst);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"version": "0.8",
|
||||
{
|
||||
"version": "0.9",
|
||||
"options": {
|
||||
"connectType": 0,
|
||||
"connectType_Doc": "连接类型:0-连接电脑上的模拟器,1-连接USB连接的安卓设备,2-连接局域网中的安卓设备。默认0",
|
||||
"connectRemoteAddress": "",
|
||||
"connectRemoteAddress_Doc": "连接局域网中的安卓设备的地址,仅在connectType为2时生效。格式举例:192.168.1.123:5678",
|
||||
"connectType_Doc": "连接类型:0-连接电脑上的模拟器;1-连接自定义端口,请手动修改'emulator.Custom'中的相关字段。默认0",
|
||||
"taskDelay": 1000,
|
||||
"taskDelay_Doc": "识别的延迟:越快识别频率越快,但会增加CPU消耗。单位毫秒,默认1000",
|
||||
"printWindow": false,
|
||||
@@ -20,7 +18,7 @@
|
||||
"controlDelayRange_Doc": "点击随机延时:每次点击操作会进行随机延时,降低封号风险(好像也没听说过谁被封号的)。格式为 [ 最小延时, 最大延时 ],单位为毫秒。例如想设置3~5秒延时,即修改为[ 3000, 5000 ],默认0~0",
|
||||
"ocrThreadNumber": 4,
|
||||
"ocrThreadNumber_Doc": "文字识别库OcrLite的线程数量,理论上开的高点识别会快点,但是会更卡。默认4",
|
||||
"adbExtraSwipeDist": 20,
|
||||
"adbExtraSwipeDist": 50,
|
||||
"adbExtraSwipeDist_Doc": "额外的滑动距离:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来",
|
||||
"adbExtraSwipeDuration": 1000,
|
||||
"adbExtraSwipeDuration_Doc": "额外的滑动持续时间:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来。若小于0,则关闭额外滑动功能"
|
||||
@@ -34,12 +32,15 @@
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]Engine\\ProgramFiles\\HD-Adb.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:5555",
|
||||
"click": "[Adb] -s 127.0.0.1:5555 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s 127.0.0.1:5555 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s 127.0.0.1:5555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s 127.0.0.1:5555 exec-out screencap -p"
|
||||
"addresses": [ "127.0.0.1:5555" ],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayFormat": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] exec-out screencap -p"
|
||||
}
|
||||
},
|
||||
"BlueStacksInternational": {
|
||||
@@ -49,12 +50,15 @@
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]HD-Adb.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:5555",
|
||||
"click": "[Adb] -s 127.0.0.1:5555 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s 127.0.0.1:5555 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s 127.0.0.1:5555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s 127.0.0.1:5555 exec-out screencap -p"
|
||||
"addresses": [ "127.0.0.1:5555" ],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayFormat": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] exec-out screencap -p"
|
||||
}
|
||||
},
|
||||
"MuMuEmulator": {
|
||||
@@ -64,42 +68,33 @@
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]..\\vmonitor\\bin\\adb_server.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:7555",
|
||||
"click": "[Adb] -s 127.0.0.1:7555 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s 127.0.0.1:7555 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s 127.0.0.1:7555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s 127.0.0.1:7555 exec-out screencap -p"
|
||||
"addresses": [ "127.0.0.1:7555" ],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayFormat": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] exec-out screencap -p"
|
||||
}
|
||||
},
|
||||
"LDPlayer": {
|
||||
"handle": {
|
||||
"class": "LDPlayerMainFrame",
|
||||
"window": "雷电模拟器"
|
||||
"window": ""
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]adb.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:5555",
|
||||
"click": "[Adb] -s emulator-5554 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s emulator-5554 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s emulator-5554 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s emulator-5554 shell screencap -p"
|
||||
}
|
||||
},
|
||||
"LDPlayer64": {
|
||||
"handle": {
|
||||
"class": "LDPlayerMainFrame",
|
||||
"window": "雷电模拟器(64)"
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]adb.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:5555",
|
||||
"click": "[Adb] -s emulator-5554 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s emulator-5554 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s emulator-5554 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s emulator-5554 shell screencap -p"
|
||||
"addresses": [ "127.0.0.1:5555", "emulator-5554" ],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayFormat": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] shell screencap -p"
|
||||
}
|
||||
},
|
||||
"Nox": {
|
||||
@@ -109,12 +104,15 @@
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]nox_adb.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:62001",
|
||||
"click": "[Adb] -s 127.0.0.1:62001 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s 127.0.0.1:62001 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s 127.0.0.1:62001 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s 127.0.0.1:62001 exec-out screencap -p"
|
||||
"addresses": [ "127.0.0.1:62001", "127.0.0.1:59865" ],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayFormat": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] exec-out screencap -p"
|
||||
}
|
||||
},
|
||||
"XYAZ": {
|
||||
@@ -124,46 +122,36 @@
|
||||
},
|
||||
"adb": {
|
||||
"path": "[EmulatorPath]adb.exe",
|
||||
"connect": "[Adb] connect 127.0.0.1:21503",
|
||||
"click": "[Adb] -s 127.0.0.1:21503 shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s 127.0.0.1:21503 shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s 127.0.0.1:21503 shell dumpsys window displays | grep init=",
|
||||
"displayRegex": " init=%dx%d",
|
||||
"screencap": "[Adb] -s 127.0.0.1:21503 shell screencap -p"
|
||||
"addresses": [ "127.0.0.1:21503" ],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init=",
|
||||
"displayFormat": " init=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] shell screencap -p"
|
||||
}
|
||||
},
|
||||
"Remote": {
|
||||
"Custom": {
|
||||
"handle": {
|
||||
"class": "Remote Class",
|
||||
"window": "Remote Window",
|
||||
"class": "Custom Class",
|
||||
"window": "Custom Window",
|
||||
"Doc": "这俩不是用来捕获窗口的,是为了兼容现有的捕获模拟器的字段,随手写的,这两个字段实际不会使用到"
|
||||
},
|
||||
"adb": {
|
||||
"Doc": "1.请将Adb解压到'path'中的路径(或者修改'path');2.请修改addresses为指定地址,若不填写,则使用devices自动查找",
|
||||
"path": "[ExecDir]platform-tools\\adb.exe",
|
||||
"addresses": [],
|
||||
"devices": "[Adb] devices",
|
||||
"addressRegex": "(.+)\tdevice",
|
||||
"connect": "[Adb] connect [Address]",
|
||||
"click": "[Adb] -s [Address] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"displayFormat": "cur=%dx%d",
|
||||
"screencap": "[Adb] -s [Address] exec-out screencap -p"
|
||||
}
|
||||
},
|
||||
"USB": {
|
||||
"handle": {
|
||||
"class": "USB Class",
|
||||
"window": "USB Window",
|
||||
"Doc": "这俩不是用来捕获窗口的,是为了兼容现有的捕获模拟器的字段,随手写的,这两个字段实际不会使用到"
|
||||
},
|
||||
"adb": {
|
||||
"Doc": "USB连接暂时不兼容多设备的情况",
|
||||
"path": "[ExecDir]platform-tools\\adb.exe",
|
||||
"connect": "[Adb] devices",
|
||||
"click": "[Adb] shell input tap [x] [y]",
|
||||
"swipe": "[Adb] shell input swipe [x1] [y1] [x2] [y2] [duration]",
|
||||
"display": "[Adb] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
|
||||
"displayRegex": "cur=%dx%d",
|
||||
"screencap": "[Adb] exec-out screencap -p"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"facility": [
|
||||
"Mfg",
|
||||
"Trade",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[
|
||||
[
|
||||
{
|
||||
"name": "Lancet-2",
|
||||
"type": "医疗",
|
||||
@@ -1317,5 +1317,49 @@
|
||||
"hidden": false,
|
||||
"name-en": "Ambriel",
|
||||
"new": true
|
||||
},
|
||||
{
|
||||
"name": "阿",
|
||||
"type": "特种",
|
||||
"level": 6,
|
||||
"sex": "男",
|
||||
"tags": [
|
||||
"远程位",
|
||||
"支援",
|
||||
"输出",
|
||||
"高级资深干员"
|
||||
],
|
||||
"hidden": false,
|
||||
"name-en": "Aak",
|
||||
"new": true
|
||||
},
|
||||
{
|
||||
"name": "吽",
|
||||
"type": "重装",
|
||||
"level": 5,
|
||||
"sex": "男",
|
||||
"tags": [
|
||||
"近战位",
|
||||
"防护",
|
||||
"治疗",
|
||||
"资深干员"
|
||||
],
|
||||
"hidden": false,
|
||||
"name-en": "Hung",
|
||||
"new": true
|
||||
},
|
||||
{
|
||||
"name": "正义骑士号",
|
||||
"type": "狙击",
|
||||
"level": 1,
|
||||
"sex": "女",
|
||||
"tags": [
|
||||
"远程位",
|
||||
"支援",
|
||||
"支援机械"
|
||||
],
|
||||
"hidden": false,
|
||||
"name-en": "Unknown",
|
||||
"new": true
|
||||
}
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"SanityBegin": {
|
||||
"algorithm": "justreturn",
|
||||
"action": "doNothing",
|
||||
@@ -297,7 +297,7 @@
|
||||
]
|
||||
},
|
||||
"Friends": {
|
||||
"templThreshold": 0.85,
|
||||
"templThreshold": 0.7,
|
||||
"action": "clickSelf",
|
||||
"next": [
|
||||
"FriendsList",
|
||||
@@ -391,7 +391,17 @@
|
||||
]
|
||||
},
|
||||
"Mall": {
|
||||
"algorithm": "OcrDetect",
|
||||
"action": "clickSelf",
|
||||
"text": [
|
||||
"中心"
|
||||
],
|
||||
"roi": [
|
||||
640,
|
||||
360,
|
||||
640,
|
||||
360
|
||||
],
|
||||
"next": [
|
||||
"CreditStore",
|
||||
"CreditStoreOcr"
|
||||
@@ -800,7 +810,7 @@
|
||||
},
|
||||
"DroneConfirm": {
|
||||
"action": "clickSelf",
|
||||
"rearDelay": 2000,
|
||||
"rearDelay": 3500,
|
||||
"templThreshold": 0.8,
|
||||
"next": [
|
||||
"DeliverableOrder",
|
||||
@@ -814,7 +824,8 @@
|
||||
"next": [
|
||||
"DeliverableOrder",
|
||||
"DroneAssist-Trade"
|
||||
]
|
||||
],
|
||||
"rearDelay": 2000
|
||||
},
|
||||
"DroneAssist-Trade-Stop": {
|
||||
"Doc": "如果找到了“无人机加速”按钮,却没有找到订单交付:说明上一次无人机没有把一个订单加速完成,也说明没有更多的无人机了,所以直接stop",
|
||||
@@ -905,7 +916,7 @@
|
||||
300
|
||||
],
|
||||
"preDelay": 1000,
|
||||
"rearDelay": 1000,
|
||||
"rearDelay": 300,
|
||||
"Doc": "这里的preDelay作为滑动duration使用,rearDelay作为滑动额外延时使用"
|
||||
},
|
||||
"InfrastOperListSwipeEnd": {
|
||||
@@ -1216,8 +1227,8 @@
|
||||
220
|
||||
]
|
||||
},
|
||||
"InfrastAddOperatorMfg": {
|
||||
"template": "AddOperatorMfg.png",
|
||||
"InfrastAddOperatorMfgGentle": {
|
||||
"template": "AddOperatorMfgGentle.png",
|
||||
"cache": false,
|
||||
"roi": [
|
||||
200,
|
||||
@@ -1227,8 +1238,25 @@
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastAddOperatorTrade": {
|
||||
"template": "AddOperatorTrade.png",
|
||||
"InfrastAddOperatorMfgAggressive": {
|
||||
"template": "AddOperatorMfgAggressive.png",
|
||||
"cache": false,
|
||||
"roi": [
|
||||
200,
|
||||
500,
|
||||
500,
|
||||
100
|
||||
],
|
||||
"rectMove": [
|
||||
5,
|
||||
50,
|
||||
80,
|
||||
80
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastAddOperatorTradeGentle": {
|
||||
"template": "AddOperatorTradeGentle.png",
|
||||
"cache": false,
|
||||
"roi": [
|
||||
200,
|
||||
@@ -1238,8 +1266,25 @@
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastAddOperatorReception": {
|
||||
"template": "AddOperatorReception.png",
|
||||
"InfrastAddOperatorTradeAggressive": {
|
||||
"template": "AddOperatorTradeAggressive.png",
|
||||
"cache": false,
|
||||
"roi": [
|
||||
200,
|
||||
500,
|
||||
500,
|
||||
100
|
||||
],
|
||||
"rectMove": [
|
||||
5,
|
||||
50,
|
||||
80,
|
||||
80
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastAddOperatorReceptionGentle": {
|
||||
"template": "AddOperatorReceptionGentle.png",
|
||||
"cache": false,
|
||||
"roi": [
|
||||
0,
|
||||
@@ -1249,19 +1294,31 @@
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastAddOperatorReceptionAggressive": {
|
||||
"algorithm": "justReturn",
|
||||
"action": "clickRect",
|
||||
"specificRect": [
|
||||
30,
|
||||
200,
|
||||
200,
|
||||
300
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastOperSelected": {
|
||||
"template": "OperSelected.png",
|
||||
"template": "empty.png",
|
||||
"cache": false,
|
||||
"templThreshold": 0.35,
|
||||
"templThreshold": 8000,
|
||||
"templThreshold_Doc": "这个任务中作为符合range的像素点数量阈值",
|
||||
"maskRange": [
|
||||
1,
|
||||
255
|
||||
95,
|
||||
105
|
||||
],
|
||||
"rectMove": [
|
||||
0,
|
||||
-252,
|
||||
-230,
|
||||
74,
|
||||
320
|
||||
283
|
||||
]
|
||||
},
|
||||
"InfrastOperOnShift": {
|
||||
@@ -1278,71 +1335,85 @@
|
||||
"InfrastClueVacancyNo1": {
|
||||
"template": "ClueVacancyNo1.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
0,
|
||||
100,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClueVacancyNo2": {
|
||||
"template": "ClueVacancyNo2.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
200,
|
||||
200,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClueVacancyNo3": {
|
||||
"template": "ClueVacancyNo3.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
400,
|
||||
100,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClueVacancyNo4": {
|
||||
"template": "ClueVacancyNo4.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
650,
|
||||
150,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClueVacancyNo5": {
|
||||
"template": "ClueVacancyNo5.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
300,
|
||||
420,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClueVacancyNo6": {
|
||||
"template": "ClueVacancyNo6.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
500,
|
||||
350,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClueVacancyNo7": {
|
||||
"template": "ClueVacancyNo7.png",
|
||||
"cache": false,
|
||||
"maskRange": [
|
||||
250,
|
||||
255
|
||||
"templThreshold": 0.75,
|
||||
"roi": [
|
||||
50,
|
||||
350,
|
||||
500,
|
||||
250
|
||||
],
|
||||
"templThreshold": 0.95,
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"InfrastClue": {
|
||||
@@ -1351,6 +1422,12 @@
|
||||
"maskRange": [
|
||||
1,
|
||||
255
|
||||
],
|
||||
"roi": [
|
||||
800,
|
||||
0,
|
||||
480,
|
||||
720
|
||||
]
|
||||
},
|
||||
"InfrastClueOnTheRightSwipeBegin": {
|
||||
@@ -1362,8 +1439,8 @@
|
||||
300,
|
||||
100
|
||||
],
|
||||
"preDelay": 1000,
|
||||
"rearDelay": 1000,
|
||||
"preDelay": 500,
|
||||
"rearDelay": 500,
|
||||
"maxTimes": 1,
|
||||
"Doc": "这里的preDelay作为滑动duration使用,rearDelay作为滑动额外延时使用,maxTimes作为重复次数使用"
|
||||
},
|
||||
@@ -1403,8 +1480,10 @@
|
||||
"action": "clickSelf",
|
||||
"cache": false,
|
||||
"next": [
|
||||
"InfrastClueNew"
|
||||
]
|
||||
"InfrastClueNew",
|
||||
"CloseCluePage"
|
||||
],
|
||||
"rearDelay": 2000
|
||||
},
|
||||
"GetClue2": {
|
||||
"template": "GetClue2.png",
|
||||
@@ -1412,12 +1491,22 @@
|
||||
"cache": false,
|
||||
"next": [
|
||||
"InfrastClueNew"
|
||||
]
|
||||
],
|
||||
"rearDelay": 2000
|
||||
},
|
||||
"CloseCluePage": {
|
||||
"template": "CloseCluePage.png",
|
||||
"action": "clickSelf",
|
||||
"cache": false,
|
||||
"next": [
|
||||
"InfrastClueNew"
|
||||
],
|
||||
"rearDelay": 1000
|
||||
},
|
||||
"UnlockClues": {
|
||||
"template": "UnlockClues.png",
|
||||
"action": "clickSelf",
|
||||
"cache": false,
|
||||
"rearDelay": 2000
|
||||
"rearDelay": 5000
|
||||
}
|
||||
}
|
||||
BIN
resource/template/AddOperatorMfgAggressive.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
BIN
resource/template/AddOperatorTradeAggressive.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 20 KiB |
81
src/MeoAssistance/.editorconfig
Normal file
@@ -0,0 +1,81 @@
|
||||
# Visual Studio generated .editorconfig file with C++ settings.
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
end_of_line = crlf
|
||||
|
||||
[*.json]
|
||||
|
||||
indent_size = 4
|
||||
|
||||
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
|
||||
|
||||
indent_size = 4
|
||||
|
||||
# Visual C++ Code Style settings
|
||||
|
||||
cpp_generate_documentation_comments = xml
|
||||
|
||||
# Visual C++ Formatting settings
|
||||
|
||||
cpp_indent_braces = false
|
||||
cpp_indent_multi_line_relative_to = innermost_parenthesis
|
||||
cpp_indent_within_parentheses = indent
|
||||
cpp_indent_preserve_within_parentheses = true
|
||||
cpp_indent_case_contents = true
|
||||
cpp_indent_case_labels = false
|
||||
cpp_indent_case_contents_when_block = false
|
||||
cpp_indent_lambda_braces_when_parameter = true
|
||||
cpp_indent_goto_labels = one_left
|
||||
cpp_indent_preprocessor = leftmost_column
|
||||
cpp_indent_access_specifiers = false
|
||||
cpp_indent_namespace_contents = true
|
||||
cpp_indent_preserve_comments = false
|
||||
cpp_new_line_before_open_brace_namespace = new_line
|
||||
cpp_new_line_before_open_brace_type = new_line
|
||||
cpp_new_line_before_open_brace_function = new_line
|
||||
cpp_new_line_before_open_brace_block = same_line
|
||||
cpp_new_line_before_open_brace_lambda = same_line
|
||||
cpp_new_line_scope_braces_on_separate_lines = false
|
||||
cpp_new_line_close_brace_same_line_empty_type = false
|
||||
cpp_new_line_close_brace_same_line_empty_function = false
|
||||
cpp_new_line_before_catch = true
|
||||
cpp_new_line_before_else = true
|
||||
cpp_new_line_before_while_in_do_while = false
|
||||
cpp_space_before_function_open_parenthesis = remove
|
||||
cpp_space_within_parameter_list_parentheses = false
|
||||
cpp_space_between_empty_parameter_list_parentheses = false
|
||||
cpp_space_after_keywords_in_control_flow_statements = true
|
||||
cpp_space_within_control_flow_statement_parentheses = false
|
||||
cpp_space_before_lambda_open_parenthesis = false
|
||||
cpp_space_within_cast_parentheses = false
|
||||
cpp_space_after_cast_close_parenthesis = false
|
||||
cpp_space_within_expression_parentheses = false
|
||||
cpp_space_before_block_open_brace = true
|
||||
cpp_space_between_empty_braces = false
|
||||
cpp_space_before_initializer_list_open_brace = false
|
||||
cpp_space_within_initializer_list_braces = true
|
||||
cpp_space_preserve_in_initializer_list = true
|
||||
cpp_space_before_open_square_bracket = false
|
||||
cpp_space_within_square_brackets = false
|
||||
cpp_space_before_empty_square_brackets = false
|
||||
cpp_space_between_empty_square_brackets = false
|
||||
cpp_space_group_square_brackets = true
|
||||
cpp_space_within_lambda_brackets = false
|
||||
cpp_space_between_empty_lambda_brackets = false
|
||||
cpp_space_before_comma = false
|
||||
cpp_space_after_comma = true
|
||||
cpp_space_remove_around_member_operators = true
|
||||
cpp_space_before_inheritance_colon = true
|
||||
cpp_space_before_constructor_colon = true
|
||||
cpp_space_remove_before_semicolon = true
|
||||
cpp_space_after_semicolon = false
|
||||
cpp_space_remove_around_unary_operator = true
|
||||
cpp_space_around_binary_operator = insert
|
||||
cpp_space_around_assignment_operator = insert
|
||||
cpp_space_pointer_reference_alignment = left
|
||||
cpp_space_around_ternary_operator = insert
|
||||
cpp_wrap_preserve_blocks = one_liners
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "AbstractConfiger.h"
|
||||
#include "AbstractConfiger.h"
|
||||
|
||||
#include <json.h>
|
||||
|
||||
@@ -24,4 +24,4 @@ bool asst::AbstractConfiger::load(const std::string& filename)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "AbstractResource.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace json {
|
||||
namespace json
|
||||
{
|
||||
class value;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class AbstractConfiger : public AbstractResource
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractConfiger() = default;
|
||||
virtual bool load(const std::string& filename) override;
|
||||
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) = 0;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Controller.h"
|
||||
|
||||
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image)
|
||||
: m_image(image)
|
||||
, m_roi(empty_rect_to_full(Rect(), image))
|
||||
: m_image(image), m_roi(empty_rect_to_full(Rect(), image))
|
||||
#ifdef LOG_TRACE
|
||||
,
|
||||
m_image_draw(image.clone())
|
||||
#endif
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image, const Rect& roi)
|
||||
: m_image(image),
|
||||
@@ -42,6 +41,11 @@ void asst::AbstractImageAnalyzer::set_roi(const Rect& roi) noexcept
|
||||
m_roi = empty_rect_to_full(roi, m_image);
|
||||
}
|
||||
|
||||
void asst::AbstractImageAnalyzer::correct_roi() noexcept
|
||||
{
|
||||
m_roi = ctrler.shaped_correct(m_roi);
|
||||
}
|
||||
|
||||
asst::Rect asst::AbstractImageAnalyzer::empty_rect_to_full(const Rect& rect, const cv::Mat& image) noexcept
|
||||
{
|
||||
return rect.empty() ? Rect(0, 0, image.cols, image.rows) : rect;
|
||||
@@ -73,4 +77,4 @@ std::string asst::AbstractImageAnalyzer::calc_hash(const Rect& roi) const
|
||||
pix++;
|
||||
}
|
||||
return hash_value.str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -19,9 +20,10 @@ namespace asst {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi);
|
||||
virtual void set_roi(const Rect& roi) noexcept;
|
||||
virtual bool analyze() = 0;
|
||||
virtual void correct_roi() noexcept;
|
||||
|
||||
std::string calc_hash() const; // 使用m_roi
|
||||
std::string calc_hash(const Rect& roi) const;// 使用参数roi
|
||||
std::string calc_hash() const; // 使用m_roi
|
||||
std::string calc_hash(const Rect& roi) const; // 使用参数roi
|
||||
|
||||
AbstractImageAnalyzer& operator=(const AbstractImageAnalyzer&) = delete;
|
||||
AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete;
|
||||
@@ -36,4 +38,4 @@ namespace asst {
|
||||
cv::Mat m_image_draw;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class AbstractResource
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "AbstractTask.h"
|
||||
#include "AbstractTask.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include "Controller.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Controller.h"
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
|
||||
@@ -42,7 +42,8 @@ bool AbstractTask::sleep(unsigned millisecond)
|
||||
|
||||
while (!need_exit() && duration < millisecond) {
|
||||
duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - start).count();
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
m_callback(AsstMsg::EndOfSleep, callback_json, m_callback_arg);
|
||||
@@ -81,4 +82,4 @@ void asst::AbstractTask::click_return_button()
|
||||
|
||||
ctrler.click(ReturnButtonRect);
|
||||
sleep(return_task_ptr->rear_delay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace cv
|
||||
class Mat;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class AbstractTask
|
||||
{
|
||||
public:
|
||||
@@ -26,6 +27,7 @@ namespace asst {
|
||||
virtual void set_task_chain(std::string name) { m_task_chain = std::move(name); }
|
||||
virtual const std::string& get_task_chain() { return m_task_chain; }
|
||||
virtual void on_run_fails(int retry_times) { ; }
|
||||
|
||||
protected:
|
||||
virtual bool sleep(unsigned millisecond);
|
||||
virtual bool save_image(const cv::Mat& iamge, const std::string& dir);
|
||||
@@ -39,4 +41,4 @@ namespace asst {
|
||||
std::string m_task_chain;
|
||||
int m_retry_times = INT_MAX;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AbstractTask.h"
|
||||
#include "AsstDef.h"
|
||||
#include "AsstMsg.h"
|
||||
#include "AbstractTask.h"
|
||||
|
||||
namespace cv {
|
||||
namespace cv
|
||||
{
|
||||
class Mat;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class Controller;
|
||||
class Identify;
|
||||
|
||||
@@ -32,9 +34,9 @@ namespace asst {
|
||||
// 捕获模拟器
|
||||
bool catch_emulator(const std::string& emulator_name = std::string());
|
||||
// 捕获usb设备
|
||||
bool catch_usb();
|
||||
bool catch_custom();
|
||||
// 捕获远程地址(安卓手机)
|
||||
bool catch_remote(const std::string& address);
|
||||
bool catch_specific(const std::string& address);
|
||||
// 不实际进行捕获,调试用接口
|
||||
bool catch_fake();
|
||||
|
||||
@@ -46,7 +48,7 @@ namespace asst {
|
||||
// 开始公开招募操作
|
||||
bool start_recruiting(const std::vector<int>& required_level, bool set_time = true);
|
||||
// 开始基建换班任务
|
||||
bool start_infrast_shift(const std::vector<std::string>& order, UsesOfDrones uses, double dorm_threshold);
|
||||
bool start_infrast_shift(InfrastWorkMode work_mode, const std::vector<std::string>& order, UsesOfDrones uses, double dorm_threshold);
|
||||
|
||||
// 开始流程任务,应该是private的,调试用临时放到public
|
||||
bool start_process_task(const std::string& task, int retry_times = ProcessTaskRetryTimesDefault, bool block = true);
|
||||
@@ -71,7 +73,7 @@ namespace asst {
|
||||
void append_task(const json::value& detail, bool front = false);
|
||||
void append_callback(AsstMsg msg, json::value detail);
|
||||
void clear_exec_times();
|
||||
json::value organize_stage_drop(const json::value& rec); // 整理关卡掉落的材料信息
|
||||
json::value organize_stage_drop(const json::value& rec); // 整理关卡掉落的材料信息
|
||||
|
||||
bool m_inited = false;
|
||||
|
||||
@@ -90,4 +92,4 @@ namespace asst {
|
||||
std::mutex m_msg_mutex;
|
||||
std::condition_variable m_msg_condvar;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "AsstCaller.h"
|
||||
#include "AsstCaller.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <json_value.h>
|
||||
|
||||
#include "Version.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Assistance.h"
|
||||
#include "AsstDef.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Version.h"
|
||||
|
||||
#if 0
|
||||
#if _MSC_VER
|
||||
@@ -14,7 +15,8 @@
|
||||
BOOL APIENTRY DllMain(HANDLE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
) {
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(hModule);
|
||||
UNREFERENCED_PARAMETER(lpReserved);
|
||||
switch (ul_reason_for_call) {
|
||||
@@ -88,35 +90,35 @@ bool AsstCatchEmulator(void* p_asst)
|
||||
return ((asst::Assistance*)p_asst)->catch_emulator();
|
||||
}
|
||||
|
||||
bool AsstCatchUSB(void* p_asst)
|
||||
bool AsstCatchCustom(void* p_asst)
|
||||
{
|
||||
if (p_asst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((asst::Assistance*)p_asst)->catch_usb();
|
||||
return ((asst::Assistance*)p_asst)->catch_custom();
|
||||
}
|
||||
|
||||
bool AsstCatchRemote(void* p_asst, const char* address)
|
||||
bool AsstCatchSpecific(void* p_asst, const char* address)
|
||||
{
|
||||
if (p_asst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((asst::Assistance*)p_asst)->catch_remote(address);
|
||||
}
|
||||
|
||||
bool MEOAPI AsstCatchFake(void* p_asst)
|
||||
return ((asst::Assistance*)p_asst)->catch_specific(address);
|
||||
}
|
||||
|
||||
bool AsstCatchFake(void* p_asst)
|
||||
{
|
||||
#ifdef LOG_TRACE
|
||||
if (p_asst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((asst::Assistance*)p_asst)->catch_fake();
|
||||
#else
|
||||
return false;
|
||||
#endif // LOG_TRACE
|
||||
return ((asst::Assistance*)p_asst)->catch_fake();
|
||||
#else
|
||||
return false;
|
||||
#endif // LOG_TRACE
|
||||
}
|
||||
|
||||
bool AsstStartSanity(void* p_asst)
|
||||
@@ -179,7 +181,7 @@ bool AsstStartRecruiting(void* p_asst, const int required_level[], int required_
|
||||
return ((asst::Assistance*)p_asst)->start_recruiting(level_vector, set_time);
|
||||
}
|
||||
|
||||
bool AsstStartInfrastShift(void* p_asst, const char** order, int order_size, int uses_of_drones, double dorm_threshold)
|
||||
bool AsstStartInfrastShift(void* p_asst, int work_mode, const char** order, int order_size, int uses_of_drones, double dorm_threshold)
|
||||
{
|
||||
if (p_asst == nullptr) {
|
||||
return false;
|
||||
@@ -187,10 +189,7 @@ bool AsstStartInfrastShift(void* p_asst, const char** order, int order_size, int
|
||||
std::vector<std::string> order_vector;
|
||||
order_vector.assign(order, order + order_size);
|
||||
|
||||
return ((asst::Assistance*)p_asst)->start_infrast_shift(
|
||||
order_vector,
|
||||
static_cast<asst::UsesOfDrones>(uses_of_drones),
|
||||
dorm_threshold);
|
||||
return ((asst::Assistance*)p_asst)->start_infrast_shift(static_cast<asst::InfrastWorkMode>(work_mode), order_vector, static_cast<asst::UsesOfDrones>(uses_of_drones), dorm_threshold);
|
||||
}
|
||||
|
||||
bool AsstStartDebugTask(void* p_asst)
|
||||
@@ -203,4 +202,4 @@ bool AsstStartDebugTask(void* p_asst)
|
||||
#else
|
||||
return false;
|
||||
#endif // LOG_TRACE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
|
||||
namespace json
|
||||
{
|
||||
class value;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
constexpr double DoubleDiff = 1e-12;
|
||||
|
||||
constexpr static int WindowWidthDefault = 1280;
|
||||
constexpr static int WindowHeightDefault = 720;
|
||||
|
||||
constexpr static double TemplThresholdDefault = 0.9;
|
||||
constexpr static double HistThresholdDefault = 0.9;
|
||||
|
||||
struct Point
|
||||
{
|
||||
Point() = default;
|
||||
@@ -33,7 +40,8 @@ namespace asst {
|
||||
Rect(const Rect&) noexcept = default;
|
||||
Rect(Rect&&) noexcept = default;
|
||||
Rect(int x, int y, int width, int height)
|
||||
: x(x), y(y), width(width), height(height) {}
|
||||
: x(x), y(y), width(width), height(height)
|
||||
{}
|
||||
Rect operator*(double rhs) const
|
||||
{
|
||||
return { x, y, static_cast<int>(width * rhs), static_cast<int>(height * rhs) };
|
||||
@@ -43,11 +51,19 @@ namespace asst {
|
||||
int half_width_scale = static_cast<int>(width * (1 - scale) / 2);
|
||||
int half_hight_scale = static_cast<int>(height * (1 - scale) / 2);
|
||||
Rect dst(x + half_width_scale, y + half_hight_scale,
|
||||
static_cast<int>(width * scale), static_cast<int>(height * scale));
|
||||
if (dst.x < 0) { dst.x = 0; }
|
||||
if (dst.y < 0) { dst.y = 0; }
|
||||
if (dst.width + dst.x >= max_width) { dst.width = max_width - dst.x; }
|
||||
if (dst.height + dst.y >= max_height) { dst.height = max_height - dst.y; }
|
||||
static_cast<int>(width * scale), static_cast<int>(height * scale));
|
||||
if (dst.x < 0) {
|
||||
dst.x = 0;
|
||||
}
|
||||
if (dst.y < 0) {
|
||||
dst.y = 0;
|
||||
}
|
||||
if (dst.width + dst.x >= max_width) {
|
||||
dst.width = max_width - dst.x;
|
||||
}
|
||||
if (dst.height + dst.y >= max_height) {
|
||||
dst.height = max_height - dst.y;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
Rect& operator=(const Rect&) noexcept = default;
|
||||
@@ -59,16 +75,11 @@ namespace asst {
|
||||
}
|
||||
bool include(const Rect& rhs) const noexcept
|
||||
{
|
||||
return x <= rhs.x
|
||||
&& y <= rhs.y
|
||||
&& (x + width) >= (rhs.x + rhs.width)
|
||||
&& (y + height) >= (rhs.y + rhs.height);
|
||||
return x <= rhs.x && y <= rhs.y && (x + width) >= (rhs.x + rhs.width) && (y + height) >= (rhs.y + rhs.height);
|
||||
}
|
||||
std::string to_string() const {
|
||||
return "[ " + std::to_string(x) + ", "
|
||||
+ std::to_string(y) + ", "
|
||||
+ std::to_string(width) + ", "
|
||||
+ std::to_string(height) + " ]";
|
||||
std::string to_string() const
|
||||
{
|
||||
return "[ " + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(width) + ", " + std::to_string(height) + " ]";
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
@@ -77,19 +88,22 @@ namespace asst {
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
struct TextRect {
|
||||
struct TextRect
|
||||
{
|
||||
TextRect() = default;
|
||||
TextRect(const TextRect&) = default;
|
||||
TextRect(TextRect&&) noexcept = default;
|
||||
|
||||
explicit operator std::string() const noexcept { return text; }
|
||||
explicit operator Rect() const noexcept { return rect; }
|
||||
std::string to_string() const {
|
||||
std::string to_string() const
|
||||
{
|
||||
return text + " : " + rect.to_string();
|
||||
}
|
||||
TextRect& operator=(const TextRect&) = default;
|
||||
TextRect& operator=(TextRect&&) noexcept = default;
|
||||
bool operator==(const TextRect& rhs) const noexcept {
|
||||
bool operator==(const TextRect& rhs) const noexcept
|
||||
{
|
||||
return text == rhs.text && rect == rhs.rect;
|
||||
}
|
||||
|
||||
@@ -98,7 +112,8 @@ namespace asst {
|
||||
};
|
||||
using TextRectProc = std::function<bool(TextRect&)>;
|
||||
|
||||
enum class AlgorithmType {
|
||||
enum class AlgorithmType
|
||||
{
|
||||
Invaild = -1,
|
||||
JustReturn,
|
||||
MatchTemplate,
|
||||
@@ -106,7 +121,8 @@ namespace asst {
|
||||
OcrDetect
|
||||
};
|
||||
|
||||
struct MatchRect {
|
||||
struct MatchRect
|
||||
{
|
||||
MatchRect() = default;
|
||||
MatchRect(const MatchRect&) = default;
|
||||
MatchRect(MatchRect&&) noexcept = default;
|
||||
@@ -121,105 +137,115 @@ namespace asst {
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
class hash<asst::Rect> {
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
class hash<asst::Rect>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const asst::Rect& rect) const
|
||||
{
|
||||
return std::hash<int>()(rect.x)
|
||||
^ std::hash<int>()(rect.y)
|
||||
^ std::hash<int>()(rect.width)
|
||||
^ std::hash<int>()(rect.height);
|
||||
return std::hash<int>()(rect.x) ^ std::hash<int>()(rect.y) ^ std::hash<int>()(rect.width) ^ std::hash<int>()(rect.height);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
class hash<asst::TextRect> {
|
||||
template <>
|
||||
class hash<asst::TextRect>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const asst::TextRect& tr) const
|
||||
{
|
||||
return std::hash<std::string>()(tr.text)
|
||||
^ std::hash<asst::Rect>()(tr.rect);
|
||||
return std::hash<std::string>()(tr.text) ^ std::hash<asst::Rect>()(tr.rect);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
enum class ProcessTaskAction {
|
||||
namespace asst
|
||||
{
|
||||
enum class ProcessTaskAction
|
||||
{
|
||||
Invalid = 0,
|
||||
BasicClick = 0x100,
|
||||
ClickSelf = BasicClick | 1, // 点击自身位置
|
||||
ClickRect = BasicClick | 2, // 点击指定区域
|
||||
ClickRand = BasicClick | 4, // 点击随机区域
|
||||
DoNothing = 0x200, // 什么都不做
|
||||
Stop = 0x400, // 停止当前Task
|
||||
StageDrops = 0x800, // 关卡结束,特化动作
|
||||
ClickSelf = BasicClick | 1, // 点击自身位置
|
||||
ClickRect = BasicClick | 2, // 点击指定区域
|
||||
ClickRand = BasicClick | 4, // 点击随机区域
|
||||
DoNothing = 0x200, // 什么都不做
|
||||
Stop = 0x400, // 停止当前Task
|
||||
StageDrops = 0x800, // 关卡结束,特化动作
|
||||
BasicSwipe = 0x1000,
|
||||
SwipeToTheLeft = BasicSwipe | 1, // 往左划一下
|
||||
SwipeToTheRight = BasicSwipe | 2, // 往右划一下
|
||||
SwipeToTheLeft = BasicSwipe | 1, // 往左划一下
|
||||
SwipeToTheRight = BasicSwipe | 2, // 往右划一下
|
||||
};
|
||||
|
||||
// 任务信息
|
||||
struct TaskInfo {
|
||||
struct TaskInfo
|
||||
{
|
||||
virtual ~TaskInfo() = default;
|
||||
std::string name; // 任务名
|
||||
AlgorithmType algorithm = // 图像算法类型
|
||||
std::string name; // 任务名
|
||||
AlgorithmType algorithm = // 图像算法类型
|
||||
AlgorithmType::Invaild;
|
||||
ProcessTaskAction action = // 要进行的操作
|
||||
ProcessTaskAction action = // 要进行的操作
|
||||
ProcessTaskAction::Invalid;
|
||||
std::vector<std::string> next; // 下一个可能的任务(列表)
|
||||
int exec_times = 0; // 任务已执行了多少次
|
||||
int max_times = INT_MAX; // 任务最多执行多少次
|
||||
std::vector<std::string> exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
|
||||
std::vector<std::string> reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
|
||||
asst::Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域
|
||||
int pre_delay = 0; // 执行该任务前的延时
|
||||
int rear_delay = 0; // 执行该任务后的延时
|
||||
int retry_times = INT_MAX; // 未找到图像时的重试次数
|
||||
Rect roi; // 要识别的区域,若为0则全图识别
|
||||
Rect rect_move; // 识别结果移动:有些结果识别到的,和要点击的不是同一个位置。即识别到了res,点击res + result_move的位置
|
||||
std::vector<std::string> next; // 下一个可能的任务(列表)
|
||||
int exec_times = 0; // 任务已执行了多少次
|
||||
int max_times = INT_MAX; // 任务最多执行多少次
|
||||
std::vector<std::string> exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
|
||||
std::vector<std::string> reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
|
||||
asst::Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域
|
||||
int pre_delay = 0; // 执行该任务前的延时
|
||||
int rear_delay = 0; // 执行该任务后的延时
|
||||
int retry_times = INT_MAX; // 未找到图像时的重试次数
|
||||
Rect roi; // 要识别的区域,若为0则全图识别
|
||||
Rect rect_move; // 识别结果移动:有些结果识别到的,和要点击的不是同一个位置。即识别到了res,点击res + result_move的位置
|
||||
};
|
||||
|
||||
// 文字识别任务的信息
|
||||
struct OcrTaskInfo : public TaskInfo {
|
||||
struct OcrTaskInfo : public TaskInfo
|
||||
{
|
||||
virtual ~OcrTaskInfo() = default;
|
||||
std::vector<std::string> text; // 文字的容器,匹配到这里面任一个,就算匹配上了
|
||||
bool need_full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了
|
||||
std::vector<std::string> text; // 文字的容器,匹配到这里面任一个,就算匹配上了
|
||||
bool need_full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了
|
||||
std::unordered_map<std::string, std::string>
|
||||
replace_map; // 部分文字容易识别错,字符串强制replace之后,再进行匹配
|
||||
bool cache = false; // 是否使用历史区域
|
||||
std::unordered_set<Rect> region_of_appeared; // 曾经出现过的区域:上次处理该任务时,在一些rect里识别到过text,这次优先在这些rect里识别,省点性能
|
||||
replace_map; // 部分文字容易识别错,字符串强制replace之后,再进行匹配
|
||||
bool cache = false; // 是否使用历史区域
|
||||
std::unordered_set<Rect> region_of_appeared; // 曾经出现过的区域:上次处理该任务时,在一些rect里识别到过text,这次优先在这些rect里识别,省点性能
|
||||
};
|
||||
|
||||
// 图片匹配任务的信息
|
||||
struct MatchTaskInfo : public TaskInfo {
|
||||
struct MatchTaskInfo : public TaskInfo
|
||||
{
|
||||
virtual ~MatchTaskInfo() = default;
|
||||
std::string templ_name; // 匹配模板图片文件名
|
||||
double templ_threshold = 0; // 模板匹配阈值
|
||||
double hist_threshold = 0; // 直方图比较阈值
|
||||
std::pair<int, int> mask_range; // 掩码的二值化范围
|
||||
bool cache = false; // 是否使用缓存(直方图),false时就一直用模板匹配。默认为true
|
||||
std::string templ_name; // 匹配模板图片文件名
|
||||
double templ_threshold = 0; // 模板匹配阈值
|
||||
double hist_threshold = 0; // 直方图比较阈值
|
||||
std::pair<int, int> mask_range; // 掩码的二值化范围
|
||||
bool cache = false; // 是否使用缓存(直方图),false时就一直用模板匹配。默认为true
|
||||
};
|
||||
|
||||
struct HandleInfo {
|
||||
struct HandleInfo
|
||||
{
|
||||
std::string class_name;
|
||||
std::string window_name;
|
||||
};
|
||||
|
||||
struct AdbCmd {
|
||||
struct AdbCmd
|
||||
{
|
||||
std::string path;
|
||||
std::vector<std::string> addresses; // 会优先尝试连接addresses中的地址,若均失败,则会使用devices获取地址
|
||||
std::string devices;
|
||||
std::string address_regex;
|
||||
std::string connect;
|
||||
std::string click;
|
||||
std::string swipe;
|
||||
std::string display;
|
||||
std::string display_regex;
|
||||
std::string display_format;
|
||||
std::string screencap;
|
||||
//std::string pullscreen;
|
||||
int display_width = 0;
|
||||
int display_height = 0;
|
||||
};
|
||||
|
||||
struct EmulatorInfo {
|
||||
struct EmulatorInfo
|
||||
{
|
||||
std::string name;
|
||||
HandleInfo handle;
|
||||
AdbCmd adb;
|
||||
@@ -229,50 +255,58 @@ namespace asst {
|
||||
/* 基建相关 */
|
||||
|
||||
// 设施信息
|
||||
struct InfrastFacilityInfo {
|
||||
struct InfrastFacilityInfo
|
||||
{
|
||||
std::string id;
|
||||
std::vector<std::string> products;
|
||||
int max_num_of_opers = 0;
|
||||
};
|
||||
|
||||
enum class InfrastSmileyType {
|
||||
enum class InfrastSmileyType
|
||||
{
|
||||
Invalid = -1,
|
||||
Rest, // 休息完成,绿色笑脸
|
||||
Work, // 工作中,黄色笑脸
|
||||
Distract // 注意力涣散,红色哭脸
|
||||
Rest, // 休息完成,绿色笑脸
|
||||
Work, // 工作中,黄色笑脸
|
||||
Distract // 注意力涣散,红色哭脸
|
||||
};
|
||||
struct InfrastSmileyInfo {
|
||||
struct InfrastSmileyInfo
|
||||
{
|
||||
InfrastSmileyType type;
|
||||
Rect rect;
|
||||
};
|
||||
struct InfrastOperMoodInfo {
|
||||
std::string hash; // 干员部分立绘的hash,作为唯一标识
|
||||
struct InfrastOperMoodInfo
|
||||
{
|
||||
std::string hash; // 干员部分立绘的hash,作为唯一标识
|
||||
InfrastSmileyInfo smiley;
|
||||
double percentage = 0; // 心情进度条的百分比
|
||||
double percentage = 0; // 心情进度条的百分比
|
||||
Rect rect;
|
||||
bool working = false; // 干员是否在工作中
|
||||
bool selected = false; // 干员是否已被选择(蓝色的选择框)
|
||||
bool working = false; // 干员是否在工作中
|
||||
bool selected = false; // 干员是否已被选择(蓝色的选择框)
|
||||
};
|
||||
|
||||
struct InfrastSkill {
|
||||
struct InfrastSkill
|
||||
{
|
||||
std::string id;
|
||||
std::string templ_name;
|
||||
std::vector<std::string> names; // 很多基建技能是一样的,就是名字不同。所以一个技能id可能对应多个名字
|
||||
std::vector<std::string> names; // 很多基建技能是一样的,就是名字不同。所以一个技能id可能对应多个名字
|
||||
std::string intro;
|
||||
std::unordered_map<std::string, double>
|
||||
efficient; // 技能效率,key:产品名(赤金、经验书等), value: 效率数值
|
||||
efficient; // 技能效率,key:产品名(赤金、经验书等), value: 效率数值
|
||||
std::unordered_map<std::string, std::string>
|
||||
efficient_regex; // 技能效率正则,key:产品名(赤金、经验书等), value: 效率正则。如不为空,会先对正则进行计算,再加上efficient里面的值
|
||||
efficient_regex; // 技能效率正则,key:产品名(赤金、经验书等), value: 效率正则。如不为空,会先对正则进行计算,再加上efficient里面的值
|
||||
|
||||
bool operator==(const InfrastSkill& skill) const noexcept {
|
||||
bool operator==(const InfrastSkill& skill) const noexcept
|
||||
{
|
||||
return id == skill.id;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
class hash<asst::InfrastSkill> {
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
class hash<asst::InfrastSkill>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const asst::InfrastSkill& skill) const
|
||||
{
|
||||
@@ -284,9 +318,11 @@ namespace std {
|
||||
namespace asst
|
||||
{
|
||||
// 基建单个干员的技能
|
||||
struct InfrastSkillsComb {
|
||||
struct InfrastSkillsComb
|
||||
{
|
||||
InfrastSkillsComb() = default;
|
||||
InfrastSkillsComb(std::unordered_set<InfrastSkill> skill_vec) {
|
||||
InfrastSkillsComb(std::unordered_set<InfrastSkill> skill_vec)
|
||||
{
|
||||
skills = std::move(skill_vec);
|
||||
for (const auto& s : skills) {
|
||||
for (const auto& [key, value] : s.efficient) {
|
||||
@@ -297,7 +333,8 @@ namespace asst
|
||||
}
|
||||
}
|
||||
}
|
||||
bool operator==(const InfrastSkillsComb& rhs) const {
|
||||
bool operator==(const InfrastSkillsComb& rhs) const
|
||||
{
|
||||
return skills == rhs.skills;
|
||||
}
|
||||
|
||||
@@ -307,20 +344,30 @@ namespace asst
|
||||
std::unordered_map<std::string, std::string> efficient_regex;
|
||||
};
|
||||
// 基建 干员技能信息
|
||||
struct InfrastOperSkillInfo {
|
||||
struct InfrastOperSkillInfo
|
||||
{
|
||||
InfrastOperSkillInfo() = default;
|
||||
InfrastOperSkillInfo(InfrastSkillsComb skills_comb) : skills_comb(std::move(skills_comb)) {}
|
||||
std::string hash; // 有些干员的技能是完全一样的,做个hash区分一下不同干员
|
||||
std::string hash; // 有些干员的技能是完全一样的,做个hash区分一下不同干员
|
||||
InfrastSkillsComb skills_comb;
|
||||
Rect rect;
|
||||
bool selected = false; // 干员是否已被选择(蓝色的选择框)
|
||||
bool selected = false; // 干员是否已被选择(蓝色的选择框)
|
||||
};
|
||||
// 基建技能组
|
||||
struct InfrastSkillsGroup {
|
||||
std::string intro; // 文字介绍,实际不起作用
|
||||
std::unordered_map<std::string, int> conditions; // 技能组合可用条件,例如:key 发电站数量,value 3
|
||||
std::vector<InfrastSkillsComb> necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用
|
||||
std::vector<InfrastSkillsComb> optional; // 可选技能。
|
||||
bool allow_external = false; // 当干员数没满3个的时候,是否允许补充外部干员
|
||||
struct InfrastSkillsGroup
|
||||
{
|
||||
std::string intro; // 文字介绍,实际不起作用
|
||||
std::unordered_map<std::string, int> conditions; // 技能组合可用条件,例如:key 发电站数量,value 3
|
||||
std::vector<InfrastSkillsComb> necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用
|
||||
std::vector<InfrastSkillsComb> optional; // 可选技能。
|
||||
bool allow_external = false; // 当干员数没满3个的时候,是否允许补充外部干员
|
||||
};
|
||||
}
|
||||
|
||||
enum class InfrastWorkMode
|
||||
{
|
||||
Invaild = -1,
|
||||
Gentle, // 温和换班模式:会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班
|
||||
Aggressive, // 激进换班模式:会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员
|
||||
Extreme // 偏激换班模式:会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,95 +1,92 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <json.h>
|
||||
|
||||
namespace asst {
|
||||
enum class AsstMsg {
|
||||
namespace asst
|
||||
{
|
||||
enum class AsstMsg
|
||||
{
|
||||
/* Error Msg */
|
||||
PtrIsNull, // 指针为空
|
||||
ImageIsEmpty, // 图像为空
|
||||
WindowMinimized, // [已弃用] 窗口被最小化了
|
||||
InitFaild, // 初始化失败
|
||||
TaskError, // 任务错误(任务一直出错,retry次数达到上限)
|
||||
OcrResultError, // Ocr识别结果错误
|
||||
PtrIsNull, // 指针为空
|
||||
ImageIsEmpty, // 图像为空
|
||||
WindowMinimized, // [已弃用] 窗口被最小化了
|
||||
InitFaild, // 初始化失败
|
||||
TaskError, // 任务错误(任务一直出错,retry次数达到上限)
|
||||
OcrResultError, // Ocr识别结果错误
|
||||
/* Info Msg: about Task */
|
||||
TaskStart = 1000, // 任务开始
|
||||
TaskMatched, // 任务匹配成功
|
||||
ReachedLimit, // 单个原子任务达到次数上限
|
||||
ReadyToSleep, // 准备开始睡眠
|
||||
EndOfSleep, // 睡眠结束
|
||||
AppendProcessTask, // 新增流程任务,Assistance内部消息,外部不需要处理
|
||||
AppendTask, // 新增任务,Assistance内部消息,外部不需要处理
|
||||
TaskCompleted, // 单个原子任务完成
|
||||
PrintWindow, // 截图消息
|
||||
ProcessTaskStopAction, // 流程任务执行到了Stop的动作
|
||||
TaskChainCompleted, // 任务链完成
|
||||
ProcessTaskNotMatched, // 流程任务识别错误
|
||||
TaskStart = 1000, // 任务开始
|
||||
TaskMatched, // 任务匹配成功
|
||||
ReachedLimit, // 单个原子任务达到次数上限
|
||||
ReadyToSleep, // 准备开始睡眠
|
||||
EndOfSleep, // 睡眠结束
|
||||
AppendProcessTask, // 新增流程任务,Assistance内部消息,外部不需要处理
|
||||
AppendTask, // 新增任务,Assistance内部消息,外部不需要处理
|
||||
TaskCompleted, // 单个原子任务完成
|
||||
PrintWindow, // 截图消息
|
||||
ProcessTaskStopAction, // 流程任务执行到了Stop的动作
|
||||
TaskChainCompleted, // 任务链完成
|
||||
ProcessTaskNotMatched, // 流程任务识别错误
|
||||
/* Info Msg: about Identify */
|
||||
TextDetected = 2000, // 识别到文字
|
||||
ImageFindResult, // 查找图像的结果
|
||||
ImageMatched, // 图像匹配成功
|
||||
StageDrops, // 关卡掉落信息
|
||||
TextDetected = 2000, // 识别到文字
|
||||
ImageFindResult, // 查找图像的结果
|
||||
ImageMatched, // 图像匹配成功
|
||||
StageDrops, // 关卡掉落信息
|
||||
/* Open Recruit Msg */
|
||||
RecruitTagsDetected = 3000, // 公招识别到了Tags
|
||||
RecruitSpecialTag, // 公招识别到了特殊的Tag
|
||||
RecruitResult, // 公开招募结果
|
||||
RecruitTagsDetected = 3000, // 公招识别到了Tags
|
||||
RecruitSpecialTag, // 公招识别到了特殊的Tag
|
||||
RecruitResult, // 公开招募结果
|
||||
/* Infrast Msg */
|
||||
OpersDetected = 4000, // 识别到了干员s
|
||||
OpersIdtfResult, // 干员识别结果(总的)
|
||||
InfrastComb, // 当前房间的最优干员组合
|
||||
EnterStation, // 进入某个房间
|
||||
StationInfo, // 当前房间信息
|
||||
ReadyToShift, // 准备换班
|
||||
ShiftCompleted, // 换班完成(单个房间)
|
||||
NoNeedToShift // 无需换班(单个房间)
|
||||
InfrastSkillsDetected = 4000, // 识别到了基建技能(当前页面)
|
||||
InfrastSkillsResult, // 识别到的所有可用技能
|
||||
InfrastComb, // 当前房间的最优干员组合
|
||||
EnterFacility, // 进入某个房间
|
||||
FacilityInfo, // 当前设施信息
|
||||
};
|
||||
|
||||
static std::ostream& operator<<(std::ostream& os, const AsstMsg& type)
|
||||
{
|
||||
static const std::unordered_map<AsstMsg, std::string> _type_name = {
|
||||
{AsstMsg::PtrIsNull, "PtrIsNull"},
|
||||
{AsstMsg::ImageIsEmpty, "ImageIsEmpty"},
|
||||
{AsstMsg::WindowMinimized, "WindowMinimized"},
|
||||
{AsstMsg::InitFaild, "InitFaild"},
|
||||
{AsstMsg::TaskStart, "TaskStart"},
|
||||
{AsstMsg::ImageFindResult, "ImageFindResult"},
|
||||
{AsstMsg::ImageMatched, "ImageMatched"},
|
||||
{AsstMsg::StageDrops, "StageDrops"},
|
||||
{AsstMsg::TaskMatched, "TaskMatched"},
|
||||
{AsstMsg::ReachedLimit, "ReachedLimit"},
|
||||
{AsstMsg::ReadyToSleep, "ReadyToSleep"},
|
||||
{AsstMsg::EndOfSleep, "EndOfSleep"},
|
||||
{AsstMsg::AppendProcessTask, "AppendProcessTask"},
|
||||
{AsstMsg::TaskCompleted, "TaskCompleted"},
|
||||
{AsstMsg::TaskChainCompleted, "TaskChainCompleted"},
|
||||
{AsstMsg::PrintWindow, "PrintWindow"},
|
||||
{AsstMsg::TaskError, "TaskError"},
|
||||
{AsstMsg::ProcessTaskNotMatched, "ProcessTaskNotMatched"},
|
||||
{AsstMsg::ProcessTaskStopAction, "ProcessTaskStopAction"},
|
||||
{AsstMsg::TextDetected, "TextDetected"},
|
||||
{AsstMsg::RecruitTagsDetected, "RecruitTagsDetected"},
|
||||
{AsstMsg::OcrResultError, "OcrResultError"},
|
||||
{AsstMsg::RecruitSpecialTag, "RecruitSpecialTag"},
|
||||
{AsstMsg::RecruitResult, "RecruitResult"},
|
||||
{AsstMsg::AppendTask, "AppendTask"},
|
||||
{AsstMsg::OpersDetected, "OpersDetected"},
|
||||
{AsstMsg::OpersIdtfResult, "OpersIdtfResult"},
|
||||
{AsstMsg::InfrastComb, "InfrastComb"},
|
||||
{AsstMsg::EnterStation, "EnterStation"},
|
||||
{AsstMsg::StationInfo, "StationInfo"},
|
||||
{AsstMsg::ReadyToShift, "ReadyToShift"},
|
||||
{AsstMsg::ShiftCompleted, "ShiftCompleted"},
|
||||
{AsstMsg::NoNeedToShift, "NoNeedToShift"}
|
||||
{ AsstMsg::PtrIsNull, "PtrIsNull" },
|
||||
{ AsstMsg::ImageIsEmpty, "ImageIsEmpty" },
|
||||
{ AsstMsg::WindowMinimized, "WindowMinimized" },
|
||||
{ AsstMsg::InitFaild, "InitFaild" },
|
||||
{ AsstMsg::TaskStart, "TaskStart" },
|
||||
{ AsstMsg::ImageFindResult, "ImageFindResult" },
|
||||
{ AsstMsg::ImageMatched, "ImageMatched" },
|
||||
{ AsstMsg::StageDrops, "StageDrops" },
|
||||
{ AsstMsg::TaskMatched, "TaskMatched" },
|
||||
{ AsstMsg::ReachedLimit, "ReachedLimit" },
|
||||
{ AsstMsg::ReadyToSleep, "ReadyToSleep" },
|
||||
{ AsstMsg::EndOfSleep, "EndOfSleep" },
|
||||
{ AsstMsg::AppendProcessTask, "AppendProcessTask" },
|
||||
{ AsstMsg::TaskCompleted, "TaskCompleted" },
|
||||
{ AsstMsg::TaskChainCompleted, "TaskChainCompleted" },
|
||||
{ AsstMsg::PrintWindow, "PrintWindow" },
|
||||
{ AsstMsg::TaskError, "TaskError" },
|
||||
{ AsstMsg::ProcessTaskNotMatched, "ProcessTaskNotMatched" },
|
||||
{ AsstMsg::ProcessTaskStopAction, "ProcessTaskStopAction" },
|
||||
{ AsstMsg::TextDetected, "TextDetected" },
|
||||
{ AsstMsg::RecruitTagsDetected, "RecruitTagsDetected" },
|
||||
{ AsstMsg::OcrResultError, "OcrResultError" },
|
||||
{ AsstMsg::RecruitSpecialTag, "RecruitSpecialTag" },
|
||||
{ AsstMsg::RecruitResult, "RecruitResult" },
|
||||
{ AsstMsg::AppendTask, "AppendTask" },
|
||||
{ AsstMsg::InfrastSkillsDetected, "InfrastSkillsDetected" },
|
||||
{ AsstMsg::InfrastSkillsResult, "InfrastSkillsResult" },
|
||||
{ AsstMsg::InfrastComb, "InfrastComb" },
|
||||
{ AsstMsg::EnterFacility, "EnterFacility" },
|
||||
{ AsstMsg::FacilityInfo, "FacilityInfo" }
|
||||
};
|
||||
return os << _type_name.at(type);
|
||||
}
|
||||
|
||||
enum UsesOfDrones {
|
||||
enum UsesOfDrones
|
||||
{
|
||||
DronesNotUse = 0,
|
||||
DronesTrade = 0x100,
|
||||
DronesTradeMoney = DronesTrade & 0x10,
|
||||
@@ -104,4 +101,4 @@ namespace asst {
|
||||
// const json::value& 消息详情json,每种消息不同,Todo,需要补充个协议文档啥的
|
||||
// void* 外部调用者自定义参数,每次回调会带出去,建议传个(void*)this指针进来
|
||||
using AsstCallback = std::function<void(AsstMsg, const json::value&, void*)>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
namespace asst {
|
||||
namespace utils {
|
||||
namespace asst
|
||||
{
|
||||
namespace utils
|
||||
{
|
||||
static std::string get_cur_dir()
|
||||
{
|
||||
static std::string cur_dir;
|
||||
@@ -37,14 +40,32 @@ namespace asst {
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::vector<std::string> string_split(const std::string& str, const std::string& delimiter)
|
||||
{
|
||||
std::string::size_type pos1 = 0;
|
||||
std::string::size_type pos2 = str.find(delimiter);
|
||||
std::vector<std::string> result;
|
||||
|
||||
while (std::string::npos != pos2) {
|
||||
result.emplace_back(str.substr(pos1, pos2 - pos1));
|
||||
|
||||
pos1 = pos2 + delimiter.size();
|
||||
pos2 = str.find(delimiter, pos1);
|
||||
}
|
||||
if (pos1 != str.length())
|
||||
result.emplace_back(str.substr(pos1));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string get_format_time()
|
||||
{
|
||||
SYSTEMTIME curtime;
|
||||
GetLocalTime(&curtime);
|
||||
char buff[64] = { 0 };
|
||||
sprintf_s(buff, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
|
||||
curtime.wYear, curtime.wMonth, curtime.wDay,
|
||||
curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds);
|
||||
curtime.wYear, curtime.wMonth, curtime.wDay,
|
||||
curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds);
|
||||
return buff;
|
||||
}
|
||||
|
||||
@@ -60,8 +81,10 @@ namespace asst {
|
||||
memset(str, 0, len + 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
|
||||
std::string strTemp = str;
|
||||
if (wstr) delete[] wstr;
|
||||
if (str) delete[] str;
|
||||
if (wstr)
|
||||
delete[] wstr;
|
||||
if (str)
|
||||
delete[] str;
|
||||
return strTemp;
|
||||
}
|
||||
|
||||
@@ -77,12 +100,14 @@ namespace asst {
|
||||
memset(szGBK, 0, len + 1);
|
||||
WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
|
||||
std::string strTemp(szGBK);
|
||||
if (wszGBK) delete[] wszGBK;
|
||||
if (szGBK) delete[] szGBK;
|
||||
if (wszGBK)
|
||||
delete[] wszGBK;
|
||||
if (szGBK)
|
||||
delete[] szGBK;
|
||||
return strTemp;
|
||||
}
|
||||
|
||||
template<typename RetTy, typename ArgType>
|
||||
template <typename RetTy, typename ArgType>
|
||||
constexpr inline RetTy make_rect(const ArgType& rect)
|
||||
{
|
||||
return RetTy{ rect.x, rect.y, rect.width, rect.height };
|
||||
@@ -104,10 +129,7 @@ namespace asst {
|
||||
constexpr static uchar Bom_1 = 0xBB;
|
||||
constexpr static uchar Bom_2 = 0xBF;
|
||||
|
||||
if (str.size() >= 3
|
||||
&& static_cast<uchar>(str.at(0)) == Bom_0
|
||||
&& static_cast<uchar>(str.at(1)) == Bom_1
|
||||
&& static_cast<uchar>(str.at(2)) == Bom_2) {
|
||||
if (str.size() >= 3 && static_cast<uchar>(str.at(0)) == Bom_0 && static_cast<uchar>(str.at(1)) == Bom_1 && static_cast<uchar>(str.at(2)) == Bom_2) {
|
||||
str.assign(str.begin() + 3, str.end());
|
||||
return str;
|
||||
}
|
||||
@@ -122,9 +144,7 @@ namespace asst {
|
||||
hash2.insert(hash2.begin(), HammingFlags - hash2.size(), '0');
|
||||
int dist = 0;
|
||||
for (int i = 0; i < HammingFlags; i = i + 16) {
|
||||
unsigned long long x
|
||||
= strtoull(hash1.substr(i, 16).c_str(), NULL, 16)
|
||||
^ strtoull(hash2.substr(i, 16).c_str(), NULL, 16);
|
||||
unsigned long long x = strtoull(hash1.substr(i, 16).c_str(), NULL, 16) ^ strtoull(hash2.substr(i, 16).c_str(), NULL, 16);
|
||||
while (x) {
|
||||
dist++;
|
||||
x = x & (x - 1);
|
||||
@@ -174,4 +194,4 @@ namespace asst {
|
||||
// return str;
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "Controller.h"
|
||||
#include "Controller.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <WinUser.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
@@ -15,9 +16,7 @@
|
||||
#include "Resource.h"
|
||||
#include "UserConfiger.h"
|
||||
|
||||
using namespace asst;
|
||||
|
||||
Controller::Controller()
|
||||
asst::Controller::Controller()
|
||||
: m_rand_engine(std::chrono::system_clock::now().time_since_epoch().count())
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -48,6 +47,61 @@ Controller::Controller()
|
||||
m_cmd_thread = std::thread(bind_pipe_working_proc);
|
||||
}
|
||||
|
||||
bool asst::Controller::connect_adb(const std::string& address)
|
||||
{
|
||||
LogTraceScope("connect_adb " + address);
|
||||
|
||||
std::string connect_cmd = utils::string_replace_all(
|
||||
utils::string_replace_all(m_emulator_info.adb.connect, "[Adb]", m_adb_path),
|
||||
"[Address]", address);
|
||||
auto&& [connect_ret, connect_result] = call_command(connect_cmd);
|
||||
// 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断
|
||||
if (!connect_ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string display_cmd = utils::string_replace_all(
|
||||
utils::string_replace_all(m_emulator_info.adb.display, "[Adb]", m_adb_path),
|
||||
"[Address]", address);
|
||||
auto&& [display_ret, display_result] = call_command(display_cmd);
|
||||
if (!display_ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string display_pipe_str(
|
||||
std::make_move_iterator(display_result.begin()),
|
||||
std::make_move_iterator(display_result.end()));
|
||||
int size_value1 = 0;
|
||||
int size_value2 = 0;
|
||||
sscanf_s(display_pipe_str.c_str(), m_emulator_info.adb.display_format.c_str(), &size_value1, &size_value2);
|
||||
// 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高
|
||||
// 总不能有人竖屏玩明日方舟吧(?
|
||||
m_emulator_info.adb.display_width = (std::max)(size_value1, size_value2);
|
||||
m_emulator_info.adb.display_height = (std::min)(size_value1, size_value2);
|
||||
|
||||
constexpr double DefaultRatio =
|
||||
static_cast<double>(WindowWidthDefault) / static_cast<double>(WindowHeightDefault);
|
||||
double cur_ratio = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(m_emulator_info.adb.display_height);
|
||||
|
||||
if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放
|
||||
|| std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) {
|
||||
int scale_width = cur_ratio * WindowHeightDefault;
|
||||
m_scale_size = std::make_pair(scale_width, WindowHeightDefault);
|
||||
m_control_scale = static_cast<double>(m_emulator_info.adb.display_height) / static_cast<double>(WindowHeightDefault);
|
||||
}
|
||||
else { // 否则可能是偏正方形的屏幕,按宽度计算
|
||||
int scale_height = WindowWidthDefault / cur_ratio;
|
||||
m_scale_size = std::make_pair(WindowWidthDefault, scale_height);
|
||||
m_control_scale = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(WindowWidthDefault);
|
||||
}
|
||||
|
||||
m_emulator_info.adb.click = utils::string_replace_all(utils::string_replace_all(m_emulator_info.adb.click, "[Adb]", m_adb_path), "[Address]", address);
|
||||
m_emulator_info.adb.swipe = utils::string_replace_all(utils::string_replace_all(m_emulator_info.adb.swipe, "[Adb]", m_adb_path), "[Address]", address);
|
||||
m_emulator_info.adb.screencap = utils::string_replace_all(utils::string_replace_all(m_emulator_info.adb.screencap, "[Adb]", m_adb_path), "[Address]", address);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
asst::Controller::~Controller()
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -55,7 +109,7 @@ asst::Controller::~Controller()
|
||||
m_thread_exit = true;
|
||||
//m_thread_idle = true;
|
||||
m_cmd_condvar.notify_all();
|
||||
m_completed_id = UINT_MAX; // make all WinMacor::wait to exit
|
||||
m_completed_id = UINT_MAX; // make all WinMacor::wait to exit
|
||||
|
||||
if (m_cmd_thread.joinable()) {
|
||||
m_cmd_thread.join();
|
||||
@@ -67,20 +121,53 @@ asst::Controller::~Controller()
|
||||
::CloseHandle(m_pipe_child_write);
|
||||
}
|
||||
|
||||
Rect asst::Controller::shaped_correct(const Rect& rect) const
|
||||
asst::Rect asst::Controller::shaped_correct(const Rect& rect) const
|
||||
{
|
||||
if (rect.width == 0 || rect.height == 0) {
|
||||
if (rect.empty()) {
|
||||
return rect;
|
||||
}
|
||||
// 明日方舟在异形屏上,有的地方是按比例缩放的,有的地方又是直接位移。没法整,这里简单粗暴一点截一个长条
|
||||
Rect dst = rect;
|
||||
if (m_scale_size.first != GeneralConfiger::WindowWidthDefault) { // 说明是宽屏
|
||||
dst.x = 0;
|
||||
dst.width = m_scale_size.first - 1;
|
||||
if (m_scale_size.first != WindowWidthDefault) { // 说明是宽屏
|
||||
if (rect.width <= WindowWidthDefault / 2) {
|
||||
if (rect.x + rect.width <= WindowWidthDefault / 2) { // 整个矩形都在左半边
|
||||
dst.x = 0;
|
||||
dst.width = m_scale_size.first / 2;
|
||||
}
|
||||
else if (rect.x >= WindowWidthDefault / 2) { // 整个矩形都在右半边
|
||||
dst.x = m_scale_size.first / 2;
|
||||
dst.width = m_scale_size.first / 2;
|
||||
}
|
||||
else { // 整个矩形横跨了中线
|
||||
dst.x = 0;
|
||||
dst.width = m_scale_size.first;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dst.x = 0;
|
||||
dst.width = m_scale_size.first;
|
||||
}
|
||||
}
|
||||
else if (m_scale_size.second != GeneralConfiger::WindowHeightDefault) { // 说明是偏方形屏
|
||||
dst.y = 0;
|
||||
dst.height = m_scale_size.second - 1;
|
||||
else if (m_scale_size.second != WindowHeightDefault) { // 说明是偏方形屏
|
||||
if (rect.height <= WindowHeightDefault / 2) {
|
||||
if (rect.y + rect.height <= WindowHeightDefault / 2) { // 整个矩形都在上半边
|
||||
dst.y = 0;
|
||||
dst.height = m_scale_size.second / 2;
|
||||
}
|
||||
else if (rect.y >= WindowHeightDefault / 2) { // 整个矩形都在下半边
|
||||
dst.y = m_scale_size.second / 2;
|
||||
dst.height = m_scale_size.second / 2; // 整个矩形横跨了中线
|
||||
}
|
||||
else {
|
||||
dst.y = 0;
|
||||
dst.height = m_scale_size.second;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
dst.y = 0;
|
||||
dst.height = m_scale_size.second;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
@@ -92,7 +179,7 @@ void asst::Controller::pipe_working_proc()
|
||||
while (!m_thread_exit) {
|
||||
std::unique_lock<std::mutex> cmd_queue_lock(m_cmd_queue_mutex);
|
||||
|
||||
if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务
|
||||
if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务
|
||||
std::string cmd = m_cmd_queue.front();
|
||||
m_cmd_queue.pop();
|
||||
cmd_queue_lock.unlock();
|
||||
@@ -119,15 +206,14 @@ void asst::Controller::pipe_working_proc()
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::try_capture(const EmulatorInfo& info, bool without_handle)
|
||||
bool asst::Controller::try_capture(const EmulatorInfo& info, bool without_handle)
|
||||
{
|
||||
LogTraceFunction;
|
||||
LogTraceScope("try_capture | " + info.name);
|
||||
|
||||
const HandleInfo& handle_info = info.handle;
|
||||
std::string adb_dir;
|
||||
|
||||
if (!without_handle) { // 使用模拟器自带的adb
|
||||
// 转成宽字符的
|
||||
if (!without_handle) { // 使用模拟器自带的adb
|
||||
// 转成宽字符的
|
||||
wchar_t* class_wbuff = nullptr;
|
||||
if (!handle_info.class_name.empty()) {
|
||||
size_t class_len = (handle_info.class_name.size() + 1) * 2;
|
||||
@@ -189,94 +275,48 @@ bool Controller::try_capture(const EmulatorInfo& info, bool without_handle)
|
||||
m_handle = window_handle;
|
||||
log.trace("Handle:", m_handle, "Name:", m_emulator_info.name);
|
||||
|
||||
adb_dir = emulator_path.substr(0, emulator_path.find_last_of('\\') + 1);
|
||||
adb_dir = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[EmulatorPath]", adb_dir) + '"';
|
||||
adb_dir = utils::string_replace_all(adb_dir, "[ExecDir]", utils::get_cur_dir());
|
||||
std::string adb_path = emulator_path.substr(0, emulator_path.find_last_of('\\') + 1);
|
||||
adb_path = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[EmulatorPath]", adb_path) + '"';
|
||||
adb_path = utils::string_replace_all(adb_path, "[ExecDir]", utils::get_cur_dir());
|
||||
m_adb_path = std::move(adb_path);
|
||||
}
|
||||
else { // 使用辅助自带的标准adb
|
||||
else { // 使用辅助自带的标准adb
|
||||
m_emulator_info = info;
|
||||
adb_dir = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[ExecDir]", utils::get_cur_dir()) + '"';
|
||||
m_adb_path = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[ExecDir]", utils::get_cur_dir()) + '"';
|
||||
}
|
||||
|
||||
//// 针对雷电模拟器连接问题的专用补丁
|
||||
//if (m_emulator_info.handle.class_name == "LDPlayerMainFrame") {
|
||||
// log.trace("LDPlayer Connection Patch Activated.");
|
||||
// std::string connect_cmd = utils::string_replace_all("[Adb] devices", "[Adb]", adb_dir);
|
||||
// auto devices_result = call_command(connect_cmd);
|
||||
// std::string devices_list(
|
||||
// std::make_move_iterator(devices_result.begin()),
|
||||
// std::make_move_iterator(devices_result.end()));
|
||||
// // 查找连接的所有设备
|
||||
// std::string device = {};
|
||||
// 优先使用addresses里指定的地址
|
||||
for (const std::string& address : info.adb.addresses) {
|
||||
if (connect_adb(address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if (devices_list.find("emulator") != devices_list.npos) {
|
||||
// device = devices_list.substr(devices_list.find("emulator"), 13);
|
||||
// m_emulator_info.adb.connect = "";
|
||||
// }
|
||||
// else if (devices_list.find("127") != devices_list.npos) {
|
||||
// device = devices_list.substr(devices_list.find("127"), 14);
|
||||
// }
|
||||
// 若指定地址都没连上,再尝试用devices查找地址
|
||||
std::string devices_cmd = utils::string_replace_all(m_emulator_info.adb.devices, "[Adb]", m_adb_path);
|
||||
auto&& [devices_ret, devices_result] = call_command(devices_cmd);
|
||||
if (!devices_ret) {
|
||||
return false;
|
||||
}
|
||||
std::string devices_pipe_str(
|
||||
std::make_move_iterator(devices_result.begin()),
|
||||
std::make_move_iterator(devices_result.end()));
|
||||
auto lines = utils::string_split(devices_pipe_str, "\r\n");
|
||||
|
||||
// if (device.empty()) {
|
||||
// log.trace("no device detected.");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// log.trace("device detected: ", device);
|
||||
|
||||
// m_emulator_info.adb.connect = utils::string_replace_all(m_emulator_info.adb.connect, "127.0.0.1:5555", device);
|
||||
// m_emulator_info.adb.click = utils::string_replace_all(m_emulator_info.adb.click, "-e", "-s " + device);
|
||||
// m_emulator_info.adb.swipe = utils::string_replace_all(m_emulator_info.adb.swipe, "-e", "-s " + device);
|
||||
// m_emulator_info.adb.display = utils::string_replace_all(m_emulator_info.adb.display, "-e", "-s " + device);
|
||||
// m_emulator_info.adb.screencap = utils::string_replace_all(m_emulator_info.adb.screencap, "-e", "-s " + device);
|
||||
//}
|
||||
|
||||
std::string connect_cmd = utils::string_replace_all(m_emulator_info.adb.connect, "[Adb]", adb_dir);
|
||||
auto&& [connect_ret, connect_result] = call_command(connect_cmd);
|
||||
// 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断
|
||||
if (!connect_ret) {
|
||||
std::string address;
|
||||
const std::regex address_regex(m_emulator_info.adb.address_regex);
|
||||
for (const std::string& line : lines) {
|
||||
std::smatch smatch;
|
||||
if (std::regex_match(line, smatch, address_regex)) {
|
||||
address = smatch[1];
|
||||
}
|
||||
}
|
||||
log.trace("device address", address);
|
||||
if (address.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string display_cmd = utils::string_replace_all(m_emulator_info.adb.display, "[Adb]", adb_dir);
|
||||
auto&& [display_ret, display_result] = call_command(display_cmd);
|
||||
if (!display_ret) {
|
||||
return false;
|
||||
}
|
||||
std::string display_pipe_str(
|
||||
std::make_move_iterator(display_result.begin()),
|
||||
std::make_move_iterator(display_result.end()));
|
||||
int size_value1 = 0;
|
||||
int size_value2 = 0;
|
||||
sscanf_s(display_pipe_str.c_str(), m_emulator_info.adb.display_regex.c_str(), &size_value1, &size_value2);
|
||||
// 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高
|
||||
// 总不能有人竖屏玩明日方舟吧(?
|
||||
m_emulator_info.adb.display_width = (std::max)(size_value1, size_value2);
|
||||
m_emulator_info.adb.display_height = (std::min)(size_value1, size_value2);
|
||||
|
||||
constexpr double DefaultRatio =
|
||||
static_cast<double>(GeneralConfiger::WindowWidthDefault) / static_cast<double>(GeneralConfiger::WindowHeightDefault);
|
||||
double cur_ratio = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(m_emulator_info.adb.display_height);
|
||||
|
||||
if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放
|
||||
|| std::fabs(cur_ratio - DefaultRatio) < DoubleDiff)
|
||||
{
|
||||
int scale_width = cur_ratio * GeneralConfiger::WindowHeightDefault;
|
||||
m_scale_size = std::make_pair(scale_width, GeneralConfiger::WindowHeightDefault);
|
||||
m_control_scale = static_cast<double>(m_emulator_info.adb.display_height) / static_cast<double>(GeneralConfiger::WindowHeightDefault);
|
||||
}
|
||||
else
|
||||
{ // 否则可能是偏正方形的屏幕,按宽度计算
|
||||
int scale_height = GeneralConfiger::WindowWidthDefault / cur_ratio;
|
||||
m_scale_size = std::make_pair(GeneralConfiger::WindowWidthDefault, scale_height);
|
||||
m_control_scale = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(GeneralConfiger::WindowWidthDefault);
|
||||
}
|
||||
|
||||
m_emulator_info.adb.click = utils::string_replace_all(m_emulator_info.adb.click, "[Adb]", adb_dir);
|
||||
m_emulator_info.adb.swipe = utils::string_replace_all(m_emulator_info.adb.swipe, "[Adb]", adb_dir);
|
||||
m_emulator_info.adb.screencap = utils::string_replace_all(m_emulator_info.adb.screencap, "[Adb]", adb_dir);
|
||||
|
||||
return true;
|
||||
return connect_adb(address);
|
||||
}
|
||||
|
||||
//void asst::Controller::set_idle(bool flag)
|
||||
@@ -292,14 +332,14 @@ bool Controller::try_capture(const EmulatorInfo& info, bool without_handle)
|
||||
// }
|
||||
//}
|
||||
|
||||
std::pair<bool, std::vector<unsigned char>> Controller::call_command(const std::string& cmd)
|
||||
std::pair<bool, std::vector<unsigned char>> asst::Controller::call_command(const std::string& cmd)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
static std::mutex pipe_mutex;
|
||||
std::unique_lock<std::mutex> pipe_lock(pipe_mutex);
|
||||
|
||||
PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体
|
||||
PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体
|
||||
::CreateProcessA(NULL, const_cast<LPSTR>(cmd.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &m_child_startup_info, &process_info);
|
||||
|
||||
std::vector<uchar> pipe_data;
|
||||
@@ -368,7 +408,7 @@ void asst::Controller::convert_lf(std::vector<unsigned char>& data)
|
||||
data.erase(next_iter, data.end());
|
||||
}
|
||||
|
||||
Point asst::Controller::rand_point_in_rect(const Rect& rect)
|
||||
asst::Point asst::Controller::rand_point_in_rect(const Rect& rect)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
if (rect.width == 0) {
|
||||
@@ -440,7 +480,7 @@ bool asst::Controller::screencap()
|
||||
//m_cache_image = std::move(temp_image);
|
||||
}
|
||||
|
||||
int Controller::click(const Point& p, bool block)
|
||||
int asst::Controller::click(const Point& p, bool block)
|
||||
{
|
||||
int x = p.x * m_control_scale;
|
||||
int y = p.y * m_control_scale;
|
||||
@@ -449,15 +489,14 @@ int Controller::click(const Point& p, bool block)
|
||||
return click_without_scale(Point(x, y), block);
|
||||
}
|
||||
|
||||
int Controller::click(const Rect& rect, bool block)
|
||||
int asst::Controller::click(const Rect& rect, bool block)
|
||||
{
|
||||
return click(rand_point_in_rect(rect), block);
|
||||
}
|
||||
|
||||
int asst::Controller::click_without_scale(const Point& p, bool block)
|
||||
{
|
||||
if (p.x < 0 || p.x >= m_emulator_info.adb.display_width
|
||||
|| p.y < 0 || p.y >= m_emulator_info.adb.display_height) {
|
||||
if (p.x < 0 || p.x >= m_emulator_info.adb.display_width || p.y < 0 || p.y >= m_emulator_info.adb.display_height) {
|
||||
log.error("click point out of range");
|
||||
}
|
||||
std::string cur_cmd = utils::string_replace_all(m_emulator_info.adb.click, "[x]", std::to_string(p.x));
|
||||
@@ -492,10 +531,7 @@ int asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, bool b
|
||||
|
||||
int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay, bool extra_swipe)
|
||||
{
|
||||
if (p1.x < 0 || p1.x >= m_emulator_info.adb.display_width
|
||||
|| p1.y < 0 || p1.y >= m_emulator_info.adb.display_height
|
||||
|| p2.x < 0 || p2.x >= m_emulator_info.adb.display_width
|
||||
|| p2.y < 0 || p2.y >= m_emulator_info.adb.display_height) {
|
||||
if (p1.x < 0 || p1.x >= m_emulator_info.adb.display_width || p1.y < 0 || p1.y >= m_emulator_info.adb.display_height || p2.x < 0 || p2.x >= m_emulator_info.adb.display_width || p2.y < 0 || p2.y >= m_emulator_info.adb.display_height) {
|
||||
log.error("swipe point out of range");
|
||||
}
|
||||
std::string cur_cmd = utils::string_replace_all(m_emulator_info.adb.swipe, "[x1]", std::to_string(p1.x));
|
||||
@@ -511,7 +547,7 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
|
||||
|
||||
int id = 0;
|
||||
|
||||
int extra_swipe_dist = resource.cfg().get_options().adb_extra_swipe_dist * m_control_scale;
|
||||
int extra_swipe_dist = resource.cfg().get_options().adb_extra_swipe_dist /* * m_control_scale*/;
|
||||
int extra_swipe_duration = resource.cfg().get_options().adb_extra_swipe_duration;
|
||||
|
||||
// 额外的滑动:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来
|
||||
@@ -542,7 +578,7 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
|
||||
|
||||
if (block) {
|
||||
wait(id);
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(extra_delay));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
@@ -554,9 +590,15 @@ int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int du
|
||||
|
||||
cv::Mat asst::Controller::get_image(bool raw)
|
||||
{
|
||||
static bool has_successful = false;
|
||||
if (!screencap()) {
|
||||
return cv::Mat();
|
||||
// 如果之前成功截图过,这次没成功可能是一次意外,重试一次
|
||||
// 点名批评雷电模拟器!
|
||||
if (!(has_successful && screencap())) {
|
||||
return cv::Mat();
|
||||
}
|
||||
}
|
||||
has_successful = true;
|
||||
//std::shared_lock<std::shared_mutex> image_lock(m_image_mutex);
|
||||
if (raw) {
|
||||
return m_cache_image;
|
||||
@@ -570,4 +612,4 @@ cv::Mat asst::Controller::get_image(bool raw)
|
||||
cv::resize(m_cache_image, resize_mat, size, cv::INPAINT_NS);
|
||||
return resize_mat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include <memory> // for pimpl
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
#include <memory> // for pimpl
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class Controller
|
||||
{
|
||||
public:
|
||||
@@ -25,7 +26,8 @@ namespace asst {
|
||||
Controller(Controller&&) = delete;
|
||||
~Controller();
|
||||
|
||||
static Controller& get_instance() {
|
||||
static Controller& get_instance()
|
||||
{
|
||||
static Controller unique_instance;
|
||||
return unique_instance;
|
||||
}
|
||||
@@ -52,9 +54,17 @@ namespace asst {
|
||||
|
||||
Controller& operator=(const Controller&) = delete;
|
||||
Controller& operator=(Controller&&) = delete;
|
||||
|
||||
std::pair<int, int> get_scale_size() const noexcept
|
||||
{
|
||||
return m_scale_size;
|
||||
}
|
||||
|
||||
private:
|
||||
Controller();
|
||||
|
||||
bool connect_adb(const std::string& address);
|
||||
|
||||
void pipe_working_proc();
|
||||
std::pair<bool, std::vector<unsigned char>> call_command(const std::string& cmd);
|
||||
int push_cmd(const std::string& cmd);
|
||||
@@ -71,21 +81,22 @@ namespace asst {
|
||||
std::condition_variable m_cmd_condvar;
|
||||
std::queue<std::string> m_cmd_queue;
|
||||
std::atomic<unsigned> m_completed_id = 0;
|
||||
unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push,肯定是要上锁的,所以没必要原子
|
||||
unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push,肯定是要上锁的,所以没必要原子
|
||||
|
||||
//std::shared_mutex m_image_mutex;
|
||||
cv::Mat m_cache_image;
|
||||
bool m_image_convert_lf = false;
|
||||
|
||||
constexpr static int PipeBuffSize = 1048576; // 管道缓冲区大小
|
||||
HANDLE m_pipe_read = nullptr; // 读管道句柄
|
||||
HANDLE m_pipe_write = nullptr; // 写管道句柄
|
||||
HANDLE m_pipe_child_read = nullptr; // 子进程的读管道句柄
|
||||
HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄
|
||||
SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符
|
||||
STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息
|
||||
constexpr static int PipeBuffSize = 1048576; // 管道缓冲区大小
|
||||
HANDLE m_pipe_read = nullptr; // 读管道句柄
|
||||
HANDLE m_pipe_write = nullptr; // 写管道句柄
|
||||
HANDLE m_pipe_child_read = nullptr; // 子进程的读管道句柄
|
||||
HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄
|
||||
SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符
|
||||
STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息
|
||||
|
||||
EmulatorInfo m_emulator_info;
|
||||
std::string m_adb_path;
|
||||
HWND m_handle = nullptr;
|
||||
std::minstd_rand m_rand_engine;
|
||||
std::pair<int, int> m_scale_size;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "CreditShopImageAnalyzer.h"
|
||||
#include "CreditShopImageAnalyzer.h"
|
||||
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "OcrImageAnalyzer.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
@@ -13,9 +13,7 @@ bool asst::CreditShopImageAnalyzer::analyze()
|
||||
m_need_to_buy.clear();
|
||||
m_result.clear();
|
||||
|
||||
return commoditys_analyze()
|
||||
&& whether_to_buy_analyze()
|
||||
&& sold_out_analyze();
|
||||
return commoditys_analyze() && whether_to_buy_analyze() && sold_out_analyze();
|
||||
}
|
||||
|
||||
bool asst::CreditShopImageAnalyzer::commoditys_analyze()
|
||||
@@ -71,7 +69,7 @@ bool asst::CreditShopImageAnalyzer::whether_to_buy_analyze()
|
||||
}
|
||||
const auto& ocr_res = ocr_analyzer.get_result();
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
#ifdef LOG_TRACE
|
||||
cv::rectangle(m_image_draw, utils::make_rect<cv::Rect>(commodity), cv::Scalar(0, 0, 255), 2);
|
||||
#endif
|
||||
m_need_to_buy.emplace_back(commodity);
|
||||
@@ -94,7 +92,7 @@ bool asst::CreditShopImageAnalyzer::sold_out_analyze()
|
||||
for (const Rect& commodity : m_need_to_buy) {
|
||||
sold_out_analyzer.set_roi(commodity);
|
||||
if (sold_out_analyzer.analyze()) {
|
||||
#ifdef LOG_TRACE
|
||||
#ifdef LOG_TRACE
|
||||
cv::rectangle(m_image_draw, utils::make_rect<cv::Rect>(commodity), cv::Scalar(0, 0, 255));
|
||||
cv::putText(m_image_draw, "Sold Out", cv::Point(commodity.x, commodity.y), 1, 2, cv::Scalar(255, 0, 0));
|
||||
#endif // LOG_TRACE
|
||||
@@ -106,4 +104,4 @@ bool asst::CreditShopImageAnalyzer::sold_out_analyze()
|
||||
}
|
||||
|
||||
return !m_result.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class CreditShopImageAnalyzer final : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -11,15 +12,19 @@ namespace asst {
|
||||
|
||||
virtual bool analyze() override;
|
||||
|
||||
const std::vector<Rect>& get_result() const noexcept {
|
||||
const std::vector<Rect>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override {
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
AbstractImageAnalyzer::set_roi(roi);
|
||||
}
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi) {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image, roi);
|
||||
}
|
||||
bool commoditys_analyze();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "CreditShoppingTask.h"
|
||||
#include "CreditShoppingTask.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
bool asst::CreditShoppingTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "CreditShoppingTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "CreditShoppingTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
@@ -83,4 +83,4 @@ bool asst::CreditShoppingTask::run()
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "AbstractTask.h"
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class CreditShoppingTask : public AbstractTask
|
||||
{
|
||||
public:
|
||||
using AbstractTask::AbstractTask;
|
||||
virtual ~CreditShoppingTask() = default;
|
||||
virtual bool run() override;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "GeneralConfiger.h"
|
||||
#include "GeneralConfiger.h"
|
||||
|
||||
#include <json.h>
|
||||
|
||||
@@ -9,9 +9,7 @@ bool asst::GeneralConfiger::parse(const json::value& json)
|
||||
const json::value& options_json = json.at("options");
|
||||
{
|
||||
m_options.connect_type = static_cast<ConnectType>(options_json.at("connectType").as_integer());
|
||||
m_options.connect_remote_address = options_json.at("connectRemoteAddress").as_string();
|
||||
m_options.task_delay = options_json.at("taskDelay").as_integer();
|
||||
//m_options.identify_cache = options_json.at("identifyCache").as_boolean();
|
||||
m_options.control_delay_lower = options_json.at("controlDelayRange")[0].as_integer();
|
||||
m_options.control_delay_upper = options_json.at("controlDelayRange")[1].as_integer();
|
||||
m_options.print_window = options_json.at("printWindow").as_boolean();
|
||||
@@ -36,18 +34,22 @@ bool asst::GeneralConfiger::parse(const json::value& json)
|
||||
|
||||
const json::object& adb_json = emulator_json.at("adb").as_object();
|
||||
emulator_info.adb.path = adb_json.at("path").as_string();
|
||||
|
||||
for (const json::value& address_json : adb_json.at("addresses").as_array()) {
|
||||
emulator_info.adb.addresses.emplace_back(address_json.as_string());
|
||||
}
|
||||
emulator_info.adb.devices = adb_json.at("devices").as_string();
|
||||
emulator_info.adb.address_regex = adb_json.at("addressRegex").as_string();
|
||||
emulator_info.adb.connect = adb_json.at("connect").as_string();
|
||||
emulator_info.adb.click = adb_json.at("click").as_string();
|
||||
emulator_info.adb.swipe = adb_json.at("swipe").as_string();
|
||||
emulator_info.adb.display = adb_json.at("display").as_string();
|
||||
emulator_info.adb.display_regex = adb_json.at("displayRegex").as_string();
|
||||
emulator_info.adb.display_format = adb_json.at("displayFormat").as_string();
|
||||
emulator_info.adb.screencap = adb_json.at("screencap").as_string();
|
||||
//emulator_info.adb.pullscreen = adb_json.at("pullscreen").as_string();
|
||||
|
||||
emulator_info.path = emulator_json.get("path", std::string());
|
||||
|
||||
m_emulators_info.emplace(name, std::move(emulator_info));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "AbstractConfiger.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
enum class ConnectType {
|
||||
namespace asst
|
||||
{
|
||||
enum class ConnectType
|
||||
{
|
||||
Emulator,
|
||||
USB,
|
||||
Remote
|
||||
Custom
|
||||
};
|
||||
|
||||
struct Options {
|
||||
ConnectType connect_type; // 连接类型
|
||||
std::string connect_remote_address; // 连接局域网中的安卓设备的地址,仅在connectType为Remote时生效
|
||||
int task_delay = 0; // 任务间延时:越快操作越快,但会增加CPU消耗
|
||||
int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时
|
||||
int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时
|
||||
bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下
|
||||
bool penguin_report = false; // 企鹅数据汇报:每次到结算界面,是否汇报掉落数据至企鹅数据 https://penguin-stats.cn/
|
||||
std::string penguin_report_cmd_line;// 企鹅数据汇报的命令
|
||||
std::string penguin_report_server; // 企鹅数据汇报接口"server"字段,"CN", "US", "JP" and "KR".
|
||||
int ocr_gpu_index = -1; // OcrLite使用GPU编号,-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/...
|
||||
int ocr_thread_number = 0; // OcrLite线程数量
|
||||
int adb_extra_swipe_dist = 0; // 额外的滑动距离:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来
|
||||
int adb_extra_swipe_duration = -1; // 额外的滑动持续时间:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来。若小于0,则关闭额外滑动功能
|
||||
struct Options
|
||||
{
|
||||
ConnectType connect_type; // 连接类型
|
||||
int task_delay = 0; // 任务间延时:越快操作越快,但会增加CPU消耗
|
||||
int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时
|
||||
int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时
|
||||
bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下
|
||||
bool penguin_report = false; // 企鹅数据汇报:每次到结算界面,是否汇报掉落数据至企鹅数据 https://penguin-stats.cn/
|
||||
std::string penguin_report_cmd_line; // 企鹅数据汇报的命令
|
||||
std::string penguin_report_server; // 企鹅数据汇报接口"server"字段,"CN", "US", "JP" and "KR".
|
||||
int ocr_gpu_index = -1; // OcrLite使用GPU编号,-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/...
|
||||
int ocr_thread_number = 0; // OcrLite线程数量
|
||||
int adb_extra_swipe_dist = 0; // 额外的滑动距离:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来
|
||||
int adb_extra_swipe_duration = -1; // 额外的滑动持续时间:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来。若小于0,则关闭额外滑动功能
|
||||
};
|
||||
|
||||
class GeneralConfiger : public AbstractConfiger
|
||||
@@ -36,9 +37,6 @@ namespace asst {
|
||||
public:
|
||||
virtual ~GeneralConfiger() = default;
|
||||
|
||||
constexpr static int WindowWidthDefault = 1280;
|
||||
constexpr static int WindowHeightDefault = 720;
|
||||
|
||||
const std::string& get_version() const noexcept
|
||||
{
|
||||
return m_version;
|
||||
|
||||
@@ -1,22 +1,52 @@
|
||||
#include "InfrastAbstractTask.h"
|
||||
#include "InfrastAbstractTask.h"
|
||||
|
||||
#include "AsstMsg.h"
|
||||
#include "Controller.h"
|
||||
#include "InfrastFacilityImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "OcrImageAnalyzer.h"
|
||||
#include "InfrastFacilityImageAnalyzer.h"
|
||||
#include "Controller.h"
|
||||
#include "Resource.h"
|
||||
|
||||
void asst::InfrastAbstractTask::set_work_mode(InfrastWorkMode work_mode) noexcept
|
||||
{
|
||||
m_work_mode = work_mode;
|
||||
switch (work_mode) {
|
||||
case InfrastWorkMode::Gentle:
|
||||
m_work_mode_name = "Gentle";
|
||||
break;
|
||||
case InfrastWorkMode::Aggressive:
|
||||
m_work_mode_name = "Aggressive";
|
||||
break;
|
||||
case InfrastWorkMode::Extreme:
|
||||
m_work_mode_name = "Extreme";
|
||||
break;
|
||||
default:
|
||||
m_work_mode_name.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::InfrastAbstractTask::enter_facility(const std::string& facility, int index)
|
||||
{
|
||||
LogTraceFunction;
|
||||
json::value enter_json = json::object{
|
||||
{ "facility", facility },
|
||||
{ "index", index }
|
||||
};
|
||||
m_callback(AsstMsg::EnterFacility, enter_json, m_callback_arg);
|
||||
|
||||
const auto& image = ctrler.get_image();
|
||||
|
||||
InfrastFacilityImageAnalyzer analyzer(image);
|
||||
analyzer.set_to_be_analyzed({ facility });
|
||||
if (!analyzer.analyze()) {
|
||||
log.trace("result is empty");
|
||||
return false;
|
||||
}
|
||||
Rect rect = analyzer.get_rect(facility, index);
|
||||
if (rect.empty()) {
|
||||
log.trace("facility index is out of range");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -30,6 +60,8 @@ bool asst::InfrastAbstractTask::enter_facility(const std::string& facility, int
|
||||
|
||||
bool asst::InfrastAbstractTask::enter_oper_list_page()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
auto image = ctrler.get_image();
|
||||
|
||||
// 识别右边的“进驻”按钮
|
||||
@@ -40,25 +72,30 @@ bool asst::InfrastAbstractTask::enter_oper_list_page()
|
||||
|
||||
// 如果没找到,说明“进驻信息”这个按钮没有被点开,那就点开它
|
||||
if (!enter_analyzer.analyze()) {
|
||||
log.trace("ready to analyze the stationed info button");
|
||||
OcrImageAnalyzer station_analyzer(image);
|
||||
|
||||
const auto stationedinfo_task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(
|
||||
resource.task().task_ptr("InfrastStationedInfo"));
|
||||
station_analyzer.set_task_info(*stationedinfo_task_ptr);
|
||||
if (station_analyzer.analyze()) {
|
||||
log.trace("the stationed info button found");
|
||||
ctrler.click(station_analyzer.get_result().front().rect);
|
||||
sleep(stationedinfo_task_ptr->rear_delay);
|
||||
// 点开了按钮之后,再识别一次右边的
|
||||
image = ctrler.get_image();
|
||||
enter_analyzer.set_image(image);
|
||||
if (!enter_analyzer.analyze()) {
|
||||
log.error("no enterlist button");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.error("no stationed info button");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
log.trace("ready to click the enterlist button");
|
||||
ctrler.click(enter_analyzer.get_result().front().rect);
|
||||
sleep(enter_task_ptr->rear_delay);
|
||||
|
||||
@@ -67,6 +104,7 @@ bool asst::InfrastAbstractTask::enter_oper_list_page()
|
||||
|
||||
void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse)
|
||||
{
|
||||
LogTraceFunction;
|
||||
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeBegin")->specific_rect;
|
||||
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeEnd")->specific_rect;
|
||||
static int duration = resource.task().task_ptr("InfrastOperListSwipeBegin")->pre_delay;
|
||||
@@ -81,6 +119,7 @@ void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse)
|
||||
|
||||
void asst::InfrastAbstractTask::await_swipe()
|
||||
{
|
||||
LogTraceFunction;
|
||||
static int extra_delay = resource.task().task_ptr("InfrastOperListSwipeBegin")->rear_delay;
|
||||
|
||||
ctrler.wait(m_last_swipe_id);
|
||||
@@ -89,6 +128,7 @@ void asst::InfrastAbstractTask::await_swipe()
|
||||
|
||||
bool asst::InfrastAbstractTask::click_bottomleft_tab()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto task_ptr = resource.task().task_ptr("InfrastBottomLeftTab");
|
||||
ctrler.click(task_ptr->specific_rect);
|
||||
sleep(task_ptr->rear_delay);
|
||||
@@ -97,6 +137,7 @@ bool asst::InfrastAbstractTask::click_bottomleft_tab()
|
||||
|
||||
bool asst::InfrastAbstractTask::click_clear_button()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto task_ptr = resource.task().task_ptr("InfrastClearButton");
|
||||
ctrler.click(task_ptr->specific_rect);
|
||||
sleep(task_ptr->rear_delay);
|
||||
@@ -105,6 +146,7 @@ bool asst::InfrastAbstractTask::click_clear_button()
|
||||
|
||||
bool asst::InfrastAbstractTask::click_confirm_button()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(
|
||||
resource.task().task_ptr("InfrastConfirmButton"));
|
||||
ctrler.click(task_ptr->specific_rect);
|
||||
@@ -114,6 +156,9 @@ bool asst::InfrastAbstractTask::click_confirm_button()
|
||||
OcrImageAnalyzer analyzer;
|
||||
analyzer.set_task_info(*task_ptr);
|
||||
for (int i = 0; i != m_retry_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
const auto& image = ctrler.get_image();
|
||||
analyzer.set_image(image);
|
||||
if (!analyzer.analyze()) {
|
||||
@@ -133,6 +178,7 @@ void asst::InfrastAbstractTask::sync_swipe_of_operlist(bool reverse)
|
||||
|
||||
void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist()
|
||||
{
|
||||
LogTraceFunction;
|
||||
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect;
|
||||
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect;
|
||||
static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay;
|
||||
@@ -140,6 +186,9 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist()
|
||||
static int loop_times = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->max_times;
|
||||
|
||||
for (int i = 0; i != loop_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return;
|
||||
}
|
||||
ctrler.swipe(end_rect, begin_rect, duration, true, 0, false);
|
||||
}
|
||||
sleep(extra_delay);
|
||||
@@ -147,6 +196,7 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist()
|
||||
|
||||
void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui()
|
||||
{
|
||||
LogTraceFunction;
|
||||
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect;
|
||||
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect;
|
||||
static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay;
|
||||
@@ -157,10 +207,11 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui()
|
||||
|
||||
void asst::InfrastAbstractTask::swipe_to_the_right_of_main_ui()
|
||||
{
|
||||
LogTraceFunction;
|
||||
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect;
|
||||
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect;
|
||||
static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay;
|
||||
static int extra_delay = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->rear_delay;
|
||||
|
||||
ctrler.swipe(begin_rect, end_rect, duration, true, extra_delay, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractTask.h"
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastAbstractTask : public AbstractTask
|
||||
{
|
||||
public:
|
||||
using AbstractTask::AbstractTask;
|
||||
virtual ~InfrastAbstractTask() = default;
|
||||
virtual void set_work_mode(InfrastWorkMode work_mode) noexcept;
|
||||
|
||||
protected:
|
||||
virtual bool enter_facility(const std::string& facility, int index = 0);
|
||||
virtual bool enter_oper_list_page(); // 从刚点进基建的界面,到干员列表页
|
||||
virtual bool enter_oper_list_page(); // 从刚点进基建的界面,到干员列表页
|
||||
|
||||
virtual void swipe_to_the_left_of_operlist(); // 滑动到干员列表的最左侧
|
||||
virtual void swipe_to_the_left_of_main_ui(); // 滑动基建的主界面到最左侧
|
||||
virtual void swipe_to_the_right_of_main_ui(); // 滑动基建的主界面到最右侧
|
||||
virtual void swipe_to_the_left_of_operlist(); // 滑动到干员列表的最左侧
|
||||
virtual void swipe_to_the_left_of_main_ui(); // 滑动基建的主界面到最左侧
|
||||
virtual void swipe_to_the_right_of_main_ui(); // 滑动基建的主界面到最右侧
|
||||
virtual void sync_swipe_of_operlist(bool reverse = false);
|
||||
virtual void async_swipe_of_operlist(bool reverse = false);
|
||||
virtual void await_swipe();
|
||||
|
||||
virtual bool click_bottomleft_tab(); // 点击进入设施后,左下角的tab(我也不知道这玩意该叫啥)
|
||||
virtual bool click_clear_button(); // 点击干员选择页面的“清空选择”按钮
|
||||
virtual bool click_confirm_button(); // 点击干员选择页面的“确认”按钮
|
||||
virtual bool click_bottomleft_tab(); // 点击进入设施后,左下角的tab(我也不知道这玩意该叫啥)
|
||||
virtual bool click_clear_button(); // 点击干员选择页面的“清空选择”按钮
|
||||
virtual bool click_confirm_button(); // 点击干员选择页面的“确认”按钮
|
||||
|
||||
int m_last_swipe_id = 0;
|
||||
InfrastWorkMode m_work_mode = InfrastWorkMode::Gentle;
|
||||
std::string m_work_mode_name = "Gentle";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "InfrastClueImageAnalyzer.h"
|
||||
#include "InfrastClueImageAnalyzer.h"
|
||||
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
@@ -19,7 +19,7 @@ bool asst::InfrastClueImageAnalyzer::clue_detect()
|
||||
const auto task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastClue"));
|
||||
clue_analyzer.set_task_info(*task_ptr);
|
||||
clue_analyzer.set_roi(m_roi); // 改任务以外部设置的roi为准
|
||||
clue_analyzer.set_roi(m_roi); // 改任务以外部设置的roi为准
|
||||
if (!clue_analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
@@ -35,4 +35,4 @@ bool asst::InfrastClueImageAnalyzer::clue_analyze()
|
||||
{
|
||||
// todo
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastClueImageAnalyzer : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -10,14 +11,16 @@ namespace asst {
|
||||
|
||||
virtual bool analyze() override;
|
||||
|
||||
const std::vector<std::pair<Rect, std::string>>& get_result() const noexcept {
|
||||
const std::vector<std::pair<Rect, std::string>>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool clue_detect();
|
||||
bool clue_analyze();
|
||||
|
||||
bool m_need_detailed = false; // 是否需要详细分析(线索号);false时只检测,不识别
|
||||
bool m_need_detailed = false; // 是否需要详细分析(线索号);false时只检测,不识别
|
||||
std::vector<std::pair<Rect, std::string>> m_result;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "InfrastClueVacancyImageAnalyzer.h"
|
||||
#include "InfrastClueVacancyImageAnalyzer.h"
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
#include "AsstUtils.hpp"
|
||||
|
||||
bool asst::InfrastClueVacancyImageAnalyzer::analyze()
|
||||
{
|
||||
@@ -14,9 +15,11 @@ bool asst::InfrastClueVacancyImageAnalyzer::analyze()
|
||||
resource.task().task_ptr(clue_vacancy + suffix));
|
||||
analyzer.set_task_info(*task_ptr);
|
||||
if (!analyzer.analyze()) {
|
||||
log.trace("no", clue_vacancy, suffix);
|
||||
continue;
|
||||
}
|
||||
Rect rect = analyzer.get_result().rect;
|
||||
log.trace("has", clue_vacancy, suffix);
|
||||
#ifdef LOG_TRACE
|
||||
cv::rectangle(m_image_draw, utils::make_rect<cv::Rect>(rect), cv::Scalar(0, 0, 255), 2);
|
||||
cv::putText(m_image_draw, suffix, cv::Point(rect.x, rect.y + 1), 0, 1, cv::Scalar(0, 0, 255), 2);
|
||||
@@ -25,4 +28,4 @@ bool asst::InfrastClueVacancyImageAnalyzer::analyze()
|
||||
}
|
||||
|
||||
return !m_clue_vacancy.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastClueVacancyImageAnalyzer final : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -12,19 +13,24 @@ namespace asst {
|
||||
virtual bool analyze() override;
|
||||
constexpr static int MaxNumOfClue = 7;
|
||||
|
||||
void set_to_be_analyzed(std::vector<std::string> to_be_analyzed) noexcept {
|
||||
void set_to_be_analyzed(std::vector<std::string> to_be_analyzed) noexcept
|
||||
{
|
||||
m_to_be_analyzed = std::move(to_be_analyzed);
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, Rect>& get_vacancy() const noexcept {
|
||||
const std::unordered_map<std::string, Rect>& get_vacancy() const noexcept
|
||||
{
|
||||
return m_clue_vacancy;
|
||||
}
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override {
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
AbstractImageAnalyzer::set_roi(roi);
|
||||
}
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi) {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image, roi);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "InfrastConfiger.h"
|
||||
#include "InfrastConfiger.h"
|
||||
|
||||
#include <json.h>
|
||||
|
||||
@@ -53,8 +53,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
|
||||
else {
|
||||
for (const std::string& pd : products) {
|
||||
if (std::string pd_reg_key = pd + reg_suffix;
|
||||
efficient.exist(pd_reg_key))
|
||||
{
|
||||
efficient.exist(pd_reg_key)) {
|
||||
skill.efficient_regex.emplace(pd, efficient.at(pd_reg_key).as_string());
|
||||
skill.efficient.emplace(pd, 0);
|
||||
}
|
||||
@@ -116,8 +115,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
|
||||
else {
|
||||
for (const std::string& pd : products) {
|
||||
if (std::string pd_reg_key = pd + reg_suffix;
|
||||
efficient.exist(pd_reg_key))
|
||||
{
|
||||
efficient.exist(pd_reg_key)) {
|
||||
comb.efficient_regex.emplace(pd, efficient.at(pd_reg_key).as_string());
|
||||
comb.efficient.emplace(pd, 0);
|
||||
}
|
||||
@@ -166,8 +164,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
|
||||
else {
|
||||
for (const std::string& pd : products) {
|
||||
if (std::string pd_reg_key = pd + reg_suffix;
|
||||
efficient.exist(pd_reg_key))
|
||||
{
|
||||
efficient.exist(pd_reg_key)) {
|
||||
comb.efficient_regex.emplace(pd, efficient.at(pd_reg_key).as_string());
|
||||
comb.efficient.emplace(pd, 0);
|
||||
}
|
||||
@@ -199,4 +196,4 @@ bool asst::InfrastConfiger::parse(const json::value& json)
|
||||
m_facilities_info.emplace(facility_name, std::move(fac_info));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractConfiger.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -6,13 +6,15 @@
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastConfiger : public AbstractConfiger
|
||||
{
|
||||
public:
|
||||
virtual ~InfrastConfiger() = default;
|
||||
|
||||
const std::unordered_set<std::string>& get_templ_required() const noexcept {
|
||||
const std::unordered_set<std::string>& get_templ_required() const noexcept
|
||||
{
|
||||
return m_templ_required;
|
||||
}
|
||||
const std::unordered_map<std::string, InfrastSkill>&
|
||||
@@ -20,13 +22,16 @@ namespace asst {
|
||||
{
|
||||
return m_skills.at(facility_name);
|
||||
}
|
||||
const std::vector<InfrastSkillsGroup>& get_skills_group(const std::string& facility) const {
|
||||
const std::vector<InfrastSkillsGroup>& get_skills_group(const std::string& facility) const
|
||||
{
|
||||
return m_skills_groups.at(facility);
|
||||
}
|
||||
const InfrastFacilityInfo& get_facility_info(const std::string& facility) const {
|
||||
const InfrastFacilityInfo& get_facility_info(const std::string& facility) const
|
||||
{
|
||||
return m_facilities_info.at(facility);
|
||||
}
|
||||
const std::unordered_map<std::string, InfrastFacilityInfo>& get_facility_info() const noexcept {
|
||||
const std::unordered_map<std::string, InfrastFacilityInfo>& get_facility_info() const noexcept
|
||||
{
|
||||
return m_facilities_info;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
#include "InfrastDormTask.h"
|
||||
#include "InfrastDormTask.h"
|
||||
|
||||
#include "Controller.h"
|
||||
#include "InfrastMoodImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "OcrImageAnalyzer.h"
|
||||
#include "InfrastMoodImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
#include "Controller.h"
|
||||
|
||||
const std::string asst::InfrastDormTask::FacilityName = "Dorm";
|
||||
|
||||
bool asst::InfrastDormTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastDormTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastDormTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
for (; m_cur_dorm_index < m_max_num_of_dorm; ++m_cur_dorm_index) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
enter_facility(FacilityName, m_cur_dorm_index);
|
||||
if (!enter_oper_list_page()) {
|
||||
return false;
|
||||
@@ -26,9 +30,13 @@ bool asst::InfrastDormTask::run()
|
||||
|
||||
int quantity_selected = 0;
|
||||
while (quantity_selected < MaxNumOfOpers) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
const auto& image = ctrler.get_image();
|
||||
InfrastMoodImageAnalyzer mood_analyzer(image);
|
||||
if (!mood_analyzer.analyze()) {
|
||||
log.error("mood analyze faild!");
|
||||
return false;
|
||||
}
|
||||
mood_analyzer.sort_result();
|
||||
@@ -36,14 +44,18 @@ bool asst::InfrastDormTask::run()
|
||||
|
||||
int quantity_resting = 0;
|
||||
for (const auto& mood_info : mood_result) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
if (quantity_selected >= MaxNumOfOpers) {
|
||||
log.trace("quantity_selected:", quantity_selected, ", just break");
|
||||
break;
|
||||
}
|
||||
switch (mood_info.smiley.type)
|
||||
{
|
||||
switch (mood_info.smiley.type) {
|
||||
case InfrastSmileyType::Rest:
|
||||
// 如果当前页面休息完成的人数超过5个,说明已经已经把所有心情不满的滑过一遍、没有更多的了,直接退出即可
|
||||
if (++quantity_resting > MaxNumOfOpers) {
|
||||
log.trace("quantity_resting:", quantity_resting, ", confirm");
|
||||
click_confirm_button();
|
||||
return true;
|
||||
}
|
||||
@@ -51,12 +63,10 @@ bool asst::InfrastDormTask::run()
|
||||
case InfrastSmileyType::Work:
|
||||
case InfrastSmileyType::Distract:
|
||||
// 干员没有被选择的情况下,心情小于30%,或不在工作,就进驻宿舍
|
||||
if (mood_info.selected == false
|
||||
&& (mood_info.percentage < m_mood_threshold
|
||||
|| mood_info.working == false))
|
||||
{
|
||||
if (mood_info.selected == false && (mood_info.percentage < m_mood_threshold || mood_info.working == false)) {
|
||||
ctrler.click(mood_info.rect);
|
||||
if (++quantity_selected >= MaxNumOfOpers) {
|
||||
log.trace("quantity_selected:", quantity_selected, ", just break");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -66,6 +76,7 @@ bool asst::InfrastDormTask::run()
|
||||
}
|
||||
}
|
||||
if (quantity_selected >= MaxNumOfOpers) {
|
||||
log.trace("quantity_selected:", quantity_selected, ", just break");
|
||||
break;
|
||||
}
|
||||
sync_swipe_of_operlist();
|
||||
@@ -78,6 +89,8 @@ bool asst::InfrastDormTask::run()
|
||||
|
||||
bool asst::InfrastDormTask::click_confirm_button()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
const auto task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(
|
||||
resource.task().task_ptr("InfrastConfirmButton"));
|
||||
ctrler.click(task_ptr->specific_rect);
|
||||
@@ -98,6 +111,9 @@ bool asst::InfrastDormTask::click_confirm_button()
|
||||
OcrImageAnalyzer analyzer;
|
||||
analyzer.set_task_info(*task_ptr);
|
||||
for (int i = 0; i != m_retry_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
const auto& image = ctrler.get_image();
|
||||
analyzer.set_image(image);
|
||||
if (!analyzer.analyze()) {
|
||||
@@ -107,4 +123,4 @@ bool asst::InfrastDormTask::click_confirm_button()
|
||||
sleep(sec_cfm_task_ptr->rear_delay);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastAbstractTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastDormTask : public InfrastAbstractTask
|
||||
{
|
||||
public:
|
||||
@@ -9,12 +10,14 @@ namespace asst {
|
||||
virtual ~InfrastDormTask() = default;
|
||||
virtual bool run() override;
|
||||
|
||||
void set_mood_threshold(double mood_threshold) noexcept {
|
||||
void set_mood_threshold(double mood_threshold) noexcept
|
||||
{
|
||||
m_mood_threshold = mood_threshold;
|
||||
}
|
||||
|
||||
const static std::string FacilityName;
|
||||
const static int MaxNumOfOpers = 5;
|
||||
|
||||
private:
|
||||
virtual bool click_confirm_button() override;
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "InfrastFacilityImageAnalyzer.h"
|
||||
#include "InfrastFacilityImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
bool asst::InfrastFacilityImageAnalyzer::analyze()
|
||||
{
|
||||
@@ -32,13 +33,13 @@ bool asst::InfrastFacilityImageAnalyzer::analyze()
|
||||
|
||||
int cor_suffix_index = -1;
|
||||
|
||||
for (const auto& [key, task_name] : facility_task_name) {
|
||||
if (!m_to_be_analyzed.empty()) { // 若为空,则分析所有设施
|
||||
if (std::find(m_to_be_analyzed.cbegin(), m_to_be_analyzed.cend(), key)
|
||||
== m_to_be_analyzed.cend()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& key : m_to_be_analyzed) {
|
||||
auto find_iter = facility_task_name.find(key);
|
||||
if (find_iter == facility_task_name.cend()) {
|
||||
log.error("facility name error", key);
|
||||
continue;
|
||||
}
|
||||
std::string task_name = find_iter->second;
|
||||
std::vector<MatchRect> cur_facility_result;
|
||||
// 已知基建缩放状态的时候,只识别这个缩放状态下的就行了
|
||||
// 否则识别所有状态,直到找出正确的当前缩放状态
|
||||
@@ -58,9 +59,9 @@ bool asst::InfrastFacilityImageAnalyzer::analyze()
|
||||
|
||||
const auto& cur_res = mm_analyzer.get_result();
|
||||
auto cur_max_iter = std::max_element(cur_res.cbegin(), cur_res.cend(),
|
||||
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
|
||||
return lhs.score < rhs.score;
|
||||
});
|
||||
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
|
||||
return lhs.score < rhs.score;
|
||||
});
|
||||
if (cur_max_iter == cur_res.cend()) {
|
||||
continue;
|
||||
}
|
||||
@@ -91,4 +92,4 @@ bool asst::InfrastFacilityImageAnalyzer::analyze()
|
||||
}
|
||||
|
||||
return !m_result.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastFacilityImageAnalyzer final : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -11,12 +12,13 @@ namespace asst {
|
||||
|
||||
virtual bool analyze() override;
|
||||
|
||||
// 若为空,则分析所有设施
|
||||
void set_to_be_analyzed(std::vector<std::string> facilities) noexcept {
|
||||
void set_to_be_analyzed(std::vector<std::string> facilities) noexcept
|
||||
{
|
||||
m_to_be_analyzed = std::move(facilities);
|
||||
}
|
||||
|
||||
int get_quantity(const std::string& name) const {
|
||||
int get_quantity(const std::string& name) const
|
||||
{
|
||||
if (auto iter = m_result.find(name);
|
||||
iter == m_result.cend()) {
|
||||
return 0;
|
||||
@@ -25,7 +27,8 @@ namespace asst {
|
||||
return iter->second.size();
|
||||
}
|
||||
}
|
||||
Rect get_rect(const std::string& name, int index) const {
|
||||
Rect get_rect(const std::string& name, int index) const
|
||||
{
|
||||
if (auto iter = m_result.find(name);
|
||||
iter == m_result.cend()) {
|
||||
return Rect();
|
||||
@@ -39,21 +42,25 @@ namespace asst {
|
||||
}
|
||||
}
|
||||
}
|
||||
const std::unordered_map<std::string, std::vector<MatchRect>>& get_result() const noexcept {
|
||||
const std::unordered_map<std::string, std::vector<MatchRect>>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override {
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
AbstractImageAnalyzer::set_roi(roi);
|
||||
}
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi) {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image, roi);
|
||||
}
|
||||
|
||||
// key:设施名,value:所有这种设施的当前Rect(例如所有制造站的位置)
|
||||
std::unordered_map<std::string, std::vector<MatchRect>> m_result;
|
||||
// 需要识别的设施名,若为空,则分析所有设施
|
||||
// 需要识别的设施名
|
||||
std::vector<std::string> m_to_be_analyzed;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
#include "InfrastInfoTask.h"
|
||||
#include "InfrastInfoTask.h"
|
||||
|
||||
#include "Controller.h"
|
||||
#include "RuntimeStatus.h"
|
||||
#include "Resource.h"
|
||||
#include "InfrastFacilityImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
#include "RuntimeStatus.h"
|
||||
|
||||
bool asst::InfrastInfoTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastInfoTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastInfoTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
const auto& image = ctrler.get_image();
|
||||
|
||||
InfrastFacilityImageAnalyzer analyzer(image);
|
||||
std::vector<std::string> all_facilities;
|
||||
for (auto&& [key, _value] : resource.infrast().get_facility_info()) {
|
||||
all_facilities.emplace_back(key);
|
||||
}
|
||||
analyzer.set_to_be_analyzed({ "Mfg", "Trade", "Power" });
|
||||
if (!analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
@@ -32,4 +29,4 @@ bool asst::InfrastInfoTask::run()
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastAbstractTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastInfoTask : public InfrastAbstractTask
|
||||
{
|
||||
public:
|
||||
@@ -10,4 +11,4 @@ namespace asst {
|
||||
|
||||
virtual bool run() override;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "InfrastMfgTask.h"
|
||||
#include "InfrastMfgTask.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "Controller.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
const std::string asst::InfrastMfgTask::FacilityName = "Mfg";
|
||||
|
||||
bool asst::InfrastMfgTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastMfgTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastMfgTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
@@ -24,4 +24,4 @@ bool asst::InfrastMfgTask::run()
|
||||
shift_facility_list();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastProductionTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastMfgTask : public InfrastProductionTask
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1,37 +1,46 @@
|
||||
#include "InfrastMoodImageAnalyzer.h"
|
||||
#include "InfrastMoodImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
bool asst::InfrastMoodImageAnalyzer::analyze()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
mood_detect();
|
||||
mood_analyze();
|
||||
hash_calc();
|
||||
selected_analyze();
|
||||
working_analyze();
|
||||
|
||||
for (auto&& info : m_result) {
|
||||
log.trace(info.hash, info.rect.to_string(),
|
||||
"smiley:", (int)info.smiley.type, "mood:", info.percentage,
|
||||
"selected:", info.selected, "working:", info.working);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::InfrastMoodImageAnalyzer::sort_result()
|
||||
{
|
||||
std::sort(m_result.begin(), m_result.end(),
|
||||
[](const InfrastOperMoodInfo& lhs, const InfrastOperMoodInfo& rhs) ->bool {
|
||||
// 先按心情排序,心情低的放前面
|
||||
if (std::fabs(lhs.percentage - rhs.percentage) > DoubleDiff) {
|
||||
return lhs.percentage < rhs.percentage;
|
||||
}
|
||||
// 心情一样的就按位置排序,左边的放前面
|
||||
if (std::abs(lhs.rect.x - rhs.rect.x) > 5) {
|
||||
return lhs.rect.x < rhs.rect.x;
|
||||
}
|
||||
else {
|
||||
return lhs.rect.y < rhs.rect.y;
|
||||
}
|
||||
});
|
||||
[](const InfrastOperMoodInfo& lhs, const InfrastOperMoodInfo& rhs) -> bool {
|
||||
// 先按心情排序,心情低的放前面
|
||||
if (std::fabs(lhs.percentage - rhs.percentage) > DoubleDiff) {
|
||||
return lhs.percentage < rhs.percentage;
|
||||
}
|
||||
// 心情一样的就按位置排序,左边的放前面
|
||||
if (std::abs(lhs.rect.x - rhs.rect.x) > 5) {
|
||||
return lhs.rect.x < rhs.rect.x;
|
||||
}
|
||||
else {
|
||||
return lhs.rect.y < rhs.rect.y;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool asst::InfrastMoodImageAnalyzer::mood_detect()
|
||||
@@ -63,8 +72,7 @@ bool asst::InfrastMoodImageAnalyzer::mood_detect()
|
||||
prg_rect.y += smiley.rect.y;
|
||||
// mood_analyze是可以识别长度不够的心情条的
|
||||
// 这里主要是为了算立绘hash,宽度不够的hash不好算,直接忽略了
|
||||
if (prg_rect.x + prg_rect.width >= roi.x + roi.width
|
||||
|| prg_rect.x < roi.x) {
|
||||
if (prg_rect.x + prg_rect.width >= roi.x + roi.width || prg_rect.x < roi.x) {
|
||||
continue;
|
||||
}
|
||||
#ifdef LOG_TRACE
|
||||
@@ -118,15 +126,14 @@ bool asst::InfrastMoodImageAnalyzer::mood_analyze()
|
||||
cv::Mat prg_gray;
|
||||
cv::cvtColor(prg_image, prg_gray, cv::COLOR_BGR2GRAY);
|
||||
|
||||
int max_white_length = 0; // 最长横扫的白色长度,即作为进度条长度
|
||||
int max_white_length = 0; // 最长横扫的白色长度,即作为进度条长度
|
||||
for (int i = 0; i != prg_gray.rows; ++i) {
|
||||
int cur_white_length = 0;
|
||||
cv::uint8_t left_value = prg_lower_limit;
|
||||
for (int j = 0; j != prg_gray.cols; ++j) {
|
||||
auto value = prg_gray.at<cv::uint8_t>(i, j);
|
||||
// 当前点的颜色,需要大于最低阈值;且与相邻点的差值不能过大,否则就认为当前点不是进度条
|
||||
if (value >= prg_lower_limit
|
||||
&& left_value < value + prg_diff_thres) {
|
||||
if (value >= prg_lower_limit && left_value < value + prg_diff_thres) {
|
||||
left_value = value;
|
||||
++cur_white_length;
|
||||
if (max_white_length < cur_white_length) {
|
||||
@@ -145,8 +152,7 @@ bool asst::InfrastMoodImageAnalyzer::mood_analyze()
|
||||
}
|
||||
// 如果进度条的长度等于ROI的宽度,则说明这个进度条不完整,可能图像再往右滑,还有一部分进度条
|
||||
// 所以忽略掉这个结果
|
||||
if (roi.width != iter->rect.width
|
||||
&& max_white_length == prg_gray.cols) {
|
||||
if (roi.width != iter->rect.width && max_white_length == prg_gray.cols) {
|
||||
iter = m_result.erase(iter);
|
||||
continue;
|
||||
}
|
||||
@@ -186,20 +192,31 @@ bool asst::InfrastMoodImageAnalyzer::selected_analyze()
|
||||
resource.task().task_ptr("InfrastOperSelected"));
|
||||
Rect rect_move = selected_task_ptr->rect_move;
|
||||
|
||||
MatchImageAnalyzer selected_analyzer(m_image);
|
||||
selected_analyzer.set_task_info(*selected_task_ptr);
|
||||
|
||||
for (auto&& info : m_result) {
|
||||
Rect selected_rect = rect_move;
|
||||
cv::Rect selected_rect = utils::make_rect<cv::Rect>(rect_move);
|
||||
selected_rect.x += info.smiley.rect.x;
|
||||
selected_rect.y += info.smiley.rect.y;
|
||||
selected_analyzer.set_roi(selected_rect);
|
||||
if (selected_analyzer.analyze()) {
|
||||
info.selected = true;
|
||||
#ifdef LOG_TRACE
|
||||
cv::putText(m_image_draw, "SELECTED", cv::Point(selected_rect.x, selected_rect.y + 50), 1, 1, cv::Scalar(0, 0, 255), 2);
|
||||
#endif
|
||||
|
||||
cv::Mat roi = m_image(selected_rect);
|
||||
cv::Mat hsv, bin;
|
||||
cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV);
|
||||
std::vector<cv::Mat> channels;
|
||||
cv::split(hsv, channels);
|
||||
int mask_lowb = selected_task_ptr->mask_range.first;
|
||||
int mask_uppb = selected_task_ptr->mask_range.second;
|
||||
|
||||
int count = 0;
|
||||
auto& h_channel = channels.at(0);
|
||||
for (int i = 0; i != h_channel.rows; ++i) {
|
||||
for (int j = 0; j != h_channel.cols; ++j) {
|
||||
cv::uint8_t value = h_channel.at<cv::uint8_t>(i, j);
|
||||
if (mask_lowb < value && value < mask_uppb) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.trace("selected_analyze |", count);
|
||||
info.selected = count >= selected_task_ptr->templ_threshold;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -226,4 +243,4 @@ bool asst::InfrastMoodImageAnalyzer::working_analyze()
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastMoodImageAnalyzer final : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -13,16 +14,19 @@ namespace asst {
|
||||
|
||||
void sort_result();
|
||||
|
||||
const std::vector<InfrastOperMoodInfo>& get_result() const noexcept {
|
||||
const std::vector<InfrastOperMoodInfo>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override {
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
AbstractImageAnalyzer::set_roi(roi);
|
||||
}
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi) {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image, roi);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include "InfrastOfficeTask.h"
|
||||
#include "InfrastOfficeTask.h"
|
||||
|
||||
#include "Controller.h"
|
||||
|
||||
const std::string asst::InfrastOfficeTask::FacilityName = "Office";
|
||||
|
||||
bool asst::InfrastOfficeTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastOfficeTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastOfficeTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
@@ -18,28 +20,52 @@ bool asst::InfrastOfficeTask::run()
|
||||
|
||||
swipe_to_the_right_of_main_ui();
|
||||
enter_facility(FacilityName, 0);
|
||||
|
||||
click_bottomleft_tab();
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
opers_detect_with_swipe();
|
||||
swipe_to_the_left_of_operlist();
|
||||
constexpr int retry_times = 1;
|
||||
for (int i = 0; i <= retry_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
swipe_to_the_left_of_operlist();
|
||||
opers_detect_with_swipe();
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
|
||||
[&](const InfrastOperSkillInfo& info) -> bool {
|
||||
return info.selected;
|
||||
});
|
||||
// 如果之前有干员在,那就不换人,直接退出办公室
|
||||
if (find_iter != m_all_available_opers.end()) {
|
||||
m_all_available_opers.erase(find_iter);
|
||||
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
|
||||
[&](const InfrastOperSkillInfo& info) -> bool {
|
||||
return info.selected;
|
||||
});
|
||||
|
||||
bool need_shift = true;
|
||||
if (find_iter != m_all_available_opers.end()) {
|
||||
switch (m_work_mode) {
|
||||
case InfrastWorkMode::Gentle:
|
||||
// 如果之前有干员在,那就不换人,直接退出
|
||||
m_all_available_opers.erase(find_iter);
|
||||
need_shift = false;
|
||||
break;
|
||||
case InfrastWorkMode::Aggressive:
|
||||
need_shift = true;
|
||||
ctrler.click(find_iter->rect);
|
||||
break;
|
||||
case InfrastWorkMode::Extreme: // TODO
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (need_shift) {
|
||||
optimal_calc();
|
||||
bool ret = opers_choose();
|
||||
if (!ret) {
|
||||
m_all_available_opers.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
optimal_calc();
|
||||
opers_choose();
|
||||
}
|
||||
|
||||
click_confirm_button();
|
||||
click_return_button();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastProductionTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastOfficeTask : public InfrastProductionTask
|
||||
{
|
||||
public:
|
||||
@@ -11,6 +12,7 @@ namespace asst {
|
||||
|
||||
const static std::string FacilityName;
|
||||
const static int MaxNumOfOpers = 1;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include "InfrastPowerTask.h"
|
||||
#include "InfrastPowerTask.h"
|
||||
|
||||
#include "Controller.h"
|
||||
|
||||
const std::string asst::InfrastPowerTask::FacilityName = "Power";
|
||||
|
||||
bool asst::InfrastPowerTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastPowerTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastPowerTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
@@ -16,40 +18,63 @@ bool asst::InfrastPowerTask::run()
|
||||
// 发电站只能造这一个
|
||||
set_product("Drone");
|
||||
|
||||
swipe_to_the_left_of_main_ui();
|
||||
|
||||
for (int i = 0; i != 3; ++i) {
|
||||
for (int i = 0; i != MaxNumOfFacility; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
swipe_to_the_left_of_main_ui();
|
||||
enter_facility(FacilityName, i);
|
||||
|
||||
if (!enter_oper_list_page()) {
|
||||
return false;
|
||||
}
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
if (m_all_available_opers.empty()) {
|
||||
opers_detect_with_swipe();
|
||||
constexpr int retry_times = 1;
|
||||
for (int i = 0; i <= retry_times; ++i) {
|
||||
swipe_to_the_left_of_operlist();
|
||||
}
|
||||
else {
|
||||
opers_detect();
|
||||
}
|
||||
|
||||
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
|
||||
[&](const InfrastOperSkillInfo& info) -> bool {
|
||||
return info.selected;
|
||||
});
|
||||
// 如果之前有干员在,那就不换人,直接退出当前发电站
|
||||
if (find_iter != m_all_available_opers.end()) {
|
||||
m_all_available_opers.erase(find_iter);
|
||||
}
|
||||
else {
|
||||
optimal_calc();
|
||||
opers_choose();
|
||||
}
|
||||
if (m_all_available_opers.empty()) {
|
||||
opers_detect_with_swipe();
|
||||
swipe_to_the_left_of_operlist();
|
||||
}
|
||||
else {
|
||||
opers_detect();
|
||||
}
|
||||
|
||||
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
|
||||
[&](const InfrastOperSkillInfo& info) -> bool {
|
||||
return info.selected;
|
||||
});
|
||||
bool need_shift = true;
|
||||
if (find_iter != m_all_available_opers.end()) {
|
||||
switch (m_work_mode) {
|
||||
case InfrastWorkMode::Gentle:
|
||||
// 如果之前有干员在,那就不换人,直接退出
|
||||
m_all_available_opers.erase(find_iter);
|
||||
need_shift = false;
|
||||
break;
|
||||
case InfrastWorkMode::Aggressive:
|
||||
need_shift = true;
|
||||
ctrler.click(find_iter->rect);
|
||||
break;
|
||||
case InfrastWorkMode::Extreme: // TODO
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (need_shift) {
|
||||
optimal_calc();
|
||||
bool ret = opers_choose();
|
||||
if (!ret) {
|
||||
m_all_available_opers.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
click_confirm_button();
|
||||
click_return_button();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastProductionTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastPowerTask : public InfrastProductionTask
|
||||
{
|
||||
public:
|
||||
@@ -11,6 +12,8 @@ namespace asst {
|
||||
|
||||
const static std::string FacilityName;
|
||||
const static int MaxNumOfOpers = 1;
|
||||
const static int MaxNumOfFacility = 3;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "InfrastProductionTask.h"
|
||||
#include "InfrastProductionTask.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <calculator/calculator.hpp>
|
||||
|
||||
#include "Resource.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Controller.h"
|
||||
#include "InfrastSkillsImageAnalyzer.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
#include "RuntimeStatus.h"
|
||||
|
||||
//bool asst::InfrastProductionTask::run()
|
||||
@@ -35,16 +35,22 @@
|
||||
|
||||
bool asst::InfrastProductionTask::shift_facility_list()
|
||||
{
|
||||
LogTraceFunction;
|
||||
facility_list_detect();
|
||||
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
const auto tab_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastFacilityListTab" + m_facility));
|
||||
MatchImageAnalyzer add_analyzer;
|
||||
const auto add_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastAddOperator" + m_facility));
|
||||
resource.task().task_ptr("InfrastAddOperator" + m_facility + m_work_mode_name));
|
||||
add_analyzer.set_task_info(*add_task_ptr);
|
||||
|
||||
for (const Rect& tab : m_facility_list_tabs) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
ctrler.click(tab);
|
||||
sleep(tab_task_ptr->rear_delay);
|
||||
|
||||
@@ -52,9 +58,18 @@ bool asst::InfrastProductionTask::shift_facility_list()
|
||||
const auto& image = ctrler.get_image();
|
||||
add_analyzer.set_image(image);
|
||||
if (!add_analyzer.analyze()) {
|
||||
log.info("no add button, just continue");
|
||||
continue;
|
||||
}
|
||||
Rect add_button = add_analyzer.get_result().rect;
|
||||
auto& rect = add_analyzer.get_result().rect;
|
||||
Rect add_button = rect;
|
||||
auto& rect_move = add_task_ptr->rect_move;
|
||||
if (!rect_move.empty()) {
|
||||
add_button.x += rect_move.x;
|
||||
add_button.y += rect_move.y;
|
||||
add_button.width = rect_move.width;
|
||||
add_button.height = rect_move.height;
|
||||
}
|
||||
|
||||
/* 识别当前正在造什么 */
|
||||
MatchImageAnalyzer product_analyzer(image);
|
||||
@@ -71,21 +86,34 @@ bool asst::InfrastProductionTask::shift_facility_list()
|
||||
}
|
||||
}
|
||||
set_product(cur_product);
|
||||
log.info("cur product", cur_product);
|
||||
/* 进入干员选择页面 */
|
||||
ctrler.click(add_button);
|
||||
sleep(add_task_ptr->rear_delay);
|
||||
click_clear_button();
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
if (m_all_available_opers.empty()) {
|
||||
opers_detect_with_swipe();
|
||||
constexpr int retry_times = 1;
|
||||
for (int i = 0; i <= retry_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
click_clear_button();
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
if (m_all_available_opers.empty()) {
|
||||
opers_detect_with_swipe();
|
||||
swipe_to_the_left_of_operlist();
|
||||
}
|
||||
else {
|
||||
opers_detect();
|
||||
}
|
||||
optimal_calc();
|
||||
bool ret = opers_choose();
|
||||
if (!ret) {
|
||||
m_all_available_opers.clear();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
opers_detect();
|
||||
}
|
||||
optimal_calc();
|
||||
opers_choose();
|
||||
click_confirm_button();
|
||||
}
|
||||
return true;
|
||||
@@ -93,11 +121,16 @@ bool asst::InfrastProductionTask::shift_facility_list()
|
||||
|
||||
bool asst::InfrastProductionTask::opers_detect_with_swipe()
|
||||
{
|
||||
LogTraceFunction;
|
||||
m_all_available_opers.clear();
|
||||
|
||||
int first_number = 0;
|
||||
while (true) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
size_t num = opers_detect();
|
||||
log.trace("opers_detect return", num);
|
||||
|
||||
// 这里本来是判断不相等就可以退出循环。
|
||||
// 但是有时候滑动会把一个干员挡住一半,一个页面完整的干员真的只有10个,所以加个2的差值
|
||||
@@ -117,6 +150,7 @@ bool asst::InfrastProductionTask::opers_detect_with_swipe()
|
||||
|
||||
size_t asst::InfrastProductionTask::opers_detect()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto& image = ctrler.get_image();
|
||||
|
||||
InfrastSkillsImageAnalyzer skills_analyzer(image);
|
||||
@@ -129,7 +163,8 @@ size_t asst::InfrastProductionTask::opers_detect()
|
||||
max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_info.size());
|
||||
|
||||
for (const auto& cur_info : cur_all_info) {
|
||||
auto find_iter = std::find_if(m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||||
auto find_iter = std::find_if(
|
||||
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||||
[&cur_info](const InfrastOperSkillInfo& info) -> bool {
|
||||
int dist = utils::hamming(cur_info.hash, info.hash);
|
||||
return dist < HashDistThres;
|
||||
@@ -139,27 +174,7 @@ size_t asst::InfrastProductionTask::opers_detect()
|
||||
continue;
|
||||
}
|
||||
auto pred_info = cur_info;
|
||||
// 根据正则,计算当前干员的实际效率
|
||||
for (auto&& [product, formula] : pred_info.skills_comb.efficient_regex) {
|
||||
std::string cur_formula = formula;
|
||||
for (size_t pos = 0; pos != std::string::npos;) {
|
||||
pos = cur_formula.find('[', pos);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t rp_pos = cur_formula.find(']', pos);
|
||||
if (rp_pos == std::string::npos) {
|
||||
break;
|
||||
// TODO 报错!
|
||||
}
|
||||
std::string status_key = cur_formula.substr(pos + 1, rp_pos - pos - 1);
|
||||
int status_value = std::any_cast<int>(status.get(status_key));
|
||||
cur_formula.replace(pos, rp_pos - pos + 1, std::to_string(status_value));
|
||||
}
|
||||
|
||||
int eff = calculator::eval(cur_formula);
|
||||
pred_info.skills_comb.efficient[product] = eff;
|
||||
}
|
||||
pred_info.skills_comb = efficient_regex_calc(pred_info.skills_comb);
|
||||
m_all_available_opers.emplace_back(std::move(pred_info));
|
||||
}
|
||||
return cur_all_info.size();
|
||||
@@ -167,6 +182,7 @@ size_t asst::InfrastProductionTask::opers_detect()
|
||||
|
||||
bool asst::InfrastProductionTask::optimal_calc()
|
||||
{
|
||||
LogTraceFunction;
|
||||
auto& facility_info = resource.infrast().get_facility_info(m_facility);
|
||||
int max_num_of_opers = facility_info.max_num_of_opers;
|
||||
|
||||
@@ -179,9 +195,9 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
optimal_opers.reserve(max_num_of_opers);
|
||||
double max_efficient = 0;
|
||||
std::sort(m_all_available_opers.begin(), m_all_available_opers.end(),
|
||||
[&](const InfrastOperSkillInfo& lhs, const InfrastOperSkillInfo& rhs) -> bool {
|
||||
return lhs.skills_comb.efficient.at(m_product) > rhs.skills_comb.efficient.at(m_product);
|
||||
});
|
||||
[&](const InfrastOperSkillInfo& lhs, const InfrastOperSkillInfo& rhs) -> bool {
|
||||
return lhs.skills_comb.efficient.at(m_product) > rhs.skills_comb.efficient.at(m_product);
|
||||
});
|
||||
|
||||
for (const auto& oper : m_all_available_opers) {
|
||||
std::string skill_str;
|
||||
@@ -215,6 +231,7 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
cur_opers.reserve(max_num_of_opers);
|
||||
double cur_efficient = 0;
|
||||
// 条件判断,不符合的直接过滤掉
|
||||
bool meet_condition = true;
|
||||
for (const auto& [cond, cond_value] : group.conditions) {
|
||||
if (!status.exist(cond)) {
|
||||
continue;
|
||||
@@ -222,12 +239,17 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
// TODO:这里做成除了不等于,还可计算大于、小于等不同条件的
|
||||
int cur_value = std::any_cast<int>(status.get(cond));
|
||||
if (cur_value != cond_value) {
|
||||
continue;
|
||||
meet_condition = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!meet_condition) {
|
||||
continue;
|
||||
}
|
||||
// necessary里的技能,一个都不能少
|
||||
for (const InfrastSkillsComb& nec_skills : group.necessary) {
|
||||
auto find_iter = std::find_if(cur_available_opers.cbegin(), cur_available_opers.cend(),
|
||||
auto find_iter = std::find_if(
|
||||
cur_available_opers.cbegin(), cur_available_opers.cend(),
|
||||
[&](const InfrastOperSkillInfo& arg) -> bool {
|
||||
return arg.skills_comb == nec_skills;
|
||||
});
|
||||
@@ -236,7 +258,13 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
break;
|
||||
}
|
||||
cur_opers.emplace_back(nec_skills);
|
||||
cur_efficient += nec_skills.efficient.at(m_product);
|
||||
if (auto iter = nec_skills.efficient_regex.find(m_product);
|
||||
iter != nec_skills.efficient_regex.cend()) {
|
||||
cur_efficient += efficient_regex_calc(nec_skills).efficient.at(m_product);
|
||||
}
|
||||
else {
|
||||
cur_efficient += nec_skills.efficient.at(m_product);
|
||||
}
|
||||
cur_available_opers.erase(find_iter);
|
||||
}
|
||||
if (group_unavailable) {
|
||||
@@ -245,21 +273,27 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
// 排个序,因为产物不同,效率可能会发生变化,所以配置文件里默认的顺序不一定准确
|
||||
auto optional = group.optional;
|
||||
std::sort(optional.begin(), optional.end(),
|
||||
[&](const InfrastSkillsComb& lhs, const InfrastSkillsComb& rhs) -> bool {
|
||||
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
|
||||
});
|
||||
[&](const InfrastSkillsComb& lhs, const InfrastSkillsComb& rhs) -> bool {
|
||||
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
|
||||
});
|
||||
|
||||
// 可能有多个干员有同样的技能,所以这里需要循环找同一个技能,直到找不到为止
|
||||
for (const InfrastSkillsComb& opt : optional) {
|
||||
auto find_iter = cur_available_opers.cbegin();
|
||||
while (cur_opers.size() != max_num_of_opers) {
|
||||
find_iter = std::find_if(find_iter, cur_available_opers.cend(),
|
||||
[&](const InfrastOperSkillInfo& arg) -> bool {
|
||||
return arg.skills_comb.skills == opt.skills;
|
||||
});
|
||||
[&](const InfrastOperSkillInfo& arg) -> bool {
|
||||
return arg.skills_comb.skills == opt.skills;
|
||||
});
|
||||
if (find_iter != cur_available_opers.cend()) {
|
||||
cur_opers.emplace_back(opt);
|
||||
cur_efficient += opt.efficient.at(m_product);
|
||||
if (auto iter = opt.efficient_regex.find(m_product);
|
||||
iter != opt.efficient_regex.cend()) {
|
||||
cur_efficient += efficient_regex_calc(opt).efficient.at(m_product);
|
||||
}
|
||||
else {
|
||||
cur_efficient += opt.efficient.at(m_product);
|
||||
}
|
||||
find_iter = cur_available_opers.erase(find_iter);
|
||||
}
|
||||
else {
|
||||
@@ -313,7 +347,12 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
|
||||
bool asst::InfrastProductionTask::opers_choose()
|
||||
{
|
||||
LogTraceFunction;
|
||||
bool has_error = false;
|
||||
while (true) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
const auto& image = ctrler.get_image();
|
||||
|
||||
InfrastSkillsImageAnalyzer skills_analyzer(image);
|
||||
@@ -326,12 +365,26 @@ bool asst::InfrastProductionTask::opers_choose()
|
||||
|
||||
auto cur_all_info = skills_analyzer.get_result();
|
||||
|
||||
// 这个情况一般是滑动/识别出错了,把所有的干员都滑过去了
|
||||
if (cur_all_info.empty()) {
|
||||
if (!has_error) {
|
||||
has_error = true;
|
||||
// 倒回去再来一遍
|
||||
swipe_to_the_left_of_operlist();
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// 如果已经出过一次错了,那就可能不是opers_choose出错,而是之前的opers_detect出错了
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> selected_hash;
|
||||
for (auto opt_iter = m_optimal_opers.begin(); opt_iter != m_optimal_opers.end();) {
|
||||
auto find_iter = std::find_if(cur_all_info.cbegin(), cur_all_info.cend(),
|
||||
[&](const InfrastOperSkillInfo& lhs) -> bool {
|
||||
return lhs.skills_comb == opt_iter->skills_comb;
|
||||
});
|
||||
[&](const InfrastOperSkillInfo& lhs) -> bool {
|
||||
return lhs.skills_comb == opt_iter->skills_comb;
|
||||
});
|
||||
if (find_iter == cur_all_info.cend()) {
|
||||
++opt_iter;
|
||||
continue;
|
||||
@@ -344,9 +397,9 @@ bool asst::InfrastProductionTask::opers_choose()
|
||||
selected_hash.emplace_back(find_iter->hash);
|
||||
{
|
||||
auto avlb_iter = std::find_if(m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||||
[&](const InfrastOperSkillInfo& lhs) -> bool {
|
||||
return lhs.skills_comb == opt_iter->skills_comb;
|
||||
});
|
||||
[&](const InfrastOperSkillInfo& lhs) -> bool {
|
||||
return lhs.skills_comb == opt_iter->skills_comb;
|
||||
});
|
||||
m_all_available_opers.erase(avlb_iter);
|
||||
}
|
||||
cur_all_info.erase(find_iter);
|
||||
@@ -363,8 +416,35 @@ bool asst::InfrastProductionTask::opers_choose()
|
||||
return true;
|
||||
}
|
||||
|
||||
asst::InfrastSkillsComb asst::InfrastProductionTask::efficient_regex_calc(InfrastSkillsComb skills_comb) const
|
||||
{
|
||||
// 根据正则,计算当前干员的实际效率
|
||||
for (auto&& [product, formula] : skills_comb.efficient_regex) {
|
||||
std::string cur_formula = formula;
|
||||
for (size_t pos = 0; pos != std::string::npos;) {
|
||||
pos = cur_formula.find('[', pos);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t rp_pos = cur_formula.find(']', pos);
|
||||
if (rp_pos == std::string::npos) {
|
||||
break;
|
||||
// TODO 报错!
|
||||
}
|
||||
std::string status_key = cur_formula.substr(pos + 1, rp_pos - pos - 1);
|
||||
int status_value = std::any_cast<int>(status.get(status_key));
|
||||
cur_formula.replace(pos, rp_pos - pos + 1, std::to_string(status_value));
|
||||
}
|
||||
|
||||
int eff = calculator::eval(cur_formula);
|
||||
skills_comb.efficient[product] = eff;
|
||||
}
|
||||
return skills_comb;
|
||||
}
|
||||
|
||||
bool asst::InfrastProductionTask::facility_list_detect()
|
||||
{
|
||||
LogTraceFunction;
|
||||
m_facility_list_tabs.clear();
|
||||
|
||||
const auto& image = ctrler.get_image();
|
||||
@@ -384,4 +464,4 @@ bool asst::InfrastProductionTask::facility_list_detect()
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "InfrastAbstractTask.h"
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
// 生产类设施的任务,适用于制造站/贸易站
|
||||
class InfrastProductionTask : public InfrastAbstractTask
|
||||
{
|
||||
@@ -12,19 +13,23 @@ namespace asst {
|
||||
using InfrastAbstractTask::InfrastAbstractTask;
|
||||
virtual ~InfrastProductionTask() = default;
|
||||
//virtual bool run() override;
|
||||
void set_facility(std::string facility_name) noexcept {
|
||||
void set_facility(std::string facility_name) noexcept
|
||||
{
|
||||
m_facility = std::move(facility_name);
|
||||
}
|
||||
void set_product(std::string product_name) noexcept {
|
||||
void set_product(std::string product_name) noexcept
|
||||
{
|
||||
m_product = std::move(product_name);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool shift_facility_list();
|
||||
bool facility_list_detect();
|
||||
bool opers_detect_with_swipe();
|
||||
size_t opers_detect(); // 返回当前页面的干员数
|
||||
size_t opers_detect(); // 返回当前页面的干员数
|
||||
bool optimal_calc();
|
||||
bool opers_choose();
|
||||
InfrastSkillsComb efficient_regex_calc(InfrastSkillsComb skill_info) const;
|
||||
|
||||
constexpr static int HashDistThres = 25;
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#include "InfrastReceptionTask.h"
|
||||
#include "InfrastReceptionTask.h"
|
||||
|
||||
#include "Controller.h"
|
||||
#include "Resource.h"
|
||||
#include "InfrastClueVacancyImageAnalyzer.h"
|
||||
#include "InfrastClueImageAnalyzer.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "InfrastClueVacancyImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "ProcessTaskImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
const std::string asst::InfrastReceptionTask::FacilityName = "Reception";
|
||||
|
||||
bool asst::InfrastReceptionTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastReceptionTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastReceptionTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
@@ -38,8 +38,12 @@ bool asst::InfrastReceptionTask::run()
|
||||
|
||||
bool asst::InfrastReceptionTask::harvest_clue()
|
||||
{
|
||||
LogTraceFunction;
|
||||
std::vector<std::string> tasks_vec = { "InfrastClueNew" };
|
||||
while (!tasks_vec.empty()) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
ProcessTaskImageAnalyzer analyzer(ctrler.get_image(), tasks_vec);
|
||||
if (!analyzer.analyze()) {
|
||||
break;
|
||||
@@ -71,37 +75,16 @@ bool asst::InfrastReceptionTask::harvest_clue()
|
||||
|
||||
bool asst::InfrastReceptionTask::proc_clue()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const static std::string clue_vacancy = "InfrastClueVacancy";
|
||||
const static std::vector<std::string> clue_suffix = {
|
||||
"No1", "No2", "No3", "No4", "No5", "No6", "No7" };
|
||||
"No1", "No2", "No3", "No4", "No5", "No6", "No7"
|
||||
};
|
||||
|
||||
cv::Mat image;
|
||||
for (const std::string& clue : clue_suffix) {
|
||||
// 先识别线索的空位
|
||||
image = ctrler.get_image();
|
||||
InfrastClueVacancyImageAnalyzer vacancy_analyzer(image);
|
||||
vacancy_analyzer.set_to_be_analyzed({ clue });
|
||||
if (!vacancy_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
// 点开线索的空位
|
||||
Rect vacancy = vacancy_analyzer.get_vacancy().cbegin()->second;
|
||||
ctrler.click(vacancy);
|
||||
int delay = resource.task().task_ptr(clue_vacancy + clue)->rear_delay;
|
||||
sleep(delay);
|
||||
proc_vacancy();
|
||||
|
||||
// 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的)
|
||||
swipe_to_the_bottom_of_clue_list_on_the_right();
|
||||
image = ctrler.get_image();
|
||||
InfrastClueImageAnalyzer clue_analyzer(image);
|
||||
if (!clue_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
ctrler.click(clue_analyzer.get_result().back().first);
|
||||
sleep(delay);
|
||||
}
|
||||
// 开启线索交流,“解锁线索”
|
||||
image = ctrler.get_image();
|
||||
cv::Mat image = ctrler.get_image();
|
||||
MatchImageAnalyzer unlock_analyzer(image);
|
||||
const auto unlock_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("UnlockClues"));
|
||||
@@ -110,6 +93,7 @@ bool asst::InfrastReceptionTask::proc_clue()
|
||||
ctrler.click(unlock_analyzer.get_result().rect);
|
||||
sleep(unlock_task_ptr->rear_delay);
|
||||
click_bottomleft_tab();
|
||||
proc_vacancy();
|
||||
image = ctrler.get_image();
|
||||
}
|
||||
|
||||
@@ -136,37 +120,97 @@ bool asst::InfrastReceptionTask::proc_clue()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::InfrastReceptionTask::proc_vacancy()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const static std::string clue_vacancy = "InfrastClueVacancy";
|
||||
const static std::vector<std::string> clue_suffix = {
|
||||
"No1", "No2", "No3", "No4", "No5", "No6", "No7"
|
||||
};
|
||||
|
||||
for (const std::string& clue : clue_suffix) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
// 先识别线索的空位
|
||||
cv::Mat image = ctrler.get_image();
|
||||
InfrastClueVacancyImageAnalyzer vacancy_analyzer(image);
|
||||
vacancy_analyzer.set_to_be_analyzed({ clue });
|
||||
if (!vacancy_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
// 点开线索的空位
|
||||
Rect vacancy = vacancy_analyzer.get_vacancy().cbegin()->second;
|
||||
ctrler.click(vacancy);
|
||||
int delay = resource.task().task_ptr(clue_vacancy + clue)->rear_delay;
|
||||
sleep(delay);
|
||||
|
||||
// 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的)
|
||||
swipe_to_the_bottom_of_clue_list_on_the_right();
|
||||
image = ctrler.get_image();
|
||||
InfrastClueImageAnalyzer clue_analyzer(image);
|
||||
if (!clue_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
ctrler.click(clue_analyzer.get_result().back().first);
|
||||
sleep(delay);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::InfrastReceptionTask::shift()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto& image = ctrler.get_image();
|
||||
MatchImageAnalyzer add_analyzer(image);
|
||||
|
||||
const auto add_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastAddOperator" + m_facility));
|
||||
add_analyzer.set_task_info(*add_task_ptr);
|
||||
const auto raw_task_ptr = resource.task().task_ptr("InfrastAddOperator" + m_facility + m_work_mode_name);
|
||||
switch (raw_task_ptr->algorithm) {
|
||||
case AlgorithmType::JustReturn:
|
||||
if (raw_task_ptr->action == ProcessTaskAction::ClickRect) {
|
||||
ctrler.click(raw_task_ptr->specific_rect);
|
||||
}
|
||||
break;
|
||||
case AlgorithmType::MatchTemplate: {
|
||||
const auto add_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(raw_task_ptr);
|
||||
add_analyzer.set_task_info(*add_task_ptr);
|
||||
|
||||
if (!add_analyzer.analyze()) {
|
||||
return true;
|
||||
if (!add_analyzer.analyze()) {
|
||||
return true;
|
||||
}
|
||||
ctrler.click(add_analyzer.get_result().rect);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ctrler.click(add_analyzer.get_result().rect);
|
||||
sleep(add_task_ptr->rear_delay);
|
||||
sleep(raw_task_ptr->rear_delay);
|
||||
|
||||
swipe_to_the_left_of_operlist();
|
||||
click_clear_button();
|
||||
constexpr int retry_times = 1;
|
||||
for (int i = 0; i <= retry_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
swipe_to_the_left_of_operlist();
|
||||
click_clear_button();
|
||||
|
||||
opers_detect_with_swipe();
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
optimal_calc();
|
||||
opers_choose();
|
||||
opers_detect_with_swipe();
|
||||
swipe_to_the_left_of_operlist();
|
||||
|
||||
optimal_calc();
|
||||
bool ret = opers_choose();
|
||||
if (!ret) {
|
||||
m_all_available_opers.clear();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
click_confirm_button();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::InfrastReceptionTask::swipe_to_the_bottom_of_clue_list_on_the_right()
|
||||
{
|
||||
LogTraceFunction;
|
||||
static Rect begin_rect = resource.task().task_ptr("InfrastClueOnTheRightSwipeBegin")->specific_rect;
|
||||
static Rect end_rect = resource.task().task_ptr("InfrastClueOnTheRightSwipeEnd")->specific_rect;
|
||||
static int duration = resource.task().task_ptr("InfrastClueOnTheRightSwipeBegin")->pre_delay;
|
||||
@@ -178,4 +222,4 @@ bool asst::InfrastReceptionTask::swipe_to_the_bottom_of_clue_list_on_the_right()
|
||||
}
|
||||
sleep(extra_delay);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastProductionTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastReceptionTask : public InfrastProductionTask
|
||||
{
|
||||
public:
|
||||
@@ -10,9 +11,11 @@ namespace asst {
|
||||
virtual bool run() override;
|
||||
|
||||
const static std::string FacilityName;
|
||||
|
||||
private:
|
||||
bool harvest_clue();
|
||||
bool proc_clue();
|
||||
bool proc_vacancy();
|
||||
bool shift();
|
||||
bool swipe_to_the_bottom_of_clue_list_on_the_right();
|
||||
};
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
#include "InfrastSkillsImageAnalyzer.h"
|
||||
#include "InfrastSkillsImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
bool asst::InfrastSkillsImageAnalyzer::analyze()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
m_skills_detected.clear();
|
||||
m_skills_splited.clear();
|
||||
m_result.clear();
|
||||
@@ -18,26 +20,35 @@ bool asst::InfrastSkillsImageAnalyzer::analyze()
|
||||
|
||||
skill_analyze();
|
||||
|
||||
for (auto&& info : m_result) {
|
||||
std::string skill_str;
|
||||
for (auto&& skill : info.skills_comb.skills) {
|
||||
skill_str += skill.names.front() + " ";
|
||||
}
|
||||
log.trace(info.hash, info.rect.to_string(), skill_str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::InfrastSkillsImageAnalyzer::sort_result()
|
||||
{
|
||||
LogTraceFunction;
|
||||
// 按位置排个序
|
||||
std::sort(m_result.begin(), m_result.end(),
|
||||
[](const auto& lhs, const auto& rhs) -> bool {
|
||||
if (std::abs(lhs.rect.x - rhs.rect.x) < 5) { // x差距较小则理解为是同一排的,按y排序
|
||||
return lhs.rect.y < rhs.rect.y;
|
||||
}
|
||||
else {
|
||||
return lhs.rect.x < rhs.rect.x;
|
||||
}
|
||||
}
|
||||
);
|
||||
[](const auto& lhs, const auto& rhs) -> bool {
|
||||
if (std::abs(lhs.rect.x - rhs.rect.x) < 5) { // x差距较小则理解为是同一排的,按y排序
|
||||
return lhs.rect.y < rhs.rect.y;
|
||||
}
|
||||
else {
|
||||
return lhs.rect.x < rhs.rect.x;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool asst::InfrastSkillsImageAnalyzer::skills_detect()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto upper_task_ptr = resource.task().task_ptr("InfrastSkillsUpper");
|
||||
const auto lower_task_ptr = resource.task().task_ptr("InfrastSkillsLower");
|
||||
const auto hash_task_ptr = resource.task().task_ptr("InfrastSkillsHash");
|
||||
@@ -73,8 +84,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_detect()
|
||||
skills_rect.height = skills_height;
|
||||
|
||||
// 超过ROI边界了
|
||||
if (skills_rect.x + skills_rect.width > roi.x + roi.width
|
||||
|| skills_rect.x < roi.x) {
|
||||
if (skills_rect.x + skills_rect.width > roi.x + roi.width || skills_rect.x < roi.x) {
|
||||
continue;
|
||||
}
|
||||
#ifdef LOG_TRACE
|
||||
@@ -98,6 +108,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_detect()
|
||||
|
||||
bool asst::InfrastSkillsImageAnalyzer::skills_split()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastSkills"));
|
||||
const auto thres = task_ptr->hist_threshold;
|
||||
@@ -113,7 +124,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_split()
|
||||
}
|
||||
|
||||
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(roi));
|
||||
std::vector<Rect> skills_vec; // 单个干员的所有技能
|
||||
std::vector<Rect> skills_vec; // 单个干员的所有技能
|
||||
for (int i = 0; i != MaxNumOfSkills; ++i) {
|
||||
int x = i * skill_width + spacing * i;
|
||||
Rect skill_rect(x, 0, skill_width, roi.height);
|
||||
@@ -144,6 +155,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_split()
|
||||
|
||||
bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastSkills"));
|
||||
|
||||
@@ -151,13 +163,8 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
|
||||
skill_analyzer.set_mask_range(task_ptr->mask_range);
|
||||
skill_analyzer.set_threshold(task_ptr->templ_threshold);
|
||||
|
||||
const auto selected_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastOperSelected"));
|
||||
MatchImageAnalyzer selected_analyzer(m_image);
|
||||
selected_analyzer.set_task_info(*selected_task_ptr);
|
||||
|
||||
for (const auto& [hash, skills_rect_vec] : m_skills_splited) {
|
||||
std::unordered_set<InfrastSkill> skills_set; // 单个干员的全部技能
|
||||
std::unordered_set<InfrastSkill> skills_set; // 单个干员的全部技能
|
||||
std::string log_str = "[ ";
|
||||
for (const Rect& skill_rect : skills_rect_vec) {
|
||||
skill_analyzer.set_roi(skill_rect);
|
||||
@@ -189,9 +196,9 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
|
||||
// 匹配得分最高的id作为基准,排除有识别错误,其他的技能混进来了的情况
|
||||
// 即排除容器中,除了有同一个技能的不同等级,还有别的技能的情况
|
||||
auto max_iter = std::max_element(possible_skills.begin(), possible_skills.end(),
|
||||
[](const auto& lhs, const auto& rhs) -> bool {
|
||||
return lhs.second.score < rhs.second.score;
|
||||
});
|
||||
[](const auto& lhs, const auto& rhs) -> bool {
|
||||
return lhs.second.score < rhs.second.score;
|
||||
});
|
||||
std::string base_id = max_iter->first.id;
|
||||
size_t level_pos = 0;
|
||||
// 倒着找,第一个不是数字的。前面就是技能基础id名字,后面的数字就是技能等级
|
||||
@@ -207,8 +214,7 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
|
||||
if (size_t find_pos = skill.id.find(base_id);
|
||||
find_pos != std::string::npos) {
|
||||
std::string cur_skill_level = skill.id.substr(base_id.size());
|
||||
if (max_level.empty()
|
||||
|| cur_skill_level > max_level) {
|
||||
if (max_level.empty() || cur_skill_level > max_level) {
|
||||
max_level = cur_skill_level;
|
||||
most_confident_skills = skill;
|
||||
}
|
||||
@@ -233,16 +239,7 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
|
||||
// 识别该干员是否被选中。数据结构设计的不太合理,先这么凑合用了
|
||||
int smiley_x = skills_rect_vec.front().x - task_ptr->rect_move.x;
|
||||
int smiley_y = skills_rect_vec.front().y - task_ptr->rect_move.y;
|
||||
Rect selected_rect = selected_task_ptr->rect_move;
|
||||
selected_rect.x += smiley_x;
|
||||
selected_rect.y += smiley_y;
|
||||
selected_analyzer.set_roi(selected_rect);
|
||||
if (selected_analyzer.analyze()) {
|
||||
info.selected = true;
|
||||
}
|
||||
else {
|
||||
info.selected = false;
|
||||
}
|
||||
info.selected = selected_analyze(smiley_x, smiley_y);
|
||||
|
||||
m_result.emplace_back(std::move(info));
|
||||
}
|
||||
@@ -250,4 +247,39 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::InfrastSkillsImageAnalyzer::selected_analyze(int smiley_x, int smiley_y)
|
||||
{
|
||||
LogTraceFunction;
|
||||
const auto selected_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
resource.task().task_ptr("InfrastOperSelected"));
|
||||
cv::Rect selected_rect = utils::make_rect<cv::Rect>(selected_task_ptr->rect_move);
|
||||
selected_rect.x += smiley_x;
|
||||
selected_rect.y += smiley_y;
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
cv::Mat draw = m_image_draw.clone();
|
||||
cv::rectangle(draw, selected_rect, cv::Scalar(0, 0, 255), 2);
|
||||
#endif // LOG_TRACE
|
||||
cv::Mat roi = m_image(selected_rect);
|
||||
cv::Mat hsv, bin;
|
||||
cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV);
|
||||
std::vector<cv::Mat> channels;
|
||||
cv::split(hsv, channels);
|
||||
int mask_lowb = selected_task_ptr->mask_range.first;
|
||||
int mask_uppb = selected_task_ptr->mask_range.second;
|
||||
|
||||
int count = 0;
|
||||
auto& h_channel = channels.at(0);
|
||||
for (int i = 0; i != h_channel.rows; ++i) {
|
||||
for (int j = 0; j != h_channel.cols; ++j) {
|
||||
cv::uint8_t value = h_channel.at<cv::uint8_t>(i, j);
|
||||
if (mask_lowb < value && value < mask_uppb) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.trace("selected_analyze |", count);
|
||||
return count >= selected_task_ptr->templ_threshold;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastSkillsImageAnalyzer final : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -17,26 +18,31 @@ namespace asst {
|
||||
|
||||
void sort_result();
|
||||
|
||||
const std::vector<InfrastOperSkillInfo>& get_result() const noexcept {
|
||||
const std::vector<InfrastOperSkillInfo>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
void set_facility(std::string facility_name) noexcept {
|
||||
void set_facility(std::string facility_name) noexcept
|
||||
{
|
||||
m_facility = std::move(facility_name);
|
||||
}
|
||||
|
||||
constexpr static int MaxNumOfSkills = 2; // 单个干员最多有几个基建技能
|
||||
constexpr static int MaxNumOfSkills = 2; // 单个干员最多有几个基建技能
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override {
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
AbstractImageAnalyzer::set_roi(roi);
|
||||
}
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi) {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image, roi);
|
||||
}
|
||||
bool skills_detect(); // 检测出所有技能区域
|
||||
bool skills_split(); // 拆分成一个一个的区域
|
||||
bool skill_analyze(); // 识别每个技能区域是啥
|
||||
bool skills_detect(); // 检测出所有技能区域
|
||||
bool skills_split(); // 拆分成一个一个的区域
|
||||
bool skill_analyze(); // 识别每个技能区域是啥
|
||||
bool selected_analyze(int smiley_x, int smiley_y); // 识别干员是否被选中
|
||||
|
||||
std::string m_facility;
|
||||
// skills_detect()的结果,key是hash,value是单个干员的全部技能区域
|
||||
@@ -46,4 +52,4 @@ namespace asst {
|
||||
// skill_analyze()的结果,最终结果
|
||||
std::vector<InfrastOperSkillInfo> m_result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
bool asst::InfrastSmileyImageAnalyzer::analyze()
|
||||
{
|
||||
const static std::unordered_map<InfrastSmileyType, std::string> smiley_map =
|
||||
{
|
||||
{InfrastSmileyType::Rest, "InfrastSmileyOnRest"},
|
||||
{InfrastSmileyType::Work, "InfrastSmileyOnWork"},
|
||||
{InfrastSmileyType::Distract, "InfrastSmileyOnDistract"}
|
||||
const static std::unordered_map<InfrastSmileyType, std::string> smiley_map = {
|
||||
{ InfrastSmileyType::Rest, "InfrastSmileyOnRest" },
|
||||
{ InfrastSmileyType::Work, "InfrastSmileyOnWork" },
|
||||
{ InfrastSmileyType::Distract, "InfrastSmileyOnDistract" }
|
||||
};
|
||||
|
||||
m_result.clear();
|
||||
@@ -35,4 +34,4 @@ bool asst::InfrastSmileyImageAnalyzer::analyze()
|
||||
}
|
||||
m_result = std::move(temp_result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastSmileyImageAnalyzer : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -12,9 +13,11 @@ namespace asst {
|
||||
|
||||
virtual bool analyze() override;
|
||||
|
||||
const std::vector<InfrastSmileyInfo>& get_result() const noexcept {
|
||||
const std::vector<InfrastSmileyInfo>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<InfrastSmileyInfo> m_result;
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "InfrastTradeTask.h"
|
||||
#include "InfrastTradeTask.h"
|
||||
|
||||
const std::string asst::InfrastTradeTask::FacilityName = "Trade";
|
||||
|
||||
bool asst::InfrastTradeTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastTradeTask" },
|
||||
{ "task_chain", m_task_chain}
|
||||
{ "task_type", "InfrastTradeTask" },
|
||||
{ "task_chain", m_task_chain }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
@@ -20,4 +20,4 @@ bool asst::InfrastTradeTask::run()
|
||||
shift_facility_list();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "InfrastProductionTask.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class InfrastTradeTask : public InfrastProductionTask
|
||||
{
|
||||
public:
|
||||
@@ -13,4 +14,4 @@ namespace asst {
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ItemConfiger.h"
|
||||
#include "ItemConfiger.h"
|
||||
|
||||
#include <json.h>
|
||||
|
||||
@@ -9,4 +9,4 @@ bool asst::ItemConfiger::parse(const json::value& json)
|
||||
m_item_name.emplace(id, std::move(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "AbstractConfiger.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class ItemConfiger : public AbstractConfiger
|
||||
{
|
||||
public:
|
||||
@@ -21,10 +22,12 @@ namespace asst {
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
const std::unordered_map<std::string, int>& get_drop_count() const noexcept {
|
||||
const std::unordered_map<std::string, int>& get_drop_count() const noexcept
|
||||
{
|
||||
return m_drop_count;
|
||||
}
|
||||
std::unordered_map<std::string, int>& get_drop_count() noexcept {
|
||||
std::unordered_map<std::string, int>& get_drop_count() noexcept
|
||||
{
|
||||
return m_drop_count;
|
||||
}
|
||||
int get_drop_count(const std::string& id) const noexcept
|
||||
@@ -41,7 +44,8 @@ namespace asst {
|
||||
{
|
||||
m_drop_count.emplace(std::move(id), count);
|
||||
}
|
||||
void increase_drop_count(std::string id, int count) {
|
||||
void increase_drop_count(std::string id, int count)
|
||||
{
|
||||
m_drop_count[std::move(id)] += count;
|
||||
}
|
||||
void clear_drop_count() noexcept
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Version.h"
|
||||
|
||||
namespace asst {
|
||||
class Logger {
|
||||
namespace asst
|
||||
{
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
~Logger() = default;
|
||||
|
||||
@@ -25,19 +27,19 @@ namespace asst {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void trace(Args &&... args)
|
||||
inline void trace(Args&&... args)
|
||||
{
|
||||
constexpr static std::string_view level = "TRC";
|
||||
log(level, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
inline void info(Args &&... args)
|
||||
inline void info(Args&&... args)
|
||||
{
|
||||
constexpr static std::string_view level = "INF";
|
||||
log(level, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
inline void error(Args &&... args)
|
||||
inline void error(Args&&... args)
|
||||
{
|
||||
constexpr static std::string_view level = "ERR";
|
||||
log(level, std::forward<Args>(args)...);
|
||||
@@ -45,6 +47,7 @@ namespace asst {
|
||||
|
||||
const std::string m_log_filename = asst::utils::get_cur_dir() + "asst.log";
|
||||
const std::string m_log_bak_filename = asst::utils::get_cur_dir() + "asst.bak.log";
|
||||
|
||||
private:
|
||||
Logger()
|
||||
{
|
||||
@@ -63,8 +66,7 @@ namespace asst {
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch (...) {
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -80,14 +82,14 @@ namespace asst {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void log(const std::string_view& level, Args &&... args)
|
||||
void log(const std::string_view& level, Args&&... args)
|
||||
{
|
||||
std::unique_lock<std::mutex> trace_lock(m_trace_mutex);
|
||||
|
||||
char buff[128] = { 0 };
|
||||
sprintf_s(buff, "[%s][%s][Px%x][Tx%x]",
|
||||
asst::utils::get_format_time().c_str(),
|
||||
level.data(), _getpid(), ::GetCurrentThreadId());
|
||||
asst::utils::get_format_time().c_str(),
|
||||
level.data(), _getpid(), ::GetCurrentThreadId());
|
||||
|
||||
stream_args<true>(std::cout, buff, std::forward<Args>(args)...);
|
||||
std::ofstream ofs(m_log_filename, std::ios::out | std::ios::app);
|
||||
@@ -96,7 +98,7 @@ namespace asst {
|
||||
}
|
||||
|
||||
template <bool ToGbk = false, typename T, typename... Args>
|
||||
inline void stream_args(std::ostream& os, T&& first, Args && ...rest)
|
||||
inline void stream_args(std::ostream& os, T&& first, Args&&... rest)
|
||||
{
|
||||
stream<ToGbk, T>()(os, std::forward<T>(first));
|
||||
stream_args<ToGbk>(os, std::forward<Args>(rest)...);
|
||||
@@ -127,7 +129,8 @@ namespace asst {
|
||||
std::mutex m_trace_mutex;
|
||||
};
|
||||
|
||||
class LoggerAux {
|
||||
class LoggerAux
|
||||
{
|
||||
public:
|
||||
LoggerAux(const std::string& func_name)
|
||||
: m_func_name(func_name),
|
||||
@@ -139,8 +142,9 @@ namespace asst {
|
||||
{
|
||||
auto duration = std::chrono::system_clock::now() - m_start_time;
|
||||
Logger::get_instance().trace(m_func_name, " | leave,",
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(), "ms");
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(), "ms");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_func_name;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_start_time;
|
||||
@@ -148,6 +152,6 @@ namespace asst {
|
||||
|
||||
static auto& log = Logger::get_instance();
|
||||
|
||||
#define LogTraceFunction LoggerAux _func_aux(__FUNCTION__)
|
||||
#define LogTraceScope LoggerAux _func_aux
|
||||
}
|
||||
#define LogTraceFunction LoggerAux _func_aux(__FUNCTION__)
|
||||
#define LogTraceScope LoggerAux _func_aux
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
|
||||
asst::MatchImageAnalyzer::MatchImageAnalyzer(const cv::Mat& image, const Rect& roi, std::string templ_name, double templ_thres, double hist_thres)
|
||||
: AbstractImageAnalyzer(image, roi),
|
||||
@@ -56,6 +56,12 @@ bool asst::MatchImageAnalyzer::match_templ(const cv::Mat& templ)
|
||||
cv::Mat matched;
|
||||
|
||||
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(m_roi));
|
||||
if (templ.rows > image_roi.cols || templ.cols > image_roi.cols) {
|
||||
log.error("templ size is too large",
|
||||
"image_roi size:", image_roi.cols, image_roi.cols,
|
||||
"templ size:", templ.cols, templ.rows);
|
||||
return false;
|
||||
}
|
||||
if (m_mask_range.first == m_mask_range.second) {
|
||||
cv::matchTemplate(image_roi, templ, matched, cv::TM_CCOEFF_NORMED);
|
||||
}
|
||||
@@ -69,7 +75,7 @@ bool asst::MatchImageAnalyzer::match_templ(const cv::Mat& templ)
|
||||
cv::Point min_loc, max_loc;
|
||||
cv::minMaxLoc(matched, &min_val, &max_val, &min_loc, &max_loc);
|
||||
|
||||
if (max_val > m_templ_thres * 0.7) { // 得分太低的肯定不对,没必要打印
|
||||
if (max_val > m_templ_thres * 0.7) { // 得分太低的肯定不对,没必要打印
|
||||
log.trace("match_templ |", m_templ_name, "score:", max_val, "point:", max_loc);
|
||||
}
|
||||
|
||||
@@ -88,7 +94,7 @@ bool asst::MatchImageAnalyzer::comp_hist(const cv::Mat& hist, const cv::Rect roi
|
||||
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(m_roi))(roi);
|
||||
double score = 1.0 - cv::compareHist(to_hist(image_roi), hist, cv::HISTCMP_BHATTACHARYYA);
|
||||
|
||||
if (score > 0.7) { // 得分太低的肯定不对,没必要打印
|
||||
if (score > 0.7) { // 得分太低的肯定不对,没必要打印
|
||||
log.trace("comp_hist |", m_templ_name, "score:", score);
|
||||
}
|
||||
|
||||
@@ -100,4 +106,4 @@ bool asst::MatchImageAnalyzer::comp_hist(const cv::Mat& hist, const cv::Rect roi
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class MatchImageAnalyzer : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -10,23 +11,29 @@ namespace asst {
|
||||
virtual ~MatchImageAnalyzer() = default;
|
||||
|
||||
virtual bool analyze() override;
|
||||
void set_use_cache(bool is_use) noexcept {
|
||||
void set_use_cache(bool is_use) noexcept
|
||||
{
|
||||
m_use_cache = is_use;
|
||||
}
|
||||
void set_mask_range(int lower, int upper) {
|
||||
void set_mask_range(int lower, int upper)
|
||||
{
|
||||
m_mask_range = std::make_pair(lower, upper);
|
||||
}
|
||||
void set_mask_range(std::pair<int, int> mask_range) {
|
||||
void set_mask_range(std::pair<int, int> mask_range)
|
||||
{
|
||||
m_mask_range = std::move(mask_range);
|
||||
}
|
||||
void set_templ_name(std::string templ_name) noexcept {
|
||||
void set_templ_name(std::string templ_name) noexcept
|
||||
{
|
||||
m_templ_name = std::move(templ_name);
|
||||
}
|
||||
void set_threshold(double templ_thres, double hist_thres = 0.0) noexcept {
|
||||
void set_threshold(double templ_thres, double hist_thres = 0.0) noexcept
|
||||
{
|
||||
m_templ_thres = templ_thres;
|
||||
m_hist_thres = hist_thres;
|
||||
}
|
||||
void set_task_info(MatchTaskInfo task_info) noexcept {
|
||||
void set_task_info(MatchTaskInfo task_info) noexcept
|
||||
{
|
||||
m_use_cache = task_info.cache;
|
||||
m_mask_range = std::move(task_info.mask_range);
|
||||
m_templ_name = std::move(task_info.templ_name);
|
||||
@@ -38,7 +45,8 @@ namespace asst {
|
||||
//const std::vector<MatchRect>& get_result() const noexcept {
|
||||
// return m_result;
|
||||
//}
|
||||
const MatchRect& get_result() const noexcept {
|
||||
const MatchRect& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
@@ -29,15 +29,14 @@ void asst::MultiMatchImageAnalyzer::sort_result()
|
||||
{
|
||||
// 按位置排个序
|
||||
std::sort(m_result.begin(), m_result.end(),
|
||||
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
|
||||
if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的,按x排序
|
||||
return lhs.rect.x < rhs.rect.x;
|
||||
}
|
||||
else {
|
||||
return lhs.rect.y < rhs.rect.y;
|
||||
}
|
||||
}
|
||||
);
|
||||
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
|
||||
if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的,按x排序
|
||||
return lhs.rect.x < rhs.rect.x;
|
||||
}
|
||||
else {
|
||||
return lhs.rect.y < rhs.rect.y;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat& templ)
|
||||
@@ -65,13 +64,11 @@ bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat& templ)
|
||||
// 如果有两个点离得太近,只取里面得分高的那个
|
||||
// 一般相邻的都是刚刚push进去的,这里倒序快一点
|
||||
for (auto iter = m_result.rbegin(); iter != m_result.rend(); ++iter) {
|
||||
if (std::abs(j + m_roi.x - iter->rect.x) < mini_distance
|
||||
&& std::abs(i + m_roi.y - iter->rect.y) < mini_distance)
|
||||
{
|
||||
if (std::abs(j + m_roi.x - iter->rect.x) < mini_distance && std::abs(i + m_roi.y - iter->rect.y) < mini_distance) {
|
||||
if (iter->score < value) {
|
||||
iter->rect = rect;
|
||||
iter->score = value;
|
||||
} // else 这个点就放弃了
|
||||
} // else 这个点就放弃了
|
||||
need_push = false;
|
||||
break;
|
||||
}
|
||||
@@ -94,4 +91,4 @@ bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat& templ)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class MultiMatchImageAnalyzer : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -12,25 +13,31 @@ namespace asst {
|
||||
virtual bool analyze() override;
|
||||
|
||||
void sort_result(); // 按位置排序,左上角的排在前面
|
||||
void set_mask_range(int lower, int upper) {
|
||||
void set_mask_range(int lower, int upper)
|
||||
{
|
||||
m_mask_range = std::make_pair(lower, upper);
|
||||
}
|
||||
void set_mask_range(std::pair<int, int> mask_range) {
|
||||
void set_mask_range(std::pair<int, int> mask_range)
|
||||
{
|
||||
m_mask_range = std::move(mask_range);
|
||||
}
|
||||
void set_templ_name(std::string templ_name) noexcept {
|
||||
void set_templ_name(std::string templ_name) noexcept
|
||||
{
|
||||
m_templ_name = std::move(templ_name);
|
||||
}
|
||||
void set_threshold(double templ_thres) noexcept {
|
||||
void set_threshold(double templ_thres) noexcept
|
||||
{
|
||||
m_templ_thres = templ_thres;
|
||||
}
|
||||
void set_task_info(MatchTaskInfo task_info) noexcept {
|
||||
void set_task_info(MatchTaskInfo task_info) noexcept
|
||||
{
|
||||
m_mask_range = std::move(task_info.mask_range);
|
||||
m_templ_name = std::move(task_info.templ_name);
|
||||
m_templ_thres = task_info.templ_threshold;
|
||||
set_roi(task_info.roi);
|
||||
}
|
||||
const std::vector<MatchRect>& get_result() const noexcept {
|
||||
const std::vector<MatchRect>& get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "OcrImageAnalyzer.h"
|
||||
#include "OcrImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
|
||||
bool asst::OcrImageAnalyzer::analyze()
|
||||
{
|
||||
@@ -56,4 +56,4 @@ bool asst::OcrImageAnalyzer::analyze()
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class OcrImageAnalyzer : public AbstractImageAnalyzer
|
||||
{
|
||||
public:
|
||||
@@ -16,24 +17,29 @@ namespace asst {
|
||||
|
||||
virtual bool analyze() override;
|
||||
|
||||
void set_required(std::vector<std::string> required, bool full_match = false) noexcept {
|
||||
void set_required(std::vector<std::string> required, bool full_match = false) noexcept
|
||||
{
|
||||
m_required = std::move(required);
|
||||
m_full_match = full_match;
|
||||
}
|
||||
void set_replace(std::unordered_map<std::string, std::string> replace) noexcept {
|
||||
void set_replace(std::unordered_map<std::string, std::string> replace) noexcept
|
||||
{
|
||||
m_replace = std::move(replace);
|
||||
}
|
||||
void set_task_info(OcrTaskInfo task_info) noexcept {
|
||||
void set_task_info(OcrTaskInfo task_info) noexcept
|
||||
{
|
||||
m_required = std::move(task_info.text);
|
||||
m_full_match = task_info.need_full_match;
|
||||
m_replace = std::move(task_info.replace_map);
|
||||
set_roi(task_info.roi);
|
||||
}
|
||||
|
||||
void set_pred(const TextRectProc& pred) {
|
||||
void set_pred(const TextRectProc& pred)
|
||||
{
|
||||
m_pred = pred;
|
||||
}
|
||||
const std::vector<TextRect>& get_result() const noexcept {
|
||||
const std::vector<TextRect>& get_result() const noexcept
|
||||
{
|
||||
return m_ocr_result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
#include "OcrPack.h"
|
||||
#include "OcrPack.h"
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <OcrLiteOnnx/OcrLiteCaller.h>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
|
||||
asst::OcrPack::OcrPack()
|
||||
: m_ocr_ptr(std::make_unique<OcrLiteCaller>())
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
asst::OcrPack::~OcrPack() = default;
|
||||
|
||||
@@ -45,9 +44,9 @@ std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat & image, cons
|
||||
constexpr bool mostAngle = false;
|
||||
|
||||
OcrResult ocr_results = m_ocr_ptr->detect(image,
|
||||
padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh,
|
||||
unClipRatio, doAngle, mostAngle);
|
||||
padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh,
|
||||
unClipRatio, doAngle, mostAngle);
|
||||
|
||||
std::vector<TextRect> result;
|
||||
std::string log_str_raw;
|
||||
@@ -84,4 +83,4 @@ std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat & image, cons
|
||||
return pred(tr);
|
||||
};
|
||||
return recognize(image(utils::make_rect<cv::Rect>(roi)), rect_cor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractResource.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
class OcrLiteCaller;
|
||||
namespace cv {
|
||||
namespace cv
|
||||
{
|
||||
class Mat;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class OcrPack final : public AbstractResource
|
||||
{
|
||||
public:
|
||||
@@ -25,7 +27,6 @@ namespace asst {
|
||||
std::vector<TextRect> recognize(const cv::Mat& image, const Rect& roi, const TextRectProc& pred = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<OcrLiteCaller> m_ocr_ptr;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include "PenguinPack.h"
|
||||
#include "PenguinPack.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <json.h>
|
||||
namespace penguin {
|
||||
#include <opencv2/opencv.hpp>
|
||||
namespace penguin
|
||||
{
|
||||
#include <penguin-stats-recognize/penguin_wasm.h>
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ bool asst::PenguinPack::load_json(const std::string& stage_path, const std::stri
|
||||
json::object cvt_stage_json;
|
||||
try {
|
||||
for (const json::value& stage_info : stage_json.as_array()) {
|
||||
if (!stage_info.exist("dropInfos")) { // 这种一般是以前的活动关,现在已经关闭了的
|
||||
if (!stage_info.exist("dropInfos")) { // 这种一般是以前的活动关,现在已经关闭了的
|
||||
continue;
|
||||
}
|
||||
std::string key = stage_info.at("code").as_string();
|
||||
@@ -91,4 +92,4 @@ bool asst::PenguinPack::load_templ(const std::string& item_id, const std::string
|
||||
penguin::load_templ(item_id.c_str(), buf.data(), buf.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "AbstractResource.h"
|
||||
|
||||
namespace cv {
|
||||
namespace cv
|
||||
{
|
||||
class Mat;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class PenguinPack final : public AbstractResource
|
||||
{
|
||||
public:
|
||||
@@ -17,6 +19,7 @@ namespace asst {
|
||||
void set_language(const std::string& server);
|
||||
|
||||
std::string recognize(const cv::Mat& image);
|
||||
|
||||
private:
|
||||
bool load_json(const std::string& stage_path, const std::string& hash_path);
|
||||
bool load_templ(const std::string& item_id, const std::string& path);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "PenguinUploader.h"
|
||||
#include "PenguinUploader.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <json.h>
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
#include "Version.h"
|
||||
#include "Logger.hpp"
|
||||
#include "AsstUtils.hpp"
|
||||
|
||||
bool asst::PenguinUploader::upload(const std::string& rec_res)
|
||||
{
|
||||
@@ -89,4 +89,4 @@ bool asst::PenguinUploader::request_penguin(const std::string& body)
|
||||
::CloseHandle(pipe_read);
|
||||
::CloseHandle(pipe_child_write);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace json {
|
||||
namespace json
|
||||
{
|
||||
class value;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class PenguinUploader
|
||||
{
|
||||
public:
|
||||
static bool upload(const std::string& rec_res);
|
||||
|
||||
private:
|
||||
static std::string cvt_json(const std::string& rec_res);
|
||||
static bool request_penguin(const std::string& body);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ProcessTask.h"
|
||||
#include "ProcessTask.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
@@ -16,13 +16,14 @@ using namespace asst;
|
||||
bool ProcessTask::run()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "ProcessTask" },
|
||||
{ "task_chain", m_task_chain},
|
||||
{ "tasks", json::array(m_cur_tasks_name)}
|
||||
{ "task_type", "ProcessTask" },
|
||||
{ "task_chain", m_task_chain },
|
||||
{ "tasks", json::array(m_cur_tasks_name) }
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
ProcessTaskImageAnalyzer analyzer(ctrler.get_image(), m_cur_tasks_name);
|
||||
const auto& image = ctrler.get_image();
|
||||
ProcessTaskImageAnalyzer analyzer(image, m_cur_tasks_name);
|
||||
if (!analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
@@ -44,8 +45,8 @@ bool ProcessTask::run()
|
||||
{ "type", static_cast<int>(task_info_ptr->action) },
|
||||
{ "exec_times", task_info_ptr->exec_times },
|
||||
{ "max_times", task_info_ptr->max_times },
|
||||
{ "task_type", "ProcessTask"},
|
||||
{ "algorithm", static_cast<int>(task_info_ptr->algorithm)}
|
||||
{ "task_type", "ProcessTask" },
|
||||
{ "algorithm", static_cast<int>(task_info_ptr->algorithm) }
|
||||
};
|
||||
m_callback(AsstMsg::TaskMatched, callback_json, m_callback_arg);
|
||||
|
||||
@@ -73,9 +74,8 @@ bool ProcessTask::run()
|
||||
case ProcessTaskAction::ClickSelf:
|
||||
exec_click_task(rect);
|
||||
break;
|
||||
case ProcessTaskAction::ClickRand:
|
||||
{
|
||||
static const Rect full_rect(0, 0, GeneralConfiger::WindowWidthDefault, GeneralConfiger::WindowHeightDefault);
|
||||
case ProcessTaskAction::ClickRand: {
|
||||
static const Rect full_rect(0, 0, image.cols, image.rows);
|
||||
exec_click_task(full_rect);
|
||||
} break;
|
||||
case ProcessTaskAction::SwipeToTheLeft:
|
||||
@@ -85,11 +85,10 @@ bool ProcessTask::run()
|
||||
case ProcessTaskAction::DoNothing:
|
||||
break;
|
||||
case ProcessTaskAction::Stop:
|
||||
m_callback(AsstMsg::ProcessTaskStopAction, json::object{ {"task_chain", m_task_chain} }, m_callback_arg);
|
||||
m_callback(AsstMsg::ProcessTaskStopAction, json::object{ { "task_chain", m_task_chain } }, m_callback_arg);
|
||||
need_stop = true;
|
||||
break;
|
||||
case ProcessTaskAction::StageDrops:
|
||||
{
|
||||
case ProcessTaskAction::StageDrops: {
|
||||
cv::Mat image = ctrler.get_image(true);
|
||||
std::string res = resource.penguin().recognize(image);
|
||||
m_callback(AsstMsg::StageDrops, json::parse(res).value(), m_callback_arg);
|
||||
@@ -102,8 +101,7 @@ bool ProcessTask::run()
|
||||
if (opt.penguin_report) {
|
||||
PenguinUploader::upload(res);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -170,22 +168,26 @@ void asst::ProcessTask::exec_swipe_task(ProcessTaskAction action)
|
||||
if (!delay_random()) {
|
||||
return;
|
||||
}
|
||||
auto& width = resource.cfg().WindowWidthDefault;
|
||||
auto& height = resource.cfg().WindowWidthDefault;
|
||||
const auto&& [width, height] = ctrler.get_scale_size();
|
||||
|
||||
const static Rect right_rect(width * 0.8, height * 0.4, width * 0.1, height * 0.2);
|
||||
const static Rect right_rect(width * 0.8,
|
||||
height * 0.4,
|
||||
width * 0.1,
|
||||
height * 0.2);
|
||||
|
||||
const static Rect left_rect(width * 0.1, height * 0.4, width * 0.1, height * 0.2);
|
||||
const static Rect left_rect(width * 0.1,
|
||||
height * 0.4,
|
||||
width * 0.1,
|
||||
height * 0.2);
|
||||
|
||||
switch (action)
|
||||
{
|
||||
switch (action) {
|
||||
case asst::ProcessTaskAction::SwipeToTheLeft:
|
||||
ctrler.swipe(left_rect, right_rect);
|
||||
break;
|
||||
case asst::ProcessTaskAction::SwipeToTheRight:
|
||||
ctrler.swipe(right_rect, left_rect);
|
||||
break;
|
||||
default: // 走不到这里,TODO 报个错
|
||||
default: // 走不到这里,TODO 报个错
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "AbstractTask.h"
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
// 流程任务,按照配置文件里的设置的流程运行
|
||||
class ProcessTask : public AbstractTask
|
||||
{
|
||||
@@ -13,7 +14,8 @@ namespace asst {
|
||||
|
||||
virtual bool run() override;
|
||||
|
||||
virtual void set_tasks(const std::vector<std::string>& cur_tasks_name) {
|
||||
virtual void set_tasks(const std::vector<std::string>& cur_tasks_name)
|
||||
{
|
||||
m_cur_tasks_name = cur_tasks_name;
|
||||
}
|
||||
|
||||
@@ -24,4 +26,4 @@ namespace asst {
|
||||
|
||||
std::vector<std::string> m_cur_tasks_name;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "ProcessTaskImageAnalyzer.h"
|
||||
#include "ProcessTaskImageAnalyzer.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "OcrImageAnalyzer.h"
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "OcrImageAnalyzer.h"
|
||||
#include "Resource.h"
|
||||
|
||||
asst::ProcessTaskImageAnalyzer::ProcessTaskImageAnalyzer(const cv::Mat& image, std::vector<std::string> tasks_name)
|
||||
: AbstractImageAnalyzer(image),
|
||||
@@ -22,6 +22,7 @@ bool asst::ProcessTaskImageAnalyzer::match_analyze(std::shared_ptr<TaskInfo> tas
|
||||
}
|
||||
const auto match_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(task_ptr);
|
||||
m_match_analyzer->set_task_info(*match_task_ptr);
|
||||
m_match_analyzer->correct_roi();
|
||||
|
||||
if (m_match_analyzer->analyze()) {
|
||||
m_result = match_task_ptr;
|
||||
@@ -69,6 +70,7 @@ bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_
|
||||
// 识别区域文字,并加入缓存
|
||||
auto analyze_roi = [&](const Rect& roi, bool is_appeared = false) -> bool {
|
||||
m_ocr_analyzer->set_roi(roi);
|
||||
m_ocr_analyzer->correct_roi();
|
||||
bool ret = m_ocr_analyzer->analyze();
|
||||
|
||||
const auto& ocr_result = m_ocr_analyzer->get_result();
|
||||
@@ -88,8 +90,8 @@ bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_
|
||||
|
||||
// 在曾经识别到过的历史区域里识别
|
||||
for (const Rect& region : ocr_task_ptr->region_of_appeared) {
|
||||
static auto& max_width = resource.cfg().WindowWidthDefault;
|
||||
static auto& max_height = resource.cfg().WindowHeightDefault;
|
||||
static auto& max_width = WindowWidthDefault;
|
||||
static auto& max_height = WindowHeightDefault;
|
||||
bool ret = analyze_roi(region.center_zoom(2.0, max_width, max_height), true);
|
||||
if (ret) {
|
||||
log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found in appeared");
|
||||
@@ -116,8 +118,7 @@ bool asst::ProcessTaskImageAnalyzer::analyze()
|
||||
for (const std::string& task_name : m_tasks_name) {
|
||||
auto task_ptr = resource.task().task_ptr(task_name);
|
||||
|
||||
switch (task_ptr->algorithm)
|
||||
{
|
||||
switch (task_ptr->algorithm) {
|
||||
case AlgorithmType::JustReturn:
|
||||
m_result = task_ptr;
|
||||
return true;
|
||||
@@ -142,4 +143,4 @@ void asst::ProcessTaskImageAnalyzer::set_image(const cv::Mat & image)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "AbstractImageAnalyzer.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
namespace asst
|
||||
{
|
||||
class OcrImageAnalyzer;
|
||||
class MatchImageAnalyzer;
|
||||
|
||||
@@ -22,21 +23,27 @@ namespace asst {
|
||||
virtual bool analyze() override;
|
||||
virtual void set_image(const cv::Mat& image) override;
|
||||
|
||||
void set_tasks(std::vector<std::string> tasks_name) {
|
||||
void set_tasks(std::vector<std::string> tasks_name)
|
||||
{
|
||||
m_tasks_name = std::move(tasks_name);
|
||||
}
|
||||
std::shared_ptr<TaskInfo> get_result() const noexcept {
|
||||
std::shared_ptr<TaskInfo> get_result() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
const Rect& get_rect() const noexcept {
|
||||
const Rect& get_rect() const noexcept
|
||||
{
|
||||
return m_result_rect;
|
||||
}
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override {
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
AbstractImageAnalyzer::set_roi(roi);
|
||||
}
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi) {
|
||||
virtual void set_image(const cv::Mat& image, const Rect& roi)
|
||||
{
|
||||
AbstractImageAnalyzer::set_image(image, roi);
|
||||
}
|
||||
bool match_analyze(std::shared_ptr<TaskInfo> task_ptr);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "RecruitConfiger.h"
|
||||
#include "RecruitConfiger.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -29,12 +29,9 @@ bool asst::RecruitConfiger::parse(const json::value& json)
|
||||
}
|
||||
|
||||
// 按干员等级排个序
|
||||
std::sort(m_all_opers.begin(), m_all_opers.end(), [](
|
||||
const auto& lhs,
|
||||
const auto& rhs)
|
||||
-> bool {
|
||||
return lhs.level > rhs.level;
|
||||
});
|
||||
std::sort(m_all_opers.begin(), m_all_opers.end(), [](const auto& lhs, const auto& rhs) -> bool {
|
||||
return lhs.level > rhs.level;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||