Merge branch 'MaaAssistantArknights:dev' into dev

This commit is contained in:
cglcv
2022-06-08 09:12:41 +08:00
committed by GitHub
87 changed files with 821 additions and 389 deletions

View File

@@ -1,20 +1,12 @@
- 自动战斗
- 大幅优化识别及操作延迟
- 新增支持按坐标撤退/释放技能(不填坐标就还是按干员名,仍推荐尽量用干员名)
- 新增支持关卡 id 输入(第十章磨难关卡请使用关卡 id
- 修复几个干员名识别问题
- 修复作业制作器的一些小问题
- 修复击杀数识别错误
- 修复偶尔部署不上去的问题
- 肉鸽
- 新增支持月底小队,但仍然是无限刷的,请手动结束任务
- 同样大幅优化识别及操作延迟
- 自动公招
- 新增支持跳过 `支援机械` 并弹出通知,可在设置中关闭
- 刷理智
- 新增支持游戏崩溃重连,需要在设置中开启 `唤醒设置`
- 修复一个刷理智时的识别错误
- 修复一个掉落识别错误的
- 修复勾选材料后刷几次就自动停了的问题
- 其他
- 大幅优化截图延迟及性能消耗
- 新增支持启动明日方舟(不包括模拟器),请进入设置中开启~
- 新增支持选择自动关机还是休眠/睡眠,在勾选 `自动关机` 后按 `H` 键弹出选项
- Win10 将默认使用系统通知
- 修复关卡导航失败的问题
- 新增 adb 出错后的重连机制;及重连失败时的报错

View File

@@ -22,13 +22,15 @@
"action": "ClickSelf", // 可选项,表示识别到后的动作
// 不填写时默认为 DoNothing
// - ClickSelf: 点击识别到的位置(识别到的目标范围内随机点)
// - ClickRand: 点击整个画面中随机位置
// - DoNothing: 什么都不做
// - Stop: 停止当前任务
// - ClickRect: 点击指定的区域,对应 specificRect 字段,不建议使用该选项
// - SwipeToTheLeft: 滑动到最左侧
// - SwipeToTheRight: 滑动到最右侧
// - ClickSelf: 点击识别到的位置(识别到的目标范围内随机点)
// - ClickRand: 点击整个画面中随机位置
// - DoNothing: 什么都不做
// - Stop: 停止当前任务
// - ClickRect: 点击指定的区域,对应 specificRect 字段,不建议使用该选项
// - SwipeToTheLeft: 滑动到最左侧
// - SwipeToTheRight: 滑动到最右侧
// - SlowlySwipeToTheLeft: 缓慢左滑
// - SlowlySwipeToTheRight: 缓慢右滑
"sub": [ "SubTaskName1", "SubTaskName2" ],
// 可选项,子任务。会在执行完当前任务后,依次执行每一个子任务
@@ -40,7 +42,7 @@
// 为 true 时,子任务是否出错没有影响
"next": [ "OtherTaskName1", "OtherTaskName2"],
// 可选项,表示执行完当前任务后,下一个要执行的任务
// 可选项,表示执行完当前任务和 sub 任务后,下一个要执行的任务
// 会从前往后依次去识别,去执行第一个匹配上的
// 不填写默认执行完当前任务直接停止
@@ -64,21 +66,24 @@
// 但仅适用于待识别目标位置完全不会变的任务,若待识别目标位置会变请设为 false
"rectMove": [0, 0, 0, 0], // 可选项,识别后的目标移动,不建议使用该选项。以 1280 * 720 为基准自动缩放
// 例如识别到了 A 图标,但实际要点击的是 A 图标旁边的某个位置,则进行移动后再点击。可以的话尽量直接识别要点击的位置,不建议使用该选项
// 例如识别到了 A 图标,但实际要点击的是 A 图标旁边的某个位置,则进行移动后再点击。
// 可以的话尽量直接识别要点击的位置,不建议使用该选项
"reduceOtherTimes": [ "OtherTaskName1", "OtherTaskName2" ],
// 可选项,执行后减少其他任务的执行计数。
// 例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
"specificRect": [ 100, 100, 50, 50],
// 仅当 action 为 ClickRect 时有效且必选,表示指定的点击位置(范围内随机一点)。以 1280 * 720 为基准自动缩放
// 仅当 action 为 ClickRect 时有效且必选,表示指定的点击位置(范围内随机一点)。
// 以 1280 * 720 为基准自动缩放
/* algorithm MatchTemplate */
"templThreshold": 0.8, // 可选项,图片模板匹配得分的阈值,超过阈值才认为识别到了。
// 默认 0.8, 可根据日志查看实际得分是多少
"maskRange": [ 1, 255 ], // 可选项,灰度掩码范围。例如将图片不需要识别的部分涂成黑色(灰度值为 0,然后设置 [ 1, 255 ], 匹配的时候即刻忽略涂黑的部分
"maskRange": [ 1, 255 ], // 可选项,灰度掩码范围。例如将图片不需要识别的部分涂成黑色(灰度值为 0
// 然后设置"maskRange"的范围为 [ 1, 255 ], 匹配的时候即刻忽略涂黑的部分
/* algorithm OcrDetect */

View File

@@ -81,6 +81,10 @@ Todo
分辨率不被支持
- `ResolutionError`
分辨率获取错误
- `Disconnect`
连接断开adb / 模拟器 炸了,且已经重试过一次)
- `CommandExecFailed`
命令执行失败adb / 模拟器 炸了,且已经重试过一次)
#### AllTasksCompleted

View File

@@ -45,6 +45,12 @@ TaskId ASSTAPI AsstAppendTask(AsstHandle handle, const char* type, const char* p
"medicine": int, // 最大使用理智药数量,可选,默认 0
"stone": int, // 最大吃石头数量,可选,默认 0
"times": int, // 指定次数,可选,默认无穷大
"drops": { // 指定掉落数量,可选,默认不指定
"30011": int, // key - item_id, value 数量. key 可参考 resource/item_index.json 文件
"30062": int // 是或的关系
},
/* 以上全部是或的关系,即任一达到即停止任务 */
"report_to_penguin": bool, // 是否汇报企鹅数据,可选,默认 false
"penguin_id": string, // 企鹅数据汇报 id, 可选,默认为空。仅在 report_to_penguin 为 true 时有效
"server": string // 服务器,可选,默认 "CN", 会影响掉落识别及上传

View File

@@ -33,6 +33,7 @@
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init=",
"displayFormat": " init=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p",
@@ -49,6 +50,7 @@
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p",
@@ -65,6 +67,7 @@
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p",
@@ -80,7 +83,8 @@
"swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"screencapRawByNC": "[Adb] -s [AdbSerial] shell \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] shell \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] shell screencap -p",
@@ -97,6 +101,7 @@
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p",
@@ -113,6 +118,7 @@
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init=",
"displayFormat": " init=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] shell \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] shell screencap -p",
@@ -130,6 +136,7 @@
"display": "[Adb] -s [AdbSerial] shell \"wm size -d [DisplayId]\"",
"displayFormat": "Physical size:%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] shell \"screencap -d [DisplayId] | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] shell screencap -d [DisplayId] -p",
@@ -146,6 +153,7 @@
"display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init=",
"displayFormat": " init=%dx%d",
"screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"",
"ncAddress": "[Adb] -s [AdbSerial] shell \" cat /proc/net/arp | grep : \"",
"ncPort": 6723,
"screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"",
"screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p",

View File

