Compare commits
2 Commits
96259ec2ce
...
937d67df09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
937d67df09 | ||
|
|
fcba947659 |
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"time": "20250708020342",
|
"time": "20250708105647",
|
||||||
"url": "https://github.com/babalae/bettergi-scripts-list/archive/refs/heads/main.zip",
|
"url": "https://github.com/babalae/bettergi-scripts-list/archive/refs/heads/main.zip",
|
||||||
"file": "repo.json",
|
"file": "repo.json",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
@@ -68776,8 +68776,8 @@
|
|||||||
{
|
{
|
||||||
"name": "AutoHoeingOneDragon",
|
"name": "AutoHoeingOneDragon",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
"hash": "638de7201645b9d84bb244e49409ea791c45c01e",
|
"hash": "fe5bb0593eec8e9819fb47fbbd7a8bf24bb84d18",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "mno",
|
"author": "mno",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
@@ -68786,7 +68786,7 @@
|
|||||||
],
|
],
|
||||||
"description": "锄地一条龙~|~一站式解决自动化锄地,支持只拾取狗粮,请阅读README.md后使用",
|
"description": "锄地一条龙~|~一站式解决自动化锄地,支持只拾取狗粮,请阅读README.md后使用",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"lastUpdated": "2025-07-07 19:48:43"
|
"lastUpdated": "2025-07-08 10:56:30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoLeyLineOutcrop",
|
"name": "AutoLeyLineOutcrop",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
- - 小怪 :表明路线只含小怪,战斗强度低,且无需携带万叶来拾取可能掉落的狗粮,可以适当携带等级较低或不上场的角色来获取经验收益
|
- - 小怪 :表明路线只含小怪,战斗强度低,且无需携带万叶来拾取可能掉落的狗粮,可以适当携带等级较低或不上场的角色来获取经验收益
|
||||||
- - 分组逻辑:不含路径组1排除标签和任何其他组标签的路径会进入路径组1,剩余路径若含有路径组x的标签之一,则会进入路径组x
|
- - 分组逻辑:不含路径组1排除标签和任何其他组标签的路径会进入路径组1,剩余路径若含有路径组x的标签之一,则会进入路径组x
|
||||||
- - 使用示例:路径组一填写蕈兽,禁用蕈兽路线,路径组二填写次数盾,水免,处理路径组一的配队难以处理的次数盾和水免怪物,路径组三填写小怪,队伍中放升级中角色获取经验
|
- - 使用示例:路径组一填写蕈兽,禁用蕈兽路线,路径组二填写次数盾,水免,处理路径组一的配队难以处理的次数盾和水免怪物,路径组三填写小怪,队伍中放升级中角色获取经验
|
||||||
|
- **输入不运行的时间或时间段的小时数** 当你需要让js在特定的时间终止运行时,按描述填写,js会在距离目标时间小于五分钟时终止运行并等待到目标时间
|
||||||
- **路线效率计算权重:** 影响js评估路线价值,计算公式如下,权重越大越看重总收益
|
- **路线效率计算权重:** 影响js评估路线价值,计算公式如下,权重越大越看重总收益
|
||||||
- $$ 怪均^k \times 秒均 $$
|
- $$ 怪均^k \times 秒均 $$
|
||||||
- **目标数量:** 选取路线目标达到的精英怪数量,默认为400,同理小怪数量默认为2000
|
- **目标数量:** 选取路线目标达到的精英怪数量,默认为400,同理小怪数量默认为2000
|
||||||
@@ -71,5 +72,9 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
### 更新日志
|
### 更新日志
|
||||||
### 1.0(2025.07.07)
|
### 1.1.0(2025.07.08)
|
||||||
|
1. 优化日志输出
|
||||||
|
2. 新增估算剩余时间,并适配abgi
|
||||||
|
3. 新增终止运行时间的配置
|
||||||
|
### 1.0.0(2025.07.07)
|
||||||
1. 初始版本发布,支持自动锄地规划和黑白名单模式拾取。
|
1. 初始版本发布,支持自动锄地规划和黑白名单模式拾取。
|
||||||
|
|||||||
@@ -40,29 +40,24 @@ const timeMoveDown = 1200;
|
|||||||
}
|
}
|
||||||
|
|
||||||
//预处理路线并建立对象
|
//预处理路线并建立对象
|
||||||
log.info("开始预处理地图追踪文件");
|
|
||||||
pathings = await processPathings();
|
pathings = await processPathings();
|
||||||
|
|
||||||
log.info("开始合并index数据");
|
|
||||||
//优先使用index中的数据
|
//优先使用index中的数据
|
||||||
await updatePathings("assets/index1.json");
|
await updatePathings("assets/index1.json");
|
||||||
await updatePathings("assets/index2.json");
|
await updatePathings("assets/index2.json");
|
||||||
|
|
||||||
//加载路线cd信息
|
//加载路线cd信息
|
||||||
await initializeCdTime(pathings, accountName)
|
await initializeCdTime(pathings, accountName);
|
||||||
|
|
||||||
//按照用户配置标记可用路线
|
//按照用户配置标记可用路线
|
||||||
log.info("开始按照用户配置标记可用路线");
|
|
||||||
await markPathings(pathings, group1Tags, group2Tags, group3Tags, group4Tags, excludeTags);
|
await markPathings(pathings, group1Tags, group2Tags, group3Tags, group4Tags, excludeTags);
|
||||||
|
|
||||||
//找出最优组合
|
//找出最优组合
|
||||||
log.info("开始寻找最优组合");
|
|
||||||
await findBestRouteGroups(pathings, k, targetEliteNum, targetMonsterNum);
|
await findBestRouteGroups(pathings, k, targetEliteNum, targetMonsterNum);
|
||||||
|
|
||||||
//分配到不同路径组
|
//分配到不同路径组
|
||||||
log.info("开始分配到不同路径组");
|
|
||||||
groupCounts = await assignGroups(pathings, group1Tags, group2Tags, group3Tags, group4Tags);
|
groupCounts = await assignGroups(pathings, group1Tags, group2Tags, group3Tags, group4Tags);
|
||||||
/*
|
/*
|
||||||
|
//分配结果输出
|
||||||
pathings.forEach((pathing, index) => {
|
pathings.forEach((pathing, index) => {
|
||||||
log.info(`路径 ${index + 1}:`);
|
log.info(`路径 ${index + 1}:`);
|
||||||
log.info(` fullPath: ${pathing.fullPath}`);
|
log.info(` fullPath: ${pathing.fullPath}`);
|
||||||
@@ -90,7 +85,7 @@ const timeMoveDown = 1200;
|
|||||||
log.info("开始运行锄地路线");
|
log.info("开始运行锄地路线");
|
||||||
await processPathingsByGroup(pathings, targetTexts, blacklistKeywords, accountName);
|
await processPathingsByGroup(pathings, targetTexts, blacklistKeywords, accountName);
|
||||||
} else {
|
} else {
|
||||||
log.info("开始强制刷新CD");
|
log.info("强制刷新所有路线CD");
|
||||||
await initializeCdTime(pathings, "");
|
await initializeCdTime(pathings, "");
|
||||||
await updateCdTimeRecord(pathings, accountName);
|
await updateCdTimeRecord(pathings, accountName);
|
||||||
}
|
}
|
||||||
@@ -319,7 +314,7 @@ async function findBestRouteGroups(pathings, k, targetEliteNum, targetMonsterNum
|
|||||||
const minutes = Math.floor((totalTimeCombined % 3600) / 60);
|
const minutes = Math.floor((totalTimeCombined % 3600) / 60);
|
||||||
const seconds = totalTimeCombined % 60;
|
const seconds = totalTimeCombined % 60;
|
||||||
|
|
||||||
log.info(`总用时: ${hours} 时 ${minutes} 分 ${seconds.toFixed(0)} 秒`);
|
log.info(`预计总用时: ${hours} 时 ${minutes} 分 ${seconds.toFixed(0)} 秒`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function assignGroups(pathings, group1Tags, group2Tags, group3Tags, group4Tags) {
|
async function assignGroups(pathings, group1Tags, group2Tags, group3Tags, group4Tags) {
|
||||||
@@ -376,6 +371,8 @@ async function runPathWithOcr(pathFilePath, targetTexts, blacklistKeywords) {
|
|||||||
};
|
};
|
||||||
let thisMoveUpTime = 0;
|
let thisMoveUpTime = 0;
|
||||||
let lastMoveUp = 0;
|
let lastMoveUp = 0;
|
||||||
|
|
||||||
|
let lastPickupTime = new Date();
|
||||||
// 定义状态变量
|
// 定义状态变量
|
||||||
let state = { completed: false, cancelRequested: false };
|
let state = { completed: false, cancelRequested: false };
|
||||||
// 定义图像路径和目标文本列表
|
// 定义图像路径和目标文本列表
|
||||||
@@ -391,7 +388,6 @@ async function runPathWithOcr(pathFilePath, targetTexts, blacklistKeywords) {
|
|||||||
log.error(`执行路径文件时发生错误:${error.message}`);
|
log.error(`执行路径文件时发生错误:${error.message}`);
|
||||||
state.cancelRequested = true; // 修改状态变量
|
state.cancelRequested = true; // 修改状态变量
|
||||||
}
|
}
|
||||||
log.info(`路径文件 ${filePath} 执行完成`);
|
|
||||||
state.completed = true; // 修改状态变量
|
state.completed = true; // 修改状态变量
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,31 +523,14 @@ async function runPathWithOcr(pathFilePath, targetTexts, blacklistKeywords) {
|
|||||||
// 检查是否包含黑名单关键词
|
// 检查是否包含黑名单关键词
|
||||||
let containsBlacklistKeyword = blacklistKeywords.some(blacklistKeyword => ocrResult.text.includes(blacklistKeyword));
|
let containsBlacklistKeyword = blacklistKeywords.some(blacklistKeyword => ocrResult.text.includes(blacklistKeyword));
|
||||||
if (containsBlacklistKeyword) {
|
if (containsBlacklistKeyword) {
|
||||||
log.debug(`包含黑名单,不拾取: ${ocrResult.text}`);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((new Date() - lastPickupTime) > 200) {
|
||||||
log.info(`交互或拾取: "${ocrResult.text}"`);
|
log.info(`交互或拾取:"${ocrResult.text}"`);
|
||||||
// 获取当前时间并格式化为 HH:mm:ss.sss 格式
|
lastPickupTime = new Date();
|
||||||
function formatTime(date) {
|
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
||||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
||||||
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
|
|
||||||
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前时间
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
// 格式化当前时间
|
|
||||||
const formattedTime = formatTime(now);
|
|
||||||
|
|
||||||
// 输出日志
|
|
||||||
log.debug(`[${formattedTime}][INF] BetterGenshinImpact.GameTask.AutoPick.AutoPickTrigger\n交互或拾取:"${ocrResult.text}"`);
|
|
||||||
|
|
||||||
|
|
||||||
// 计算目标文本的中心Y坐标
|
// 计算目标文本的中心Y坐标
|
||||||
let centerYTargetText = ocrResult.y + ocrResult.height / 2;
|
let centerYTargetText = ocrResult.y + ocrResult.height / 2;
|
||||||
if (Math.abs(centerYTargetText - centerYF) <= texttolerance) {
|
if (Math.abs(centerYTargetText - centerYF) <= texttolerance) {
|
||||||
@@ -612,8 +591,6 @@ async function runPathWithOcr(pathFilePath, targetTexts, blacklistKeywords) {
|
|||||||
|
|
||||||
// 定义 readFolder 函数
|
// 定义 readFolder 函数
|
||||||
async function readFolder(folderPath, onlyJson) {
|
async function readFolder(folderPath, onlyJson) {
|
||||||
log.info(`开始读取文件夹:${folderPath}`);
|
|
||||||
|
|
||||||
// 新增一个堆栈,初始时包含 folderPath
|
// 新增一个堆栈,初始时包含 folderPath
|
||||||
const folderStack = [folderPath];
|
const folderStack = [folderPath];
|
||||||
|
|
||||||
@@ -735,6 +712,10 @@ async function processPathingsByGroup(pathings, targetTexts, blacklistKeywords,
|
|||||||
const seconds = totalEstimatedTime % 60;
|
const seconds = totalEstimatedTime % 60;
|
||||||
log.info(`预计用时: ${hours} 时 ${minutes} 分 ${seconds.toFixed(0)} 秒`);
|
log.info(`预计用时: ${hours} 时 ${minutes} 分 ${seconds.toFixed(0)} 秒`);
|
||||||
|
|
||||||
|
const groupStartTime = new Date();
|
||||||
|
let remainingEstimatedTime = totalEstimatedTime;
|
||||||
|
let skippedTime = 0;
|
||||||
|
|
||||||
// 遍历 pathings 数组
|
// 遍历 pathings 数组
|
||||||
for (const pathing of pathings) {
|
for (const pathing of pathings) {
|
||||||
// 检查路径是否属于指定的组
|
// 检查路径是否属于指定的组
|
||||||
@@ -743,7 +724,7 @@ async function processPathingsByGroup(pathings, targetTexts, blacklistKeywords,
|
|||||||
groupPathCount++;
|
groupPathCount++;
|
||||||
|
|
||||||
// 输出当前路径的序号信息
|
// 输出当前路径的序号信息
|
||||||
log.info(`当前路径 ${pathing.fileName} 是第 ${targetGroup} 组第 ${groupPathCount}/${totalPathsInGroup} 条`);
|
log.info(`开始处理第 ${targetGroup} 组第 ${groupPathCount}/${totalPathsInGroup} 个${pathing.fileName}`);
|
||||||
|
|
||||||
// 获取当前时间
|
// 获取当前时间
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -752,9 +733,15 @@ async function processPathingsByGroup(pathings, targetTexts, blacklistKeywords,
|
|||||||
const cdTime = new Date(pathing.cdTime);
|
const cdTime = new Date(pathing.cdTime);
|
||||||
if (cdTime > now) {
|
if (cdTime > now) {
|
||||||
log.info(`该路线未刷新,跳过。`);
|
log.info(`该路线未刷新,跳过。`);
|
||||||
|
skippedTime += pathing.t;
|
||||||
|
remainingEstimatedTime -= pathing.t;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (await isTimeRestricted(settings.timeRule)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// 输出路径已刷新并开始处理的信息
|
// 输出路径已刷新并开始处理的信息
|
||||||
log.info(`该路线已刷新,开始处理。`);
|
log.info(`该路线已刷新,开始处理。`);
|
||||||
await fakeLog(`${pathing.fileName}`, false, true, 0);
|
await fakeLog(`${pathing.fileName}`, false, true, 0);
|
||||||
@@ -781,10 +768,15 @@ async function processPathingsByGroup(pathings, targetTexts, blacklistKeywords,
|
|||||||
// 更新路径的 cdTime
|
// 更新路径的 cdTime
|
||||||
pathing.cdTime = nextEightClock.toLocaleString();
|
pathing.cdTime = nextEightClock.toLocaleString();
|
||||||
|
|
||||||
await updateCdTimeRecord(pathings, accountName)
|
await updateCdTimeRecord(pathings, accountName);
|
||||||
|
remainingEstimatedTime -= pathing.t;
|
||||||
// 输出路径的下次可用时间(本地时间格式)
|
const actualUsedTime = (new Date() - groupStartTime) / 1000;
|
||||||
log.info(`路径 ${pathing.fileName} 下次可用时间为 ${nextEightClock.toLocaleString()}`);
|
const predictRemainingTime = remainingEstimatedTime * actualUsedTime / (totalEstimatedTime - remainingEstimatedTime - skippedTime);
|
||||||
|
// 将预计剩余时间转换为时、分、秒表示
|
||||||
|
const remaininghours = Math.floor(predictRemainingTime / 3600);
|
||||||
|
const remainingminutes = Math.floor((predictRemainingTime % 3600) / 60);
|
||||||
|
const remainingseconds = predictRemainingTime % 60;
|
||||||
|
log.info(`当前进度:第 ${targetGroup} 组第 ${groupPathCount}/${totalPathsInGroup} 个 ${pathing.fileName}已完成,该组预计剩余: ${remaininghours} 时 ${remainingminutes} 分 ${remainingseconds.toFixed(0)} 秒`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -837,8 +829,6 @@ async function updateCdTimeRecord(pathings, accountName) {
|
|||||||
// 将更新后的内容写回文件
|
// 将更新后的内容写回文件
|
||||||
await file.writeText(filePath, JSON.stringify(cdTimeData, null, 2), false);
|
await file.writeText(filePath, JSON.stringify(cdTimeData, null, 2), false);
|
||||||
|
|
||||||
// 输出日志
|
|
||||||
log.info(`所有路径的 cdTime 已更新并保存到文件 ${filePath}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 捕获并记录错误
|
// 捕获并记录错误
|
||||||
log.error(`更新 cdTime 时出错: ${error.message}`);
|
log.error(`更新 cdTime 时出错: ${error.message}`);
|
||||||
@@ -990,7 +980,76 @@ async function switchPartyIfNeeded(partyName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查当前时间是否处于限制时间内或即将进入限制时间
|
||||||
|
* @param {string} timeRule - 时间规则字符串,格式如 "4, 4-6, 10-12"
|
||||||
|
* @param {number} [threshold=5] - 接近限制时间的阈值(分钟)
|
||||||
|
* @returns {Promise<boolean>} - 如果处于限制时间内或即将进入限制时间,则返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
async function isTimeRestricted(timeRule, threshold = 5) {
|
||||||
|
// 如果输入的时间规则为 undefined 或空字符串,视为不进行时间处理,返回 false
|
||||||
|
if (timeRule === undefined || timeRule === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化 0-23 小时为可用状态
|
||||||
|
const hours = Array(24).fill(false);
|
||||||
|
|
||||||
|
// 解析时间规则
|
||||||
|
const rules = timeRule.split(',').map(rule => rule.trim());
|
||||||
|
|
||||||
|
// 校验输入的字符串是否符合规则
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (rule.includes('-')) {
|
||||||
|
// 处理时间段,如 "4-6"
|
||||||
|
const [startHour, endHour] = rule.split('-').map(Number);
|
||||||
|
if (isNaN(startHour) || isNaN(endHour) || startHour < 0 || startHour >= 24 || endHour <= startHour || endHour > 24) {
|
||||||
|
// 如果时间段格式不正确或超出范围,则报错并返回 true
|
||||||
|
log.error("时间填写不符合规则,请检查");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (let i = startHour; i < endHour; i++) {
|
||||||
|
hours[i] = true; // 标记为不可用
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 处理单个时间点,如 "4"
|
||||||
|
const hour = Number(rule);
|
||||||
|
if (isNaN(hour) || hour < 0 || hour >= 24) {
|
||||||
|
// 如果时间点格式不正确或超出范围,则报错并返回 true
|
||||||
|
log.error("时间填写不符合规则,请检查");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
hours[hour] = true; // 标记为不可用
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前时间
|
||||||
|
const now = new Date();
|
||||||
|
const currentHour = now.getHours();
|
||||||
|
const currentMinute = now.getMinutes();
|
||||||
|
|
||||||
|
// 检查当前时间是否处于限制时间内
|
||||||
|
if (hours[currentHour]) {
|
||||||
|
log.warn("处于限制时间内");
|
||||||
|
return true; // 当前时间处于限制时间内
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前时间是否即将进入限制时间
|
||||||
|
for (let i = 0; i < 24; i++) {
|
||||||
|
if (hours[i]) {
|
||||||
|
const nextHour = i;
|
||||||
|
const timeUntilNextHour = (nextHour - currentHour - 1) * 60 + (60 - currentMinute);
|
||||||
|
if (timeUntilNextHour > 0 && timeUntilNextHour <= threshold) {
|
||||||
|
// 如果距离下一个限制时间小于等于阈值,则等待到限制时间开始
|
||||||
|
log.warn("接近限制时间,开始等待");
|
||||||
|
await sleep(timeUntilNextHour * 60 * 1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("不处于限制时间");
|
||||||
|
return false; // 当前时间不在限制时间内
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 1,
|
"manifest_version": 1,
|
||||||
"name": "锄地一条龙",
|
"name": "锄地一条龙",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "一站式解决自动化锄地,支持只拾取狗粮,请阅读README.md后使用",
|
"description": "一站式解决自动化锄地,支持只拾取狗粮,请阅读README.md后使用",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,6 +59,11 @@
|
|||||||
"type": "input-text",
|
"type": "input-text",
|
||||||
"label": "路径组四要【选择】的标签"
|
"label": "路径组四要【选择】的标签"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "timeRule",
|
||||||
|
"type": "input-text",
|
||||||
|
"label": "输入不运行的时间或时间段的小时数,以本地时间为准\n将提前5分钟结束路线运行,并等待到对应时间\n允许填入【单个数字】如4表示小时数为4时不运行\n或【时间段】如4-6表示小时数为4-6时都不运行\n允许多项,由【中文逗号】分隔"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "efficiencyIndex",
|
"name": "efficiencyIndex",
|
||||||
"type": "input-text",
|
"type": "input-text",
|
||||||
|
|||||||
Reference in New Issue
Block a user