rename JS

This commit is contained in:
起个名字好难
2025-05-08 01:32:15 +08:00
parent 7a9f3296bb
commit be4166d2cd
9 changed files with 1873 additions and 1873 deletions

View File

@@ -0,0 +1,411 @@
(async function () {
// 初始化游戏窗口大小和返回主界面
setGameMetrics(1920, 1080, 1);
await genshin.returnMainUi();
// 获取角色、元素、武器等设置信息
const Character = settings.Character || "纳西妲"; // 默认角色
const Element = settings.Element || "物"; // 默认元素
const Weapon = settings.Weapon || "试作金珀"; // 默认武器
const pageScrollCount = Math.min(99, Math.max(0, Math.floor(Number(settings.pageScrollCount) || 2))); // 页面滚动次数
const ocrRegion = { x: 1463, y: 135, width: 256, height: 32 }; // OCR识别区域
const replacementMap = { "卵": "卯", "姐": "妲", "去": "云", "日": "甘", "螨": "螭", "知": "矢", "钱": "钺", "础": "咄", "厘": "匣", "排": "绯", "朦": "曚", "矿": "斫", "镰": "簾", "廉": "簾", "救": "赦", "塑": "槊", "雍": "薙" }; // OCR替换映射表
const elements = [ "火", "水", "草", "雷", "风", "冰", "岩", "物"]; // 元素列表
const weaponTypeMap = {
"1": "单手剑",
"11": "双手剑",
"12": "弓箭",
"10": "法器",
"13": "长枪"
}; // 武器类型映射表
// 加载角色数据
const filePath = "assets/combat_avatar.json";
const { aliasToNameMap, nameToWeaponMap } = await loadCombatAvatarData(filePath);
if (!aliasToNameMap || !nameToWeaponMap) {
log.error("无法加载角色数据OCR 识别无法进行。");
return;
}
// 加载武器名称数据
const weaponNamesMap = await loadWeaponNames("assets/weaponName.json");
if (!weaponNamesMap) {
log.error("无法加载武器名称数据");
return;
}
// 开始执行角色路径
await CharacterPath();
// 加载角色数据
async function loadCombatAvatarData(filePath) {
try {
const jsonData = file.readTextSync(filePath);
const combatAvatarData = JSON.parse(jsonData);
const aliasToNameMap = {}; // 用于存储别名到正式名称的映射
const nameToWeaponMap = {}; // 用于存储正式名称到武器属性的映射
combatAvatarData.forEach(character => {
aliasToNameMap[character.name] = character.name; // 存储正式名称
nameToWeaponMap[character.name] = character.weapon; // 存储武器属性
character.alias.forEach(alias => {
aliasToNameMap[alias] = character.name; // 存储别名到正式名称的映射
});
});
return { aliasToNameMap, nameToWeaponMap };
} catch (error) {
log.error(`加载或解析 JSON 文件失败: ${error}`);
return null;
}
}
// 加载武器名称数据
async function loadWeaponNames(filePath) {
try {
const jsonData = file.readTextSync(filePath);
const weaponNamesData = JSON.parse(jsonData);
const weaponNamesMap = {};
// 将武器名称数据存储到一个对象中,键为武器类型,值为武器名称数组
weaponNamesData.forEach(item => {
for (const [weaponType, weaponNames] of Object.entries(item)) {
weaponNamesMap[weaponType] = weaponNames;
}
});
// log.info(`角色别名映射表: ${JSON.stringify(aliasToNameMap)}`);
// log.info(`角色武器映射表: ${JSON.stringify(nameToWeaponMap)}`);
return weaponNamesMap;
} catch (error) {
log.error(`加载武器名称文件失败: ${error}`);
return null;
}
}
// OCR识别文本
async function recognizeText(targetText, ocrRegion, aliasToNameMap, timeout = 10000, retryInterval = 20, maxAttempts = 5) {
let startTime = Date.now();
let retryCount = 0;
const targetFormalName = aliasToNameMap ? aliasToNameMap[targetText] || targetText : targetText;
while (Date.now() - startTime < timeout && retryCount < maxAttempts) {
try {
let captureRegion = captureGameRegion();
let ocrObject = RecognitionObject.Ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height);
ocrObject.threshold = 0.8;
let resList = captureRegion.findMulti(ocrObject);
for (let res of resList) {
let correctedText = res.text;
for (let [wrongChar, correctChar] of Object.entries(replacementMap)) {
correctedText = correctedText.replace(new RegExp(wrongChar, 'g'), correctChar);
}
// log.info(`识别结果: ${correctedText}, 原始坐标: x=${res.x}, y=${res.y}`);
let recognizedFormalName = aliasToNameMap ? aliasToNameMap[correctedText] || correctedText : correctedText;
recognizedFormalName = fuzzyMatch(correctedText, Object.values(aliasToNameMap)) || recognizedFormalName;
if (recognizedFormalName === targetFormalName) {
return { success: true, text: recognizedFormalName, x: res.x, y: res.y };
}
}
} catch (error) {
retryCount++;
log.warn(`OCR 识别失败,正在进行第 ${retryCount} 次重试...`);
}
await sleep(retryInterval);
}
return { success: false };
}
// 模糊匹配文本
function fuzzyMatch(target, candidates, weightThreshold = 0.6) {
function levenshteinDistance(a, b) {
const m = a.length + 1;
const n = b.length + 1;
const d = Array(m).fill(null).map(() => Array(n).fill(0));
for (let i = 0; i < m; i++) d[i][0] = i;
for (let j = 0; j < n; j++) d[0][j] = j;
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
}
}
return d[m - 1][n - 1];
}
let bestMatch = null;
let bestWeight = 0;
for (const candidate of candidates) {
const distance = levenshteinDistance(target, candidate);
const keywordWeight = 0.8;
const lengthWeight = 0.2;
const keywordMatch = candidate.includes(target);
const weight = (keywordMatch ? keywordWeight : 0) + (1 - distance / Math.max(target.length, candidate.length)) * lengthWeight;
if (weight >= weightThreshold) {
return candidate;
}
if (weight > bestWeight) {
bestWeight = weight;
bestMatch = candidate;
}
}
return bestMatch;
}
// 合并OCR识别结果
function combineResults(results) {
const frequencyMap = {};
results.forEach(result => {
if (!frequencyMap[result]) {
frequencyMap[result] = 0;
}
frequencyMap[result]++;
});
const sortedResults = Object.keys(frequencyMap).sort((a, b) => frequencyMap[b] - frequencyMap[a]);
for (let result of sortedResults) {
if (result.length === 2) {
return result;
}
}
if (sortedResults.length >= 2) {
return sortedResults[0] + sortedResults[1];
}
return sortedResults[0] || "";
}
// 滚动页面
async function scrollPage(totalDistance, stepDistance = 10, delayMs = 10) {
moveMouseTo(525, 920);
await sleep(500);
leftButtonDown();
const steps = Math.ceil(totalDistance / stepDistance);
for (let j = 0; j < steps; j++) {
const remainingDistance = totalDistance - j * stepDistance;
const moveDistance = remainingDistance < stepDistance ? remainingDistance : stepDistance;
moveMouseBy(0, -moveDistance);
await sleep(delayMs);
}
await sleep(700);
leftButtonUp();
await sleep(100);
}
// 选择角色
async function selectCharacter(characterName) {
const SwitchingSteps = 99; // 最大切换次数
for (let i = 0; i < SwitchingSteps; i++) {
let result = await recognizeText(characterName, ocrRegion, aliasToNameMap, 200);
if (result.success) {
// log.info(`找到 ${characterName},识别结果: ${result.text},坐标: x=${result.x}, y=${result.y}`);
return true;
}
await click(1840, 540); // 点击切换角色
await sleep(200);
}
log.warn(`扫描完成,未找到 ${characterName}`);
return false;
}
// 选择元素
async function selectElement(element) {
if (element === "物") return; // 如果是物理属性,无需切换
const ElementClickX = Math.round(787 + elements.indexOf(element) * 57.5); // 计算点击位置
await click(960, 45); // 点击元素切换按钮
await sleep(100);
leftButtonDown();
const steps = 10;
const stepDistance = 15;
for (let j = 0; j < steps; j++) {
moveMouseBy(stepDistance, 0); // 拖动鼠标选择元素
await sleep(10);
}
await sleep(500);
leftButtonUp();
await sleep(500);
await click(ElementClickX, 130); // 点击选择元素
await sleep(500);
await click(540, 45); // 点击确认
await sleep(200);
}
// 识别并组合武器名称
async function recognizeAndCombineWeaponName(ocrRegion, maxAttempts = 5) {
const allResults = [];
for (let i = 0; i < maxAttempts; i++) {
try {
let captureRegion = captureGameRegion();
let ocrObject = RecognitionObject.Ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height);
ocrObject.threshold = 0.8;
let resList = captureRegion.findMulti(ocrObject);
for (let res of resList) {
let correctedText = res.text;
for (let [wrongChar, correctChar] of Object.entries(replacementMap)) {
correctedText = correctedText.replace(new RegExp(wrongChar, 'g'), correctChar);
}
// log.info(`OCR 识别结果: ${correctedText}, 原始坐标: x=${res.x}, y=${res.y}`);
allResults.push(correctedText);
}
} catch (error) {
log.warn(`OCR 识别失败,正在进行第 ${i + 1} 次重试...`);
}
await sleep(20);
}
const combinedResult = combineResults(allResults);
// log.info(`组合后的识别结果: ${combinedResult}`);
return combinedResult;
}
// 扫描武器
async function scanWeapons(settingsWeapon) {
// 获取角色的正式名称
const characterName = aliasToNameMap[Character] || Character;
log.info(`寻找到角色 ${Character} 正式名称 ${characterName}`);
if (!characterName) {
log.error(`未找到角色 ${Character} 的正式名称`);
return false;
}
// 获取角色的武器类型
const characterWeaponType = nameToWeaponMap[characterName];
log.info(`寻找到角色 ${Character} 的武器类型为 ${characterWeaponType}`);
if (!characterWeaponType) {
log.warn(`未找到角色 ${characterName} 的武器类型,将直接匹配 目标武器名 和 当前武器名 `);
}
// 获取对应的武器名称列表
const weaponType = weaponTypeMap[characterWeaponType];
const weaponNames = weaponNamesMap[weaponType] || [];
// 如果武器名称列表为空,则将 settingsWeapon 添加到列表中,以便后续匹配
if (!weaponNames.length) {
log.warn(`未找到武器类型 ${weaponType} 的武器名称列表,将使用原始武器名 ${settingsWeapon}`);
weaponNames.push(settingsWeapon);
}
let weaponName1 = fuzzyMatch(settingsWeapon, weaponNames, 0.9);
log.info(`寻找到 目标武器 正式名称 ${weaponName1}`);
if (!weaponName1) {
log.warn(`未找到与 ${settingsWeapon} 匹配的武器名,使用原始名称作为 目标武器名`);
weaponName1 = settingsWeapon;
}
const startX = 99.5;
const startY = 213.5;
const rowHeight = 167;
const columnWidth = 141;
const maxRows = 4;
const maxColumns = 4;
for (let scroll = 0; scroll <= pageScrollCount; scroll++) {
for (let row = 0; row < maxRows; row++) {
for (let column = 0; column < maxColumns; column++) {
const clickX = Math.round(startX + column * columnWidth);
const clickY = Math.round(startY + row * rowHeight);
await click(clickX, clickY); // 点击武器
await sleep(50);
const combinedWeaponName2 = await recognizeAndCombineWeaponName(ocrRegion);
if (!combinedWeaponName2) {
log.warn("OCR 识别失败,未找到任何武器名");
continue;
}
// 尝试模糊匹配武器名称
let weaponName2 = fuzzyMatch(combinedWeaponName2, weaponNames, 1);
// 如果未匹配到已知武器名称,则将 OCR 识别结果直接作为武器名称使用
if (!weaponName2) {
log.warn(`未找到与 ${combinedWeaponName2} 匹配的已知武器名,将使用 OCR 识别结果作为 当前武器名`);
weaponName2 = combinedWeaponName2;
}
// 计算匹配占比,排除干扰词
const matchRatio = calculateMatchRatio(weaponName1, weaponName2);
if (matchRatio >= 0.8) { // 如果匹配占比大于等于 80%,则认为匹配成功
log.info(`成功匹配武器:${weaponName1},匹配占比 ${matchRatio.toFixed(2)}`);
await click(1600, 1005); // 点击确认
await sleep(1000);
await click(1320, 755); // 点击确认
await sleep(1000);
return true;
} else {
log.warn(` 目标武器名 (${weaponName1}) 和 当前武器名 (${weaponName2}) 不匹配,匹配占比 ${matchRatio.toFixed(2)}`);
}
}
}
if (scroll < pageScrollCount) {
await scrollPage(673, 10, 10); // 滚动页面
}
}
log.warn(`扫描完成,未找到 ${settingsWeapon}`);
return false;
}
// 计算匹配占比
function calculateMatchRatio(target, candidate) {
const ignoreWords = ["剑", "之", "弓", "枪", "长", "大", "典", "章"]; // 需要排除的干扰词
const targetClean = target.split('').filter(char => !ignoreWords.includes(char)).join('');
const candidateClean = candidate.split('').filter(char => !ignoreWords.includes(char)).join('');
const commonChars = targetClean.split('').filter(char => candidateClean.includes(char)).length;
const totalChars = targetClean.length;
return commonChars / totalChars;
}
// 执行角色路径
async function CharacterPath() {
log.info("开始寻找");
await genshin.returnMainUi(); // 返回主界面
keyPress("1"); // 按键操作
await sleep(500);
keyPress("C"); // 打开角色界面
await sleep(1000);
await selectElement(Element); // 选择元素属性
if (!await selectCharacter(Character)) { // 选择角色
log.error("角色筛选失败,退出脚本");
return;
}
await click(125, 225); // 点击武器详情
await sleep(1000);
await click(1600, 1005); // 点击替换武器
await sleep(1000);
await click(500, 1005); // 点击武器排序
await sleep(200);
await click(500, 905); // 点击排序类型
await sleep(200);
await click(605, 137); // 点击武器排序
await moveMouseTo(605, 140); // 移动鼠标至滑条顶端
await sleep(200);
leftButtonDown(); // 长按左键重置武器滑条
await sleep(300);
leftButtonUp();
await sleep(200);
if (!await scanWeapons(Weapon)) { // 未找到指定武器
log.warn(` ${pageScrollCount+1} 页扫描完,未找到 ${Weapon}`);
}
await genshin.returnMainUi(); // 返回主界面
}
})();