@@ -500,31 +500,19 @@
},
"StageCE": {
"action": "ClickSelf",
"cache": false,
"next": [
"StageCE6"
]
"cache": false
},
"StageLS": {
"action": "ClickSelf",
"cache": false,
"next": [
"StageLS6"
]
"cache": false
},
"StageCA": {
"action": "ClickSelf",
"cache": false,
"next": [
"StageCA5"
]
"cache": false
},
"StageAP": {
"action": "ClickSelf",
"cache": false,
"next": [
"StageAP5"
]
"cache": false
},
"AbleToAgent": {
"algorithm": "JustReturn",
@@ -827,6 +815,7 @@
"UsePrts",
"UsePrts-StageSN",
"StartButton1",
"PRTS3",
"PRTS",
"PRTS2",
"EndOfAction"
@@ -847,6 +836,10 @@
1,
255
],
"reduceOtherTimes": [
"StartButton1",
"StartButton2"
],
"next": [
"GameStart",
"StartToWakeUp",
@@ -1161,6 +1154,7 @@
340
],
"next": [
"PRTS3",
"PRTS",
"PRTS2",
"EndOfAction",
@@ -1185,6 +1179,7 @@
"rearDelay": 5000,
"next": [
"PRTS",
"PRTS3",
"PRTS2",
"EndOfAction",
"PrtsErrorConfirm",
@@ -1206,6 +1201,7 @@
"action": "DoNothing",
"rearDelay": 5000,
"next": [
"PRTS3",
"PRTS2",
"PRTS",
"EndOfAction",
@@ -1214,26 +1210,52 @@
"WaitAfterPRTS"
]
},
"PRTS3": {
"action": "DoNothing",
"template": "BattleOfficiallyBegin.png",
"roi": [
1165,
20,
65,
65
],
"maskRange": [
100,
255
],
"rearDelay": 5000,
"next": [
"PRTS3",
"PRTS",
"PRTS2",
"EndOfAction",
"PrtsErrorConfirm",
"OfflineConfirm",
"WaitAfterPRTS"
]
},
"WaitAfterPRTS": {
"algorithm": "JustReturn",
"action": "DoNothing",
"next": [
"PRTS3",
"PRTS",
"PRTS2",
"EndOfAction",
"ClickCornerAfterPRTS"
"ClickCornerAfterPRTS_1"
]
},
"ClickCornerAfterPRTS": {
"ClickCornerAfterPRTS_1": {
"ClickCorner_Doc": "点击屏幕右边,不会点到掉落物品,且不会关掉关卡选择的一块区域",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [
1000,
10,
120,
270,
120
10
],
"rearDelay": 3000,
"next": [
"EndOfAction",
"StartButton1",
@@ -1241,7 +1263,59 @@
"UseMedicine",
"UseStone",
"NoStone",
"ClickCornerAfterPRTS"
"ClickCornerAfterPRTS_2"
]
},
"ClickCornerAfterPRTS_2": {
"ClickCorner_Doc": "点击屏幕右边,不会点到掉落物品,且不会关掉关卡选择的一块区域",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [
1000,
120,
270,
10
],
"rearDelay": 3000,
"next": [
"EndOfAction",
"StartButton1",
"StartButton2",
"UseMedicine",
"UseStone",
"NoStone",
"MaybeCrashAndRestartGame"
]
},
"MaybeCrashAndRestartGame": {
"ClickCorner_Doc": "一般能点这么多下还不行的,多半是游戏崩了,所以重启游戏试试",
"Doc": "本任务注册了插件 GameCrashRestartTaskPlugin",
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [
1000,
120,
270,
10
],
"rearDelay": 3000,
"next": [
"EndOfAction",
"StartButton1",
"StartButton2",
"UseMedicine",
"UseStone",
"NoStone",
"GameStart",
"StartToWakeUp",
"StartUpConnectingFlag",
"StartLoginBServer",
"Terminal",
"CloseAnno",
"TodaysSupplies",
"ReturnToTerminal",
"OfflineConfirm",
"StageSNFlag"
]
},
"EndOfAction": {
@@ -1267,9 +1341,9 @@
"action": "ClickRect",
"specificRect": [
1000,
10,
120,
270,
120
10
],
"next": [
"StartButton1",
@@ -1438,6 +1512,7 @@
160
],
"next": [
"PRTS3",
"PRTS",
"PRTS2",
"PrtsErrorConfirm",
@@ -6562,7 +6637,7 @@
1280,
2
],
"templThreshold": 25,
"templThreshold": 17,
"templThreshold_Doc": "这里用来作为基线间距阈值",
"specialThreshold": 100,
"specialThreshold_Doc": "基建长度阈值不小于120",

View File

@@ -4,7 +4,7 @@
#include "Controller.h"
#include "Logger.hpp"
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image)
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image)
: m_image(image), m_roi(empty_rect_to_full(Rect(), image))
#ifdef ASST_DEBUG
,
@@ -12,7 +12,7 @@ asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image)
#endif
{}
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image, const Rect& roi)
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image, const Rect& roi)
: m_image(image),
m_roi(empty_rect_to_full(roi, image))
#ifdef ASST_DEBUG
@@ -20,7 +20,6 @@ asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image, const Re
m_image_draw(image.clone())
#endif
{
;
}
void asst::AbstractImageAnalyzer::set_image(const cv::Mat image)
@@ -42,13 +41,13 @@ void asst::AbstractImageAnalyzer::set_roi(const Rect& roi) noexcept
m_roi = empty_rect_to_full(roi, m_image);
}
asst::Rect asst::AbstractImageAnalyzer::empty_rect_to_full(const Rect& rect, const cv::Mat image) noexcept
asst::Rect asst::AbstractImageAnalyzer::empty_rect_to_full(const Rect& rect, const cv::Mat& image) noexcept
{
if (image.empty()) {
return rect;
}
if (rect.empty()) {
return Rect(0, 0, image.cols, image.rows);
return {0, 0, image.cols, image.rows};
}
Rect res = rect;

View File

@@ -15,8 +15,8 @@ namespace asst
{
public:
AbstractImageAnalyzer() = default;
AbstractImageAnalyzer(const cv::Mat image);
AbstractImageAnalyzer(const cv::Mat image, const Rect& roi);
AbstractImageAnalyzer(const cv::Mat& image);
AbstractImageAnalyzer(const cv::Mat& image, const Rect& roi);
AbstractImageAnalyzer(const AbstractImageAnalyzer&) = delete;
AbstractImageAnalyzer(AbstractImageAnalyzer&&) = delete;
virtual ~AbstractImageAnalyzer() = default;
@@ -31,7 +31,7 @@ namespace asst
AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete;
protected:
static Rect empty_rect_to_full(const Rect& rect, const cv::Mat image) noexcept;
static Rect empty_rect_to_full(const Rect& rect, const cv::Mat& image) noexcept;
bool save_img();

View File

@@ -6,6 +6,7 @@
#include <regex>
#include <opencv2/opencv.hpp>
#include <utility>
#include "AsstUtils.hpp"
#include "Controller.h"
@@ -16,11 +17,10 @@
using namespace asst;
AbstractTask::AbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain)
: m_callback(callback),
: m_callback(std::move(callback)),
m_callback_arg(callback_arg),
m_task_chain(std::move(task_chain))
{
;
}
bool asst::AbstractTask::run()
@@ -64,13 +64,13 @@ AbstractTask& asst::AbstractTask::set_retry_times(int times) noexcept
AbstractTask& asst::AbstractTask::set_ctrler(std::shared_ptr<Controller> ctrler) noexcept
{
m_ctrler = ctrler;
m_ctrler = std::move(ctrler);
return *this;
}
AbstractTask& asst::AbstractTask::set_status(std::shared_ptr<RuntimeStatus> status) noexcept
{
m_status = status;
m_status = std::move(status);
return *this;
}
@@ -158,7 +158,7 @@ bool AbstractTask::sleep(unsigned millisecond)
return !need_exit();
}
bool AbstractTask::save_image(const cv::Mat image, const std::string& dir)
bool AbstractTask::save_image(const cv::Mat& image, const std::string& dir)
{
std::filesystem::create_directory(dir);
const std::string time_str = utils::string_replace_all(utils::string_replace_all(utils::get_format_time(), " ", "_"), ":", "-");

View File

@@ -67,7 +67,7 @@ namespace asst
json::value basic_info_with_what(std::string what) const;
bool sleep(unsigned millisecond);
bool need_exit() const;
bool save_image(const cv::Mat image, const std::string& dir);
bool save_image(const cv::Mat& image, const std::string& dir);
bool m_enable = true;
bool m_ignore_error = true;

View File

@@ -47,7 +47,7 @@ bool asst::AipOcr::request_access_token(const std::string& client_id, const std:
return true;
}
bool asst::AipOcr::request_ocr_general(const cv::Mat image, std::vector<TextRect>& out_result, const TextRectProc& pred)
bool asst::AipOcr::request_ocr_general(const cv::Mat& image, std::vector<TextRect>& out_result, const TextRectProc& pred)
{
LogTraceFunction;
@@ -62,7 +62,7 @@ bool asst::AipOcr::request_ocr_general(const cv::Mat image, std::vector<TextRect
return request_ocr_and_parse(cmd_fmt, image, out_result, pred);
}
bool asst::AipOcr::request_ocr_accurate(const cv::Mat image, std::vector<TextRect>& out_result, const TextRectProc& pred)
bool asst::AipOcr::request_ocr_accurate(const cv::Mat& image, std::vector<TextRect>& out_result, const TextRectProc& pred)
{
LogTraceFunction;
@@ -77,7 +77,7 @@ bool asst::AipOcr::request_ocr_accurate(const cv::Mat image, std::vector<TextRec
return request_ocr_and_parse(cmd_fmt, image, out_result, pred);
}
bool asst::AipOcr::request_ocr_and_parse(std::string_view cmd_fmt, const cv::Mat image, std::vector<TextRect>& out_result, const TextRectProc& pred)
bool asst::AipOcr::request_ocr_and_parse(std::string_view cmd_fmt, const cv::Mat& image, std::vector<TextRect>& out_result, const TextRectProc& pred)
{
if (m_access_token.empty()) {
return false;
@@ -90,7 +90,7 @@ bool asst::AipOcr::request_ocr_and_parse(std::string_view cmd_fmt, const cv::Mat
std::string encoded = base64_encode(enc_msg, buf.size());
size_t cmd_len = encoded.size() + cmd_fmt.size() + m_access_token.size();
char* cmd = new char[cmd_len];
auto cmd = new char[cmd_len];
memset(cmd, 0, cmd_len);
#ifdef _MSC_VER
sprintf_s(cmd, cmd_len, cmd_fmt.data(), m_access_token.c_str(), encoded.c_str());

View File

@@ -27,15 +27,15 @@ namespace asst
}
bool request_access_token(const std::string& client_id, const std::string& client_secret);
bool request_ocr_general(const cv::Mat image, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
bool request_ocr_accurate(const cv::Mat image, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
bool request_ocr_general(const cv::Mat& image, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
bool request_ocr_accurate(const cv::Mat& image, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
private:
AipOcr() = default;
AipOcr(const AipOcr&) = default;
AipOcr(AipOcr&&) noexcept = default;
bool request_ocr_and_parse(std::string_view cmd_fmt, const cv::Mat image, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
bool request_ocr_and_parse(std::string_view cmd_fmt, const cv::Mat& image, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
bool parse_response(const json::value& json, std::vector<TextRect>& out_result, const TextRectProc& pred = nullptr);
std::string m_access_token;

View File

@@ -1,6 +1,6 @@
#include "Assistant.h"
#include <time.h>
#include <ctime>
#include <meojson/json.hpp>
#include <opencv2/opencv.hpp>
@@ -33,7 +33,7 @@ Assistant::Assistant(AsstApiCallback callback, void* callback_arg)
LogTraceFunction;
m_status = std::make_shared<RuntimeStatus>();
m_ctrler = std::make_shared<Controller>(task_callback, (void*)this);
m_ctrler = std::make_shared<Controller>(task_callback, static_cast<void*>(this));
m_working_thread = std::thread(&Assistant::working_proc, this);
m_msg_thread = std::thread(&Assistant::msg_proc, this);
@@ -85,31 +85,31 @@ asst::Assistant::TaskId asst::Assistant::append_task(const std::string& type, co
std::shared_ptr<PackageTask> ptr = nullptr;
if (type == FightTask::TaskType) {
ptr = std::make_shared<FightTask>(task_callback, (void*)this);
ptr = std::make_shared<FightTask>(task_callback, static_cast<void*>(this));
}
else if (type == StartUpTask::TaskType) {
ptr = std::make_shared<StartUpTask>(task_callback, (void*)this);
ptr = std::make_shared<StartUpTask>(task_callback, static_cast<void*>(this));
}
else if (type == AwardTask::TaskType) {
ptr = std::make_shared<AwardTask>(task_callback, (void*)this);
ptr = std::make_shared<AwardTask>(task_callback, static_cast<void*>(this));
}
else if (type == VisitTask::TaskType) {
ptr = std::make_shared<VisitTask>(task_callback, (void*)this);
ptr = std::make_shared<VisitTask>(task_callback, static_cast<void*>(this));
}
else if (type == MallTask::TaskType) {
ptr = std::make_shared<MallTask>(task_callback, (void*)this);
ptr = std::make_shared<MallTask>(task_callback, static_cast<void*>(this));
}
else if (type == InfrastTask::TaskType) {
ptr = std::make_shared<InfrastTask>(task_callback, (void*)this);
ptr = std::make_shared<InfrastTask>(task_callback, static_cast<void*>(this));
}
else if (type == RecruitTask::TaskType) {
ptr = std::make_shared<RecruitTask>(task_callback, (void*)this);
ptr = std::make_shared<RecruitTask>(task_callback, static_cast<void*>(this));
}
else if (type == RoguelikeTask::TaskType) {
ptr = std::make_shared<RoguelikeTask>(task_callback, (void*)this);
ptr = std::make_shared<RoguelikeTask>(task_callback, static_cast<void*>(this));
}
else if (type == CopilotTask::TaskType) {
ptr = std::make_shared<CopilotTask>(task_callback, (void*)this);
ptr = std::make_shared<CopilotTask>(task_callback, static_cast<void*>(this));
}
#ifdef ASST_DEBUG
else if (type == DebugTask::TaskType) {
@@ -162,7 +162,7 @@ bool asst::Assistant::set_task_params(TaskId task_id, const std::string& params)
std::vector<uchar> asst::Assistant::get_image() const
{
if (!m_inited) {
return std::vector<uchar>();
return {};
}
return m_ctrler->get_image_encode();
}
@@ -276,7 +276,7 @@ void Assistant::msg_proc()
if (!m_msg_queue.empty()) {
// 结构化绑定只能是引用后续的pop会使引用失效所以需要重新构造一份这里采用了move的方式
auto&& [temp_msg, temp_detail] = m_msg_queue.front();
AsstMsg msg = std::move(temp_msg);
AsstMsg msg = temp_msg;
json::value detail = std::move(temp_detail);
m_msg_queue.pop();
lock.unlock();
@@ -293,7 +293,7 @@ void Assistant::msg_proc()
void Assistant::task_callback(AsstMsg msg, const json::value& detail, void* custom_arg)
{
Assistant* p_this = (Assistant*)custom_arg;
auto p_this = static_cast<Assistant*>(custom_arg);
json::value more_detail = detail;
more_detail["uuid"] = p_this->m_uuid;
@@ -321,6 +321,6 @@ void asst::Assistant::append_callback(AsstMsg msg, json::value detail)
void Assistant::clear_cache()
{
m_status->clear_data();
m_status->clear_number();
//Task.clear_cache();
}

View File

@@ -1,6 +1,6 @@
#include "AsstCaller.h"
#include <string.h>
#include <cstring>
#include <iostream>
#include <meojson/json.hpp>
@@ -122,7 +122,7 @@ TaskId AsstAppendTask(AsstHandle handle, const char* type, const char* params)
bool AsstSetTaskParams(AsstHandle handle, TaskId id, const char* params)
{
if (!inited || handle == nullptr) {
return 0;
return false;
}
return handle->set_task_params(id, params ? params : "");

View File

@@ -98,7 +98,7 @@ namespace asst
}
Rect move(Rect move) const
{
return Rect(x + move.x, y + move.y, move.width, move.height);
return {x + move.x, y + move.y, move.width, move.height};
}
int x = 0;

View File

@@ -90,13 +90,13 @@ namespace asst
const char* src_str = ansi_str.c_str();
int len = MultiByteToWideChar(CP_ACP, 0, src_str, -1, nullptr, 0);
const std::size_t wstr_length = static_cast<std::size_t>(len) + 1U;
wchar_t *wstr = new wchar_t[wstr_length];
auto wstr = new wchar_t[wstr_length];
memset(wstr, 0, sizeof(wstr[0]) * wstr_length);
MultiByteToWideChar(CP_ACP, 0, src_str, -1, wstr, len);
len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
const std::size_t str_length = static_cast<std::size_t>(len) + 1;
char *str = new char[str_length];
auto str = new char[str_length];
memset(str, 0, sizeof(str[0]) * str_length);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, nullptr, nullptr);
std::string strTemp = str;
@@ -121,13 +121,13 @@ namespace asst
const char* src_str = utf8_str.c_str();
int len = MultiByteToWideChar(CP_UTF8, 0, src_str, -1, nullptr, 0);
const std::size_t wsz_ansi_length = static_cast<std::size_t>(len) + 1;
wchar_t *wsz_ansi = new wchar_t[wsz_ansi_length];
auto wsz_ansi = new wchar_t[wsz_ansi_length];
memset(wsz_ansi, 0, sizeof(wsz_ansi[0]) * wsz_ansi_length);
MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wsz_ansi, len);
len = WideCharToMultiByte(CP_ACP, 0, wsz_ansi, -1, nullptr, 0, nullptr, nullptr);
const std::size_t sz_ansi_length = static_cast<std::size_t>(len) + 1;
char *sz_ansi = new char[sz_ansi_length];
auto sz_ansi = new char[sz_ansi_length];
memset(sz_ansi, 0, sizeof(sz_ansi[0]) * sz_ansi_length);
WideCharToMultiByte(CP_ACP, 0, wsz_ansi, -1, sz_ansi, len, nullptr, nullptr);
std::string strTemp(sz_ansi);
@@ -156,7 +156,7 @@ namespace asst
{
std::ifstream ifs(filename, std::ios::in);
if (!ifs.is_open()) {
return std::string();
return {};
}
std::stringstream iss;
iss << ifs.rdbuf();
@@ -197,7 +197,7 @@ namespace asst
si.hStdOutput = pipe_child_write;
si.hStdError = pipe_child_write;
PROCESS_INFORMATION pi = { 0 };
PROCESS_INFORMATION pi = { nullptr };
BOOL p_ret = CreateProcessA(nullptr, const_cast<LPSTR>(cmdline.c_str()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi);
if (p_ret) {

View File

@@ -1,9 +1,11 @@
#include "AwardTask.h"
#include <utility>
#include "ProcessTask.h"
asst::AwardTask::AwardTask(AsstCallback callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
: PackageTask(std::move(callback), callback_arg, TaskType),
m_award_task_ptr(std::make_shared<ProcessTask>(m_callback, m_callback_arg, TaskType))
{
m_award_task_ptr->set_tasks({ "AwardBegin" });

View File

@@ -15,6 +15,11 @@ bool asst::BattleImageAnalyzer::set_target(int target)
return true;
}
void asst::BattleImageAnalyzer::set_pre_total_kills(int pre_total_kills)
{
m_pre_total_kills = pre_total_kills;
}
bool asst::BattleImageAnalyzer::analyze()
{
clear();
@@ -86,6 +91,11 @@ int asst::BattleImageAnalyzer::get_kills() const noexcept
return m_kills;
}
int asst::BattleImageAnalyzer::get_total_kills() const noexcept
{
return m_total_kills;
}
int asst::BattleImageAnalyzer::get_cost() const noexcept
{
return m_cost;
@@ -172,7 +182,7 @@ asst::BattleRole asst::BattleImageAnalyzer::oper_role_analyze(const Rect& roi)
MatchImageAnalyzer role_analyzer(m_image);
BattleRole result = BattleRole::Unknown;
auto result = BattleRole::Unknown;
double max_score = 0;
for (auto&& [role, role_name] : RolesName) {
role_analyzer.set_task_info("BattleOperRole" + role_name);
@@ -414,7 +424,7 @@ bool asst::BattleImageAnalyzer::hp_analyze()
for (const std::string& num_name : hash_analyzer.get_min_dist_name()) {
if (num_name.empty()) {
Log.error("hash result is empty");
return 0;
return false;
}
hp *= 10;
hp += num_name.at(0) - '0';
@@ -438,20 +448,37 @@ bool asst::BattleImageAnalyzer::kills_analyze()
return false;
}
std::string kills_text;
size_t pos = std::string::npos;
for (auto& res : kills_analyzer.get_result()) {
// 这里的结果应该是 "击杀数/总的敌人数",例如 "0/41"
pos = res.text.find('/');
if (pos != std::string::npos) {
kills_text = res.text;
break;
}
}
if (pos == std::string::npos) {
std::string kills_text = kills_analyzer.get_result().front().text;
if (kills_text.empty()) {
return false;
}
size_t pos = kills_text.find('/');
if (pos == std::string::npos) {
Log.warn("cannot found flag /");
// 这种时候绝大多数是把 "0/41" 中的 '/' 识别成了别的什么东西(其中又有绝大部分情况是识别成了 '1'
// 所以这里依赖 m_pre_total_kills 转一下
if (m_pre_total_kills == 0) {
// 第一次识别就识别错了,识别成了 "0141"
if (kills_text.at(0) == '0') {
pos = 1;
}
else {
Log.error("m_pre_total_kills is zero");
return false;
}
}
else {
size_t pre_pos = kills_text.find(std::to_string(m_pre_total_kills));
if (pre_pos == std::string::npos || pre_pos == 0) {
Log.error("can't get pre_pos");
return false;
}
Log.trace("pre total kills pos:", pre_pos);
pos = pre_pos - 1;
}
}
// 例子中的"0"
std::string kills_count = kills_text.substr(0, pos);
if (kills_count.empty() ||
@@ -461,7 +488,22 @@ bool asst::BattleImageAnalyzer::kills_analyze()
}
int cur_kills = std::stoi(kills_count);
m_kills = std::max(m_kills, cur_kills);
Log.trace("Kills:", m_kills, cur_kills);
// 例子中的"41"
std::string total_kills = kills_text.substr(pos + 1, std::string::npos);
int cur_total_kills = 0;
if (total_kills.empty() ||
!std::all_of(total_kills.cbegin(), total_kills.cend(),
[](char c) -> bool {return std::isdigit(c);})) {
Log.warn("total kills recognition failed, set to", m_pre_total_kills);
cur_total_kills = m_pre_total_kills;
}
else {
cur_total_kills = std::stoi(total_kills);
}
m_total_kills = std::max(cur_total_kills, m_pre_total_kills);
Log.trace("Kills:", m_kills, "/", m_total_kills);
return true;
}

View File

@@ -26,6 +26,7 @@ namespace asst
virtual ~BattleImageAnalyzer() = default;
bool set_target(int target);
void set_pre_total_kills(int pre_total_kills);
virtual bool analyze() override;
virtual const std::vector<BattleRealTimeOper>& get_opers() const noexcept;
@@ -34,6 +35,7 @@ namespace asst
const std::vector<Rect>& get_ready_skills() const noexcept;
int get_hp() const noexcept;
int get_kills() const noexcept;
int get_total_kills() const noexcept;
int get_cost() const noexcept;
void clear() noexcept;
@@ -53,6 +55,7 @@ namespace asst
bool vacancies_analyze();// 识别剩余可部署人数
int m_target = 0; // 待识别的目标
int m_pre_total_kills = 0; // 之前的击杀总数,因为击杀数经常识别不准所以依赖外部传入作为参考
/* 识别结果缓存 */
std::vector<BattleRealTimeOper> m_opers; // 下方干员信息
@@ -60,6 +63,7 @@ namespace asst
std::vector<Rect> m_ready_skills; // 可以释放的技能Rect实际是干员的位置
int m_hp = 0; // 剩余生命值
int m_kills = 0; // 击杀数
int m_total_kills = 0; // 击杀总数
int m_cost = 0; // 部署费用
};
}

View File

@@ -110,6 +110,15 @@ bool asst::BattleProcessTask::analyze_opers_preview()
std::this_thread::yield();
}
// 识别一帧总击杀数
BattleImageAnalyzer kills_analyzer(image);
kills_analyzer.set_target(BattleImageAnalyzer::Target::Kills);
if (kills_analyzer.analyze()) {
m_kills = kills_analyzer.get_kills();
m_total_kills = kills_analyzer.get_total_kills();
}
auto draw = image.clone();
// 暂停游戏准备识别干员
// 在刚进入游戏的时候(画面刚刚完全亮起来的时候),点暂停是没反应的
// 所以这里一直点,直到真的点上了为止
@@ -124,7 +133,6 @@ bool asst::BattleProcessTask::analyze_opers_preview()
}
auto draw_future = std::async(std::launch::async, [&]() {
auto draw = image.clone();
for (const auto& [loc, info] : m_normal_tile_info) {
std::string text = "( " + std::to_string(loc.x) + ", " + std::to_string(loc.y) + " )";
cv::putText(draw, text, cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2);
@@ -389,9 +397,13 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action)
image = m_ctrler->get_image();
}
BattleImageAnalyzer analyzer(image);
if (m_total_kills) {
analyzer.set_pre_total_kills(m_total_kills);
}
analyzer.set_target(BattleImageAnalyzer::Target::Kills);
if (analyzer.analyze()) {
m_kills = analyzer.get_kills();
m_total_kills = analyzer.get_total_kills();
if (m_kills >= action.kills) {
break;
}
@@ -422,7 +434,7 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action)
try_possible_skill(image);
std::this_thread::yield();
image = m_ctrler->get_image();
};
}
}
if (image.empty()) {
image = m_ctrler->get_image();
@@ -454,7 +466,7 @@ bool asst::BattleProcessTask::oper_deploy(const BattleAction& action)
std::sqrt(
(std::abs(placed_point.x - oper_rect.x) << 1)
+ (std::abs(placed_point.y - oper_rect.y) << 1)));
int duration = static_cast<int>(swipe_oper_task_ptr->pre_delay / 500.0 * dist); // 随便取的一个系数
int duration = static_cast<int>(swipe_oper_task_ptr->pre_delay / 1000 * dist); // 随便取的一个系数
m_ctrler->swipe(oper_rect, placed_rect, duration, true, 0);
sleep(use_oper_task_ptr->rear_delay);
@@ -503,7 +515,7 @@ bool asst::BattleProcessTask::oper_retreat(const BattleAction& action)
const std::string& name = m_group_to_oper_mapping[action.group_name].name;
Point pos;
if (auto iter = m_used_opers.find(name);
action.location.x != 0 && action.location.y != 0 &&
action.location.x == 0 && action.location.y == 0 &&
iter != m_used_opers.cend()) {
pos = iter->second.pos;
m_used_opers.erase(name);
@@ -522,7 +534,7 @@ bool asst::BattleProcessTask::use_skill(const BattleAction& action)
const std::string& name = m_group_to_oper_mapping[action.group_name].name;
Point pos;
if (auto iter = m_used_opers.find(name);
action.location.x != 0 && action.location.y != 0 &&
action.location.x == 0 && action.location.y == 0 &&
iter != m_used_opers.cend()) {
pos = iter->second.pos;
m_used_opers.erase(name);

View File

@@ -44,6 +44,7 @@ namespace asst
/* 实时更新的数据 */
int m_kills = 0;
int m_total_kills = 0;
std::unordered_map<std::string, BattleRealTimeOper> m_all_opers_info;
std::unordered_map<std::string, BattleRealTimeOper> m_cur_opers_info;
std::unordered_map<std::string, BattleDeployInfo> m_used_opers;

View File

@@ -8,7 +8,7 @@
#include <sys/wait.h>
#endif
#include <stdint.h>
#include <cstdint>
#include <algorithm>
#include <chrono>
#include <regex>
@@ -55,7 +55,7 @@ private:
#endif
asst::Controller::Controller(AsstCallback callback, void* callback_arg)
: m_callback(callback),
: m_callback(std::move(callback)),
m_callback_arg(callback_arg),
m_rand_engine(static_cast<unsigned int>(time(nullptr)))
{
@@ -91,7 +91,7 @@ asst::Controller::Controller(AsstCallback callback, void* callback_arg)
break;
}
m_support_socket = true;
} while (0);
} while (false);
#else
int pipe_in_ret = pipe(m_pipe_in);
@@ -244,12 +244,10 @@ void asst::Controller::pipe_working_proc()
std::optional<std::vector<unsigned char>> asst::Controller::call_command(const std::string& cmd, int64_t timeout, bool recv_by_socket)
{
LogTraceFunction;
LogTraceScope(std::string(__FUNCTION__) + " | `" + cmd + "`");
std::vector<uchar> pipe_data;
std::unique_lock<std::mutex> pipe_lock(m_pipe_mutex);
auto start_time = std::chrono::steady_clock::now();
auto check_timeout = [&]() -> bool {
return timeout &&
@@ -259,15 +257,16 @@ std::optional<std::vector<unsigned char>> asst::Controller::call_command(const s
};
#ifdef _WIN32
PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体
PROCESS_INFORMATION process_info = { nullptr }; // 进程信息结构体
BOOL create_ret = ::CreateProcessA(nullptr, const_cast<LPSTR>(cmd.c_str()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &m_child_startup_info, &process_info);
if (!create_ret) {
Log.error("Call `", cmd, "` create error, ret", create_ret);
Log.error("Call `", cmd, "` create process failed, ret", create_ret);
return std::nullopt;
}
if (!recv_by_socket) {
DWORD peek_num = 0;
DWORD read_num = 0;
std::unique_lock<std::mutex> pipe_lock(m_pipe_mutex);
do {
//DWORD write_num = 0;
//WriteFile(parent_write, cmd.c_str(), cmd.size(), &write_num, nullptr);
@@ -279,20 +278,25 @@ std::optional<std::vector<unsigned char>> asst::Controller::call_command(const s
} while (::WaitForSingleObject(process_info.hProcess, 0) == WAIT_TIMEOUT && !check_timeout());
}
else {
SOCKADDR client_addr = { 0 };
int size = sizeof(SOCKADDR);
SOCKET client_sock = ::accept(m_server_sock, (SOCKADDR*)&client_addr, &size);
int recv_size = 0;
do {
recv_size = ::recv(client_sock, (char*)m_socket_buffer.get(), SocketBuffSize, NULL);
if (recv_size < 0) {
Log.error("recv error", recv_size);
break;
}
pipe_data.insert(pipe_data.end(), m_socket_buffer.get(), m_socket_buffer.get() + recv_size);
} while (recv_size > 0 && !check_timeout());
::closesocket(client_sock);
::WaitForSingleObject(process_info.hProcess, static_cast<DWORD>(timeout));
std::unique_lock<std::mutex> pipe_lock(m_pipe_mutex);
fd_set fdset = { 0 };
FD_SET(m_server_sock, &fdset);
timeval select_timeout = { 3, 0 };
select(static_cast<int>(m_server_sock) + 1, &fdset, NULL, NULL, &select_timeout);
if (FD_ISSET(m_server_sock, &fdset)) {
SOCKET client_sock = ::accept(m_server_sock, NULL, NULL);
int recv_size = 0;
do {
recv_size = ::recv(client_sock, (char*)m_socket_buffer.get(), SocketBuffSize, NULL);
if (recv_size < 0) {
Log.error("recv error", recv_size);
break;
}
pipe_data.insert(pipe_data.end(), m_socket_buffer.get(), m_socket_buffer.get() + recv_size);
} while (recv_size > 0 && !check_timeout());
::closesocket(client_sock);
}
::WaitForSingleObject(process_info.hProcess, 3000);
}
DWORD exit_ret = 0;
::GetExitCodeProcess(process_info.hProcess, &exit_ret);
@@ -323,6 +327,7 @@ std::optional<std::vector<unsigned char>> asst::Controller::call_command(const s
else if (m_child > 0) {
// parent process
// LogTraceScope("Parent process: " + cmd);
std::unique_lock<std::mutex> pipe_lock(m_pipe_mutex);
do {
ssize_t read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize);
@@ -346,9 +351,48 @@ std::optional<std::vector<unsigned char>> asst::Controller::call_command(const s
if (!exit_ret) {
return pipe_data;
}
else {
return std::nullopt;
else if (m_inited) {
// 之前可以运行,突然运行不了了,这种情况多半是 adb 炸了。所以重新连接一下
m_inited = false;
auto reconnect_ret = call_command(m_adb.connect, 60 * 1000);
bool is_reconnect_success = false;
if (reconnect_ret) {
auto& reconnect_val = reconnect_ret.value();
std::string reconnect_str(
std::make_move_iterator(reconnect_val.begin()),
std::make_move_iterator(reconnect_val.end()));
is_reconnect_success = reconnect_str.find("error") == std::string::npos;
}
if (is_reconnect_success) {
auto recall_ret = call_command(cmd, timeout, recv_by_socket);
if (recall_ret) {
m_inited = true;
return recall_ret;
}
else {
json::value info = json::object{
{ "uuid", m_uuid},
{ "what", "CommandExecFailed" },
{ "why", "I dont fucking know, go look at the log!" },
{ "details", json::object {
{ "cmd", cmd },
{ "recv_by_socket", recv_by_socket}
}} };
m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg);
}
}
else {
json::value info = json::object{
{ "uuid", m_uuid},
{ "what", "Disconnect" },
{ "why", "Reconnect failed" },
{ "details", json::object {
{ "cmd", m_adb.connect }
}} };
m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg);
}
}
return std::nullopt;
}
void asst::Controller::convert_lf(std::vector<unsigned char>& data)
@@ -378,11 +422,11 @@ void asst::Controller::convert_lf(std::vector<unsigned char>& data)
Iter next_iter = first_iter;
while (++first_iter != end_r1_iter) {
if (!pred(first_iter)) {
*next_iter = std::move(*first_iter);
*next_iter = *first_iter;
++next_iter;
}
}
*next_iter = std::move(*end_r1_iter);
*next_iter = *end_r1_iter;
++next_iter;
data.erase(next_iter, data.end());
}
@@ -407,7 +451,7 @@ asst::Point asst::Controller::rand_point_in_rect(const Rect& rect)
y = y_rand + rect.y;
}
return Point(x, y);
return { x, y };
}
void asst::Controller::random_delay() const
@@ -429,6 +473,7 @@ void asst::Controller::random_delay() const
void asst::Controller::clear_info() noexcept
{
m_inited = false;
m_adb = decltype(m_adb)();
m_uuid.clear();
m_width = 0;
@@ -453,7 +498,7 @@ int asst::Controller::push_cmd(const std::string& cmd)
return ++m_push_id;
}
std::optional<asst::Controller::SocketInfo> asst::Controller::try_to_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times)
std::optional<unsigned short> asst::Controller::try_to_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times)
{
LogTraceFunction;
@@ -477,7 +522,7 @@ std::optional<asst::Controller::SocketInfo> asst::Controller::try_to_init_socket
#ifdef _WIN32
m_server_addr.sin_port = htons(port);
int bind_ret = ::bind(m_server_sock, (SOCKADDR*)&m_server_addr, sizeof(SOCKADDR));
int bind_ret = ::bind(m_server_sock, reinterpret_cast<SOCKADDR*>(&m_server_addr), sizeof(SOCKADDR));
int listen_ret = ::listen(m_server_sock, 3);
server_start = bind_ret == 0 && listen_ret == 0;
#else
@@ -494,11 +539,8 @@ std::optional<asst::Controller::SocketInfo> asst::Controller::try_to_init_socket
return std::nullopt;
}
// Todo: detect remote address, and check remote port
// reference from https://github.com/ArknightsAutoHelper/ArknightsAutoHelper/blob/master/automator/connector/ADBConnector.py#L436
std::string remote_address = "10.0.2.2";
Log.info("server_start", remote_address, port_result);
return SocketInfo{ remote_address, port_result };
Log.info("server_start", local_address, port_result);
return port_result;
}
void asst::Controller::wait(unsigned id) const noexcept
@@ -528,8 +570,6 @@ bool asst::Controller::screencap()
return false;
}
size_t header_size = data.size() - std_size;
Log.trace("header size:", header_size);
bool is_all_zero = std::all_of(data.data() + header_size, data.data() + std_size,
[](uchar uch) -> bool {
return uch == 0;
@@ -549,7 +589,6 @@ bool asst::Controller::screencap()
DecodeFunc decode_raw_with_gzip = [&](std::vector<uchar>& data) -> bool {
auto unzip_data = gzip::decompress(data.data(), data.size());
Log.trace("unzip data size:", unzip_data.size());
if (unzip_data.empty()) {
return false;
}
@@ -558,8 +597,6 @@ bool asst::Controller::screencap()
return false;
}
size_t header_size = unzip_data.size() - std_size;
Log.trace("header size:", header_size);
bool is_all_zero = std::all_of(unzip_data.data() + header_size, unzip_data.data() + std_size,
[](uchar uch) -> bool {
return uch == 0;
@@ -594,16 +631,19 @@ bool asst::Controller::screencap()
screencap(m_adb.screencap_raw_by_nc, decode_raw, true)) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::RawByNc;
Log.info("screencap by RawByNc");
m_inited = true;
return true;
}
else if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip;
Log.info("screencap by RawWithGzip");
m_inited = true;
return true;
}
else if (screencap(m_adb.screencap_encode, decode_encode)) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode;
Log.info("screencap by Encode");
m_inited = true;
return true;
}
else {
@@ -630,7 +670,7 @@ bool asst::Controller::screencap()
return false;
}
bool asst::Controller::screencap(const std::string& cmd, DecodeFunc decode_func, bool by_socket)
bool asst::Controller::screencap(const std::string& cmd, const DecodeFunc& decode_func, bool by_socket)
{
if ((!m_support_socket || !m_server_started) && by_socket) {
return false;
@@ -682,7 +722,7 @@ cv::Mat asst::Controller::get_resized_image() const
std::shared_lock<std::shared_mutex> image_lock(m_image_mutex);
if (m_cache_image.empty()) {
Log.error("image is empty");
return cv::Mat(dsize, CV_8UC3);
return { dsize, CV_8UC3 };
}
cv::Mat resized_mat;
cv::resize(m_cache_image, resized_mat, dsize, 0.0, 0.0, cv::INTER_AREA);
@@ -781,7 +821,7 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
extra_cmd = utils::string_replace_all(extra_cmd, "[y1]", std::to_string(p2.y));
int end_x = 0, end_y = 0;
if (p2.x != p1.x) {
double k = (double)(p2.y - p1.y) / (p2.x - p1.x);
double k = static_cast<double>(p2.y - p1.y) / (p2.x - p1.x);
double temp = extra_swipe_dist / std::sqrt(1 + k * k);
end_x = p2.x + static_cast<int>((p2.x > p1.x ? -1.0 : 1.0) * temp);
end_y = p2.y + static_cast<int>((p2.y > p1.y ? -1.0 : 1.0) * std::fabs(k) * temp);
@@ -848,7 +888,7 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a
const auto adb_cfg = std::move(adb_ret.value());
std::string display_id;
std::string nc_address;
std::string nc_address = "10.0.2.2";
u_short nc_port = 0;
auto cmd_replace = [&](const std::string& cfg_cmd) -> std::string {
@@ -868,9 +908,18 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a
/* connect */
{
auto connect_ret = call_command(cmd_replace(adb_cfg.connect), 60 * 1000);
m_adb.connect = cmd_replace(adb_cfg.connect);
auto connect_ret = call_command(m_adb.connect, 60 * 1000);
// 端口即使错误命令仍然会返回0TODO 对connect_result进行判断
if (!connect_ret) {
bool is_connect_success = false;
if (connect_ret) {
auto& connect_val = connect_ret.value();
std::string connect_str(
std::make_move_iterator(connect_val.begin()),
std::make_move_iterator(connect_val.end()));
is_connect_success = connect_str.find("error") == std::string::npos;
}
if (!is_connect_success) {
json::value info = get_info_json() |
json::object{
{ "what", "ConnectFailed" },
@@ -1033,11 +1082,23 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a
bind_address = "127.0.0.1";
}
// Todo: detect remote address, and check remote port
// reference from https://github.com/ArknightsAutoHelper/ArknightsAutoHelper/blob/master/automator/connector/ADBConnector.py#L436
auto nc_address_ret = call_command(cmd_replace(adb_cfg.nc_address));
if (nc_address_ret) {
auto& nc_result = nc_address_ret.value();
std::string nc_result_str(
std::make_move_iterator(nc_result.begin()),
std::make_move_iterator(nc_result.end()));
if (auto pos = nc_result_str.find(' ');
pos != std::string::npos) {
nc_address = nc_result_str.substr(0, pos);
}
}
auto socket_opt = try_to_init_socket(bind_address, adb_cfg.nc_port);
if (socket_opt) {
auto& socket_info = socket_opt.value();
nc_address = socket_info.remote_address;
nc_port = socket_info.remote_port;
nc_port = socket_opt.value();
m_adb.screencap_raw_by_nc = cmd_replace(adb_cfg.screencap_raw_by_nc);
m_server_started = true;
}
@@ -1077,11 +1138,16 @@ const std::string& asst::Controller::get_uuid() const
cv::Mat asst::Controller::get_image(bool raw)
{
bool inited = m_inited;
// 有些模拟器adb偶尔会莫名其妙截图失败多试几次
for (int i = 0; i != 20; ++i) {
if (screencap()) {
break;
}
// 截图之前正常,截图之后不正常了,说明截图过程中发现 adb 炸了
if (inited && !m_inited) {
break;
}
}
if (raw) {

View File

@@ -66,16 +66,11 @@ namespace asst
std::optional<std::vector<unsigned char>> call_command(const std::string& cmd, int64_t timeout = 20000, bool recv_by_socket = false);
int push_cmd(const std::string& cmd);
struct SocketInfo
{
std::string remote_address;
unsigned short remote_port = 0U;
};
std::optional<SocketInfo> try_to_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times = 10U);
std::optional<unsigned short> try_to_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times = 10U);
using DecodeFunc = std::function<bool(std::vector<uchar>&)>;
bool screencap();
bool screencap(const std::string& cmd, DecodeFunc decode_func, bool by_nc = false);
bool screencap(const std::string& cmd, const DecodeFunc& decode_func, bool by_nc = false);
cv::Mat get_resized_image() const;
Point rand_point_in_rect(const Rect& rect);
@@ -119,6 +114,7 @@ namespace asst
struct AdbProperty
{
/* command */
std::string connect;
std::string click;
std::string swipe;
@@ -155,6 +151,7 @@ namespace asst
int m_height = 0;
bool m_support_socket = false;
bool m_server_started = false;
bool m_inited = false;
inline static int m_instance_count = 0;

View File

@@ -5,7 +5,7 @@
#include "BattleProcessTask.h"
#include "BattleFormationTask.h"
asst::CopilotTask::CopilotTask(AsstCallback callback, void* callback_arg)
asst::CopilotTask::CopilotTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
m_formation_task_ptr(std::make_shared<BattleFormationTask>(callback, callback_arg, TaskType)),
m_battle_task_ptr(std::make_shared<BattleProcessTask>(callback, callback_arg, TaskType))

View File

@@ -12,7 +12,7 @@ namespace asst
class CopilotTask final : public PackageTask
{
public:
CopilotTask(AsstCallback callback, void* callback_arg);
CopilotTask(const AsstCallback& callback, void* callback_arg);
virtual ~CopilotTask() = default;
virtual bool set_params(const json::value& params) override;

View File

@@ -51,7 +51,7 @@ bool asst::CreditShopImageAnalyzer::commoditys_analyze()
commodity.y = mr.rect.y + commodity_task_ptr->rect_move.y;
commodity.width = commodity_task_ptr->rect_move.width;
commodity.height = commodity_task_ptr->rect_move.height;
m_commoditys.emplace_back(std::move(commodity));
m_commoditys.emplace_back(commodity);
}
return true;

View File

@@ -4,7 +4,7 @@
#include "StageDropsTaskPlugin.h"
asst::DebugTask::DebugTask(AsstCallback callback, void* callback_arg)
asst::DebugTask::DebugTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType)
{
//auto task_ptr = std::make_shared<RoguelikeSkillSelectionTaskPlugin>(callback, callback_arg, TaskType);

View File

@@ -6,7 +6,7 @@ namespace asst
class DebugTask : public PackageTask
{
public:
DebugTask(AsstCallback callback, void* callback_arg);
DebugTask(const AsstCallback& callback, void* callback_arg);
virtual ~DebugTask() = default;
static constexpr const char* TaskType = "Debug";

View File

@@ -1,10 +1,13 @@
#include "FightTask.h"
#include <utility>
#include "ProcessTask.h"
#include "StageDropsTaskPlugin.h"
#include "GameCrashRestartTaskPlugin.h"
asst::FightTask::FightTask(AsstCallback callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
: PackageTask(std::move(callback), callback_arg, TaskType),
m_start_up_task_ptr(std::make_shared<ProcessTask>(m_callback, m_callback_arg, TaskType)),
m_stage_task_ptr(std::make_shared<ProcessTask>(m_callback, m_callback_arg, TaskType)),
m_fight_task_ptr(std::make_shared<ProcessTask>(m_callback, m_callback_arg, TaskType))
@@ -37,6 +40,8 @@ asst::FightTask::FightTask(AsstCallback callback, void* callback_arg)
.set_times_limit("StartButton2", INT_MAX);
m_stage_drops_plugin_ptr = m_fight_task_ptr->regiseter_plugin<StageDropsTaskPlugin>();
m_stage_drops_plugin_ptr->set_retry_times(0);
m_game_restart_plugin_ptr = m_fight_task_ptr->regiseter_plugin<GameCrashRestartTaskPlugin>();
m_game_restart_plugin_ptr->set_retry_times(0);
m_subtasks.emplace_back(m_start_up_task_ptr);
m_subtasks.emplace_back(m_stage_task_ptr);
@@ -52,6 +57,15 @@ bool asst::FightTask::set_params(const json::value& params)
bool enable_penguid = params.get("report_to_penguin", false);
std::string penguin_id = params.get("penguin_id", "");
std::string server = params.get("server", "CN");
std::string client_type = params.get("client_type", std::string());
if (params.contains("drops")) {
std::unordered_map<std::string, int> drops;
for (const auto& [item_id, quantity] : params.at("drops").as_object()) {
drops.insert_or_assign(item_id, quantity.as_integer());
}
m_stage_drops_plugin_ptr->set_specify_quantity(drops);
}
if (!m_runned) {
if (stage.empty()) {
@@ -71,6 +85,9 @@ bool asst::FightTask::set_params(const json::value& params)
.set_times_limit("StartButton2", times);
m_stage_drops_plugin_ptr->set_enable_penguid(enable_penguid);
m_stage_drops_plugin_ptr->set_penguin_id(std::move(penguin_id));
if (!client_type.empty()) {
m_game_restart_plugin_ptr->set_client_type(client_type);
}
return true;
}

View File

@@ -7,6 +7,7 @@ namespace asst
{
class ProcessTask;
class StageDropsTaskPlugin;
class GameCrashRestartTaskPlugin;
class FightTask final : public PackageTask
{
@@ -23,5 +24,6 @@ namespace asst
std::shared_ptr<ProcessTask> m_stage_task_ptr = nullptr;
std::shared_ptr<ProcessTask> m_fight_task_ptr = nullptr;
std::shared_ptr<StageDropsTaskPlugin> m_stage_drops_plugin_ptr = nullptr;
std::shared_ptr<GameCrashRestartTaskPlugin> m_game_restart_plugin_ptr = nullptr;
};
}

View File

@@ -0,0 +1,16 @@
#include "GameCrashRestartTaskPlugin.h"
bool asst::GameCrashRestartTaskPlugin::verify(AsstMsg msg, const json::value& details) const
{
if (msg != AsstMsg::SubTaskCompleted
|| details.get("subtask", std::string()) != "ProcessTask") {
return false;
}
if (details.at("details").at("task").as_string() == "MaybeCrashAndRestartGame") {
return true;
}
else {
return false;
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "StartGameTaskPlugin.h"
namespace asst
{
class GameCrashRestartTaskPlugin : public StartGameTaskPlugin
{
public:
using StartGameTaskPlugin::StartGameTaskPlugin;
virtual ~GameCrashRestartTaskPlugin() noexcept = default;
virtual bool verify(AsstMsg msg, const json::value& details) const override;
};
}

View File

@@ -48,6 +48,7 @@ bool asst::GeneralConfiger::parse(const json::value& json)
adb.display_format = cfg_json.at("displayFormat").as_string();
adb.screencap_raw_with_gzip = cfg_json.at("screencapRawWithGzip").as_string();
adb.screencap_raw_by_nc = cfg_json.at("screencapRawByNC").as_string();
adb.nc_address = cfg_json.at("ncAddress").as_string();
adb.nc_port = static_cast<unsigned short>(cfg_json.at("ncPort").as_integer());
adb.screencap_encode = cfg_json.at("screencapEncode").as_string();
adb.release = cfg_json.at("release").as_string();

View File

@@ -52,6 +52,7 @@ namespace asst
std::string display;
std::string screencap_raw_with_gzip;
std::string screencap_raw_by_nc;
std::string nc_address;
unsigned short nc_port = 0U;
std::string screencap_encode;
std::string release;

View File

@@ -1,6 +1,7 @@
#include "InfrastAbstractTask.h"
#include <regex>
#include <utility>
#include "AsstMsg.h"
#include "Controller.h"
@@ -12,7 +13,7 @@
#include "ProcessTask.h"
asst::InfrastAbstractTask::InfrastAbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain)
: AbstractTask(callback, callback_arg, task_chain)
: AbstractTask(std::move(callback), callback_arg, std::move(task_chain))
{
m_retry_times = TaskRetryTimes;
}

View File

@@ -31,11 +31,11 @@ namespace asst
{
if (auto iter = m_result.find(name);
iter == m_result.cend()) {
return Rect();
return {};
}
else {
if (index < 0 || index >= iter->second.size()) {
return Rect();
return {};
}
else {
return iter->second.at(index).rect;

View File

@@ -20,7 +20,7 @@ bool asst::InfrastInfoTask::_run()
for (auto&& [name, res] : analyzer.get_result()) {
std::string key = "NumOf" + name;
//int size = static_cast<int>(res.size());
m_status->set_data(key, res.size());
m_status->set_number(key, res.size());
Log.trace("InfrastInfoTask | ", key, res.size());
}

View File

@@ -319,7 +319,7 @@ bool asst::InfrastProductionTask::optimal_calc()
// 条件判断,不符合的直接过滤掉
bool meet_condition = true;
for (const auto& [cond, cond_value] : group.conditions) {
auto cond_opt = m_status->get_data(cond);
auto cond_opt = m_status->get_number(cond);
if (!cond_opt) {
continue;
}
@@ -614,7 +614,7 @@ asst::InfrastProductionTask::efficient_regex_calc(
// TODO 报错!
}
std::string status_key = cur_formula.substr(pos + 1, rp_pos - pos - 1);
auto status_opt = m_status->get_data(status_key);
auto status_opt = m_status->get_number(status_key);
const int64_t status_value = status_opt ? status_opt.value() : 0;
cur_formula.replace(pos, rp_pos - pos + 1, std::to_string(status_value));
}

View File

@@ -14,7 +14,7 @@
#include "DronesForShamareTaskPlugin.h"
#include "ReplenishOriginiumShardTaskPlugin.h"
asst::InfrastTask::InfrastTask(AsstCallback callback, void* callback_arg)
asst::InfrastTask::InfrastTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
m_infrast_begin_task_ptr(std::make_shared<ProcessTask>(callback, callback_arg, TaskType)),
m_info_task_ptr(std::make_shared<InfrastInfoTask>(callback, callback_arg, TaskType)),

View File

@@ -18,7 +18,7 @@ namespace asst
{
public:
public:
InfrastTask(AsstCallback callback, void* callback_arg);
InfrastTask(const AsstCallback& callback, void* callback_arg);
virtual ~InfrastTask() = default;
virtual bool set_params(const json::value& params) override;

View File

@@ -107,7 +107,6 @@ namespace asst
}
}
catch (...) {
;
}
}
void log_init_info()

View File

@@ -3,7 +3,7 @@
#include "ProcessTask.h"
#include "CreditShoppingTask.h"
asst::MallTask::MallTask(AsstCallback callback, void* callback_arg)
asst::MallTask::MallTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
m_mall_task_ptr(std::make_shared<ProcessTask>(callback, callback_arg, TaskType)),
m_shopping_first_task_ptr(std::make_shared<CreditShoppingTask>(callback, callback_arg, TaskType)),

View File

@@ -9,7 +9,7 @@ namespace asst
class MallTask final : public PackageTask
{
public:
MallTask(AsstCallback callback, void* callback_arg);
MallTask(const AsstCallback& callback, void* callback_arg);
virtual ~MallTask() = default;
virtual bool set_params(const json::value& params) override;

View File

@@ -4,12 +4,11 @@
#include "Logger.hpp"
#include "Resource.h"
asst::MatchImageAnalyzer::MatchImageAnalyzer(const cv::Mat image, const Rect& roi, std::string templ_name, double templ_thres)
asst::MatchImageAnalyzer::MatchImageAnalyzer(const cv::Mat& image, const Rect& roi, std::string templ_name, double templ_thres)
: AbstractImageAnalyzer(image, roi),
m_templ_name(std::move(templ_name)),
m_templ_thres(templ_thres)
{
;
}
bool asst::MatchImageAnalyzer::analyze()
@@ -54,7 +53,7 @@ void asst::MatchImageAnalyzer::set_threshold(double templ_thres) noexcept
m_templ_thres = templ_thres;
}
void asst::MatchImageAnalyzer::set_task_info(std::shared_ptr<TaskInfo> task_ptr)
void asst::MatchImageAnalyzer::set_task_info(const std::shared_ptr<TaskInfo>& task_ptr)
{
set_task_info(*std::dynamic_pointer_cast<MatchTaskInfo>(task_ptr));
}
@@ -66,7 +65,7 @@ void asst::MatchImageAnalyzer::set_task_info(const std::string& task_name)
void asst::MatchImageAnalyzer::set_region_of_appeared(Rect region) noexcept
{
m_region_of_appeared = std::move(region);
m_region_of_appeared = region;
if (m_use_cache && !m_region_of_appeared.empty()) {
m_roi = m_region_of_appeared;
}

View File

@@ -7,7 +7,7 @@ namespace asst
{
public:
using AbstractImageAnalyzer::AbstractImageAnalyzer;
MatchImageAnalyzer(const cv::Mat image, const Rect& roi, std::string templ_name, double templ_thres = 0.0);
MatchImageAnalyzer(const cv::Mat& image, const Rect& roi, std::string templ_name, double templ_thres = 0.0);
virtual ~MatchImageAnalyzer() = default;
virtual bool analyze() override;
@@ -18,7 +18,7 @@ namespace asst
void set_templ_name(std::string templ_name) noexcept;
void set_templ(cv::Mat templ) noexcept;
void set_threshold(double templ_thres) noexcept;
void set_task_info(std::shared_ptr<TaskInfo> task_ptr);
void set_task_info(const std::shared_ptr<TaskInfo>& task_ptr);
void set_task_info(const std::string& task_name);
void set_region_of_appeared(Rect region) noexcept;
void set_mask_with_close(int with_close) noexcept;

View File

@@ -29,6 +29,7 @@
<ClInclude Include="BattleFormationTask.h" />
<ClInclude Include="BattleImageAnalyzer.h" />
<ClInclude Include="BattleProcessTask.h" />
<ClInclude Include="GameCrashRestartTaskPlugin.h" />
<ClInclude Include="RoguelikeRecruitConfiger.h" />
<ClInclude Include="CopilotTask.h" />
<ClInclude Include="DebugTask.h" />
@@ -86,7 +87,7 @@
<ClInclude Include="StageDropsConfiger.h" />
<ClInclude Include="StageDropsImageAnalyzer.h" />
<ClInclude Include="StageDropsTaskPlugin.h" />
<ClInclude Include="StartGameTask.h" />
<ClInclude Include="StartGameTaskPlugin.h" />
<ClInclude Include="StartUpTask.h" />
<ClInclude Include="TaskData.h" />
<ClInclude Include="MatchImageAnalyzer.h" />
@@ -108,6 +109,7 @@
<ClCompile Include="BattleFormationTask.cpp" />
<ClCompile Include="BattleImageAnalyzer.cpp" />
<ClCompile Include="BattleProcessTask.cpp" />
<ClCompile Include="GameCrashRestartTaskPlugin.cpp" />
<ClCompile Include="RoguelikeRecruitConfiger.cpp" />
<ClCompile Include="CopilotTask.cpp" />
<ClCompile Include="DebugTask.cpp" />
@@ -166,7 +168,7 @@
<ClCompile Include="StageDropsConfiger.cpp" />
<ClCompile Include="StageDropsImageAnalyzer.cpp" />
<ClCompile Include="StageDropsTaskPlugin.cpp" />
<ClCompile Include="StartGameTask.cpp" />
<ClCompile Include="StartGameTaskPlugin.cpp" />
<ClCompile Include="StartUpTask.cpp" />
<ClCompile Include="TaskData.cpp" />
<ClCompile Include="MatchImageAnalyzer.cpp" />
@@ -336,4 +338,4 @@
<UserProperties _1_1_4_1_1_4resource_4config_1json__JsonSchema="" _1_1_4_1_1_4resource_4tasks_1json__JsonSchema="../docs/maa_tasks_schema.json" />
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>

View File

@@ -240,9 +240,6 @@
<ClInclude Include="RecruitCalcTask.h">
<Filter>头文件\Task\Sub</Filter>
</ClInclude>
<ClInclude Include="StartGameTask.h">
<Filter>头文件\Task\Sub</Filter>
</ClInclude>
<ClInclude Include="BattleImageAnalyzer.h">
<Filter>头文件\ImageAnalyzer\Final</Filter>
</ClInclude>
@@ -336,6 +333,12 @@
<ClInclude Include="RoguelikeRecruitImageAnalyzer.h">
<Filter>头文件\ImageAnalyzer\Final</Filter>
</ClInclude>
<ClInclude Include="StartGameTaskPlugin.h">
<Filter>头文件\Task\Plugin</Filter>
</ClInclude>
<ClInclude Include="GameCrashRestartTaskPlugin.h">
<Filter>头文件\Task\Plugin</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Controller.cpp">
@@ -476,9 +479,6 @@
<ClCompile Include="ProcessTask.cpp">
<Filter>源文件\Task\Sub</Filter>
</ClCompile>
<ClCompile Include="StartGameTask.cpp">
<Filter>源文件\Task\Sub</Filter>
</ClCompile>
<ClCompile Include="BattleImageAnalyzer.cpp">
<Filter>源文件\ImageAnalyzer\Final</Filter>
</ClCompile>
@@ -569,6 +569,12 @@
<ClCompile Include="RoguelikeRecruitImageAnalyzer.cpp">
<Filter>源文件\ImageAnalyzer\Final</Filter>
</ClCompile>
<ClCompile Include="StartGameTaskPlugin.cpp">
<Filter>源文件\Task\Plugin</Filter>
</ClCompile>
<ClCompile Include="GameCrashRestartTaskPlugin.cpp">
<Filter>源文件\Task\Plugin</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\resource\config.json">
@@ -584,4 +590,4 @@
<Filter>资源文件</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View File

@@ -1,15 +1,16 @@
#include "MultiMatchImageAnalyzer.h"
#include <utility>
#include "Logger.hpp"
#include "Resource.h"
#include "TaskData.h"
asst::MultiMatchImageAnalyzer::MultiMatchImageAnalyzer(const cv::Mat image, const Rect& roi, std::string templ_name, double templ_thres)
asst::MultiMatchImageAnalyzer::MultiMatchImageAnalyzer(const cv::Mat& image, const Rect& roi, std::string templ_name, double templ_thres)
: AbstractImageAnalyzer(image, roi),
m_templ_name(templ_name),
m_templ_name(std::move(templ_name)),
m_templ_thres(templ_thres)
{
;
}
bool asst::MultiMatchImageAnalyzer::analyze()
@@ -68,7 +69,7 @@ void asst::MultiMatchImageAnalyzer::set_task_info(MatchTaskInfo task_info) noexc
set_roi(task_info.roi);
}
void asst::MultiMatchImageAnalyzer::set_task_info(std::shared_ptr<TaskInfo> task_ptr)
void asst::MultiMatchImageAnalyzer::set_task_info(const std::shared_ptr<TaskInfo>& task_ptr)
{
set_task_info(*std::dynamic_pointer_cast<MatchTaskInfo>(task_ptr));
}

View File

@@ -7,7 +7,7 @@ namespace asst
{
public:
using AbstractImageAnalyzer::AbstractImageAnalyzer;
MultiMatchImageAnalyzer(const cv::Mat image, const Rect& roi, std::string templ_name, double templ_thres);
MultiMatchImageAnalyzer(const cv::Mat& image, const Rect& roi, std::string templ_name, double templ_thres);
virtual ~MultiMatchImageAnalyzer() = default;
virtual bool analyze() override;
@@ -19,7 +19,7 @@ namespace asst
void set_templ_name(std::string templ_name) noexcept;
void set_threshold(double templ_thres) noexcept;
void set_task_info(std::shared_ptr<TaskInfo> task_ptr);
void set_task_info(const std::shared_ptr<TaskInfo>& task_ptr);
void set_task_info(const std::string& task_name);
const std::vector<MatchRect>& get_result() const noexcept;

View File

@@ -154,7 +154,7 @@ void asst::OcrImageAnalyzer::set_task_info(const std::string& task_name)
void asst::OcrImageAnalyzer::set_region_of_appeared(Rect region) noexcept
{
m_region_of_appeared = std::move(region);
m_region_of_appeared = region;
if (m_use_cache && !m_region_of_appeared.empty()) {
m_roi = m_region_of_appeared;
m_without_det = true;

View File

@@ -34,10 +34,10 @@ bool asst::OcrPack::load(const std::string& dir)
return false;
}
constexpr static const char* DetName = "/det";
constexpr static auto DetName = "/det";
//constexpr static const char* ClsName = "/cls";
constexpr static const char* RecName = "/rec";
constexpr static const char* KeysName = "/ppocr_keys_v1.txt";
constexpr static auto RecName = "/rec";
constexpr static auto KeysName = "/ppocr_keys_v1.txt";
const std::string dst_filename = dir + DetName;
//const std::string cls_filename = dir + ClsName;
@@ -52,7 +52,7 @@ bool asst::OcrPack::load(const std::string& dir)
return m_ocr != nullptr;
}
std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat image, const asst::TextRectProc& pred, bool without_det)
std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat& image, const asst::TextRectProc& pred, bool without_det)
{
LogTraceFunction;
@@ -89,10 +89,10 @@ std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat image, const
int* box = m_boxes_buffer + i * 8;
int x_collect[4] = { *(box + 0), *(box + 2), *(box + 4), *(box + 6) };
int y_collect[4] = { *(box + 1), *(box + 3), *(box + 5), *(box + 7) };
int left = int(*std::min_element(x_collect, x_collect + 4));
int right = int(*std::max_element(x_collect, x_collect + 4));
int top = int(*std::min_element(y_collect, y_collect + 4));
int bottom = int(*std::max_element(y_collect, y_collect + 4));
int left = static_cast<int>(*std::min_element(x_collect, x_collect + 4));
int right = static_cast<int>(*std::max_element(x_collect, x_collect + 4));
int top = static_cast<int>(*std::min_element(y_collect, y_collect + 4));
int bottom = static_cast<int>(*std::max_element(y_collect, y_collect + 4));
rect = Rect(left, top, right - left, bottom - top);
}
std::string text(*(m_strs_buffer + i));
@@ -117,7 +117,7 @@ std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat image, const
return result;
}
std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat image, const asst::Rect& roi, const asst::TextRectProc& pred, bool without_det)
std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat& image, const asst::Rect& roi, const asst::TextRectProc& pred, bool without_det)
{
auto rect_cor = [&roi, &pred, &without_det](TextRect& tr) -> bool {
if (without_det) {

View File

@@ -23,15 +23,15 @@ namespace asst
virtual bool load(const std::string& dir) override;
std::vector<TextRect> recognize(const cv::Mat image, const TextRectProc& pred = nullptr, bool without_det = false);
std::vector<TextRect> recognize(const cv::Mat image, const Rect& roi, const TextRectProc& pred = nullptr, bool without_det = false);
std::vector<TextRect> recognize(const cv::Mat& image, const TextRectProc& pred = nullptr, bool without_det = false);
std::vector<TextRect> recognize(const cv::Mat& image, const Rect& roi, const TextRectProc& pred = nullptr, bool without_det = false);
private:
paddle_ocr_t* m_ocr = nullptr;
// each box has 8 value ( 4 points, x and y )
int m_boxes_buffer[MaxBoxSize * 8] = { 0 };
char* m_strs_buffer[MaxBoxSize] = { 0 };
char* m_strs_buffer[MaxBoxSize] = { nullptr };
float m_scores_buffer[MaxBoxSize] = { 0 };
};
}

View File

@@ -2,12 +2,12 @@
#include "TaskData.h"
asst::OcrWithFlagTemplImageAnalyzer::OcrWithFlagTemplImageAnalyzer(const cv::Mat image)
asst::OcrWithFlagTemplImageAnalyzer::OcrWithFlagTemplImageAnalyzer(const cv::Mat& image)
: OcrWithPreprocessImageAnalyzer(image),
m_multi_match_image_analyzer(image)
{}
asst::OcrWithFlagTemplImageAnalyzer::OcrWithFlagTemplImageAnalyzer(const cv::Mat image, const Rect& roi)
asst::OcrWithFlagTemplImageAnalyzer::OcrWithFlagTemplImageAnalyzer(const cv::Mat& image, const Rect& roi)
: OcrWithPreprocessImageAnalyzer(image, roi),
m_multi_match_image_analyzer(image, roi)
{}
@@ -59,17 +59,18 @@ const std::vector<asst::TextRect>& asst::OcrWithFlagTemplImageAnalyzer::get_resu
return m_all_result;
}
void asst::OcrWithFlagTemplImageAnalyzer::set_task_info(std::string templ_task_name, std::string ocr_task_name)
void asst::OcrWithFlagTemplImageAnalyzer::set_task_info(const std::string& templ_task_name, const std::string&
ocr_task_name)
{
auto ocr_task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(Task.get(ocr_task_name));
OcrWithPreprocessImageAnalyzer::set_task_info(*ocr_task_ptr);
m_flag_rect_move = ocr_task_ptr->roi;
m_multi_match_image_analyzer.set_task_info(std::move(templ_task_name));
m_multi_match_image_analyzer.set_task_info(templ_task_name);
}
void asst::OcrWithFlagTemplImageAnalyzer::set_flag_rect_move(Rect flag_rect_move)
{
m_flag_rect_move = std::move(flag_rect_move);
m_flag_rect_move = flag_rect_move;
}
std::vector<asst::TextRect>& asst::OcrWithFlagTemplImageAnalyzer::get_result() noexcept

View File

@@ -8,8 +8,8 @@ namespace asst
{
public:
OcrWithFlagTemplImageAnalyzer() = default;
OcrWithFlagTemplImageAnalyzer(const cv::Mat image);
OcrWithFlagTemplImageAnalyzer(const cv::Mat image, const Rect& roi);
OcrWithFlagTemplImageAnalyzer(const cv::Mat& image);
OcrWithFlagTemplImageAnalyzer(const cv::Mat& image, const Rect& roi);
virtual ~OcrWithFlagTemplImageAnalyzer() = default;
virtual void set_image(const cv::Mat image);
@@ -21,7 +21,7 @@ namespace asst
virtual const std::vector<TextRect>& get_result() const noexcept override;
virtual std::vector<TextRect>& get_result() noexcept override;
void set_task_info(std::string templ_task_name, std::string ocr_task_name);
void set_task_info(const std::string& templ_task_name, const std::string& ocr_task_name);
void set_flag_rect_move(Rect flag_rect_move);
protected:

View File

@@ -14,7 +14,7 @@ bool asst::OcrWithPreprocessImageAnalyzer::analyze()
cv::Rect bounding_rect = cv::boundingRect(bin);
bounding_rect.x += m_roi.x;
bounding_rect.y += m_roi.y;
Rect new_roi = utils::make_rect<Rect>(bounding_rect);
auto new_roi = utils::make_rect<Rect>(bounding_rect);
if (new_roi.empty()) {
return false;

View File

@@ -205,7 +205,7 @@ bool ProcessTask::_run()
break;
}
m_status->set_data("Last" + cur_name, time(nullptr));
m_status->set_number("Last" + cur_name, time(nullptr));
// 减少其他任务的执行次数
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效

View File

@@ -1,6 +1,7 @@
#include "ProcessTaskImageAnalyzer.h"
#include <regex>
#include <utility>
#include "AsstUtils.hpp"
#include "Logger.hpp"
@@ -9,16 +10,14 @@
#include "Resource.h"
#include "RuntimeStatus.h"
asst::ProcessTaskImageAnalyzer::ProcessTaskImageAnalyzer(const cv::Mat image, std::vector<std::string> tasks_name)
asst::ProcessTaskImageAnalyzer::ProcessTaskImageAnalyzer(const cv::Mat& image, std::vector<std::string> tasks_name)
: AbstractImageAnalyzer(image),
m_tasks_name(std::move(tasks_name))
{
;
}
{}
asst::ProcessTaskImageAnalyzer::~ProcessTaskImageAnalyzer() = default;
bool asst::ProcessTaskImageAnalyzer::match_analyze(std::shared_ptr<TaskInfo> task_ptr)
bool asst::ProcessTaskImageAnalyzer::match_analyze(const std::shared_ptr<TaskInfo>& task_ptr)
{
if (!m_match_analyzer) {
m_match_analyzer = std::make_unique<MatchImageAnalyzer>(m_image);
@@ -26,7 +25,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_region_of_appeared(Rect());
m_match_analyzer->set_task_info(match_task_ptr);
auto region_opt = m_status->get_region(match_task_ptr->name);
auto region_opt = m_status->get_rect(match_task_ptr->name);
if (region_opt) {
m_match_analyzer->set_region_of_appeared(region_opt.value());
}
@@ -34,13 +33,13 @@ bool asst::ProcessTaskImageAnalyzer::match_analyze(std::shared_ptr<TaskInfo> tas
if (m_match_analyzer->analyze()) {
m_result = match_task_ptr;
m_result_rect = m_match_analyzer->get_result().rect;
m_status->set_region(match_task_ptr->name, m_result_rect);
m_status->set_rect(match_task_ptr->name, m_result_rect);
return true;
}
return false;
}
bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_ptr)
bool asst::ProcessTaskImageAnalyzer::ocr_analyze(const std::shared_ptr<TaskInfo>& task_ptr)
{
std::shared_ptr<OcrTaskInfo> ocr_task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(task_ptr);
@@ -75,7 +74,7 @@ bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_
}
m_ocr_analyzer->set_region_of_appeared(Rect());
m_ocr_analyzer->set_task_info(ocr_task_ptr);
auto region_opt = m_status->get_region(ocr_task_ptr->name);
auto region_opt = m_status->get_rect(ocr_task_ptr->name);
if (region_opt) {
m_ocr_analyzer->set_region_of_appeared(region_opt.value());
}
@@ -88,7 +87,7 @@ bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_
auto& res = ocr_result.front();
m_result = ocr_task_ptr;
m_result_rect = res.rect;
m_status->set_region(ocr_task_ptr->name, m_result_rect);
m_status->set_rect(ocr_task_ptr->name, m_result_rect);
//m_ocr_cache.insert(m_ocr_cache.end(), ocr_result.begin(), ocr_result.end());
Log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found", res.to_string());
}
@@ -149,5 +148,5 @@ void asst::ProcessTaskImageAnalyzer::set_tasks(std::vector<std::string> tasks_na
void asst::ProcessTaskImageAnalyzer::set_status(std::shared_ptr<RuntimeStatus> status) noexcept
{
m_status = status;
m_status = std::move(status);
}

View File

@@ -18,7 +18,7 @@ namespace asst
public:
using AbstractImageAnalyzer::AbstractImageAnalyzer;
ProcessTaskImageAnalyzer(const cv::Mat image, const Rect& roi) = delete;
ProcessTaskImageAnalyzer(const cv::Mat image, std::vector<std::string> tasks_name);
ProcessTaskImageAnalyzer(const cv::Mat& image, std::vector<std::string> tasks_name);
virtual ~ProcessTaskImageAnalyzer();
virtual bool analyze() override;
@@ -49,8 +49,8 @@ namespace asst
{
AbstractImageAnalyzer::set_image(image, roi);
}
bool match_analyze(std::shared_ptr<TaskInfo> task_ptr);
bool ocr_analyze(std::shared_ptr<TaskInfo> task_ptr);
bool match_analyze(const std::shared_ptr<TaskInfo>& task_ptr);
bool ocr_analyze(const std::shared_ptr<TaskInfo>& task_ptr);
void reset() noexcept;
std::unique_ptr<OcrImageAnalyzer> m_ocr_analyzer;

View File

@@ -4,7 +4,7 @@
#include "RecruitCalcTask.h"
#include "ProcessTask.h"
asst::RecruitTask::RecruitTask(AsstCallback callback, void* callback_arg)
asst::RecruitTask::RecruitTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
m_recruit_begin_task_ptr(std::make_shared<ProcessTask>(callback, callback_arg, TaskType)),
m_auto_recruit_task_ptr(std::make_shared<AutoRecruitTask>(callback, callback_arg, TaskType)),

View File

@@ -10,7 +10,7 @@ namespace asst
class RecruitTask final : public PackageTask
{
public:
RecruitTask(AsstCallback callback, void* callback_arg);
RecruitTask(const AsstCallback& callback, void* callback_arg);
virtual ~RecruitTask() = default;
virtual bool set_params(const json::value& params) override;

View File

@@ -13,20 +13,20 @@ bool asst::Resource::load(const std::string& dir)
{
LogTraceFunction;
constexpr static const char* TemplsFilename = "template";
constexpr static const char* GeneralCfgFilename = "config.json";
constexpr static const char* TaskDataFilename = "tasks.json";
constexpr static const char* RoguelikeRecruitCfgFilename = "roguelike_recruit.json";
constexpr static const char* RecruitCfgFilename = "recruit.json";
constexpr static const char* ItemCfgFilename = "item_index.json";
constexpr static const char* InfrastCfgFilename = "infrast.json";
constexpr static const char* InfrastTempls = "template/infrast";
constexpr static auto TemplsFilename = "template";
constexpr static auto GeneralCfgFilename = "config.json";
constexpr static auto TaskDataFilename = "tasks.json";
constexpr static auto RoguelikeRecruitCfgFilename = "roguelike_recruit.json";
constexpr static auto RecruitCfgFilename = "recruit.json";
constexpr static auto ItemCfgFilename = "item_index.json";
constexpr static auto InfrastCfgFilename = "infrast.json";
constexpr static auto InfrastTempls = "template/infrast";
//constexpr static const char* CopilotCfgDirname = "copilot";
constexpr static const char* RoguelikeCfgDirname = "roguelike";
constexpr static const char* OcrResourceFilename = "PaddleOCR";
constexpr static const char* TilesCalcResourceFilename = "Arknights-Tile-Pos";
constexpr static const char* StageDropsCfgFilename = "stages.json";
constexpr static const char* StageDropsTempls = "template/items";
constexpr static auto RoguelikeCfgDirname = "roguelike";
constexpr static auto OcrResourceFilename = "PaddleOCR";
constexpr static auto TilesCalcResourceFilename = "Arknights-Tile-Pos";
constexpr static auto StageDropsCfgFilename = "stages.json";
constexpr static auto StageDropsTempls = "template/items";
bool overload = false;

View File

@@ -253,7 +253,7 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
}
// 将干员拖动到场上
Loc loc = Loc::All;
auto loc = Loc::All;
switch (opt_oper.role) {
case BattleRole::Medic:
case BattleRole::Support:
@@ -296,7 +296,7 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
std::sqrt(
(std::abs(placed_point.x - opt_oper.rect.x) << 1)
+ (std::abs(placed_point.y - opt_oper.rect.y) << 1)));
int duration = static_cast<int>(swipe_oper_task_ptr->pre_delay / 1000.0 * dist); // 随便取的一个系数
int duration = static_cast<int>(swipe_oper_task_ptr->pre_delay / 800.0 * dist); // 随便取的一个系数
m_ctrler->swipe(opt_oper.rect, placed_rect, duration, true, 0);
sleep(use_oper_task_ptr->rear_delay);
@@ -373,7 +373,7 @@ void asst::RoguelikeBattleTaskPlugin::clear()
m_used_tiles.clear();
for (auto& [key, status] : m_restore_status) {
m_status->set_data(key, status);
m_status->set_number(key, status);
}
m_restore_status.clear();
}
@@ -388,8 +388,8 @@ bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
bool used = false;
for (auto& [loc, name] : m_used_tiles) {
std::string status_key = "RoguelikeSkillUsage-" + name;
BattleSkillUsage usage = BattleSkillUsage::Possibly;
auto usage_opt = m_status->get_data(status_key);
auto usage = BattleSkillUsage::Possibly;
auto usage_opt = m_status->get_number(status_key);
if (usage_opt) {
usage = static_cast<BattleSkillUsage>(usage_opt.value());
}
@@ -408,7 +408,7 @@ bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
m_ctrler->click(pos_rect);
used |= ProcessTask(*this, { "BattleSkillReadyOnClick" }).set_task_delay(0).run();
if (usage == BattleSkillUsage::Once) {
m_status->set_data(status_key, static_cast<int64_t>(BattleSkillUsage::OnceUsed));
m_status->set_number(status_key, static_cast<int64_t>(BattleSkillUsage::OnceUsed));
m_restore_status[status_key] = static_cast<int64_t>(BattleSkillUsage::Once);
}
}

View File

@@ -29,7 +29,7 @@ bool asst::RoguelikeFormationImageAnalyzer::analyze()
}
#endif
m_result.emplace_back(std::move(oper));
m_result.emplace_back(oper);
}
return !m_result.empty();

View File

@@ -26,7 +26,7 @@ bool asst::RoguelikeRecruitConfiger::parse(const json::value& json)
for (const auto& role_name : json.at("roles").as_array()) {
std::string str_role = role_name.as_string();
for (const auto& oper_info : json.at(str_role).as_array()) {
std::string name = oper_info.at("name").as_string();;
std::string name = oper_info.at("name").as_string();
RoguelikeOperInfo info;
info.name = name;
info.level = oper_info.at("level").as_integer();

View File

@@ -82,6 +82,6 @@ int asst::RoguelikeRecruitImageAnalyzer::match_level(const Rect& raw_roi)
return 0;
}
std::string level = analyzer.get_result().front().text;;
std::string level = analyzer.get_result().front().text;
return std::stoi(level);
}

View File

@@ -54,7 +54,7 @@ std::string asst::RoguelikeSkillSelectionImageAnalyzer::name_analyze(const Rect&
->replace_map);
if (!analyzer.analyze()) {
return std::string();
return {};
}
return analyzer.get_result().front().text;
}

View File

@@ -42,7 +42,7 @@ bool asst::RoguelikeSkillSelectionTaskPlugin::_run()
}
const auto& oper_info = rg_src.get_oper_info(name);
int index = 0;
BattleSkillUsage usage = BattleSkillUsage::Possibly;
auto usage = BattleSkillUsage::Possibly;
if (skill_vec.size() >= oper_info.skill) {
index = oper_info.skill - 1;
usage = oper_info.skill_usage;
@@ -56,7 +56,7 @@ bool asst::RoguelikeSkillSelectionTaskPlugin::_run()
Log.info(__FUNCTION__, name, " select skill:", index + 1);
m_ctrler->click(skill_vec.at(index));
}
m_status->set_data("RoguelikeSkillUsage-" + name, static_cast<int>(usage));
m_status->set_number("RoguelikeSkillUsage-" + name, static_cast<int>(usage));
}
return true;

View File

@@ -9,7 +9,7 @@
#include "Logger.hpp"
asst::RoguelikeTask::RoguelikeTask(AsstCallback callback, void* callback_arg)
asst::RoguelikeTask::RoguelikeTask(const AsstCallback& callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
m_roguelike_task_ptr(std::make_shared<ProcessTask>(callback, callback_arg, TaskType))
{

View File

@@ -11,7 +11,7 @@ namespace asst
class RoguelikeTask : public PackageTask
{
public:
RoguelikeTask(AsstCallback callback, void* callback_arg);
RoguelikeTask(const AsstCallback& callback, void* callback_arg);
virtual ~RoguelikeTask() = default;
virtual bool set_params(const json::value& params) override;

View File

@@ -1,9 +1,9 @@
#include "RuntimeStatus.h"
std::optional<int64_t> asst::RuntimeStatus::get_data(const std::string& key) const noexcept
std::optional<int64_t> asst::RuntimeStatus::get_number(const std::string& key) const noexcept
{
if (auto iter = m_data.find(key);
iter != m_data.cend()) {
if (auto iter = m_number.find(key);
iter != m_number.cend()) {
return iter->second;
}
else {
@@ -11,20 +11,20 @@ std::optional<int64_t> asst::RuntimeStatus::get_data(const std::string& key) con
}
}
void asst::RuntimeStatus::set_data(std::string key, int64_t value)
void asst::RuntimeStatus::set_number(std::string key, int64_t value)
{
m_data[std::move(key)] = value;
m_number.insert_or_assign(std::move(key), value);
}
void asst::RuntimeStatus::clear_data() noexcept
void asst::RuntimeStatus::clear_number() noexcept
{
m_data.clear();
m_number.clear();
}
std::optional<asst::Rect> asst::RuntimeStatus::get_region(const std::string& key) const noexcept
std::optional<asst::Rect> asst::RuntimeStatus::get_rect(const std::string& key) const noexcept
{
if (auto iter = m_region_of_appeared.find(key);
iter != m_region_of_appeared.cend()) {
if (auto iter = m_rect.find(key);
iter != m_rect.cend()) {
return iter->second;
}
else {
@@ -32,12 +32,33 @@ std::optional<asst::Rect> asst::RuntimeStatus::get_region(const std::string& key
}
}
void asst::RuntimeStatus::set_region(std::string key, Rect rect)
void asst::RuntimeStatus::set_rect(std::string key, Rect rect)
{
m_region_of_appeared[std::move(key)] = std::move(rect);
m_rect.insert_or_assign(std::move(key), rect);
}
void asst::RuntimeStatus::clear_region() noexcept
void asst::RuntimeStatus::clear_rect() noexcept
{
m_region_of_appeared.clear();
m_rect.clear();
}
std::optional<std::string> asst::RuntimeStatus::get_str(const std::string& key) const noexcept
{
if (auto iter = m_string.find(key);
iter != m_string.cend()) {
return iter->second;
}
else {
return std::nullopt;
}
}
void asst::RuntimeStatus::set_str(std::string key, std::string value)
{
m_string.insert_or_assign(std::move(key), std::move(value));
}
void asst::RuntimeStatus::clear_str() noexcept
{
m_string.clear();
}

View File

@@ -17,20 +17,25 @@ namespace asst
RuntimeStatus(RuntimeStatus&& rhs) noexcept = delete;
~RuntimeStatus() = default;
std::optional<int64_t> get_data(const std::string& key) const noexcept;
void set_data(std::string key, int64_t value);
void clear_data() noexcept;
std::optional<int64_t> get_number(const std::string& key) const noexcept;
void set_number(std::string key, int64_t value);
void clear_number() noexcept;
std::optional<Rect> get_region(const std::string& key) const noexcept;
void set_region(std::string key, Rect rect);
void clear_region() noexcept;
std::optional<Rect> get_rect(const std::string& key) const noexcept;
void set_rect(std::string key, Rect rect);
void clear_rect() noexcept;
std::optional<std::string> get_str(const std::string& key) const noexcept;
void set_str(std::string key, std::string value);
void clear_str() noexcept;
RuntimeStatus& operator=(const RuntimeStatus& rhs) = delete;
RuntimeStatus& operator=(RuntimeStatus&& rhs) noexcept = delete;
private:
std::unordered_map<std::string, int64_t> m_data;
std::unordered_map<std::string, Rect> m_region_of_appeared;
std::unordered_map<std::string, int64_t> m_number;
std::unordered_map<std::string, Rect> m_rect;
std::unordered_map<std::string, std::string> m_string;
};
}

View File

@@ -132,7 +132,7 @@ bool asst::StageDropsImageAnalyzer::analyze_difficulty()
};
MatchImageAnalyzer analyzer(m_image);
StageDifficulty matched = StageDifficulty::Normal;
auto matched = StageDifficulty::Normal;
double max_score = 0.0;
#ifdef ASST_DEBUG
@@ -188,7 +188,7 @@ bool asst::StageDropsImageAnalyzer::analyze_drops()
for (int i = 1; i <= size; ++i) {
// 因为第一个黄色的 baseline 是渐变的,圈出来的一般左边会少一段,所以这里直接从右边开始往左推
int x = baseline.x + baseline.width - i * roi.width;
Rect item_roi = Rect(x, baseline.y + roi.y, roi.width, roi.height);
auto item_roi = Rect(x, baseline.y + roi.y, roi.width, roi.height);
std::string item = match_item(item_roi, droptype, size - i, size);
int quantity = match_quantity(item_roi);
@@ -322,7 +322,7 @@ asst::StageDropType asst::StageDropsImageAnalyzer::match_droptype(const Rect& ro
};
MatchImageAnalyzer analyzer(m_image);
StageDropType matched = StageDropType::Unknown;
auto matched = StageDropType::Unknown;
double max_score = 0.0;
#ifdef ASST_DEBUG
@@ -380,7 +380,7 @@ std::string asst::StageDropsImageAnalyzer::match_item(const Rect& roi, StageDrop
}
else {
Log.error("StageDropType::ExpAndLMB, size", size);
return std::string();
return {};
}
break;
case StageDropType::Furniture:
@@ -391,7 +391,7 @@ std::string asst::StageDropsImageAnalyzer::match_item(const Rect& roi, StageDrop
return "4003"; // 合成玉
}
auto match_item_with_templs = [&](std::vector<std::string> templs_list) -> std::string {
auto match_item_with_templs = [&](const std::vector<std::string>& templs_list) -> std::string {
MatchImageAnalyzer analyzer(m_image);
analyzer.set_task_info("StageDrops-Item");
analyzer.set_mask_with_close(true);

View File

@@ -22,9 +22,9 @@ bool asst::StageDropsTaskPlugin::verify(AsstMsg msg, const json::value& details)
}
if (details.at("details").at("task").as_string() == "EndOfAction") {
auto pre_time_opt = m_status->get_data("LastStartButton2");
auto pre_time_opt = m_status->get_number("LastStartButton2");
int64_t pre_start_time = pre_time_opt ? pre_time_opt.value() : 0;
auto pre_reg_time_opt = m_status->get_data("LastRecognizeDrops");
auto pre_reg_time_opt = m_status->get_number("LastRecognizeDrops");
int64_t pre_recognize_time = pre_reg_time_opt ? pre_reg_time_opt.value() : 0;
if (pre_start_time + RecognizationTimeOffset == pre_recognize_time) {
Log.info("Recognization time too close, pass", pre_start_time, pre_recognize_time);
@@ -61,6 +61,12 @@ bool asst::StageDropsTaskPlugin::set_server(std::string server)
return true;
}
bool asst::StageDropsTaskPlugin::set_specify_quantity(std::unordered_map<std::string, int> quantity)
{
m_specify_quantity = std::move(quantity);
return true;
}
bool asst::StageDropsTaskPlugin::_run()
{
LogTraceFunction;
@@ -75,7 +81,9 @@ bool asst::StageDropsTaskPlugin::_run()
}
drop_info_callback();
check_stage_valid();
if (!check_stage_valid() || check_specify_quantity()) {
stop_task();
}
if (m_enable_penguid) {
auto upload_future = std::async(
@@ -111,9 +119,9 @@ bool asst::StageDropsTaskPlugin::recognize_drops()
m_stars = analyzer.get_stars();
m_cur_drops = analyzer.get_drops();
auto last_time_opt = m_status->get_data("LastStartButton2");
auto last_time_opt = m_status->get_number("LastStartButton2");
auto last_time = last_time_opt ? last_time_opt.value() : 0;
m_status->set_data("LastRecognizeDrops", last_time + RecognizationTimeOffset);
m_status->set_number("LastRecognizeDrops", last_time + RecognizationTimeOffset);
return true;
}
@@ -170,7 +178,7 @@ void asst::StageDropsTaskPlugin::set_startbutton_delay()
LogTraceFunction;
if (!m_startbutton_delay_setted) {
auto last_time_opt = m_status->get_data("LastStartButton2");;
auto last_time_opt = m_status->get_number("LastStartButton2");
int64_t pre_start_time = last_time_opt ? last_time_opt.value() : 0;
if (pre_start_time > 0) {
@@ -257,12 +265,26 @@ bool asst::StageDropsTaskPlugin::check_stage_valid()
info["why"] = "EX关卡";
callback(AsstMsg::SubTaskError, info);
m_cast_ptr->set_times_limit("StartButton1", 0)
.set_times_limit("StartButton2", 0)
.set_times_limit("MedicineConfirm", 0)
.set_times_limit("StoneConfirm", 0);
return true;
return false;
}
return true;
}
bool asst::StageDropsTaskPlugin::check_specify_quantity() const
{
for (const auto& [id, quantity] : m_specify_quantity) {
if (auto find_iter = m_drop_stats.find(id);
find_iter != m_drop_stats.end() && find_iter->second >= quantity) {
return true;
}
}
return false;
}
void asst::StageDropsTaskPlugin::stop_task()
{
m_cast_ptr->set_times_limit("StartButton1", 0)
.set_times_limit("StartButton2", 0)
.set_times_limit("MedicineConfirm", 0)
.set_times_limit("StoneConfirm", 0);
}

View File

@@ -24,6 +24,7 @@ namespace asst
bool set_enable_penguid(bool enable);
bool set_penguin_id(std::string id);
bool set_server(std::string server);
bool set_specify_quantity(std::unordered_map<std::string, int> quantity);
private:
virtual bool _run() override;
@@ -31,6 +32,8 @@ namespace asst
void drop_info_callback();
void set_startbutton_delay();
bool check_stage_valid();
bool check_specify_quantity() const;
void stop_task();
void upload_to_penguin();
static constexpr int64_t RecognizationTimeOffset = 20;
@@ -48,5 +51,6 @@ namespace asst
bool m_enable_penguid = false;
std::string m_penguin_id;
std::string m_server = "CN";
std::unordered_map<std::string, int> m_specify_quantity;
};
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include "AbstractTask.h"
#include "AsstTypes.h"
namespace asst
{
class StartGameTask : public AbstractTask
{
public:
using AbstractTask::AbstractTask;
~StartGameTask() override = default;
StartGameTask& set_param(std::string client_type) noexcept;
protected:
bool _run() override;
std::string m_client_type = "";
};
}

View File

@@ -1,10 +1,10 @@
#include "StartGameTask.h"
#include "StartGameTaskPlugin.h"
#include "Controller.h"
using namespace asst;
bool StartGameTask::_run()
bool StartGameTaskPlugin::_run()
{
if (m_client_type.empty()) {
return false;
@@ -12,7 +12,7 @@ bool StartGameTask::_run()
return m_ctrler->start_game(m_client_type).has_value();
}
StartGameTask& StartGameTask::set_param(std::string client_type) noexcept
StartGameTaskPlugin& StartGameTaskPlugin::set_client_type(std::string client_type) noexcept
{
m_client_type = std::move(client_type);
return *this;

View File

@@ -0,0 +1,28 @@
#pragma once
#include "AbstractTaskPlugin.h"
#include "AsstTypes.h"
namespace asst
{
inline static const std::string ClientTypeKey = "client_type";
class StartGameTaskPlugin : public AbstractTaskPlugin
{
public:
using AbstractTaskPlugin::AbstractTaskPlugin;
virtual ~StartGameTaskPlugin() noexcept = default;
virtual bool verify(AsstMsg msg, const json::value& details) const override
{
std::ignore = msg; std::ignore = details; return true;
};
StartGameTaskPlugin& set_client_type(std::string client_type) noexcept;
protected:
bool _run() override;
std::string m_client_type = "";
};
}

View File

@@ -1,10 +1,12 @@
#include "StartUpTask.h"
#include <utility>
#include "ProcessTask.h"
asst::StartUpTask::StartUpTask(AsstCallback callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
m_start_game_task_ptr(std::make_shared<StartGameTask>(m_callback, m_callback_arg, TaskType)),
: PackageTask(std::move(callback), callback_arg, TaskType),
m_start_game_task_ptr(std::make_shared<StartGameTaskPlugin>(m_callback, m_callback_arg, TaskType)),
m_start_up_task_ptr(std::make_shared<ProcessTask>(m_callback, m_callback_arg, TaskType))
{
m_start_up_task_ptr->set_tasks({ "StartUp" })
@@ -16,7 +18,7 @@ asst::StartUpTask::StartUpTask(AsstCallback callback, void* callback_arg)
bool asst::StartUpTask::set_params(const json::value& params)
{
m_start_game_task_ptr->set_param(params.get("client_type", ""))
m_start_game_task_ptr->set_client_type(params.get("client_type", ""))
.set_enable(params.get("start_game_enable", false));
return true;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "PackageTask.h"
#include "StartGameTask.h"
#include "StartGameTaskPlugin.h"
namespace asst
{
@@ -16,7 +16,7 @@ namespace asst
static constexpr const char* TaskType = "StartUp";
private:
std::shared_ptr<StartGameTask> m_start_game_task_ptr = nullptr;
std::shared_ptr<StartGameTaskPlugin> m_start_game_task_ptr = nullptr;
std::shared_ptr<ProcessTask> m_start_up_task_ptr = nullptr;
};
}

View File

@@ -40,7 +40,7 @@ bool asst::TaskData::parse(const json::value& json)
for (const auto& [name, task_json] : json.as_object()) {
std::string algorithm_str = task_json.get("algorithm", "matchtemplate");
std::transform(algorithm_str.begin(), algorithm_str.end(), algorithm_str.begin(), to_lower);
AlgorithmType algorithm = AlgorithmType::Invalid;
auto algorithm = AlgorithmType::Invalid;
if (algorithm_str == "matchtemplate") {
algorithm = AlgorithmType::MatchTemplate;
}

View File

@@ -14,7 +14,7 @@ bool asst::TilePack::load(const std::string& dir)
return false;
}
constexpr static const char* filename = "/levels.json";
constexpr static auto filename = "/levels.json";
try {
m_tile_calculator = std::make_unique<Map::TileCalc>(
@@ -40,7 +40,7 @@ std::unordered_map<asst::Point, asst::TilePack::TileInfo> asst::TilePack::calc(
Log.trace("After tiles cacl run");
if (!ret) {
Log.info("Tiles calc error!");
return std::unordered_map<asst::Point, asst::TilePack::TileInfo>();
return {};
}
std::unordered_map<asst::Point, asst::TilePack::TileInfo> dst;
@@ -62,7 +62,7 @@ std::unordered_map<asst::Point, asst::TilePack::TileInfo> asst::TilePack::calc(
const auto& cv_p = pos[y][x];
const auto& tile = tiles[y][x];
TileKey key = TileKey::Invalid;
auto key = TileKey::Invalid;
if (auto iter = TileKeyMapping.find(tile.tileKey);
iter != TileKeyMapping.cend()) {
key = iter->second;

View File

@@ -1,9 +1,11 @@
#include "VisitTask.h"
#include <utility>
#include "ProcessTask.h"
asst::VisitTask::VisitTask(AsstCallback callback, void* callback_arg)
: PackageTask(callback, callback_arg, TaskType),
: PackageTask(std::move(callback), callback_arg, TaskType),
m_visit_task_ptr(std::make_shared<ProcessTask>(m_callback, m_callback_arg, TaskType))
{
m_visit_task_ptr->set_tasks({ "VisitBegin" });

View File

@@ -170,11 +170,17 @@ namespace MeoAsstGui
break;
case "UnsupportedResolution":
mainModel.AddLog("分辨率过低\n请设置为 720p 或更高", "darkred");
mainModel.AddLog("分辨率过低请设置为 720p 或更高", "darkred");
break;
case "ResolutionError":
mainModel.AddLog("分辨率获取失败\n建议重启电脑\n或更换模拟器后再试", "darkred");
mainModel.AddLog("分辨率获取失败建议重启电脑或更换模拟器后再试", "darkred");
break;
case "Disconnect":
case "CommandExecFailed":
mainModel.AddLog("错误!连接断开!", "darkred");
AsstStop();
break;
}
}
@@ -258,8 +264,8 @@ namespace MeoAsstGui
switch (subTask)
{
case "StartGameTask":
mainModel.AddLog("打开客户端失败,请\n检查配置文件", "darkred");
case "StartGameTaskPlugin":
mainModel.AddLog("打开客户端失败,请检查配置文件", "darkred");
break;
case "AutoRecruitTask":
@@ -376,6 +382,14 @@ namespace MeoAsstGui
case "Roguelike1StageTraderInvestSystemFull":
mainModel.AddLog("投资达到上限", "darkcyan");
break;
case "MaybeCrashAndRestartGame":
mainModel.AddLog("游戏崩溃,重新启动");
break;
case "OfflineConfirm":
mainModel.AddLog("游戏掉线,重新连接");
break;
}
}
}
@@ -670,7 +684,7 @@ namespace MeoAsstGui
return AsstAppendTask(_handle, type, JsonConvert.SerializeObject(task_params)) != 0;
}
public bool AsstAppendFight(string stage, int max_medicine, int max_stone, int max_times)
public bool AsstAppendFight(string stage, int max_medicine, int max_stone, int max_times, string drops_item_id, int drops_item_quantity)
{
var task_params = new JObject();
task_params["stage"] = stage;
@@ -678,7 +692,13 @@ namespace MeoAsstGui
task_params["stone"] = max_stone;
task_params["times"] = max_times;
task_params["report_to_penguin"] = true;
if (drops_item_quantity != 0)
{
task_params["drops"] = new JObject();
task_params["drops"][drops_item_id] = drops_item_quantity;
}
var settings = _container.Get<SettingsViewModel>();
task_params["client_type"] = settings.ClientType;
task_params["penguin_id"] = settings.PenguinId;
task_params["server"] = "CN";
return AsstAppendTaskWithEncoding("Fight", task_params);

View File

@@ -15,6 +15,7 @@
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" VerticalAlignment="Center">
<CheckBox IsChecked="{Binding UseMedicine}" IsHitTestVisible="{Binding Idle}" Content="吃理智药" Margin="10" />
@@ -33,15 +34,26 @@
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="2" VerticalAlignment="Center">
<CheckBox IsChecked="{Binding HasTimesLimited}" IsHitTestVisible="{Binding Idle}" Content="指定次数" Margin="10" />
<TextBox Text="{Binding MaxTimes}" IsReadOnly="{c:Binding Path=!Idle}" TextWrapping="Wrap" Margin="10"
Width="50" InputMethod.IsInputMethodEnabled="False" />
<TextBlock Style="{StaticResource TextBlockDefault}" Text="次" />
<CheckBox IsChecked="{Binding IsSpecifiedDrops}" IsHitTestVisible="{Binding Idle}" Content="指定材料" Margin="10" />
<ComboBox Width="120" Margin="10"
IsHitTestVisible ="{Binding Path=Idle}"
IsEditable="True"
IsTextSearchEnabled ="True"
DisplayMemberPath="Display"
SelectedValuePath="Value"
ItemsSource="{Binding DropsList}"
SelectedValue="{Binding DropsItemId}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="3" VerticalAlignment="Center">
<!--<TextBlock Style="{StaticResource TextBlockDefault}" Margin="35,10,0,10" Text="掉落数 " />-->
<TextBox Text="{Binding DropsQuantity}" IsReadOnly="{c:Binding Path=!Idle}" TextWrapping="Wrap"
Width="95" Margin="98,10,10,10" InputMethod.IsInputMethodEnabled="False" />
<TextBlock Style="{StaticResource TextBlockDefault}" Text="个" />
</StackPanel>
<StackPanel Grid.Row="4" Orientation="Horizontal">
<TextBlock Style="{StaticResource TextBlockDefault}" Margin="10" VerticalAlignment="Center"
Text="关卡选择" />
<ComboBox Width="110" Margin="10"
<TextBlock Style="{StaticResource TextBlockDefault}" Margin="10,10,0,10" VerticalAlignment="Center"
Text="关卡选择" FontSize="14" />
<ComboBox Width="120" Margin="31,10,10,10"
IsHitTestVisible ="{Binding Path=Idle}"
ItemsSource="{Binding StageList}"
DisplayMemberPath="Display"

View File

@@ -9,7 +9,11 @@
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using System.IO;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace MeoAsstGui
{

View File

@@ -134,7 +134,7 @@ namespace MeoAsstGui
new CombData { Display = "刷源石锭投资,投资过后退出", Value = "2" }
};
ServerTypeList = new List<CombData>
ClientTypeList = new List<CombData>
{
new CombData { Display = "官服", Value = "Official" },
new CombData { Display = "Bilibili服", Value = "Bilibili" }
@@ -177,15 +177,15 @@ namespace MeoAsstGui
}
}
private string _serverType = ViewStatusStorage.Get("Start.ServerType", "Official");
private string _clientType = ViewStatusStorage.Get("Start.ClientType", "Official");
public string ServerType
public string ClientType
{
get { return _serverType; }
get { return _clientType; }
set
{
SetAndNotify(ref _serverType, value);
ViewStatusStorage.Set("Start.ServerType", value);
SetAndNotify(ref _clientType, value);
ViewStatusStorage.Set("Start.ClientType", value);
}
}
@@ -195,7 +195,7 @@ namespace MeoAsstGui
public List<CombData> UsesOfDronesList { get; set; }
public List<CombData> RoguelikeModeList { get; set; }
public List<CombData> ServerTypeList { get; set; }
public List<CombData> ClientTypeList { get; set; }
public List<CombData> ConnectConfigList { get; set; }
private int _dormThreshold = Convert.ToInt32(ViewStatusStorage.Get("Infrast.DormThreshold", "30"));

View File

@@ -12,8 +12,12 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stylet;
using StyletIoC;
@@ -118,7 +122,7 @@ namespace MeoAsstGui
{
new CombData { Display = "当前关卡", Value = string.Empty },
new CombData { Display = "上次作战", Value = "LastBattle" },
// “覆潮之下” 活动关卡 //复刻活动,结束后直接删除
new CombData { Display = "SV-9", Value = "SV-9" },
new CombData { Display = "SV-8", Value = "SV-8" },
@@ -129,7 +133,6 @@ namespace MeoAsstGui
new CombData { Display = "经验-6/5", Value = "LS-6" },
new CombData { Display = "红票-5", Value = "AP-5" },
new CombData { Display = "技能-5", Value = "CA-5" },
new CombData { Display = "剿灭作战", Value = "Annihilation" },
// “愚人号” 活动关卡
//new CombData { Display = "SN-8", Value = "SN-8" },
@@ -139,7 +142,9 @@ namespace MeoAsstGui
//// “风雪过境” 活动关卡
//new CombData { Display = "BI-7", Value = "BI-7" },
//new CombData { Display = "BI-8", Value = "BI-8" }
};
};
InitDrops();
UpdateDatePrompt();
}
@@ -330,19 +335,21 @@ namespace MeoAsstGui
ViewStatusStorage.Set("MainFunction.UseMedicine.Quantity", MedicineNumber);
// 吃石头颗数
ViewStatusStorage.Set("MainFunction.UseStone.Quantity", StoneNumber);
// 指定刷关次数
ViewStatusStorage.Set("MainFunction.TimesLimited.Quantity", MaxTimes);
// 指定掉落材料
ViewStatusStorage.Set("MainFunction.Drops.ItemId", DropsItemId);
// 指定掉落材料数量
ViewStatusStorage.Set("MainFunction.Drops.Quantity", DropsQuantity);
}
private bool appendStart()
{
var settings = _container.Get<SettingsViewModel>();
var asstProxy = _container.Get<AsstProxy>();
var mode = settings.ServerType;
var mode = settings.ClientType;
var enable = settings.StartGameEnable;
return asstProxy.AsstAppendStartUp(mode, enable);
}
private bool appendFight()
{
int medicine = 0;
@@ -361,17 +368,25 @@ namespace MeoAsstGui
stone = 0;
}
}
int times = int.MaxValue;
if (HasTimesLimited)
//int times = int.MaxValue;
//if (IsSpecifiedDrops)
//{
// if (!int.TryParse(DropsQuantity, out times))
// {
// times = 0;
// }
//}
int drops_quantity = 0;
if (IsSpecifiedDrops)
{
if (!int.TryParse(MaxTimes, out times))
if (!int.TryParse(DropsQuantity, out drops_quantity))
{
times = 0;
drops_quantity = 0;
}
}
var asstProxy = _container.Get<AsstProxy>();
return asstProxy.AsstAppendFight(Stage, medicine, stone, times);
return asstProxy.AsstAppendFight(Stage, medicine, stone, int.MaxValue, DropsItemId, drops_quantity);
}
private bool appendInfrast()
@@ -590,25 +605,71 @@ namespace MeoAsstGui
}
}
private bool _hasTimesLimited;
private bool _isSpecifiedDrops = System.Convert.ToBoolean(ViewStatusStorage.Get("MainFunction.Drops.Enable", bool.FalseString));
public bool HasTimesLimited
public bool IsSpecifiedDrops
{
get { return _hasTimesLimited; }
get { return _isSpecifiedDrops; }
set
{
SetAndNotify(ref _hasTimesLimited, value);
SetAndNotify(ref _isSpecifiedDrops, value);
ViewStatusStorage.Set("MainFunction.Drops.Enable", value.ToString());
}
}
private string _maxTimes = ViewStatusStorage.Get("MainFunction.TimesLimited.Quantity", "5");
private static readonly string _DropsFilename = System.Environment.CurrentDirectory + "\\resource\\item_index.json";
public string MaxTimes
public List<CombData> AllDrops { get; set; } = new List<CombData>();
private void InitDrops()
{
get { return _maxTimes; }
string jsonStr = File.ReadAllText(_DropsFilename);
var reader = (JObject)JsonConvert.DeserializeObject(jsonStr);
foreach (var item in reader)
{
var val = item.Key;
if (!int.TryParse(val, out _)) // 不是数字的东西都是正常关卡不会掉的(大概吧)
{
continue;
}
var dis = item.Value["name"].ToString();
if (dis.EndsWith("双芯片") || dis.EndsWith("寻访凭证") || dis.EndsWith("加固建材")
|| dis.EndsWith("许可") || dis == "资质凭证" || dis == "高级凭证" || dis == "演习券"
|| dis.Contains("源石") || dis == "D32钢" || dis == "双极纳米片" || dis == "聚合剂" || dis == "晶体电子单元")
{
continue;
}
AllDrops.Add(new CombData { Display = dis, Value = val });
}
AllDrops.Sort((a, b) =>
{
return a.Value.CompareTo(b.Value);
});
DropsList = new ObservableCollection<CombData>(AllDrops);
}
public ObservableCollection<CombData> DropsList { get; set; }
private string _dropsItemId = ViewStatusStorage.Get("MainFunction.Drops.ItemId", "0");
public string DropsItemId
{
get { return _dropsItemId; }
set
{
SetAndNotify(ref _maxTimes, value);
SetAndNotify(ref _dropsItemId, value);
}
}
private string _dropsQuantity = ViewStatusStorage.Get("MainFunction.Drops.Quantity", "5");
public string DropsQuantity
{
get { return _dropsQuantity; }
set
{
SetAndNotify(ref _dropsQuantity, value);
}
}
}