diff --git a/myscripts/.gitignore b/myscripts/.gitignore new file mode 100644 index 000000000..1f5d64097 --- /dev/null +++ b/myscripts/.gitignore @@ -0,0 +1 @@ +export \ No newline at end of file diff --git a/myscripts/collateArtifact.js b/myscripts/collateArtifact.js new file mode 100644 index 000000000..78a055405 --- /dev/null +++ b/myscripts/collateArtifact.js @@ -0,0 +1,55 @@ +// object map that converts relic EquipType to a property name +const relicTypeToPropertyName = { 'EQUIP_BRACER': 'flower', 'EQUIP_NECKLACE': 'plume', 'EQUIP_SHOES': 'sands', 'EQUIP_RING': 'goblet', 'EQUIP_DRESS': 'circlet'}; + +function collateArtifact(lang) { + const language = getLanguage(lang); + const xsets = getExcel('ReliquarySetExcelConfigData'); + const xrelics = getExcel('ReliquaryExcelConfigData'); + const xreliccodex = getExcel('ReliquaryCodexExcelConfigData'); + const xrefine = getExcel('EquipAffixExcelConfigData'); + + let myartifact = xsets.reduce((accum, obj) => { + if(obj.SetIcon === '') return accum; + let setname; + let filename; + let data = {}; + + // get available rarities + data.rarity = xreliccodex.reduce((accum, relic) => { + if(obj.SetId !== relic.SuitId) return accum; + relic.Level = relic.Level.toString(); + if(accum.indexOf(relic.Level) === -1) accum.push(relic.Level); + return accum; + }, []); + + // set bonus effects + obj.SetNeedNum.forEach((ele, ind) => { + let effect = xrefine.find(e => e.AffixId === obj.EquipAffixId*10 + ind); + data[ele+'pc'] = language[effect.DescTextMapHash]; + if(setname === undefined) { + setname = language[effect.NameTextMapHash]; + filename = makeFileName(getLanguage('EN')[effect.NameTextMapHash]); + } + }); + + data.images = {}; + // relic pieces + obj.ContainsList.forEach(ele => { + let relic = xrelics.find(e => e.Id === ele); + let relicdata = {}; + relicdata.name = language[relic.NameTextMapHash]; + relicdata.relictype = xmanualtext.find(ele => ele.TextMapId === relic.EquipType).TextMapContentTextMapHash; + relicdata.relictype = language[relicdata.relictype]; + relicdata.description = language[relic.DescTextMapHash]; + data[relicTypeToPropertyName[relic.EquipType]] = relicdata; + data.images[relicTypeToPropertyName[relic.EquipType]] = `https://upload-os-bbs.mihoyo.com/game_record/genshin/equip/${relic.Icon}.png`; + }); + + data.name = setname; + accum[filename] = data; + return accum; + }, {}); + return myartifact; +} + +module.exports = collateArtifact; \ No newline at end of file diff --git a/myscripts/collateCharacter.js b/myscripts/collateCharacter.js new file mode 100644 index 000000000..fa480b6f6 --- /dev/null +++ b/myscripts/collateCharacter.js @@ -0,0 +1,78 @@ +// avatar extra info +const xextrainfo = getExcel('FetterInfoExcelConfigData'); + + +// object map that converts player's avatar id to TextMapHash +const playerIdToTextMapHash = { 10000005: 2329553598, 10000007: 3241049361 }; + +function collateCharacter(lang) { + const language = getLanguage(lang); + const xsubstat = getExcel('AvatarPromoteExcelConfigData'); + let myavatar = xplayableAvatar.reduce((accum, obj) => { + let data = {}; + let extra = xextrainfo.find(ele => ele.AvatarId === obj.Id); + + data.name = language[obj.NameTextMapHash]; + if(isPlayer(obj)) data.name = language[playerIdToTextMapHash[obj.Id]]; + //if(data.name === 'Traveler') data.name = capitalizeFirst(avatarIdToFileName[obj.Id]); + data.description = language[obj.DescTextMapHash]; + data.weapontype = language[weaponTextMapHash[obj.WeaponType]]; + data.body = obj.BodyType.slice(obj.BodyType.indexOf('BODY_')+5); + data.rarity = obj.QualityType === 'QUALITY_PURPLE' ? '4' : '5'; + data.birthmonth = extra.InfoBirthMonth; + data.birthday = extra.InfoBirthDay; + data.affiliation = isPlayer(obj) ? '' : language[extra.AvatarNativeTextMapHash]; + data.element = language[extra.AvatarVisionBeforTextMapHash]; + data.constellation = language[extra.AvatarConstellationBeforTextMapHash]; + if(obj.Id === 10000030) data.constellation = language[extra.AvatarConstellationAfterTextMapHash]; // Zhongli exception + data.title = language[extra.AvatarTitleTextMapHash]; + data.association = extra.AvatarAssocType.slice(extra.AvatarAssocType.indexOf('TYPE_')+5); + data.cv = { + english: language[extra.CvEnglishTextMapHash], + chinese: language[extra.CvChineseTextMapHash], + japanese: language[extra.CvJapaneseTextMapHash], + korean: language[extra.CvKoreanTextMapHash] + }; + + const xsubstat = getExcel('AvatarPromoteExcelConfigData'); + const xmanualtext = getExcel('ManualTextMapConfigData'); + + let substat = xsubstat.find(ele => ele.AvatarPromoteId === obj.AvatarPromoteId).AddProps[3].PropType; + data.substat = language[xmanualtext.find(ele => ele.TextMapId === substat).TextMapContentTextMapHash]; + + data.icon = obj.IconName; + data.sideicon = obj.SideIconName; + + // INFORMATION TO CALCULATE STATS AT EACH LEVEL + let stats = { base: {}, curve: {} }; + stats.base.hp = obj.HpBase; + stats.base.attack = obj.AttackBase; + stats.base.defense = obj.DefenseBase; + stats.base.critrate = obj.Critical; + stats.base.critdmg = obj.CriticalHurt; + + stats.curve.hp = obj.PropGrowCurves.find(ele => ele.Type === 'FIGHT_PROP_BASE_HP').GrowCurve; + stats.curve.attack = obj.PropGrowCurves.find(ele => ele.Type === 'FIGHT_PROP_BASE_ATTACK').GrowCurve; + stats.curve.defense = obj.PropGrowCurves.find(ele => ele.Type === 'FIGHT_PROP_BASE_DEFENSE').GrowCurve; + stats.specialized = substat; + stats.promotion = xsubstat.reduce((accum, ele) => { + if(ele.AvatarPromoteId !== obj.AvatarPromoteId) return accum; + let promotelevel = ele.PromoteLevel || 0; + accum[promotelevel] = { + maxlevel: ele.UnlockMaxLevel, + hp: ele.AddProps.find(ele => ele.PropType === 'FIGHT_PROP_BASE_HP').Value || 0, + attack: ele.AddProps.find(ele => ele.PropType === 'FIGHT_PROP_BASE_ATTACK').Value || 0, + defense: ele.AddProps.find(ele => ele.PropType === 'FIGHT_PROP_BASE_DEFENSE').Value || 0, + specialized: ele.AddProps.find(ele => ele.PropType === substat).Value || 0, + }; + return accum; + }, []); + data.stats = stats; + + accum[avatarIdToFileName[obj.Id]] = data; + return accum; + }, {}) + return myavatar; +} + +module.exports = collateCharacter; \ No newline at end of file diff --git a/myscripts/collateConstellation.js b/myscripts/collateConstellation.js new file mode 100644 index 000000000..efdc3181b --- /dev/null +++ b/myscripts/collateConstellation.js @@ -0,0 +1,42 @@ +const xconstellation = getExcel('AvatarTalentExcelConfigData'); + +function collateConstellation(lang) { + const language = getLanguage(lang); + let myconstellation = xplayableAvatar.reduce((accum, obj) => { + // bad practice to declare functions inside loop but i need to be able to call it multiple times for players + function dowork() { + let data = {}; + let depot = xskilldepot.find(ele => ele.Id === obj.SkillDepotId); + if(depot === undefined || depot.EnergySkill === undefined) return; // not a finished (traveler) character + if(depot.TalentStarName === '') return; // unfinished + + data.name = language[obj.NameTextMapHash]; + if(isPlayer(obj)) data.name += ` (${language[elementTextMapHash[getPlayerElement(obj.SkillDepotId)]]})` + //console.log(depot) + data.images = {}; + let stars = depot.Talents.map(talentId => xconstellation.find(ele => ele.TalentId === talentId)); + for(let i = 1; i <= 6; i++) { + data['c'+i] = { + name: language[stars[i-1].NameTextMapHash], + effect: sanitizeDescription(language[stars[i-1].DescTextMapHash]) + }; + data.images['c'+i] = `https://upload-os-bbs.mihoyo.com/game_record/genshin/constellation_icon/${stars[i-1].Icon}.png`; + } + + accum[avatarIdToFileName[isPlayer(obj) ? obj.SkillDepotId : obj.Id]] = data; + } + + if(isPlayer(obj)) { + obj.CandSkillDepotIds.forEach(ele => { + obj.SkillDepotId = ele; + dowork(); + }); + } else { + dowork(); + } + return accum; + }, {}); + return myconstellation; +} + +module.exports = collateConstellation; \ No newline at end of file diff --git a/myscripts/collateFood.js b/myscripts/collateFood.js new file mode 100644 index 000000000..f48b234ef --- /dev/null +++ b/myscripts/collateFood.js @@ -0,0 +1,98 @@ +const xrecipe = getExcel('CookRecipeExcelConfigData'); +const xmaterial = getExcel('MaterialExcelConfigData'); +const xsource = getExcel('MaterialSourceDataExcelConfigData'); +const xspecialty = getExcel('CookBonusExcelConfigData'); +const xavatar = getExcel('AvatarExcelConfigData'); + +function getSpecialty(id) { return xspecialty.find(ele => ele.RecipeId === id); } +function getMaterial(id) { return xmaterial.find(ele => ele.Id === id); } +function getAvatar(id) { return xavatar.find(ele => ele.Id === id); } +function getManualTextMapHash(id) { + if(id === 'COOK_FOOD_DEFENSE') id = 'COOK_FOOD_DEFENCE'; + return xmanualtext.find(ele => ele.TextMapId === id).TextMapContentTextMapHash; +} + +const mapQualityToProp = { + 'FOOD_QUALITY_STRANGE': 'suspicious', + 'FOOD_QUALITY_ORDINARY': 'normal', + 'FOOD_QUALITY_DELICIOUS': 'delicious', +} + +function collateFood(lang) { + const language = getLanguage(lang); + + let myfood = xrecipe.reduce((accum, obj) => { + //if(obj.Id !== 1003) return accum; + + let data = {}; + + data.name = language[obj.NameTextMapHash]; + data.rarity = obj.RankLevel; + data.foodtype = 'NORMAL'; + data.foodfilter = language[getManualTextMapHash(obj.FoodType)]; + data.foodcategory = undefined; + data.effect = obj.EffectDesc.reduce((accum, eff) => { + const tmp = stripHTML(language[eff]); + if(tmp) accum.push(tmp); + return accum; + }, []).join('\n'); + data.description = sanitizeDescription(language[obj.DescTextMapHash]); + // check error + for(let i = 2; i <= 3; i++) { const tmp = language[obj.EffectDesc[i]]; if(tmp) console.log(`${obj.Id} ${data.name}: ${tmp}`); } + + // get suspicious, normal, delicious + for(let xd of obj.QualityOutputVec) { + xd = getMaterial(xd.Id); + let subdata = {}; + if(language[xd.InteractionTitleTextMapHash]) console.log(`food ${obj.Id} has interaction`); + if(language[xd.SpecialDescTextMapHash]) console.log(`food ${obj.Id} has special`); + subdata.effect = language[xd.EffectDescTextMapHash]; + subdata.description = sanitizeDescription(language[xd.DescTextMapHash]); + data[mapQualityToProp[xd.FoodQuality]] = subdata; + data.foodcategory = xd.EffectIcon.substring(13); + } + data.ingredients = obj.InputVec.reduce((accum, ing) => { + if(ing.Id === undefined) return accum; + const mat = getMaterial(ing.Id); + accum.push({ name: language[mat.NameTextMapHash], count: ing.Count }); + return accum; + }, []); + // data.source = + + accum[makeFileName(getLanguage('EN')[obj.NameTextMapHash])] = data; + + // check if there is a specialty + let myspec = getSpecialty(obj.Id); + if(myspec === undefined) return accum; + let xd = getMaterial(myspec.ParamVec[0]); + // if(xd === undefined) return accum; + let foodfilter = data.foodfilter; + let basedish = data.name; + let ingredients = data.ingredients; + + data = {}; + data.name = language[xd.NameTextMapHash]; + data.rarity = xd.RankLevel; + data.foodtype = 'SPECIALTY'; + data.foodfilter = foodfilter; + data.foodcategory = xd.EffectIcon.substring(13); + + if(language[xd.InteractionTitleTextMapHash]) console.log(`specialty ${obj.Id} has interaction`); + if(language[xd.SpecialDescTextMapHash]) console.log(`specialty ${obj.Id} has special`); + data.effect = language[xd.EffectDescTextMapHash]; + data.description = sanitizeDescription(language[xd.DescTextMapHash]); + + data.basedish = basedish; + data.character = language[getAvatar(myspec.AvatarId).NameTextMapHash]; + + data.ingredients = ingredients; + + accum[makeFileName(getLanguage('EN')[obj.NameTextMapHash])] = data; + return accum; + }, {}); + // console.log(myfood); + + return myfood; +} + +module.exports = collateFood; \ No newline at end of file diff --git a/myscripts/collateTalent.js b/myscripts/collateTalent.js new file mode 100644 index 000000000..7139e13a7 --- /dev/null +++ b/myscripts/collateTalent.js @@ -0,0 +1,61 @@ +const xtalent = getExcel('AvatarSkillExcelConfigData'); // combat talents +const xpassive = getExcel('ProudSkillExcelConfigData'); // passive talents + +// object map that converts index to the talent type +const talentCombatTypeMap = { '0': 'combat1', '1': 'combat2', '2': 'combatsp', '4': 'combat3' }; + +function collateTalent(lang) { + const language = getLanguage(lang); + let mytalent = xplayableAvatar.reduce((accum, obj) => { + // bad practice to declare functions inside loop but i need to be able to call it multiple times for players + function dowork() { + let data = {}; + let depot = xskilldepot.find(ele => ele.Id === obj.SkillDepotId); + if(depot === undefined || depot.EnergySkill === undefined) return; // not a finished (traveler) character + if(depot.TalentStarName === '') return; // unfinished + + data.name = language[obj.NameTextMapHash]; // client-facing name + if(isPlayer(obj)) data.name += ` (${language[elementTextMapHash[getPlayerElement(obj.SkillDepotId)]]})` + + let combat = depot.Skills.concat([depot.EnergySkill]) // get array of combat skills IDs + let passive = depot.InherentProudSkillOpens.reduce((accum2, proud) => { // get array of passive skill IDs + if(proud.ProudSkillGroupId) accum2.push(proud.ProudSkillGroupId); + return accum2; + }, []) + + combat.forEach((skId, index) => { + if(skId === 0) return; + let talent = xtalent.find(tal => tal.Id === skId); + let ref = data[talentCombatTypeMap[index]] = {}; + + ref.name = language[talent.NameTextMapHash]; + let desc = language[talent.DescTextMapHash].split('\\n\\n'); // extract out the italicized part + ref.info = sanitizeDescription(desc[0]); + if(desc[1]) ref.description = sanitizeDescription(desc[1]); + }); + + passive.forEach((skId, index) => { + let talent = xpassive.find(pas => pas.ProudSkillGroupId === skId); + let ref = data['passive'+(index+1)] = {}; // store reference in variable to make it easier to access + + ref.name = language[talent.NameTextMapHash]; + ref.info = sanitizeDescription(language[talent.DescTextMapHash]); + }); + + accum[avatarIdToFileName[isPlayer(obj) ? obj.SkillDepotId : obj.Id]] = data; + } + + if(isPlayer(obj)) { + obj.CandSkillDepotIds.forEach(ele => { + obj.SkillDepotId = ele; + dowork(); + }); + } else { + dowork(); + } + return accum; + }, {}); + return mytalent; +} + +module.exports = collateTalent; \ No newline at end of file diff --git a/myscripts/collateWeapon.js b/myscripts/collateWeapon.js new file mode 100644 index 000000000..d5fa14ca0 --- /dev/null +++ b/myscripts/collateWeapon.js @@ -0,0 +1,93 @@ +const xweapon = getExcel('WeaponExcelConfigData'); +const xrefine = getExcel('EquipAffixExcelConfigData'); + +const xplayableWeapon = xweapon.filter(obj => { + if(obj.RankLevel >= 3 && obj.SkillAffix[0] === 0) return false; + if(obj.SkillAffix[1] !== 0) { console.log('danger'); return false }; + if(getLanguage('EN')[obj.NameTextMapHash] === '') return false; + return true; +}); + + + +function collateWeapon(lang) { + const language = getLanguage(lang); + const xsubstat = getExcel('WeaponPromoteExcelConfigData'); + let myweapon = xplayableWeapon.reduce((accum, obj) => { + + let data = {}; + + data.name = language[obj.NameTextMapHash]; + data.description = language[obj.DescTextMapHash]; + data.weapontype = language[weaponTextMapHash[obj.WeaponType]]; + data.rarity = ''+obj.RankLevel; + + if(obj.WeaponProp[0].PropType !== 'FIGHT_PROP_BASE_ATTACK') console.log(obj,'weapon did not find base atk'); + data.baseatk = obj.WeaponProp.find(obj => obj.PropType === 'FIGHT_PROP_BASE_ATTACK').InitValue; + + let substat = obj.WeaponProp[1].PropType; + if(substat !== undefined) { + data.substat = language[xmanualtext.find(ele => ele.TextMapId === substat).TextMapContentTextMapHash]; + let subvalue = obj.WeaponProp[1].InitValue; + data.subvalue = subvalue; + } + + if(obj.SkillAffix[0] !== 0) { + let affixId = obj.SkillAffix[0] * 10; + for(let offset = 0; offset < 5; offset++) { + let ref = xrefine.find(ele => ele.AffixId === affixId+offset); + if(ref === undefined) break; + if(offset === 0) data.effectname = language[ref.NameTextMapHash]; + let effect = language[ref.DescTextMapHash]; + effect = effect.replace(//gi, '{').replace(/<\/color>/gi, '}'); + effect = effect.split(/{|}/); + data['r'+(offset+1)] = []; + data['effect'] = effect.reduce((accum, ele, i) => { + if(i % 2 === 0) { + return accum + ele; + } else { + data['r'+(offset+1)].push(ele); + return accum + `{${(i-1)/2}}`; + } + }, ''); + } + } + + // INFORMATION TO CALCULATE STATS AT EACH LEVEL + let stats = { base: {}, curve: {} }; + stats.base.attack = obj.WeaponProp[0].InitValue; + stats.base.specialized = obj.WeaponProp[1].InitValue || 0; + + stats.curve.attack = obj.WeaponProp[0].Type; + stats.curve.specialized = obj.WeaponProp[1].Type; + stats.specialized = substat; + stats.promotion = xsubstat.reduce((accum, ele) => { + if(ele.WeaponPromoteId !== obj.WeaponPromoteId) return accum; + let promotelevel = ele.PromoteLevel || 0; + accum[promotelevel] = { + maxlevel: ele.UnlockMaxLevel, + attack: ele.AddProps.find(ele => ele.PropType === 'FIGHT_PROP_BASE_ATTACK').Value || 0 + }; + let special = ele.AddProps.find(ele => ele.PropType === substat);//.Value; + if(special) special = special.Value; + if(special !== undefined) { + console.log('WEAPON SPECIAL SUBSTAT FOUND: ' + obj.Id) + accum[promotelevel].specialized = special; + } + return accum; + }, []) + data.stats = stats; + + data.icon = obj.Icon; + data.awakenicon = obj.AwakenIcon; + + let filename = makeFileName(getLanguage('EN')[obj.NameTextMapHash]); + if(accum[filename] !== undefined) console.log(filename+' IS NOT UNIQUE'); + accum[filename] = data; + return accum; + }, {}); + + return myweapon; +} + +module.exports = collateWeapon; \ No newline at end of file diff --git a/myscripts/myscript.js b/myscripts/myscript.js new file mode 100644 index 000000000..b92237f83 --- /dev/null +++ b/myscripts/myscript.js @@ -0,0 +1,110 @@ +const fs = require('fs'); + +global.getExcel = function(file) { return require(`../ExcelBinOutput/${file}.json`); } +global.getTextMap = function(langcode) { return require(`../TextMap/Text${langcode}.json`); } + +const xavatar = getExcel('AvatarExcelConfigData'); // array + +global.xskilldepot = getExcel('AvatarSkillDepotExcelConfigData'); + +global.xmanualtext = getExcel('ManualTextMapConfigData'); + +const langcodes = ['CHS', 'CHT', 'DE', 'EN', 'ES', 'FR', 'ID', 'JA', 'KO', 'PT', 'RU', 'TH', 'VI']; + +/* ========================================================================================== */ + + +// const weaponIdToFileName = xweapon.reduce((accum, obj) => { +// accum[obj.Id] = + +// }, {}) + + +// UNUSED object map that converts AvatarAssocType into a TextMapHash +const assocTextMapHash = ['ASSOC_TYPE_MONDSTADT', 'ASSOC_TYPE_LIYUE', 'ASSOC_TYPE_FATUI']; + +global.isPlayer = function(data) { return data.CandSkillDepotIds.length !== 0; } +global.getPlayerElement = function(SkillDepotId) { let tmp = xskilldepot.find(ele => ele.Id === SkillDepotId); return tmp === undefined ? tmp : tmp.TalentStarName.split('_').pop(); } +global.getLanguage = function(abbriev) { return getTextMap(abbriev.toUpperCase()); } +global.normalizeStr = function(str) { return str.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); } +global.makeFileName = function(str, lang) { return normalizeStr(str).toLowerCase().replace(/[^a-z]/g,''); } +global.convertBold = function(str) { return str.replace(/(.*?)<\/color>/gi, '**$1**'); } +global.stripHTML = function(str) { return (str || '').replace(/(<([^>]+)>)/gi, ''); } +global.capitalizeFirst = function(str) { return str[0].toUpperCase() + str.toLowerCase().slice(1); } +global.replaceLayout = function(str) { return str.replace(/{LAYOUT_MOBILE#Tap}{LAYOUT_PC#Press}{LAYOUT_PS#Press}/gi,'Press').replace('#',''); } +global.replaceNewline = function(str) { return str.replace(/\\n/gi, '\n'); } +global.sanitizeDescription = function(str) { return replaceNewline(replaceLayout(stripHTML(convertBold(str)))); } + +/* ======================================================================================= */ + +// object map that converts the genshin coded element into a TextMapHash +global.elementTextMapHash = ['Fire', 'Water', 'Grass', 'Electric', 'Wind', 'Ice', 'Rock'].reduce((accum, element) => { + accum[element] = xmanualtext.find(ele => ele.TextMapId === element).TextMapContentTextMapHash; + return accum; +}, {}); + +global.xplayableAvatar = xavatar.filter(obj => obj.AvatarPromoteId !== 2); // array +// object map that converts an avatar Id or traveler SkillDepotId to filename +global.avatarIdToFileName = xplayableAvatar.reduce((accum, obj) => { + if(obj.Id === 10000005) accum[obj.Id] = 'aether'; + else if(obj.Id === 10000007) accum[obj.Id] = 'lumine'; + else accum[obj.Id] = makeFileName(getLanguage('EN')[obj.NameTextMapHash]); + if(isPlayer(obj)) { // + obj.CandSkillDepotIds.forEach(skdeId => { + let trelement = elementTextMapHash[getPlayerElement(skdeId)]; + if(trelement === undefined) return; + accum[skdeId] = makeFileName(getLanguage('EN')[obj.NameTextMapHash] + getLanguage('EN')[trelement]); + }) + } + return accum; +}, {}); + +// object map that converts a WeaponType into a TextMapHash +global.weaponTextMapHash = ['WEAPON_SWORD_ONE_HAND', 'WEAPON_CATALYST', 'WEAPON_CLAYMORE', 'WEAPON_BOW', 'WEAPON_POLE'].reduce((accum, str) => { + accum[str] = xmanualtext.find(ele => ele.TextMapId === str).TextMapContentTextMapHash; + return accum; +}, {}); + +/* =========================================================================================== */ + +function exportCurve(folder, file) { + const xcurve = getExcel(file); + let output = {}; + xcurve.forEach(ele => { + let curveinfo = {}; + ele.CurveInfos.forEach(ele => { + curveinfo[ele.Type] = ele.Value; + }); + output[ele.Level] = curveinfo; + }); + fs.mkdirSync(`./export/curve`, { recursive: true }); + fs.writeFileSync(`./export/curve/${folder}.json`, JSON.stringify(output, null, '\t')); +} + +function exportData(folder, collateFunc, englishonly, skipwrite) { + langcodes.forEach(lang => { + if(englishonly && lang !== 'EN') return; + let data = collateFunc(lang); + fs.mkdirSync(`./export/${lang}`, { recursive: true }); + if(!skipwrite) { + fs.writeFileSync(`./export/${lang}/${folder}.json`, JSON.stringify(data, null, '\t')); + if(JSON.stringify(data).search('undefined') !== -1) console.log('undefined found in '+folder); + } + }); + console.log("done "+folder); +} + +// exportData('characters', require('./collateCharacter.js')); +// exportCurve('characters', 'AvatarCurveExcelConfigData'); +// exportData('constellations', require('./collateConstellation')); +// exportData('talents', require('./collateTalent.js')); +// exportData('weapons', require('./collateWeapon.js')); +// exportCurve('weapons', 'WeaponCurveExcelConfigData') +// exportData('artifacts', require('./collateArtifact.js')); +exportData('foods', require('./collateFood')); + +//console.log(collateCharacter('EN')) +//console.log(collateConstellation('EN').hutao) +//console.log(collateTalent('EN').mona) +//console.log(collateWeapon('EN')); +// console.log(collateArtifact('EN'));