mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 17:57:01 +08:00
feat: 支持肉鸽事件choice_require解析 (#7904)
- fix #5442 - fix #7789 - fix #7800 - fix #7900 - fix #7938 - fix #7943
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -12664,6 +12664,29 @@
|
||||
"Roguelike@StageEncounterJudgeOption"
|
||||
]
|
||||
},
|
||||
"Roguelike@SpecialValRecognition": {
|
||||
"algorithm": "OcrDetect",
|
||||
"action": "DoNothing",
|
||||
"Doc": "特殊值识别,如抗干扰值,需修改对应主题的roi",
|
||||
"text": [],
|
||||
"roi": [
|
||||
615,
|
||||
0,
|
||||
80,
|
||||
45
|
||||
]
|
||||
},
|
||||
"Roguelike@HpRecognition": {
|
||||
"baseTask": "NumberOcrReplace",
|
||||
"Doc": "生命值识别,暂时是把整个框进去,估计会把后面的生命值也识别到,后面得改改",
|
||||
"text": [],
|
||||
"roi": [
|
||||
190,
|
||||
30,
|
||||
60,
|
||||
30
|
||||
]
|
||||
},
|
||||
"Roguelike@StageEncounterJudgeOption": {
|
||||
"algorithm": "JustReturn",
|
||||
"action": "DoNothing",
|
||||
|
||||
@@ -16,7 +16,33 @@ bool asst::RoguelikeStageEncounterConfig::parse(const json::value& json)
|
||||
event.name = event_json.at("name").as_string();
|
||||
event.option_num = event_json.get("option_num", 0);
|
||||
event.default_choose = event_json.get("choose", 0);
|
||||
auto choice_require_opt = event_json.find("choice_require");
|
||||
if (!choice_require_opt || !choice_require_opt->is_array()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& requirement_json : choice_require_opt->as_array()) {
|
||||
ChoiceRequire requirement;
|
||||
requirement.name = requirement_json.at("name").as_string();
|
||||
requirement.choose = requirement_json.get("choose", -1);
|
||||
if (auto vision_opt = requirement_json.find("Vision")) {
|
||||
requirement.vision.value = vision_opt->get("value", "");
|
||||
requirement.vision.type = parse_comparison_type(vision_opt->get("type", ""));
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
event.choice_require.emplace_back(std::move(requirement));
|
||||
}
|
||||
m_events[theme].emplace_back(std::move(event));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
asst::RoguelikeStageEncounterConfig::ComparisonType asst::RoguelikeStageEncounterConfig::parse_comparison_type(
|
||||
const std::string& type_str)
|
||||
{
|
||||
if (type_str == ">") return ComparisonType::GreaterThan;
|
||||
if (type_str == "<") return ComparisonType::LessThan;
|
||||
if (type_str == "=") return ComparisonType::Equal;
|
||||
return ComparisonType::Unsupported;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,6 @@
|
||||
|
||||
namespace asst
|
||||
{
|
||||
struct RoguelikeEvent
|
||||
{
|
||||
std::string name;
|
||||
int option_num = 0;
|
||||
int default_choose = 0;
|
||||
};
|
||||
|
||||
class RoguelikeStageEncounterConfig final : public SingletonHolder<RoguelikeStageEncounterConfig>,
|
||||
public AbstractConfig
|
||||
{
|
||||
@@ -23,9 +16,39 @@ namespace asst
|
||||
|
||||
const auto& get_events(const std::string& theme) const noexcept { return m_events.at(theme); }
|
||||
|
||||
enum class ComparisonType
|
||||
{
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
Equal,
|
||||
None, // 没有配置
|
||||
Unsupported, // 配置错误或其他
|
||||
};
|
||||
|
||||
struct Vision
|
||||
{
|
||||
std::string value;
|
||||
ComparisonType type = ComparisonType::None;
|
||||
};
|
||||
struct ChoiceRequire
|
||||
{
|
||||
std::string name;
|
||||
Vision vision;
|
||||
int choose = -1;
|
||||
};
|
||||
struct RoguelikeEvent
|
||||
{
|
||||
std::string name;
|
||||
int option_num = 0;
|
||||
int default_choose = 0;
|
||||
std::vector<ChoiceRequire> choice_require;
|
||||
};
|
||||
|
||||
private:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
static ComparisonType parse_comparison_type(const std::string& type_str);
|
||||
|
||||
std::unordered_map<std::string, std::vector<RoguelikeEvent>> m_events;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "RoguelikeStageEncounterTaskPlugin.h"
|
||||
|
||||
#include "Config/Roguelike/RoguelikeStageEncounterConfig.h"
|
||||
#include "Config/TaskData.h"
|
||||
#include "Controller/Controller.h"
|
||||
#include "Status.h"
|
||||
#include "Task/ProcessTask.h"
|
||||
#include "Utils/ImageIo.hpp"
|
||||
#include "Utils/Logger.hpp"
|
||||
#include "Vision/OCRer.h"
|
||||
|
||||
bool asst::RoguelikeStageEncounterTaskPlugin::verify(AsstMsg msg, const json::value& details) const
|
||||
{
|
||||
@@ -39,13 +37,13 @@ bool asst::RoguelikeStageEncounterTaskPlugin::_run()
|
||||
LogTraceFunction;
|
||||
|
||||
auto mode = m_config->get_mode();
|
||||
std::vector<RoguelikeEvent> events = RoguelikeStageEncounter.get_events(m_config->get_theme());
|
||||
std::vector events = RoguelikeStageEncounter.get_events(m_config->get_theme());
|
||||
// 刷源石锭模式和烧水模式
|
||||
if (mode == RoguelikeMode::Investment || mode == RoguelikeMode::Collectible) {
|
||||
events = RoguelikeStageEncounter.get_events(m_config->get_theme() + "_deposit");
|
||||
}
|
||||
std::vector<std::string> event_names;
|
||||
std::unordered_map<std::string, RoguelikeEvent> event_map;
|
||||
std::unordered_map<std::string, Config::RoguelikeEvent> event_map;
|
||||
for (const auto& event : events) {
|
||||
event_names.emplace_back(event.name);
|
||||
event_map.emplace(event.name, event);
|
||||
@@ -71,19 +69,123 @@ bool asst::RoguelikeStageEncounterTaskPlugin::_run()
|
||||
}
|
||||
std::string text = resultVec.front().text;
|
||||
|
||||
RoguelikeEvent event = event_map.at(text);
|
||||
Config::RoguelikeEvent event = event_map.at(text);
|
||||
|
||||
int special_val = 0;
|
||||
// 水月的不好识别,先试试萨米能不能用
|
||||
if (m_config->get_theme() == RoguelikeTheme::Sami) {
|
||||
OCRer analyzer(image);
|
||||
analyzer.set_task_info(m_config->get_theme() + "@Roguelike@SpecialValRecognition");
|
||||
analyzer.set_replace(Task.get<OcrTaskInfo>("NumberOcrReplace")->replace_map);
|
||||
analyzer.set_use_char_model(true);
|
||||
|
||||
if (!analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
utils::chars_to_number(analyzer.get_result().front().text, special_val);
|
||||
}
|
||||
|
||||
// 现在只有抗干扰值判断
|
||||
int choose_option = process_task(event, special_val);
|
||||
Log.info("Event:", event.name, "special_val", special_val, "choose option", choose_option);
|
||||
|
||||
Log.info("Event:", event.name, "choose option", event.default_choose);
|
||||
auto info = basic_info_with_what("RoguelikeEvent");
|
||||
info["details"]["name"] = event.name;
|
||||
info["details"]["default_choose"] = event.default_choose;
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
|
||||
const auto click_option_task_name = [&](int item, int total) {
|
||||
return m_config->get_theme() + "@Roguelike@OptionChoose" + std::to_string(total) + "-" + std::to_string(item);
|
||||
};
|
||||
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ProcessTask(*this, { m_config->get_theme() + "@Roguelike@OptionChoose" + std::to_string(event.option_num) + "-" +
|
||||
std::to_string(event.default_choose) })
|
||||
.run();
|
||||
ProcessTask(*this, { click_option_task_name(choose_option, event.option_num) }).run();
|
||||
sleep(300);
|
||||
}
|
||||
|
||||
// 判断是否点击成功,成功进入对话后左上角的生命值会消失
|
||||
image = ctrler()->get_image();
|
||||
if (hp(image) <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int max_time = event.option_num;
|
||||
while (max_time > 0) {
|
||||
// 从下往上点
|
||||
for (int i = max_time; i > 0; --i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ProcessTask(*this, { click_option_task_name(i, max_time) }).run();
|
||||
sleep(300);
|
||||
}
|
||||
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
image = ctrler()->get_image();
|
||||
if (hp(image) <= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 没通关结局有些事件会少选项
|
||||
--max_time;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeStageEncounterTaskPlugin::satisfies_condition(const Config::ChoiceRequire& requirement,
|
||||
int special_val)
|
||||
{
|
||||
int num = 0;
|
||||
bool ret = true;
|
||||
switch (requirement.vision.type) {
|
||||
case Config::ComparisonType::GreaterThan:
|
||||
ret &= utils::chars_to_number(requirement.vision.value, num) && special_val > num;
|
||||
break;
|
||||
case Config::ComparisonType::LessThan:
|
||||
ret &= utils::chars_to_number(requirement.vision.value, num) && special_val < num;
|
||||
break;
|
||||
case Config::ComparisonType::Equal:
|
||||
ret &= utils::chars_to_number(requirement.vision.value, num) && special_val == num;
|
||||
break;
|
||||
case Config::ComparisonType::None:
|
||||
break;
|
||||
default:
|
||||
Log.warn("unsupported vision type");
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
switch (requirement.hp.type) {
|
||||
// ...
|
||||
}
|
||||
*/
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int asst::RoguelikeStageEncounterTaskPlugin::process_task(const Config::RoguelikeEvent& event, const int special_val)
|
||||
{
|
||||
for (const auto& requirement : event.choice_require) {
|
||||
if (requirement.choose == -1) continue;
|
||||
if (satisfies_condition(requirement, special_val)) {
|
||||
return requirement.choose;
|
||||
}
|
||||
}
|
||||
return event.default_choose;
|
||||
}
|
||||
|
||||
int asst::RoguelikeStageEncounterTaskPlugin::hp(const cv::Mat& image)
|
||||
{
|
||||
int hp_val;
|
||||
asst::OCRer analyzer(image);
|
||||
analyzer.set_task_info("Roguelike@HpRecognition");
|
||||
|
||||
auto res_vec_opt = analyzer.analyze();
|
||||
if (!res_vec_opt) {
|
||||
return -1;
|
||||
}
|
||||
return utils::chars_to_number(res_vec_opt->front().text, hp_val) ? hp_val : 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#include "AbstractRoguelikeTaskPlugin.h"
|
||||
#include "Config/Roguelike/RoguelikeStageEncounterConfig.h"
|
||||
#include "Config/TaskData.h"
|
||||
#include "Vision/OCRer.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
@@ -7,6 +10,7 @@ namespace asst
|
||||
{
|
||||
public:
|
||||
using AbstractRoguelikeTaskPlugin::AbstractRoguelikeTaskPlugin;
|
||||
using Config = RoguelikeStageEncounterConfig;
|
||||
virtual ~RoguelikeStageEncounterTaskPlugin() override = default;
|
||||
|
||||
public:
|
||||
@@ -14,5 +18,8 @@ namespace asst
|
||||
|
||||
protected:
|
||||
virtual bool _run() override;
|
||||
static bool satisfies_condition(const Config::ChoiceRequire& requirement, int special_val);
|
||||
static int process_task(const Config::RoguelikeEvent& event, const int special_val);
|
||||
static int hp(const cv::Mat& image);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user