Created
June 18, 2020 04:34
-
-
Save HoeenCoder/330a9533ad392c6ba4107172be05e65e to your computer and use it in GitHub Desktop.
Script for converting raw datamine & existing learnsets.ts file to new learnsets.ts - Mess atm, want to improve later as its too brittle for me.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {FS} from '../../lib/fs'; | |
// import {BattleLearnsets} from '../../data/learnsets'; | |
const BattleLearnsets = require('../../.data-dist/learnsets').BattleLearnsets; | |
// TO HANDLE | |
/** | |
* "1" versions of pokemon | |
* different pokemon stages | |
* ??? | |
*/ | |
interface DLCData { | |
species: string; | |
num: number; | |
baseStats: StatsTable; | |
genderRatio?: {M: number, F: number}; | |
gender?: 'M' | 'F' | 'N'; | |
abilities: SpeciesAbility; | |
types: string[]; | |
eggGroups: string[]; | |
weightkg: number; | |
color: string; | |
heightm: number; | |
movepool: LearnsetData; | |
} | |
const rawData = FS('dlc_dump.txt').readIfExistsSync(); | |
if (!rawData) throw new Error(`Raw data for parsing not found.`); | |
const lines = rawData.split('\n').map(l => l.trim()); | |
const data: {[species: string]: DLCData} = {}; | |
function parseRawData() { | |
let currentSpecies = ''; | |
let currentData: Partial<DLCData> = {}; | |
let inHeader = false; | |
let lsetData: {[moveid: string]: MoveSource[]} = {}; | |
let lsetArea = ''; | |
const skipable = ['Galar Dex', 'EV Yield', 'Catch Rate', 'Item', 'EXP Group', 'Hatch Cycles']; | |
for (const line of lines) { | |
// console.log(line); | |
if (line === '======') { | |
inHeader = !inHeader; | |
continue; | |
} | |
if (inHeader) { | |
// 001 - Bulbasaur (Stage: 1) | |
currentData.num = parseInt(line.split('-')[0]); | |
currentSpecies = line.split('-')[1].trim(); | |
currentSpecies = currentSpecies.substring(0, currentSpecies.indexOf('(')).trim(); | |
const alola = ['Raichu', 'Sandshrew', 'Sandslash', 'Vulpix', 'Ninetales', 'Diglett', 'Dugtrio', 'Persian', | |
'Exeggutor', 'Marowak']; | |
const galar = ['Ponyta', 'Rapidash', 'Slowpoke', 'Slowbro', 'Farfetch’d', 'Weezing', 'Mr. Mime', 'Corsola', | |
'Zigzagoon', 'Linoone', 'Darumaka', 'Yamask', 'Stunfisk']; | |
// TODO special case for meowth & darmanitans because reasons | |
if (!Dex.getSpecies(currentSpecies).exists) { | |
const baseSpecies = currentSpecies.substring(0, currentSpecies.length - 2).trim(); | |
if (Dex.getSpecies(baseSpecies).exists) { | |
if (alola.includes(baseSpecies)) currentSpecies = baseSpecies + '-Alola'; | |
if (galar.includes(baseSpecies)) currentSpecies = baseSpecies + '-Galar'; | |
if (baseSpecies === 'Meowth') { | |
if (currentSpecies.endsWith('1')) { | |
currentSpecies = baseSpecies + '-Alola'; | |
} else { | |
currentSpecies = baseSpecies + '-Galar'; | |
} | |
} else if (baseSpecies === 'Darmanitan') { | |
if (currentSpecies.endsWith('1')) { | |
// Zen - Ignore | |
} else if (currentSpecies.endsWith('2')) { | |
currentSpecies = baseSpecies + '-Galar'; | |
} else { | |
// Zen - Ignore | |
} | |
} else if (baseSpecies === 'Urshifu') { | |
currentSpecies = baseSpecies + '-Rapid-Strike'; | |
} else if (baseSpecies === 'Zarude') { | |
currentSpecies = baseSpecies + '-Dada'; | |
} else if (baseSpecies === 'Toxtricity') { | |
currentSpecies = baseSpecies + '-Low-Key'; | |
} | |
} | |
} | |
currentData.species = currentSpecies; | |
// console.log(`Species: ${currentSpecies}`); | |
continue; | |
} | |
let skipLine = false; | |
for (const skip of skipable) { | |
if (line.startsWith(skip)) { | |
skipLine = true; | |
break; | |
} | |
} | |
if (skipLine) continue; | |
if (line.startsWith('Base Stats:')) { | |
const rawStats = line.match(/(\d{1,3}\.?){6}/); | |
if (!rawStats) throw new Error(`Unable to extract stats from line: ${line}, result: ${rawStats}`); | |
const stats: {[stat: string]: number} = {}; | |
const indexToStat: {[string: string]: string} = { | |
'0': 'hp', | |
'1': 'atk', | |
'2': 'def', | |
'3': 'spa', | |
'4': 'spd', | |
'5': 'spe', | |
}; | |
rawStats[0].split('.').map((stat, index) => { | |
const statName = indexToStat[('' + index)]; | |
if (!statName) throw new Error(`Could not find stat for index ${index}`); | |
stats[statName] = parseInt(stat); | |
return ''; | |
}); | |
currentData.baseStats = (stats as StatsTable); | |
continue; | |
} else if (line.startsWith('Gender Ratio:')) { | |
const ratio = line.split(':')[1].trim(); | |
switch (ratio) { | |
case '31': | |
// 7/8 M, 1/8 F | |
currentData.genderRatio = {M: 0.875, F: 0.125}; | |
break; | |
case '255': | |
// N | |
currentData.gender = 'N'; | |
break; | |
case '127': | |
// 50/50 | |
currentData.genderRatio = {M: 0.5, F: 0.5}; | |
break; | |
case '254': | |
// F | |
currentData.gender = 'F'; | |
break; | |
case '0': | |
// M | |
currentData.gender = 'M'; | |
break; | |
case '191': | |
// 3/4 F, 1/4 M | |
currentData.genderRatio = {M: 0.25, F: 0.75}; | |
break; | |
case '63': | |
// 1/4 F, 3/4 M | |
currentData.genderRatio = {M: 0.75, F: 0.25}; | |
break; | |
default: | |
throw new Error(`Unexpected gender ratio for ${currentSpecies}: ${ratio}`); | |
} | |
continue; | |
} else if (line.startsWith('Abilities:')) { | |
// Abilities: Thick Fat (1) | Huge Power (2) | Sap Sipper (H) | |
const parts = line.slice(9).split('|').map(p => p.trim()); | |
const abilities: {[key: string]: string} = {}; | |
for (const a of parts) { | |
const key = a.match(/(\([12H]\))/); | |
if (!key) throw new Error(`Could not find key for ability: ${a} ${key} ${currentSpecies}`); | |
const trueKey = key[0] === '2' ? '1' : (key[0] === '1' ? '0' : 'H'); | |
abilities[trueKey] = a.substring(0, a.indexOf('(')).trim(); | |
} | |
currentData.abilities = (abilities as unknown as SpeciesAbility); | |
continue; | |
} else if (line.startsWith('Type:')) { | |
const types = line.slice(5).split('/').map(t => t.trim()); | |
currentData.types = types; | |
continue; | |
} else if (line.startsWith('Egg Group:')) { | |
const groups = line.slice(10).split('/').map(t => t.trim()); | |
currentData.eggGroups = groups; | |
continue; | |
} else if (line.startsWith('Height:')) { | |
// Height: 00.60 m, Weight: 008.5 kg, Color: Red | |
const parts = line.split(',').map(p => p.trim()); | |
for (const p of parts) { | |
const subParts = p.split(':'); | |
switch (subParts[0].trim()) { | |
case 'Height': | |
currentData.heightm = parseFloat(subParts[1]); | |
break; | |
case 'Weight': | |
currentData.weightkg = parseFloat(subParts[1]); | |
break; | |
case 'Color': | |
currentData.color = subParts[1].trim(); | |
break; | |
} | |
} | |
continue; | |
} else if (line.startsWith('Level Up Moves:')) { | |
lsetArea = 'L'; | |
continue; | |
} else if (line.startsWith('Egg Moves:')) { | |
lsetArea = 'E'; | |
continue; | |
} else if (line.startsWith('TMs:') || line.startsWith('TRs:')) { | |
lsetArea = 'M'; | |
continue; | |
} else if (line.startsWith('Armor Tutors:')) { | |
lsetArea = 'T'; | |
continue; | |
} else if (!line.startsWith('- ')) { | |
// console.log(line); | |
lsetArea = ''; | |
} | |
// Learnset data or garbage data | |
// console.log(lsetArea); | |
let matches: RegExpMatchArray | null; | |
let move: string; | |
switch (lsetArea) { | |
case 'L': | |
matches = line.match(/\[(\d{2,3})\] (.*)/); | |
// [0] - full match, [1] is the level, [2] is the move | |
if (!matches) throw new Error(`Unable to match for learnset data: ${lsetArea} ${line} ${matches}`); | |
const lvl = parseInt(matches[1]); | |
move = toID(matches[2]); | |
if (!lsetData[move]) lsetData[move] = []; | |
lsetData[move].push(`8L${lvl}`); | |
break; | |
case 'E': | |
move = toID(line.slice(1)); | |
if (!lsetData[move]) lsetData[move] = []; | |
lsetData[move].push(`8E`); | |
break; | |
case 'M': | |
matches = line.match(/\[T[MR]\d{2}\] (.*)/); | |
if (!matches) throw new Error(`Unable to match for learnset data: ${lsetArea} ${line} ${matches}`); | |
move = toID(matches[1]); | |
if (!lsetData[move]) lsetData[move] = []; | |
lsetData[move].push(`8M`); | |
break; | |
case 'T': | |
move = toID(line.slice(1)); | |
if (!lsetData[move]) lsetData[move] = []; | |
lsetData[move].push(`8T`); | |
break; | |
case '': | |
if (!currentSpecies) continue; | |
currentData.movepool = {learnset: lsetData}; | |
if (data[currentSpecies]) throw new Error(`Duplicate species! ${currentSpecies}.`); | |
const spec = Dex.getSpecies(currentSpecies); | |
// console.log(spec.exists); | |
if (!spec.exists) { | |
if (!isNaN(parseInt(currentSpecies[currentSpecies.length - 1]))) { | |
console.log(`SKIP: ${currentSpecies}`); | |
currentSpecies = ''; | |
currentData = {}; | |
lsetData = {}; | |
continue; | |
} else { | |
console.log(`NEW SPECIES: ${currentSpecies}`); | |
} | |
} | |
data[toID(currentSpecies)] = (currentData as DLCData); | |
currentSpecies = ''; | |
currentData = {}; | |
lsetData = {}; | |
continue; | |
default: | |
throw new Error(`Unexpected lsetArea type: ${lsetArea} ${currentSpecies}`); | |
} | |
} | |
} | |
parseRawData(); | |
const output: {[speciesid: string]: LearnsetData} = Dex.deepClone(BattleLearnsets); | |
// Now we read learnset data and update it | |
function updateLearnsets() { | |
for (const species in data) { | |
const newData = data[species]; | |
if (!(species in output)) { | |
console.log(`NEW LEARNSET: ${species}`); | |
output[species] = newData.movepool; | |
} else { | |
if (!output[species].learnset) output[species].learnset = {}; | |
for (const move in newData.movepool.learnset) { | |
// @ts-ignore | |
const a = newData.movepool.learnset[move].concat(output[species].learnset[move]); | |
// @ts-ignore defined literally 5 lines above this | |
output[species].learnset[move] = Array.from(new Set(a)).filter(m => m); | |
} | |
} | |
} | |
console.log('updated'); | |
} | |
updateLearnsets(); | |
const WRITE_TO = 'learnsets.ts'; | |
function writeLearnsets() { | |
const outputStream = FS(WRITE_TO).createAppendStream(); | |
// Write headers | |
outputStream.writeLine('/* eslint-disable max-len */'); | |
outputStream.writeLine(''); | |
outputStream.writeLine('export const BattleLearnsets: {[speciesid: string]: LearnsetData} = {'); | |
// loop and log each species learnsets | |
for (const species in output) { | |
const block = output[species]; | |
outputStream.writeLine(`\t${species}: {`); | |
const lsets = block.learnset; | |
if (lsets) { | |
outputStream.writeLine(`\t\tlearnset: {`); | |
const movepool = Object.keys(lsets).sort(); | |
for (const move of movepool) { | |
lsets[move].sort((a, b) => { | |
const LEARN_ORDER = 'MTLREDSVC'; | |
const aGen = parseInt(a.charAt(0)); | |
const aLearn = LEARN_ORDER.indexOf(a.charAt(1)); | |
const aLvl = parseInt(a.slice(2) || '1'); | |
const bGen = parseInt(b.charAt(0)); | |
const bLearn = LEARN_ORDER.indexOf(b.charAt(1)); | |
const bLvl = parseInt(b.slice(2) || '1'); | |
if (aGen === bGen) { | |
if (aLearn === bLearn) { | |
return aLvl - bLvl; | |
} | |
return aLearn - bLearn; | |
} else { | |
return bGen - aGen; | |
} | |
}); | |
outputStream.writeLine(`\t\t\t${move}: ["${lsets[move].join('", "')}"],`); | |
} | |
outputStream.writeLine(`\t\t},`); | |
} | |
if (block.eventData) { | |
outputStream.writeLine(`\t\teventData: [`); | |
for (const event of block.eventData) { | |
outputStream.write('\t\t\t{'); | |
const keys = Object.keys(event); | |
for (const key in event) { | |
// @ts-ignore | |
const part = event[key]; | |
outputStream.write(`${key}: `); | |
if (Array.isArray(part)) { | |
outputStream.write(`["${part.join(`", "`)}"]`); | |
} else if (typeof part === 'object') { | |
outputStream.write('{'); | |
const ks = Object.keys(part); | |
for (const k in part) { | |
outputStream.write(`${k}: ${part[k]}`); | |
if ((ks.indexOf(k) + 1) < ks.length) { | |
outputStream.write(', '); | |
} | |
} | |
outputStream.write('}'); | |
} else if (typeof part === 'string') { | |
outputStream.write(`"${part}"`); | |
} else { | |
outputStream.write('' + part); | |
} | |
if ((keys.indexOf(key) + 1) < keys.length) { | |
outputStream.write(', '); | |
} | |
} | |
outputStream.write(`},\n`); | |
} | |
outputStream.writeLine(`\t\t],`); | |
} | |
if (block.encounters) { | |
outputStream.writeLine(`\t\tencounters: [`); | |
for (const encounter of block.encounters) { | |
outputStream.write('\t\t\t{'); | |
const keys = Object.keys(encounter); | |
for (const key in encounter) { | |
// @ts-ignore | |
const part = encounter[key]; | |
outputStream.write(`${key}: `); | |
if (Array.isArray(part)) { | |
outputStream.write(`["${part.join(`", "`)}"]`); | |
} else if (typeof part === 'object') { | |
outputStream.write('{'); | |
const ks = Object.keys(part); | |
for (const k in part) { | |
outputStream.write(`${k}: ${part[k]}`); | |
if ((ks.indexOf(k) + 1) < ks.length) { | |
outputStream.write(', '); | |
} | |
} | |
outputStream.write('}'); | |
} else if (typeof part === 'string') { | |
outputStream.write(`"${part}"`); | |
} else { | |
outputStream.write('' + part); | |
} | |
if ((keys.indexOf(key) + 1) < keys.length) { | |
outputStream.write(', '); | |
} | |
} | |
outputStream.write(`},\n`); | |
} | |
outputStream.writeLine(`\t\t],`); | |
} | |
if (block.eventOnly) { | |
outputStream.writeLine('\t\teventOnly: true,'); | |
} | |
outputStream.writeLine(`\t},`); | |
} | |
outputStream.writeLine(`};`); | |
// Newline at end of file to appease github and the linter | |
outputStream.writeLine(``); | |
} | |
writeLearnsets(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment