mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-15 03:23:22 +08:00
添加通过ocr识别圣遗物并解析为json格式的js脚本 (#2119)
* Create README.md * Create settings.json * Create manifest.json * Create main.js * Create Artifact.js * Create Constants.js * Create Parser.js * Create a.txt * Delete repo/js/OCRArtifacts/records/a.txt * Create a.txt * Create a.txt
This commit is contained in:
7
repo/js/OCRArtifacts/README.md
Normal file
7
repo/js/OCRArtifacts/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 圣遗物解析 - OCRArtifacts
|
||||
|
||||
使用ocr识别背包中的圣遗物并解析为json格式
|
||||
|
||||
tips: 暂时没有适配识别非满星圣遗物, 如: 猎人套的4星圣遗物识别可能会有问题
|
||||
推荐在1920*1080分辨率下使用
|
||||
|
||||
1
repo/js/OCRArtifacts/assets/a.txt
Normal file
1
repo/js/OCRArtifacts/assets/a.txt
Normal file
@@ -0,0 +1 @@
|
||||
占位
|
||||
73
repo/js/OCRArtifacts/lib/Artifact.js
Normal file
73
repo/js/OCRArtifacts/lib/Artifact.js
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
class _Artifact {
|
||||
constructor(arr) {
|
||||
this.number = parseInt(arr.shift())
|
||||
this.name = arr.shift()
|
||||
this.slot = arr.shift()
|
||||
this.main = arr.shift()
|
||||
this.mainVal = arr.shift().replace(',', '')
|
||||
const last = arr.pop()
|
||||
if (last.includes('已装备')) {
|
||||
this.equip = last.replace('已装备', '')
|
||||
}
|
||||
const index = findIndex(arr, '2件套')
|
||||
arr = arr.slice(0, index)
|
||||
this.setType = arr.pop().replace(/[::]/, '')
|
||||
this.getStar()
|
||||
this.getSource(arr)
|
||||
this.getLv(arr)
|
||||
this.getAttr(arr)
|
||||
}
|
||||
|
||||
getStar() {
|
||||
const { setType } = this
|
||||
if (STAR_3_SET_TYPE.includes(setType)) this.star = 3
|
||||
else if (STAR_4_SET_TYPE.includes(setType)) this.star = 4
|
||||
else this.star = 5
|
||||
}
|
||||
getSource(arr) {
|
||||
if (findIndex(arr, '祝圣之霜定义') !== -1) this.source = 'SEDefinition'
|
||||
}
|
||||
|
||||
getLv(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i].startsWith('+')) {
|
||||
let str = arr.splice(i, 1)[0]
|
||||
this.lv = str.replace('+', '') || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAttr(arr) {
|
||||
let r = []
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let str = arr[i]
|
||||
for (let n of ATTR_NAMES) {
|
||||
if (str.includes(n)) {
|
||||
let t = {}
|
||||
if (str.includes('待激活')) {
|
||||
t['active'] = false
|
||||
}
|
||||
if (str.includes('%') && ['攻击力', '防御力', '生命值'].includes(n)) {
|
||||
t['isPercent'] = true
|
||||
}
|
||||
t[n] = str.match(/\+([.0-9%]+)/)[1]
|
||||
r.push(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.attr = r
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function findIndex(arr, target) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].includes(target)) return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
var Artifact = _Artifact
|
||||
|
||||
15
repo/js/OCRArtifacts/lib/Constants.js
Normal file
15
repo/js/OCRArtifacts/lib/Constants.js
Normal file
@@ -0,0 +1,15 @@
|
||||
var SET_TYPES = ["冒险家", "悠古的磐岩", "战狂", "冰风迷途的勇士", "染血的骑士道",
|
||||
"勇士之心", "炽烈的炎之魔女", "深林的记忆", "守护之心", "沙上楼阁史话",
|
||||
"来歆余响", "绝缘之旗印", "深廊终曲", "乐园遗落之花", "谐律异想断章",
|
||||
"赌徒", "饰金之梦", "角斗士的终幕礼", "黄金剧团", "沉沦之心", "华馆梦醒形骸记",
|
||||
"教官", "渡过烈火的贤人", "长夜之誓", "幸运儿", "被怜爱的少女", "逐影猎人", "武人",
|
||||
"穹境示现之夜", "回声之林夜话", "昔日宗室之仪", "水仙之梦", "黑曜秘典", "海染砗磲",
|
||||
"苍白之火", "祭水之人", "祭火之人", "祭雷之人", "祭冰之人", "行者之心", "逆飞的流星",
|
||||
"学士", "烬城勇者绘卷", "追忆之注连", "纺月的夜歌", "昔时之歌", "千岩牢固", "流放者",
|
||||
"如雷的盛怒", "平息鸣雷的尊者", "奇迹", "游医", "未竟的遐思", "辰砂往生录", "翠绿之影",
|
||||
"花海甘露之光", "流浪大地的乐团"]
|
||||
|
||||
var ATTR_NAMES = ['生命值', '元素精通', '暴击率', '暴击伤害', '防御力', '攻击力', '元素充能效率']
|
||||
|
||||
var STAR_3_SET_TYPE = ["冒险家", "幸运儿", "游医"]
|
||||
var STAR_4_SET_TYPE = ["祭水之人", "祭火之人", "祭雷之人", "祭冰之人", "学士", "赌徒", "武人", "战狂", "流放者", "行者之心", "奇迹", "守护之心", "勇士之心"]
|
||||
16
repo/js/OCRArtifacts/lib/Parser.js
Normal file
16
repo/js/OCRArtifacts/lib/Parser.js
Normal file
@@ -0,0 +1,16 @@
|
||||
async function parse(filePath) {
|
||||
let content = file.readTextSync(filePath)
|
||||
content = content.replace(/=+第(\d*)个圣遗物信息:/g, ';;;;;$1')
|
||||
content = content.replace(/=+.*/g, '')
|
||||
const arr = content.split(';;;;;')
|
||||
const total = arr.shift().match(/圣遗物数量: (\d*),/)[1]
|
||||
log.info(`total: ${total}`)
|
||||
let result = []
|
||||
while (arr.length > 0) {
|
||||
result.push(new Artifact(arr.shift().trim().split('\n')))
|
||||
}
|
||||
let name = filePath.replace('artifact_log', 'parsed_result').replace('.txt', '.json')
|
||||
file.WriteTextSync(name, JSON.stringify(result, null, 2), true)
|
||||
log.info(`解析完成,请查看文件: ${name}`)
|
||||
return new Promise((res, rej) => { res() })
|
||||
}
|
||||
196
repo/js/OCRArtifacts/main.js
Normal file
196
repo/js/OCRArtifacts/main.js
Normal file
@@ -0,0 +1,196 @@
|
||||
// 加载额外模块
|
||||
eval(file.readTextSync("lib/Constants.js"))
|
||||
eval(file.readTextSync("lib/Artifact.js"))
|
||||
eval(file.readTextSync("lib/Parser.js"))
|
||||
|
||||
// 每行圣遗物数量
|
||||
const COUNT_PER_LINE = 8
|
||||
// 每页圣遗物行数
|
||||
const LINE_PER_PAGE = 5
|
||||
// 左上方第一个圣遗物图标中心点坐标
|
||||
const centerX = 180
|
||||
const centerY = 253
|
||||
// 每次向右移动鼠标的偏移量
|
||||
const offsetX = 145
|
||||
// 每次向下移动鼠标的偏移量
|
||||
const offsetY = 166
|
||||
|
||||
// 翻页 使用模拟鼠标滚轮向下滚动实现
|
||||
let _cnt = 0
|
||||
async function scrollPage() {
|
||||
let count = 49
|
||||
for (let i = 0; i < count; i++) {
|
||||
await verticalScroll(-1)
|
||||
}
|
||||
if (++_cnt === 3) {
|
||||
await verticalScroll(1)
|
||||
_cnt = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 在指定区域进行 OCR 识别并输出识别内容
|
||||
async function recognizeTextInRegion(ocrRegion, timeout = 5000) {
|
||||
let startTime = Date.now()
|
||||
let retryCount = 0 // 重试计数
|
||||
while (Date.now() - startTime < timeout) {
|
||||
try {
|
||||
// 在指定区域进行 OCR 识别
|
||||
let ocrResult = captureGameRegion().find(
|
||||
RecognitionObject.ocr(
|
||||
ocrRegion.x,
|
||||
ocrRegion.y,
|
||||
ocrRegion.width,
|
||||
ocrRegion.height
|
||||
)
|
||||
)
|
||||
if (ocrResult) {
|
||||
return ocrResult.text // 返回识别到的内容
|
||||
} else {
|
||||
log.warn(`OCR 识别区域未找到内容`)
|
||||
return null // 如果 OCR 未识别到内容,返回 null
|
||||
}
|
||||
} catch (error) {
|
||||
retryCount++ // 增加重试计数
|
||||
log.warn(`OCR识别失败,正在进行第 ${retryCount} 次重试...`)
|
||||
}
|
||||
await sleep(500) // 短暂延迟,避免过快循环
|
||||
}
|
||||
log.warn(`经过多次尝试,仍然无法在指定区域识别到文字`)
|
||||
return null // 如果未识别到文字,返回 null
|
||||
}
|
||||
|
||||
// 识别右上方的圣遗物总数
|
||||
async function recognizedArtifactCount() {
|
||||
let ocrRegion = { x: 1500, y: 0, width: 300, height: 80 }
|
||||
let text = await recognizeTextInRegion(ocrRegion)
|
||||
log.info('圣遗物数量识别结果: ', text)
|
||||
let res = text.match(/(\d+)\/(\d+)/)
|
||||
log.info('圣遗物数量:', res[1])
|
||||
return res[1]
|
||||
}
|
||||
|
||||
// 识别左侧的圣遗物文本
|
||||
async function recognizedArtifactText() {
|
||||
let ocrRegion = { x: 1306, y: 122, width: 496, height: 845 }
|
||||
return await recognizeTextInRegion(ocrRegion)
|
||||
}
|
||||
|
||||
// 遍历当前页
|
||||
async function traversePage(n, totalCount) {
|
||||
let x = centerX
|
||||
let y = centerY
|
||||
let num = n * LINE_PER_PAGE * COUNT_PER_LINE
|
||||
let startRow = 0
|
||||
const sy = totalCount - num
|
||||
if (sy < LINE_PER_PAGE * COUNT_PER_LINE) {
|
||||
let rowNum = Math.floor(sy / COUNT_PER_LINE)
|
||||
let last = sy - rowNum * COUNT_PER_LINE
|
||||
if (last > 0) rowNum++
|
||||
startRow = LINE_PER_PAGE - rowNum - 1
|
||||
if (startRow < 0) startRow = 0
|
||||
log.info(
|
||||
`当前为最后一页,开始行:${startRow},行数:${rowNum},最后一行有${last}个`
|
||||
)
|
||||
}
|
||||
let content = `开始遍历第${n + 1}页
|
||||
===================`
|
||||
for (let j = startRow; j < LINE_PER_PAGE; j++) {
|
||||
y = centerY + offsetY * j
|
||||
for (let i = 0; i < COUNT_PER_LINE; i++) {
|
||||
x = centerX + offsetX * i
|
||||
await moveMouseTo(x, y)
|
||||
await click(x, y)
|
||||
await sleep(50)
|
||||
let text = await recognizedArtifactText()
|
||||
content += `第${++num}个圣遗物信息:
|
||||
${text}
|
||||
=====================`
|
||||
if (num == totalCount) return content
|
||||
}
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// 同步写入文件
|
||||
async function writeFileSync(
|
||||
filePath,
|
||||
content,
|
||||
succLogFn = null,
|
||||
errorLogFn = null
|
||||
) {
|
||||
result = file.WriteTextSync(filePath, `${content}`, true)
|
||||
if (result) {
|
||||
if (succLogFn) succLogFn()
|
||||
else log.info(`成功写入日志文件`)
|
||||
} else {
|
||||
if (errorLogFn) errorLogFn()
|
||||
else log.error(`写入日志文件失败`)
|
||||
}
|
||||
}
|
||||
|
||||
// 识别圣遗物
|
||||
async function doSearch(startTimeStamp) {
|
||||
const filePath = `records/artifact_log_${startTimeStamp}.txt`
|
||||
await genshin.returnMainUi()
|
||||
// 按下 B 键
|
||||
keyPress('B')
|
||||
await sleep(1000)
|
||||
// 点击圣遗物菜单的图标
|
||||
await click(675, 50)
|
||||
await sleep(1500)
|
||||
let content = `开始识别圣遗物,时间:${new Date()}`
|
||||
// 识别圣遗物数量
|
||||
const artifactCount = await recognizedArtifactCount()
|
||||
const lineCount = Math.ceil(artifactCount / COUNT_PER_LINE)
|
||||
const pageCount = Math.ceil(artifactCount / (COUNT_PER_LINE * LINE_PER_PAGE))
|
||||
let tips = `
|
||||
圣遗物数量: ${artifactCount}, 页数: ${pageCount}, 行数: ${lineCount}
|
||||
=============================`
|
||||
log.info(tips)
|
||||
content += tips
|
||||
writeFileSync(filePath, content)
|
||||
for (let n = 0; n < pageCount; n++) {
|
||||
let res = await traversePage(n, artifactCount)
|
||||
writeFileSync(
|
||||
filePath,
|
||||
res,
|
||||
() => log.info(`第${n + 1}页成功写入日志文件`),
|
||||
() => log.error(`第${n + 1}页写入日志文件失败`)
|
||||
)
|
||||
await moveMouseTo(centerX, centerY)
|
||||
await sleep(100)
|
||||
if (n < pageCount - 1) {
|
||||
await scrollPage()
|
||||
}
|
||||
await sleep(200)
|
||||
}
|
||||
await sleep(500)
|
||||
const searchEndTime = new Date().getTime()
|
||||
content = `识别结束,时间:${new Date()},识别耗时:${searchEndTime - startTimeStamp}`
|
||||
writeFileSync(filePath, content)
|
||||
return searchEndTime
|
||||
}
|
||||
|
||||
async function doParse(filePath) {
|
||||
await parse(filePath)
|
||||
}
|
||||
|
||||
; (async function () {
|
||||
setGameMetrics(1920, 1080, 1)
|
||||
const startTimeStamp = new Date().getTime()
|
||||
const filePath = `records/artifact_log_${startTimeStamp}.txt`
|
||||
try {
|
||||
await doSearch(startTimeStamp)
|
||||
await doParse(filePath)
|
||||
await genshin.returnMainUi()
|
||||
const endTimeStamp = new Date().getTime()
|
||||
content = `任务结束,时间:${new Date()},总耗时:${endTimeStamp - startTimeStamp}`
|
||||
writeFileSync(filePath, content)
|
||||
} catch (e) {
|
||||
log.error(e.stack)
|
||||
writeFileSync(filePath, `
|
||||
错误调用堆栈:
|
||||
${e.stack}`)
|
||||
}
|
||||
|
||||
})()
|
||||
18
repo/js/OCRArtifacts/manifest.json
Normal file
18
repo/js/OCRArtifacts/manifest.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "OCRArtifacts",
|
||||
"version": "1.0.0",
|
||||
"description": "读取背包圣遗物并解析为json格式",
|
||||
"authors": [
|
||||
{
|
||||
"name": "zlh",
|
||||
"links": "https://github.com/coderzhlh"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
"main": "main.js",
|
||||
"saved_files": [
|
||||
"records/*.txt",
|
||||
"records/*.json"
|
||||
]
|
||||
}
|
||||
1
repo/js/OCRArtifacts/records/a.txt
Normal file
1
repo/js/OCRArtifacts/records/a.txt
Normal file
@@ -0,0 +1 @@
|
||||
站位
|
||||
7
repo/js/OCRArtifacts/settings.json
Normal file
7
repo/js/OCRArtifacts/settings.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "notify",
|
||||
"type": "checkbox",
|
||||
"label": "通知"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user