mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-05-25 23:15:54 +08:00
@@ -42,6 +42,7 @@
|
|||||||
- 根据所给表格,结合自身情况选择每个路径组的刷新cd类型
|
- 根据所给表格,结合自身情况选择每个路径组的刷新cd类型
|
||||||
- 选择每个路径组要运行的文件夹
|
- 选择每个路径组要运行的文件夹
|
||||||
- 填写运行该组使用的配队名称(不填就不换队,有啥用啥)
|
- 填写运行该组使用的配队名称(不填就不换队,有啥用啥)
|
||||||
|
- 其余自定义配置项参考自定义配置界面的介绍
|
||||||
|
|
||||||
4. 运行脚本:
|
4. 运行脚本:
|
||||||
- 运行脚本,脚本将按照路径组的顺序依次执行任务。
|
- 运行脚本,脚本将按照路径组的顺序依次执行任务。
|
||||||
@@ -54,3 +55,10 @@
|
|||||||
- 伪造日志:伪造日志功能仅用于日志分析,不会影响脚本的实际运行。
|
- 伪造日志:伪造日志功能仅用于日志分析,不会影响脚本的实际运行。
|
||||||
- 识别到满背包的物品将会被加入拾取黑名单,同时加入自定义配置中的 **禁用的路线的关键词** ,将会跳过文件路径包含该关键词的路线,其他你希望跳过的路线也可以使用该配置来跳过,如填写钟离来跳过部分需要钟离的挖矿路线(仅文件路径中包含钟离时有效,还是建议手动删除这些路线)
|
- 识别到满背包的物品将会被加入拾取黑名单,同时加入自定义配置中的 **禁用的路线的关键词** ,将会跳过文件路径包含该关键词的路线,其他你希望跳过的路线也可以使用该配置来跳过,如填写钟离来跳过部分需要钟离的挖矿路线(仅文件路径中包含钟离时有效,还是建议手动删除这些路线)
|
||||||
- 不同配置组中的本js仅共用相同账号的拾取黑名单,路线刷新cd,路径组等其他信息并不关联,settings.json仅供配置每个配置组使用,本身变化不影响其他配置组功能
|
- 不同配置组中的本js仅共用相同账号的拾取黑名单,路线刷新cd,路径组等其他信息并不关联,settings.json仅供配置每个配置组使用,本身变化不影响其他配置组功能
|
||||||
|
|
||||||
|
六、进阶配置
|
||||||
|
-
|
||||||
|
1. 在自定义配置中勾选高级配置后展开即可查看
|
||||||
|
2. 优先采集材料:按规则填写后,每天会尝试获取指定数量的材料后再进入路径组模式,注意,需要有足够多的拾取历史或文件路径包含目标材料的路径,其余路线无法被识别。
|
||||||
|
3. 优先关键词:在路径组模式中,含有这些关键词的路线会被视为最高效率,配合效率降序排序或者最低效率配置项使用
|
||||||
|
4. 路径组临界效率:执行路径组时,分均效率低于指定值的路线会被排除,特别的,无拾取记录或记录少于3次的路线会被视为恰好处于该临界,不会被排除
|
||||||
58
repo/js/采集cd管理/assets/A00-塞洛海原(学习螃蟹技能).json
Normal file
58
repo/js/采集cd管理/assets/A00-塞洛海原(学习螃蟹技能).json
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"name": "A00-塞洛海原(学习螃蟹技能)",
|
||||||
|
"type": "collect",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "芝士贝果",
|
||||||
|
"links": "https://github.com/cheese-bagel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "1.0",
|
||||||
|
"description": "",
|
||||||
|
"map_name": "Teyvat",
|
||||||
|
"bgi_version": "0.50.0",
|
||||||
|
"tags": [],
|
||||||
|
"last_modified_time": 1755574896298,
|
||||||
|
"enable_monster_loot_split": false,
|
||||||
|
"map_match_method": ""
|
||||||
|
},
|
||||||
|
"positions": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"x": 4399.8906,
|
||||||
|
"y": 3083.4001,
|
||||||
|
"action": "",
|
||||||
|
"move_mode": "walk",
|
||||||
|
"action_params": "",
|
||||||
|
"type": "teleport"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"x": 4421.7334,
|
||||||
|
"y": 3081.2798,
|
||||||
|
"action": "combat_script",
|
||||||
|
"move_mode": "walk",
|
||||||
|
"action_params": "attack;wait(0.5);moveby(0,2285);wait(0.5);attack;click(middle)",
|
||||||
|
"type": "orientation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"x": 4413.1172,
|
||||||
|
"y": 3106.9414,
|
||||||
|
"type": "orientation",
|
||||||
|
"move_mode": "walk",
|
||||||
|
"action": "combat_script",
|
||||||
|
"action_params": "attack;wait(0.5);moveby(0,2285);wait(0.5);attack;click(middle)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"x": 4390.1826,
|
||||||
|
"y": 3099.9868,
|
||||||
|
"action": "combat_script",
|
||||||
|
"move_mode": "walk",
|
||||||
|
"action_params": "attack;wait(0.5);moveby(0,2285);wait(0.5);attack",
|
||||||
|
"type": "orientation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -19,6 +19,9 @@ const accountName = settings.infoFileName || "默认账户";
|
|||||||
// 定义目标文件夹路径和记录文件路径
|
// 定义目标文件夹路径和记录文件路径
|
||||||
const recordFolder = "record"; // 存储记录文件的文件夹路径
|
const recordFolder = "record"; // 存储记录文件的文件夹路径
|
||||||
const defaultTimeStamp = "2023-10-13T00:00:00.000Z"; // 固定的时间戳
|
const defaultTimeStamp = "2023-10-13T00:00:00.000Z"; // 固定的时间戳
|
||||||
|
let pickupRecordFile;
|
||||||
|
const MAX_PICKUP_DAYS = 30;
|
||||||
|
|
||||||
|
|
||||||
// 从 settings 中读取用户配置,并设置默认值
|
// 从 settings 中读取用户配置,并设置默认值
|
||||||
const userSettings = {
|
const userSettings = {
|
||||||
@@ -95,6 +98,8 @@ FiconRo.InitTemplate();
|
|||||||
|
|
||||||
const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150);
|
const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150);
|
||||||
|
|
||||||
|
let underWater = false;
|
||||||
|
|
||||||
(async function () {
|
(async function () {
|
||||||
/* ===== 零基构建 settings.json(BEGIN) ===== */
|
/* ===== 零基构建 settings.json(BEGIN) ===== */
|
||||||
const SETTINGS_FILE = `settings.json`;
|
const SETTINGS_FILE = `settings.json`;
|
||||||
@@ -205,6 +210,16 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
|
|
||||||
if (settings.enableMoreSettings) {
|
if (settings.enableMoreSettings) {
|
||||||
newSettings.push(
|
newSettings.push(
|
||||||
|
{
|
||||||
|
"name": "priorityItems",
|
||||||
|
"type": "input-text",
|
||||||
|
"label": "优先采集材料,每天会尝试优先采集指定数量的目标物品,随后才执行路径组\n格式:材料名*数量,由加号+连接\n如萃凝晶*160+甜甜花*10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "priorityItemsPartyName",
|
||||||
|
"type": "input-text",
|
||||||
|
"label": "优先采集材料使用的配队名称"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "priorityTags",
|
"name": "priorityTags",
|
||||||
"type": "input-text",
|
"type": "input-text",
|
||||||
@@ -221,6 +236,12 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
],
|
],
|
||||||
"default": "文件顺序,按在文件夹中位置顺序运行"
|
"default": "文件顺序,按在文件夹中位置顺序运行"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defaultEffPercentile",
|
||||||
|
"type": "input-text",
|
||||||
|
"label": "默认效率指数,范围0-1\n数值越大时,未知效率的路线被视作的默认效率越高",
|
||||||
|
"default": "0.5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "weightedRule",
|
"name": "weightedRule",
|
||||||
"type": "input-text",
|
"type": "input-text",
|
||||||
@@ -267,7 +288,7 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
newSettings.push({
|
newSettings.push({
|
||||||
"name": `pathGroup${g}thresholdEfficiency`,
|
"name": `pathGroup${g}thresholdEfficiency`,
|
||||||
"type": "input-text",
|
"type": "input-text",
|
||||||
"label": `路径组${g}临界效率\n分均拾取个数效率低于临界效率的路线会被排除\n无历史记录或历史记录少于3次的路线会被视为恰好处于临界效率`,
|
"label": `路径组${g}临界效率\n分均拾取个数效率低于临界效率的路线会被排除`,
|
||||||
"default": "0"
|
"default": "0"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -298,6 +319,7 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
// 获取子文件夹路径
|
// 获取子文件夹路径
|
||||||
const subFolderName = userSettings.infoFileName;
|
const subFolderName = userSettings.infoFileName;
|
||||||
const subFolderPath = `${recordFolder}/${subFolderName}`;
|
const subFolderPath = `${recordFolder}/${subFolderName}`;
|
||||||
|
pickupRecordFile = `${recordFolder}/${subFolderName}/拾取记录.json`;
|
||||||
|
|
||||||
// 读取子文件夹中的所有文件路径
|
// 读取子文件夹中的所有文件路径
|
||||||
const filesInSubFolder = file.ReadPathSync(subFolderPath);
|
const filesInSubFolder = file.ReadPathSync(subFolderPath);
|
||||||
@@ -327,6 +349,14 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
|
|
||||||
/* 禁用BGI原生拾取,强制模板匹配 */
|
/* 禁用BGI原生拾取,强制模板匹配 */
|
||||||
targetItems = await loadTargetItems();
|
targetItems = await loadTargetItems();
|
||||||
|
/* ===== 别名索引 ===== */
|
||||||
|
const name2Other = new Map(); // 本名 → 别名数组
|
||||||
|
const other2Name = new Map(); // 别名 → 本名
|
||||||
|
for (const it of targetItems) {
|
||||||
|
name2Other.set(it.itemName, it.otherName || []);
|
||||||
|
for (const a of (it.otherName || [])) other2Name.set(a, it.itemName);
|
||||||
|
}
|
||||||
|
|
||||||
await loadBlacklist(true);
|
await loadBlacklist(true);
|
||||||
state.running = true;
|
state.running = true;
|
||||||
|
|
||||||
@@ -408,9 +438,435 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
|
|
||||||
let cookInterval = 60 * 60 * 1000;
|
let cookInterval = 60 * 60 * 1000;
|
||||||
let settimeInterval = 10 * 60 * 1000;
|
let settimeInterval = 10 * 60 * 1000;
|
||||||
|
// ==================== 优先级材料前置采集 ====================
|
||||||
|
|
||||||
|
if (settings.priorityItems) {
|
||||||
|
/* ---------- 1. 解析 ---------- */
|
||||||
|
const priorityList = [];
|
||||||
|
const segments = settings.priorityItems.split('+').map(s => s.trim());
|
||||||
|
for (const seg of segments) {
|
||||||
|
const [itemName, countStr] = seg.split('*').map(s => s.trim());
|
||||||
|
if (itemName && countStr && !isNaN(Number(countStr))) {
|
||||||
|
priorityList.push({ itemName, count: Number(countStr) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info(`优先级材料解析完成: ${priorityList.map(e => `${e.itemName}*${e.count}`).join(', ')}`);
|
||||||
|
/* ===== 追加:扣除今日已拾取(UTC+8 0 点分界) ===== */
|
||||||
|
const utc8 = new Date(Date.now() + 8 * 3600_000); // 手动+8小时
|
||||||
|
const today = utc8.toISOString().slice(0, 10); // "YYYY-MM-DD"
|
||||||
|
let todayPicked = {}; // 今日已拾取数量
|
||||||
|
try {
|
||||||
|
const txt = await file.readText(pickupRecordFile);
|
||||||
|
if (txt) {
|
||||||
|
const arr = JSON.parse(txt);
|
||||||
|
const todayItem = arr.find(it => it.date === today);
|
||||||
|
if (todayItem) todayPicked = todayItem.items || {};
|
||||||
|
}
|
||||||
|
} catch (_) { /* 文件不存在或解析失败 */ }
|
||||||
|
|
||||||
|
/* 扣除今日已拾取:别名→本名 */
|
||||||
|
for (let i = priorityList.length - 1; i >= 0; i--) {
|
||||||
|
const task = priorityList[i];
|
||||||
|
let got = 0;
|
||||||
|
/* 先算本名 */
|
||||||
|
got += todayPicked[task.itemName] || 0;
|
||||||
|
/* 再算别名 */
|
||||||
|
const others = name2Other.get(task.itemName) || [];
|
||||||
|
for (const a of others) got += todayPicked[a] || 0;
|
||||||
|
task.count -= got;
|
||||||
|
if (task.count <= 0) priorityList.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorityList.length === 0) {
|
||||||
|
log.info("今日优先材料已达标,跳过优先采集阶段");
|
||||||
|
}
|
||||||
|
/* ================================= */
|
||||||
|
|
||||||
|
/* ---------- 2. 材料→CD类型 映射表(仅列出现过的,其余默认 1次0点刷新)---------- */
|
||||||
|
const materialCdMap = {
|
||||||
|
// 46h 特产
|
||||||
|
"小灯草": "46小时刷新",
|
||||||
|
"嘟嘟莲": "46小时刷新",
|
||||||
|
"落落莓": "46小时刷新",
|
||||||
|
"塞西莉亚花": "46小时刷新",
|
||||||
|
"慕风蘑菇": "46小时刷新",
|
||||||
|
"蒲公英籽": "46小时刷新",
|
||||||
|
"钩钩果": "46小时刷新",
|
||||||
|
"风车菊": "46小时刷新",
|
||||||
|
"霓裳花": "46小时刷新",
|
||||||
|
"清心": "46小时刷新",
|
||||||
|
"琉璃袋": "46小时刷新",
|
||||||
|
"琉璃百合": "46小时刷新",
|
||||||
|
"夜泊石": "46小时刷新",
|
||||||
|
"绝云椒椒": "46小时刷新",
|
||||||
|
"星螺": "46小时刷新",
|
||||||
|
"石珀": "46小时刷新",
|
||||||
|
"清水玉": "46小时刷新",
|
||||||
|
"海灵芝": "46小时刷新",
|
||||||
|
"鬼兜虫": "46小时刷新",
|
||||||
|
"绯樱绣球": "46小时刷新",
|
||||||
|
"鸣草": "46小时刷新",
|
||||||
|
"珊瑚真珠": "46小时刷新",
|
||||||
|
"晶化骨髓": "46小时刷新",
|
||||||
|
"血斛": "46小时刷新",
|
||||||
|
"天云草实": "46小时刷新",
|
||||||
|
"幽灯蕈": "46小时刷新",
|
||||||
|
"沙脂蛹": "46小时刷新",
|
||||||
|
"月莲": "46小时刷新",
|
||||||
|
"帕蒂沙兰": "46小时刷新",
|
||||||
|
"树王圣体菇": "46小时刷新",
|
||||||
|
"圣金虫": "46小时刷新",
|
||||||
|
"万相石": "46小时刷新",
|
||||||
|
"悼灵花": "46小时刷新",
|
||||||
|
"劫波莲": "46小时刷新",
|
||||||
|
"赤念果": "46小时刷新",
|
||||||
|
"苍晶螺": "46小时刷新",
|
||||||
|
"海露花": "46小时刷新",
|
||||||
|
"柔灯铃": "46小时刷新",
|
||||||
|
"子探测单元": "46小时刷新",
|
||||||
|
"湖光铃兰": "46小时刷新",
|
||||||
|
"幽光星星": "46小时刷新",
|
||||||
|
"虹彩蔷薇": "46小时刷新",
|
||||||
|
"初露之源": "46小时刷新",
|
||||||
|
"浪沫羽鳃": "46小时刷新",
|
||||||
|
"灼灼彩菊": "46小时刷新",
|
||||||
|
"肉龙掌": "46小时刷新",
|
||||||
|
"青蜜莓": "46小时刷新",
|
||||||
|
"枯叶紫英": "46小时刷新",
|
||||||
|
"微光角菌": "46小时刷新",
|
||||||
|
"云岩裂叶": "46小时刷新",
|
||||||
|
"琉鳞石": "46小时刷新",
|
||||||
|
"奇异的「牙齿」": "46小时刷新",
|
||||||
|
|
||||||
|
// 12h 素材
|
||||||
|
"兽肉": "12小时刷新",
|
||||||
|
"禽肉": "12小时刷新",
|
||||||
|
"神秘的肉": "12小时刷新",
|
||||||
|
"鱼肉": "12小时刷新",
|
||||||
|
"鳗肉": "12小时刷新",
|
||||||
|
"螃蟹": "12小时刷新",
|
||||||
|
"蝴蝶翅膀": "12小时刷新",
|
||||||
|
"青蛙": "12小时刷新",
|
||||||
|
"发光髓": "12小时刷新",
|
||||||
|
"蜥蜴尾巴": "12小时刷新",
|
||||||
|
"晶核": "12小时刷新",
|
||||||
|
"鳅鳅宝玉": "12小时刷新",
|
||||||
|
|
||||||
|
// 4点
|
||||||
|
"盐": "1次4点刷新",
|
||||||
|
"胡椒": "1次4点刷新",
|
||||||
|
"洋葱": "1次4点刷新",
|
||||||
|
"牛奶": "1次4点刷新",
|
||||||
|
"番茄": "1次4点刷新",
|
||||||
|
"卷心菜": "1次4点刷新",
|
||||||
|
"土豆": "1次4点刷新",
|
||||||
|
"小麦": "1次4点刷新",
|
||||||
|
"稻米": "1次4点刷新",
|
||||||
|
"虾仁": "1次4点刷新",
|
||||||
|
"豆腐": "1次4点刷新",
|
||||||
|
"杏仁": "1次4点刷新",
|
||||||
|
"发酵果实汁": "1次4点刷新",
|
||||||
|
"咖啡豆": "1次4点刷新",
|
||||||
|
"秃秃豆": "1次4点刷新",
|
||||||
|
|
||||||
|
// 0点
|
||||||
|
"甜甜花": "1次0点刷新",
|
||||||
|
"胡萝卜": "1次0点刷新",
|
||||||
|
"蘑菇": "1次0点刷新",
|
||||||
|
"松茸": "1次0点刷新",
|
||||||
|
"松果": "1次0点刷新",
|
||||||
|
"金鱼草": "1次0点刷新",
|
||||||
|
"莲蓬": "1次0点刷新",
|
||||||
|
"薄荷": "1次0点刷新",
|
||||||
|
"鸟蛋": "1次0点刷新",
|
||||||
|
"树莓": "1次0点刷新",
|
||||||
|
"白萝卜": "1次0点刷新",
|
||||||
|
"苹果": "1次0点刷新",
|
||||||
|
"日落果": "1次0点刷新",
|
||||||
|
"竹笋": "1次0点刷新",
|
||||||
|
"海草": "1次0点刷新",
|
||||||
|
"堇瓜": "1次0点刷新",
|
||||||
|
"星蕈": "1次0点刷新",
|
||||||
|
"墩墩桃": "1次0点刷新",
|
||||||
|
"须弥蔷薇": "1次0点刷新",
|
||||||
|
"香辛果": "1次0点刷新",
|
||||||
|
"枣椰": "1次0点刷新",
|
||||||
|
"泡泡桔": "1次0点刷新",
|
||||||
|
"汐藻": "1次0点刷新",
|
||||||
|
"茉洁草": "1次0点刷新",
|
||||||
|
"久雨莲": "1次0点刷新",
|
||||||
|
"沉玉仙茗": "24小时刷新",
|
||||||
|
"颗粒果": "1次0点刷新",
|
||||||
|
"烛伞蘑菇": "1次0点刷新",
|
||||||
|
"澄晶实": "1次0点刷新",
|
||||||
|
"红果果菇": "1次0点刷新",
|
||||||
|
"马尾": "1次0点刷新",
|
||||||
|
"烈焰花花蕊": "1次0点刷新",
|
||||||
|
"铁块": "1次0点刷新",
|
||||||
|
"白铁块": "2次0点刷新",
|
||||||
|
"星银矿石": "2次0点刷新",
|
||||||
|
"水晶块": "3次0点刷新",
|
||||||
|
"紫晶块": "3次0点刷新",
|
||||||
|
"萃凝晶": "3次0点刷新",
|
||||||
|
"虹滴晶": "3次0点刷新",
|
||||||
|
"苦种": "1次0点刷新",
|
||||||
|
"烬芯花": "1次0点刷新"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let changedParty = false;
|
||||||
|
|
||||||
|
/* ---------- 3. 主循环 ---------- */
|
||||||
|
while (priorityList.length > 0) {
|
||||||
|
|
||||||
|
const priorityItemSet = new Set(priorityList.map(p => p.itemName));
|
||||||
|
for (const a of priorityItemSet) {
|
||||||
|
const others = name2Other.get(a) || [];
|
||||||
|
for (const o of others) priorityItemSet.add(o); // 别名也加入
|
||||||
|
}
|
||||||
|
|
||||||
|
const pickedCounter = {};
|
||||||
|
priorityItemSet.forEach(n => pickedCounter[n] = 0);
|
||||||
|
/* ===== 每轮开始输出剩余物品 ===== */
|
||||||
|
log.info(`剩余目标材料 ${priorityList.map(t => `${t.itemName}*${t.count}`).join(', ')}`);
|
||||||
|
/* 4-1 扫描 + 读 record + 前置过滤(禁用/时间/材料相关)+ 计算效率 + CD后置排除 */
|
||||||
|
const allFiles = await readFolder('pathing', true);
|
||||||
|
const rawRecord = await file.readText(`${recordFolder}/${subFolderName}/record.json`);
|
||||||
|
let recordArray = [];
|
||||||
|
try { recordArray = JSON.parse(rawRecord); } catch { /* 空记录 */ }
|
||||||
|
const cdMap = new Map(recordArray.map(it => [it.fileName, it]));
|
||||||
|
const now = new Date();
|
||||||
|
/* 时间管制 */
|
||||||
|
if (await isTimeRestricted(settings.timeRule, 10)) { priorityList.length = 0; break; }
|
||||||
|
|
||||||
|
/* ---- 先算效率(不判CD)---- */
|
||||||
|
for (const file of allFiles) {
|
||||||
|
const fullName = file.fileName;
|
||||||
|
const rec = cdMap.get(fullName);
|
||||||
|
|
||||||
|
/* 禁用关键词 */
|
||||||
|
let skip = false;
|
||||||
|
for (const kw of disableArray) { if (file.fullPath.includes(kw)) { skip = true; break; } }
|
||||||
|
if (skip) { file._priorityEff = -1; continue; }
|
||||||
|
|
||||||
|
/* 材料相关 */
|
||||||
|
const pathHit = [...priorityItemSet].some(n => file.fullPath.includes(n));
|
||||||
|
const histHit = rec?.history?.some(log =>
|
||||||
|
Object.keys(log.items).some(name => priorityItemSet.has(name))
|
||||||
|
) ?? false;
|
||||||
|
let descHit = false;
|
||||||
|
if (file.description) {
|
||||||
|
descHit = [...priorityItemSet].some(kw => file.description.includes(kw));
|
||||||
|
}
|
||||||
|
if (!pathHit && !histHit && !descHit) {
|
||||||
|
file._priorityEff = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 计算仅看优先材料的分均效率 */
|
||||||
|
let eff = -2; // 未知标记
|
||||||
|
if (rec?.history && rec.history.length >= 3) {
|
||||||
|
const effList = rec.history.map(log => {
|
||||||
|
const total = Object.entries(log.items)
|
||||||
|
.filter(([name]) => priorityItemSet.has(name))
|
||||||
|
.reduce((sum, [, cnt]) => sum + cnt, 0);
|
||||||
|
return (total / log.durationSec) * 60;
|
||||||
|
});
|
||||||
|
eff = effList.reduce((a, b) => a + b, 0) / effList.length;
|
||||||
|
}
|
||||||
|
file._priorityEff = eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 用可运行路线算分位默认值 ---- */
|
||||||
|
const knownEff = allFiles
|
||||||
|
.filter(f => {
|
||||||
|
const rec = cdMap.get(f.fileName);
|
||||||
|
const nextCD = rec ? new Date(rec.cdTime) : new Date(0);
|
||||||
|
return f._priorityEff >= 0 && now > nextCD;
|
||||||
|
})
|
||||||
|
.map(f => f._priorityEff)
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
let defaultEff;
|
||||||
|
if (knownEff.length === 0) {
|
||||||
|
defaultEff = 1;
|
||||||
|
} else {
|
||||||
|
const rawPct = settings.defaultEffPercentile;
|
||||||
|
const pct = Math.max(0, Math.min(1, rawPct === "" ? 0.5 : Number(rawPct)));
|
||||||
|
const idx = Math.ceil(pct * knownEff.length) - 1;
|
||||||
|
defaultEff = knownEff[Math.max(0, idx)];
|
||||||
|
}
|
||||||
|
/* 回填未知 + 排除CD */
|
||||||
|
allFiles.forEach(f => {
|
||||||
|
if (f._priorityEff === -2) f._priorityEff = defaultEff;
|
||||||
|
const rec = cdMap.get(f.fileName);
|
||||||
|
const nextCD = rec ? new Date(rec.cdTime) : new Date(0);
|
||||||
|
if (now <= nextCD) f._priorityEff = -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (priorityList.length === 0) break;
|
||||||
|
|
||||||
|
/* 4-2 只跑最高效率路线 */
|
||||||
|
const candidateRoutes = allFiles.filter(f => f._priorityEff >= 0)
|
||||||
|
.sort((a, b) => b._priorityEff - a._priorityEff);
|
||||||
|
if (candidateRoutes.length === 0) {
|
||||||
|
log.info('已无可用优先路线(可能全部在CD或已达标),退出优先采集阶段');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const bestRoute = candidateRoutes[0];
|
||||||
|
const filePath = bestRoute.fullPath;
|
||||||
|
const fileName = basename(filePath).replace('.json', '');
|
||||||
|
const fullName = fileName + '.json';
|
||||||
|
const targetObj = cdMap.get(fullName);
|
||||||
|
const startTime = new Date();
|
||||||
|
|
||||||
|
if (!changedParty && settings.priorityItemsPartyName) {
|
||||||
|
await switchPartyIfNeeded(settings.priorityItemsPartyName);
|
||||||
|
changedParty = true;
|
||||||
|
}
|
||||||
|
let timeNow = new Date();
|
||||||
|
if (Foods.length != 0 && (((timeNow - lastCookTime) > cookInterval) || firstCook)) {
|
||||||
|
firstCook = false;
|
||||||
|
await ingredientProcessing();
|
||||||
|
lastCookTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.setTimeMode && settings.setTimeMode != "不调节时间" && (((timeNow - lastsettimeTime) > settimeInterval) || firstsettime)) {
|
||||||
|
firstsettime = false;
|
||||||
|
if (settings.setTimeMode === "尽量调为白天") {
|
||||||
|
await pathingScript.runFile("assets/调为白天.json");
|
||||||
|
} else {
|
||||||
|
await pathingScript.runFile("assets/调为夜晚.json");
|
||||||
|
}
|
||||||
|
lastsettimeTime = new Date();
|
||||||
|
}
|
||||||
|
await fakeLog(fileName, false, true, 0);
|
||||||
|
|
||||||
|
/* ================================= */
|
||||||
|
log.info(`当前进度:执行路线 ${fileName}`);
|
||||||
|
state.running = true;
|
||||||
|
const pickupTask = recognizeAndInteract();
|
||||||
|
if (!underWater && filePath.includes('枫丹水下')) {
|
||||||
|
await pathingScript.runFile("assets/A00-塞洛海原(学习螃蟹技能).json");
|
||||||
|
underWater = true;
|
||||||
|
}
|
||||||
|
if (underWater && !filePath.includes('枫丹水下')) {
|
||||||
|
underWater = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await pathingScript.runFile(filePath);
|
||||||
|
} catch (e) {
|
||||||
|
log.error(`优先采集路线执行失败: ${filePath}`);
|
||||||
|
state.running = false; await pickupTask; continue;
|
||||||
|
}
|
||||||
|
state.running = false; await pickupTask;
|
||||||
|
await fakeLog(fileName, false, false, 0);
|
||||||
|
/* 4-3 扣除进度:别名→本名 */
|
||||||
|
state.runPickupLog.forEach(name => {
|
||||||
|
const realName = other2Name.get(name) || name; // 别名→本名
|
||||||
|
if (priorityItemSet.has(name) || priorityItemSet.has(realName)) {
|
||||||
|
pickedCounter[realName] = (pickedCounter[realName] || 0) + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ===== 追加:立即把 pickedCounter 回写到 priorityList ===== */
|
||||||
|
for (const task of priorityList) {
|
||||||
|
const left = task.count - (pickedCounter[task.itemName] || 0);
|
||||||
|
task.count = Math.max(0, left); // 防止负数
|
||||||
|
}
|
||||||
|
/* 倒序删除已达标项 */
|
||||||
|
for (let i = priorityList.length - 1; i >= 0; i--) {
|
||||||
|
if (priorityList[i].count <= 0) {
|
||||||
|
log.info(`优先材料已达标: ${priorityList[i].itemName}`);
|
||||||
|
priorityList.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================ */
|
||||||
|
|
||||||
|
/* 4-4 计算CD(掉落材料决定)*/
|
||||||
|
const timeDiff = new Date() - startTime;
|
||||||
|
if (timeDiff > 3000) {
|
||||||
|
/* 1) 如果runPickupLog中不含优先材料,则按其他材料查找,使用最晚刷新时间 */
|
||||||
|
let hasPriority = state.runPickupLog.some(name => priorityItemSet.has(name));
|
||||||
|
let hitMaterials;
|
||||||
|
if (hasPriority) {
|
||||||
|
hitMaterials = [...new Set(state.runPickupLog.filter(n => priorityItemSet.has(n)))];
|
||||||
|
} else {
|
||||||
|
/* 非优先材料也按同一张表查CD */
|
||||||
|
hitMaterials = [...new Set(state.runPickupLog)];
|
||||||
|
}
|
||||||
|
|
||||||
|
let latestCD = new Date(0); // 初始极小值
|
||||||
|
let foundAny = false;
|
||||||
|
hitMaterials.forEach(name => {
|
||||||
|
const cdType = materialCdMap[name] || "1次0点刷新";
|
||||||
|
let tmpDate = new Date(startTime);
|
||||||
|
switch (cdType) {
|
||||||
|
case "1次0点刷新":
|
||||||
|
tmpDate.setDate(tmpDate.getDate() + 1);
|
||||||
|
tmpDate.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case "2次0点刷新":
|
||||||
|
tmpDate.setDate(tmpDate.getDate() + 2);
|
||||||
|
tmpDate.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case "3次0点刷新":
|
||||||
|
tmpDate.setDate(tmpDate.getDate() + 3);
|
||||||
|
tmpDate.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case "1次4点刷新":
|
||||||
|
tmpDate.setHours(4, 0, 0, 0);
|
||||||
|
if (tmpDate <= startTime) tmpDate.setDate(tmpDate.getDate() + 1);
|
||||||
|
break;
|
||||||
|
case "12小时刷新":
|
||||||
|
tmpDate = new Date(startTime.getTime() + 12 * 60 * 60 * 1000);
|
||||||
|
break;
|
||||||
|
case "24小时刷新":
|
||||||
|
tmpDate = new Date(startTime.getTime() + 24 * 60 * 60 * 1000);
|
||||||
|
break;
|
||||||
|
case "46小时刷新":
|
||||||
|
tmpDate = new Date(startTime.getTime() + 46 * 60 * 60 * 1000);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tmpDate.setDate(tmpDate.getDate() + 1);
|
||||||
|
tmpDate.setHours(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if (tmpDate > latestCD) latestCD = tmpDate;
|
||||||
|
foundAny = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* 兜底:没有任何材料被识别到,按1次0点刷新 */
|
||||||
|
if (!foundAny) {
|
||||||
|
latestCD = new Date(startTime);
|
||||||
|
latestCD.setDate(latestCD.getDate() + 1);
|
||||||
|
latestCD.setHours(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const durationSec = Math.round(timeDiff / 1000);
|
||||||
|
const itemCounter = {};
|
||||||
|
state.runPickupLog.forEach(n => { itemCounter[n] = (itemCounter[n] || 0) + 1; });
|
||||||
|
if (!targetObj.history) targetObj.history = [];
|
||||||
|
targetObj.history.push({ items: itemCounter, durationSec });
|
||||||
|
if (targetObj.history.length > 7) targetObj.history = targetObj.history.slice(-7);
|
||||||
|
targetObj.cdTime = latestCD.toISOString();
|
||||||
|
await file.writeText(recordFilePath,
|
||||||
|
JSON.stringify(Array.from(cdMap.values()), null, 2));
|
||||||
|
|
||||||
|
await appendDailyPickup(state.runPickupLog);
|
||||||
|
state.runPickupLog = [];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let runnedAnyPath = true;
|
||||||
// ==================== 路径组循环 ====================
|
// ==================== 路径组循环 ====================
|
||||||
|
while (runnedAnyPath) {
|
||||||
|
runnedAnyPath = false;
|
||||||
|
if (await isTimeRestricted(settings.timeRule, 10)) break;
|
||||||
for (let i = 1; i <= groupCount; i++) {
|
for (let i = 1; i <= groupCount; i++) {
|
||||||
|
if (await isTimeRestricted(settings.timeRule, 10)) break;
|
||||||
const currentCdType = settings[`pathGroup${i}CdType`] || "";
|
const currentCdType = settings[`pathGroup${i}CdType`] || "";
|
||||||
if (!currentCdType) continue;
|
if (!currentCdType) continue;
|
||||||
|
|
||||||
@@ -453,11 +909,11 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) 先计算一次基础效率,并找出全局最大效率
|
// 2) 先计算一次基础效率(未知路线先标 -1)
|
||||||
filePaths.forEach(p => {
|
filePaths.forEach(p => {
|
||||||
const fullName = basename(p);
|
const fullName = basename(p);
|
||||||
const obj = cdMap.get(fullName);
|
const obj = cdMap.get(fullName);
|
||||||
let avgEff = 0;
|
let avgEff = -1; // 先标记为“未知”
|
||||||
|
|
||||||
if (obj && obj.history && obj.history.length >= 3) {
|
if (obj && obj.history && obj.history.length >= 3) {
|
||||||
const effList = obj.history.map(log => {
|
const effList = obj.history.map(log => {
|
||||||
@@ -468,37 +924,54 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
return (total / log.durationSec) * 60;
|
return (total / log.durationSec) * 60;
|
||||||
});
|
});
|
||||||
avgEff = effList.reduce((a, b) => a + b, 0) / effList.length;
|
avgEff = effList.reduce((a, b) => a + b, 0) / effList.length;
|
||||||
} else {
|
|
||||||
const threshold = Number(settings[`pathGroup${i}thresholdEfficiency`]) || 0;
|
|
||||||
avgEff = threshold;
|
|
||||||
}
|
}
|
||||||
p._efficiency = avgEff; // 先存基础值
|
p._efficiency = avgEff; // 已知路线存真实效率,未知路线存 -1
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3) 计算全局最大效率值
|
// 3) 计算默认效率(分位值)
|
||||||
|
const knownEff = filePaths
|
||||||
|
.map(p => p._efficiency)
|
||||||
|
.filter(e => e >= 0) // 只保留已知路线
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
let defaultEff;
|
||||||
|
if (knownEff.length === 0) {
|
||||||
|
// 一条已知路线都没有 → 回退到老逻辑
|
||||||
|
defaultEff = Number(settings[`pathGroup${i}thresholdEfficiency`]) || 0;
|
||||||
|
} else {
|
||||||
|
// 按配置的分位取默认效率
|
||||||
|
const rawPct = settings.defaultEffPercentile;
|
||||||
|
const pct = Math.max(0, Math.min(1, rawPct === "" ? 0.5 : Number(rawPct)));
|
||||||
|
const idx = Math.ceil(pct * knownEff.length) - 1;
|
||||||
|
defaultEff = knownEff[Math.max(0, idx)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 把 -1 的未知路线替换成默认效率
|
||||||
|
filePaths.forEach(p => {
|
||||||
|
if (p._efficiency === -1) p._efficiency = defaultEff;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5) 计算全局最大效率值(已含默认效率)
|
||||||
const maxEff = Math.max(...filePaths.map(p => p._efficiency), 0);
|
const maxEff = Math.max(...filePaths.map(p => p._efficiency), 0);
|
||||||
|
|
||||||
// 4) 优先关键词加分
|
// 6) 优先关键词加分(逻辑不变)
|
||||||
filePaths.forEach(p => {
|
filePaths.forEach(p => {
|
||||||
const fullName = basename(p);
|
const fullName = basename(p);
|
||||||
const obj = cdMap.get(fullName);
|
const obj = cdMap.get(fullName);
|
||||||
|
|
||||||
// 4-1) 历史拾取物里是否含关键词
|
|
||||||
const itemHit = obj?.history?.some(log =>
|
const itemHit = obj?.history?.some(log =>
|
||||||
Object.keys(log.items).some(item =>
|
Object.keys(log.items).some(item =>
|
||||||
priorityKeywords.some(key => item.includes(key))
|
priorityKeywords.some(key => item.includes(key))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 4-2) 文件路径(含文件名)是否含关键词
|
|
||||||
const pathHit = priorityKeywords.some(key => p.includes(key));
|
const pathHit = priorityKeywords.some(key => p.includes(key));
|
||||||
|
const descHit = priorityKeywords.some(key => (p.description || '').includes(key));
|
||||||
|
|
||||||
if (itemHit || pathHit) {
|
if (itemHit || pathHit || descHit) {
|
||||||
p._efficiency += maxEff; // 把最大效率值直接加给它
|
p._efficiency += maxEff + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* ================== 排序分支 ================== */
|
/* ================== 排序分支 ================== */
|
||||||
switch (settings.sortMode) {
|
switch (settings.sortMode) {
|
||||||
case "优先最早刷新,将优先执行最早刷新的路线":
|
case "优先最早刷新,将优先执行最早刷新的路线":
|
||||||
@@ -597,9 +1070,16 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
|
|
||||||
state.running = true;
|
state.running = true;
|
||||||
const pickupTask = recognizeAndInteract();
|
const pickupTask = recognizeAndInteract();
|
||||||
|
runnedAnyPath = true;
|
||||||
|
|
||||||
log.info(`当前进度:路径组${i} ${folder} ${fileName} 为第 ${filePaths.indexOf(filePath) + 1}/${filePaths.length} 个`);
|
log.info(`当前进度:路径组${i} ${folder} ${fileName} 为第 ${filePaths.indexOf(filePath) + 1}/${filePaths.length} 个`);
|
||||||
|
if (!underWater && filePath.includes('枫丹水下')) {
|
||||||
|
await pathingScript.runFile("assets/A00-塞洛海原(学习螃蟹技能).json");
|
||||||
|
underWater = true;
|
||||||
|
}
|
||||||
|
if (underWater && !filePath.includes('枫丹水下')) {
|
||||||
|
underWater = false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
state.runPickupLog = []; // 新路线开始前清空
|
state.runPickupLog = []; // 新路线开始前清空
|
||||||
await pathingScript.runFile(filePath);
|
await pathingScript.runFile(filePath);
|
||||||
@@ -666,6 +1146,7 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
targetObj.cdTime = newTimestamp.toISOString();
|
targetObj.cdTime = newTimestamp.toISOString();
|
||||||
await file.writeText(recordFilePath,
|
await file.writeText(recordFilePath,
|
||||||
JSON.stringify(Array.from(cdMap.values()), null, 2));
|
JSON.stringify(Array.from(cdMap.values()), null, 2));
|
||||||
|
await appendDailyPickup(state.runPickupLog);
|
||||||
|
|
||||||
// 清空本次记录
|
// 清空本次记录
|
||||||
state.runPickupLog = [];
|
state.runPickupLog = [];
|
||||||
@@ -679,6 +1160,7 @@ const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`操作失败: ${error}`);
|
log.error(`操作失败: ${error}`);
|
||||||
@@ -910,14 +1392,18 @@ async function loadTargetItems() {
|
|||||||
it.roi.Threshold = itsThreshold;
|
it.roi.Threshold = itsThreshold;
|
||||||
it.roi.InitTemplate();
|
it.roi.InitTemplate();
|
||||||
|
|
||||||
/* ---------- 2. 解析中括号内容 ---------- */
|
/* ---------- 2. 解析中括号内容 + 纯中文过滤 ---------- */
|
||||||
const otherNames = [];
|
const otherNames = new Set(); // 用 Set 去重
|
||||||
const bracketMatch = it.fullPath.matchAll(/\[(.*?)\]/g);
|
// 2-1 中括号匹配
|
||||||
for (const m of bracketMatch) {
|
for (const m of it.fullPath.matchAll(/\[(.*?)\]/g)) {
|
||||||
if (m[1].trim()) otherNames.push(m[1].trim());
|
const pure = (m[1] || '').replace(/[^\u4e00-\u9fff]/g, '').trim();
|
||||||
|
if (pure) otherNames.add(pure);
|
||||||
}
|
}
|
||||||
it.otherName = otherNames; // 始终返回数组,无则为空数组
|
// 2-2 若 itemName 本身含非中文,也生成纯中文别名
|
||||||
|
const namePure = it.itemName.replace(/[^\u4e00-\u9fff]/g, '').trim();
|
||||||
|
if (namePure && namePure !== it.itemName) otherNames.add(namePure);
|
||||||
|
|
||||||
|
it.otherName = Array.from(otherNames); // 转回数组
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`[loadTargetItems] ${it.fullPath}: ${error.message}`);
|
log.error(`[loadTargetItems] ${it.fullPath}: ${error.message}`);
|
||||||
}
|
}
|
||||||
@@ -1052,57 +1538,55 @@ function removeJsonSuffix(fileName) {
|
|||||||
|
|
||||||
// 定义 readFolder 函数
|
// 定义 readFolder 函数
|
||||||
async function readFolder(folderPath, onlyJson) {
|
async function readFolder(folderPath, onlyJson) {
|
||||||
// 新增一个堆栈,初始时包含 folderPath
|
|
||||||
const folderStack = [folderPath];
|
const folderStack = [folderPath];
|
||||||
|
|
||||||
// 新增一个数组,用于存储文件信息对象
|
|
||||||
const files = [];
|
const files = [];
|
||||||
|
|
||||||
// 当堆栈不为空时,继续处理
|
|
||||||
while (folderStack.length > 0) {
|
while (folderStack.length > 0) {
|
||||||
// 从堆栈中弹出一个路径
|
|
||||||
const currentPath = folderStack.pop();
|
const currentPath = folderStack.pop();
|
||||||
|
const filesInSubFolder = file.ReadPathSync(currentPath); // 同步读取
|
||||||
// 读取当前路径下的所有文件和子文件夹路径
|
|
||||||
const filesInSubFolder = file.ReadPathSync(currentPath);
|
|
||||||
|
|
||||||
// 临时数组,用于存储子文件夹路径
|
|
||||||
const subFolders = [];
|
const subFolders = [];
|
||||||
|
|
||||||
for (const filePath of filesInSubFolder) {
|
for (const filePath of filesInSubFolder) {
|
||||||
if (file.IsFolder(filePath)) {
|
if (file.IsFolder(filePath)) {
|
||||||
// 如果是文件夹,先存储到临时数组中
|
|
||||||
subFolders.push(filePath);
|
subFolders.push(filePath);
|
||||||
} else {
|
|
||||||
if (filePath.endsWith(".js")) {
|
|
||||||
//跳过js结尾的文件
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 如果是文件,根据 onlyJson 判断是否存储
|
|
||||||
|
if (filePath.endsWith('.js')) continue; // 跳过 js
|
||||||
|
|
||||||
|
// 仅 json 模式
|
||||||
if (onlyJson) {
|
if (onlyJson) {
|
||||||
if (filePath.endsWith(".json")) {
|
if (!filePath.endsWith('.json')) continue;
|
||||||
const fileName = filePath.split('\\').pop(); // 提取文件名
|
|
||||||
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
|
let description = '';
|
||||||
|
try {
|
||||||
|
// 同步读文本,避免 async 传染
|
||||||
|
const txt = file.readTextSync(filePath);
|
||||||
|
const parsed = JSON.parse(txt);
|
||||||
|
description = parsed?.info?.description ?? '';
|
||||||
|
} catch {
|
||||||
|
/* 读盘或解析失败就留空串 */
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = filePath.split('\\').pop();
|
||||||
|
const folderPathArray = filePath.split('\\').slice(0, -1);
|
||||||
|
|
||||||
files.push({
|
files.push({
|
||||||
fullPath: filePath,
|
fullPath: filePath,
|
||||||
fileName: fileName,
|
fileName,
|
||||||
folderPathArray: folderPathArray
|
folderPathArray,
|
||||||
|
description
|
||||||
});
|
});
|
||||||
//log.info(`找到 JSON 文件:${filePath}`);
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const fileName = filePath.split('\\').pop(); // 提取文件名
|
const fileName = filePath.split('\\').pop();
|
||||||
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
|
const folderPathArray = filePath.split('\\').slice(0, -1);
|
||||||
files.push({
|
files.push({ fullPath: filePath, fileName, folderPathArray });
|
||||||
fullPath: filePath,
|
|
||||||
fileName: fileName,
|
|
||||||
folderPathArray: folderPathArray
|
|
||||||
});
|
|
||||||
//log.info(`找到文件:${filePath}`);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// 子文件夹按原顺序入栈(深度优先)
|
||||||
// 将临时数组中的子文件夹路径按原顺序压入堆栈
|
folderStack.push(...subFolders.reverse());
|
||||||
folderStack.push(...subFolders.reverse()); // 反转子文件夹路径
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
@@ -1193,8 +1677,6 @@ async function isTimeRestricted(timeRule, threshold = 5) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("不处于限制时间");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1474,3 +1956,46 @@ async function ingredientProcessing() {
|
|||||||
}
|
}
|
||||||
await genshin.returnMainUi();
|
await genshin.returnMainUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把本次路线的掉落合并到“拾取记录.json”中同一天条目(不含 durationSec)
|
||||||
|
* @param {string[]} pickupLog 本次路线的 state.runPickupLog
|
||||||
|
*/
|
||||||
|
async function appendDailyPickup(pickupLog) {
|
||||||
|
if (!pickupLog || !pickupLog.length) return;
|
||||||
|
|
||||||
|
let oldArr = [];
|
||||||
|
try {
|
||||||
|
const txt = await file.readText(pickupRecordFile);
|
||||||
|
if (txt) oldArr = JSON.parse(txt);
|
||||||
|
} catch (_) { /* 文件不存在或解析失败 */ }
|
||||||
|
|
||||||
|
// 统一按 UTC+8 的 0 点划分日期
|
||||||
|
const utc8 = new Date(Date.now() + 8 * 3600_000);
|
||||||
|
const today = utc8.toISOString().slice(0, 10); // "YYYY-MM-DD"
|
||||||
|
|
||||||
|
let todayItem = oldArr.find(e => e.date === today);
|
||||||
|
if (!todayItem) {
|
||||||
|
todayItem = { date: today, items: {} };
|
||||||
|
oldArr.push(todayItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
const todayItems = todayItem.items;
|
||||||
|
pickupLog.forEach(name => {
|
||||||
|
todayItems[name] = (todayItems[name] || 0) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 滑动窗口:只保留最近 MAX_PICKUP_DAYS 天
|
||||||
|
if (oldArr.length > MAX_PICKUP_DAYS) oldArr = oldArr.slice(-MAX_PICKUP_DAYS);
|
||||||
|
|
||||||
|
// 按日期倒序(最新在前)
|
||||||
|
oldArr.sort((a, b) => b.date.localeCompare(a.date));
|
||||||
|
|
||||||
|
// 写盘 + 异常捕获
|
||||||
|
try {
|
||||||
|
await file.writeText(pickupRecordFile, JSON.stringify(oldArr, null, 2), false);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`appendDailyPickup 写盘失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 1,
|
"manifest_version": 1,
|
||||||
"name": "采集cd管理",
|
"name": "采集cd管理",
|
||||||
"version": "2.4.1",
|
"version": "2.6.0",
|
||||||
"bgi_version": "0.44.8",
|
"bgi_version": "0.44.8",
|
||||||
"description": "仅面对会操作文件和读readme的用户,基于文件夹操作自动管理采集路线的cd,会按照路径组的顺序依次运行,直到指定的时间,并会按照给定的cd类型,自动跳过未刷新的路线",
|
"description": "仅面对会操作文件和读readme的用户,基于文件夹操作自动管理采集路线的cd,会按照路径组的顺序依次运行,直到指定的时间,并会按照给定的cd类型,自动跳过未刷新的路线",
|
||||||
"saved_files": [
|
"saved_files": [
|
||||||
|
|||||||
Reference in New Issue
Block a user