diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/水晶块.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/水晶块.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/水晶块.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/水晶块.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/紫晶块.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/紫晶块.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/紫晶块.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/紫晶块.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/萃凝晶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/萃凝晶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/萃凝晶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/萃凝晶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/虹滴晶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/虹滴晶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/虹滴晶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/A矿/[锻造矿]/虹滴晶.png diff --git a/repo/js/采集cd管理/main.js b/repo/js/采集cd管理/main.js index 513b753d0..7e69bf647 100644 --- a/repo/js/采集cd管理/main.js +++ b/repo/js/采集cd管理/main.js @@ -1,4 +1,3 @@ - /* ===== 强制模板匹配拾取(BEGIN) ===== */ let targetItems = []; let blacklist = []; @@ -23,7 +22,6 @@ const defaultTimeStamp = "2023-10-13T00:00:00.000Z"; // 固定的时间戳 let pickupRecordFile; const MAX_PICKUP_DAYS = 30; - // 从 settings 中读取用户配置,并设置默认值 const userSettings = { operationMode: settings.operationMode || "执行任务(若不存在索引文件则自动创建)", @@ -352,10 +350,14 @@ let underWater = false; targetItems = await loadTargetItems(); /* ===== 别名索引 ===== */ const name2Other = new Map(); // 本名 → 别名数组 - const other2Name = new Map(); // 别名 → 本名 + const alias2Names = new Map(); // 别名 → 本名数组(支持多对一) for (const it of targetItems) { - name2Other.set(it.itemName, it.otherName || []); - for (const a of (it.otherName || [])) other2Name.set(a, it.itemName); + const aliases = it.otherName || []; + name2Other.set(it.itemName, aliases); + for (const a of aliases) { + if (!alias2Names.has(a)) alias2Names.set(a, []); + alias2Names.get(a).push(it.itemName); // 一个别名可指向多个本名 + } } await loadBlacklist(true); @@ -422,7 +424,6 @@ let underWater = false; log.error(`写入文件失败: ${recordFilePath}`); } - if (typeof ingredientProcessingFood === 'string' && ingredientProcessingFood.trim()) { Foods = ingredientProcessingFood .split(/[,,;;\s]+/) // 支持中英文逗号、分号、空格 @@ -465,15 +466,23 @@ let underWater = false; } } catch (_) { /* 文件不存在或解析失败 */ } - /* 扣除今日已拾取:别名→本名 */ + /* 扣除今日已拾取:双向扣除 */ for (let i = priorityList.length - 1; i >= 0; i--) { const task = priorityList[i]; let got = 0; - /* 先算本名 */ + + /* 1. 字面名(可能是别名)直接扣 */ got += todayPicked[task.itemName] || 0; - /* 再算别名 */ + + /* 2. 如果字面名是别名,把对应本名也扣一遍 */ + const realNames = alias2Names.get(task.itemName) || []; // 现在是数组 + + for (const n of realNames) got += todayPicked[n] || 0; + + /* 3. 如果字面名是本名,把所有别名再扣一遍 */ 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); } @@ -617,16 +626,24 @@ let underWater = false; /* ---------- 3. 主循环 ---------- */ while (priorityList.length > 0) { + /* 1. 先把用户填的字面名(可能是别名)全部弄进来 */ const priorityItemSet = new Set(priorityList.map(p => p.itemName)); - for (const a of priorityItemSet) { + + /* 2. 双向扩:本名↔别名 */ + for (const a of [...priorityItemSet]) { // 复制一份避免遍历过程中增长 + // 2.1 如果 a 是“本名”,把它的所有别名加进来(原来就有的逻辑) const others = name2Other.get(a) || []; - for (const o of others) priorityItemSet.add(o); // 别名也加入 + for (const o of others) priorityItemSet.add(o); + + // 2.2 如果 a 是“别名”,把对应的本名加进来(新增反向) + const realName = alias2Names.get(a) || []; + for (const r of realName) priorityItemSet.add(r); } const pickedCounter = {}; priorityItemSet.forEach(n => pickedCounter[n] = 0); /* ===== 每轮开始输出剩余物品 ===== */ - log.info(`剩余目标材料 ${priorityList.map(t => `${t.itemName}*${t.count}`).join(', ')}`); + 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`); @@ -720,7 +737,7 @@ let underWater = false; /* ---------- 智能选队:按路线所在文件夹反查路径组 ---------- */ { const fullPath = bestRoute.fullPath; // 例:pathing/须弥/xxx.json - const folderName = fullPath.split(/\\|\//).slice(-2, -1)[0]; // 倒数第二层文件夹名 + const folderName = fullPath.split(/\\|\//)[1]; // 索引 1 就是第二层 let targetParty = ''; // 最终要用的队伍名 const groupCount = Math.min(99, Math.max(1, parseInt(settings.groupCount || '3'))); @@ -742,6 +759,7 @@ let underWater = false; firstCook = false; await ingredientProcessing(); lastCookTime = new Date(); + underWater = false; } if (settings.setTimeMode && settings.setTimeMode != "不调节时间" && (((timeNow - lastsettimeTime) > settimeInterval) || firstsettime)) { @@ -774,19 +792,34 @@ let underWater = false; } 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; + /* 就地展开:别名→本名数组,再把所有相关名称都计数 */ + const realNames = alias2Names.get(name) || [name]; // 可能是多个本名 + for (const rn of realNames) { + if (priorityItemSet.has(name) || priorityItemSet.has(rn)) { + pickedCounter[rn] = (pickedCounter[rn] || 0) + 1; + } } }); - /* ===== 追加:立即把 pickedCounter 回写到 priorityList ===== */ + /* ===== 追加:立即把 pickedCounter 回写到 priorityList(双向扣减)===== */ for (const task of priorityList) { - const left = task.count - (pickedCounter[task.itemName] || 0); - task.count = Math.max(0, left); // 防止负数 + let picked = 0; + + /* 1. 字面名(可能是别名)直接扣 */ + picked += pickedCounter[task.itemName] || 0; + + /* 2. 别名→本名反向扣(多对一) */ + const realNames = alias2Names.get(task.itemName) || []; + for (const rn of realNames) picked += pickedCounter[rn] || 0; + + /* 3. 本名→别名顺向扣 */ + const others = name2Other.get(task.itemName) || []; + for (const a of others) picked += pickedCounter[a] || 0; + + task.count = Math.max(0, task.count - picked); } + /* 倒序删除已达标项 */ for (let i = priorityList.length - 1; i >= 0; i--) { if (priorityList[i].count <= 0) { @@ -884,7 +917,7 @@ let underWater = false; if (!currentCdType) continue; const folder = folderNames[i - 1] || `路径组${i}`; - const targetFolder = `pathing/${folder}`; + const targetFolder = `pathing/${folder} `; /* 运行期同样用 Map 只改 cdTime */ const rawRecord = await file.readText(recordFilePath); @@ -1041,6 +1074,7 @@ let underWater = false; firstCook = false; await ingredientProcessing(); lastCookTime = new Date(); + underWater = false; } if (settings.setTimeMode && settings.setTimeMode != "不调节时间" && (((timeNow - lastsettimeTime) > settimeInterval) || firstsettime)) { @@ -1403,17 +1437,20 @@ async function loadTargetItems() { it.roi.InitTemplate(); /* ---------- 2. 解析中括号内容 + 纯中文过滤 ---------- */ - const otherNames = new Set(); // 用 Set 去重 - // 2-1 中括号匹配 + const otherNames = new Set(); + + // 一次性扫描完整路径里的所有 [] for (const m of it.fullPath.matchAll(/\[(.*?)\]/g)) { const pure = (m[1] || '').replace(/[^\u4e00-\u9fff]/g, '').trim(); if (pure) otherNames.add(pure); } - // 2-2 若 itemName 本身含非中文,也生成纯中文别名 + + // 若 itemName 本身含非中文,也生成纯中文别名 const namePure = it.itemName.replace(/[^\u4e00-\u9fff]/g, '').trim(); if (namePure && namePure !== it.itemName) otherNames.add(namePure); - it.otherName = Array.from(otherNames); // 转回数组 + it.otherName = Array.from(otherNames); + } catch (error) { log.error(`[loadTargetItems] ${it.fullPath}: ${error.message}`); } @@ -2027,4 +2064,3 @@ async function appendDailyPickup(pickupLog) { log.error(`appendDailyPickup 写盘失败: ${error.message}`); } } - diff --git a/repo/js/采集cd管理/manifest.json b/repo/js/采集cd管理/manifest.json index ec55fa8ff..c55c30bb3 100644 --- a/repo/js/采集cd管理/manifest.json +++ b/repo/js/采集cd管理/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "采集cd管理", - "version": "2.6.1", + "version": "2.6.3", "bgi_version": "0.44.8", "description": "仅面对会操作文件和读readme的用户,基于文件夹操作自动管理采集路线的cd,会按照路径组的顺序依次运行,直到指定的时间,并会按照给定的cd类型,自动跳过未刷新的路线", "saved_files": [