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();