Merge pull request #3680 from WLLEGit/ReclamationAlgorithm

refactor&feat: 重构生息演算,Core添加刷赤金参数
This commit is contained in:
LambdaLe
2023-02-13 22:45:10 +08:00
committed by GitHub
21 changed files with 821 additions and 145 deletions

View File

@@ -235,7 +235,10 @@ AsstTaskId ASSTAPI AsstAppendTask(AsstHandle handle, const char* type, const cha
```jsonc
{
"enable": bool
"enable": bool,
"mode": int, // 模式,可选项。默认 0
// 0 - 刷分与建造点,进入战斗直接退出
// 1 - 刷赤金,联络员买水后基地锻造
}
```

View File

@@ -223,7 +223,8 @@ For more details about auto-copilot JSON, please refer to [Copilot Schema](3.3-C
```jsonc
{
"enable": bool
"enable": bool,
"mode": int,
}
```

View File

@@ -220,7 +220,8 @@ TaskId ASSTAPI AsstAppendTask(AsstHandle handle, const char* type, const char* p
```jsonc
{
"enable": bool
"enable": bool,
"mode": int,
}
```

View File

@@ -221,7 +221,8 @@ TaskId ASSTAPI AsstAppendTask(AsstHandle handle, const char* type, const char* p
```jsonc
{
"enable": bool
"enable": bool,
"mode": int,
}
```

View File

@@ -9822,7 +9822,12 @@
"Reclamation@Begin"
]
},
"Reclamation": {
"Reclamation@BetweenDayFlag":{
"roi": [
508, 142, 267, 283
]
},
"Reclamation":{
"template": "Terminal.png",
"action": "ClickSelf",
"roi": [
@@ -9873,7 +9878,6 @@
"template": "Return.png",
"next": [
"Reclamation@AtHomePageFlag",
"Reclamation@ResetScope",
"Reclamation@LevelForceExitConfirm",
"Reclamation@LevelForceExit",
"Reclamation@LevelComplete",
@@ -10057,7 +10061,9 @@
],
"action": "ClickSelf",
"next": [
"Reclamation@ConfirmStart#next"
"Reclamation@SkipAnnounce",
"Reclamation@SkipDailyReport",
"Reclamation@AtMapFlag"
]
},
"Reclamation@SkipDailyReport": {
@@ -10081,9 +10087,24 @@
48
],
"next": [
"Reclamation@ConfirmStart#next"
"Reclamation@SkipAnnounce",
"Reclamation@SkipDailyReport",
"Reclamation@AtMapFlag"
]
},
"Reclamation@ClickBlackMarketCorner":{
"roi": [
318, 40, 115, 113
],
"action": "ClickSelf"
},
"Reclamation@ClickBlackMarket":{
"roi": [
302, 28, 146, 126
],
"cache": false,
"action": "ClickSelf"
},
"Reclamation@ClickCenterBase": {
"roi": [
557,
@@ -10199,10 +10220,6 @@
"Reclamation@BattleStart": {
"baseTask": "Reclamation@BattlePackIconTemplate",
"template": "Reclamation@ClickExitLevel.png",
"Doc": "注册插件ReclamationBattlePlugin",
"onErrorNext": [
"Reclamation@Begin"
],
"next": [
"Reclamation@LevelComplete"
],
@@ -10217,10 +10234,7 @@
"baseTask": "Reclamation@BattlePackIconTemplate",
"template": "Reclamation@ClickExitLevel.png",
"specificRect": [
46,
28,
59,
61
49, 30, 58, 54
]
},
"Reclamation@ExitLevelConfirm": {
@@ -10299,7 +10313,6 @@
177
],
"action": "ClickSelf",
"postDelay": 4000,
"next": [
"Reclamation@Emergency",
"Reclamation@NextDay",
@@ -10384,8 +10397,7 @@
"next": [
"Reclamation@GainReward",
"Reclamation@AtHomePageFlag",
"Reclamation@GiveupSkipConfirm",
"Reclamation@ClickChooseInitUnits"
"Reclamation@GiveupSkipConfirm"
]
},
"Reclamation@ConclusionReportBadgeIcon": {
@@ -10530,20 +10542,46 @@
],
"action": "ClickSelf",
"next": [
"Reclamation@EnterResearchLab",
"Reclamation@ClickCmdCenter",
"Reclamation@CmdCenterSwipeRight"
"Reclamation@AtCmdCenterFlag",
"Reclamation@ClickCmdCenter"
]
},
"Reclamation@CmdCenterSwipeRight": {
"baseTask": "SwipeToTheRight",
"postDelay": 1500,
"Reclamation@AtCmdCenterFlag":{
"algorithm": "OcrDetect",
"roi": [
68, 0, 326, 137
],
"cache": false,
"text": [
"COM",
"MAND",
"ENTER"
],
"next": [
"Reclamation@EnterResearchLab",
"Reclamation@CmdCenterSwipeRight"
"Stop"
]
},
"Reclamation@EnterResearchLab": {
"Reclamation@CmdCenterSwipeRight":{
"baseTask": "SwipeToTheRight",
"postDelay": 1000
},
"Reclamation@CmdCenterSwipeLeft":{
"baseTask": "SwipeToTheLeft",
"postDelay": 1000
},
"Reclamation@EnterSmeltGoldPage":{
"algorithm": "OcrDetect",
"cache": false,
"roi": [
0, 31, 1280, 175
],
"text": [
"粗制",
"赤金"
],
"action": "ClickSelf"
},
"Reclamation@EnterResearchLab":{
"algorithm": "OcrDetect",
"roi": [
0,
@@ -10557,11 +10595,7 @@
"所"
],
"action": "ClickSelf",
"postDelay": 500,
"next": [
"Reclamation@LabClickDetailView",
"Reclamation@EnterResearchLab"
]
"postDelay": 500
},
"Reclamation@LabClickDetailView": {
"algorithm": "OcrDetect",
@@ -10578,11 +10612,7 @@
"图"
],
"postDelay": 500,
"action": "ClickSelf",
"next": [
"Reclamation@LabClickAssembleable",
"Reclamation@LabReturnToMap"
]
"action": "ClickSelf"
},
"Reclamation@LabClickAssembleable": {
"algorithm": "OcrDetect",
@@ -10594,15 +10624,10 @@
],
"action": "ClickSelf",
"text": [
"可",
"组",
"装"
],
"next": [
"Reclamation@LabAssembleIncrease"
"可", "组", "装"
]
},
"Reclamation@LabAssembleStatus": {
"Reclamation@ManufactureStatus":{
"Doc": "base_task",
"algorithm": "OcrDetect",
"roi": [
@@ -10613,8 +10638,8 @@
],
"text": []
},
"Reclamation@LabAssembleIncrease": {
"baseTask": "Reclamation@LabAssembleStatus",
"Reclamation@ManufactureIncrease": {
"baseTask": "Reclamation@ManufactureStatus",
"action": "ClickRect",
"specificRect": [
1120,
@@ -10623,18 +10648,11 @@
26
],
"text": [
"开",
"始",
"组",
"装"
],
"next": [
"Reclamation@LabAssembleIncrease",
"Reclamation@LabAssembleDecrease"
"开", "始"
]
},
"Reclamation@LabAssembleDecrease": {
"baseTask": "Reclamation@LabAssembleStatus",
"Reclamation@ManufactureDecrease": {
"baseTask": "Reclamation@ManufactureStatus",
"action": "ClickRect",
"specificRect": [
1096,
@@ -10643,69 +10661,219 @@
28
],
"text": [
"材",
"料",
"不",
"足"
],
"next": [
"Reclamation@LabDoAssemble",
"Reclamation@LabAssembleDecrease"
"材", "料", "不", "足"
]
},
"Reclamation@LabDoAssemble": {
"baseTask": "Reclamation@LabAssembleStatus",
"Reclamation@DoManufacture":{
"baseTask": "Reclamation@ManufactureStatus",
"action": "ClickSelf",
"text": [
"开",
"始",
"组",
"装"
"开", "始"
],
"postDelay": 1000,
"next": [
"Reclamation@LabClickDetailView"
"postDelay": 1000
},
"Reclamation@ManufactureInsufficientMaterial": {
"baseTask": "Reclamation@ManufactureStatus",
"text": [
"材", "料", "不", "足"
]
},
"Reclamation@LabInsufficientMaterial": {
"Reclamation@ManufactureSufficientMaterial": {
"baseTask": "Reclamation@ManufactureStatus",
"text": [
"开", "始"
]
},
"Reclamation@Liaison":{
"algorithm": "OcrDetect",
"roi": [
1080,
320,
200,
139
0, 141, 179, 130
],
"action": "DoNothing",
"text": [
"材",
"料",
"不",
"足"
],
"next": [
"Reclamation@LabClickDetailView"
"联络员"
]
},
"Reclamation@LabReturnToMap": {
"Reclamation@BalckMarketDialogOption":{
"algorithm": "OcrDetect",
"roi": [
0,
0,
148,
148
721, 78, 543, 492
],
"templThreshold": 0.7,
"action": "ClickSelf",
"next": [
"Reclamation@LabArriveMap",
"Reclamation@LabReturnToMap",
"Reclamation@AtHomePageFlag"
"text": [],
"postDelay": 500
},
"Reclamation@BuyWaterProcedure":{
"algorithm": "OcrDetect",
"text": [
"Skip",
"建材",
"清水",
"Skip"
]
},
"Reclamation@LabArriveMap": {
"baseTask": "Reclamation@AtMapFlag",
"Reclamation@SkillReadyRoleOffset":{
"Doc": "技能准备完成距离npc中心的偏移",
"template": "empty.png",
"specialParams": [
100
]
},
"Reclamation@BattleSkillReadyOnClick": {
"baseTask": "BattleSkillReadyOnClick",
"template": "BattleSkillReadyOnClick.png",
"roi": [
439, 76, 841, 644
]
},
"Reclamation@DialogSkip":{
"baseTask": "Reclamation@SkipAnnounce",
"next": []
},
"NavigateHome@Reclamation@Begin":{
"algorithm": "JustReturn",
"next": [
"NavigateHome@Reclamation@AtHomePageFlag",
"NavigateHome@Reclamation@ReturnTo#next",
"NavigateHome@Reclamation@SkipAnnounce",
"NavigateHome@Reclamation@SkipDailyReport",
"NavigateHome@Reclamation@LevelComplete",
"NavigateHome@Reclamation@GiveupReportSkip",
"NavigateHome@Reclamation@GiveupSkipConfirm",
"NavigateHome@Reclamation@GainReward",
"NavigateHome@Reclamation@StartUpThemes#next",
"NavigateHome@Reclamation@TodoEnter"
]
},
"NavigateHome@Reclamation@AtHomePageFlag":{
"template": "Reclamation@AtHomePageFlag.png",
"next": [
"Stop"
]
},
"NavigateHome@Reclamation@ResetScope":{
"template": "Reclamation@ResetScope.png",
"next": [
"Reclamation@ExitAlgorithm"
"NavigateHome@Reclamation@Begin"
]
},
"NavigateHome@Reclamation@AtMapFlag":{
"template": "Reclamation@ResetScope.png",
"next": [
"NavigateHome@Reclamation@Begin"
]
},
"GiveupAlgorithm@Reclamation@Begin":{
"algorithm": "JustReturn",
"next": [
"GiveupAlgorithm@Reclamation@AtHomePageFlag",
"GiveupAlgorithm@Reclamation@GiveupAlgorithmConfirm",
"GiveupAlgorithm@Reclamation@GiveupAlgorithm"
]
},
"GiveupAlgorithm@Reclamation@AtHomePageFlag":{
"template": "Reclamation@AtHomePageFlag.png",
"next": [
"GiveupAlgorithm@Reclamation@GiveupAlgorithmConfirm",
"GiveupAlgorithm@Reclamation@GiveupAlgorithm",
"Stop"
]
},
"StartWithDefaultFormation@Reclamation@StartAlgorithm":{
"template": "Reclamation@StartAlgorithm.png",
"next": [
"StartWithDefaultFormation@Reclamation@ClickChooseInitUnits",
"StartWithDefaultFormation@Reclamation@StartAlgorithm"
]
},
"StartWithDefaultFormation@Reclamation@ConfirmStart":{
"next": [
"Stop"
]
},
"SkipAnnounceReport@Reclamation@Begin":{
"algorithm": "JustReturn",
"next": [
"SkipAnnounceReport@Reclamation@SkipAnnounce",
"SkipAnnounceReport@Reclamation@SkipDailyReport",
"SkipAnnounceReport@Reclamation@AtMapFlag"
]
},
"SkipAnnounceReport@Reclamation@AtMapFlag":{
"template": "Reclamation@ResetScope.png",
"next": [
"Stop"
]
},
"StartActionEnter@Reclamation@StartActionEnter":{
"next": [
"Stop"
]
},
"BattleDefaultFormationStart@Reclamation@Begin":{
"algorithm": "JustReturn",
"next": [
"BattleDefaultFormationStart@Reclamation@PrepareAction"
]
},
"BattleDefaultFormationStart@Reclamation@BattleStart":{
"template": "Reclamation@ClickExitLevel.png",
"next": [
"Stop"
]
},
"LevelCompleteConfirm@Reclamation@LevelComplete":{
"next": [
"LevelCompleteConfirm@Reclamation@AtMapFlag",
"LevelCompleteConfirm@Reclamation@LevelComplete"
]
},
"LevelCompleteConfirm@Reclamation@AtMapFlag":{
"template": "Reclamation@ResetScope.png",
"next": [
"Stop"
]
},
"CheckNextDay@Reclamation@NextDay":{
"template": "Reclamation@NextDay.png",
"postDelay": 0,
"next": ["Stop"]
},
"EnterNextDay@Reclamation@Begin":{
"baseTask": "Reclamation@NextDay",
"template": "Reclamation@NextDay.png",
"next": ["Stop"]
},
"WaitNextDayFlag@Reclamation@BetweenDayFlag":{
"template": "Reclamation@BetweenDayFlag.png",
"postDelay": 500,
"next": [
"Stop"
]
},
"CheckEmergency@Reclamation@Emergency":{
"template": "Reclamation@Emergency.png",
"next": [
"Stop"
]
},
"ClickCenterBase@Reclamation@Begin":{
"baseTask": "Reclamation@ClickCenterBase",
"template": "Reclamation@ClickCenterBase.png",
"next": [
"Stop"
]
},
"ClickAnyZone@Reclamation@Begin":{
"baseTask": "Reclamation@ClickAnyZoneOcr",
"next": [
"Stop"
]
},
"ResetScope@Reclamation@Begin":{
"baseTask": "Reclamation@ResetScope",
"template": "Reclamation@ResetScope.png",
"next": [
"Stop"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -32,6 +32,7 @@
<ClInclude Include="Config\Miscellaneous\SSSCopilotConfig.h" />
<ClInclude Include="Controller.h" />
<ClInclude Include="InstHelper.h" />
<ClInclude Include="Task\Reclamation\ReclamationControlTask.h" />
<ClInclude Include="Task\Reclamation\ReclamationConclusionReportPlugin.h" />
<ClInclude Include="Task\Reclamation\ReclamationBattlePlugin.h" />
<ClInclude Include="Task\Interface\ReclamationTask.h" />
@@ -161,6 +162,7 @@
<ClCompile Include="Config\Miscellaneous\SSSCopilotConfig.cpp" />
<ClCompile Include="Controller.cpp" />
<ClCompile Include="InstHelper.cpp" />
<ClCompile Include="Task\Reclamation\ReclamationControlTask.cpp" />
<ClCompile Include="Task\Reclamation\ReclamationConclusionReportPlugin.cpp" />
<ClCompile Include="Task\Reclamation\ReclamationBattlePlugin.cpp" />
<ClCompile Include="Task\Interface\ReclamationTask.cpp" />

View File

@@ -504,6 +504,9 @@
<ClInclude Include="Task\Reclamation\ReclamationConclusionReportPlugin.h">
<Filter>源文件\Task\Reclamation</Filter>
</ClInclude>
<ClInclude Include="Task\Reclamation\ReclamationControlTask.h">
<Filter>源文件\Task\Reclamation</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Vision\AbstractImageAnalyzer.cpp">
@@ -830,5 +833,8 @@
<ClCompile Include="Task\Reclamation\ReclamationConclusionReportPlugin.cpp">
<Filter>源文件\Task\Reclamation</Filter>
</ClCompile>
<ClCompile Include="Task\Reclamation\ReclamationControlTask.cpp">
<Filter>源文件\Task\Reclamation</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -7,17 +7,33 @@
#include "Task/Reclamation/ReclamationBattlePlugin.h"
#include "Task/Reclamation/ReclamationConclusionReportPlugin.h"
#include "Task/Reclamation/ReclamationControlTask.h"
asst::ReclamationTask::ReclamationTask(const AsstCallback& callback, Assistant* inst)
: InterfaceTask(callback, inst, TaskType),
m_reclamation_task_ptr(std::make_shared<ProcessTask>(callback, inst, TaskType))
m_reclamation_task_ptr(std::make_shared<ReclamationControlTask>(callback, inst, TaskType))
{
m_reclamation_task_ptr->set_tasks({ "Reclamation@Begin" });
m_reclamation_task_ptr->set_ignore_error(true);
m_reclamation_task_ptr->register_plugin<ReclamationBattlePlugin>();
m_reclamation_task_ptr->register_plugin<ReclamationConclusionReportPlugin>();
for (int i = 0; i != 100; ++i) {
m_subtasks.emplace_back(m_reclamation_task_ptr);
}
m_subtasks.emplace_back(m_reclamation_task_ptr);
}
bool asst::ReclamationTask::set_params(const json::value& params)
{
// 0 - 刷分与建造点,进入战斗直接退出
// 1 - 刷赤金,联络员买水后基地锻造
int mode = params.get("mode", 0);
switch (mode) {
case 0:
m_reclamation_task_ptr->set_task_mode(ReclamationTaskMode::GiveupUponFight);
break;
case 1:
m_reclamation_task_ptr->set_task_mode(ReclamationTaskMode::SmeltGold);
break;
default:
Log.error(__FUNCTION__, "| Unknown mode", mode);
return false;
}
return true;
}

View File

@@ -4,6 +4,7 @@
namespace asst
{
class ProcessTask;
class ReclamationControlTask;
class ReclamationTask : public InterfaceTask
{
@@ -13,9 +14,9 @@ namespace asst
ReclamationTask(const AsstCallback& callback, Assistant* inst);
virtual ~ReclamationTask() override = default;
//virtual bool set_params(const json::value& params) override;
virtual bool set_params(const json::value& params) override;
private:
std::shared_ptr<ProcessTask> m_reclamation_task_ptr = nullptr;
std::shared_ptr<ReclamationControlTask> m_reclamation_task_ptr = nullptr;
};
}

View File

@@ -8,27 +8,28 @@
#include "Utils/Logger.hpp"
#include "Vision/OcrImageAnalyzer.h"
#include "Vision/MatchImageAnalyzer.h"
#include "Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.h"
#include "Config/TaskData.h"
#include "ReclamationControlTask.h"
using namespace asst;
asst::ReclamationBattlePlugin::ReclamationBattlePlugin(const AsstCallback& callback, Assistant* inst,
std::string_view task_chain)
: AbstractTaskPlugin(callback, inst, task_chain), BattleHelper(inst)
{}
bool asst::ReclamationBattlePlugin::verify(AsstMsg msg, const json::value& details) const
bool asst::ReclamationBattlePlugin::verify(AsstMsg, const json::value&) const
{
if (msg != AsstMsg::SubTaskCompleted || details.get("subtask", std::string()) != "ProcessTask") {
return false;
}
// 直接调用run()
return false;
}
const std::string& task = details.get("details", "task", "");
std::string_view task_view = task;
if (task_view == "Reclamation@BattleStart") {
return true;
}
else {
return false;
}
ReclamationBattlePlugin& asst::ReclamationBattlePlugin::set_battle_mode(const ReclamationBattleMode& mode)
{
m_battle_mode = mode;
return *this;
}
bool asst::ReclamationBattlePlugin::_run()
@@ -37,6 +38,26 @@ bool asst::ReclamationBattlePlugin::_run()
wait_until_start(false);
if (m_battle_mode == ReclamationBattleMode::Giveup) {
return quit_action();
}
else if (m_battle_mode == ReclamationBattleMode::BuyWater) {
sleep(1500); // 等待技能图标
bool result = buy_water();
quit_action();
return result;
}
return false;
}
bool asst::ReclamationBattlePlugin::do_strategic_action(const cv::Mat&)
{
return true;
}
bool asst::ReclamationBattlePlugin::quit_action()
{
while (!need_exit()) {
int retry = 0;
while (!need_exit()) {
@@ -44,18 +65,18 @@ bool asst::ReclamationBattlePlugin::_run()
bool stage2 = ProcessTask(*this, { "Reclamation@ExitLevelConfirm" }).set_retry_times(3).run();
Log.info(__FUNCTION__, "| click exit level perform ", stage1, stage2);
if (stage2) break;
retry++; // stage1==true&&stage2==false应该是手动按了确定或是没按到退出
retry++; // stage1==true&&stage2==false应该是手动按了确定或是没按到退出
if ((!stage1 && !stage2) || retry > 5) {
// 已经到了结算界面,或是用户手动操作了
Log.error(__FUNCTION__, "| fail to operate");
return ProcessTask(*this, { "Reclamation@Begin" }).run();
return false;
}
}
sleep(Task.get("Reclamation@BattleStart")->special_params.front());
const auto img = ctrler()->get_image();
bool check1 = check_in_battle(img, false);
bool check1 = check_in_battle(img, false);
OcrImageAnalyzer confirmAnalyzer(img);
confirmAnalyzer.set_task_info("Reclamation@ExitLevelConfirm");
@@ -74,11 +95,121 @@ bool asst::ReclamationBattlePlugin::_run()
break;
}
}
return true;
}
bool asst::ReclamationBattlePlugin::do_strategic_action(const cv::Mat&)
bool asst::ReclamationBattlePlugin::buy_water()
{
if (!communicate_with(Task.get<OcrTaskInfo>("Reclamation@Liaison")->text.front())) return false;
if (!do_dialog_procedure(Task.get<OcrTaskInfo>("Reclamation@BuyWaterProcedure")->text)) return false;
return true;
}
bool asst::ReclamationBattlePlugin::communicate_with(const std::string& npcName)
{
// 在存在两个及以上npc时地图会移动从上到下、从下到上各试一次还不行算了
if (communicate_with_aux(npcName, [](const MatchRect& l, const MatchRect& r) {
return l.rect.y < r.rect.y || (l.rect.y == r.rect.y && l.rect.x < r.rect.x);
}))
return true;
if (communicate_with_aux(npcName, [](const MatchRect& l, const MatchRect& r) {
return l.rect.y > r.rect.y || (l.rect.y == r.rect.y && l.rect.x > r.rect.x);
}))
return true;
Log.info(__FUNCTION__, " | ", "fail to communicate with npc");
return false;
}
bool asst::ReclamationBattlePlugin::communicate_with_aux(const std::string& npcName, std::function<bool(const MatchRect&, const MatchRect&)> orderComp)
{
auto image = ctrler()->get_image();
BattleSkillReadyImageAnalyzer skillReadyAnalyzer(image);
if (!skillReadyAnalyzer.analyze()) {
Log.info(__FUNCTION__, " | ", "no ready skills");
return false;
}
std::vector<MatchRect> skill_results = skillReadyAnalyzer.get_result();
std::sort(skill_results.begin(), skill_results.end(), orderComp);
for (const auto& [score, rect] : skill_results) {
Rect center(rect.x + rect.width / 2,
rect.y + rect.height / 2 + Task.get("Reclamation@SkillReadyRoleOffset")->special_params.front(), 5,
5);
const auto use_oper_task_ptr = Task.get("BattleUseOper");
ctrler()->click(center);
sleep(use_oper_task_ptr->pre_delay);
image = ctrler()->get_image();
OcrImageAnalyzer npcNameAnalyzer(image);
npcNameAnalyzer.set_task_info("Reclamation@Liaison");
npcNameAnalyzer.set_required({});
if (!npcNameAnalyzer.analyze()) {
// 地图发生了移动
Log.info(__FUNCTION__, " | ", "map moved ");
break;
}
if (npcNameAnalyzer.get_result().front() != npcName) {
// npc名称不正确
Log.info(__FUNCTION__, " | ", "npc name not match ", npcNameAnalyzer.get_result().front());
cancel_oper_selection();
continue;
}
ProcessTask skill_task(this_task(), { "Reclamation@BattleSkillReadyOnClick" });
skill_task.set_task_delay(0);
bool ret = skill_task.set_retry_times(5).run();
if (!ret) {
cancel_oper_selection();
Log.info(__FUNCTION__, " | ", "fail to click skill of npc");
return false;
}
return true;
}
return false;
}
bool asst::ReclamationBattlePlugin::do_dialog_procedure(const std::vector<std::string>& procedure)
{
for (auto& step : procedure) {
if (step == "Skip") {
ProcessTask(this_task(), { "Reclamation@DialogSkip" }).set_task_delay(0).set_retry_times(5).run();
}
else {
const int max_retry = 5;
int retry = 0;
bool succeed = false;
while (!need_exit()){
if (retry == max_retry) break;
const auto& image = ctrler()->get_image();
OcrImageAnalyzer dialogAnalyzer(image);
dialogAnalyzer.set_task_info("Reclamation@BalckMarketDialogOption");
dialogAnalyzer.set_required({ step });
if (!dialogAnalyzer.analyze()) {
if (succeed) {
break;
}
else {
retry++;
continue;
}
}
const auto& rect = dialogAnalyzer.get_result().front().rect;
ctrler()->click(rect);
Log.info(__FUNCTION__, " | ", "perform dialog option ", step);
sleep(Task.get("Reclamation@BalckMarketDialogOption")->post_delay);
succeed = true;
}
if (retry == max_retry) {
Log.info(__FUNCTION__, " | ", "fail to perform dialog option ", step);
return false;
}
}
}
return true;
}

View File

@@ -5,6 +5,14 @@
namespace asst
{
enum class ReclamationBattleMode
{
Giveup,
BuyWater
};
enum class ReclamationTaskMode;
class ReclamationBattlePlugin : public AbstractTaskPlugin, private BattleHelper
{
public:
@@ -13,9 +21,21 @@ namespace asst
virtual bool verify(AsstMsg msg, const json::value& details) const override;
ReclamationBattlePlugin& set_battle_mode(const ReclamationBattleMode& mode);
protected:
virtual bool _run() override;
virtual bool do_strategic_action(const cv::Mat& reusable = cv::Mat()) override;
virtual AbstractTask& this_task() override { return *this; }
bool quit_action();
bool buy_water();
bool communicate_with(const std::string& npcName);
bool communicate_with_aux(const std::string& npcName,
std::function<bool(const MatchRect&, const MatchRect&)> orderComp);
bool do_dialog_procedure(const std::vector<std::string>& procedure);
ReclamationBattleMode m_battle_mode = ReclamationBattleMode::Giveup;
};
}

View File

@@ -18,7 +18,7 @@ bool asst::ReclamationConclusionReportPlugin::verify(AsstMsg msg, const json::va
return false;
}
const std::string task = details.at("details").at("task").as_string();
if (task == "Reclamation@GiveupSkipConfirm") {
if (task.ends_with("Reclamation@GiveupSkipConfirm")) {
return true;
}
return false;

View File

@@ -0,0 +1,244 @@
#include "ReclamationControlTask.h"
#include "Config/Miscellaneous/BattleDataConfig.h"
#include "Controller.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
#include "Vision/OcrImageAnalyzer.h"
#include "Vision/OcrWithPreprocessImageAnalyzer.h"
#include "ReclamationBattlePlugin.h"
#define RunCheckSuccess(func, ...) \
do { \
if (!func(__VA_ARGS__)) return false; \
} while (false);
bool asst::ReclamationControlTask::_run()
{
LogTraceFunction;
if (m_task_mode == ReclamationTaskMode::GiveupUponFight) {
int times = 0;
while (!need_exit()) {
procedure_start_callback(++times);
run_giveup_upon_fight_procedure();
}
}
else if (m_task_mode == ReclamationTaskMode::SmeltGold) {
m_total_gold = 0;
int times = 0;
while (!need_exit()) {
procedure_start_callback(++times);
run_smelt_gold_procedure();
}
}
return false;
}
bool asst::ReclamationControlTask::run_giveup_upon_fight_procedure()
{
LogTraceFunction;
RunCheckSuccess(navigate_to_reclamation_home);
give_up_last_algorithm_if();
RunCheckSuccess(start_with_default_formation);
RunCheckSuccess(wait_between_day);
skip_announce_report();
while (!need_exit() && !check_emergency()) {
RunCheckSuccess(reset_scope);
RunCheckSuccess(click_center_base);
RunCheckSuccess(click_any_zone);
RunCheckSuccess(start_action_enter);
RunCheckSuccess(battle_default_formation_start);
RunCheckSuccess(ReclamationBattlePlugin(m_callback, m_inst, m_task_chain)
.set_battle_mode(ReclamationBattleMode::Giveup)
.run);
RunCheckSuccess(level_complete_comfirm);
if (enter_next_day_if_useup()) {
RunCheckSuccess(wait_between_day);
skip_announce_report();
}
}
return navigate_to_reclamation_home();
}
bool asst::ReclamationControlTask::run_smelt_gold_procedure()
{
LogTraceFunction;
RunCheckSuccess(navigate_to_reclamation_home);
give_up_last_algorithm_if();
RunCheckSuccess(start_with_default_formation);
RunCheckSuccess(wait_between_day);
skip_announce_report();
bool buy_result = false;
while (!need_exit() && !buy_result) {
RunCheckSuccess(reset_scope);
RunCheckSuccess(click_corner_black_market);
RunCheckSuccess(click_black_market);
RunCheckSuccess(start_action_enter);
buy_result = ReclamationBattlePlugin(m_callback, m_inst, m_task_chain)
.set_battle_mode(ReclamationBattleMode::BuyWater)
.set_retry_times(0)
.run();
RunCheckSuccess(level_complete_comfirm);
}
RunCheckSuccess(enter_command_center);
for (int i = 0; i < 2; ++i)
swipe_right();
RunCheckSuccess(ProcessTask(*this, { "Reclamation@EnterSmeltGoldPage" }).run);
if (check_manufacture_status() != 1) return false;
while (!need_exit() && check_manufacture_status() == 1) {
smelt_gold_callback(++m_total_gold);
do_manufacture();
}
return navigate_to_reclamation_home();
}
bool asst::ReclamationControlTask::navigate_to_reclamation_home()
{
return ProcessTask(*this, { "NavigateHome@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::give_up_last_algorithm_if()
{
return ProcessTask(*this, { "GiveupAlgorithm@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::start_with_default_formation()
{
return ProcessTask(*this, { "StartWithDefaultFormation@Reclamation@StartAlgorithm" }).run();
}
bool asst::ReclamationControlTask::skip_announce_report()
{
return ProcessTask(*this, { "SkipAnnounceReport@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::start_action_enter()
{
return ProcessTask(*this, { "StartActionEnter@Reclamation@StartActionEnter" }).run();
}
bool asst::ReclamationControlTask::battle_default_formation_start()
{
return ProcessTask(*this, { "BattleDefaultFormationStart@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::level_complete_comfirm()
{
return ProcessTask(*this, { "LevelCompleteConfirm@Reclamation@LevelComplete" }).run();
}
bool asst::ReclamationControlTask::enter_next_day_if_useup()
{
return ProcessTask(*this, { "EnterNextDay@Reclamation@Begin" }).set_retry_times(0).run();
}
bool asst::ReclamationControlTask::wait_between_day()
{
bool flag = false;
while (!need_exit()) {
if (ProcessTask(*this, { "WaitNextDayFlag@Reclamation@BetweenDayFlag" }).set_retry_times(0).run()) {
flag = true;
}
else if (ProcessTask(*this, { "SkipAnnounceReport@Reclamation@Begin" }).set_retry_times(0).run()) {
Log.info(__FUNCTION__, " | ", "arrive next day without detect BetweenDayFlag");
return true;
}
else {
if (flag) return true;
}
}
return false;
}
bool asst::ReclamationControlTask::reset_scope()
{
return ProcessTask(*this, { "ResetScope@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::enter_command_center()
{
return ProcessTask(*this, { "Reclamation@ClickCmdCenter" }).run();
}
bool asst::ReclamationControlTask::do_manufacture()
{
return ProcessTask(*this, { "Reclamation@DoManufacture" }).run();
}
bool asst::ReclamationControlTask::check_next_day()
{
return ProcessTask(*this, { "CheckNextDay@Reclamation@NextDay" }).set_retry_times(0).run();
}
bool asst::ReclamationControlTask::check_emergency()
{
return ProcessTask(*this, { "CheckEmergency@Reclamation@Emergency" }).set_retry_times(0).run();
}
int asst::ReclamationControlTask::check_manufacture_status()
{
if (ProcessTask(*this, { "Reclamation@ManufactureInsufficientMaterial" }).set_retry_times(0).run())
return 0;
else if (ProcessTask(*this, { "Reclamation@ManufactureSufficientMaterial" }).set_retry_times(0).run())
return 1;
else
return -1;
}
bool asst::ReclamationControlTask::click_center_base()
{
return ProcessTask(*this, { "ClickCenterBase@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::click_corner_black_market()
{
return ProcessTask(*this, { "Reclamation@ClickBlackMarketCorner" }).run();
}
bool asst::ReclamationControlTask::click_any_zone()
{
return ProcessTask(*this, { "ClickAnyZone@Reclamation@Begin" }).run();
}
bool asst::ReclamationControlTask::click_black_market()
{
return ProcessTask(*this, { "Reclamation@ClickBlackMarket" }).run();
}
bool asst::ReclamationControlTask::swipe_right()
{
return ProcessTask(*this, { "Reclamation@CmdCenterSwipeRight" }).run();
}
bool asst::ReclamationControlTask::swipe_left()
{
return ProcessTask(*this, { "Reclamation@CmdCenterSwipeLeft" }).run();
}
void asst::ReclamationControlTask::procedure_start_callback(int times) {
json::value info = basic_info_with_what("ReclamationProcedureStart");
json::value& details = info["details"];
details["times"] = times;
callback(AsstMsg::SubTaskExtraInfo, info);
}
void asst::ReclamationControlTask::smelt_gold_callback(int times) {
json::value info = basic_info_with_what("ReclamationSmeltGold");
json::value& details = info["details"];
details["times"] = times;
callback(AsstMsg::SubTaskExtraInfo, info);
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include "Task/AbstractTask.h"
namespace asst
{
enum class ReclamationTaskMode
{
GiveupUponFight,
SmeltGold
};
class ReclamationControlTask : public AbstractTask
{
public:
using AbstractTask::AbstractTask;
virtual ~ReclamationControlTask() override = default;
void set_task_mode(const ReclamationTaskMode& mode) { m_task_mode = mode; }
private:
virtual bool _run() override;
bool run_giveup_upon_fight_procedure();
bool run_smelt_gold_procedure();
bool navigate_to_reclamation_home(); // 导航至生息演算主界面
bool give_up_last_algorithm_if(); // 如果有,放弃上次演算
bool start_with_default_formation(); // 开始演算,至第一天
bool skip_announce_report(); // 跳过每日公告
bool start_action_enter(); // 区域交互确认
bool battle_default_formation_start(); // 默认战斗编队并开始战斗
bool level_complete_comfirm(); // 场景退出结算,至地图界面
bool enter_next_day_if_useup(); // 如果决断点耗尽,进入下一天
bool wait_between_day(); // 等待日期转换动画结束
bool reset_scope(); // 重置地图大小
bool enter_command_center(); // 进入侧边栏的命令中心
bool do_manufacture(); // 点击制作按钮
bool check_next_day(); // 决断点消耗完进入下一天的标志
bool check_emergency(); // 敌袭到达
int check_manufacture_status(); // 制造界面是否材料充足0不足1充足-1非建造页面
bool click_center_base(); // 点击中心基地图标
bool click_corner_black_market(); // 点击左上角黑市图标
bool click_any_zone(); // 点击奇遇外任意区
bool click_black_market(); // 点击黑市
bool swipe_right();
bool swipe_left();
void procedure_start_callback(int times);
void smelt_gold_callback(int times);
ReclamationTaskMode m_task_mode = ReclamationTaskMode::GiveupUponFight;
int m_total_gold = 0; // 制作赤金的次数
};
}

View File

@@ -45,10 +45,34 @@ bool asst::BattleSkillReadyImageAnalyzer::analyze()
match /= template_mask.cols * template_mask.rows;
cv::sqrt(match, match);
// you may use something similar to MultiMatchImageAnalyzer instead
double min_val = 255.;
cv::minMaxLoc(match, &min_val);
return min_val < 130.;
// 修改自MultiMatchImageAnalyzer
const double templ_thres = 130.;
int mini_distance = (std::min)(templ.cols, templ.rows) / 2;
for (int i = 0; i != match.rows; ++i) {
for (int j = 0; j != match.cols; ++j) {
auto value = match.at<float>(i, j);
if (value < templ_thres) {
Rect rect(j + m_roi.x, i + m_roi.y, templ.cols, templ.rows);
bool need_push = true;
for (auto& iter : ranges::reverse_view(m_result)) {
if (std::abs(j + m_roi.x - iter.rect.x) < mini_distance &&
std::abs(i + m_roi.y - iter.rect.y) < mini_distance) {
if (iter.score > value) {
iter.rect = rect;
iter.score = value;
}
need_push = false;
break;
}
}
if (need_push) {
m_result.emplace_back(value, rect);
}
}
}
}
return !m_result.empty();
}
void asst::BattleSkillReadyImageAnalyzer::set_base_point(const Point& pt)

View File

@@ -11,10 +11,11 @@ namespace asst
virtual ~BattleSkillReadyImageAnalyzer() override = default;
virtual bool analyze() override;
const std::vector<MatchRect>& get_result() const noexcept { return m_result; }
void set_base_point(const Point& pt);
private:
MatchRect m_result;
std::vector<MatchRect> m_result;
};
}

View File

@@ -741,11 +741,6 @@ namespace MaaWpfGui
case "BattleStartAll":
_copilotViewModel.AddLog(Localization.GetString("MissionStart"), UILogColor.Info);
break;
/* 生息演算 */
case "StartAlgorithm":
_taskQueueViewModel.AddLog(Localization.GetString("MissionStart") + $" {execTimes} " + Localization.GetString("UnitTime"), UILogColor.Info);
break;
}
}
}
@@ -1002,12 +997,15 @@ namespace MaaWpfGui
/* 生息演算 */
case "ReclamationReport":
{
_taskQueueViewModel.AddLog(Localization.GetString("AlgorithmFinish") + "\n" +
Localization.GetString("AlgorithmBadge") + ": " + $"{(int)subTaskDetails["total_badges"]}(+{(int)subTaskDetails["badges"]})" + "\n" +
Localization.GetString("AlgorithmConstructionPoint") + ": " + $"{(int)subTaskDetails["total_construction_points"]}(+{(int)subTaskDetails["construction_points"]})");
}
_taskQueueViewModel.AddLog(Localization.GetString("AlgorithmFinish") + "\n" +
Localization.GetString("AlgorithmBadge") + ": " + $"{(int)subTaskDetails["total_badges"]}(+{(int)subTaskDetails["badges"]})" + "\n" +
Localization.GetString("AlgorithmConstructionPoint") + ": " + $"{(int)subTaskDetails["total_construction_points"]}(+{(int)subTaskDetails["construction_points"]})");
break;
case "ReclamationProcedureStart":
_taskQueueViewModel.AddLog(Localization.GetString("MissionStart") + $" {(int)subTaskDetails["times"]} " + Localization.GetString("UnitTime"), UILogColor.Info);
break;
case "ReclamationSmeltGold":
_taskQueueViewModel.AddLog(Localization.GetString("AlgorithmDoneSmeltGold") + $" {(int)subTaskDetails["times"]} " + Localization.GetString("UnitTime"));
break;
}
}

View File

@@ -103,6 +103,9 @@
<system:String x:Key="AlgorithmFinish">演算结束</system:String>
<system:String x:Key="AlgorithmBadge">繁荣证章</system:String>
<system:String x:Key="AlgorithmConstructionPoint">建造点数</system:String>
<system:String x:Key="AlgorithmDoneSmeltGold">锻造赤金</system:String>
<system:String x:Key="NotSelected">不选择</system:String>
<system:String x:Key="Official">官服</system:String>