/* 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`)) } }