diff --git a/resource/roguelike_shopping.json b/resource/roguelike_shopping.json
new file mode 100644
index 0000000000..24c83f75f4
--- /dev/null
+++ b/resource/roguelike_shopping.json
@@ -0,0 +1,413 @@
+[
+ {
+ "name": "金酒之杯"
+ },
+ {
+ "name": "骑士戒律·新编"
+ },
+ {
+ "name": "投币玩具"
+ },
+ {
+ "name": "锈蚀的铁锤"
+ },
+ {
+ "name": "幸运硬币"
+ },
+ {
+ "name": "“时光之末”"
+ },
+ {
+ "name": "人事部密信"
+ },
+ {
+ "name": "四叶草化石"
+ },
+ {
+ "name": "全局作战文件"
+ },
+ {
+ "name": "《旧高卢地名源流考》"
+ },
+ {
+ "name": "赤金的远征"
+ },
+ {
+ "name": "长生者之证"
+ },
+ {
+ "name": "假面舞会面具"
+ },
+ {
+ "name": "“逝者垂泪”"
+ },
+ {
+ "name": "血魔的寝床"
+ },
+ {
+ "name": "石像鬼塑像"
+ },
+ {
+ "name": "特殊抑制器"
+ },
+ {
+ "name": "《杜林地上环游记》"
+ },
+ {
+ "name": "维多利亚蛋糕"
+ },
+ {
+ "name": "试制巧克力"
+ },
+ {
+ "name": "乌萨斯列巴"
+ },
+ {
+ "name": "源石鸢尾花"
+ },
+ {
+ "name": "铁棍面包"
+ },
+ {
+ "name": "高卢小圆饼"
+ },
+ {
+ "name": "苦行者泡芙"
+ },
+ {
+ "name": "玻璃小鸟"
+ },
+ {
+ "name": "《第二经济改革法》"
+ },
+ {
+ "name": "高卢银行支票"
+ },
+ {
+ "name": "爱丽舍钱袋"
+ },
+ {
+ "name": "古高卢银币"
+ },
+ {
+ "name": "迷迭香之拥"
+ },
+ {
+ "name": "古怪的长笛"
+ },
+ {
+ "name": "镶金骨骰"
+ },
+ {
+ "name": "独奏八音盒"
+ },
+ {
+ "name": "银餐叉"
+ },
+ {
+ "name": "猩红剧团票根"
+ },
+ {
+ "name": "“黑夜呢喃”"
+ },
+ {
+ "name": "蓝色丝巾"
+ },
+ {
+ "name": "贵族刺剑"
+ },
+ {
+ "name": "赶车夫的长鞭"
+ },
+ {
+ "name": "空白名片"
+ },
+ {
+ "name": "红色蝴蝶结"
+ },
+ {
+ "name": "破损的面具"
+ },
+ {
+ "name": "黄铜指南针"
+ },
+ {
+ "name": "073号安全试剂"
+ },
+ {
+ "name": "昏暗的提灯"
+ },
+ {
+ "name": "绿叶菜罐头"
+ },
+ {
+ "name": "女皇之愿"
+ },
+ {
+ "name": "路线说明图"
+ },
+ {
+ "name": "莱塔尼亚荣誉勋章"
+ },
+ {
+ "name": "至宝指环"
+ },
+ {
+ "name": "维多利亚“废铁”勋章"
+ },
+ {
+ "name": "远程协议-遥击"
+ },
+ {
+ "name": "远程协议-克敌"
+ },
+ {
+ "name": "破坏协议-消除"
+ },
+ {
+ "name": "破坏协议-压制"
+ },
+ {
+ "name": "万能钥匙"
+ },
+ {
+ "name": "突击协议-利刃"
+ },
+ {
+ "name": "解约协议"
+ },
+ {
+ "name": "折戟-浴血",
+ "role": "WARRIOR"
+ },
+ {
+ "name": "古旧乐谱残章"
+ },
+ {
+ "name": "被撬开的道具箱"
+ },
+ {
+ "name": "“聚焦”"
+ },
+ {
+ "name": "Miss.Christine摸摸券"
+ },
+ {
+ "name": "尖笑鬼牌"
+ },
+ {
+ "name": "开裂的束缚带"
+ },
+ {
+ "name": "老蒲扇"
+ },
+ {
+ "name": "地区行动方案"
+ },
+ {
+ "name": "“噤声”"
+ },
+ {
+ "name": "折戟-典训",
+ "role": "WARRIOR"
+ },
+ {
+ "name": "“迷醉荷谟伊”"
+ },
+ {
+ "name": "残弩-典训",
+ "role": "SNIPER"
+ },
+ {
+ "name": "断杖-典训",
+ "role": "CASTER"
+ },
+ {
+ "name": "支柱-典训",
+ "role": "SUPPORT"
+ },
+ {
+ "name": "钝爪-典训",
+ "role": "PIONEER"
+ },
+ {
+ "name": "安洁莉娜的创想"
+ },
+ {
+ "name": "食腐者手杖"
+ },
+ {
+ "name": "“绽放”"
+ },
+ {
+ "name": "折戟-突破",
+ "role": "WARRIOR"
+ },
+ {
+ "name": "折戟-锋刃",
+ "role": "WARRIOR"
+ },
+ {
+ "name": "皇家利口酒"
+ },
+ {
+ "name": "“童趣玩偶”"
+ },
+ {
+ "name": "悬丝傀儡"
+ },
+ {
+ "name": "“当红歌手”"
+ },
+ {
+ "name": "残弩-神速",
+ "role": "SNIPER"
+ },
+ {
+ "name": "迷梦香精"
+ },
+ {
+ "name": "折戟-一夫当关",
+ "role": "WARRIOR"
+ },
+ {
+ "name": "残弩-战场依存",
+ "role": "SNIPER"
+ },
+ {
+ "name": "女妖之吻"
+ },
+ {
+ "name": "残弩-百步穿杨",
+ "role": "SNIPER"
+ },
+ {
+ "name": "折戟-破釜沉舟",
+ "role": "WARRIOR"
+ },
+ {
+ "name": "微缩舞台模型"
+ },
+ {
+ "name": "钝爪-爆发",
+ "role": "PIONEER"
+ },
+ {
+ "name": "钝爪-百战",
+ "role": "PIONEER"
+ },
+ {
+ "name": "钝爪-熟稔",
+ "role": "PIONEER"
+ },
+ {
+ "name": "断杖-咏唱",
+ "role": "CASTER"
+ },
+ {
+ "name": "断杖-织法者",
+ "role": "CASTER"
+ },
+ {
+ "name": "断杖-凝神",
+ "role": "CASTER"
+ },
+ {
+ "name": "医者-妙手",
+ "role": "MEDIC"
+ },
+ {
+ "name": "医者-理智固剂",
+ "role": "MEDIC"
+ },
+ {
+ "name": "皇族金胸针"
+ },
+ {
+ "name": "人偶之家"
+ },
+ {
+ "name": "显圣吊坠"
+ },
+ {
+ "name": "苍白花冠"
+ },
+ {
+ "name": "活玫瑰"
+ },
+ {
+ "name": "皇帝的收藏"
+ },
+ {
+ "name": "制式防暴用具"
+ },
+ {
+ "name": "锈蚀刀片"
+ },
+ {
+ "name": "急救药箱"
+ },
+ {
+ "name": "难闻的止血剂"
+ },
+ {
+ "name": "精神治疗录像带"
+ },
+ {
+ "name": "高闪相机"
+ },
+ {
+ "name": "凯旋号角"
+ },
+ {
+ "name": "舞者手链"
+ },
+ {
+ "name": "设计师量尺"
+ },
+ {
+ "name": "“法术杀手”"
+ },
+ {
+ "name": "演出用香水"
+ },
+ {
+ "name": "生命之水"
+ },
+ {
+ "name": "摩根队长佳酿"
+ },
+ {
+ "name": "荒地龙舌兰"
+ },
+ {
+ "name": "羽兽肝酱"
+ },
+ {
+ "name": "香草沙士汽水"
+ },
+ {
+ "name": "球球果汁"
+ },
+ {
+ "name": "作者的喉舌"
+ },
+ {
+ "name": "皇帝的恩宠"
+ },
+ {
+ "name": "残破合影"
+ },
+ {
+ "name": "奇渊面具"
+ },
+ {
+ "name": "Scout的狙击镜"
+ },
+ {
+ "name": "军团护心镜"
+ },
+ {
+ "name": "异铁小圆盾"
+ }
+]
diff --git a/resource/tasks.json b/resource/tasks.json
index 835a06c745..ddf4a200d2 100644
--- a/resource/tasks.json
+++ b/resource/tasks.json
@@ -6460,12 +6460,13 @@
},
"Roguelike1TraderRandomShopping": {
"action": "ClickSelf",
+ "templThreshold": 0.7,
"cache": false,
"roi": [
- 427,
- 374,
- 661,
- 196
+ 480,
+ 240,
+ 750,
+ 280
],
"next": [
"Roguelike1TraderRandomShoppingConfirm",
@@ -6476,6 +6477,56 @@
255
]
},
+ "Roguelike1TraderShopping": {
+ "template": "Roguelike1TraderRandomShopping.png",
+ "templThreshold": 0.7,
+ "roi": [
+ 480,
+ 240,
+ 750,
+ 280
+ ],
+ "maskRange": [
+ 1,
+ 255
+ ]
+ },
+ "Roguelike1TraderShoppingOcr": {
+ "algorithm": "OcrDetect",
+ "text": [],
+ "roi": [
+ -120,
+ -160,
+ 190,
+ 35
+ ],
+ "ocrReplace": [
+
+ ]
+ },
+ "Roguelike1TraderShoppingConfirm": {
+ "template": "Roguelike1TraderRandomShoppingConfirm.png",
+ "action": "ClickSelf",
+ "cache": false,
+ "roi": [
+ 872,
+ 425,
+ 221,
+ 136
+ ],
+ "rearDelay": 1000
+ },
+ "Roguelike1TraderShoppingCancel": {
+ "template": "Roguelike1StageTraderInvestCancel.png",
+ "action": "ClickSelf",
+ "cache": false,
+ "roi": [
+ 350,
+ 350,
+ 500,
+ 250
+ ]
+ },
"Roguelike1TraderRandomShoppingConfirm": {
"action": "ClickSelf",
"cache": false,
diff --git a/resource/template/Roguelike1TraderRandomShopping.png b/resource/template/Roguelike1TraderRandomShopping.png
index 3613b2772d..72c958e040 100644
Binary files a/resource/template/Roguelike1TraderRandomShopping.png and b/resource/template/Roguelike1TraderRandomShopping.png differ
diff --git a/src/MeoAssistant/MeoAssistant.vcxproj b/src/MeoAssistant/MeoAssistant.vcxproj
index 8c0bacdc36..96cd4a6f02 100644
--- a/src/MeoAssistant/MeoAssistant.vcxproj
+++ b/src/MeoAssistant/MeoAssistant.vcxproj
@@ -90,6 +90,8 @@
+
+
@@ -178,6 +180,8 @@
+
+
@@ -200,6 +204,7 @@
+
diff --git a/src/MeoAssistant/MeoAssistant.vcxproj.filters b/src/MeoAssistant/MeoAssistant.vcxproj.filters
index 57eb31f468..041d71d206 100644
--- a/src/MeoAssistant/MeoAssistant.vcxproj.filters
+++ b/src/MeoAssistant/MeoAssistant.vcxproj.filters
@@ -360,6 +360,12 @@
头文件\Resource
+
+ 头文件\Resource
+
+
+ 头文件\Task\Plugin\Roguelike
+
@@ -620,6 +626,12 @@
源文件\Resource
+
+ 源文件\Resource
+
+
+ 源文件\Task\Plugin\Roguelike
+
@@ -637,5 +649,8 @@
资源文件
+
+ 资源文件
+
\ No newline at end of file
diff --git a/src/MeoAssistant/Resource.cpp b/src/MeoAssistant/Resource.cpp
index 3e4b8ef5b2..6aa1246929 100644
--- a/src/MeoAssistant/Resource.cpp
+++ b/src/MeoAssistant/Resource.cpp
@@ -28,6 +28,7 @@ bool asst::Resource::load(const std::string& dir)
constexpr static auto StageDropsCfgFilename = "stages.json";
constexpr static auto StageDropsTempls = "template/items";
constexpr static auto BattleDataCfgFilename = "battle_data.json";
+ constexpr static auto RoguelikeShoppingCfgFilename = "roguelike_shopping.json";
bool overload = false;
@@ -168,6 +169,17 @@ bool asst::Resource::load(const std::string& dir)
overload = true;
}
+ // 加载肉鸽购物需要的数据
+ if (!m_roguelike_shopping_cfg_unique_ins.load(dir + RoguelikeShoppingCfgFilename)) {
+ if (!m_loaded) {
+ m_last_error = std::string(RoguelikeShoppingCfgFilename) + ": " + m_roguelike_shopping_cfg_unique_ins.get_last_error();
+ return false;
+ }
+ }
+ else {
+ overload = true;
+ }
+
/* 加载OCR库所需要的资源 */
//m_ocr_pack_unique_ins.set_param(opt.ocr_gpu_index, opt.ocr_thread_number);
if (!m_ocr_pack_unique_ins.load(dir + OcrResourceFilename)) {
diff --git a/src/MeoAssistant/Resource.h b/src/MeoAssistant/Resource.h
index be0e6e1ca2..15d3fd06b3 100644
--- a/src/MeoAssistant/Resource.h
+++ b/src/MeoAssistant/Resource.h
@@ -18,6 +18,7 @@
#include "RoguelikeCopilotConfiger.h"
#include "StageDropsConfiger.h"
#include "BattleDataConfiger.h"
+#include "RoguelikeShoppingConfiger.h"
namespace asst
{
@@ -82,6 +83,10 @@ namespace asst
{
return m_battle_data_cfg_unique_ins;
}
+ RoguelikeShoppingConfiger& roguelike_shopping() noexcept
+ {
+ return m_roguelike_shopping_cfg_unique_ins;
+ }
const TemplResource& templ() const noexcept
{
@@ -131,6 +136,10 @@ namespace asst
{
return m_battle_data_cfg_unique_ins;
}
+ const RoguelikeShoppingConfiger& roguelike_shopping() const noexcept
+ {
+ return m_roguelike_shopping_cfg_unique_ins;
+ }
Resource& operator=(const Resource&) = delete;
Resource& operator=(Resource&&) noexcept = delete;
@@ -150,6 +159,7 @@ namespace asst
TilePack m_tile_pack_unique_ins;
StageDropsConfiger m_stage_drops_cfg_unique_ins;
BattleDataConfiger m_battle_data_cfg_unique_ins;
+ RoguelikeShoppingConfiger m_roguelike_shopping_cfg_unique_ins;
bool m_loaded = false;
};
diff --git a/src/MeoAssistant/RoguelikeShoppingConfiger.cpp b/src/MeoAssistant/RoguelikeShoppingConfiger.cpp
new file mode 100644
index 0000000000..a4c59a8b2a
--- /dev/null
+++ b/src/MeoAssistant/RoguelikeShoppingConfiger.cpp
@@ -0,0 +1,27 @@
+#include "RoguelikeShoppingConfiger.h"
+
+#include
+
+bool asst::RoguelikeShoppingConfiger::parse(const json::value& json)
+{
+ for (const auto& commodity_json : json.as_array()) {
+ std::string name = commodity_json.at("name").as_string();
+ static const std::unordered_map RoleMap = {
+ { "CASTER", BattleRole::Caster },
+ { "MEDIC", BattleRole::Medic },
+ { "PIONEER", BattleRole::Pioneer },
+ { "SNIPER", BattleRole::Sniper},
+ { "SPECIAL", BattleRole::Special},
+ { "SUPPORT", BattleRole::Support},
+ { "TANK", BattleRole::Tank},
+ { "WARRIOR", BattleRole::Warrior},
+ };
+ BattleRole role = BattleRole::Unknown;
+ std::string str_role = commodity_json.get("role", "");
+ if (!str_role.empty()) {
+ role = RoleMap.at(str_role);
+ }
+ m_goods.emplace_back(RoguelikeGoods{ name, role });
+ }
+ return true;
+}
diff --git a/src/MeoAssistant/RoguelikeShoppingConfiger.h b/src/MeoAssistant/RoguelikeShoppingConfiger.h
new file mode 100644
index 0000000000..b743ea1164
--- /dev/null
+++ b/src/MeoAssistant/RoguelikeShoppingConfiger.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "AbstractConfiger.h"
+
+#include
+
+#include "AsstBattleDef.h"
+
+namespace asst
+{
+ struct RoguelikeGoods
+ {
+ std::string name;
+ BattleRole restriction = BattleRole::Unknown;
+ };
+
+ class RoguelikeShoppingConfiger : public AbstractConfiger
+ {
+ public:
+ virtual ~RoguelikeShoppingConfiger() = default;
+
+ const auto& get_goods() const noexcept
+ {
+ return m_goods;
+ }
+
+ private:
+ virtual bool parse(const json::value& json) override;
+
+ std::vector m_goods;
+ };
+}
diff --git a/src/MeoAssistant/RoguelikeShoppingTaskPlugin.cpp b/src/MeoAssistant/RoguelikeShoppingTaskPlugin.cpp
new file mode 100644
index 0000000000..752c797245
--- /dev/null
+++ b/src/MeoAssistant/RoguelikeShoppingTaskPlugin.cpp
@@ -0,0 +1,58 @@
+#include "RoguelikeShoppingTaskPlugin.h"
+
+#include "OcrWithFlagTemplImageAnalyzer.h"
+#include "Resource.h"
+#include "Controller.h"
+#include "ProcessTask.h"
+
+bool asst::RoguelikeShoppingTaskPlugin::verify(AsstMsg msg, const json::value& details) const
+{
+ if (msg != AsstMsg::SubTaskStart
+ || details.get("subtask", std::string()) != "ProcessTask") {
+ return false;
+ }
+
+ if (details.at("details").at("task").as_string() == "Roguelike1TraderRandomShopping") {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+bool asst::RoguelikeShoppingTaskPlugin::_run()
+{
+ OcrWithFlagTemplImageAnalyzer analyzer;
+ analyzer.set_task_info("Roguelike1TraderShopping", "Roguelike1TraderShoppingOcr");
+
+ analyzer.set_image(m_ctrler->get_image());
+ if (!analyzer.analyze()) {
+ return false;
+ }
+ const auto& result = analyzer.get_result();
+ const auto& order_goods_list = Resrc.roguelike_shopping().get_goods();
+
+ for (const auto& goods : order_goods_list) {
+ if (goods.restriction != BattleRole::Unknown) {
+ // TODO
+ continue;
+ }
+ auto find_it = std::find_if(result.cbegin(), result.cend(),
+ [&](const TextRect& tr) -> bool {
+ return tr.text == goods.name;
+ });
+ if (find_it == result.cend()) {
+ continue;
+ }
+ m_ctrler->click(find_it->rect);
+ bool confirmed = ProcessTask(*this, { "Roguelike1TraderShoppingConfirm" })
+ .set_retry_times(5)
+ .run();
+
+ if (!confirmed) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/MeoAssistant/RoguelikeShoppingTaskPlugin.h b/src/MeoAssistant/RoguelikeShoppingTaskPlugin.h
new file mode 100644
index 0000000000..0f5f9ec6a5
--- /dev/null
+++ b/src/MeoAssistant/RoguelikeShoppingTaskPlugin.h
@@ -0,0 +1,19 @@
+#pragma once
+#include "AbstractTaskPlugin.h"
+
+namespace asst
+{
+ class RoguelikeShoppingTaskPlugin : public AbstractTaskPlugin
+ {
+ public:
+ using AbstractTaskPlugin::AbstractTaskPlugin;
+ virtual ~RoguelikeShoppingTaskPlugin() = default;
+
+ virtual bool verify(AsstMsg msg, const json::value& details) const override;
+
+ protected:
+ virtual bool _run() override;
+
+ private:
+ };
+}
diff --git a/src/MeoAssistant/RoguelikeTask.cpp b/src/MeoAssistant/RoguelikeTask.cpp
index 5fe9cc0376..3236c2c709 100644
--- a/src/MeoAssistant/RoguelikeTask.cpp
+++ b/src/MeoAssistant/RoguelikeTask.cpp
@@ -8,6 +8,7 @@
#include "RoguelikeBattleTaskPlugin.h"
#include "RoguelikeControlTaskPlugin.h"
#include "RoguelikeCustomStartTaskPlugin.h"
+#include "RoguelikeShoppingTaskPlugin.h"
#include "Logger.hpp"
@@ -20,6 +21,7 @@ asst::RoguelikeTask::RoguelikeTask(const AsstCallback& callback, void* callback_
m_roguelike_task_ptr->regiseter_plugin();
m_roguelike_task_ptr->regiseter_plugin();
+ m_roguelike_task_ptr->regiseter_plugin();
m_custom_start_task_ptr = m_roguelike_task_ptr->regiseter_plugin();
m_battle_task_ptr = m_roguelike_task_ptr->regiseter_plugin();