mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-01 01:10:34 +08:00
@@ -48,6 +48,24 @@ asst::CreditShoppingTask& asst::CreditShoppingTask::set_reserve_max_credit(bool
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool asst::CreditShoppingTask::verify_commodity_name(const TextRect& commodity) const
|
||||
{
|
||||
if (commodity.text.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto product_name_task_ptr = Task.get<OcrTaskInfo>("CreditShop-ProductName");
|
||||
Rect name_roi = product_name_task_ptr->roi;
|
||||
name_roi.x += commodity.rect.x;
|
||||
name_roi.y += commodity.rect.y;
|
||||
|
||||
OCRer ocr_analyzer(ctrler()->get_image());
|
||||
ocr_analyzer.set_roi(name_roi);
|
||||
ocr_analyzer.set_replace(product_name_task_ptr->replace_map);
|
||||
ocr_analyzer.set_required({ commodity.text });
|
||||
return ocr_analyzer.analyze().has_value();
|
||||
}
|
||||
|
||||
int asst::CreditShoppingTask::credit_ocr()
|
||||
{
|
||||
cv::Mat credit_image = ctrler()->get_image();
|
||||
@@ -101,7 +119,7 @@ int asst::CreditShoppingTask::discount_ocr(const asst::Rect& commodity)
|
||||
return utils::chars_to_number(discount, discount_number) ? discount_number : 0;
|
||||
}
|
||||
|
||||
bool asst::CreditShoppingTask::credit_shopping(bool white_list_enabled, bool credit_ocr_enabled)
|
||||
bool asst::CreditShoppingTask::credit_shopping(bool white_list_enabled, bool should_stop_on_credit)
|
||||
{
|
||||
const cv::Mat& image = ctrler()->get_image();
|
||||
|
||||
@@ -120,7 +138,7 @@ bool asst::CreditShoppingTask::credit_shopping(bool white_list_enabled, bool cre
|
||||
}
|
||||
const auto& shopping_list = shop_analyzer.get_result();
|
||||
|
||||
for (const Rect& commodity : shopping_list) {
|
||||
for (const TextRect& commodity : shopping_list) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
@@ -132,7 +150,7 @@ bool asst::CreditShoppingTask::credit_shopping(bool white_list_enabled, bool cre
|
||||
}
|
||||
|
||||
if (!m_is_white_list && m_only_buy_discount) {
|
||||
int discount = discount_ocr(commodity);
|
||||
int discount = discount_ocr(commodity.rect);
|
||||
if (discount <= 0) {
|
||||
int credit = credit_ocr();
|
||||
if (credit > MaxCredit && m_info_credit_full) {
|
||||
@@ -149,12 +167,21 @@ bool asst::CreditShoppingTask::credit_shopping(bool white_list_enabled, bool cre
|
||||
|
||||
// 因动画过渡等原因,ctrler()->click(commodity) 点击商品时可能失败,共可尝试 4 次
|
||||
// 若点击商品成功,则 CreditShop-BuyIt 的 ProcessTask 应顺利执行并返回 true
|
||||
bool bought = false;
|
||||
for (int clickCount = 0; clickCount <= 3; ++clickCount) {
|
||||
ctrler()->click(commodity);
|
||||
// 点击前复核一次商品名,避免动画或列表偏移导致盲点到错误商品。
|
||||
if (verify_commodity_name(commodity)) {
|
||||
ctrler()->click(commodity.rect);
|
||||
}
|
||||
if (ProcessTask(*this, { "CreditShop-BuyIt" }).run()) {
|
||||
bought = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 多次点击仍未进入购买流程,说明当前商品无法购买,跳过后续 NoMoney / 信用 OCR 检查
|
||||
if (!bought) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ProcessTask(*this, { "CreditShop-NoMoney" }).set_task_delay(0).set_retry_times(0).run()) {
|
||||
break;
|
||||
@@ -162,7 +189,7 @@ bool asst::CreditShoppingTask::credit_shopping(bool white_list_enabled, bool cre
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
if (credit_ocr_enabled) {
|
||||
if (should_stop_on_credit) {
|
||||
int credit = credit_ocr();
|
||||
if (credit <= MaxCredit) { // 信用值不再溢出,停止购物
|
||||
break;
|
||||
|
||||
@@ -30,12 +30,13 @@ protected:
|
||||
bool m_only_buy_discount = false; // 设置只购买折扣信用商品(未打折的白名单物品仍会购买)
|
||||
bool m_reserve_max_credit = false; // 设置消耗信用点至300以下时停止购买商品(仍然会购买白名单物品)
|
||||
bool m_info_credit_full = false; // 设置是否在不购买黑名单物品阶段通知信用点溢出
|
||||
bool verify_commodity_name(const TextRect& commodity) const;
|
||||
int credit_ocr();
|
||||
|
||||
// 用于在信用商店界面识别商品信息左上角的折扣信息
|
||||
int discount_ocr(const Rect& commodity);
|
||||
|
||||
bool credit_shopping(bool white_list_enabled, bool credit_ocr_enabled);
|
||||
bool credit_shopping(bool white_list_enabled, bool should_stop_on_credit);
|
||||
|
||||
std::vector<std::string> m_shopping_list;
|
||||
bool m_is_white_list = false;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "Config/Miscellaneous/OcrConfig.h"
|
||||
#include "MaaUtils/NoWarningCV.hpp"
|
||||
|
||||
#include "Config/TaskData.h"
|
||||
@@ -10,6 +11,26 @@
|
||||
#include "Vision/MultiMatcher.h"
|
||||
#include "Vision/OCRer.h"
|
||||
|
||||
size_t asst::CreditShopImageAnalyzer::match_required_index(
|
||||
const std::string& text,
|
||||
const std::vector<std::string>& required)
|
||||
{
|
||||
if (text.empty()) {
|
||||
return required.size();
|
||||
}
|
||||
|
||||
auto& ocr_config = OcrConfig::get_instance();
|
||||
const std::string equ_text = ocr_config.process_equivalence_class(text);
|
||||
|
||||
for (size_t index = 0; index != required.size(); ++index) {
|
||||
if (equ_text.find(ocr_config.process_equivalence_class(required.at(index))) != std::string::npos) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return required.size();
|
||||
}
|
||||
|
||||
void asst::CreditShopImageAnalyzer::set_black_list(std::vector<std::string> black_list)
|
||||
{
|
||||
Log.info(__FUNCTION__, black_list);
|
||||
@@ -82,30 +103,33 @@ bool asst::CreditShopImageAnalyzer::whether_to_buy_analyze()
|
||||
OCRer ocr_analyzer(m_image);
|
||||
ocr_analyzer.set_roi(name_roi);
|
||||
ocr_analyzer.set_replace(product_name_task_ptr->replace_map);
|
||||
ocr_analyzer.set_required(m_shopping_list);
|
||||
if (ocr_analyzer.analyze()) {
|
||||
// 黑名单模式,有识别结果说明这个商品不买,直接跳过
|
||||
if (!m_is_white_list && !m_shopping_list.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (!ocr_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
// 白名单模式,没有识别结果说明这个商品不买,直接跳过
|
||||
else if (m_is_white_list) {
|
||||
|
||||
const auto& ocr_result = ocr_analyzer.get_result();
|
||||
if (ocr_result.empty()) {
|
||||
continue;
|
||||
}
|
||||
const std::string& name = ocr_result.front().text;
|
||||
const size_t match_index = match_required_index(name, m_shopping_list);
|
||||
|
||||
// 黑名单模式,命中黑名单商品则跳过;白名单模式,未命中白名单则跳过。
|
||||
if ((!m_is_white_list && !m_shopping_list.empty() && match_index != m_shopping_list.size()) ||
|
||||
(m_is_white_list && match_index == m_shopping_list.size())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef ASST_DEBUG
|
||||
cv::rectangle(m_image_draw, make_rect<cv::Rect>(commodity), cv::Scalar(0, 0, 255), 2);
|
||||
#endif
|
||||
const std::string& name =
|
||||
ocr_analyzer.get_result().empty() ? std::string() : ocr_analyzer.get_result().front().text;
|
||||
Log.info("need to buy", name);
|
||||
m_need_to_buy.emplace_back(commodity, name);
|
||||
m_need_to_buy.emplace_back(commodity, 0.0, name);
|
||||
}
|
||||
|
||||
if (m_is_white_list) {
|
||||
std::ranges::sort(m_need_to_buy, std::less {}, [&](const auto& pair) {
|
||||
return std::ranges::find(m_shopping_list, pair.second);
|
||||
std::ranges::sort(m_need_to_buy, std::less {}, [&](const TextRect& commodity) {
|
||||
return match_required_index(commodity.text, m_shopping_list);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,12 +142,18 @@ bool asst::CreditShopImageAnalyzer::sold_out_analyze()
|
||||
Matcher sold_out_analyzer(m_image);
|
||||
sold_out_analyzer.set_task_info("CreditShop-SoldOut");
|
||||
|
||||
for (const auto& commodity : m_need_to_buy | std::views::keys) {
|
||||
sold_out_analyzer.set_roi(commodity);
|
||||
for (const TextRect& commodity : m_need_to_buy) {
|
||||
sold_out_analyzer.set_roi(commodity.rect);
|
||||
if (sold_out_analyzer.analyze()) {
|
||||
#ifdef ASST_DEBUG
|
||||
cv::rectangle(m_image_draw, make_rect<cv::Rect>(commodity), cv::Scalar(0, 0, 255));
|
||||
cv::putText(m_image_draw, "Sold Out", cv::Point(commodity.x, commodity.y), 1, 2, cv::Scalar(255, 0, 0));
|
||||
cv::rectangle(m_image_draw, make_rect<cv::Rect>(commodity.rect), cv::Scalar(0, 0, 255));
|
||||
cv::putText(
|
||||
m_image_draw,
|
||||
"Sold Out",
|
||||
cv::Point(commodity.rect.x, commodity.rect.y),
|
||||
1,
|
||||
2,
|
||||
cv::Scalar(255, 0, 0));
|
||||
#endif // ASST_DEBUG
|
||||
|
||||
// 如果识别到了售罄,那这个商品就不用买了,跳过
|
||||
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
void set_black_list(std::vector<std::string> black_list);
|
||||
void set_white_list(std::vector<std::string> white_list);
|
||||
|
||||
const std::vector<Rect>& get_result() const noexcept { return m_result; }
|
||||
const std::vector<TextRect>& get_result() const noexcept { return m_result; }
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
@@ -23,9 +23,13 @@ private:
|
||||
bool whether_to_buy_analyze();
|
||||
bool sold_out_analyze();
|
||||
|
||||
static size_t match_required_index(
|
||||
const std::string& text,
|
||||
const std::vector<std::string>& required);
|
||||
|
||||
std::vector<Rect> m_commodities;
|
||||
std::vector<std::pair<Rect, std::string>> m_need_to_buy;
|
||||
std::vector<Rect> m_result;
|
||||
std::vector<TextRect> m_need_to_buy;
|
||||
std::vector<TextRect> m_result;
|
||||
|
||||
std::vector<std::string> m_shopping_list;
|
||||
bool m_is_white_list = false;
|
||||
|
||||
Reference in New Issue
Block a user