Files
bettergi-scripts-list/repo/js/自动小怪锄地规划/main.js
Patrick-Ze 10949c38d9 js: 调整fakeLog的日志级别为debug,移除CD-Aware-AutoGather的本地图片 (#1009)
* js: 调整`fakeLog`的日志级别为`debug`以精简遮罩窗口输出

* CD-Aware-AutoGather: README改用在线图片
2025-06-06 23:08:51 +08:00

356 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// fakeLog 函数,使用方法:将本函数放在主函数前,调用时请务必使用await否则可能出现v8白框报错
//在js开头处伪造该js结束运行的日志信息如 await fakeLog("js脚本", true, true, 0);
//在js结尾处伪造该js开始运行的日志信息如 await fakeLog("js脚本", true, false, 2333);(可以不加)
//duration项目仅在伪造结束信息时有效且无实际作用可以任意填写当你需要在日志中输出特定值时才需要单位为毫秒
//在调用地图追踪前伪造该地图追踪开始运行的日志信息,如 await fakeLog(`地图追踪.json`, false, true, 0);
//在调用地图追踪后伪造该地图追踪结束运行的日志信息,如 await fakeLog(`地图追踪.json`, false, false, 0);
//如此便可以在js运行过程中伪造地图追踪的日志信息可以在日志分析等中查看
async function fakeLog(name, isJs, isStart, duration) {
await sleep(10);
const currentTime = Date.now();
// 参数检查
if (typeof name !== 'string') {
log.error("参数 'name' 必须是字符串类型!");
return;
}
if (typeof isJs !== 'boolean') {
log.error("参数 'isJs' 必须是布尔型!");
return;
}
if (typeof isStart !== 'boolean') {
log.error("参数 'isStart' 必须是布尔型!");
return;
}
if (typeof currentTime !== 'number' || !Number.isInteger(currentTime)) {
log.error("参数 'currentTime' 必须是整数!");
return;
}
if (typeof duration !== 'number' || !Number.isInteger(duration)) {
log.error("参数 'duration' 必须是整数!");
return;
}
// 将 currentTime 转换为 Date 对象并格式化为 HH:mm:ss.sss
const date = new Date(currentTime);
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');
const formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`;
// 将 duration 转换为分钟和秒,并保留三位小数
const durationInSeconds = duration / 1000; // 转换为秒
const durationMinutes = Math.floor(durationInSeconds / 60);
const durationSeconds = (durationInSeconds % 60).toFixed(3); // 保留三位小数
// 使用四个独立的 if 语句处理四种情况
if (isJs && isStart) {
// 处理 isJs = true 且 isStart = true 的情况
const logMessage = `正在伪造js开始的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 开始执行JS脚本: "${name}"`;
log.debug(logMessage);
}
if (isJs && !isStart) {
// 处理 isJs = true 且 isStart = false 的情况
const logMessage = `正在伪造js结束的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}${durationSeconds}\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------`;
log.debug(logMessage);
}
if (!isJs && isStart) {
// 处理 isJs = false 且 isStart = true 的情况
const logMessage = `正在伪造地图追踪开始的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 开始执行地图追踪任务: "${name}"`;
log.debug(logMessage);
}
if (!isJs && !isStart) {
// 处理 isJs = false 且 isStart = false 的情况
const logMessage = `正在伪造地图追踪结束的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}${durationSeconds}\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------`;
log.debug(logMessage);
}
}
// 定义自定义函数 basename用于获取文件名
function basename(filePath) {
const lastSlashIndex = filePath.lastIndexOf('\\'); // 或者使用 '/',取决于你的路径分隔符
return filePath.substring(lastSlashIndex + 1);
}
(async function () {
// 初始化所有用到的参数
const operationMode = settings.operationMode || "执行路径文件"; // 默认值为“执行路径文件”
const requiredMonsterCount = parseInt(settings.requiredMonsterCount || 1750, 10); // 默认值为1750
const excludeWaterFree = !!settings.excludeWaterFree; // 默认值为false默认不排除
const excludeHighRisk = !!settings.excludeHighRisk; // 默认值为false默认不排除
const weight = parseFloat(settings.weight || 2); // 默认值为2
const disableAutoPick = !!settings.disableAutoPick; // 默认值为false默认不禁用
const disableCooldownCheck = !!settings.disableCooldownCheck; // 默认值为false默认不禁用CD检测
const accountName = settings.accountName || "一个账户名"; // 新增账户名,默认值为“默认账户”
const pathingDir = 'pathing/'; // 初始化pathingDir参数
const enableCooldownCheck = !disableCooldownCheck; // 如果 disableCooldownCheck 未设置或为 false则 enableCooldownCheck 为 true
// 初始化全局排除关键词配置
let globalExclusionKeywords = settings.globalExclusionKeywords || "不跑"; // 默认为不跑
const exclusionKeywordsArray = globalExclusionKeywords.split("").map(keyword => keyword.trim()); // 使用中文分号分隔,并去除空格
// 初始化用于计数的变量
let outputFolderName = accountName; // 初始化为空
let totalMonsterCount = 0; // 筛选出的怪物总数
let selectedRoutes = []; // 筛选出的路线数组
let successCount = 0; // 成功复制的文件数量
let failCount = 0; // 失败复制的文件数量
let routeIndex = 0; // 当前筛选的路线序号
// 日志输出配置参数
log.info(
`自动小怪锄地规划 - 配置信息:` +
`操作模式=${operationMode}(默认:生成路径文件),` +
`怪物数量=${requiredMonsterCount}默认1750` +
`${excludeWaterFree ? '排除水免(默认:不排除)' : '包含水免(默认)'}` +
`${excludeHighRisk ? '排除高危(默认:不排除)' : '包含高危(默认)'}` +
`权重=${weight}默认2` +
`自动拾取=${disableAutoPick ? '禁用' : '启用'}` +
`CD检测=${disableCooldownCheck ? '禁用' : '启用'}` +
`账户名=${accountName}`
);
// 在日志中输出全局排除关键词配置信息
log.info(`全局排除关键词=${globalExclusionKeywords ? exclusionKeywordsArray.join("") : "无"}`);
// 验证配置参数
if (
isNaN(requiredMonsterCount) ||
requiredMonsterCount < 0 ||
!Number.isInteger(requiredMonsterCount)
) {
log.warn(`怪物数量 ${requiredMonsterCount} 不符合要求必须为0以上的整数`);
return;
}
if (isNaN(weight) || weight < 0) {
log.warn(`权重 ${weight} 不符合要求必须为0及以上的数`);
return;
}
// 检查是否所有配置都是默认状态
const isDefaultConfig = (
operationMode === "执行路径文件" &&
requiredMonsterCount === 1750 &&
!excludeWaterFree &&
!excludeHighRisk &&
weight === 2 &&
!disableAutoPick &&
enableCooldownCheck &&
accountName === "一个账户名" &&
globalExclusionKeywords === "不跑"
);
if (isDefaultConfig) {
log.warn(`你没有修改自定义配置请在配置组界面中右键本js以修改自定义配置`);
await sleep(5000);
}
// 根据自定义配置,如果没有禁用自动拾取,则启用自动拾取
if (!disableAutoPick) {
log.info("启用自动拾取的实时任务");
dispatcher.addTimer(new RealtimeTimer("AutoPick"));
}
const routeDir = 'route/'; // 文件夹路径
// 读取routeDir文件夹中的所有文件路径
const filesInRouteDir = file.ReadPathSync(routeDir);
// 检查路径组文件是否存在
let doGenerate = true;
for (const filePath of filesInRouteDir) {
const fileName = basename(filePath); // 提取文件名
if (fileName === `${outputFolderName}.txt`) {
doGenerate = false;
break;
}
}
if (doGenerate) {
log.warn('路径组文件不存在,尝试重新生成');
await sleep(5000);
}
if (operationMode !== "执行路径文件" || doGenerate) {
// 筛选、排序并选取路线
const indexPath = `index.txt`;
try {
const indexContent = await file.readText(indexPath);
const pathingFiles = indexContent.trim().split('\n').map(line => {
const parts = line.trim().split('\t');
return {
name: parts[0],
monsterCount: parseFloat(parts[1]),
secPerMonster: parseFloat(parts[2]),
monsterPerSec: parseFloat(parts[3]),
isWaterFree: parseInt(parts[4], 10) === 1,
isHighRisk: parseInt(parts[5], 10) === 1,
efficiency: weight >= 5 ? parseFloat(parts[2]) : Math.pow(parseFloat(parts[2]), weight) * parseFloat(parts[3]),
selected: 0
};
}).filter(route => !isNaN(route.monsterCount) && route.monsterCount > 0 && Number.isInteger(route.monsterCount));
const sortedPathingFiles = [...pathingFiles];
sortedPathingFiles.sort((a, b) => b.efficiency - a.efficiency || a.monsterCount - b.monsterCount);
for (const route of sortedPathingFiles) {
routeIndex++; // 每次循环时递增
const meetsKeywordCondition = exclusionKeywordsArray.every(keyword => !route.name.toLowerCase().includes(keyword));
const meetsWaterFreeCondition = !excludeWaterFree || !route.isWaterFree;
const meetsHighRiskCondition = !excludeHighRisk || !route.isHighRisk;
// 修改后的日志输出,增加当前路线的序号
log.debug(`筛选路线 ${routeIndex}${route.name}):关键词条件 ${meetsKeywordCondition},水免条件 ${meetsWaterFreeCondition},高危条件 ${meetsHighRiskCondition}`);
if (meetsKeywordCondition && meetsWaterFreeCondition && meetsHighRiskCondition) {
totalMonsterCount += route.monsterCount;
route.selected = 1;
selectedRoutes.push(route.name);
if (totalMonsterCount >= requiredMonsterCount) break;
}
}
if (totalMonsterCount < requiredMonsterCount) {
log.warn(`数量不足,最多可以包含 ${totalMonsterCount} 只怪物,不满足所需的 ${requiredMonsterCount} 只怪物`);
}
// 根据操作模式执行相应的操作
if (operationMode === "强制刷新路径文件" || doGenerate) {
// 使用 outputFolderName 作为路径组文件的名称,并添加 .txt 扩展名
const pathGroupName = `${outputFolderName}.txt`;
const pathGroupFilePath = `route/${pathGroupName}`;
// 初始化CD信息的时间戳
const initialCDTimestamp = "::2000-01-01T00:00:00.000Z";
// 按照 index.txt 中的顺序依次检验每个路线是否需要写入文件
const resultContent = pathingFiles
.filter(route => selectedRoutes.includes(route.name))
.map(route => `${route.name}${initialCDTimestamp}`)
.join('\n');
await file.writeText(pathGroupFilePath, resultContent);
log.info(`生成成功,共计 ${selectedRoutes.length} 条路线,路径组文件已保存到 ${pathGroupFilePath}`);
} else if (operationMode === "输出地图追踪文件") {
const pathingOutDir = `pathingout/${outputFolderName}/`; // 输出文件夹路径
// 将选中的地图追踪文件复制到对应的输出文件夹
for (const routeName of selectedRoutes) {
const sourceFilePath = `${pathingDir}${routeName}.json`;
const targetFilePath = `${pathingOutDir}${routeName}.json`;
try {
const fileContent = await file.readText(sourceFilePath);
await file.writeText(targetFilePath, fileContent);
// log.debug(`文件 ${routeName}.json 已复制到 ${pathingOutDir}`);
successCount++;
} catch (error) {
log.warn(`复制文件 ${routeName}.json 时出错: ${error}`);
failCount++;
}
}
log.info(`筛选完成,共计 ${selectedRoutes.length} 条路线。`);
log.info(`文件复制完成:成功 ${successCount} 个,失败 ${failCount} 个。`);
}
} catch (error) {
log.error(`读取路径文件索引文件 ${indexPath} 时出错: ${error}`);
}
}
// 判断操作模式是否为“执行路径文件”,若是则执行路径文件
if (operationMode === "执行路径文件") {
try {
//伪造js已经结束的记录
await fakeLog("自动小怪锄地规划", true, false, 1);
// 定义路径组文件的路径,使用 outputFolderName
const pathGroupFilePath = `route/${outputFolderName}.txt`;
const pathGroupContent = await file.readText(pathGroupFilePath);
const savedRoutes = pathGroupContent.trim().split('\n');
for (let i = 0; i < savedRoutes.length; i++) {
const routeWithTimestamp = savedRoutes[i].trim();
const [routeName, routeTimestamp] = routeWithTimestamp.split('::');
log.info(`当前进度:${routeName}为第 ${i + 1}/${savedRoutes.length}`);
const now = new Date(); // 获取开始时间
const startTime = now.toISOString();
if (enableCooldownCheck && startTime < routeTimestamp) {
log.info(`当前路线 ${routeName} 未刷新,跳过任务`);
await sleep(10);
continue; // 跳过当前循环
}
log.info(`当前路线 ${routeName} 已刷新或未启用CD检测执行任务`);
// 拼接路径文件的完整路径
const pathingFilePath = `pathing/${routeName}.json`;
//伪造地图追踪开始的日志记录
await fakeLog(routeName + ".json", false, true, 0); // 开始时 duration 通常为 0
// 执行路径文件
await pathingScript.runFile(pathingFilePath);
const endTime = new Date(); // 获取结束时间
const timeDiff = endTime - now; // 计算时间差(单位:毫秒)
//伪造地图追踪结束的日志记录
await fakeLog(routeName + ".json", false, false, timeDiff);
//尝试捕获任务取消的错误,捕获后终止循环
try {
await sleep(10);
} catch (error) {
log.error(`发生错误: ${error}`);
return; // 终止循环
}
// 如果启用了CD检测
if (enableCooldownCheck) {
if (timeDiff > 3000) { // 如果时间差大于3秒3000毫秒
// 计算新的时间戳增加12小时
const newTimestamp = new Date(startTime).getTime() + 12 * 60 * 60 * 1000;
const formattedNewTimestamp = new Date(newTimestamp).toISOString();
const nextAvailableTime = new Date(formattedNewTimestamp).toLocaleString(); // 转换为本地时间格式
log.info(`任务 ${routeName} 运行超过10秒且当前启用刷新CD检测下一次可用时间为 ${nextAvailableTime}`);
log.info(`新的时间戳为:${formattedNewTimestamp}`);
// 更新 savedRoutes 中对应部分的时间戳
savedRoutes[i] = `${routeName}::${formattedNewTimestamp}`;
// 立即将更新后的内容写回文件
const updatedContent = savedRoutes.join('\n');
await file.writeText(pathGroupFilePath, updatedContent);
}
}
}
} catch (error) {
log.error(`读取或写入路径组文件时出错: ${error}`);
}
//伪造一个js开始的记录实际上没必要
await fakeLog("自动小怪锄地规划", true, true, 0);
}
})();