From b3564e7f0d744ab48099be03dab8500be5ca45a4 Mon Sep 17 00:00:00 2001 From: MistEO Date: Thu, 7 Oct 2021 19:24:40 +0800 Subject: [PATCH] =?UTF-8?q?refactor.=E6=95=B4=E4=BD=93=E9=87=8D=E6=9E=84?= =?UTF-8?q?=EF=BC=8COOP=E5=8C=96=E3=80=81=E7=83=AD=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=8C=96=EF=BC=9B=E6=9A=82=E6=97=B6=E5=88=A0=E9=99=A4=E5=9F=BA?= =?UTF-8?q?=E5=BB=BA=E9=83=A8=E5=88=86=E9=80=BB=E8=BE=91=EF=BC=9B=E5=88=9D?= =?UTF-8?q?=E6=AD=A5=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/AsstCaller.h | 6 +- resource/config.json | 75 +- resource/infrast.json | 1043 ----------------- resource/tasks.json | 174 +-- resource/template/{special => }/02.png | Bin resource/template/{special => }/03.png | Bin resource/template/{special => }/04.png | Bin resource/template/{special => }/05.png | Bin .../{special => }/AddOperator-Trade.png | Bin resource/template/{special => }/BuyIt.png | Bin .../{special => }/ContactSuspended.png | Bin .../template/{special => }/CreditPoint.png | Bin resource/template/{special => }/DogFood.png | Bin resource/template/{special => }/Dorm.png | Bin .../template/{special => }/DormConfirm.png | Bin resource/template/{special => }/DormMini.png | Bin resource/template/{special => }/Elite1.png | Bin resource/template/{special => }/Elite2.png | Bin resource/template/{special => }/GoIn.png | Bin resource/template/Listless.png | Bin 20961 -> 6976 bytes resource/template/{special => }/Office.png | Bin .../template/{special => }/OfficeMini.png | Bin resource/template/{special => }/OnShift.png | Bin resource/template/Power.png | Bin 26328 -> 21108 bytes resource/template/{special => }/PowerMini.png | Bin resource/template/{special => }/PureGold.png | Bin resource/template/{special => }/Resting.png | Bin .../template/{special => }/SmileyAtRest.png | Bin .../template/{special => }/SmileyAtWork.png | Bin resource/template/{special => }/SoldOut.png | Bin .../{special => }/StationInfoSelected.png | Bin .../template/{special => }/TradeMoney.png | Bin .../template/{special => }/UnlockClues.png | Bin resource/template/special/AddOperator.png | Bin 2261 -> 0 bytes resource/template/special/ClearSelection.png | Bin 7429 -> 0 bytes resource/template/special/Listless.png | Bin 6976 -> 0 bytes resource/template/special/Power.png | Bin 21108 -> 0 bytes src/MeoAssistance/AbstractConfiger.cpp | 13 +- src/MeoAssistance/AbstractConfiger.h | 18 +- src/MeoAssistance/AbstractImageAnalyzer.cpp | 24 + src/MeoAssistance/AbstractImageAnalyzer.h | 30 + src/MeoAssistance/AbstractResource.cpp | 1 + src/MeoAssistance/AbstractResource.h | 27 + src/MeoAssistance/AbstractTask.cpp | 26 +- src/MeoAssistance/AbstractTask.h | 8 - src/MeoAssistance/Assistance.cpp | 492 +++----- src/MeoAssistance/Assistance.h | 9 +- src/MeoAssistance/AsstCaller.cpp | 34 +- src/MeoAssistance/AsstDef.h | 237 ++-- src/MeoAssistance/Configer.h | 45 - .../{WinMacro.cpp => Controller.cpp} | 104 +- .../{WinMacro.h => Controller.h} | 21 +- src/MeoAssistance/CreditShopImageAnalyzer.cpp | 123 ++ src/MeoAssistance/CreditShopImageAnalyzer.h | 25 + src/MeoAssistance/CreditShoppingTask.cpp | 131 +-- src/MeoAssistance/CreditShoppingTask.h | 7 +- .../{Configer.cpp => GeneralConfiger.cpp} | 28 +- src/MeoAssistance/GeneralConfiger.h | 73 ++ src/MeoAssistance/Identify.cpp | 349 ------ src/MeoAssistance/Identify.h | 72 -- src/MeoAssistance/IdentifyOperTask.cpp | 172 --- src/MeoAssistance/IdentifyOperTask.h | 22 - src/MeoAssistance/InfrastAbstractTask.cpp | 264 ----- src/MeoAssistance/InfrastAbstractTask.h | 41 - src/MeoAssistance/InfrastConfiger.cpp | 49 - src/MeoAssistance/InfrastConfiger.h | 38 - src/MeoAssistance/InfrastDormTask.cpp | 366 ------ src/MeoAssistance/InfrastDormTask.h | 45 - src/MeoAssistance/InfrastOfficeTask.cpp | 72 -- src/MeoAssistance/InfrastOfficeTask.h | 18 - src/MeoAssistance/InfrastPowerTask.cpp | 114 -- src/MeoAssistance/InfrastPowerTask.h | 22 - src/MeoAssistance/InfrastProductionTask.cpp | 283 ----- src/MeoAssistance/InfrastProductionTask.h | 34 - src/MeoAssistance/ItemConfiger.cpp | 5 - src/MeoAssistance/ItemConfiger.h | 54 +- src/MeoAssistance/Logger.hpp | 35 +- src/MeoAssistance/MatchImageAnalyzer.cpp | 92 ++ src/MeoAssistance/MatchImageAnalyzer.h | 43 + src/MeoAssistance/MeoAssistance.vcxproj | 429 +++---- .../MeoAssistance.vcxproj.filters | 467 ++++---- src/MeoAssistance/MultiMatchImageAnalyzer.cpp | 63 + src/MeoAssistance/MultiMatchImageAnalyzer.h | 31 + src/MeoAssistance/OcrAbstractTask.cpp | 43 - src/MeoAssistance/OcrAbstractTask.h | 99 -- src/MeoAssistance/OcrImageAnalyzer.cpp | 57 + src/MeoAssistance/OcrImageAnalyzer.h | 56 + src/MeoAssistance/OcrPack.cpp | 80 ++ src/MeoAssistance/OcrPack.h | 31 + src/MeoAssistance/PenguinPack.cpp | 105 ++ src/MeoAssistance/PenguinPack.h | 26 + src/MeoAssistance/PenguinUploader.cpp | 12 +- src/MeoAssistance/PenguinUploader.h | 2 +- src/MeoAssistance/ProcessTask.cpp | 272 +---- src/MeoAssistance/ProcessTask.h | 10 +- .../ProcessTaskImageAnalyzer.cpp | 142 +++ src/MeoAssistance/ProcessTaskImageAnalyzer.h | 46 + src/MeoAssistance/RecruitConfiger.cpp | 6 - src/MeoAssistance/RecruitConfiger.h | 45 +- src/MeoAssistance/RecruitImageAnalyzer.cpp | 68 ++ src/MeoAssistance/RecruitImageAnalyzer.h | 28 + .../{OpenRecruitTask.cpp => RecruitTask.cpp} | 73 +- .../{OpenRecruitTask.h => RecruitTask.h} | 8 +- src/MeoAssistance/Resource.cpp | 71 ++ src/MeoAssistance/Resource.h | 92 ++ src/MeoAssistance/ScreenCaptureTask.cpp | 13 +- src/MeoAssistance/Task.h | 7 +- src/MeoAssistance/TaskConfiger.h | 26 - .../{TaskConfiger.cpp => TaskData.cpp} | 161 ++- src/MeoAssistance/TaskData.h | 30 + src/MeoAssistance/TemplResource.cpp | 69 ++ src/MeoAssistance/TemplResource.h | 34 + src/MeoAssistance/UserConfiger.cpp | 14 +- src/MeoAssistance/UserConfiger.h | 22 +- src/MeoAsstGui/Helper/AsstProxy.cs | 10 +- src/MeoAsstGui/ViewModels/RecruitViewModel.cs | 2 +- tools/TestCaller/main.cpp | 2 +- 117 files changed, 2756 insertions(+), 4960 deletions(-) delete mode 100644 resource/infrast.json rename resource/template/{special => }/02.png (100%) rename resource/template/{special => }/03.png (100%) rename resource/template/{special => }/04.png (100%) rename resource/template/{special => }/05.png (100%) rename resource/template/{special => }/AddOperator-Trade.png (100%) rename resource/template/{special => }/BuyIt.png (100%) rename resource/template/{special => }/ContactSuspended.png (100%) rename resource/template/{special => }/CreditPoint.png (100%) rename resource/template/{special => }/DogFood.png (100%) rename resource/template/{special => }/Dorm.png (100%) rename resource/template/{special => }/DormConfirm.png (100%) rename resource/template/{special => }/DormMini.png (100%) rename resource/template/{special => }/Elite1.png (100%) rename resource/template/{special => }/Elite2.png (100%) rename resource/template/{special => }/GoIn.png (100%) rename resource/template/{special => }/Office.png (100%) rename resource/template/{special => }/OfficeMini.png (100%) rename resource/template/{special => }/OnShift.png (100%) rename resource/template/{special => }/PowerMini.png (100%) rename resource/template/{special => }/PureGold.png (100%) rename resource/template/{special => }/Resting.png (100%) rename resource/template/{special => }/SmileyAtRest.png (100%) rename resource/template/{special => }/SmileyAtWork.png (100%) rename resource/template/{special => }/SoldOut.png (100%) rename resource/template/{special => }/StationInfoSelected.png (100%) rename resource/template/{special => }/TradeMoney.png (100%) rename resource/template/{special => }/UnlockClues.png (100%) delete mode 100644 resource/template/special/AddOperator.png delete mode 100644 resource/template/special/ClearSelection.png delete mode 100644 resource/template/special/Listless.png delete mode 100644 resource/template/special/Power.png create mode 100644 src/MeoAssistance/AbstractImageAnalyzer.cpp create mode 100644 src/MeoAssistance/AbstractImageAnalyzer.h create mode 100644 src/MeoAssistance/AbstractResource.cpp create mode 100644 src/MeoAssistance/AbstractResource.h delete mode 100644 src/MeoAssistance/Configer.h rename src/MeoAssistance/{WinMacro.cpp => Controller.cpp} (81%) rename src/MeoAssistance/{WinMacro.h => Controller.h} (87%) create mode 100644 src/MeoAssistance/CreditShopImageAnalyzer.cpp create mode 100644 src/MeoAssistance/CreditShopImageAnalyzer.h rename src/MeoAssistance/{Configer.cpp => GeneralConfiger.cpp} (71%) create mode 100644 src/MeoAssistance/GeneralConfiger.h delete mode 100644 src/MeoAssistance/Identify.cpp delete mode 100644 src/MeoAssistance/Identify.h delete mode 100644 src/MeoAssistance/IdentifyOperTask.cpp delete mode 100644 src/MeoAssistance/IdentifyOperTask.h delete mode 100644 src/MeoAssistance/InfrastAbstractTask.cpp delete mode 100644 src/MeoAssistance/InfrastAbstractTask.h delete mode 100644 src/MeoAssistance/InfrastConfiger.cpp delete mode 100644 src/MeoAssistance/InfrastConfiger.h delete mode 100644 src/MeoAssistance/InfrastDormTask.cpp delete mode 100644 src/MeoAssistance/InfrastDormTask.h delete mode 100644 src/MeoAssistance/InfrastOfficeTask.cpp delete mode 100644 src/MeoAssistance/InfrastOfficeTask.h delete mode 100644 src/MeoAssistance/InfrastPowerTask.cpp delete mode 100644 src/MeoAssistance/InfrastPowerTask.h delete mode 100644 src/MeoAssistance/InfrastProductionTask.cpp delete mode 100644 src/MeoAssistance/InfrastProductionTask.h create mode 100644 src/MeoAssistance/MatchImageAnalyzer.cpp create mode 100644 src/MeoAssistance/MatchImageAnalyzer.h create mode 100644 src/MeoAssistance/MultiMatchImageAnalyzer.cpp create mode 100644 src/MeoAssistance/MultiMatchImageAnalyzer.h delete mode 100644 src/MeoAssistance/OcrAbstractTask.cpp delete mode 100644 src/MeoAssistance/OcrAbstractTask.h create mode 100644 src/MeoAssistance/OcrImageAnalyzer.cpp create mode 100644 src/MeoAssistance/OcrImageAnalyzer.h create mode 100644 src/MeoAssistance/OcrPack.cpp create mode 100644 src/MeoAssistance/OcrPack.h create mode 100644 src/MeoAssistance/PenguinPack.cpp create mode 100644 src/MeoAssistance/PenguinPack.h create mode 100644 src/MeoAssistance/ProcessTaskImageAnalyzer.cpp create mode 100644 src/MeoAssistance/ProcessTaskImageAnalyzer.h create mode 100644 src/MeoAssistance/RecruitImageAnalyzer.cpp create mode 100644 src/MeoAssistance/RecruitImageAnalyzer.h rename src/MeoAssistance/{OpenRecruitTask.cpp => RecruitTask.cpp} (76%) rename src/MeoAssistance/{OpenRecruitTask.h => RecruitTask.h} (61%) create mode 100644 src/MeoAssistance/Resource.cpp create mode 100644 src/MeoAssistance/Resource.h delete mode 100644 src/MeoAssistance/TaskConfiger.h rename src/MeoAssistance/{TaskConfiger.cpp => TaskData.cpp} (51%) create mode 100644 src/MeoAssistance/TaskData.h create mode 100644 src/MeoAssistance/TemplResource.cpp create mode 100644 src/MeoAssistance/TemplResource.h diff --git a/include/AsstCaller.h b/include/AsstCaller.h index 4ecc59743a..51216a1f3e 100644 --- a/include/AsstCaller.h +++ b/include/AsstCaller.h @@ -19,9 +19,9 @@ extern "C" { bool MEOAPI AsstStartSanity(void* p_asst); bool MEOAPI AsstStartVisit(void* p_asst, bool with_shopping); bool MEOAPI AsstStartProcessTask(void* p_asst, const char* task); - bool MEOAPI AsstStartOpenRecruit(void* p_asst, const int required_level[], int required_len, bool set_time); - bool MEOAPI AsstStartIndertifyOpers(void* p_asst); - bool MEOAPI AsstStartInfrast(void* p_asst); + bool MEOAPI AsstStartRecruiting(void* p_asst, const int required_level[], int required_len, bool set_time); + //bool MEOAPI AsstStartIndertifyOpers(void* p_asst); + //bool MEOAPI AsstStartInfrast(void* p_asst); bool MEOAPI AsstStartDebugTask(void* p_asst); void MEOAPI AsstStop(void* p_asst); diff --git a/resource/config.json b/resource/config.json index 145a440a0a..911ceaf2d8 100644 --- a/resource/config.json +++ b/resource/config.json @@ -21,10 +21,6 @@ "ocrThreadNumber": 4, "ocrThreadNumber_Doc": "文字识别库OcrLite的线程数量,理论上开的高点识别会快点,但是会更卡。默认4" }, - "infrastOptions": { - "dormThreshold": 0.3, - "dormThreshold_Doc": "宿舍心情百分比阈值,心情百分比小于该值的干员会被放进宿舍。取值范围(0, 1.0),默认0.3" - }, "emulator_Doc": "下面的和模拟器窗口捕获逻辑有关,不需要修改", "emulator": { "BlueStacks": { @@ -150,74 +146,5 @@ "screencap": "[Adb] exec-out screencap -p" } } - }, - "ocrReplace_Doc": "下面为常见的文字识别错误纠正,不需要修改", - "recruitOcrReplace": [ - [ - "沮击", - "狙击" - ], - [ - "犯击", - "狙击" - ], - [ - "泪击", - "狙击" - ], - [ - "都出", - "输出" - ], - [ - "都乐", - "输出" - ], - [ - "抓出", - "输出" - ], - [ - "都任", - "输出" - ], - [ - "新于", - "新手" - ], - [ - "防拍", - "防护" - ] - ], - "infrastOcrReplace": [ - [ - "蛇居箱", - "蛇屠箱" - ], - [ - "叫米", - "古米" - ], - [ - "节草", - "苇草" - ], - [ - "粹兰", - "梓兰" - ], - [ - "艾丝菜尔", - "艾丝黛尔" - ], - [ - "艾丝莱尔", - "艾丝黛尔" - ], - [ - ",斑点", - "斑点" - ] - ] + } } \ No newline at end of file diff --git a/resource/infrast.json b/resource/infrast.json deleted file mode 100644 index e423d1b324..0000000000 --- a/resource/infrast.json +++ /dev/null @@ -1,1043 +0,0 @@ -{ - "infrast": [ - { - "facility": "DogFood", - "Doc": "制造站-狗粮", - "combs": [ - { - "opers": [ - { - "name": "刻俄柏", - "elite": 2 - }, - { - "name": "稀音", - "elite": 2 - }, - { - "name": "红云", - "elite": 1 - } - ], - "efficiency": 105 - }, - { - "opers": [ - { - "name": "贝娜", - "elite": 0 - }, - { - "name": "泡泡", - "elite": 1 - }, - { - "name": "火神", - "elite": 2 - } - ], - "efficiency": 93 - }, - { - "opers": [ - { - "name": "温蒂", - "elite": 2 - }, - { - "name": "异客", - "elite": 2 - }, - { - "name": "森蚺", - "elite": 2 - } - ], - "efficiency": 90 - }, - { - "opers": [ - { - "name": "槐琥", - "elite": 2 - } - ], - "efficiency": 40 - }, - { - "opers": [ - { - "name": "食铁兽", - "elite": 2 - } - ], - "efficiency": 35 - }, - { - "opers": [ - { - "name": "断罪者", - "elite": 2 - } - ], - "efficiency": 35 - }, - { - "opers": [ - { - "name": "白雪", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "霜叶", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "红豆", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "Castle-3", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "梅尔", - "elite": 2 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "帕拉斯", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "水月", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "白面鸮", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "赫默", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "杰西卡", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "史都华德", - "elite": 1 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "香草", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "调香师", - "elite": 1 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "石棉", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "泡普卡", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "芬", - "elite": 0 - } - ], - "efficiency": 23 - }, - { - "opers": [ - { - "name": "克洛斯", - "elite": 0 - } - ], - "efficiency": 20 - }, - { - "opers": [ - { - "name": "豆苗", - "elite": 0 - } - ], - "efficiency": 15 - }, - { - "opers": [ - { - "name": "流星", - "elite": 0 - } - ], - "efficiency": 15 - }, - { - "opers": [ - { - "name": "夜刀", - "elite": 0 - } - ], - "efficiency": 15 - }, - { - "opers": [ - { - "name": "蛇屠箱", - "elite": 0 - } - ], - "efficiency": 10 - }, - { - "opers": [ - { - "name": "黑角", - "elite": 0 - } - ], - "efficiency": 10 - }, - { - "opers": [ - { - "name": "卡缇", - "elite": 0 - } - ], - "efficiency": 10 - }, - { - "opers": [ - { - "name": "米格鲁", - "elite": 0 - } - ], - "efficiency": 10 - } - ], - "endFlag": [ - "安洁莉娜", - "嵯峨", - "风笛", - "赫拉格", - "棘刺", - "假日威龙陈", - "卡涅利安", - "傀影", - "铃兰", - "塞雷娅", - "闪灵", - "史尔特尔", - "斯卡蒂", - "夜莺", - "推进之王", - "星熊", - "银灰", - "浊心斯卡蒂", - "歌蕾蒂娅", - "莫斯提马", - "早露", - "瑕光", - "阿米娅" - ], - "endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名" - }, - { - "facility": "PureGold", - "Doc": "制造站-赤金", - "combs": [ - { - "opers": [ - { - "name": "贝娜", - "elite": 0 - }, - { - "name": "泡泡", - "elite": 1 - }, - { - "name": "火神", - "elite": 2 - } - ], - "efficiency": 93 - }, - { - "opers": [ - { - "name": "温蒂", - "elite": 2 - }, - { - "name": "异客", - "elite": 2 - }, - { - "name": "森蚺", - "elite": 2 - } - ], - "efficiency": 90 - }, - { - "opers": [ - { - "name": "砾", - "elite": 2 - } - ], - "efficiency": 35 - }, - { - "opers": [ - { - "name": "斑点", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "夜烟", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "槐琥", - "elite": 2 - } - ], - "efficiency": 40 - }, - { - "opers": [ - { - "name": "梅尔", - "elite": 2 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "水月", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "白面鸮", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "赫默", - "elite": 2 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "杰西卡", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "史都华德", - "elite": 1 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "香草", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "调香师", - "elite": 1 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "石棉", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "泡普卡", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "芬", - "elite": 0 - } - ], - "efficiency": 23 - }, - { - "opers": [ - { - "name": "克洛斯", - "elite": 0 - } - ], - "efficiency": 20 - }, - { - "opers": [ - { - "name": "豆苗", - "elite": 0 - } - ], - "efficiency": 15 - }, - { - "opers": [ - { - "name": "流星", - "elite": 0 - } - ], - "efficiency": 15 - }, - { - "opers": [ - { - "name": "夜刀", - "elite": 0 - } - ], - "efficiency": 15 - }, - { - "opers": [ - { - "name": "蛇屠箱", - "elite": 0 - } - ], - "efficiency": 10 - }, - { - "opers": [ - { - "name": "黑角", - "elite": 0 - } - ], - "efficiency": 10 - }, - { - "opers": [ - { - "name": "卡缇", - "elite": 0 - } - ], - "efficiency": 10 - }, - { - "opers": [ - { - "name": "米格鲁", - "elite": 0 - } - ], - "efficiency": 10 - } - ], - "endFlag": [ - "安洁莉娜", - "嵯峨", - "风笛", - "赫拉格", - "棘刺", - "假日威龙陈", - "卡涅利安", - "傀影", - "铃兰", - "塞雷娅", - "闪灵", - "史尔特尔", - "斯卡蒂", - "夜莺", - "推进之王", - "星熊", - "银灰", - "浊心斯卡蒂", - "歌蕾蒂娅", - "莫斯提马", - "早露", - "瑕光", - "阿米娅" - ], - "endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名" - }, - { - "facility": "TradeMoney", - "Doc": "贸易站-龙门币", - "combs": [ - { - "opers": [ - { - "name": "巫恋", - "elite": 2 - }, - { - "name": "龙舌兰", - "elite": 2 - }, - { - "name": "卡夫卡", - "elite": 2 - } - ], - "efficiency": 150, - "Doc": "这三个有效率加成,不知道怎么算的,反正很高,先按150写" - }, - { - "opers": [ - { - "name": "巫恋", - "elite": 2 - }, - { - "name": "龙舌兰", - "elite": 2 - }, - { - "name": "柏喙", - "elite": 2 - } - ], - "efficiency": 150, - "Doc": "这三个有效率加成,不知道怎么算的,反正很高,先按150写" - }, - { - "opers": [ - { - "name": "能天使", - "elite": 2 - }, - { - "name": "拉普兰德", - "elite": 2 - }, - { - "name": "德克萨斯", - "elite": 2 - } - ], - "efficiency": 100 - }, - { - "opers": [ - { - "name": "空弦", - "elite": 2 - } - ], - "efficiency": 40 - }, - { - "opers": [ - { - "name": "能天使", - "elite": 2 - } - ], - "efficiency": 35 - }, - { - "opers": [ - { - "name": "雪雉", - "elite": 2 - } - ], - "efficiency": 35 - }, - { - "opers": [ - { - "name": "古米", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "空爆", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "月见夜", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "空", - "elite": 2 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "安比尔", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "缠丸", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "芬", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "芬", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "慕斯", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "夜刀", - "elite": 0 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "夜烟", - "elite": 1 - } - ], - "efficiency": 30 - }, - { - "opers": [ - { - "name": "玫兰莎", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "梓兰", - "elite": 0 - } - ], - "efficiency": 25 - }, - { - "opers": [ - { - "name": "远山", - "elite": 0 - } - ], - "efficiency": 25 - } - ], - "endFlag": [ - "安洁莉娜", - "嵯峨", - "风笛", - "赫拉格", - "棘刺", - "假日威龙陈", - "卡涅利安", - "傀影", - "铃兰", - "塞雷娅", - "闪灵", - "史尔特尔", - "斯卡蒂", - "夜莺", - "推进之王", - "星熊", - "浊心斯卡蒂", - "歌蕾蒂娅", - "莫斯提马", - "早露", - "瑕光", - "阿米娅" - ], - "endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名" - } - ], - "allNames": [ - "桃金娘", - "食铁兽", - "断罪者", - "白雪", - "Castle-3", - "霜叶", - "红豆", - "帕拉斯", - "砾", - "夜烟", - "斑点", - "清流", - "迷迭香", - "槐琥", - "梅尔", - "赫默", - "白面鸮", - "水月", - "调香师", - "杰西卡", - "史都华德", - "香草", - "石棉", - "泡普卡", - "芬", - "克洛丝", - "豆苗", - "流星", - "夜刀", - "温蒂", - "森蚺", - "异客", - "蛇屠箱", - "黑角", - "卡缇", - "米格鲁", - "火神", - "贝娜", - "卡达", - "稀音", - "泡泡", - "刻俄柏", - "红云", - "艾雅法拉", - "炎熔", - "月见夜", - "安洁莉娜", - "夜莺", - "嵯峨", - "风笛", - "赫拉格", - "黑", - "煌", - "棘刺", - "卡涅利安", - "傀影", - "铃兰", - "泥岩", - "塞雷娅", - "山", - "史尔特尔", - "闪灵", - "斯卡蒂", - "推进之王", - "星熊", - "银灰", - "浊心斯卡蒂", - "W", - "歌蕾蒂娅", - "假日威龙陈", - "空弦", - "麦哲伦", - "莫斯提马", - "年", - "早露", - "阿", - "夕", - "瑕光", - "阿米娅", - "幽灵鲨", - "白金", - "柏喙", - "鞭刃", - "华法琳", - "极境", - "贾维", - "空", - "临光", - "狮蝎", - "星极", - "陨星", - "真理", - "初雪", - "格劳克斯", - "凛冬", - "送葬人", - "苇草", - "爱丽丝", - "蓝毒", - "天火", - "锡兰", - "罗宾", - "特米米", - "图耶", - "雪雉", - "炎狱炎熔", - "羽毛笔", - "嘉维尔", - "末药", - "安比尔", - "暗索", - "缠丸", - "古米", - "坚雷", - "孑", - "刻刀", - "猎蜂", - "梅", - "苏苏洛", - "讯使", - "宴", - "远山", - "玫兰莎", - "空爆", - "安德切尔", - "安赛尔", - "芙蓉", - "翎羽", - "梓兰", - "安哲拉", - "奥斯塔", - "拜松", - "暴行", - "暴雨", - "薄绿", - "布洛卡", - "赤冬", - "断崖", - "芙兰卡", - "格拉尼", - "吽", - "灰喉", - "惊蛰", - "可颂", - "苦艾", - "莱恩哈特", - "蜜蜡", - "普罗旺斯", - "绮良", - "熔泉", - "慑砂", - "诗怀雅", - "守林人", - "四月", - "燧石", - "微风", - "乌有", - "絮雨", - "崖心", - "亚叶", - "炎客", - "夜魔", - "月禾", - "铸铁", - "艾丝黛尔", - "波登可", - "地灵", - "杜宾", - "芳汀", - "角峰", - "杰克", - "慕斯", - "清道夫", - "深靛", - "深海色", - "松果", - "酸糖", - "12F", - "杜林", - "巡林者", - "Lancet-2", - "闪击", - "战车", - "红", - "卡夫卡", - "龙舌兰", - "巫恋", - "阿消", - "雷蛇", - "伊桑", - "陈", - "灰烬", - "凯尔希", - "霜华", - "拉普兰德", - "格雷伊", - "能天使", - "德克萨斯", - "因陀罗", - "伊芙利特", - "THRM-EX" - ] -} \ No newline at end of file diff --git a/resource/tasks.json b/resource/tasks.json index a61a059ad3..f2c53f4bec 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -20,7 +20,6 @@ ] }, "UsePrts": { - "template": "UsePrts.png", "templThreshold": 0.8, "action": "clickSelf", "next": [ @@ -28,8 +27,16 @@ ] }, "StartButton1": { - "template": "StartButton1.png", - "templThreshold": 0.75, + "algorithm": "OcrDetect", + "text": [ + "开始行动" + ], + "roi": [ + 800, + 400, + 480, + 320 + ], "action": "clickSelf", "next": [ "StartButton2", @@ -38,7 +45,6 @@ ] }, "StartButton2": { - "template": "StartButton2.png", "action": "clickSelf", "rearDelay": 20000, "templThreshold": 0.95, @@ -54,7 +60,7 @@ "剩余可放置角色", "接管作战" ], - "identifyArea": [ + "roi": [ 0, 530, 1279, @@ -84,7 +90,7 @@ "ClickCorner_Doc": "点击屏幕右边,不会点到掉落物品,且不会关掉关卡选择的一块区域", "algorithm": "justreturn", "action": "clickRect", - "specificArea": [ + "specificRect": [ 1000, 1, 270, @@ -92,7 +98,6 @@ ], "next": [ "EndOfAction", - "Loading", "StartButton1", "StartButton2", "UseMedicine", @@ -117,14 +122,13 @@ "algorithm": "justreturn", "action": "clickRect", "rearDelay": 2000, - "specificArea": [ + "specificRect": [ 1000, 1, 270, 340 ], "next": [ - "Loading", "StartButton1", "StartButton2", "UseMedicine", @@ -132,23 +136,13 @@ "EndOfAction" ] }, - "Loading": { - "template": "Loading.png", - "templThreshold": 0.8, - "action": "doNothing", - "next": [ - "StartButton1" - ] - }, "UseMedicine": { - "template": "UseMedicine.png", "action": "doNothing", "next": [ "MedicineConfirm" ] }, "MedicineConfirm": { - "template": "MedicineConfirm.png", "action": "clickSelf", "maxTimes": 0, "reduceOtherTimes": [ @@ -159,7 +153,6 @@ ] }, "UseStone": { - "template": "UseStone.png", "action": "doNothing", "next": [ "StoneConfirm" @@ -177,7 +170,6 @@ ] }, "PrtsErrorConfirm": { - "template": "PrtsErrorConfirm.png", "templThreshold": 0.8, "cache": false, "action": "doNothing", @@ -187,13 +179,11 @@ ] }, "AbandonAction": { - "template": "AbandonAction.png", "action": "clickSelf", "cache": false, "reduceOtherTimes": [ "StartButton1", - "StartButton2", - "Loading" + "StartButton2" ], "next": [ "EndOfAction", @@ -213,13 +203,11 @@ ] }, "OfflineConfirm": { - "template": "OfflineConfirm.png", "action": "clickSelf", "cache": false, "reduceOtherTimes": [ "StartButton1", - "StartButton2", - "Loading" + "StartButton2" ], "next": [ "CloseAnno", @@ -228,7 +216,6 @@ ] }, "StartToWakeUp": { - "template": "StartToWakeUp.png", "action": "clickSelf", "cache": false, "next": [ @@ -239,7 +226,6 @@ ] }, "CloseAnno": { - "template": "CloseAnno.png", "action": "clickSelf", "cache": false, "next": [ @@ -249,7 +235,6 @@ ] }, "Terminal": { - "template": "Terminal.png", "cache": false, "action": "clickSelf", "next": [ @@ -265,7 +250,7 @@ "今日配给" ], "action": "clickRect", - "specificArea": [ + "specificRect": [ 1000, 1, 270, @@ -312,7 +297,6 @@ ] }, "Friends": { - "template": "Friends.png", "templThreshold": 0.85, "action": "clickSelf", "next": [ @@ -321,7 +305,6 @@ ] }, "FriendsList": { - "template": "FriendsList.png", "action": "clickSelf", "next": [ "StartToVisit", @@ -329,14 +312,12 @@ ] }, "NoFriends": { - "template": "NoFriends.png", "action": "doNothing", "next": [ "ReturnToMall" ] }, "StartToVisit": { - "template": "StartToVisit.png", "templThreshold": 0.8, "action": "clickSelf", "rearDelay": 3000, @@ -347,11 +328,10 @@ ] }, "VisitNext": { - "template": "VisitNext.png", "action": "clickSelf", "cache": false, "rearDelay": 2000, - "identifyArea": [ + "roi": [ 1080, 570, 195, @@ -374,7 +354,7 @@ ], "action": "clickSelf", "rearDelay": 2000, - "identifyArea": [ + "roi": [ 1080, 570, 195, @@ -411,7 +391,6 @@ ] }, "Mall": { - "template": "Mall.png", "action": "clickSelf", "next": [ "CreditStore", @@ -419,7 +398,6 @@ ] }, "CreditStore": { - "template": "CreditStore.png", "templThreshold": 0.85, "rearDelay": 1000, "action": "clickSelf", @@ -433,7 +411,7 @@ "text": [ "信用交易所" ], - "identifyArea": [ + "roi": [ 1000, 0, 280, @@ -447,7 +425,6 @@ ] }, "CollectCredit": { - "template": "CollectCredit.png", "action": "clickSelf", "rearDelay": 2000, "next": [ @@ -457,7 +434,7 @@ "CloseCollectCredit": { "algorithm": "justReturn", "action": "clickRect", - "specificArea": [ + "specificRect": [ 1000, 1, 270, @@ -474,7 +451,7 @@ "已达上限" ], "action": "doNothing", - "identifyArea": [ + "roi": [ 900, 50, 375, @@ -485,9 +462,8 @@ ] }, "VisitNextBlack": { - "template": "VisitNextBlack.png", "action": "doNothing", - "identifyArea": [ + "roi": [ 1080, 570, 195, @@ -497,6 +473,30 @@ "ReturnToMall" ] }, + "CreditShop-CreditPoint": { + "template": "CreditPoint.png" + }, + "CreditShop-SoldOut": { + "template": "SoldOut.png" + }, + "CreditShop-BuyIt": { + "template": "BuyIt.png", + "preDelay": 1000, + "rareDelay": 1000 + }, + "CreditShop-NoMoney": { + "algorithm": "OcrDetect", + "text": [ + "信用不足", + "无法购买" + ], + "roi": [ + 940, + 60, + 339, + 110 + ] + }, "Stop": { "algorithm": "justreturn", "action": "stop", @@ -505,22 +505,60 @@ "RecruitTime": { "template": "RecruitTimeReduce.png", "templThreshold": 0.89, - "action": "clickRect", - "specificArea": [ - 391, - 279, + "action": "clickSelf", + "resultMove": [ + 7, + 94, 129, 44 - ], - "next": [ - "Stop" ] }, - "Listless": { - "template": "Listless.png", - "action": "clickSelf", - "next": [ - "Stop" + "RecruitTags": { + "algorithm": "OcrDetect", + "text": [], + "roi": [ + 200, + 200, + 800, + 500 + ], + "ocrReplace": [ + [ + "沮击", + "狙击" + ], + [ + "犯击", + "狙击" + ], + [ + "泪击", + "狙击" + ], + [ + "都出", + "输出" + ], + [ + "都乐", + "输出" + ], + [ + "抓出", + "输出" + ], + [ + "都任", + "输出" + ], + [ + "新于", + "新手" + ], + [ + "防拍", + "防护" + ] ] }, "OperatorBegin": { @@ -532,7 +570,6 @@ ] }, "EnterOperator": { - "template": "EnterOperator.png", "rareDelay": 1000, "templThreshold": 0.7, "action": "clickSelf", @@ -575,7 +612,6 @@ ] }, "EnterInfrast": { - "template": "EnterInfrast.png", "templThreshold": 0.85, "histThreshold": 0.85, "rareDelay": 3000, @@ -601,7 +637,6 @@ ] }, "Trade": { - "template": "Trade.png", "templThreshold": 0.8, "cache": false, "action": "clickSelf", @@ -611,7 +646,6 @@ ] }, "TradeMini": { - "template": "TradeMini.png", "templThreshold": 0.8, "cache": false, "action": "clickSelf", @@ -621,7 +655,6 @@ ] }, "Manufacturing": { - "template": "Manufacturing.png", "templThreshold": 0.8, "cache": false, "action": "clickSelf", @@ -631,7 +664,6 @@ ] }, "ManufacturingMini": { - "template": "ManufacturingMini.png", "templThreshold": 0.8, "cache": false, "action": "clickSelf", @@ -644,7 +676,7 @@ "Doc": "制造站点进去之后,左下角那个“中级作战记录 制造中”", "algorithm": "justreturn", "action": "clickRect", - "specificArea": [ + "specificRect": [ 1, 535, 525, @@ -658,7 +690,7 @@ "Doc": "设施列表里第一个(第一个制造站或者贸易站)", "algorithm": "justreturn", "action": "clickRect", - "specificArea": [ + "specificRect": [ 1, 183, 220, @@ -669,7 +701,6 @@ ] }, "InfrastNotification": { - "template": "InfrastNotification.png", "templThreshold": 0.8, "action": "clickSelf", "next": [ @@ -686,7 +717,7 @@ ], "rearDelay": 1000, "action": "clickSelf", - "identifyArea": [ + "roi": [ 0, 600, 800, @@ -702,7 +733,7 @@ "Doc": "干员疲劳、线索收集 是不能一键收获的,所以要先退出一键收获的界面,这里点击的是控制中枢左侧那一片空白的区域", "algorithm": "justreturn", "action": "clickRect", - "specificArea": [ + "specificRect": [ 250, 100, 380, @@ -713,7 +744,6 @@ ] }, "UavAssist-MFG": { - "template": "UavAssist-MFG.png", "action": "clickSelf", "cache": false, "maxTimes": 1, @@ -724,7 +754,6 @@ ] }, "UavAssist-Trade": { - "template": "UavAssist-Trade.png", "action": "clickSelf", "cache": false, "next": [ @@ -733,14 +762,12 @@ ] }, "UavMax": { - "template": "UavMax.png", "action": "clickSelf", "next": [ "UavConfirm" ] }, "UavConfirm": { - "template": "UavConfirm.png", "action": "clickSelf", "rearDelay": 2000, "next": [ @@ -750,7 +777,6 @@ ] }, "DeliverableOrder": { - "template": "DeliverableOrder.png", "cache": false, "action": "clickSelf", "next": [ diff --git a/resource/template/special/02.png b/resource/template/02.png similarity index 100% rename from resource/template/special/02.png rename to resource/template/02.png diff --git a/resource/template/special/03.png b/resource/template/03.png similarity index 100% rename from resource/template/special/03.png rename to resource/template/03.png diff --git a/resource/template/special/04.png b/resource/template/04.png similarity index 100% rename from resource/template/special/04.png rename to resource/template/04.png diff --git a/resource/template/special/05.png b/resource/template/05.png similarity index 100% rename from resource/template/special/05.png rename to resource/template/05.png diff --git a/resource/template/special/AddOperator-Trade.png b/resource/template/AddOperator-Trade.png similarity index 100% rename from resource/template/special/AddOperator-Trade.png rename to resource/template/AddOperator-Trade.png diff --git a/resource/template/special/BuyIt.png b/resource/template/BuyIt.png similarity index 100% rename from resource/template/special/BuyIt.png rename to resource/template/BuyIt.png diff --git a/resource/template/special/ContactSuspended.png b/resource/template/ContactSuspended.png similarity index 100% rename from resource/template/special/ContactSuspended.png rename to resource/template/ContactSuspended.png diff --git a/resource/template/special/CreditPoint.png b/resource/template/CreditPoint.png similarity index 100% rename from resource/template/special/CreditPoint.png rename to resource/template/CreditPoint.png diff --git a/resource/template/special/DogFood.png b/resource/template/DogFood.png similarity index 100% rename from resource/template/special/DogFood.png rename to resource/template/DogFood.png diff --git a/resource/template/special/Dorm.png b/resource/template/Dorm.png similarity index 100% rename from resource/template/special/Dorm.png rename to resource/template/Dorm.png diff --git a/resource/template/special/DormConfirm.png b/resource/template/DormConfirm.png similarity index 100% rename from resource/template/special/DormConfirm.png rename to resource/template/DormConfirm.png diff --git a/resource/template/special/DormMini.png b/resource/template/DormMini.png similarity index 100% rename from resource/template/special/DormMini.png rename to resource/template/DormMini.png diff --git a/resource/template/special/Elite1.png b/resource/template/Elite1.png similarity index 100% rename from resource/template/special/Elite1.png rename to resource/template/Elite1.png diff --git a/resource/template/special/Elite2.png b/resource/template/Elite2.png similarity index 100% rename from resource/template/special/Elite2.png rename to resource/template/Elite2.png diff --git a/resource/template/special/GoIn.png b/resource/template/GoIn.png similarity index 100% rename from resource/template/special/GoIn.png rename to resource/template/GoIn.png diff --git a/resource/template/Listless.png b/resource/template/Listless.png index 61ee7aff8be43322501f0db05fc2d80a9a3e0414..014335c901a534e94da261f1338584f3a5dcb3b1 100644 GIT binary patch literal 6976 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 SQUKTh00006}&0{ literal 20961 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 Mq9!y?(e(EJ1Bhr~uK)l5 diff --git a/resource/template/special/Office.png b/resource/template/Office.png similarity index 100% rename from resource/template/special/Office.png rename to resource/template/Office.png diff --git a/resource/template/special/OfficeMini.png b/resource/template/OfficeMini.png similarity index 100% rename from resource/template/special/OfficeMini.png rename to resource/template/OfficeMini.png diff --git a/resource/template/special/OnShift.png b/resource/template/OnShift.png similarity index 100% rename from resource/template/special/OnShift.png rename to resource/template/OnShift.png diff --git a/resource/template/Power.png b/resource/template/Power.png index fa1db56ce7882f469e81c1fab1e2aecdeb5afcfe..c04f3d78e19dbf4573ac46698ed6a053095bf418 100644 GIT binary patch literal 21108 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 z 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 LNkvXXu0mjfSX3`q diff --git a/resource/template/special/PowerMini.png b/resource/template/PowerMini.png similarity index 100% rename from resource/template/special/PowerMini.png rename to resource/template/PowerMini.png diff --git a/resource/template/special/PureGold.png b/resource/template/PureGold.png similarity index 100% rename from resource/template/special/PureGold.png rename to resource/template/PureGold.png diff --git a/resource/template/special/Resting.png b/resource/template/Resting.png similarity index 100% rename from resource/template/special/Resting.png rename to resource/template/Resting.png diff --git a/resource/template/special/SmileyAtRest.png b/resource/template/SmileyAtRest.png similarity index 100% rename from resource/template/special/SmileyAtRest.png rename to resource/template/SmileyAtRest.png diff --git a/resource/template/special/SmileyAtWork.png b/resource/template/SmileyAtWork.png similarity index 100% rename from resource/template/special/SmileyAtWork.png rename to resource/template/SmileyAtWork.png diff --git a/resource/template/special/SoldOut.png b/resource/template/SoldOut.png similarity index 100% rename from resource/template/special/SoldOut.png rename to resource/template/SoldOut.png diff --git a/resource/template/special/StationInfoSelected.png b/resource/template/StationInfoSelected.png similarity index 100% rename from resource/template/special/StationInfoSelected.png rename to resource/template/StationInfoSelected.png diff --git a/resource/template/special/TradeMoney.png b/resource/template/TradeMoney.png similarity index 100% rename from resource/template/special/TradeMoney.png rename to resource/template/TradeMoney.png diff --git a/resource/template/special/UnlockClues.png b/resource/template/UnlockClues.png similarity index 100% rename from resource/template/special/UnlockClues.png rename to resource/template/UnlockClues.png diff --git a/resource/template/special/AddOperator.png b/resource/template/special/AddOperator.png deleted file mode 100644 index bebe549b84dc34cd6c0b235d29df11028b59fc85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2261 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 z 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 F`ak74bN>JU diff --git a/resource/template/special/Listless.png b/resource/template/special/Listless.png deleted file mode 100644 index 014335c901a534e94da261f1338584f3a5dcb3b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6976 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 SQUKTh00006}&0{ diff --git a/resource/template/special/Power.png b/resource/template/special/Power.png deleted file mode 100644 index c04f3d78e19dbf4573ac46698ed6a053095bf418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21108 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 #include #include #include -#include "Logger.hpp" - bool asst::AbstractConfiger::load(const std::string& filename) { - DebugTraceFunction; - DebugTrace("Configer::load | ", filename); - std::ifstream ifs(filename, std::ios::in); if (!ifs.is_open()) { + m_last_error = filename + " can't be opened"; return false; } std::stringstream iss; @@ -24,7 +19,7 @@ bool asst::AbstractConfiger::load(const std::string& filename) auto&& ret = json::parser::parse(content); if (!ret) { - DebugTrace("parse error", content); + m_last_error = "json pasing error " + content; return false; } @@ -34,10 +29,8 @@ bool asst::AbstractConfiger::load(const std::string& filename) parse(root); } catch (json::exception& e) { - DebugTraceError("Load config json error!", e.what()); + m_last_error = std::string("json field error ") + e.what(); return false; } - - DebugTrace("Load config succeed"); return true; } \ No newline at end of file diff --git a/src/MeoAssistance/AbstractConfiger.h b/src/MeoAssistance/AbstractConfiger.h index 9df0f2e8ce..ef8903b241 100644 --- a/src/MeoAssistance/AbstractConfiger.h +++ b/src/MeoAssistance/AbstractConfiger.h @@ -1,30 +1,20 @@ #pragma once -#include -#include -#include +#include "AbstractResource.h" -#include "AsstDef.h" +#include namespace json { class value; } namespace asst { - class AbstractConfiger + class AbstractConfiger : public AbstractResource { public: virtual ~AbstractConfiger() = default; - virtual bool load(const std::string& filename); - + virtual bool load(const std::string& filename) override; protected: - AbstractConfiger() = default; - AbstractConfiger(const AbstractConfiger& rhs) = default; - AbstractConfiger(AbstractConfiger&& rhs) noexcept = default; - - AbstractConfiger& operator=(const AbstractConfiger& rhs) = default; - AbstractConfiger& operator=(AbstractConfiger&& rhs) noexcept = default; - virtual bool parse(const json::value& json) = 0; }; } diff --git a/src/MeoAssistance/AbstractImageAnalyzer.cpp b/src/MeoAssistance/AbstractImageAnalyzer.cpp new file mode 100644 index 0000000000..53fa2c523b --- /dev/null +++ b/src/MeoAssistance/AbstractImageAnalyzer.cpp @@ -0,0 +1,24 @@ +#include "AbstractImageAnalyzer.h" + +asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image, const Rect& roi) + : m_image(image), + m_roi(empty_rect_to_full(roi, image)) +{ + ; +} + +void asst::AbstractImageAnalyzer::set_image(const cv::Mat& image, const Rect& roi) +{ + m_image = image; + m_roi = empty_rect_to_full(roi, image); +} + +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 +{ + return rect.empty() ? Rect(0, 0, image.cols, image.rows) : rect; +} \ No newline at end of file diff --git a/src/MeoAssistance/AbstractImageAnalyzer.h b/src/MeoAssistance/AbstractImageAnalyzer.h new file mode 100644 index 0000000000..e8c5e25544 --- /dev/null +++ b/src/MeoAssistance/AbstractImageAnalyzer.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "AsstDef.h" + +namespace asst { + class AbstractImageAnalyzer + { + public: + AbstractImageAnalyzer() = default; + AbstractImageAnalyzer(const cv::Mat& image, const Rect& roi = Rect()); + AbstractImageAnalyzer(const AbstractImageAnalyzer&) = delete; + AbstractImageAnalyzer(AbstractImageAnalyzer&&) = delete; + virtual ~AbstractImageAnalyzer() = default; + + virtual void set_image(const cv::Mat& image, const Rect& roi = Rect()); + virtual void set_roi(const Rect& roi) noexcept; + virtual bool analyze() = 0; + + AbstractImageAnalyzer& operator=(const AbstractImageAnalyzer&) = delete; + AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete; + + protected: + static Rect empty_rect_to_full(const Rect& rect, const cv::Mat& image) noexcept; + + cv::Mat m_image; + Rect m_roi; + }; +} \ No newline at end of file diff --git a/src/MeoAssistance/AbstractResource.cpp b/src/MeoAssistance/AbstractResource.cpp new file mode 100644 index 0000000000..e9b6196692 --- /dev/null +++ b/src/MeoAssistance/AbstractResource.cpp @@ -0,0 +1 @@ +#include "AbstractResource.h" \ No newline at end of file diff --git a/src/MeoAssistance/AbstractResource.h b/src/MeoAssistance/AbstractResource.h new file mode 100644 index 0000000000..04e67bf3a0 --- /dev/null +++ b/src/MeoAssistance/AbstractResource.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace asst { + class AbstractResource + { + public: + AbstractResource() = default; + AbstractResource(const AbstractResource& rhs) = delete; + AbstractResource(AbstractResource&& rhs) noexcept = delete; + + virtual ~AbstractResource() = default; + + virtual bool load(const std::string& filename) = 0; + virtual const std::string& get_last_error() const noexcept + { + return m_last_error; + } + + AbstractResource& operator=(const AbstractResource& rhs) = delete; + AbstractResource& operator=(AbstractResource&& rhs) noexcept = delete; + + protected: + std::string m_last_error; + }; +} diff --git a/src/MeoAssistance/AbstractTask.cpp b/src/MeoAssistance/AbstractTask.cpp index 22a28c8400..7feeb1f23c 100644 --- a/src/MeoAssistance/AbstractTask.cpp +++ b/src/MeoAssistance/AbstractTask.cpp @@ -6,9 +6,7 @@ #include -#include "WinMacro.h" -#include "Identify.h" -#include "Configer.h" +#include "Controller.h" #include "AsstAux.h" #include "Logger.hpp" @@ -21,14 +19,6 @@ AbstractTask::AbstractTask(AsstCallback callback, void* callback_arg) ; } -void AbstractTask::set_ptr( - std::shared_ptr controller_ptr, - std::shared_ptr identify_ptr) -{ - m_controller_ptr = controller_ptr; - m_identify_ptr = identify_ptr; -} - void AbstractTask::set_exit_flag(bool* exit_flag) { m_exit_flag = exit_flag; @@ -81,20 +71,10 @@ bool asst::AbstractTask::need_exit() const noexcept return m_exit_flag != NULL && *m_exit_flag == true; } -bool asst::AbstractTask::is_ptr_inited() const noexcept -{ - if (m_controller_ptr == NULL - || m_identify_ptr == NULL) - { - return false; - } - return true; -} - bool asst::AbstractTask::click_return_button() { - DebugTraceFunction; + LogTraceFunction; const static Rect ConfirmButtonRect(20, 20, 135, 35); - return m_controller_ptr->click(ConfirmButtonRect); + return ctrler.click(ConfirmButtonRect); } \ No newline at end of file diff --git a/src/MeoAssistance/AbstractTask.h b/src/MeoAssistance/AbstractTask.h index 31a8ec4694..bdff8d1acb 100644 --- a/src/MeoAssistance/AbstractTask.h +++ b/src/MeoAssistance/AbstractTask.h @@ -10,9 +10,6 @@ namespace cv } namespace asst { - class WinMacro; - class Identify; - class AbstractTask { public: @@ -21,7 +18,6 @@ namespace asst { AbstractTask(const AbstractTask&) = default; AbstractTask(AbstractTask&&) = default; - virtual void set_ptr(std::shared_ptr controller_ptr, std::shared_ptr identify_ptr); virtual bool run() = 0; virtual void set_exit_flag(bool* exit_flag); @@ -34,13 +30,9 @@ namespace asst { virtual bool sleep(unsigned millisecond); virtual bool save_image(const cv::Mat& iamge, const std::string& dir); virtual bool need_exit() const noexcept; - virtual bool is_ptr_inited() const noexcept; virtual bool click_return_button(); - std::shared_ptr m_controller_ptr = nullptr; - std::shared_ptr m_identify_ptr = nullptr; - AsstCallback m_callback; void* m_callback_arg = NULL; bool* m_exit_flag = NULL; diff --git a/src/MeoAssistance/Assistance.cpp b/src/MeoAssistance/Assistance.cpp index b16cb5aa15..9de9b3f74c 100644 --- a/src/MeoAssistance/Assistance.cpp +++ b/src/MeoAssistance/Assistance.cpp @@ -6,113 +6,30 @@ #include #include -#include "WinMacro.h" -#include "Configer.h" -#include "Identify.h" +#include "Controller.h" #include "Logger.hpp" #include "AsstAux.h" -#include "Task.h" -#include "TaskConfiger.h" -#include "ItemConfiger.h" -#include "RecruitConfiger.h" -#include "InfrastConfiger.h" -#include "UserConfiger.h" +#include "Resource.h" using namespace asst; Assistance::Assistance(AsstCallback callback, void* callback_arg) : m_callback(callback), m_callback_arg(callback_arg) { - DebugTraceFunction; + LogTraceFunction; - // 检查返回值,若为false则回调错误 - auto callback_error = [&](const std::string& filename = std::string()) { - DebugTraceError("resource broken", filename); + bool resource_ret = resource.load(GetResourceDir()); + if (!resource_ret) { + const std::string& error = resource.get_last_error(); + log.error("resource broken", error); if (m_callback == nullptr) { return; } json::value callback_json; - callback_json["filename"] = filename; - callback_json["what"] = "resource broken"; + callback_json["type"] = "resource broken"; + callback_json["what"] = error; m_callback(AsstMsg::InitFaild, callback_json, m_callback_arg); - }; - - /* 项目本身的Configer */ - bool ret = Configer::get_instance().load(GetResourceDir() + "config.json"); - if (!ret) { - callback_error("config.json"); - throw "resource broken"; } - ret = TaskConfiger::get_instance().load(GetResourceDir() + "tasks.json"); - if (!ret) { - callback_error("tasks.json"); - throw "resource broken"; - } - ret = RecruitConfiger::get_instance().load(GetResourceDir() + "recruit.json"); - if (!ret) { - callback_error("recruit.json"); - throw "resource broken"; - } - ret = InfrastConfiger::get_instance().load(GetResourceDir() + "infrast.json"); - if (!ret) { - callback_error("infrast.json"); - throw "resource broken"; - } - ret = ItemConfiger::get_instance().load(GetResourceDir() + "item_index.json"); - if (!ret) { - callback_error("item_index.json"); - throw "resource broken"; - } - - /* 项目使用的图片模板 */ - m_identify_ptr = std::make_shared(); - for (const auto& [name, info] : TaskConfiger::get_instance().m_all_tasks_info) { - if (info->algorithm != AlgorithmType::MatchTemplate) { - continue; - } - std::string filename = std::dynamic_pointer_cast(info)->template_filename; - ret = m_identify_ptr->add_image(name, GetResourceDir() + "template\\" + filename); - if (!ret) { - callback_error(filename); - throw "resource broken"; - } - } - for (auto& p : std::filesystem::directory_iterator(GetResourceDir() + "template\\special\\")) { - if (p.path().extension() == ".png") { - std::string filename = p.path().filename().u8string(); - std::string without_extension = filename.substr(0, filename.size() - 4); - ret = m_identify_ptr->add_image(without_extension, p.path().u8string()); - } - } - - /* 第三方库`OcrLite`所需资源*/ - m_identify_ptr->set_ocr_param(Configer::get_instance().m_options.ocr_gpu_index, Configer::get_instance().m_options.ocr_thread_number); - ret = m_identify_ptr->ocr_init_models(GetResourceDir() + "OcrLiteOnnx\\models\\"); - if (!ret) { - callback_error("OcrLiteOnnx\\models\\"); - throw "resource broken"; - } - - /* 第三方库`penguin-stats-recognize`(企鹅物流掉落识别)所需资源*/ - m_identify_ptr->penguin_load_server(Configer::get_instance().m_options.penguin_server); - m_identify_ptr->penguin_load_json(GetResourceDir() + "penguin-stats-recognize\\json\\stages.json", GetResourceDir() + "penguin-stats-recognize\\json\\hash_index.json"); - - for (const auto& file : std::filesystem::directory_iterator(GetResourceDir() + "penguin-stats-recognize\\items")) { - ret = m_identify_ptr->penguin_load_templ(file.path().stem().u8string(), file.path().u8string()); - if (!ret) { - callback_error(file.path().stem().u8string()); - throw "resource broken"; - } - } - - /* 用户配置 */ - ret = UserConfiger::get_instance().load(GetCurrentDir() + "user.json"); - if (!ret) { - callback_error("user.json"); - throw "resource broken"; - } - - m_controller_ptr = std::make_shared(); m_working_thread = std::thread(std::bind(&Assistance::working_proc, this)); m_msg_thread = std::thread(std::bind(&Assistance::msg_proc, this)); @@ -120,11 +37,7 @@ Assistance::Assistance(AsstCallback callback, void* callback_arg) Assistance::~Assistance() { - DebugTraceFunction; - - //if (m_controller_ptr != nullptr) { - // m_controller_ptr->show_window(); - //} + LogTraceFunction; m_thread_exit = true; m_thread_idle = true; @@ -141,15 +54,16 @@ Assistance::~Assistance() bool asst::Assistance::catch_default() { - DebugTraceFunction; + LogTraceFunction; - switch (Configer::get_instance().m_options.connect_type) { + auto& opt = resource.cfg().get_options(); + switch (opt.connect_type) { case ConnectType::Emulator: return catch_emulator(); case ConnectType::USB: return catch_usb(); case ConnectType::Remote: - return catch_remote(Configer::get_instance().m_options.connect_remote_address); + return catch_remote(opt.connect_remote_address); default: return false; } @@ -157,19 +71,20 @@ bool asst::Assistance::catch_default() bool Assistance::catch_emulator(const std::string& emulator_name) { - DebugTraceFunction; + LogTraceFunction; stop(); bool ret = false; //std::string cor_name = emulator_name; + auto& cfg = resource.cfg(); std::unique_lock lock(m_mutex); // 自动匹配模拟器,逐个找 if (emulator_name.empty()) { - for (const auto& [name, info] : Configer::get_instance().m_handles) { - ret = m_controller_ptr->try_capture(info); + for (const auto& [name, info] : cfg.get_emulators_info()) { + ret = ctrler.try_capture(info); if (ret) { //cor_name = name; break; @@ -177,7 +92,8 @@ bool Assistance::catch_emulator(const std::string& emulator_name) } } else { // 指定的模拟器 - ret = m_controller_ptr->try_capture(Configer::get_instance().m_handles[emulator_name]); + auto& info = cfg.get_emulators_info().at(emulator_name); + ret = ctrler.try_capture(info); } m_inited = ret; @@ -186,17 +102,18 @@ bool Assistance::catch_emulator(const std::string& emulator_name) bool asst::Assistance::catch_usb() { - DebugTraceFunction; + LogTraceFunction; stop(); bool ret = false; + auto& cfg = resource.cfg(); std::unique_lock lock(m_mutex); - EmulatorInfo remote_info = Configer::get_instance().m_handles["USB"]; + EmulatorInfo remote_info = cfg.get_emulators_info().at("USB"); - ret = m_controller_ptr->try_capture(remote_info, true); + ret = ctrler.try_capture(remote_info, true); m_inited = ret; return ret; @@ -204,22 +121,23 @@ bool asst::Assistance::catch_usb() bool asst::Assistance::catch_remote(const std::string& address) { - DebugTraceFunction; + LogTraceFunction; stop(); bool ret = false; + auto& cfg = resource.cfg(); std::unique_lock lock(m_mutex); - EmulatorInfo remote_info = Configer::get_instance().m_handles["Remote"]; + EmulatorInfo remote_info = cfg.get_emulators_info().at("Remote"); remote_info.adb.connect = StringReplaceAll(remote_info.adb.connect, "[Address]", address); remote_info.adb.click = StringReplaceAll(remote_info.adb.click, "[Address]", address); remote_info.adb.swipe = StringReplaceAll(remote_info.adb.swipe, "[Address]", address); remote_info.adb.display = StringReplaceAll(remote_info.adb.display, "[Address]", address); remote_info.adb.screencap = StringReplaceAll(remote_info.adb.screencap, "[Address]", address); - ret = m_controller_ptr->try_capture(remote_info, true); + ret = ctrler.try_capture(remote_info, true); m_inited = ret; return ret; @@ -232,13 +150,13 @@ bool asst::Assistance::start_sanity() bool asst::Assistance::start_visit(bool with_shopping) { - DebugTraceFunction; + LogTraceFunction; if (!m_thread_idle || !m_inited) { return false; } std::unique_lock lock(m_mutex); - m_identify_ptr->clear_cache(); + resource.templ().clear_hists(); append_match_task("VisitBegin", { "VisitBegin" }, ProcessTaskRetryTimesDefault); @@ -256,8 +174,8 @@ bool asst::Assistance::start_visit(bool with_shopping) bool Assistance::start_process_task(const std::string& task, int retry_times, bool block) { - DebugTraceFunction; - DebugTrace("Start |", task, block ? "block" : "non block"); + LogTraceFunction; + log.trace("Start |", task, block ? "block" : "non block"); if (!m_thread_idle || !m_inited) { return false; } @@ -266,7 +184,7 @@ bool Assistance::start_process_task(const std::string& task, int retry_times, bo if (block) { lock = std::unique_lock(m_mutex); //clear_exec_times(); - m_identify_ptr->clear_cache(); + resource.templ().clear_hists(); } append_match_task(task, { task }, retry_times); @@ -280,7 +198,7 @@ bool Assistance::start_process_task(const std::string& task, int retry_times, bo #ifdef LOG_TRACE bool Assistance::start_debug_task() { - DebugTraceFunction; + LogTraceFunction; if (!m_thread_idle || !m_inited) { return false; } @@ -296,7 +214,7 @@ bool Assistance::start_debug_task() // auto ret = get_opers_idtf_result(); // if (!ret) { - // DebugTraceInfo("Get opers info error"); + // log.info("Get opers info error"); // //return false; // } // else { @@ -307,7 +225,7 @@ bool Assistance::start_debug_task() //} { constexpr static const char* DebugTaskChain = "Debug"; - auto shift_task_ptr = std::make_shared(task_callback, (void*)this); + auto shift_task_ptr = std::make_shared(task_callback, (void*)this); shift_task_ptr->set_task_chain(DebugTaskChain); m_tasks_deque.emplace_back(shift_task_ptr); } @@ -319,16 +237,16 @@ bool Assistance::start_debug_task() } #endif -bool Assistance::start_open_recruit(const std::vector& required_level, bool set_time) +bool Assistance::start_recruiting(const std::vector& required_level, bool set_time) { - DebugTraceFunction; + LogTraceFunction; if (!m_thread_idle || !m_inited) { return false; } std::unique_lock lock(m_mutex); - auto task_ptr = std::make_shared(task_callback, (void*)this); + auto task_ptr = std::make_shared(task_callback, (void*)this); task_ptr->set_param(required_level, set_time); task_ptr->set_retry_times(OpenRecruitTaskRetyrTimesDefault); task_ptr->set_task_chain("OpenRecruit"); @@ -340,130 +258,130 @@ bool Assistance::start_open_recruit(const std::vector& required_level, bool return true; } -bool asst::Assistance::start_to_identify_opers() -{ - DebugTraceFunction; - if (!m_thread_idle || !m_inited) { - return false; - } - - std::unique_lock lock(m_mutex); - - append_match_task("IdentifyOpers", { "OperatorBegin" }); - - auto task_ptr = std::make_shared(task_callback, (void*)this); - task_ptr->set_task_chain("IdentifyOpers"); - m_tasks_deque.emplace_back(task_ptr); - - m_thread_idle = false; - m_condvar.notify_one(); - - return true; -} - -bool asst::Assistance::start_infrast() -{ - DebugTraceFunction; - if (!m_thread_idle || !m_inited) { - return false; - } - - std::unique_lock lock(m_mutex); - auto ret = get_opers_idtf_result(); - if (!ret) { - DebugTraceInfo("Get opers info error"); - return false; - } - constexpr static const char* InfrastTaskCahin = "Infrast"; - // 换班任务,依次遍历基建设施列表里的最多5个设施,识别并选择最优解干员组合 - auto shift_task_ptr = std::make_shared(task_callback, (void*)this); - shift_task_ptr->set_task_chain(InfrastTaskCahin); - shift_task_ptr->set_all_opers_info(std::move(ret.value())); - - /* 基建任务整体流程: - 1. 从任意界面进入基建,使用ProcessTask - 2. 一键收获贸易站、制造站、干员信赖,使用ProcessTask - 1) 如果收获了,使用基建全缩放到最小的模板匹配 - 2) 如果没收获,使用基建默认大小的模板匹配 - 3. 进入宿舍,把心情低于阈值的、心情没满但不在工作的,都换下去,使用InfrastDormTask - 4. 根据用户设置,按顺序进入制造站or贸易站,使用ProcessTask - 5. 对制造站or贸易站进行换班,使用InfrastStationTask - 6. 根据用户设置,使用无人机加速制造or贸易,使用ProcessTask - 7. 会客室线索处理、发电站换班、控制中枢、办公室换班,同样需要根据用户设置决定顺序,TODO - 8. 再次进入宿舍,把基建中可能换下来的干员(心情不低的)加入宿舍 - */ - - // 1. 从任意界面进入基建,使用ProcessTask - // 2. 一键收获贸易站、制造站、干员信赖,使用ProcessTask - // 这个任务结束后,是在进入基建后的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - // 3. 进入宿舍,把心情低于阈值的、心情没满但不在工作的,都换下去 - auto dorm_task_ptr = std::make_shared(task_callback, (void*)this); - dorm_task_ptr->set_task_chain(InfrastTaskCahin); - m_tasks_deque.emplace_back(dorm_task_ptr); - - // 返回基建的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - // 5. 对制造站or贸易站进行换班,使用InfrastStationTask - - // TODO,这里需要根据用户设置,是先制造站还是先贸易站,或者是别的设施 - // 从进入制造站,到设施列表的界面 - append_match_task(InfrastTaskCahin, { "ManufacturingMini", "Manufacturing" }); - - // 制造站换班 - m_tasks_deque.emplace_back(shift_task_ptr); - - // 返回基建的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - // TODO,这里需要根据用户设置,是先制造站还是先贸易站,或者是别的设施 - // 从进入贸易站,到设施列表的界面 - append_match_task(InfrastTaskCahin, { "Trade", "TradeMini" }); - - // 贸易站换班 - m_tasks_deque.emplace_back(shift_task_ptr); - - // 6. 根据用户设置,使用无人机加速制造or贸易,使用ProcessTask - // 对贸易站使用无人机加速 - append_match_task(InfrastTaskCahin, { "UavAssist-Trade" }); - - // 返回基建的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - // 7. 会客室线索处理、发电站换班、控制中枢、办公室换班,同样需要根据用户设置决定顺序,TODO - // 发电站换班 - auto power_task_ptr = std::make_shared(task_callback, (void*)this); - power_task_ptr->set_task_chain(InfrastTaskCahin); - m_tasks_deque.emplace_back(std::move(power_task_ptr)); - - // 返回基建的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - auto office_task_ptr = std::make_shared(task_callback, (void*)this); - office_task_ptr->set_task_chain(InfrastTaskCahin); - m_tasks_deque.emplace_back(std::move(office_task_ptr)); - - // 返回基建的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - // 8. 再次进入宿舍,把基建中可能换下来的干员(心情不低的)加入宿舍 - m_tasks_deque.emplace_back(dorm_task_ptr); - - // 全操作完之后,再返回基建的主界面 - append_match_task(InfrastTaskCahin, { "InfrastBegin" }); - - m_thread_idle = false; - m_condvar.notify_one(); - - return true; -} +//bool asst::Assistance::start_to_identify_opers() +//{ +// LogTraceFunction; +// if (!m_thread_idle || !m_inited) { +// return false; +// } +// +// std::unique_lock lock(m_mutex); +// +// append_match_task("IdentifyOpers", { "OperatorBegin" }); +// +// auto task_ptr = std::make_shared(task_callback, (void*)this); +// task_ptr->set_task_chain("IdentifyOpers"); +// m_tasks_deque.emplace_back(task_ptr); +// +// m_thread_idle = false; +// m_condvar.notify_one(); +// +// return true; +//} +// +//bool asst::Assistance::start_infrast() +//{ +// LogTraceFunction; +// if (!m_thread_idle || !m_inited) { +// return false; +// } +// +// std::unique_lock lock(m_mutex); +// auto ret = get_opers_idtf_result(); +// if (!ret) { +// log.info("Get opers info error"); +// return false; +// } +// constexpr static const char* InfrastTaskCahin = "Infrast"; +// // 换班任务,依次遍历基建设施列表里的最多5个设施,识别并选择最优解干员组合 +// auto shift_task_ptr = std::make_shared(task_callback, (void*)this); +// shift_task_ptr->set_task_chain(InfrastTaskCahin); +// shift_task_ptr->set_all_opers_info(std::move(ret.value())); +// +// /* 基建任务整体流程: +// 1. 从任意界面进入基建,使用ProcessTask +// 2. 一键收获贸易站、制造站、干员信赖,使用ProcessTask +// 1) 如果收获了,使用基建全缩放到最小的模板匹配 +// 2) 如果没收获,使用基建默认大小的模板匹配 +// 3. 进入宿舍,把心情低于阈值的、心情没满但不在工作的,都换下去,使用InfrastDormTask +// 4. 根据用户设置,按顺序进入制造站or贸易站,使用ProcessTask +// 5. 对制造站or贸易站进行换班,使用InfrastStationTask +// 6. 根据用户设置,使用无人机加速制造or贸易,使用ProcessTask +// 7. 会客室线索处理、发电站换班、控制中枢、办公室换班,同样需要根据用户设置决定顺序,TODO +// 8. 再次进入宿舍,把基建中可能换下来的干员(心情不低的)加入宿舍 +// */ +// +// // 1. 从任意界面进入基建,使用ProcessTask +// // 2. 一键收获贸易站、制造站、干员信赖,使用ProcessTask +// // 这个任务结束后,是在进入基建后的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// // 3. 进入宿舍,把心情低于阈值的、心情没满但不在工作的,都换下去 +// auto dorm_task_ptr = std::make_shared(task_callback, (void*)this); +// dorm_task_ptr->set_task_chain(InfrastTaskCahin); +// m_tasks_deque.emplace_back(dorm_task_ptr); +// +// // 返回基建的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// // 5. 对制造站or贸易站进行换班,使用InfrastStationTask +// +// // TODO,这里需要根据用户设置,是先制造站还是先贸易站,或者是别的设施 +// // 从进入制造站,到设施列表的界面 +// append_match_task(InfrastTaskCahin, { "ManufacturingMini", "Manufacturing" }); +// +// // 制造站换班 +// m_tasks_deque.emplace_back(shift_task_ptr); +// +// // 返回基建的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// // TODO,这里需要根据用户设置,是先制造站还是先贸易站,或者是别的设施 +// // 从进入贸易站,到设施列表的界面 +// append_match_task(InfrastTaskCahin, { "Trade", "TradeMini" }); +// +// // 贸易站换班 +// m_tasks_deque.emplace_back(shift_task_ptr); +// +// // 6. 根据用户设置,使用无人机加速制造or贸易,使用ProcessTask +// // 对贸易站使用无人机加速 +// append_match_task(InfrastTaskCahin, { "UavAssist-Trade" }); +// +// // 返回基建的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// // 7. 会客室线索处理、发电站换班、控制中枢、办公室换班,同样需要根据用户设置决定顺序,TODO +// // 发电站换班 +// auto power_task_ptr = std::make_shared(task_callback, (void*)this); +// power_task_ptr->set_task_chain(InfrastTaskCahin); +// m_tasks_deque.emplace_back(std::move(power_task_ptr)); +// +// // 返回基建的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// auto office_task_ptr = std::make_shared(task_callback, (void*)this); +// office_task_ptr->set_task_chain(InfrastTaskCahin); +// m_tasks_deque.emplace_back(std::move(office_task_ptr)); +// +// // 返回基建的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// // 8. 再次进入宿舍,把基建中可能换下来的干员(心情不低的)加入宿舍 +// m_tasks_deque.emplace_back(dorm_task_ptr); +// +// // 全操作完之后,再返回基建的主界面 +// append_match_task(InfrastTaskCahin, { "InfrastBegin" }); +// +// m_thread_idle = false; +// m_condvar.notify_one(); +// +// return true; +//} void Assistance::stop(bool block) { - DebugTraceFunction; - DebugTrace("Stop |", block ? "block" : "non block"); + LogTraceFunction; + log.trace("Stop |", block ? "block" : "non block"); m_thread_idle = true; @@ -474,36 +392,35 @@ void Assistance::stop(bool block) decltype(m_tasks_deque) empty; m_tasks_deque.swap(empty); clear_exec_times(); - m_identify_ptr->clear_cache(); + resource.templ().clear_hists(); } bool Assistance::set_param(const std::string& type, const std::string& param, const std::string& value) { - DebugTraceFunction; - DebugTrace("SetParam |", type, param, value); + LogTraceFunction; + log.trace("SetParam |", type, param, value); - return TaskConfiger::get_instance().set_param(type, param, value); + return resource.task().set_param(type, param, value); } void Assistance::working_proc() { - DebugTraceFunction; + LogTraceFunction; auto p_this = this; int retry_times = 0; while (!m_thread_exit) { - //DebugTraceScope("Assistance::working_proc Loop"); + //LogTraceScope("Assistance::working_proc Loop"); std::unique_lock lock(m_mutex); if (!m_thread_idle && !m_tasks_deque.empty()) { - //m_controller_ptr->set_idle(false); + //controller.set_idle(false); auto start_time = std::chrono::system_clock::now(); std::shared_ptr task_ptr = m_tasks_deque.front(); // 先pop出来,如果执行失败再还原回去 m_tasks_deque.pop_front(); - task_ptr->set_ptr(m_controller_ptr, m_identify_ptr); task_ptr->set_exit_flag(&m_thread_idle); bool ret = task_ptr->run(); if (ret) { @@ -534,13 +451,13 @@ void Assistance::working_proc() } } - int& delay = Configer::get_instance().m_options.task_delay; + auto& delay = resource.cfg().get_options().task_delay; m_condvar.wait_until(lock, start_time + std::chrono::milliseconds(delay), [&]() -> bool { return m_thread_idle; }); } else { m_thread_idle = true; - //m_controller_ptr->set_idle(true); + //controller.set_idle(true); m_condvar.wait(lock); } } @@ -548,10 +465,10 @@ void Assistance::working_proc() void Assistance::msg_proc() { - DebugTraceFunction; + LogTraceFunction; while (!m_thread_exit) { - //DebugTraceScope("Assistance::msg_proc Loop"); + //LogTraceScope("Assistance::msg_proc Loop"); std::unique_lock lock(m_msg_mutex); if (!m_msg_queue.empty()) { // 结构化绑定只能是引用,后续的pop会使引用失效,所以需要重新构造一份,这里采用了move的方式 @@ -573,7 +490,7 @@ void Assistance::msg_proc() void Assistance::task_callback(AsstMsg msg, const json::value& detail, void* custom_arg) { - DebugTrace("Assistance::task_callback |", msg, detail.to_string()); + log.trace("Assistance::task_callback |", msg, detail.to_string()); Assistance* p_this = (Assistance*)custom_arg; json::value more_detail = detail; @@ -583,7 +500,7 @@ void Assistance::task_callback(AsstMsg msg, const json::value& detail, void* cus p_this->stop(false); break; //case AsstMsg::WindowMinimized: - // p_this->m_controller_ptr->show_window(); + // p_this->controller.show_window(); // break; case AsstMsg::AppendProcessTask: more_detail["type"] = "ProcessTask"; @@ -592,9 +509,6 @@ void Assistance::task_callback(AsstMsg msg, const json::value& detail, void* cus p_this->append_task(more_detail, true); return; // 这俩消息Assistance会新增任务,外部不需要处理,直接拦掉 break; - case AsstMsg::OpersIdtfResult: - set_opers_idtf_result(more_detail); // 保存到文件 - break; case AsstMsg::StageDrops: more_detail = p_this->organize_stage_drop(more_detail); break; @@ -653,70 +567,26 @@ void asst::Assistance::append_callback(AsstMsg msg, json::value detail) void Assistance::clear_exec_times() { - for (auto&& pair : TaskConfiger::get_instance().m_all_tasks_info) { - pair.second->exec_times = 0; - } - ItemConfiger::get_instance().clear_drop_count(); -} - -void asst::Assistance::set_opers_idtf_result(const json::value& detail) -{ - constexpr static const char* Filename = "operators.json"; - std::ofstream ofs(GetCurrentDir() + Filename, std::ios::out); - ofs << detail.format() << std::endl; - ofs.close(); -} - -std::optional> Assistance::get_opers_idtf_result() -{ - constexpr static const char* Filename = "operators.json"; - std::ifstream ifs(GetCurrentDir() + Filename, std::ios::in); - if (!ifs.is_open()) { - return std::nullopt; - } - std::stringstream iss; - iss << ifs.rdbuf(); - ifs.close(); - std::string content(iss.str()); - - auto&& parse_ret = json::parse(content); - if (!parse_ret) { - DebugTraceError(__FUNCTION__, "json parsing error!"); - return std::nullopt; - } - json::value root = std::move(parse_ret.value()); - std::unordered_map opers_info; - try { - for (const json::value& info_json : root["all"].as_array()) { - OperInfrastInfo info; - info.name = info_json.at("name").as_string(); - info.elite = info_json.at("elite").as_integer(); - info.level = info_json.get("level", 0); - opers_info.emplace(info.name, std::move(info)); - } - } - catch (json::exception& exp) { - DebugTraceError(__FUNCTION__, "json parsing error!", exp.what()); - return std::nullopt; - } - return opers_info; + resource.task().clear_exec_times(); + resource.item().clear_drop_count(); } json::value asst::Assistance::organize_stage_drop(const json::value& rec) { json::value dst = rec; + auto& item = resource.item(); for (json::value& drop : dst["drops"].as_array()) { std::string id = drop["itemId"].as_string(); int quantity = drop["quantity"].as_integer(); - ItemConfiger::get_instance().m_drop_count[id] += quantity; - const std::string& name = ItemConfiger::get_instance().m_item_name[id]; + item.increase_drop_count(id, quantity); + const std::string& name = item.get_item_name(id); drop["itemName"] = name.empty() ? "未知材料" : name; } std::vector statistics_vec; - for (auto&& [id, count] : ItemConfiger::get_instance().m_drop_count) { + for (auto&& [id, count] : item.get_drop_count()) { json::value info; info["itemId"] = id; - const std::string& name = ItemConfiger::get_instance().m_item_name[id]; + const std::string& name = item.get_item_name(id); info["itemName"] = name.empty() ? "未知材料" : name; info["count"] = count; statistics_vec.emplace_back(std::move(info)); @@ -730,7 +600,7 @@ json::value asst::Assistance::organize_stage_drop(const json::value& rec) dst["statistics"] = json::array(std::move(statistics_vec)); - DebugTrace("organize_stage_drop | ", dst.to_string()); + log.trace("organize_stage_drop | ", dst.to_string()); return dst; } \ No newline at end of file diff --git a/src/MeoAssistance/Assistance.h b/src/MeoAssistance/Assistance.h index 323581e8f3..a69005975f 100644 --- a/src/MeoAssistance/Assistance.h +++ b/src/MeoAssistance/Assistance.h @@ -17,7 +17,7 @@ namespace cv { } namespace asst { - class WinMacro; + class Controller; class Identify; class Assistance @@ -41,7 +41,7 @@ namespace asst { bool start_visit(bool with_shopping = false); // 开始公开招募操作 - bool start_open_recruit(const std::vector& required_level, bool set_time = true); + bool start_recruiting(const std::vector& required_level, bool set_time = true); // 开始干员识别任务 bool start_to_identify_opers(); // 开始全自动基建任务 @@ -70,13 +70,8 @@ namespace asst { void append_task(const json::value& detail, bool front = false); void append_callback(AsstMsg msg, json::value detail); void clear_exec_times(); - static void set_opers_idtf_result(const json::value& detail); // 保存干员识别结果 - static std::optional> - get_opers_idtf_result(); // 读取干员识别结果 json::value organize_stage_drop(const json::value& rec); // 整理关卡掉落的材料信息 - std::shared_ptr m_controller_ptr = nullptr; - std::shared_ptr m_identify_ptr = nullptr; bool m_inited = false; bool m_thread_exit = false; diff --git a/src/MeoAssistance/AsstCaller.cpp b/src/MeoAssistance/AsstCaller.cpp index d42a61960d..51c7b90c56 100644 --- a/src/MeoAssistance/AsstCaller.cpp +++ b/src/MeoAssistance/AsstCaller.cpp @@ -156,31 +156,31 @@ const char* AsstGetVersion() return asst::Version; } -bool AsstStartOpenRecruit(void* p_asst, const int required_level[], int required_len, bool set_time) +bool AsstStartRecruiting(void* p_asst, const int required_level[], int required_len, bool set_time) { if (p_asst == nullptr) { return false; } std::vector level_vector; level_vector.assign(required_level, required_level + required_len); - return ((asst::Assistance*)p_asst)->start_open_recruit(level_vector, set_time); + return ((asst::Assistance*)p_asst)->start_recruiting(level_vector, set_time); } -bool AsstStartIndertifyOpers(void* p_asst) -{ - if (p_asst == nullptr) { - return false; - } - return ((asst::Assistance*)p_asst)->start_to_identify_opers(); -} - -bool AsstStartInfrast(void* p_asst) -{ - if (p_asst == nullptr) { - return false; - } - return ((asst::Assistance*)p_asst)->start_infrast(); -} +//bool AsstStartIndertifyOpers(void* p_asst) +//{ +// if (p_asst == nullptr) { +// return false; +// } +// return ((asst::Assistance*)p_asst)->start_to_identify_opers(); +//} +// +//bool AsstStartInfrast(void* p_asst) +//{ +// if (p_asst == nullptr) { +// return false; +// } +// return ((asst::Assistance*)p_asst)->start_infrast(); +//} bool AsstStartDebugTask(void* p_asst) { diff --git a/src/MeoAssistance/AsstDef.h b/src/MeoAssistance/AsstDef.h index 1b2c0da218..62675151fe 100644 --- a/src/MeoAssistance/AsstDef.h +++ b/src/MeoAssistance/AsstDef.h @@ -13,28 +13,6 @@ namespace json } namespace asst { - enum class ProcessTaskAction { - Invalid = 0, - BasicClick = 0x100, - ClickSelf = BasicClick | 1, // 点击自身位置 - ClickRect = BasicClick | 2, // 点击指定区域 - ClickRand = BasicClick | 4, // 点击随机区域 - DoNothing = 0x200, // 什么都不做 - Stop = 0x400, // 停止当前Task - StageDrops = 0x800, // 截图 - BasicSwipe = 0x1000, - SwipeToTheLeft = BasicSwipe | 1, // 往左划一下 - SwipeToTheRight = BasicSwipe | 2, // 往右划一下 - }; - - enum class AlgorithmType { - Invaild = -1, - JustReturn, - MatchTemplate, - CompareHist, - OcrDetect - }; - struct Point { Point() = default; @@ -72,6 +50,7 @@ namespace asst { } Rect& operator=(const Rect&) noexcept = default; Rect& operator=(Rect&&) noexcept = default; + bool empty() const noexcept { return width == 0 || height == 0; } bool operator==(const Rect& rhs) const noexcept { return x == rhs.x && y == rhs.y && width == rhs.width && height == rhs.height; @@ -90,28 +69,100 @@ namespace asst { int height = 0; }; - struct TextArea { - TextArea() = default; - TextArea(const TextArea&) = default; - TextArea(TextArea&&) noexcept = default; - template - TextArea(std::string text, RectArgs &&... rect_args) - : text(std::move(text)), - rect(std::forward(rect_args)...) { - static_assert(std::is_constructible::value, - "Parameter can't be used to construct a asst::Rect"); - } - operator std::string() const { return text; } - TextArea& operator=(const TextArea&) = default; - TextArea& operator=(TextArea&&) noexcept = default; - bool operator==(const TextArea& rhs) const noexcept - { + struct TextRect { + TextRect() = default; + TextRect(const TextRect&) = default; + TextRect(TextRect&&) noexcept = default; + + explicit operator std::string() const noexcept { return text; } + explicit operator Rect() const noexcept { return rect; } + TextRect& operator=(const TextRect&) = default; + TextRect& operator=(TextRect&&) noexcept = default; + bool operator==(const TextRect& rhs) const noexcept { return text == rhs.text && rect == rhs.rect; } std::string text; Rect rect; }; + using TextRectProc = std::function; + + enum class AlgorithmType { + Invaild = -1, + JustReturn, + MatchTemplate, + CompareHist, + OcrDetect + }; + + struct MatchRect { + MatchRect() = default; + MatchRect(const MatchRect&) = default; + MatchRect(MatchRect&&) noexcept = default; + + explicit operator Rect() const noexcept { return rect; } + MatchRect& operator=(const MatchRect&) = default; + MatchRect& operator=(MatchRect&&) noexcept = default; + + AlgorithmType algorithm; + double score = 0.0; + Rect rect; + }; + + enum class ProcessTaskAction { + Invalid = 0, + BasicClick = 0x100, + ClickSelf = BasicClick | 1, // 点击自身位置 + ClickRect = BasicClick | 2, // 点击指定区域 + ClickRand = BasicClick | 4, // 点击随机区域 + DoNothing = 0x200, // 什么都不做 + Stop = 0x400, // 停止当前Task + StageDrops = 0x800, // 关卡结束,特化动作 + BasicSwipe = 0x1000, + SwipeToTheLeft = BasicSwipe | 1, // 往左划一下 + SwipeToTheRight = BasicSwipe | 2, // 往右划一下 + }; + + // 任务信息 + struct TaskInfo { + virtual ~TaskInfo() = default; + std::string name; // 任务名 + AlgorithmType algorithm = // 图像算法类型 + AlgorithmType::Invaild; + ProcessTaskAction action = // 要进行的操作 + ProcessTaskAction::Invalid; + std::vector next; // 下一个可能的任务(列表) + int exec_times = 0; // 任务已执行了多少次 + int max_times = INT_MAX; // 任务最多执行多少次 + std::vector exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表) + std::vector reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1 + asst::Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域 + int pre_delay = 0; // 执行该任务前的延时 + int rear_delay = 0; // 执行该任务后的延时 + int retry_times = INT_MAX; // 未找到图像时的重试次数 + Rect roi; // 要识别的区域,若为0则全图识别 + Rect result_move; // 识别结果移动:有些结果识别到的,和要点击的不是同一个位置。即识别到了res,点击res + result_move的位置 + }; + + // 文字识别任务的信息 + struct OcrTaskInfo : public TaskInfo { + virtual ~OcrTaskInfo() = default; + std::vector text; // 文字的容器,匹配到这里面任一个,就算匹配上了 + bool need_full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了 + std::unordered_map + replace_map; // 部分文字容易识别错,字符串强制replace之后,再进行匹配 + bool cache = false; // 是否使用历史区域 + std::vector region_of_appeared; // 曾经出现过的区域:上次处理该任务时,在一些rect里识别到过text,这次优先在这些rect里识别,省点性能 + }; + + // 图片匹配任务的信息 + struct MatchTaskInfo : public TaskInfo { + virtual ~MatchTaskInfo() = default; + std::string templ_name; // 匹配模板图片文件名 + double templ_threshold = 0; // 模板匹配阈值 + double hist_threshold = 0; // 直方图比较阈值 + bool cache = false; // 是否使用缓存(直方图),false时就一直用模板匹配。默认为true + }; struct HandleInfo { std::string class_name; @@ -138,116 +189,10 @@ namespace asst { std::string path; }; - // 任务信息 - struct TaskInfo { - virtual ~TaskInfo() = default; - std::string name; // 任务名 - AlgorithmType algorithm = // 图像算法类型 - AlgorithmType::Invaild; - ProcessTaskAction action = // 要进行的操作 - ProcessTaskAction::Invalid; - std::vector next; // 下一个可能的任务(列表) - int exec_times = 0; // 任务已执行了多少次 - int max_times = INT_MAX; // 任务最多执行多少次 - std::vector exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表) - std::vector reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1 - asst::Rect specific_area; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域 - int pre_delay = 0; // 执行该任务前的延时 - int rear_delay = 0; // 执行该任务后的延时 - int retry_times = INT_MAX; // 未找到图像时的重试次数 - Rect identify_area; // 要识别的区域,若为0则全图识别 - }; - - // 文字识别任务的信息 - struct OcrTaskInfo : public TaskInfo { - virtual ~OcrTaskInfo() = default; - std::vector text; // 文字的容器,匹配到这里面任一个,就算匹配上了 - bool need_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了 - std::unordered_map - replace_map; // 部分文字容易识别错,字符串强制replace之后,再进行匹配 - bool cache = false; // 是否使用历史区域 - std::vector history_area; // 历史区域:上次处理该任务时,在一些rect里识别到过text,这次优先在这些rect里识别,省点性能 - }; - - // 图片匹配任务的信息 - struct MatchTaskInfo : public TaskInfo { - virtual ~MatchTaskInfo() = default; - std::string template_filename; // 匹配模板图片文件名 - double templ_threshold = 0; // 模板匹配阈值 - double hist_threshold = 0; // 直方图比较阈值 - bool cache = false; // 是否使用缓存(直方图),false时就一直用模板匹配。默认为true - }; - - enum class ConnectType { - Emulator, - USB, - Remote - }; - - struct Options { - ConnectType connect_type; // 连接类型 - std::string connect_remote_address; // 连接局域网中的安卓设备的地址,仅在connectType为Remote时生效 - int task_delay = 0; // 任务间延时:越快操作越快,但会增加CPU消耗 - int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时 - int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时 - bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下 - bool upload_to_penguin = false; // 上传企鹅物流:每次到结算界面,是否上传企鹅物流数据统计网站 https://penguin-stats.cn/ - std::string penguin_api; // 企鹅物流汇报接口 - std::string penguin_server; // 企鹅物流汇报接口"server"字段,"CN", "US", "JP" and "KR". - int ocr_gpu_index = -1; // OcrLite使用GPU编号,-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/... - int ocr_thread_number = 0; // OcrLite线程数量 - }; - - struct InfrastOptions { // 基建的选项 - double dorm_threshold = 0; // 宿舍心情百分比阈值,心情百分比小于该值的干员会被放进宿舍 - }; - - // 干员信息,公开招募相关 - struct OperRecruitInfo { - std::string name; - std::string type; - int level = 0; - std::string sex; - std::unordered_set tags; - bool hidden = false; - std::string name_en; - }; - - // 公开招募的干员组合 - struct OperRecruitCombs { - std::vector opers; - int max_level = 0; - int min_level = 0; - double avg_level = 0; - }; - - // 干员信息,基建相关 - struct OperInfrastInfo { - std::string name; - int elite = 0; // 精英化等级 - int level = 0; // 等级 - bool operator==(const OperInfrastInfo& rhs) const { - return name == rhs.name; - } - }; - // 基建干员组合 - struct OperInfrastComb { - std::vector comb; - int efficiency = 0; // 组合的效率 - }; - constexpr double DoubleDiff = 1e-12; } namespace std { - template<> - class hash { - public: - size_t operator()(const asst::OperInfrastInfo& info) const - { - return std::hash()(info.name); - } - }; template<> class hash { public: @@ -260,9 +205,9 @@ namespace std { } }; template<> - class hash { + class hash { public: - size_t operator()(const asst::TextArea& textarea) const + size_t operator()(const asst::TextRect& textarea) const { return std::hash()(textarea.text) ^ std::hash()(textarea.rect); diff --git a/src/MeoAssistance/Configer.h b/src/MeoAssistance/Configer.h deleted file mode 100644 index f3cca09ff5..0000000000 --- a/src/MeoAssistance/Configer.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "AbstractConfiger.h" - -#include -#include -#include - -#include "AsstDef.h" - -namespace asst { - class Configer : public AbstractConfiger - { - public: - virtual ~Configer() = default; - - static Configer& get_instance() { - static Configer unique_instance; - return unique_instance; - } - - constexpr static int WindowWidthDefault = 1280; - constexpr static int WindowHeightDefault = 720; - constexpr static double TemplThresholdDefault = 0.9; - constexpr static double HistThresholdDefault = 0.9; - - std::string m_version; - Options m_options; - std::unordered_map m_handles; - std::unordered_map m_recruit_ocr_replace; - std::unordered_map m_infrast_ocr_replace; - - InfrastOptions m_infrast_options; - - protected: - Configer() = default; - Configer(const Configer& rhs) = default; - Configer(Configer&& rhs) noexcept = default; - - Configer& operator=(const Configer& rhs) = default; - Configer& operator=(Configer&& rhs) noexcept = default; - - virtual bool parse(const json::value& json) override; - }; -} diff --git a/src/MeoAssistance/WinMacro.cpp b/src/MeoAssistance/Controller.cpp similarity index 81% rename from src/MeoAssistance/WinMacro.cpp rename to src/MeoAssistance/Controller.cpp index 8f881babd8..006186960c 100644 --- a/src/MeoAssistance/WinMacro.cpp +++ b/src/MeoAssistance/Controller.cpp @@ -1,4 +1,4 @@ -#include "WinMacro.h" +#include "Controller.h" #include #include @@ -12,15 +12,15 @@ #include "AsstDef.h" #include "Logger.hpp" -#include "Configer.h" +#include "Resource.h" #include "UserConfiger.h" using namespace asst; -WinMacro::WinMacro() +Controller::Controller() : m_rand_engine(std::chrono::system_clock::now().time_since_epoch().count()) { - DebugTraceFunction; + LogTraceFunction; // 安全属性描述符 m_pipe_sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES); @@ -44,13 +44,13 @@ WinMacro::WinMacro() m_child_startup_info.hStdOutput = m_pipe_child_write; m_child_startup_info.hStdError = m_pipe_child_write; - auto bind_pipe_working_proc = std::bind(&WinMacro::pipe_working_proc, this); + auto bind_pipe_working_proc = std::bind(&Controller::pipe_working_proc, this); m_cmd_thread = std::thread(bind_pipe_working_proc); } -asst::WinMacro::~WinMacro() +asst::Controller::~Controller() { - DebugTraceFunction; + LogTraceFunction; m_thread_exit = true; //m_thread_idle = true; @@ -67,27 +67,27 @@ asst::WinMacro::~WinMacro() ::CloseHandle(m_pipe_child_write); } -Rect asst::WinMacro::shaped_correct(const Rect& rect) const +Rect asst::Controller::shaped_correct(const Rect& rect) const { if (rect.width == 0 || rect.height == 0) { return rect; } // 明日方舟在异形屏上,有的地方是按比例缩放的,有的地方又是直接位移。没法整,这里简单粗暴一点截一个长条 Rect dst = rect; - if (m_scale_size.first != Configer::WindowWidthDefault) { // 说明是宽屏 + if (m_scale_size.first != GeneralConfiger::WindowWidthDefault) { // 说明是宽屏 dst.x = 0; dst.width = m_scale_size.first - 1; } - else if (m_scale_size.second != Configer::WindowHeightDefault) { // 说明是偏方形屏 + else if (m_scale_size.second != GeneralConfiger::WindowHeightDefault) { // 说明是偏方形屏 dst.y = 0; dst.height = m_scale_size.second - 1; } return dst; } -void asst::WinMacro::pipe_working_proc() +void asst::Controller::pipe_working_proc() { - DebugTraceFunction; + LogTraceFunction; while (!m_thread_exit) { std::unique_lock cmd_queue_lock(m_cmd_queue_mutex); @@ -119,9 +119,9 @@ void asst::WinMacro::pipe_working_proc() } } -bool WinMacro::try_capture(const EmulatorInfo& info, bool without_handle) +bool Controller::try_capture(const EmulatorInfo& info, bool without_handle) { - DebugTraceFunction; + LogTraceFunction; const HandleInfo& handle_info = info.handle; std::string adb_dir; @@ -177,7 +177,7 @@ bool WinMacro::try_capture(const EmulatorInfo& info, bool without_handle) if (emulator_path.empty()) { return false; } - UserConfiger::get_instance().set_emulator_path(info.name, emulator_path); + resource.cfg().set_emulator_path(info.name, emulator_path); } else { emulator_path = info.path; @@ -186,7 +186,7 @@ bool WinMacro::try_capture(const EmulatorInfo& info, bool without_handle) // 到这一步说明句柄和权限没问题了,接下来就是adb的事情了 m_emulator_info = info; m_handle = window_handle; - DebugTrace("Handle:", m_handle, "Name:", m_emulator_info.name); + log.trace("Handle:", m_handle, "Name:", m_emulator_info.name); adb_dir = emulator_path.substr(0, emulator_path.find_last_of('\\') + 1); adb_dir = '"' + StringReplaceAll(m_emulator_info.adb.path, "[EmulatorPath]", adb_dir) + '"'; @@ -215,21 +215,21 @@ bool WinMacro::try_capture(const EmulatorInfo& info, bool without_handle) m_emulator_info.adb.display_height = (std::min)(size_value1, size_value2); constexpr double DefaultRatio = - static_cast(Configer::WindowWidthDefault) / static_cast(Configer::WindowHeightDefault); + static_cast(GeneralConfiger::WindowWidthDefault) / static_cast(GeneralConfiger::WindowHeightDefault); double cur_ratio = static_cast(m_emulator_info.adb.display_width) / static_cast(m_emulator_info.adb.display_height); if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放 || std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) { - int scale_width = cur_ratio * Configer::WindowHeightDefault; - m_scale_size = std::make_pair(scale_width, Configer::WindowHeightDefault); - m_control_scale = static_cast(m_emulator_info.adb.display_height) / static_cast(Configer::WindowHeightDefault); + int scale_width = cur_ratio * GeneralConfiger::WindowHeightDefault; + m_scale_size = std::make_pair(scale_width, GeneralConfiger::WindowHeightDefault); + m_control_scale = static_cast(m_emulator_info.adb.display_height) / static_cast(GeneralConfiger::WindowHeightDefault); } else { // 否则可能是偏正方形的屏幕,按宽度计算 - int scale_height = Configer::WindowWidthDefault / cur_ratio; - m_scale_size = std::make_pair(Configer::WindowWidthDefault, scale_height); - m_control_scale = static_cast(m_emulator_info.adb.display_width) / static_cast(Configer::WindowWidthDefault); + int scale_height = GeneralConfiger::WindowWidthDefault / cur_ratio; + m_scale_size = std::make_pair(GeneralConfiger::WindowWidthDefault, scale_height); + m_control_scale = static_cast(m_emulator_info.adb.display_width) / static_cast(GeneralConfiger::WindowWidthDefault); } m_emulator_info.adb.click = StringReplaceAll(m_emulator_info.adb.click, "[Adb]", adb_dir); @@ -239,9 +239,9 @@ bool WinMacro::try_capture(const EmulatorInfo& info, bool without_handle) return true; } -//void asst::WinMacro::set_idle(bool flag) +//void asst::Controller::set_idle(bool flag) //{ -// DebugTraceFunction; +// LogTraceFunction; // // m_thread_idle = flag; // if (!flag) { @@ -252,9 +252,9 @@ bool WinMacro::try_capture(const EmulatorInfo& info, bool without_handle) // } //} -std::vector WinMacro::call_command(const std::string& cmd) +std::vector Controller::call_command(const std::string& cmd) { - DebugTraceFunction; + LogTraceFunction; static std::mutex pipe_mutex; std::unique_lock pipe_lock(pipe_mutex); @@ -287,17 +287,17 @@ std::vector WinMacro::call_command(const std::string& cmd) ::CloseHandle(process_info.hProcess); ::CloseHandle(process_info.hThread); - DebugTrace("Call", cmd, "ret", exit_ret, ", output size:", pipe_data.size()); + log.trace("Call", cmd, "ret", exit_ret, ", output size:", pipe_data.size()); if (!pipe_data.empty() && pipe_data.size() < 4096) { - DebugTrace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); + log.trace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); } return pipe_data; } -void asst::WinMacro::convert_lf(std::vector& data) +void asst::Controller::convert_lf(std::vector& data) { - DebugTraceFunction; + LogTraceFunction; if (data.empty() || data.size() <= 1) { return; @@ -328,7 +328,7 @@ void asst::WinMacro::convert_lf(std::vector& data) data.erase(next_iter, data.end()); } -Point asst::WinMacro::rand_point_in_rect(const Rect& rect) +Point asst::Controller::rand_point_in_rect(const Rect& rect) { int x = 0, y = 0; if (rect.width == 0) { @@ -351,7 +351,7 @@ Point asst::WinMacro::rand_point_in_rect(const Rect& rect) return Point(x, y); } -int asst::WinMacro::push_cmd(const std::string& cmd) +int asst::Controller::push_cmd(const std::string& cmd) { std::unique_lock lock(m_cmd_queue_mutex); m_cmd_queue.emplace(cmd); @@ -359,16 +359,16 @@ int asst::WinMacro::push_cmd(const std::string& cmd) return ++m_push_id; } -void asst::WinMacro::wait(unsigned id) const noexcept +void asst::Controller::wait(unsigned id) const noexcept { while (id > m_completed_id) { std::this_thread::yield(); } } -bool asst::WinMacro::screencap() +bool asst::Controller::screencap() { - DebugTraceFunction; + LogTraceFunction; auto data = call_command(m_emulator_info.adb.screencap); if (!data.empty()) { @@ -377,12 +377,12 @@ bool asst::WinMacro::screencap() } m_cache_image = cv::imdecode(data, cv::IMREAD_COLOR); if (m_cache_image.empty()) { - DebugTraceInfo("Data is not empty, but image is empty, try to convert lf"); + log.info("Data is not empty, but image is empty, try to convert lf"); convert_lf(data); m_cache_image = cv::imdecode(data, cv::IMREAD_COLOR); if (m_cache_image.empty()) { m_image_convert_lf = false; - DebugTraceError("convert lf and retry decode falied!"); + log.error("convert lf and retry decode falied!"); return false; } m_image_convert_lf = true; @@ -391,7 +391,7 @@ bool asst::WinMacro::screencap() return true; } else { - DebugTraceError("Data is empty!"); + log.error("Data is empty!"); return false; } @@ -400,25 +400,25 @@ bool asst::WinMacro::screencap() //m_cache_image = std::move(temp_image); } -int WinMacro::click(const Point& p, bool block) +int Controller::click(const Point& p, bool block) { int x = p.x * m_control_scale; int y = p.y * m_control_scale; - //DebugTrace("Click, raw:", p.x, p.y, "corr:", x, y); + //log.trace("Click, raw:", p.x, p.y, "corr:", x, y); return click_without_scale(Point(x, y), block); } -int WinMacro::click(const Rect& rect, bool block) +int Controller::click(const Rect& rect, bool block) { return click(rand_point_in_rect(rect), block); } -int asst::WinMacro::click_without_scale(const Point& p, bool block) +int asst::Controller::click_without_scale(const Point& p, bool block) { if (p.x < 0 || p.x >= m_emulator_info.adb.display_width || p.y < 0 || p.y >= m_emulator_info.adb.display_height) { - DebugTraceError("click point out of range"); + log.error("click point out of range"); } std::string cur_cmd = StringReplaceAll(m_emulator_info.adb.click, "[x]", std::to_string(p.x)); cur_cmd = StringReplaceAll(cur_cmd, "[y]", std::to_string(p.y)); @@ -429,34 +429,34 @@ int asst::WinMacro::click_without_scale(const Point& p, bool block) return id; } -int asst::WinMacro::click_without_scale(const Rect& rect, bool block) +int asst::Controller::click_without_scale(const Rect& rect, bool block) { return click_without_scale(rand_point_in_rect(rect), block); } -int asst::WinMacro::swipe(const Point& p1, const Point& p2, int duration, bool block, int extra_delay) +int asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool block, int extra_delay) { int x1 = p1.x * m_control_scale; int y1 = p1.y * m_control_scale; int x2 = p2.x * m_control_scale; int y2 = p2.y * m_control_scale; - //DebugTrace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2); + //log.trace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2); return swipe_without_scale(Point(x1, y1), Point(x2, y2), duration, block, extra_delay); } -int asst::WinMacro::swipe(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay) +int asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay) { return swipe(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay); } -int asst::WinMacro::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay) +int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay) { if (p1.x < 0 || p1.x >= m_emulator_info.adb.display_width || p1.y < 0 || p1.y >= m_emulator_info.adb.display_height || p2.x < 0 || p2.x >= m_emulator_info.adb.display_width || p2.y < 0 || p2.y >= m_emulator_info.adb.display_height) { - DebugTraceError("swipe point out of range"); + log.error("swipe point out of range"); } std::string cur_cmd = StringReplaceAll(m_emulator_info.adb.swipe, "[x1]", std::to_string(p1.x)); cur_cmd = StringReplaceAll(cur_cmd, "[y1]", std::to_string(p1.y)); @@ -477,12 +477,12 @@ int asst::WinMacro::swipe_without_scale(const Point& p1, const Point& p2, int du return id; } -int asst::WinMacro::swipe_without_scale(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay) +int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay) { return swipe_without_scale(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay); } -cv::Mat asst::WinMacro::get_image(bool raw) +cv::Mat asst::Controller::get_image(bool raw) { if (!screencap()) { return cv::Mat(); diff --git a/src/MeoAssistance/WinMacro.h b/src/MeoAssistance/Controller.h similarity index 87% rename from src/MeoAssistance/WinMacro.h rename to src/MeoAssistance/Controller.h index 554d333d33..8619d086c1 100644 --- a/src/MeoAssistance/WinMacro.h +++ b/src/MeoAssistance/Controller.h @@ -18,15 +18,19 @@ #include "AsstDef.h" namespace asst { - class WinMacro + class Controller { public: - WinMacro(); - ~WinMacro(); + Controller(const Controller&) = delete; + Controller(Controller&&) = delete; + ~Controller(); + + static Controller& get_instance() { + static Controller unique_instance; + return unique_instance; + } bool try_capture(const EmulatorInfo& info, bool without_handle = false); - //void set_idle(bool flag); - cv::Mat get_image(bool raw = false); // 点击和滑动都是异步执行,返回该任务的id @@ -43,7 +47,12 @@ namespace asst { // 异形屏矫正 Rect shaped_correct(const Rect& rect) const; + + Controller& operator=(const Controller&) = delete; + Controller& operator=(Controller&&) = delete; private: + Controller(); + void pipe_working_proc(); std::vector call_command(const std::string& cmd); int push_cmd(const std::string& cmd); @@ -81,4 +90,6 @@ namespace asst { std::pair m_scale_size; double m_control_scale = 1.0; }; + + static auto& ctrler = Controller::get_instance(); } diff --git a/src/MeoAssistance/CreditShopImageAnalyzer.cpp b/src/MeoAssistance/CreditShopImageAnalyzer.cpp new file mode 100644 index 0000000000..24bba53063 --- /dev/null +++ b/src/MeoAssistance/CreditShopImageAnalyzer.cpp @@ -0,0 +1,123 @@ +#include "CreditShopImageAnalyzer.h" + +#include "MultiMatchImageAnalyzer.h" +#include "OcrImageAnalyzer.h" +#include "MatchImageAnalyzer.h" +#include "Resource.h" + +#include "AsstAux.h" + +bool asst::CreditShopImageAnalyzer::analyze() +{ + m_credit_points.clear(); + m_need_to_buy.clear(); + m_result.clear(); + + return credit_point_analyze() + && commoditys_analyze() + && sold_out_analyze(); +} + +bool asst::CreditShopImageAnalyzer::credit_point_analyze() +{ + const static auto credit_point_task_ptr = std::dynamic_pointer_cast( + resource.task().task_ptr("CreditShop-CreditPoint")); + + // 识别信用点的图标 + MultiMatchImageAnalyzer mm_annlyzer( + m_image, + credit_point_task_ptr->roi, + credit_point_task_ptr->templ_name, + credit_point_task_ptr->templ_threshold); + + if (!mm_annlyzer.analyze()) { + return false; + } + auto credit_points_result = mm_annlyzer.get_result(); + if (credit_points_result.empty()) { + return false; + } + // 按位置排个序 + std::sort(credit_points_result.begin(), credit_points_result.end(), + [](const MatchRect& lhs, const MatchRect& rhs) -> bool { + if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的,按x排序 + return lhs.rect.x < rhs.rect.x; + } + else { + return lhs.rect.y < rhs.rect.y; + } + } + ); + m_credit_points.reserve(credit_points_result.size()); + for (const MatchRect& mr : credit_points_result) { + m_credit_points.emplace_back(mr.rect); + } + + return true; +} + +bool asst::CreditShopImageAnalyzer::commoditys_analyze() +{ + for (const Rect& credit_point : m_credit_points) { + // 商品名的区域 + Rect name(credit_point.x - 80, credit_point.y - 210, 230, 70); + if (name.x < 0) { + name.x = 0; + } + + // TODO,哪些要买哪些不买,提供个接口出去 + static const std::vector not_to_buy_names = { "碳", "家具" }; + + OcrImageAnalyzer ocr_analyzer(m_image, name); + if (!ocr_analyzer.analyze()) { + continue; + } + const auto& ocr_res = ocr_analyzer.get_result(); + + auto find_iter = std::find_first_of(ocr_res.cbegin(), ocr_res.cend(), not_to_buy_names.cbegin(), not_to_buy_names.cend(), + [](const auto& lhs, const std::string& rhs) -> bool { + return lhs.text.find(rhs) != std::string::npos; + }); + if (find_iter != ocr_res.cend()) { // 找到了不需要买的名字 +#ifdef LOG_TRACE + cv::putText(m_image, "Not to buy", cv::Point(credit_point.x, credit_point.y), 1, 2, cv::Scalar(255, 0, 0)); +#endif // LOG_TRACE + continue; + } + Rect commodity(credit_point.x - 60, credit_point.y - 180, 220, 220); +#ifdef LOG_TRACE + cv::rectangle(m_image, make_rect(commodity), cv::Scalar(0, 0, 255), 2); +#endif + m_need_to_buy.emplace_back(commodity); + } + return !m_need_to_buy.empty(); +} + +bool asst::CreditShopImageAnalyzer::sold_out_analyze() +{ + const static auto sold_out_task_ptr = std::dynamic_pointer_cast( + resource.task().task_ptr("CreditShop-SoldOut")); + + // 识别是否售罄 + MatchImageAnalyzer sold_out_analyzer( + m_image, + sold_out_task_ptr->roi, + sold_out_task_ptr->templ_name, + sold_out_task_ptr->templ_threshold); + + for (const Rect& commodity : m_need_to_buy) { + sold_out_analyzer.set_roi(commodity); + if (sold_out_analyzer.analyze()) { +#ifdef LOG_TRACE + cv::rectangle(m_image, make_rect(commodity), cv::Scalar(0, 0, 255)); + cv::putText(m_image, "Sold Out", cv::Point(commodity.x, commodity.y), 1, 2, cv::Scalar(255, 0, 0)); +#endif // LOG_TRACE + + // 如果识别到了售罄,那这个商品就不用买了,跳过 + continue; + } + m_result.emplace_back(commodity); + } + + return !m_result.empty(); +} \ No newline at end of file diff --git a/src/MeoAssistance/CreditShopImageAnalyzer.h b/src/MeoAssistance/CreditShopImageAnalyzer.h new file mode 100644 index 0000000000..7e4de30a83 --- /dev/null +++ b/src/MeoAssistance/CreditShopImageAnalyzer.h @@ -0,0 +1,25 @@ +#pragma once +#include "AbstractImageAnalyzer.h" + +namespace asst { + class CreditShopImageAnalyzer final : public AbstractImageAnalyzer + { + public: + using AbstractImageAnalyzer::AbstractImageAnalyzer; + virtual ~CreditShopImageAnalyzer() = default; + + virtual bool analyze() override; + + const std::vector& get_result() const noexcept { + return m_result; + } + protected: + bool credit_point_analyze(); + bool commoditys_analyze(); + bool sold_out_analyze(); + + std::vector m_credit_points; + std::vector m_need_to_buy; + std::vector m_result; + }; +} diff --git a/src/MeoAssistance/CreditShoppingTask.cpp b/src/MeoAssistance/CreditShoppingTask.cpp index ba645de6d7..1e60bd91eb 100644 --- a/src/MeoAssistance/CreditShoppingTask.cpp +++ b/src/MeoAssistance/CreditShoppingTask.cpp @@ -5,116 +5,75 @@ #include -#include "Identify.h" -#include "WinMacro.h" -#include "Configer.h" +#include "Controller.h" +#include "CreditShopImageAnalyzer.h" +#include "MatchImageAnalyzer.h" +#include "OcrImageAnalyzer.h" +#include "Resource.h" bool asst::CreditShoppingTask::run() { - if (m_controller_ptr == nullptr - || m_identify_ptr == nullptr) - { - m_callback(AsstMsg::PtrIsNull, json::value(), m_callback_arg); + const cv::Mat& image = ctrler.get_image(); + + CreditShopImageAnalyzer shop_analyzer(image); + if (!shop_analyzer.analyze()) { return false; } + const auto& shopping_list = shop_analyzer.get_result(); - cv::Mat image = m_controller_ptr->get_image(); - - // 识别信用点的图标 - auto find_res_vec = m_identify_ptr->find_all_images(image, "CreditPoint", Configer::TemplThresholdDefault, false); - - // 按位置排个序 - std::sort(find_res_vec.begin(), find_res_vec.end(), []( - const auto& lhs, const auto& rhs) -> bool { - if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的,按x排序 - return lhs.rect.x < rhs.rect.x; - } - else { - return lhs.rect.y < rhs.rect.y; - } - }); - - // 计算哪些商品是需要买的 - std::vector need_to_buy; - for (const auto& res : find_res_vec) { - cv::Rect name_rect; - name_rect.x = res.rect.x - 80; - name_rect.y = res.rect.y - 210; - name_rect.width = 230; - name_rect.height = 70; - if (name_rect.x < 0) { - name_rect.x = 0; - } - // 过滤掉不买的 - // TODO,哪些要买哪些不买,提供个接口出去 - static const std::vector not_to_buy_names = { "碳", "家具" }; - const auto& ocr_res = m_identify_ptr->ocr_detect(image(name_rect)); - auto find_iter = std::find_first_of(ocr_res.cbegin(), ocr_res.cend(), not_to_buy_names.cbegin(), not_to_buy_names.cend(), - [](const auto& lhs, const std::string& rhs) -> bool { - return lhs.text.find(rhs) != std::string::npos; - }); - if (find_iter != ocr_res.cend()) { -#ifdef LOG_TRACE - cv::putText(image, "Not to buy", cv::Point(res.rect.x, res.rect.y), 1, 2, cv::Scalar(255, 0, 0)); -#endif // LOG_TRACE - continue; - } - Rect commodity_rect(res.rect.x - 60, res.rect.y - 180, 220, 220); - // 识别“售罄” - auto sold_out_res = m_identify_ptr->find_image(image(make_rect(commodity_rect)), "SoldOut"); - -#ifdef LOG_TRACE - cv::rectangle(image, make_rect(commodity_rect), cv::Scalar(0, 0, 255)); - if (sold_out_res.score >= Configer::TemplThresholdDefault) { - cv::putText(image, "Sold Out", cv::Point(commodity_rect.x, commodity_rect.y), 1, 2, cv::Scalar(255, 0, 0)); - } -#endif // LOG_TRACE - - if (sold_out_res.score >= Configer::TemplThresholdDefault) { - continue; - } - need_to_buy.emplace_back(commodity_rect.center_zoom(0.8)); - } - - for (const Rect& commodity_rect : need_to_buy) { + for (const Rect& commodity : shopping_list) { if (need_exit()) { return false; } - m_controller_ptr->click(commodity_rect); - sleep(1000); - image = m_controller_ptr->get_image(); + ctrler.click(commodity); + + static int pre_delay = 1000; + static int rare_delay = 1000; + + sleep(pre_delay); // “购买商品”按钮 static Rect buy_it_rect; - if (buy_it_rect.width == 0) { - auto buy_it_res = m_identify_ptr->find_image(image, "BuyIt", Configer::TemplThresholdDefault); - if (buy_it_res.score < Configer::TemplThresholdDefault) { - // 没事别到“购买商品”按钮,不应该出现这种情况,TODO 报错 + if (buy_it_rect.empty()) { + const auto buy_it_task_ptr = std::dynamic_pointer_cast( + resource.task().task_ptr("CreditShop-BuyIt")); + + const cv::Mat& buy_image = ctrler.get_image(); + MatchImageAnalyzer buy_it_analyzer( + buy_image, + buy_it_task_ptr->roi, + buy_it_task_ptr->templ_name, + buy_it_task_ptr->templ_threshold); + if (!buy_it_analyzer.analyze()) { + // 没识别到“购买商品”按钮,不应该出现这种情况,TODO 报错 return false; } - buy_it_rect = buy_it_res.rect; + buy_it_rect = buy_it_analyzer.get_result().rect; + pre_delay = buy_it_task_ptr->pre_delay; + rare_delay = buy_it_task_ptr->rear_delay; } + if (need_exit()) { return false; } - m_controller_ptr->click(buy_it_rect); - sleep(1000); + ctrler.click(buy_it_rect); + sleep(rare_delay); + // 识别是否信用不足无法购买 - image = m_controller_ptr->get_image(); - static const std::vector shopping_finished = { "信用不足", "无法购买" }; - const auto& ocr_res = ocr_detect(image(cv::Rect(940, 60, 339, 110))); - auto find_iter = std::find_first_of(ocr_res.cbegin(), ocr_res.cend(), shopping_finished.cbegin(), shopping_finished.cend(), - [](const auto& lhs, const std::string& rhs) -> bool { - return lhs.text.find(rhs) != std::string::npos; - }); - if (find_iter != ocr_res.cend()) { + const cv::Mat& prompt_image = ctrler.get_image(); + + const static auto no_money_task_ptr = std::dynamic_pointer_cast( + resource.task().task_ptr("CreditShop-NoMoney")); + OcrImageAnalyzer prompt_analyzer(prompt_image, no_money_task_ptr->roi); + prompt_analyzer.set_required(no_money_task_ptr->text); + if (prompt_analyzer.analyze()) { click_return_button(); return true; } // 这里随便点一下都行,把购买完弹出来物品的界面取消掉 - m_controller_ptr->click(buy_it_rect); - sleep(1000); + ctrler.click(buy_it_rect); + sleep(rare_delay); } return true; diff --git a/src/MeoAssistance/CreditShoppingTask.h b/src/MeoAssistance/CreditShoppingTask.h index 2a9331fe1b..0c8b2396a4 100644 --- a/src/MeoAssistance/CreditShoppingTask.h +++ b/src/MeoAssistance/CreditShoppingTask.h @@ -1,15 +1,16 @@ #pragma once -#include "OcrAbstractTask.h" + +#include "AbstractTask.h" #include #include "AsstDef.h" namespace asst { - class CreditShoppingTask : public OcrAbstractTask + class CreditShoppingTask : public AbstractTask { public: - using OcrAbstractTask::OcrAbstractTask; + using AbstractTask::AbstractTask; virtual ~CreditShoppingTask() = default; virtual bool run() override; private: diff --git a/src/MeoAssistance/Configer.cpp b/src/MeoAssistance/GeneralConfiger.cpp similarity index 71% rename from src/MeoAssistance/Configer.cpp rename to src/MeoAssistance/GeneralConfiger.cpp index 1ca5da5602..3b25fd3d6c 100644 --- a/src/MeoAssistance/Configer.cpp +++ b/src/MeoAssistance/GeneralConfiger.cpp @@ -1,16 +1,8 @@ -#include "Configer.h" - -#include -#include -#include +#include "GeneralConfiger.h" #include -#include "Logger.hpp" - -using namespace asst; - -bool asst::Configer::parse(const json::value& json) +bool asst::GeneralConfiger::parse(const json::value& json) { m_version = json.at("version").as_string(); @@ -28,13 +20,6 @@ bool asst::Configer::parse(const json::value& json) m_options.penguin_server = options_json.get("penguinServer", "CN"); m_options.ocr_thread_number = options_json.at("ocrThreadNumber").as_integer(); } - DebugTrace("Options", options_json.to_string()); - - const json::value& infrast_options_json = json.at("infrastOptions"); - { - m_infrast_options.dorm_threshold = infrast_options_json.get("dormThreshold", 0.3); - } - DebugTrace("InfrastOptions", infrast_options_json.to_string()); for (const auto& [name, emulator_json] : json.at("emulator").as_object()) { EmulatorInfo emulator_info; @@ -56,14 +41,7 @@ bool asst::Configer::parse(const json::value& json) emulator_info.path = emulator_json.get("path", std::string()); - m_handles.emplace(name, std::move(emulator_info)); - } - - for (const json::value& rep : json.at("infrastOcrReplace").as_array()) { - m_infrast_ocr_replace.emplace(rep.as_array()[0].as_string(), rep.as_array()[1].as_string()); - } - for (const json::value& rep : json.at("recruitOcrReplace").as_array()) { - m_recruit_ocr_replace.emplace(rep.as_array()[0].as_string(), rep.as_array()[1].as_string()); + m_emulators_info.emplace(name, std::move(emulator_info)); } return true; diff --git a/src/MeoAssistance/GeneralConfiger.h b/src/MeoAssistance/GeneralConfiger.h new file mode 100644 index 0000000000..8bdd44cbb5 --- /dev/null +++ b/src/MeoAssistance/GeneralConfiger.h @@ -0,0 +1,73 @@ +#pragma once + +#include "AbstractConfiger.h" + +#include +#include +#include + +#include "AsstDef.h" + +namespace asst { + enum class ConnectType { + Emulator, + USB, + Remote + }; + + struct Options { + ConnectType connect_type; // 连接类型 + std::string connect_remote_address; // 连接局域网中的安卓设备的地址,仅在connectType为Remote时生效 + int task_delay = 0; // 任务间延时:越快操作越快,但会增加CPU消耗 + int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时 + int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时 + bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下 + bool upload_to_penguin = false; // 上传企鹅物流:每次到结算界面,是否上传企鹅物流数据统计网站 https://penguin-stats.cn/ + std::string penguin_api; // 企鹅物流汇报接口 + std::string penguin_server; // 企鹅物流汇报接口"server"字段,"CN", "US", "JP" and "KR". + int ocr_gpu_index = -1; // OcrLite使用GPU编号,-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/... + int ocr_thread_number = 0; // OcrLite线程数量 + }; + + class GeneralConfiger : public AbstractConfiger + { + public: + virtual ~GeneralConfiger() = default; + + constexpr static int WindowWidthDefault = 1280; + constexpr static int WindowHeightDefault = 720; + + const std::string& get_version() const noexcept + { + return m_version; + } + const Options& get_options() const noexcept + { + return m_options; + } + const std::unordered_map& get_emulators_info() const noexcept + { + return m_emulators_info; + } + + void set_options(Options opt) noexcept + { + m_options = std::move(opt); + } + void set_emulator_info(std::string name, EmulatorInfo emu) + { + m_emulators_info.emplace(std::move(name), std::move(emu)); + } + void set_emulator_path(std::string name, std::string path) + { + m_emulators_info[name].path = path; + } + + protected: + virtual bool parse(const json::value& json) override; + + std::string m_version; + Options m_options; + std::unordered_map m_emulators_info; + }; +} diff --git a/src/MeoAssistance/Identify.cpp b/src/MeoAssistance/Identify.cpp deleted file mode 100644 index 1596803cf3..0000000000 --- a/src/MeoAssistance/Identify.cpp +++ /dev/null @@ -1,349 +0,0 @@ -#include "Identify.h" - -#include -#include -#include - -#include -#include - -#include - -namespace penguin { -#include -} - -#include "Logger.hpp" -#include "AsstAux.h" -#include "Configer.h" - -using namespace asst; -using namespace cv; - -bool Identify::add_image(const std::string& name, const std::string& path) -{ - Mat image = imread(path); - if (image.empty()) { - return false; - } - m_mat_map.emplace(name, image); - return true; -} - -Mat Identify::image_2_hist(const cv::Mat& src) -{ - //Mat src_hsv; - //cvtColor(src, src_hsv, COLOR_BGR2HSV); - - int histSize[] = { 50, 60 }; - float h_ranges[] = { 0, 180 }; - float s_ranges[] = { 0, 256 }; - const float* ranges[] = { h_ranges, s_ranges }; - int channels[] = { 0, 1 }; - - MatND src_hist; - - calcHist(&src, 1, channels, Mat(), src_hist, 2, histSize, ranges); - normalize(src_hist, src_hist, 0, 1, NORM_MINMAX); - - return src_hist; -} - -double Identify::image_hist_comp(const cv::Mat& src, const cv::MatND& hist) -{ - // keep the interface return value unchanged - return 1 - compareHist(image_2_hist(src), hist, CV_COMP_BHATTACHARYYA); -} - -std::vector