mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 02:10:21 +08:00
feat: 添加刷常乐节点策略 (#13868)
* feat: 支持识别常乐节点的子类型 * perf: 将树洞节点优先级拆到配置里 * refactor: 将界园树洞策略从通用Routing策略中分离出来 * feat: 初步实现刷常乐节点 * feat: 为刷常乐节点功能添加UI * refactor: 重命名节点类型获取函数 * perf: 优化节点类型的日志输出 * feat: 添加发现目标常乐节点后退出 * i18n: CN常乐节点显示文本 * fix: 修复type2name映射初始化潜在问题 * perf: 添加目标子类型的设置和获取方法 * refactor: 优化优先级节点类型解析逻辑 * feat: 刷常乐节点策略下保存路上的招募券 * i18n: 为其它语言提供占位 * perf: 将新增的本地化键值放到一起 * chore: 调整日志输出中的空格 * perf: 仅在非开局招募时保存招募券 * perf: 保留招募券到达上限时正常招募 * fix: 修复gui日志输出没有使用本地化 * fix: 给界园树洞节点点击加上200ms的前置延时 * perf: 在 RoguelikeRoutingTaskPlugin 中添加运行模式以区分普通地图与界园树洞 * refactor: 优化RoguelikeBoskyPassageMap的单例使用 * feat: 添加界园树洞节点类型wpf输出及其颜色配置 * fix: 不在每次开始肉鸽重置m_target_subtype
This commit is contained in:
@@ -506,6 +506,94 @@
|
||||
"template": "empty.png",
|
||||
"specialParams": [188, 107, 608, 321, 1028, 535, 64, 57, 140, 107, 15]
|
||||
},
|
||||
"JieGarden@Roguelike@RoutingBoskyPassagePriority_Default": {
|
||||
"doc": [
|
||||
"界园树洞节点优先级策略配置",
|
||||
"按优先级从高到低排列节点类型名称",
|
||||
"拾遗>传说>故肆>易与>筹谋>常乐>杂疑(暂时取消)>灾祸(暂时取消)"
|
||||
],
|
||||
"template": "empty.png",
|
||||
"next": [
|
||||
"JieGarden@Roguelike@MapNodeOmissions",
|
||||
"JieGarden@Roguelike@MapNodeLegend",
|
||||
"JieGarden@Roguelike@MapNodeOldShop",
|
||||
"JieGarden@Roguelike@MapNodeYiTrader",
|
||||
"JieGarden@Roguelike@MapNodeScheme",
|
||||
"JieGarden@Roguelike@MapNodePlaytime"
|
||||
]
|
||||
},
|
||||
"JieGarden@Roguelike@RoutingBoskyPassagePriority_FindPlaytime": {
|
||||
"doc": ["常乐>拾遗>传说>故肆>易与>筹谋>杂疑(暂时取消)>灾祸(暂时取消)"],
|
||||
"template": "empty.png",
|
||||
"next": [
|
||||
"JieGarden@Roguelike@MapNodePlaytime",
|
||||
"JieGarden@Roguelike@MapNodeOmissions",
|
||||
"JieGarden@Roguelike@MapNodeLegend",
|
||||
"JieGarden@Roguelike@MapNodeOldShop",
|
||||
"JieGarden@Roguelike@MapNodeYiTrader",
|
||||
"JieGarden@Roguelike@MapNodeScheme"
|
||||
]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeYiTrader": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageYiTraderEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeDisaster": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageCombatDpsEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeOmissions": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageEncounterEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeLegend": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageEncounterEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeOldShop": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageEncounterEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeScheme": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageEncounterEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodePlaytime": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageEncounterEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@MapNodeDoubts": {
|
||||
"template": "empty.png",
|
||||
"next": ["JieGarden@Roguelike@RoutingAction-StageEncounterEnter"]
|
||||
},
|
||||
"JieGarden@Roguelike@ReserveRecruitmentVoucher": {
|
||||
"doc": "保留招募券 - hsv用来区分是否到达保留上限",
|
||||
"algorithm": "MatchTemplate",
|
||||
"method": "HSVCount",
|
||||
"roi": [948, 5, 229, 56],
|
||||
"colorScales": [
|
||||
[
|
||||
[0, 0, 50],
|
||||
[10, 255, 210]
|
||||
],
|
||||
[
|
||||
[170, 0, 50],
|
||||
[180, 255, 210]
|
||||
],
|
||||
[
|
||||
[0, 0, 230],
|
||||
[180, 30, 255]
|
||||
]
|
||||
],
|
||||
"action": "ClickSelf",
|
||||
"next": ["JieGarden@Roguelike@ReserveRecruitmentVoucherConfirm", "#self"]
|
||||
},
|
||||
"JieGarden@Roguelike@ReserveRecruitmentVoucherConfirm": {
|
||||
"algorithm": "OcrDetect",
|
||||
"text": ["确认留存"],
|
||||
"roi": [766, 572, 142, 49],
|
||||
"action": "ClickSelf"
|
||||
},
|
||||
"JieGarden@Roguelike@RoutingBoskyPassageNodeAnalyze": {
|
||||
"template": [
|
||||
"JieGarden@Roguelike@MapNodeOldShop.png",
|
||||
@@ -1195,6 +1283,22 @@
|
||||
"binThreshold": [200, 255],
|
||||
"useRaw": false
|
||||
},
|
||||
"JieGarden@Roguelike@StrategyChange_mode20001": {
|
||||
"text": ["_default", "_restart", "_boskyPassageDefault"],
|
||||
"ocrReplace": [
|
||||
["洪陆楼", "_default"],
|
||||
["山水阁", "_restart"],
|
||||
["云(瓦)?(亭|亨)", "_restart"],
|
||||
["汝吾(门)?", "_restart"],
|
||||
["见字祠", "_restart"],
|
||||
["始末陵", "_restart"],
|
||||
["是非境", "_boskyPassageDefault"]
|
||||
],
|
||||
"roi": [627, 14, 71, 28],
|
||||
"withoutDet": true,
|
||||
"binThreshold": [200, 255],
|
||||
"useRaw": false
|
||||
},
|
||||
"JieGarden@Roguelike@StrategyChange_mode4": {
|
||||
"text": ["_default", "_exit", "_leaveBoskyPassage"],
|
||||
"ocrReplace": [
|
||||
|
||||
@@ -1742,6 +1742,9 @@
|
||||
"Roguelike@StrategyChange_mode10001": {
|
||||
"baseTask": "Roguelike@StrategyChange_default"
|
||||
},
|
||||
"Roguelike@StrategyChange_mode20001": {
|
||||
"baseTask": "Roguelike@StrategyChange_default"
|
||||
},
|
||||
"Roguelike@StrategyChange_mode4": {
|
||||
"baseTask": "Roguelike@StrategyChange_default"
|
||||
},
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -8,39 +8,11 @@ bool asst::RoguelikeMapConfig::parse(const json::value& json)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
static const std::unordered_map<std::string, RoguelikeNodeType> NodeTypeMapping = {
|
||||
{ "CombatOps", RoguelikeNodeType::CombatOps },
|
||||
{ "EmergencyOps", RoguelikeNodeType::EmergencyOps },
|
||||
{ "DreadfulFoe", RoguelikeNodeType::DreadfulFoe },
|
||||
{ "Encounter", RoguelikeNodeType::Encounter },
|
||||
{ "Boons", RoguelikeNodeType::Boons },
|
||||
{ "SafeHouse", RoguelikeNodeType::SafeHouse },
|
||||
{ "Recreation", RoguelikeNodeType::Recreation },
|
||||
{ "RogueTrader", RoguelikeNodeType::RogueTrader },
|
||||
{ "RegionalCommissions", RoguelikeNodeType::RegionalCommissions },
|
||||
{ "LostAndFound", RoguelikeNodeType::LostAndFound },
|
||||
{ "Scout", RoguelikeNodeType::Scout },
|
||||
{ "BoskyPassage", RoguelikeNodeType::BoskyPassage },
|
||||
{ "Prophecy", RoguelikeNodeType::Prophecy },
|
||||
{ "MysteriousPresage", RoguelikeNodeType::MysteriousPresage },
|
||||
{ "FerociousPresage", RoguelikeNodeType::FerociousPresage },
|
||||
{ "IdeaFilter", RoguelikeNodeType::IdeaFilter },
|
||||
{ "FaceOff", RoguelikeNodeType::FaceOff },
|
||||
{ "Legend", RoguelikeNodeType::Legend },
|
||||
{ "Omissions", RoguelikeNodeType::Omissions },
|
||||
{ "Doubts", RoguelikeNodeType::Doubts },
|
||||
{ "Disaster", RoguelikeNodeType::Disaster },
|
||||
{ "Playtime", RoguelikeNodeType::Playtime },
|
||||
{ "OldShop", RoguelikeNodeType::OldShop },
|
||||
{ "YiTrader", RoguelikeNodeType::YiTrader },
|
||||
{ "Scheme", RoguelikeNodeType::Scheme }
|
||||
};
|
||||
|
||||
const std::string theme = json.at("theme").as_string();
|
||||
m_templ_type_mappings.erase(theme);
|
||||
|
||||
for (const auto& node_json : json.at("node").as_array()) {
|
||||
const RoguelikeNodeType node_type = NodeTypeMapping.at(node_json.at("type").as_string());
|
||||
const RoguelikeNodeType node_type = name2type(node_json.at("type").as_string());
|
||||
for (const auto& template_json : node_json.at("template").as_array()) {
|
||||
const std::string node_template = template_json.as_string();
|
||||
m_templ_type_mappings[theme].emplace(node_template, node_type);
|
||||
|
||||
72
src/MaaCore/Task/Roguelike/Map/AbstractRoguelikeMap.cpp
Normal file
72
src/MaaCore/Task/Roguelike/Map/AbstractRoguelikeMap.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "AbstractRoguelikeMap.h"
|
||||
#include "RoguelikeBoskyPassageMap.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace asst
|
||||
{
|
||||
|
||||
// 共用的节点类型映射表
|
||||
static const std::unordered_map<std::string, RoguelikeNodeType> NodeTypeMapping = {
|
||||
{ "CombatOps", RoguelikeNodeType::CombatOps },
|
||||
{ "EmergencyOps", RoguelikeNodeType::EmergencyOps },
|
||||
{ "DreadfulFoe", RoguelikeNodeType::DreadfulFoe },
|
||||
{ "Encounter", RoguelikeNodeType::Encounter },
|
||||
{ "Boons", RoguelikeNodeType::Boons },
|
||||
{ "SafeHouse", RoguelikeNodeType::SafeHouse },
|
||||
{ "Recreation", RoguelikeNodeType::Recreation },
|
||||
{ "RogueTrader", RoguelikeNodeType::RogueTrader },
|
||||
{ "RegionalCommissions", RoguelikeNodeType::RegionalCommissions },
|
||||
{ "LostAndFound", RoguelikeNodeType::LostAndFound },
|
||||
{ "Scout", RoguelikeNodeType::Scout },
|
||||
{ "BoskyPassage", RoguelikeNodeType::BoskyPassage },
|
||||
{ "Prophecy", RoguelikeNodeType::Prophecy },
|
||||
{ "MysteriousPresage", RoguelikeNodeType::MysteriousPresage },
|
||||
{ "FerociousPresage", RoguelikeNodeType::FerociousPresage },
|
||||
{ "IdeaFilter", RoguelikeNodeType::IdeaFilter },
|
||||
{ "FaceOff", RoguelikeNodeType::FaceOff },
|
||||
{ "Legend", RoguelikeNodeType::Legend },
|
||||
{ "Omissions", RoguelikeNodeType::Omissions },
|
||||
{ "Doubts", RoguelikeNodeType::Doubts },
|
||||
{ "Disaster", RoguelikeNodeType::Disaster },
|
||||
{ "Playtime", RoguelikeNodeType::Playtime },
|
||||
{ "OldShop", RoguelikeNodeType::OldShop },
|
||||
{ "YiTrader", RoguelikeNodeType::YiTrader },
|
||||
{ "Scheme", RoguelikeNodeType::Scheme }
|
||||
};
|
||||
|
||||
RoguelikeNodeType name2type(const std::string& node_name)
|
||||
{
|
||||
auto it = NodeTypeMapping.find(node_name);
|
||||
return it != NodeTypeMapping.end() ? it->second : RoguelikeNodeType::Unknown;
|
||||
}
|
||||
|
||||
std::string type2name(RoguelikeNodeType node_type)
|
||||
{
|
||||
static const std::unordered_map<RoguelikeNodeType, std::string> reverse_mapping = []() {
|
||||
std::unordered_map<RoguelikeNodeType, std::string> mapping;
|
||||
for (const auto& [name, type] : NodeTypeMapping) {
|
||||
mapping[type] = name;
|
||||
}
|
||||
return mapping;
|
||||
}();
|
||||
|
||||
auto it = reverse_mapping.find(node_type);
|
||||
return it != reverse_mapping.end() ? it->second : "Unknown";
|
||||
}
|
||||
|
||||
std::string subtype2name(RoguelikeBoskySubNodeType sub_node_type)
|
||||
{
|
||||
// 界园树洞子节点类型映射表
|
||||
static const std::unordered_map<RoguelikeBoskySubNodeType, std::string> subtype_mapping = {
|
||||
{ RoguelikeBoskySubNodeType::Unknown, "Unknown" },
|
||||
{ RoguelikeBoskySubNodeType::Ling, "Ling" }, // 令 - 常乐 掷地有声
|
||||
{ RoguelikeBoskySubNodeType::Shu, "Shu" }, // 黍 - 常乐 种因得果
|
||||
{ RoguelikeBoskySubNodeType::Nian, "Nian" } // 年 - 常乐 三缺一
|
||||
};
|
||||
|
||||
auto it = subtype_mapping.find(sub_node_type);
|
||||
return it != subtype_mapping.end() ? it->second : "Unknown";
|
||||
}
|
||||
|
||||
} // namespace asst
|
||||
@@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace asst
|
||||
{
|
||||
|
||||
enum class RoguelikeBoskySubNodeType;
|
||||
|
||||
enum class RoguelikeNodeType
|
||||
{
|
||||
Init = -1, // Init
|
||||
@@ -39,4 +41,14 @@ enum class RoguelikeNodeType
|
||||
YiTrader = 24, // 易与
|
||||
Scheme = 25 // 筹谋
|
||||
};
|
||||
|
||||
// 根据字符串获取节点类型
|
||||
RoguelikeNodeType name2type(const std::string& node_name);
|
||||
|
||||
// 获取节点类型的字符串名称
|
||||
std::string type2name(RoguelikeNodeType node_type);
|
||||
|
||||
// 获取树篱之途子节点类型的字符串名称
|
||||
std::string subtype2name(RoguelikeBoskySubNodeType sub_node_type);
|
||||
|
||||
} // namespace asst
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
#include "RoguelikeBoskyPassageMap.h"
|
||||
#include "AbstractRoguelikeMap.h"
|
||||
#include "Utils/Logger.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace asst
|
||||
{
|
||||
|
||||
RoguelikeBoskyPassageMap::RoguelikeBoskyPassageMap()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
std::optional<size_t>
|
||||
RoguelikeBoskyPassageMap::create_and_insert_node(int x, int y, RoguelikeNodeType type, bool is_open)
|
||||
{
|
||||
if (!in_bounds(x, y)) {
|
||||
Log.warn(__FUNCTION__, "| Coordinates (", x, ", ", y, ") out of bounds");
|
||||
Log.warn(__FUNCTION__, "| Coordinates (", x, ",", y, ") out of bounds");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -36,7 +31,7 @@ std::optional<size_t>
|
||||
return idx;
|
||||
}
|
||||
else {
|
||||
Log.warn(__FUNCTION__, "| Node already exists at (", x, ", ", y, ")");
|
||||
Log.warn(__FUNCTION__, "| Node already exists at (", x, ",", y, ")");
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -68,11 +63,35 @@ void RoguelikeBoskyPassageMap::set_node_type(size_t index, RoguelikeNodeType typ
|
||||
n->type = type;
|
||||
}
|
||||
|
||||
void RoguelikeBoskyPassageMap::set_node_subtype(size_t index, RoguelikeBoskySubNodeType sub_type)
|
||||
{
|
||||
Node* n = get_valid_node(index);
|
||||
if (!n || !n->exists) {
|
||||
Log.warn(__FUNCTION__, "| Invalid index or node doesn't exist:", index);
|
||||
return;
|
||||
}
|
||||
n->sub_type = sub_type;
|
||||
Log.info(__FUNCTION__, "| Set node", index, "subtype to", subtype2name(sub_type));
|
||||
}
|
||||
|
||||
RoguelikeBoskySubNodeType RoguelikeBoskyPassageMap::get_node_subtype(size_t index) const
|
||||
{
|
||||
if (index >= m_nodes.size()) {
|
||||
return RoguelikeBoskySubNodeType::Unknown;
|
||||
}
|
||||
const Node& n = m_nodes[index];
|
||||
if (!n.exists) {
|
||||
return RoguelikeBoskySubNodeType::Unknown;
|
||||
}
|
||||
return n.sub_type;
|
||||
}
|
||||
|
||||
void RoguelikeBoskyPassageMap::reset()
|
||||
{
|
||||
for (auto& n : m_nodes) {
|
||||
n = Node {}; // reset
|
||||
}
|
||||
m_curr_pos = 0;
|
||||
m_existing_count = 0;
|
||||
}
|
||||
|
||||
@@ -213,10 +232,10 @@ std::optional<size_t> RoguelikeBoskyPassageMap::ensure_node_from_pixel(
|
||||
}
|
||||
|
||||
auto [gx, gy] = pixel_to_grid_coords(px, py, config);
|
||||
Log.info(__FUNCTION__, "| analyzing node (", px, ", ", py, ") -> (", gx, ", ", gy, ")");
|
||||
Log.info(__FUNCTION__, "| analyzing node (", px, ",", py, ") -> (", gx, ",", gy, ")");
|
||||
|
||||
if (!in_bounds(gx, gy)) {
|
||||
Log.warn(__FUNCTION__, "| Grid coordinates (", gx, ", ", gy, ") out of bounds");
|
||||
Log.warn(__FUNCTION__, "| Grid coordinates (", gx, ",", gy, ") out of bounds");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractRoguelikeMap.h"
|
||||
#include "Utils/SingletonHolder.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
@@ -8,10 +9,18 @@
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class RoguelikeBoskyPassageMap
|
||||
|
||||
enum class RoguelikeBoskySubNodeType
|
||||
{
|
||||
Unknown = 0,
|
||||
Ling = 1, // 令 - 常乐 掷地有声
|
||||
Shu = 2, // 黍 - 常乐 种因得果
|
||||
Nian = 3, // 年 - 常乐 三缺一
|
||||
};
|
||||
|
||||
class RoguelikeBoskyPassageMap : public SingletonHolder<RoguelikeBoskyPassageMap>
|
||||
{
|
||||
public:
|
||||
RoguelikeBoskyPassageMap();
|
||||
~RoguelikeBoskyPassageMap() = default;
|
||||
|
||||
// ===================== 创建/更新 =====================
|
||||
@@ -24,6 +33,8 @@ public:
|
||||
|
||||
void set_node_type(size_t index, RoguelikeNodeType type);
|
||||
|
||||
void set_curr_pos(size_t index) { m_curr_pos = index; }
|
||||
|
||||
void reset();
|
||||
|
||||
// ===================== 获取信息 =====================
|
||||
@@ -39,6 +50,8 @@ public:
|
||||
// 全部开放节点(包含已通关)
|
||||
[[nodiscard]] std::vector<size_t> get_open_nodes(std::optional<RoguelikeNodeType> type_filter = std::nullopt) const;
|
||||
|
||||
[[nodiscard]] size_t get_curr_pos() const { return m_curr_pos; }
|
||||
|
||||
[[nodiscard]] RoguelikeNodeType get_node_type(size_t index) const;
|
||||
[[nodiscard]] bool get_node_exists(size_t index) const;
|
||||
[[nodiscard]] bool get_node_open(size_t index) const;
|
||||
@@ -102,6 +115,18 @@ public:
|
||||
bool is_open,
|
||||
RoguelikeNodeType type = RoguelikeNodeType::Unknown);
|
||||
|
||||
// 设置子类型
|
||||
void set_node_subtype(size_t index, RoguelikeBoskySubNodeType sub_type);
|
||||
|
||||
// 获取子类型
|
||||
[[nodiscard]] RoguelikeBoskySubNodeType get_node_subtype(size_t index) const;
|
||||
|
||||
// 设置目标子类型
|
||||
void set_target_subtype(RoguelikeBoskySubNodeType target) { m_target_subtype = target; }
|
||||
|
||||
// 获取目标子类型
|
||||
[[nodiscard]] RoguelikeBoskySubNodeType get_target_subtype() const { return m_target_subtype; }
|
||||
|
||||
// 中心节点 index
|
||||
static constexpr int CENTER_X = 3;
|
||||
static constexpr int CENTER_Y = 2;
|
||||
@@ -117,13 +142,16 @@ private:
|
||||
struct Node
|
||||
{
|
||||
RoguelikeNodeType type = RoguelikeNodeType::Unknown;
|
||||
RoguelikeBoskySubNodeType sub_type = RoguelikeBoskySubNodeType::Unknown;
|
||||
bool exists = false; // 是否已创建
|
||||
bool is_open = false; // 是否开放可探索
|
||||
bool visited = false; // 是否已访问
|
||||
};
|
||||
|
||||
std::array<Node, WIDTH * HEIGHT> m_nodes {}; // 直接按 y*WIDTH + x 索引
|
||||
size_t m_curr_pos = 0;
|
||||
size_t m_existing_count = 0;
|
||||
RoguelikeBoskySubNodeType m_target_subtype = RoguelikeBoskySubNodeType::Unknown;
|
||||
|
||||
// 辅助函数:获取有效的节点引用,如果索引无效或节点不存在则返回 nullptr
|
||||
[[nodiscard]] Node* get_valid_node(size_t index);
|
||||
|
||||
@@ -58,10 +58,17 @@ bool asst::RoguelikeRoutingTaskPlugin::load_params([[maybe_unused]] const json::
|
||||
m_routing_strategy = RoutingStrategy::FastInvestment_JieGarden;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
m_routing_strategy = RoutingStrategy::BoskyPassage_JieGarden;
|
||||
|
||||
if (mode == RoguelikeMode::FindPlaytime) {
|
||||
m_bosky_routing_strategy = RoutingStrategy::FindPlaytime_JieGarden;
|
||||
int target = m_config->get_find_playTime_target();
|
||||
RoguelikeBoskyPassageMap::get_instance().set_target_subtype(static_cast<RoguelikeBoskySubNodeType>(target));
|
||||
Log.info(__FUNCTION__, "| FindPlaytime mode enabled with target:", target);
|
||||
return true;
|
||||
}
|
||||
|
||||
m_bosky_routing_strategy = RoutingStrategy::BoskyPassage_JieGarden;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mode == RoguelikeMode::FastPass && squad == "蓝图测绘分队") {
|
||||
@@ -75,7 +82,7 @@ bool asst::RoguelikeRoutingTaskPlugin::load_params([[maybe_unused]] const json::
|
||||
void asst::RoguelikeRoutingTaskPlugin::reset_in_run_variables()
|
||||
{
|
||||
m_map.reset();
|
||||
m_bosky_map.reset();
|
||||
RoguelikeBoskyPassageMap::get_instance().reset();
|
||||
m_need_generate_map = true;
|
||||
m_selected_column = 0;
|
||||
m_selected_x = 0;
|
||||
@@ -88,6 +95,17 @@ bool asst::RoguelikeRoutingTaskPlugin::verify(const AsstMsg msg, const json::val
|
||||
}
|
||||
|
||||
std::string task_name = details.get("details", "task", "");
|
||||
Log.debug(__FUNCTION__, "| Checking task:", task_name);
|
||||
|
||||
// 检查是否包含 BoskyPassage 并设置运行模式
|
||||
if (task_name.find("JieGarden@Roguelike@Routing-BoskyPassage") != std::string::npos) {
|
||||
m_run_mode = RoguelikeRoutingTaskRunMode::BoskyPassage_JieGarden;
|
||||
Log.debug(__FUNCTION__, "| Setting run mode to BoskyPassage_JieGarden");
|
||||
}
|
||||
else {
|
||||
m_run_mode = RoguelikeRoutingTaskRunMode::Default;
|
||||
Log.debug(__FUNCTION__, "| Setting run mode to Default");
|
||||
}
|
||||
|
||||
// trigger 任务的名字可以为 "...@Roguelike@Routing-..." 的形式
|
||||
if (const size_t pos = task_name.find('-'); pos != std::string::npos) {
|
||||
@@ -95,6 +113,7 @@ bool asst::RoguelikeRoutingTaskPlugin::verify(const AsstMsg msg, const json::val
|
||||
}
|
||||
|
||||
if (task_name == m_config->get_theme() + "@Roguelike@Routing") {
|
||||
Log.info(__FUNCTION__, "| RoguelikeRoutingTaskPlugin triggered for task:", task_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -105,102 +124,135 @@ bool asst::RoguelikeRoutingTaskPlugin::_run()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
switch (m_routing_strategy) {
|
||||
case RoutingStrategy::FastInvestment_Sarkaz:
|
||||
if (m_need_generate_map) {
|
||||
// 随机点击一个第一列的节点,先随便写写,垃圾代码迟早要重构
|
||||
ProcessTask(*this, { "Sarkaz@Roguelike@Routing-CombatOps" }).run();
|
||||
// 刷新节点
|
||||
ProcessTask(*this, { m_config->get_theme() + "@Roguelike@RoutingRefreshNode" }).run();
|
||||
// 不识别了,进商店,Go!
|
||||
Task.set_task_base("Sarkaz@Roguelike@RoutingAction", "Sarkaz@Roguelike@RoutingAction-StageTraderEnter");
|
||||
// 偷懒,直接用 m_need_generate_map 判断是否已进过商店
|
||||
m_need_generate_map = false;
|
||||
}
|
||||
else {
|
||||
Task.set_task_base("Sarkaz@Roguelike@RoutingAction", "Sarkaz@Roguelike@RoutingAction-ExitThenAbandon");
|
||||
}
|
||||
break;
|
||||
case RoutingStrategy::FastInvestment_JieGarden:
|
||||
if (m_need_generate_map) {
|
||||
cv::Mat image = ctrler()->get_image();
|
||||
cv::Mat image_draw = image.clone();
|
||||
update_map(image, RoguelikeMap::INIT_INDEX + 1, image_draw);
|
||||
#ifdef ASST_DEBUG
|
||||
const std::filesystem::path& relative_dir = utils::path("debug") / utils::path("roguelikeMap");
|
||||
const auto relative_path = relative_dir / (std::format("{}_draw.png", utils::format_now_for_filename()));
|
||||
Log.debug(__FUNCTION__, "| Saving image to ", relative_path);
|
||||
asst::imwrite(relative_path, image_draw);
|
||||
#endif
|
||||
m_need_generate_map = false;
|
||||
}
|
||||
if (m_map.get_curr_pos() == RoguelikeMap::INIT_INDEX) {
|
||||
m_map.set_cost_fun([&](const RoguelikeNodePtr& node) {
|
||||
if (node->type == RoguelikeNodeType::CombatOps || node->type == RoguelikeNodeType::EmergencyOps ||
|
||||
node->type == RoguelikeNodeType::DreadfulFoe) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
m_map.update_node_costs();
|
||||
const size_t next_node = m_map.get_next_node();
|
||||
Log.info(
|
||||
__FUNCTION__,
|
||||
"| Running with routing_strategy:",
|
||||
static_cast<int>(m_routing_strategy),
|
||||
"bosky_routing_strategy:",
|
||||
static_cast<int>(m_bosky_routing_strategy));
|
||||
|
||||
// 若无法避免超过两场战斗则重开
|
||||
if (m_map.get_node_cost(next_node) >= 2) {
|
||||
callback(
|
||||
AsstMsg::TaskChainExtraInfo,
|
||||
json::object {
|
||||
{ "what", "RoutingRestart" },
|
||||
{ "why", "TooManyBattlesAhead" },
|
||||
{ "node_cost", m_map.get_node_cost(next_node) },
|
||||
});
|
||||
|
||||
Task.set_task_base(
|
||||
"JieGarden@Roguelike@RoutingAction",
|
||||
"JieGarden@Roguelike@RoutingAction-ExitThenAbandon");
|
||||
reset_in_run_variables();
|
||||
if (m_run_mode == RoguelikeRoutingTaskRunMode::Default) {
|
||||
switch (m_routing_strategy) {
|
||||
case RoutingStrategy::FastInvestment_Sarkaz:
|
||||
if (m_need_generate_map) {
|
||||
// 随机点击一个第一列的节点,先随便写写,垃圾代码迟早要重构
|
||||
ProcessTask(*this, { "Sarkaz@Roguelike@Routing-CombatOps" }).run();
|
||||
// 刷新节点
|
||||
ProcessTask(*this, { m_config->get_theme() + "@Roguelike@RoutingRefreshNode" }).run();
|
||||
// 不识别了,进商店,Go!
|
||||
Task.set_task_base("Sarkaz@Roguelike@RoutingAction", "Sarkaz@Roguelike@RoutingAction-StageTraderEnter");
|
||||
// 偷懒,直接用 m_need_generate_map 判断是否已进过商店
|
||||
m_need_generate_map = false;
|
||||
}
|
||||
else {
|
||||
const int next_node_x = m_origin_x;
|
||||
const int next_node_y = m_map.get_node_y(next_node);
|
||||
Point next_node_center = Point(next_node_x + m_node_width / 2, next_node_y + m_node_height / 2);
|
||||
ctrler()->click(next_node_center);
|
||||
sleep(200);
|
||||
|
||||
Task.set_task_base(
|
||||
"JieGarden@Roguelike@RoutingAction",
|
||||
"JieGarden@Roguelike@RoutingAction-StageCombatOpsEnterThenLeave");
|
||||
m_map.set_curr_pos(next_node);
|
||||
Task.set_task_base("Sarkaz@Roguelike@RoutingAction", "Sarkaz@Roguelike@RoutingAction-ExitThenAbandon");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 执行默认的避战策略
|
||||
Task.set_task_base("JieGarden@Roguelike@RoutingAction", "JieGarden@Roguelike@Stages_default");
|
||||
}
|
||||
break;
|
||||
case RoutingStrategy::FastPass:
|
||||
if (m_need_generate_map) {
|
||||
generate_map();
|
||||
m_need_generate_map = false;
|
||||
}
|
||||
break;
|
||||
case RoutingStrategy::FastInvestment_JieGarden:
|
||||
if (m_need_generate_map) {
|
||||
cv::Mat image = ctrler()->get_image();
|
||||
cv::Mat image_draw = image.clone();
|
||||
update_map(image, RoguelikeMap::INIT_INDEX + 1, image_draw);
|
||||
#ifdef ASST_DEBUG
|
||||
const std::filesystem::path& relative_dir = utils::path("debug") / utils::path("roguelikeMap");
|
||||
const auto relative_path =
|
||||
relative_dir / (std::format("{}_draw.png", utils::format_now_for_filename()));
|
||||
Log.debug(__FUNCTION__, "| Saving image to", relative_path);
|
||||
asst::imwrite(relative_path, image_draw);
|
||||
#endif
|
||||
m_need_generate_map = false;
|
||||
}
|
||||
if (m_map.get_curr_pos() == RoguelikeMap::INIT_INDEX) {
|
||||
m_map.set_cost_fun([&](const RoguelikeNodePtr& node) {
|
||||
if (node->type == RoguelikeNodeType::CombatOps || node->type == RoguelikeNodeType::EmergencyOps ||
|
||||
node->type == RoguelikeNodeType::DreadfulFoe) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
m_map.update_node_costs();
|
||||
const size_t next_node = m_map.get_next_node();
|
||||
|
||||
m_selected_column = m_map.get_node_column(m_map.get_curr_pos());
|
||||
update_selected_x();
|
||||
// 若无法避免超过两场战斗则重开
|
||||
if (m_map.get_node_cost(next_node) >= 2) {
|
||||
callback(
|
||||
AsstMsg::TaskChainExtraInfo,
|
||||
json::object {
|
||||
{ "what", "RoutingRestart" },
|
||||
{ "why", "TooManyBattlesAhead" },
|
||||
{ "node_cost", m_map.get_node_cost(next_node) },
|
||||
});
|
||||
|
||||
refresh_following_combat_nodes();
|
||||
navigate_route();
|
||||
break;
|
||||
Task.set_task_base(
|
||||
"JieGarden@Roguelike@RoutingAction",
|
||||
"JieGarden@Roguelike@RoutingAction-ExitThenAbandon");
|
||||
reset_in_run_variables();
|
||||
}
|
||||
else {
|
||||
const int next_node_x = m_origin_x;
|
||||
const int next_node_y = m_map.get_node_y(next_node);
|
||||
Point next_node_center = Point(next_node_x + m_node_width / 2, next_node_y + m_node_height / 2);
|
||||
ctrler()->click(next_node_center);
|
||||
sleep(200);
|
||||
|
||||
case RoutingStrategy::BoskyPassage_JieGarden: {
|
||||
bosky_update_map();
|
||||
bosky_decide_and_click();
|
||||
break;
|
||||
Task.set_task_base(
|
||||
"JieGarden@Roguelike@RoutingAction",
|
||||
"JieGarden@Roguelike@RoutingAction-StageCombatOpsEnterThenLeave");
|
||||
m_map.set_curr_pos(next_node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 执行默认的避战策略
|
||||
Task.set_task_base("JieGarden@Roguelike@RoutingAction", "JieGarden@Roguelike@Stages_default");
|
||||
}
|
||||
break;
|
||||
case RoutingStrategy::FastPass:
|
||||
if (m_need_generate_map) {
|
||||
generate_map();
|
||||
m_need_generate_map = false;
|
||||
}
|
||||
|
||||
m_selected_column = m_map.get_node_column(m_map.get_curr_pos());
|
||||
update_selected_x();
|
||||
|
||||
refresh_following_combat_nodes();
|
||||
navigate_route();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (m_run_mode == RoguelikeRoutingTaskRunMode::BoskyPassage_JieGarden) {
|
||||
switch (m_bosky_routing_strategy) {
|
||||
case RoutingStrategy::BoskyPassage_JieGarden: {
|
||||
bosky_update_map();
|
||||
const std::vector<RoguelikeNodeType> priority_order = get_bosky_passage_priority("Default");
|
||||
bosky_decide_and_click(priority_order);
|
||||
break;
|
||||
}
|
||||
|
||||
case RoutingStrategy::FindPlaytime_JieGarden: {
|
||||
// 更新地图
|
||||
bosky_update_map();
|
||||
const std::vector<RoguelikeNodeType> priority_order = get_bosky_passage_priority("FindPlaytime");
|
||||
|
||||
// 获取目标常乐节点子类型
|
||||
Log.info(
|
||||
__FUNCTION__,
|
||||
"| Looking for playtime subtype:",
|
||||
subtype2name(RoguelikeBoskyPassageMap::get_instance().get_target_subtype()));
|
||||
|
||||
// 尝试找到目标节点,使用常乐节点优先的策略
|
||||
bosky_decide_and_click(priority_order);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -225,7 +277,7 @@ void asst::RoguelikeRoutingTaskPlugin::bosky_update_map()
|
||||
}
|
||||
|
||||
MultiMatcher::ResultsVec match_results = node_analyzer.get_result();
|
||||
Log.info(__FUNCTION__, "| found ", match_results.size(), " nodes");
|
||||
Log.info(__FUNCTION__, "| found", match_results.size(), "nodes");
|
||||
|
||||
// 排序 靠左上优先
|
||||
sort_by_vertical_(match_results);
|
||||
@@ -238,26 +290,27 @@ void asst::RoguelikeRoutingTaskPlugin::bosky_update_map()
|
||||
|
||||
// 处理每个识别到的节点
|
||||
for (const auto& [rect, score, templ_name] : match_results) {
|
||||
Log.debug(__FUNCTION__, "| analyzing node ", templ_name, " at (", rect.x, ", ", rect.y, ")");
|
||||
Log.debug(__FUNCTION__, "| analyzing node", templ_name, "at (", rect.x, ",", rect.y, ")");
|
||||
|
||||
const RoguelikeNodeType type = RoguelikeMapInfo.templ2type(theme, templ_name);
|
||||
if (type == RoguelikeNodeType::Unknown) {
|
||||
Log.warn(__FUNCTION__, "| unknown template: ", templ_name);
|
||||
Log.warn(__FUNCTION__, "| unknown template:", templ_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否为灰色节点
|
||||
const bool is_open = templ_name.find("Grey") == std::string::npos;
|
||||
|
||||
auto idx = m_bosky_map.ensure_node_from_pixel(rect.x, rect.y, m_bosky_config, is_open, type);
|
||||
auto idx = RoguelikeBoskyPassageMap::get_instance()
|
||||
.ensure_node_from_pixel(rect.x, rect.y, m_bosky_config, is_open, type);
|
||||
|
||||
if (idx.has_value()) {
|
||||
// 更新节点类型(防止类型不一致)
|
||||
m_bosky_map.set_node_type(idx.value(), type);
|
||||
Log.debug(__FUNCTION__, "| updated node (", idx.value(), ") type: (", static_cast<int>(type), ")");
|
||||
RoguelikeBoskyPassageMap::get_instance().set_node_type(idx.value(), type);
|
||||
Log.debug(__FUNCTION__, "| updated node (", idx.value(), ") type: (", type2name(type), ")");
|
||||
}
|
||||
else {
|
||||
Log.warn(__FUNCTION__, "| failed to create/update node from pixel (", rect.x, ", ", rect.y, ")");
|
||||
Log.warn(__FUNCTION__, "| failed to create/update node from pixel (", rect.x, ",", rect.y, ")");
|
||||
}
|
||||
|
||||
#ifdef ASST_DEBUG
|
||||
@@ -265,8 +318,9 @@ void asst::RoguelikeRoutingTaskPlugin::bosky_update_map()
|
||||
cv::rectangle(image_draw, make_rect<cv::Rect>(rect), cv::Scalar(0, 0, 255), 2);
|
||||
cv::putText(
|
||||
image_draw,
|
||||
std::to_string(static_cast<int>(type)) + " (" + std::to_string(m_bosky_map.get_node_x(idx.value())) +
|
||||
", " + std::to_string(m_bosky_map.get_node_y(idx.value())) + ")",
|
||||
std::to_string(static_cast<int>(type)) + " (" +
|
||||
std::to_string(RoguelikeBoskyPassageMap::get_instance().get_node_x(idx.value())) + ", " +
|
||||
std::to_string(RoguelikeBoskyPassageMap::get_instance().get_node_y(idx.value())) + ")",
|
||||
cv::Point(rect.x, rect.y - 5),
|
||||
cv::FONT_HERSHEY_SIMPLEX,
|
||||
0.5,
|
||||
@@ -279,47 +333,29 @@ void asst::RoguelikeRoutingTaskPlugin::bosky_update_map()
|
||||
#ifdef ASST_DEBUG
|
||||
const std::filesystem::path& relative_dir = utils::path("debug") / utils::path("roguelikeMap");
|
||||
const auto relative_path = relative_dir / (std::format("{}_bosky_draw.png", utils::format_now_for_filename()));
|
||||
Log.debug(__FUNCTION__, "| Saving bosky map image to ", relative_path);
|
||||
Log.debug(__FUNCTION__, "| Saving bosky map image to", relative_path);
|
||||
asst::imwrite(relative_path, image_draw);
|
||||
#endif
|
||||
|
||||
Log.info(__FUNCTION__, "| map updated with ", m_bosky_map.size(), " nodes");
|
||||
Log.info(__FUNCTION__, "| map updated with", RoguelikeBoskyPassageMap::get_instance().size(), "nodes");
|
||||
}
|
||||
|
||||
void asst::RoguelikeRoutingTaskPlugin::bosky_decide_and_click()
|
||||
void asst::RoguelikeRoutingTaskPlugin::bosky_decide_and_click(const std::vector<RoguelikeNodeType>& priority_order)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
Log.info(__FUNCTION__, "| deciding and clicking a bosky passage node");
|
||||
|
||||
// 定义节点类型的优先级顺序
|
||||
const std::vector<RoguelikeNodeType> priority_order = {
|
||||
RoguelikeNodeType::Omissions, // 拾遗 - 最高优先级
|
||||
RoguelikeNodeType::Legend, // 传说
|
||||
RoguelikeNodeType::YiTrader, // 易与
|
||||
RoguelikeNodeType::OldShop, // 故肆
|
||||
RoguelikeNodeType::Scheme, // 筹谋
|
||||
RoguelikeNodeType::Playtime, // 常乐
|
||||
RoguelikeNodeType::Doubts, // 杂疑
|
||||
// RoguelikeNodeType::Disaster // 祸乱 - 最低优先级
|
||||
};
|
||||
|
||||
size_t chosen = 0;
|
||||
bool found = false;
|
||||
|
||||
// 按优先级顺序查找可用的节点
|
||||
for (const auto& node_type : priority_order) {
|
||||
auto nodes_of_type = m_bosky_map.get_open_unvisited_nodes(node_type);
|
||||
auto nodes_of_type = RoguelikeBoskyPassageMap::get_instance().get_open_unvisited_nodes(node_type);
|
||||
if (!nodes_of_type.empty()) {
|
||||
chosen = nodes_of_type.front();
|
||||
found = true;
|
||||
Log.debug(
|
||||
__FUNCTION__,
|
||||
"| found node of type (",
|
||||
static_cast<int>(node_type),
|
||||
") with index (",
|
||||
chosen,
|
||||
")");
|
||||
Log.debug(__FUNCTION__, "| found node of type (", type2name(node_type), ") with index (", chosen, ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -331,14 +367,14 @@ void asst::RoguelikeRoutingTaskPlugin::bosky_decide_and_click()
|
||||
"JieGarden@Roguelike@RoutingAction-ClickRemainingCandleFlame");
|
||||
return;
|
||||
}
|
||||
int gx = m_bosky_map.get_node_x(chosen);
|
||||
int gy = m_bosky_map.get_node_y(chosen);
|
||||
RoguelikeNodeType node_type = m_bosky_map.get_node_type(chosen);
|
||||
int gx = RoguelikeBoskyPassageMap::get_instance().get_node_x(chosen);
|
||||
int gy = RoguelikeBoskyPassageMap::get_instance().get_node_y(chosen);
|
||||
RoguelikeNodeType node_type = RoguelikeBoskyPassageMap::get_instance().get_node_type(chosen);
|
||||
|
||||
Log.info(__FUNCTION__, "| chosen node: ", chosen, " (", gx, ", ", gy, ") type: ", static_cast<int>(node_type));
|
||||
Log.info(__FUNCTION__, "| chosen node:", chosen, "(", gx, ",", gy, ") type:", type2name(node_type));
|
||||
|
||||
// 点击节点中心
|
||||
auto [px, py] = m_bosky_map.get_node_pixel(
|
||||
auto [px, py] = RoguelikeBoskyPassageMap::get_instance().get_node_pixel(
|
||||
chosen,
|
||||
m_bosky_config.origin_x,
|
||||
m_bosky_config.origin_y,
|
||||
@@ -346,38 +382,29 @@ void asst::RoguelikeRoutingTaskPlugin::bosky_decide_and_click()
|
||||
m_bosky_config.row_offset);
|
||||
|
||||
if (px == -1 || py == -1) {
|
||||
Log.error(__FUNCTION__, "| Invalid pixel coordinates for node ", chosen, ": (", px, ", ", py, ")");
|
||||
Log.error(__FUNCTION__, "| Invalid pixel coordinates for node", chosen, ": (", px, ",", py, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Point click_point(px + m_bosky_config.node_width / 2, py + m_bosky_config.node_height / 2);
|
||||
sleep(200);
|
||||
ctrler()->click(click_point);
|
||||
m_bosky_map.set_visited(chosen);
|
||||
RoguelikeBoskyPassageMap::get_instance().set_visited(chosen);
|
||||
RoguelikeBoskyPassageMap::get_instance().set_curr_pos(chosen);
|
||||
|
||||
// 根据节点类型判断 Task.set_task_base base_task_name
|
||||
switch (m_bosky_map.get_node_type(chosen)) {
|
||||
case RoguelikeNodeType::YiTrader:
|
||||
Task.set_task_base("JieGarden@Roguelike@RoutingAction", "JieGarden@Roguelike@RoutingAction-StageYiTraderEnter");
|
||||
break;
|
||||
case RoguelikeNodeType::Disaster:
|
||||
Task.set_task_base(
|
||||
"JieGarden@Roguelike@RoutingAction",
|
||||
"JieGarden@Roguelike@RoutingAction-StageCombatDpsEnter");
|
||||
break;
|
||||
case RoguelikeNodeType::Omissions:
|
||||
case RoguelikeNodeType::Legend:
|
||||
case RoguelikeNodeType::OldShop:
|
||||
case RoguelikeNodeType::Scheme:
|
||||
case RoguelikeNodeType::Playtime:
|
||||
case RoguelikeNodeType::Doubts:
|
||||
Task.set_task_base(
|
||||
"JieGarden@Roguelike@RoutingAction",
|
||||
"JieGarden@Roguelike@RoutingAction-StageEncounterEnter");
|
||||
break;
|
||||
default:
|
||||
Log.warn(__FUNCTION__, "| unknown node type: ", static_cast<int>(node_type));
|
||||
break;
|
||||
}
|
||||
// 发送节点类型到 WPF
|
||||
std::string node_type_name = type2name(node_type);
|
||||
auto node_info = basic_info_with_what("BoskyPassageNode");
|
||||
node_info["details"]["node_type"] = node_type_name;
|
||||
callback(AsstMsg::SubTaskExtraInfo, node_info);
|
||||
|
||||
// 执行节点类型对应的任务
|
||||
const std::string& theme = m_config->get_theme();
|
||||
std::string node_name = type2name(node_type);
|
||||
|
||||
const std::string node_task_name = theme + "@Roguelike@MapNode" + node_name;
|
||||
// 设置 next
|
||||
Task.set_task_base("JieGarden@Roguelike@RoutingAction", node_task_name);
|
||||
}
|
||||
|
||||
bool asst::RoguelikeRoutingTaskPlugin::update_map(
|
||||
@@ -715,3 +742,52 @@ void asst::RoguelikeRoutingTaskPlugin::update_selected_x()
|
||||
m_selected_x = m_middle_x;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<asst::RoguelikeNodeType>
|
||||
asst::RoguelikeRoutingTaskPlugin::get_bosky_passage_priority(const std::string& strategy)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
const std::string& theme = m_config->get_theme();
|
||||
const std::string config_name = theme + "@Roguelike@RoutingBoskyPassagePriority_" + strategy;
|
||||
|
||||
auto task_info = Task.get(config_name);
|
||||
if (!task_info) {
|
||||
Log.error(__FUNCTION__, "| priority config not found:", config_name);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 从 next 字段中读取优先级配置
|
||||
const auto& next_tasks = task_info->next;
|
||||
if (next_tasks.empty()) {
|
||||
Log.warn(__FUNCTION__, "| Priority config is empty in:", config_name);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 从任务名称中解析节点类型
|
||||
std::vector<RoguelikeNodeType> priority_order;
|
||||
priority_order.reserve(next_tasks.size());
|
||||
|
||||
for (const std::string& task_name : next_tasks) {
|
||||
// 解析类似 "JieGarden@Roguelike@MapNodeYiTrader" 这样的任务名 -> "YiTrader"
|
||||
constexpr std::string_view map_node_prefix = "MapNode";
|
||||
const size_t pos = task_name.rfind(map_node_prefix);
|
||||
if (pos == std::string::npos) {
|
||||
Log.warn(__FUNCTION__, "| Invalid task name in priority config:", task_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string node_name = task_name.substr(pos + map_node_prefix.length());
|
||||
RoguelikeNodeType node_type = name2type(node_name);
|
||||
if (node_type != RoguelikeNodeType::Unknown) {
|
||||
priority_order.push_back(node_type);
|
||||
Log.debug(__FUNCTION__, "| Added priority node type:", type2name(node_type), "from task:", task_name);
|
||||
}
|
||||
else {
|
||||
Log.warn(__FUNCTION__, "| Failed to parse node type from task:", task_name);
|
||||
}
|
||||
}
|
||||
|
||||
Log.info(__FUNCTION__, "| Loaded", priority_order.size(), "node types from priority config");
|
||||
return priority_order;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,14 @@ public:
|
||||
FastInvestment_Sarkaz,
|
||||
FastInvestment_JieGarden,
|
||||
FastPass,
|
||||
BoskyPassage_JieGarden
|
||||
BoskyPassage_JieGarden,
|
||||
FindPlaytime_JieGarden
|
||||
};
|
||||
|
||||
enum class RoguelikeRoutingTaskRunMode
|
||||
{
|
||||
Default, // 普通肉鸽地图
|
||||
BoskyPassage_JieGarden, // 界园树洞地图
|
||||
};
|
||||
|
||||
protected:
|
||||
@@ -61,9 +68,11 @@ private:
|
||||
|
||||
// ———————— constants and variables ———————————————————————————————————————————————
|
||||
RoutingStrategy m_routing_strategy = RoutingStrategy::None;
|
||||
RoutingStrategy m_bosky_routing_strategy = RoutingStrategy::None;
|
||||
RoguelikeMap m_map;
|
||||
// 界园树洞平面地图
|
||||
RoguelikeBoskyPassageMap m_bosky_map;
|
||||
// 界园树洞地图使用单例模式,通过 RoguelikeBoskyPassageMap::get_instance() 访问
|
||||
// 运行模式
|
||||
mutable RoguelikeRoutingTaskRunMode m_run_mode = RoguelikeRoutingTaskRunMode::Default;
|
||||
bool m_need_generate_map = true;
|
||||
size_t m_selected_column = 0; // 当前选中节点所在列
|
||||
int m_selected_x = 0; // 当前选中节点的横坐标 (Rect.x)
|
||||
@@ -79,8 +88,9 @@ private:
|
||||
int m_direction_threshold = 0; // 节点间连线方向判定的阈值
|
||||
|
||||
// ==================== BoskyPassage (JieGarden) 专用 ====================
|
||||
void bosky_update_map(); // 从当前截图识别所有可见节点并更新/创建
|
||||
void bosky_decide_and_click(); // 策略
|
||||
void bosky_update_map(); // 从当前截图识别所有可见节点并更新/创建
|
||||
void bosky_decide_and_click(const std::vector<RoguelikeNodeType>& priority_order); // 策略,可指定优先级策略
|
||||
std::vector<RoguelikeNodeType> get_bosky_passage_priority(const std::string& strategy); // 从配置文件读取优先级
|
||||
|
||||
RoguelikeBoskyPassageMap::BoskyPassageMapConfig m_bosky_config;
|
||||
};
|
||||
|
||||
@@ -82,6 +82,10 @@ bool asst::RoguelikeConfig::verify_and_load_params(const json::value& params)
|
||||
// 启用特殊策略,联动 RoguelikeRoutingTaskPlugin
|
||||
Task.set_task_base(strategy_task, "JieGarden@Roguelike@StrategyChange_mode4-FastPass");
|
||||
}
|
||||
if (m_mode == RoguelikeMode::FindPlaytime) {
|
||||
// 启用刷常乐节点策略,联动 RoguelikeRoutingTaskPlugin
|
||||
Task.set_task_base(strategy_task, "JieGarden@Roguelike@StrategyChange_mode20001");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +105,14 @@ bool asst::RoguelikeConfig::verify_and_load_params(const json::value& params)
|
||||
m_run_for_collectible = true; // 烧开水模式下,如果不是只凹直升,第一轮游戏先烧水
|
||||
}
|
||||
|
||||
if (m_mode == RoguelikeMode::FindPlaytime) {
|
||||
m_find_playTime_target = params.get("find_playTime_target", 0);
|
||||
if (m_find_playTime_target < 1 || m_find_playTime_target > 3) {
|
||||
Log.error(__FUNCTION__, "| Invalid find_playTime_target", m_find_playTime_target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,10 @@ enum class RoguelikeMode
|
||||
CLP_PDS = 5, // 5 - 刷隐藏坍缩范式,以增加坍缩值为最优先目标
|
||||
|
||||
// ------------------ 萨卡兹主题专用模式 ------------------
|
||||
FastPass = 10001 // 10001 - 快速通过第一层
|
||||
FastPass = 10001, // 10001 - 快速通过第一层
|
||||
|
||||
// ------------------ 界园主题专用模式 ------------------
|
||||
FindPlaytime = 20001 // 20001 - 刷常乐节点,第一层进洞,找不到需要的节点就重开
|
||||
};
|
||||
|
||||
struct RoguelikeOper
|
||||
@@ -92,7 +95,8 @@ public:
|
||||
return mode == RoguelikeMode::Exp || mode == RoguelikeMode::Investment || mode == RoguelikeMode::Collectible ||
|
||||
mode == RoguelikeMode::Squad || mode == RoguelikeMode::Exploration ||
|
||||
(mode == RoguelikeMode::CLP_PDS && theme == RoguelikeTheme::Sami) ||
|
||||
(mode == RoguelikeMode::FastPass && theme == RoguelikeTheme::Sarkaz);
|
||||
(mode == RoguelikeMode::FastPass && theme == RoguelikeTheme::Sarkaz) ||
|
||||
(mode == RoguelikeMode::FindPlaytime && theme == RoguelikeTheme::JieGarden);
|
||||
}
|
||||
|
||||
bool verify_and_load_params(const json::value& params);
|
||||
@@ -134,6 +138,11 @@ public:
|
||||
|
||||
bool get_collectible_mode_shopping() const { return m_collectible_mode_shopping; }
|
||||
|
||||
// ------------------ 刷常乐节点模式 ------------------
|
||||
void set_find_playTime_target(int target) { m_find_playTime_target = target; }
|
||||
|
||||
int get_find_playTime_target() const { return m_find_playTime_target; }
|
||||
|
||||
private:
|
||||
std::string m_theme; // 主题
|
||||
RoguelikeMode m_mode = RoguelikeMode::Exp; // 模式
|
||||
@@ -151,6 +160,9 @@ private:
|
||||
// ------------------ 刷开局模式 ------------------
|
||||
bool m_collectible_mode_shopping = false; // 刷开局模式下进入商店时购物
|
||||
|
||||
// ------------------ 刷常乐节点模式 ------------------
|
||||
int m_find_playTime_target = 0; // 目标常乐节点子类型 (1=令, 2=黍, 3=年)
|
||||
|
||||
private:
|
||||
// =========================== 萨米主题专用参数 ===========================
|
||||
public:
|
||||
|
||||
@@ -101,6 +101,15 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
}
|
||||
}
|
||||
|
||||
// 刷常乐节点保存路上招募券
|
||||
// 只在非开局招募时保存招募券
|
||||
if (theme == RoguelikeTheme::JieGarden && mode == RoguelikeMode::FindPlaytime && !m_initail_recruit) {
|
||||
bool ret = ProcessTask(*this, { "JieGarden@Roguelike@ReserveRecruitmentVoucher" }).run();
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_initail_recruit && m_recruit_count == 1) {
|
||||
if (m_config->get_use_support()) { // 是否使用助战干员开局
|
||||
if (recruit_support_char()) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Config/Roguelike/RoguelikeStageEncounterConfig.h"
|
||||
#include "Controller/Controller.h"
|
||||
#include "Task/ProcessTask.h"
|
||||
#include "Task/Roguelike/Map/RoguelikeBoskyPassageMap.h"
|
||||
#include "Utils/Logger.hpp"
|
||||
#include "Utils/NoWarningCV.h"
|
||||
#include "Vision/Matcher.h"
|
||||
@@ -131,6 +132,35 @@ std::optional<std::string> asst::RoguelikeStageEncounterTaskPlugin::handle_singl
|
||||
}
|
||||
}
|
||||
|
||||
// 界园树洞
|
||||
if (m_config->get_theme() == RoguelikeTheme::JieGarden) {
|
||||
auto& bosky_map = RoguelikeBoskyPassageMap::get_instance();
|
||||
if (event.name == "掷地有声") {
|
||||
bosky_map.set_node_subtype(bosky_map.get_curr_pos(), RoguelikeBoskySubNodeType::Ling);
|
||||
}
|
||||
else if (event.name == "种因得果") {
|
||||
bosky_map.set_node_subtype(bosky_map.get_curr_pos(), RoguelikeBoskySubNodeType::Shu);
|
||||
}
|
||||
else if (event.name == "三缺一") {
|
||||
bosky_map.set_node_subtype(bosky_map.get_curr_pos(), RoguelikeBoskySubNodeType::Nian);
|
||||
}
|
||||
|
||||
if (bosky_map.get_target_subtype() != RoguelikeBoskySubNodeType::Unknown) {
|
||||
if (bosky_map.get_node_subtype(bosky_map.get_curr_pos()) == bosky_map.get_target_subtype()) {
|
||||
Log.info(__FUNCTION__, "| Found target playtime node, completing task and exiting");
|
||||
|
||||
auto target_info = basic_info_with_what("RoguelikeJieGardenTargetFound");
|
||||
target_info["details"]["target_subtype"] = subtype2name(bosky_map.get_target_subtype());
|
||||
callback(AsstMsg::SubTaskExtraInfo, target_info);
|
||||
|
||||
// 完成任务,退出
|
||||
m_control_ptr->exit_then_stop();
|
||||
m_task_ptr->set_enable(false);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto click_option_task_name = [&](int item, int total) {
|
||||
if (item > total) {
|
||||
Log.warn("Event:", event.name, "Total:", total, "Choice", item, "out of range, switch to choice", total);
|
||||
|
||||
@@ -224,4 +224,35 @@ public enum RoguelikeMode
|
||||
/// 深入调查,尽可能稳定地打更多层数,不期而遇采用激进策略
|
||||
/// </summary>
|
||||
Exploration = 7,
|
||||
|
||||
/// <summary>
|
||||
/// 20001 - 刷常乐节点,第一层进洞,找不到需要的节点就重开
|
||||
/// </summary>
|
||||
FindPlaytime = 20001,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 界园常乐节点子类型
|
||||
/// </summary>
|
||||
public enum RoguelikeBoskySubNodeType
|
||||
{
|
||||
/// <summary>
|
||||
/// 未知
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 令 - 常乐 掷地有声
|
||||
/// </summary>
|
||||
Ling = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 黍 - 常乐 种因得果
|
||||
/// </summary>
|
||||
Shu = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 年 - 常乐 三缺一
|
||||
/// </summary>
|
||||
Nian = 3,
|
||||
}
|
||||
|
||||
@@ -165,6 +165,7 @@ public static class ConfigurationKeys
|
||||
public const string RoguelikeMonthlySquadAutoIterate = "Roguelike.MonthlySquadAutoIterate";
|
||||
public const string RoguelikeMonthlySquadCheckComms = "Roguelike.MonthlySquadCheckComms";
|
||||
public const string RoguelikeDeepExplorationAutoIterate = "Roguelike.DeepExplorationAutoIterate";
|
||||
public const string RoguelikeFindPlaytimeTarget = "Roguelike.FindPlaytimeTarget";
|
||||
public const string ReclamationTheme = "Reclamation.Theme";
|
||||
public const string ReclamationMode = "Reclamation.Mode";
|
||||
public const string ReclamationToolToCraft = "Reclamation.ToolToCraft";
|
||||
|
||||
@@ -189,6 +189,38 @@ public static class UiLogColor
|
||||
/// </summary>
|
||||
public const string ExplorationAbandonedIS = "ExplorationAbandonedIS";
|
||||
|
||||
// Bosky Passage Node Colors (JieGarden Theme)
|
||||
|
||||
/// <summary>
|
||||
/// The recommended color for Omissions node (拾遗).
|
||||
/// </summary>
|
||||
public const string BoskyOmissionsIS = "BoskyOmissionsIS";
|
||||
|
||||
/// <summary>
|
||||
/// The recommended color for Legend node (传说).
|
||||
/// </summary>
|
||||
public const string BoskyLegendIS = "BoskyLegendIS";
|
||||
|
||||
/// <summary>
|
||||
/// The recommended color for OldShop node (故肆).
|
||||
/// </summary>
|
||||
public const string BoskyOldShopIS = "BoskyOldShopIS";
|
||||
|
||||
/// <summary>
|
||||
/// The recommended color for Scheme node (筹谋).
|
||||
/// </summary>
|
||||
public const string BoskySchemeIS = "BoskySchemeIS";
|
||||
|
||||
/// <summary>
|
||||
/// The recommended color for Playtime node (常乐).
|
||||
/// </summary>
|
||||
public const string BoskyPlaytimeIS = "BoskyPlaytimeIS";
|
||||
|
||||
/// <summary>
|
||||
/// The recommended color for Doubts node (杂疑).
|
||||
/// </summary>
|
||||
public const string BoskyDoubtsIS = "BoskyDoubtsIS";
|
||||
|
||||
// 颜色在MaaWpfGui\Res\Themes中定义
|
||||
// Brush are defined in MaaWpfGui\Res\Themes
|
||||
}
|
||||
|
||||
@@ -179,6 +179,11 @@ public class AsstRoguelikeTask : AsstBaseTask
|
||||
/// </summary>
|
||||
public bool DeepExplorationAutoIterate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets FindPlaytime 模式的目标常乐节点子类型
|
||||
/// </summary>
|
||||
public RoguelikeBoskySubNodeType FindPlaytimeTarget { get; set; } = RoguelikeBoskySubNodeType.Ling;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 是否在五层BOSS前停下来
|
||||
/// </summary>
|
||||
@@ -253,6 +258,11 @@ public class AsstRoguelikeTask : AsstBaseTask
|
||||
taskParams["deep_exploration_auto_iterate"] = DeepExplorationAutoIterate;
|
||||
}
|
||||
|
||||
if (Mode == RoguelikeMode.FindPlaytime)
|
||||
{
|
||||
taskParams["find_playTime_target"] = (int)FindPlaytimeTarget;
|
||||
}
|
||||
|
||||
if (SamiFirstFloorFoldartal && SamiStartFloorFoldartal.Length > 0)
|
||||
{
|
||||
taskParams["first_floor_foldartal"] = SamiStartFloorFoldartal;
|
||||
|
||||
@@ -166,6 +166,12 @@ To customize rotation schedules, please use 「{key=InfrastModeCustom}」.</syst
|
||||
<system:String x:Key="RoguelikeCollectibleModeSquad">Squad to get collectibles</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyMonthlySquad">Grind Monthly Squad, clear as far as possible</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyDeepExploration">Grind Deep Investigation, clear as far as possible</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyFindPlaytime">刷常乐节点,第一层进洞,找不到需要的节点就重开</system:String>
|
||||
<system:String x:Key="RoguelikeFindPlaytimeTarget">目标常乐节点</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeLing">令 - 掷地有声</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeShu">黍 - 种因得果</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeNian">年 - 三缺一</system:String>
|
||||
<system:String x:Key="RoguelikeJieGardenTargetFound">发现目标常乐节点 {0}</system:String>
|
||||
<system:String x:Key="StartingSquad">Starting Squad</system:String>
|
||||
<system:String x:Key="StartingRoles">Starting Roles</system:String>
|
||||
<system:String x:Key="StartingCoreChar">Starting {key=Operator}</system:String>
|
||||
@@ -997,6 +1003,14 @@ Right-click to clear inactive tasks</system:String>
|
||||
<system:String x:Key="CombatDps">Node: Combat Operation</system:String>
|
||||
<system:String x:Key="EmergencyDps">Node: Emergency Operation</system:String>
|
||||
<system:String x:Key="DreadfulFoe">Node: Dreadful Foe</system:String>
|
||||
<system:String x:Key="BoskyOmissions">节点: 拾遗</system:String>
|
||||
<system:String x:Key="BoskyLegend">节点: 传说</system:String>
|
||||
<system:String x:Key="BoskyOldShop">节点: 故肆</system:String>
|
||||
<system:String x:Key="BoskyYiTrader">节点: 易与</system:String>
|
||||
<system:String x:Key="BoskyScheme">节点: 筹谋</system:String>
|
||||
<system:String x:Key="BoskyPlaytime">节点: 常乐</system:String>
|
||||
<system:String x:Key="BoskyDoubts">节点: 杂疑</system:String>
|
||||
<system:String x:Key="BoskyDisaster">节点: 灾祸</system:String>
|
||||
<system:String x:Key="RoguelikeEvent">Event: </system:String>
|
||||
<system:String x:Key="EncounterOcrError">Event Name OCR Failed</system:String>
|
||||
<system:String x:Key="FoldartalGainOcrNextLevel">Foldartal Foresee: </system:String>
|
||||
|
||||
@@ -166,6 +166,12 @@
|
||||
<system:String x:Key="RoguelikeCollectibleModeSquad">支援選択を得るモードの分隊</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyMonthlySquad">月間小隊をグラインドし、できるだけ多くの層をクリア</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyDeepExploration">深層探索をグラインドし、できるだけ多くの層をクリア</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyFindPlaytime">刷常乐节点,第一层进洞,找不到需要的节点就重开</system:String>
|
||||
<system:String x:Key="RoguelikeFindPlaytimeTarget">目标常乐节点</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeLing">令 - 掷地有声</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeShu">黍 - 种因得果</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeNian">年 - 三缺一</system:String>
|
||||
<system:String x:Key="RoguelikeJieGardenTargetFound">发现目标常乐节点 {0}</system:String>
|
||||
<system:String x:Key="StartingSquad">最初の分隊</system:String>
|
||||
<system:String x:Key="StartingRoles">最初の職業</system:String>
|
||||
<system:String x:Key="StartingCoreChar">最初の{key=Operator}</system:String>
|
||||
@@ -997,6 +1003,14 @@ C:\\leidian\\LDPlayer9
|
||||
<system:String x:Key="CombatDps">ステージ: 通常作戦</system:String>
|
||||
<system:String x:Key="EmergencyDps">ステージ: 緊急作戦</system:String>
|
||||
<system:String x:Key="DreadfulFoe">ステージ: 悪路凶敵</system:String>
|
||||
<system:String x:Key="BoskyOmissions">节点: 拾遗</system:String>
|
||||
<system:String x:Key="BoskyLegend">节点: 传说</system:String>
|
||||
<system:String x:Key="BoskyOldShop">节点: 故肆</system:String>
|
||||
<system:String x:Key="BoskyYiTrader">节点: 易与</system:String>
|
||||
<system:String x:Key="BoskyScheme">节点: 筹谋</system:String>
|
||||
<system:String x:Key="BoskyPlaytime">节点: 常乐</system:String>
|
||||
<system:String x:Key="BoskyDoubts">节点: 杂疑</system:String>
|
||||
<system:String x:Key="BoskyDisaster">节点: 灾祸</system:String>
|
||||
<system:String x:Key="RoguelikeEvent">イベント:</system:String>
|
||||
<system:String x:Key="EncounterOcrError">イベント名認識失敗</system:String>
|
||||
<system:String x:Key="FoldartalGainOcrNextLevel">予見啓示板:</system:String>
|
||||
|
||||
@@ -166,6 +166,12 @@
|
||||
<system:String x:Key="RoguelikeCollectibleModeSquad">전기주전자 획득 분대</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyMonthlySquad">월간 분대 파밍, 최대한 많은 구역 클리어</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyDeepExploration">심층 조사 파밍, 최대한 많은 구역 클리어</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyFindPlaytime">刷常乐节点,第一层进洞,找不到需要的节点就重开</system:String>
|
||||
<system:String x:Key="RoguelikeFindPlaytimeTarget">目标常乐节点</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeLing">令 - 掷地有声</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeShu">黍 - 种因得果</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeNian">年 - 三缺一</system:String>
|
||||
<system:String x:Key="RoguelikeJieGardenTargetFound">发现目标常乐节点 {0}</system:String>
|
||||
<system:String x:Key="StartingSquad">시작 분대</system:String>
|
||||
<system:String x:Key="StartingRoles">모집 조합</system:String>
|
||||
<system:String x:Key="StartingCoreChar">시작 {key=Operator}</system:String>
|
||||
@@ -997,6 +1003,14 @@ C:\\leidian\\LDPlayer9
|
||||
<system:String x:Key="CombatDps">노드: 작전</system:String>
|
||||
<system:String x:Key="EmergencyDps">노드: 긴급 작전</system:String>
|
||||
<system:String x:Key="DreadfulFoe">노드: 험난한 길</system:String>
|
||||
<system:String x:Key="BoskyOmissions">节点: 拾遗</system:String>
|
||||
<system:String x:Key="BoskyLegend">节点: 传说</system:String>
|
||||
<system:String x:Key="BoskyOldShop">节点: 故肆</system:String>
|
||||
<system:String x:Key="BoskyYiTrader">节点: 易与</system:String>
|
||||
<system:String x:Key="BoskyScheme">节点: 筹谋</system:String>
|
||||
<system:String x:Key="BoskyPlaytime">节点: 常乐</system:String>
|
||||
<system:String x:Key="BoskyDoubts">节点: 杂疑</system:String>
|
||||
<system:String x:Key="BoskyDisaster">节点: 灾祸</system:String>
|
||||
<system:String x:Key="RoguelikeEvent">이벤트:</system:String>
|
||||
<system:String x:Key="EncounterOcrError">이벤트 인식 오류</system:String>
|
||||
<system:String x:Key="FoldartalGainOcrNextLevel">암호판:</system:String>
|
||||
|
||||
@@ -166,6 +166,12 @@
|
||||
<system:String x:Key="RoguelikeCollectibleModeSquad">烧水使用分队</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyMonthlySquad">刷月度小队,尽可能稳定地打更多层数</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyDeepExploration">刷深入调查,尽可能稳定地打更多层数</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyFindPlaytime">刷常乐节点,第一层进洞,找不到需要的节点就重开</system:String>
|
||||
<system:String x:Key="RoguelikeFindPlaytimeTarget">目标常乐节点</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeLing">令 - 掷地有声</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeShu">黍 - 种因得果</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeNian">年 - 三缺一</system:String>
|
||||
<system:String x:Key="RoguelikeJieGardenTargetFound">发现目标常乐节点 {0}</system:String>
|
||||
<system:String x:Key="StartingSquad">开局分队</system:String>
|
||||
<system:String x:Key="StartingRoles">开局职业组</system:String>
|
||||
<system:String x:Key="StartingCoreChar">开局{key=Operator}</system:String>
|
||||
@@ -997,6 +1003,14 @@ C:\\leidian\\LDPlayer9。\n
|
||||
<system:String x:Key="CombatDps">关卡: 普通作战</system:String>
|
||||
<system:String x:Key="EmergencyDps">关卡: 紧急作战</system:String>
|
||||
<system:String x:Key="DreadfulFoe">关卡: 险路恶敌</system:String>
|
||||
<system:String x:Key="BoskyOmissions">节点: 拾遗</system:String>
|
||||
<system:String x:Key="BoskyLegend">节点: 传说</system:String>
|
||||
<system:String x:Key="BoskyOldShop">节点: 故肆</system:String>
|
||||
<system:String x:Key="BoskyYiTrader">节点: 易与</system:String>
|
||||
<system:String x:Key="BoskyScheme">节点: 筹谋</system:String>
|
||||
<system:String x:Key="BoskyPlaytime">节点: 常乐</system:String>
|
||||
<system:String x:Key="BoskyDoubts">节点: 杂疑</system:String>
|
||||
<system:String x:Key="BoskyDisaster">节点: 灾祸</system:String>
|
||||
<system:String x:Key="RoguelikeEvent">事件:</system:String>
|
||||
<system:String x:Key="EncounterOcrError">事件名识别错误</system:String>
|
||||
<system:String x:Key="FoldartalGainOcrNextLevel">预见:</system:String>
|
||||
|
||||
@@ -165,6 +165,12 @@
|
||||
<system:String x:Key="RoguelikeCollectibleModeSquad">燒水分隊</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyMonthlySquad">刷月度小隊,盡可能穩定地打更多層數</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyDeepExploration">刷深入調查,盡可能穩定地打更多層數</system:String>
|
||||
<system:String x:Key="RoguelikeStrategyFindPlaytime">刷常乐节点,第一层进洞,找不到需要的节点就重开</system:String>
|
||||
<system:String x:Key="RoguelikeFindPlaytimeTarget">目标常乐节点</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeLing">令 - 掷地有声</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeShu">黍 - 种因得果</system:String>
|
||||
<system:String x:Key="RoguelikePlaytimeNian">年 - 三缺一</system:String>
|
||||
<system:String x:Key="RoguelikeJieGardenTargetFound">发现目标常乐节点 {0}</system:String>
|
||||
<system:String x:Key="StartingSquad">開局分隊</system:String>
|
||||
<system:String x:Key="StartingRoles">開局職業組</system:String>
|
||||
<system:String x:Key="StartingCoreChar">開局{key=Operator}</system:String>
|
||||
@@ -995,6 +1001,14 @@ C:\\leidian\\LDPlayer9。\n
|
||||
<system:String x:Key="CombatDps">關卡: 普通作戰</system:String>
|
||||
<system:String x:Key="EmergencyDps">關卡: 緊急作戰</system:String>
|
||||
<system:String x:Key="DreadfulFoe">關卡: 險路惡敵</system:String>
|
||||
<system:String x:Key="BoskyOmissions">節點: 拾遺</system:String>
|
||||
<system:String x:Key="BoskyLegend">節點: 傳說</system:String>
|
||||
<system:String x:Key="BoskyOldShop">節點: 故肆</system:String>
|
||||
<system:String x:Key="BoskyYiTrader">節點: 易與</system:String>
|
||||
<system:String x:Key="BoskyScheme">節點: 籌謀</system:String>
|
||||
<system:String x:Key="BoskyPlaytime">節點: 常樂</system:String>
|
||||
<system:String x:Key="BoskyDoubts">節點: 雜疑</system:String>
|
||||
<system:String x:Key="BoskyDisaster">節點: 災禍</system:String>
|
||||
<system:String x:Key="RoguelikeEvent">事件:</system:String>
|
||||
<system:String x:Key="EncounterOcrError">事件名辨識錯誤</system:String>
|
||||
<system:String x:Key="FoldartalGainOcrNextLevel">預見:</system:String>
|
||||
|
||||
@@ -117,4 +117,19 @@
|
||||
<SolidColorBrush x:Key="BossIS" Color="#ab8e8e" />
|
||||
<SolidColorBrush x:Key="ExplorationAbandonedIS" Color="#9ea1a4" />
|
||||
|
||||
<!-- UiLogColor - Bosky Passage Nodes (JieGarden) -->
|
||||
<SolidColorBrush x:Key="BoskyOmissionsISRaw" Color="#98D8C8" />
|
||||
<SolidColorBrush x:Key="BoskyLegendISRaw" Color="#00CED1" />
|
||||
<SolidColorBrush x:Key="BoskyOldShopISRaw" Color="#FFB347" />
|
||||
<SolidColorBrush x:Key="BoskySchemeISRaw" Color="#9370DB" />
|
||||
<SolidColorBrush x:Key="BoskyPlaytimeISRaw" Color="#FFB6C1" />
|
||||
<SolidColorBrush x:Key="BoskyDoubtsISRaw" Color="#F0E68C" />
|
||||
|
||||
<SolidColorBrush x:Key="BoskyOmissionsIS" Color="#9ab8b0" />
|
||||
<SolidColorBrush x:Key="BoskyLegendIS" Color="#8db5b6" />
|
||||
<SolidColorBrush x:Key="BoskyOldShopIS" Color="#baaa91" />
|
||||
<SolidColorBrush x:Key="BoskySchemeIS" Color="#a79bb5" />
|
||||
<SolidColorBrush x:Key="BoskyPlaytimeIS" Color="#baabb0" />
|
||||
<SolidColorBrush x:Key="BoskyDoubtsIS" Color="#b5b09a" />
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -99,4 +99,19 @@
|
||||
<SolidColorBrush x:Key="BossIS" Color="#8a6d6d" />
|
||||
<SolidColorBrush x:Key="ExplorationAbandonedIS" Color="#7d8083" />
|
||||
|
||||
<!-- UiLogColor - Bosky Passage Nodes (JieGarden) -->
|
||||
<SolidColorBrush x:Key="BoskyOmissionsISRaw" Color="#98D8C8" />
|
||||
<SolidColorBrush x:Key="BoskyLegendISRaw" Color="#00CED1" />
|
||||
<SolidColorBrush x:Key="BoskyOldShopISRaw" Color="#FFB347" />
|
||||
<SolidColorBrush x:Key="BoskySchemeISRaw" Color="#9370DB" />
|
||||
<SolidColorBrush x:Key="BoskyPlaytimeISRaw" Color="#FFB6C1" />
|
||||
<SolidColorBrush x:Key="BoskyDoubtsISRaw" Color="#F0E68C" />
|
||||
|
||||
<SolidColorBrush x:Key="BoskyOmissionsIS" Color="#79978f" />
|
||||
<SolidColorBrush x:Key="BoskyLegendIS" Color="#6c9495" />
|
||||
<SolidColorBrush x:Key="BoskyOldShopIS" Color="#998970" />
|
||||
<SolidColorBrush x:Key="BoskySchemeIS" Color="#867a94" />
|
||||
<SolidColorBrush x:Key="BoskyPlaytimeIS" Color="#998a8f" />
|
||||
<SolidColorBrush x:Key="BoskyDoubtsIS" Color="#948f79" />
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -28,9 +28,11 @@ using MaaWpfGui.ViewModels.UI;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Mode = MaaWpfGui.Configuration.Single.MaaTask.RoguelikeMode;
|
||||
using RoguelikeBoskySubNodeType = MaaWpfGui.Configuration.Single.MaaTask.RoguelikeBoskySubNodeType;
|
||||
using Theme = MaaWpfGui.Configuration.Single.MaaTask.RoguelikeTheme;
|
||||
|
||||
namespace MaaWpfGui.ViewModels.UserControl.TaskQueue;
|
||||
|
||||
public class RoguelikeSettingsUserControlModel : TaskViewModel
|
||||
{
|
||||
static RoguelikeSettingsUserControlModel()
|
||||
@@ -111,6 +113,7 @@ public class RoguelikeSettingsUserControlModel : TaskViewModel
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikeStrategyExp"), Value = Mode.Exp },
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikeStrategyGold"), Value = Mode.Investment },
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikeStrategyLastReward"), Value = Mode.Collectible },
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikeStrategyFindPlaytime"), Value = Mode.FindPlaytime },
|
||||
];
|
||||
break;
|
||||
|
||||
@@ -918,6 +921,36 @@ public class RoguelikeSettingsUserControlModel : TaskViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private RoguelikeBoskySubNodeType _roguelikeFindPlaytimeTarget = ConfigurationHelper.GetValue(ConfigurationKeys.RoguelikeFindPlaytimeTarget, RoguelikeBoskySubNodeType.Ling);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target playtime subnode type for FindPlaytime mode.
|
||||
/// </summary>
|
||||
public RoguelikeBoskySubNodeType RoguelikeFindPlaytimeTarget
|
||||
{
|
||||
get => _roguelikeFindPlaytimeTarget;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _roguelikeFindPlaytimeTarget, value);
|
||||
ConfigurationHelper.SetValue(ConfigurationKeys.RoguelikeFindPlaytimeTarget, value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of available playtime target options for FindPlaytime mode.
|
||||
/// </summary>
|
||||
public ObservableCollection<GenericCombinedData<RoguelikeBoskySubNodeType>> RoguelikeFindPlaytimeTargetList { get; } = new()
|
||||
{
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikePlaytimeLing"), Value = RoguelikeBoskySubNodeType.Ling },
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikePlaytimeShu"), Value = RoguelikeBoskySubNodeType.Shu },
|
||||
new() { Display = LocalizationHelper.GetString("RoguelikePlaytimeNian"), Value = RoguelikeBoskySubNodeType.Nian },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the FindPlaytime target selection should be visible.
|
||||
/// </summary>
|
||||
public bool RoguelikeFindPlaytimeTargetVisible => RoguelikeMode == Mode.FindPlaytime && RoguelikeTheme == Theme.JieGarden;
|
||||
|
||||
private bool _roguelikeStopAtMaxLevel = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.RoguelikeStopAtMaxLevel, bool.FalseString));
|
||||
|
||||
/// <summary>
|
||||
@@ -1057,10 +1090,44 @@ public class RoguelikeSettingsUserControlModel : TaskViewModel
|
||||
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("RoguelikeEvent") + $" {subTaskDetails!["name"]}", UiLogColor.EventIS);
|
||||
break;
|
||||
|
||||
case "BoskyPassageNode":
|
||||
{
|
||||
var nodeType = subTaskDetails!["node_type"]?.ToString();
|
||||
var (localizedNodeType, logColor) = nodeType switch
|
||||
{
|
||||
"Omissions" => (LocalizationHelper.GetString("BoskyOmissions"), UiLogColor.BoskyOmissionsIS),
|
||||
"Legend" => (LocalizationHelper.GetString("BoskyLegend"), UiLogColor.BoskyLegendIS),
|
||||
"OldShop" => (LocalizationHelper.GetString("BoskyOldShop"), UiLogColor.BoskyOldShopIS),
|
||||
"YiTrader" => (LocalizationHelper.GetString("BoskyYiTrader"), UiLogColor.TraderIS),
|
||||
"Scheme" => (LocalizationHelper.GetString("BoskyScheme"), UiLogColor.BoskySchemeIS),
|
||||
"Playtime" => (LocalizationHelper.GetString("BoskyPlaytime"), UiLogColor.BoskyPlaytimeIS),
|
||||
"Doubts" => (LocalizationHelper.GetString("BoskyDoubts"), UiLogColor.BoskyDoubtsIS),
|
||||
"Disaster" => (LocalizationHelper.GetString("BoskyDisaster"), UiLogColor.EmergencyIS),
|
||||
_ => (nodeType ?? "Unknown", UiLogColor.EventIS),
|
||||
};
|
||||
|
||||
Instances.TaskQueueViewModel.AddLog(localizedNodeType, logColor);
|
||||
break;
|
||||
}
|
||||
|
||||
case "EncounterOcrError":
|
||||
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("EncounterOcrError"), UiLogColor.Error);
|
||||
break;
|
||||
|
||||
case "RoguelikeJieGardenTargetFound":
|
||||
{
|
||||
var targetSubtype = subTaskDetails!["target_subtype"]?.ToString();
|
||||
var localizedTarget = targetSubtype switch
|
||||
{
|
||||
"Ling" => LocalizationHelper.GetString("RoguelikePlaytimeLing"),
|
||||
"Shu" => LocalizationHelper.GetString("RoguelikePlaytimeShu"),
|
||||
"Nian" => LocalizationHelper.GetString("RoguelikePlaytimeNian"),
|
||||
_ => targetSubtype ?? "Unknown",
|
||||
};
|
||||
Instances.TaskQueueViewModel.AddLog(string.Format(LocalizationHelper.GetString("RoguelikeJieGardenTargetFound"), localizedTarget), UiLogColor.Success);
|
||||
break;
|
||||
}
|
||||
|
||||
case "FoldartalGainOcrNextLevel":
|
||||
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("FoldartalGainOcrNextLevel") + $" {subTaskDetails!["foldartal"]}");
|
||||
break;
|
||||
@@ -1139,6 +1206,9 @@ public class RoguelikeSettingsUserControlModel : TaskViewModel
|
||||
// 深入探索
|
||||
DeepExplorationAutoIterate = RoguelikeDeepExplorationAutoIterate,
|
||||
|
||||
// 刷常乐节点
|
||||
FindPlaytimeTarget = RoguelikeFindPlaytimeTarget,
|
||||
|
||||
SamiFirstFloorFoldartal = RoguelikeTheme == Theme.Sami && RoguelikeMode == Mode.Collectible && Roguelike3FirstFloorFoldartal,
|
||||
SamiStartFloorFoldartal = Roguelike3FirstFloorFoldartals,
|
||||
SamiNewSquad2StartingFoldartal = Roguelike3NewSquad2StartingFoldartal && RoguelikeSquadIsFoldartal,
|
||||
|
||||
@@ -80,6 +80,17 @@
|
||||
ItemsSource="{Binding RoguelikeRolesList}"
|
||||
SelectedValue="{Binding RoguelikeRoles}"
|
||||
SelectedValuePath="Value" />
|
||||
<hc:ComboBox
|
||||
Margin="0,5"
|
||||
d:Visibility="Visible"
|
||||
hc:InfoElement.Title="{DynamicResource RoguelikeFindPlaytimeTarget}"
|
||||
DisplayMemberPath="Display"
|
||||
IsHitTestVisible="{Binding Idle, Source={x:Static setting:TaskQueueViewModel.TaskSettingDataContext}}"
|
||||
ItemsSource="{Binding RoguelikeFindPlaytimeTargetList}"
|
||||
SelectedValue="{Binding RoguelikeFindPlaytimeTarget}"
|
||||
SelectedValuePath="Value"
|
||||
Visibility="{c:Binding 'RoguelikeMode == task:RoguelikeMode.FindPlaytime and RoguelikeTheme == task:RoguelikeTheme.JieGarden',
|
||||
Source={x:Static ui:RoguelikeSettingsUserControlModel.Instance}}" />
|
||||
<StackPanel Margin="0,5">
|
||||
<StackPanel Margin="0,0,0,5" Orientation="Horizontal">
|
||||
<controls:TextBlock Margin="8,0,0,0" Text="{DynamicResource StartingCoreChar}" />
|
||||
|
||||
Reference in New Issue
Block a user