const fs = require('fs'); const crypto = require('crypto'); const Card = require('./official-API/parseCard'); const Skill = require('./official-API/parseSkill'); const runDate = new Date(); const officialAPI = [ //来源于官方API { code: "ja", customName: ["cht", "chs"] }, { code: "en", customName: [] }, { code: "ko", customName: [] } ]; //数组去重 Array.prototype.distinct = function () { let _set = new Set(this); return Array.from(_set) } //比较两只怪物是否是同一只(在不同语言服务器) function sameCard(m1, m2) { if (m1 == undefined || m2 == undefined) return false; //是否存在 if (m1.attrs[0] != m2.attrs[0]) return false; //主属性 //if (m1.attrs[1] != m2.attrs[1]) return false; //副属性 //全部类型有任意不同的时候也返回false,但是考虑到上修,不要求总数一致 for (let i = 0; i < Math.min(m1.types.length, m2.types.length); i++) { if (m1.types[i] != m2.types[i]) return false; } if (m1.maxLevel != m2.maxLevel) return false; //最大等级 if (m1.seriesId != m2.seriesId) return false; //系列ID return true; } //查找到真正起作用的那一个技能 function getActuallySkills(skillsDataset, skillid, skillTypes, searchRandom = true) { const skill = skillsDataset[skillid]; if (skill) { if (skillTypes.includes(skill.type)) { return [skill]; } else if (skill.type == 116 || //主动技 (searchRandom && skill.type == 118) || //随机主动技 skill.type == 138 || //队长技 skill.type == 232 || //进化技能1,不循环 skill.type == 233) { //进化技能2,循环 //因为可能有多层调用,特别是随机118再调用组合116的,所以需要递归 let subSkills = skill.params.flatMap(id => getActuallySkills(skillsDataset, id, skillTypes, searchRandom)); subSkills = subSkills.filter(s => s); return subSkills; } else { return []; } } else { return []; } } /* * 正式流程 */ officialAPI.forEach(function (lang) { console.log("正在读取官方 %s 信息", lang.code); const cardJson = fs.readFileSync("official-API/" + lang.code + "-card.json", 'utf-8'); //使用同步读取怪物 const cardJsonObj = JSON.parse(cardJson); const oCards = lang.cardOriginal = cardJsonObj.card;//将字符串转换为json对象 const monCards = lang.cards = []; //const monCards = lang.cards = oCards.map(ocard=>new Card(ocard)); /*oCards.forEach((oCard, idx)=>{ let mid = oCard[0]; if (mid === idx) //原始怪物 { monCards.push(new Card(oCard)); }else { const nCard = monCards[mid % 100000]; if (!nCard.specialVersion) nCard.specialVersion = {}; nCard.specialVersion[Math.floor(mid / 100000)] = new Card(oCard); } });*/ //为了减小数据文件容量,删掉一些用不上的,有的可以后面游戏内再重新加上 for (let cardIndex = 0; oCards[cardIndex][0] < 1e5; cardIndex++) { const card = new Card(oCards[cardIndex]); delete card.enemy; //不做地下城解析,用不上 delete card.onlyAssist; //数量太少了,游戏内再加 delete card.unk01; delete card.unk02; delete card.unk03; delete card.unk04; delete card.unk05; delete card.unk06; delete card.unk07; delete card.unk08; if (card.searchFlags.every(num => isNaN(num))) delete card.searchFlags; //没有队长技能的,用不上 if (!card.syncAwakening) delete card.syncAwakeningConditions; //没有同步觉醒 monCards.push(card); } //加入自定义的语言 lang.customName.forEach(function (lcode) { console.log("正在读取自定义 " + lcode + " 信息"); const ljson = fs.readFileSync("custom/" + lcode + ".json", 'utf-8'); //使用同步读取 const ccard = JSON.parse(ljson);//将字符串转换为json对象 ccard.forEach(function (cm) { //每个文件内的名字循环 let m = monCards[cm.id]; if (m) { if (!m.otLangName) //如果没有其他语言名称属性,则添加一个对象属性 m.otLangName = {}; m.otLangName[lcode] = cm.name; if (!m.otTags) //如果没有其他语言名称属性,则添加一个对象属性 m.otTags = []; let newTags = Array.from(new Set(cm.tags)); newTags = newTags.filter(tag => !m.altName.includes(tag) && !m.otTags.includes(tag)); m.otTags.push(...newTags); } }); }); const skillJson = fs.readFileSync("official-API/" + lang.code + "-skill.json", 'utf-8'); //使用同步读取技能 const skillJsonObj = JSON.parse(skillJson); const oSkills = lang.skillOriginal = skillJsonObj.skill;//将字符串转换为json对象 lang.skills = oSkills.map((oc, idx) => new Skill(idx, oc)); //每一项生成分析对象 lang.cards.forEach((m, cardId, cardArr) => { let henshinTo = null; const henshinSkill = getActuallySkills(lang.skills, m.activeSkillId, [202, 236]); if (henshinSkill.length) { const skill = henshinSkill[0]; henshinTo = skill.params.distinct(); if (Array.isArray(henshinTo)) { m.henshinTo = henshinTo; //变身来源可能有多个,因此将变身来源修改为数组 for (let toId of henshinTo) { let henshinFrom = cardArr[toId].henshinFrom; if (Array.isArray(henshinFrom)) { henshinFrom.push(cardId); } else { cardArr[toId].henshinFrom = [cardId]; } } } } }); //const eskillJson = fs.readFileSync("official-API/" + lang.code +"-enemy_skill.json", 'utf-8'); //使用同步读取技能 //const eskillJsonObj = JSON.parse(eskillJson); //lang.enemy_skills = eskillJsonObj.enemy_skills; }); //加入其他服务器相同角色的名字 for (let li = 0; li < officialAPI.length; li++) { const otherLangs = officialAPI.concat(); //复制一份原始数组,储存其他语言 const lang = otherLangs.splice(li, 1)[0]; //删掉并取得当前的语言 const langCard = lang.cards; const langCardCount = langCard.length; for (let mi = 0; mi < langCardCount; mi++) { const m = langCard[mi]; const name = m.name; //当前语言的名字 //名字对象 otherLangs.forEach((otLang) => { let _m = otLang.cards[mi]; //获得这种其他语言的当前这个怪物数据 let isSame = false; //与原语言怪物是否是同一只 const l1 = lang.code, l2 = otLang.code; if ( l1 == 'ja' && (l2 == 'en' || l2 == 'ko') || l2 == 'ja' && (l1 == 'en' || l1 == 'ko') ) { //当同id两者不同,日服和英韩服比较时的一些人工确认相同的特殊id差异卡片 const langIsJa = l1 == 'ja' ? true : false; //原始语言是否是日语 let diff = 0; //日语和其它语言的id差异 switch (true) { case (langIsJa && mi >= 671 && mi <= 680) || (!langIsJa && mi >= 1049 && mi <= 1058): //神罗 日服 671-680 等于英韩服 1049-1058 diff = 378; break; case (langIsJa && mi >= 669 && mi <= 670) || (!langIsJa && mi >= 934 && mi <= 935): //神罗 日服 669-670 等于英韩服 934-935 diff = 265; break; case (langIsJa && mi >= 924 && mi <= 935) || (!langIsJa && mi >= 669 && mi <= 680): //蝙蝠侠 日服 924-935 等于英韩服 669-680 diff = -255; break; case (langIsJa && mi >= 1049 && mi <= 1058) || (!langIsJa && mi >= 924 && mi <= 933): //蝙蝠侠 日服 1049-1058 等于英韩服 924-933 diff = -125; break; } if (diff != 0) { _m = langIsJa ? otLang.cards[mi + diff] : otLang.cards[mi - diff]; isSame = true; } } if (!isSame) isSame = sameCard(m, _m); //不属于特殊指定区间的情况下,判断与原语言怪物是否是同一只 if (_m && isSame) //如果有这个怪物,且与原语言怪物是同一只 { const otName = _m.name; if (/^(?:\?+|\*+|초월\s*\?+)/i.test(otName)) //名字以问号、星号、韩文的问号开头 { return; //跳过 } else { if (!m.otLangName) //如果没有其他语言名称属性,则添加一个对象属性 m.otLangName = {}; m.otLangName[otLang.code] = otName; if (_m.otLangName) { //增加储存当前语言的全部其他语言 Object.entries(_m.otLangName).forEach(entry => { const lcode = entry[0]; if (lcode != l1 && !Object.keys(m.otLangName).includes(lcode)) //如果不是本来的的语言 m.otLangName[lcode] = entry[1]; }); } } if (!m.otTags) //如果没有其他语言标签属性,则添加一个数组属性 m.otTags = []; let otTags = Array.from(new Set(_m.otTags ? _m.altName.concat(_m.otTags) : _m.altName)); otTags = otTags.filter(tag => !m.altName.includes(tag) && !m.otTags.includes(tag)); m.otTags.push(...otTags); } }); } } var newCkeyObjs = officialAPI.map(lang => { const lcode = lang.code; const cardStr = JSON.stringify(lang.cards); const skillStr = JSON.stringify(lang.skills); //写入Cards fs.writeFile(`./mon_${lcode}.json`, cardStr, function (err) { if (err) { console.error(err); } console.log(`mon_${lcode}.json 导出成功`); }); //写入Skills fs.writeFile(`./skill_${lcode}.json`, skillStr, function (err) { if (err) { console.error(err); } console.log(`skill_${lcode}.json 导出成功`); }); //const enemy_skillsStr = lang.enemy_skills; //写入enemy_skills //fs.writeFile(`./enemy_skills_${lcode}.json`,enemy_skillsStr,function(err){ // if(err){ // console.error(err); // } // console.log(`enemy_skills_${lcode}.json 导出成功`); //}); const cardHash = crypto.createHash('md5'); const skillHash = crypto.createHash('md5'); cardHash.update(cardStr, 'utf8'); skillHash.update(skillStr, 'utf8'); const ckeyOutObj = { code: lcode, ckey: { card: cardHash.digest('hex'), skill: skillHash.digest('hex'), }, updateTime: runDate.getTime(), }; return ckeyOutObj; }); //读取旧的ckeyObj var ckeyObjs; fs.readFile('./ckey.json', 'utf-8', function (err, data) { if (err) { //如果读取错误,直接使用全新ckey ckeyObjs = newCkeyObjs; } else { //如果读取正确,则读入JSON,并判断是否和旧有的一致 ckeyObjs = JSON.parse(data); for (let ci = 0; ci < ckeyObjs.length; ci++) { const oldCkey = ckeyObjs[ci]; const newCkey = newCkeyObjs.find(nckey => nckey.code === oldCkey.code); if (newCkey && (oldCkey.ckey.card != newCkey.ckey.card || oldCkey.ckey.skill != newCkey.ckey.skill)) { ckeyObjs[ci] = newCkey; } } } fs.writeFile('./ckey.json', JSON.stringify(ckeyObjs, null, '\t'), function (err) { if (err) { console.error(err); } console.log('ckey.json 导出成功'); }); });