131 lines
5.2 KiB
JavaScript
131 lines
5.2 KiB
JavaScript
/* eslint-disable no-undef */
|
|
import path from "path"
|
|
import dotenv from "dotenv"
|
|
import { writeSync, readSync } from "./file.js"
|
|
import Downloader from "./downloader.js"
|
|
|
|
dotenv.config()
|
|
|
|
// zh_TW uses an older version of charword_table.json
|
|
// zh_TW is removed
|
|
const REGIONS = ["zh_CN", "en_US", "ja_JP", "ko_KR"]
|
|
const REGION_URLS = {
|
|
"zh_CN": "Kengxxiao/ArknightsGameData",
|
|
"en_US": "Kengxxiao/ArknightsGameData_YoStar",
|
|
"ja_JP": "Kengxxiao/ArknightsGameData_YoStar",
|
|
"ko_KR": "Kengxxiao/ArknightsGameData_YoStar",
|
|
}
|
|
const DEFAULT_REGION = REGIONS[0]
|
|
const NICKNAME = {
|
|
"zh_CN": "博士",
|
|
"en_US": "Doctor",
|
|
"ja_JP": "ドクター",
|
|
"ko_KR": "박사",
|
|
"zh_TW": "博士",
|
|
}
|
|
|
|
export function getOperatorId(operatorConfig) {
|
|
return operatorConfig.filename.replace(/^(dyn_illust_)(char_[\d]+)(_[\w]+)(|(_.+))$/g, '$2$3$4')
|
|
}
|
|
|
|
export default class CharwordTable {
|
|
#operatorIDs = Object.values(__config.operators).map(operator => { return getOperatorId(operator) })
|
|
#charwordTablePath = path.join(__projectRoot, __config.folder.auto_update_data)
|
|
#charwordTableFile = path.join(this.#charwordTablePath, 'charword_table.json')
|
|
#charwordTable = JSON.parse(readSync(this.#charwordTableFile)) || {
|
|
config: {
|
|
default_region: DEFAULT_REGION,
|
|
regions: REGIONS,
|
|
},
|
|
operators: {},
|
|
}
|
|
|
|
async process() {
|
|
const regionObject = REGIONS.reduce((acc, cur) => ({ ...acc, [cur]: {} }), {})
|
|
this.#operatorIDs.forEach(id => {
|
|
this.#charwordTable.operators[id] = {
|
|
alternativeId: id.replace(/^(char_)([\d]+)(_[\w]+)(|(_.+))$/g, '$1$2$3'),
|
|
voice: JSON.parse(JSON.stringify(regionObject)), // deep copy
|
|
info: JSON.parse(JSON.stringify(regionObject)), // deep copy
|
|
}
|
|
})
|
|
await this.#load(DEFAULT_REGION)
|
|
await Promise.all(REGIONS.slice(1).map(async (region) => {await this.#load(region)}))
|
|
writeSync(JSON.stringify(this.#charwordTable), this.#charwordTableFile)
|
|
}
|
|
|
|
lookup(operatorName) {
|
|
const operatorId = getOperatorId(__config.operators[operatorName])
|
|
const operatorBlock = this.#charwordTable.operators[operatorId]
|
|
return {
|
|
config: this.#charwordTable.config,
|
|
operator: operatorBlock.ref ? this.#charwordTable.operators[operatorBlock.alternativeId] : operatorBlock,
|
|
}
|
|
}
|
|
|
|
async #load(region) {
|
|
|
|
const data = await this.#download(region)
|
|
|
|
// put voice actor info into charword_table
|
|
for (const [id, element] of Object.entries(this.#charwordTable.operators)) {
|
|
let operatorId = id
|
|
let useAlternativeId = false
|
|
if (typeof data.voiceLangDict[operatorId] === 'undefined') {
|
|
operatorId = element.alternativeId
|
|
useAlternativeId = true
|
|
}
|
|
if (region === DEFAULT_REGION) {
|
|
element.infile = this.#operatorIDs.includes(operatorId);
|
|
element.ref = useAlternativeId && element.infile;
|
|
}
|
|
// not available in other region
|
|
if (typeof data.voiceLangDict[operatorId] === 'undefined') {
|
|
console.log(`Voice actor info of ${id} is not available in ${region}.`)
|
|
continue
|
|
}
|
|
|
|
if (element.infile && useAlternativeId) {
|
|
// if using alternative id and infile is true, means data can be
|
|
// refered inside the file
|
|
// if infile is false, useAlternativeId is always true
|
|
// if useAlternativeId is false, infile is always true
|
|
// | case | infile | useAlternativeId | Note |
|
|
// | ------------------- | ------ | ---------------- | --------------- |
|
|
// | lee_trust_your_eyes | false | true | skin only |
|
|
// | nearl_relight | true | true | skin, operator, no voice |
|
|
// | nearl | true | false | operator only |
|
|
// | w_fugue | true | false | skin, operator, voice |
|
|
continue
|
|
}
|
|
Object.values(data.voiceLangDict[operatorId].dict).forEach(item => {
|
|
if (typeof element.info[region][item.wordkey] === 'undefined') {
|
|
element.info[region][item.wordkey] = {}
|
|
}
|
|
element.info[region][item.wordkey][item.voiceLangType] = [...(typeof item.cvName === 'string' ? [item.cvName] : item.cvName)]
|
|
})
|
|
}
|
|
|
|
// put voice lines into charword_table
|
|
Object.values(data.charWords).forEach(item => {
|
|
const operatorInfo = Object.values(this.#charwordTable.operators).filter(element => element.info[region][item.wordKey])
|
|
if (operatorInfo.length > 0) {
|
|
for (const operator of operatorInfo) {
|
|
if (typeof operator.voice[region][item.wordKey] === 'undefined') {
|
|
operator.voice[region][item.wordKey] = {}
|
|
}
|
|
operator.voice[region][item.wordKey][item.voiceId] = {
|
|
title: item.voiceTitle,
|
|
text: item.voiceText.replace(/{@nickname}/g, NICKNAME[region]),
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
async #download(region) {
|
|
return await (new Downloader()).github(`https://api.github.com/repos/${REGION_URLS[region]}/commits?path=${region}/gamedata/excel/charword_table.json`, `https://raw.githubusercontent.com/${REGION_URLS[region]}/master/${region}/gamedata/excel/charword_table.json`, path.join(this.#charwordTablePath, `charword_table_${region}.json`))
|
|
}
|
|
|
|
}
|