pogo-data-generator
Version:
Pokemon GO project data generator
1,050 lines (1,049 loc) • 60.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const pogo_protos_1 = require("@na-ji/pogo-protos");
const tempEvolutions_1 = require("../utils/tempEvolutions");
const enum_1 = require("../utils/enum");
const Item_1 = __importDefault(require("./Item"));
const LocationCards_1 = __importDefault(require("./LocationCards"));
const Masterfile_1 = __importDefault(require("./Masterfile"));
const PokeApi_1 = __importDefault(require("./PokeApi"));
const PokemonOverrides_1 = __importDefault(require("./PokemonOverrides"));
const reconcileBaseFormChanges = (parsedPokemon, parsedForms, id) => {
const pokemon = parsedPokemon[id];
if (!pokemon?.formChanges) {
return;
}
pokemon.forms
?.filter((formId) => formId !== 0)
.forEach((formId) => {
const form = parsedForms[formId];
if (!form?.formChanges) {
return;
}
const seenFormChanges = new Set();
const baseFormChanges = new Set(pokemon.formChanges.map((formChange) => JSON.stringify(formChange)));
const formChanges = form.formChanges.filter((formChange) => {
const key = JSON.stringify(formChange);
if (seenFormChanges.has(key) || baseFormChanges.has(key)) {
return false;
}
seenFormChanges.add(key);
return true;
});
if (formChanges.length) {
form.formChanges = formChanges;
}
else {
delete form.formChanges;
}
});
};
const cleanNumberList = (arr) => Array.isArray(arr)
? Array.from(new Set(arr.filter((value) => typeof value === 'number')))
: [];
const hasExactPlaceholderMoves = (moves, expected) => moves.length === expected.length &&
moves.every((value, index) => value === expected[index]);
const shouldPreferEstimatedPlaceholderQuickMoves = (actualQuickMoves, actualChargedMoves, fallbackQuickMoves) => hasExactPlaceholderMoves(actualQuickMoves, [pogo_protos_1.Rpc.HoloPokemonMove.SPLASH_FAST]) &&
hasExactPlaceholderMoves(actualChargedMoves, [pogo_protos_1.Rpc.HoloPokemonMove.STRUGGLE]) &&
!fallbackQuickMoves.includes(pogo_protos_1.Rpc.HoloPokemonMove.SPLASH_FAST) &&
fallbackQuickMoves.length > 0;
const excludedPlaceholderFallbackChargedMoves = new Set([
pogo_protos_1.Rpc.HoloPokemonMove.FRUSTRATION,
pogo_protos_1.Rpc.HoloPokemonMove.RETURN,
]);
class Pokemon extends Masterfile_1.default {
parsedPokemon;
parsedPokeForms;
parsedForms;
formsRef;
generations;
lcBanList;
evolvedPokemon;
options;
formsToSkip;
evolutionQuests;
parsedCostumes;
jungleCupRules;
constructor(options) {
super();
this.options = options;
this.formsToSkip = this.options.skipForms
? this.options.skipForms.map((name) => name.toLowerCase())
: [];
this.parsedPokemon = {};
this.parsedForms = {
0: {
formName: options.unsetFormName ?? 'Unset',
proto: 'FORM_UNSET',
formId: 0,
},
};
this.formsRef = {};
this.evolvedPokemon = new Set();
this.generations = {
1: {
name: 'Kanto',
range: [1, 151],
},
2: {
name: 'Johto',
range: [152, 251],
},
3: {
name: 'Hoenn',
range: [252, 386],
},
4: {
name: 'Sinnoh',
range: [387, 493],
},
5: {
name: 'Unova',
range: [494, 649],
},
6: {
name: 'Kalos',
range: [650, 721],
},
7: {
name: 'Alola',
range: [722, 809],
},
8: {
name: 'Galar',
range: [810, 905],
},
9: {
name: 'Paldea',
range: [906, 1010],
},
};
this.evolutionQuests = {};
this.parsedCostumes = {};
this.jungleCupRules = { types: [], banned: [] };
}
pokemonName(id) {
try {
switch (id) {
case 29:
return 'Nidoran♀';
case 32:
return 'Nidoran♂';
default:
return this.capitalize(pogo_protos_1.Rpc.HoloPokemonId[id]);
}
}
catch (e) {
console.warn(e, `Failed to set pokemon name for ${id}`);
}
}
formName(id, formName) {
if (!formName)
return '';
try {
const name = formName.substring(id === pogo_protos_1.Rpc.HoloPokemonId.NIDORAN_FEMALE ||
id === pogo_protos_1.Rpc.HoloPokemonId.NIDORAN_MALE
? 8
: pogo_protos_1.Rpc.HoloPokemonId[id].length + 1);
return this.capitalize(name);
}
catch (e) {
console.warn(e, `Failed to lookup form name for ${formName}, ID#`, id);
}
}
skipForms(formName) {
if (!formName)
return false;
try {
return this.formsToSkip.some((form) => formName.toLowerCase() === form);
}
catch (e) {
console.warn(e, `Failed to skip forms for ${formName}`);
}
}
lookupPokemon(name) {
if (!name)
return '';
try {
for (const key of Object.keys(pogo_protos_1.Rpc.HoloPokemonId)) {
if (name.startsWith('PORYGON_Z_')) {
return 'PORYGON_Z';
}
else if (name.startsWith(`${key}_`)) {
return key;
}
}
}
catch (e) {
console.warn(e, `Failed to lookup pokemon for ${name}`);
}
}
lookupForm(id) {
if (!id)
return '';
try {
return (Object.entries(pogo_protos_1.Rpc.PokemonDisplayProto.Form).find(([_, v]) => {
return v === id;
})?.[0] || '');
}
catch (e) {
console.warn(e, `Failed to lookup form for ${id}`);
}
}
getGeneration(id) {
try {
const genInfo = {};
genInfo.genId = +Object.keys(this.generations).find((gen) => {
return (id >= this.generations[gen].range[0] &&
id <= this.generations[gen].range[1]);
});
if (genInfo.genId) {
genInfo.generation = this.generations[genInfo.genId].name;
}
return genInfo;
}
catch (e) {
console.warn(e, `Failed to lookup generation for ${id}`);
}
}
getMoves(moves) {
if (!moves)
return [];
try {
return moves
.map((move) => typeof move === 'string'
? pogo_protos_1.Rpc.HoloPokemonMove[move]
: move)
.filter(Boolean)
.sort((a, b) => a - b);
}
catch (e) {
console.warn(e, `Failed to lookup moves for ${moves}`);
}
}
getTypes(incomingTypes) {
if (!incomingTypes)
return [];
try {
if (!incomingTypes[1]) {
incomingTypes.pop();
}
return incomingTypes
.map((type) => typeof type === 'string'
? pogo_protos_1.Rpc.HoloPokemonType[type]
: type)
.filter(Boolean)
.sort((a, b) => a - b);
}
catch (e) {
console.warn(e, `Failed to lookup types for ${incomingTypes}`);
}
}
getCostumeOverrides(costumes) {
try {
return costumes.map((costume) => ({
costumeId: pogo_protos_1.Rpc.PokemonDisplayProto.Costume[costume],
costumeProto: costume,
costumeName: this.capitalize(costume),
}));
}
catch (e) {
console.warn('Failed to process costume overrides', e, costumes);
}
}
compileFormChanges(formChanges) {
if (!Array.isArray(formChanges))
return [];
try {
const seenFormChanges = new Set();
return formChanges
.map((formChange) => {
const availableForms = (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.PokemonDisplayProto.Form, formChange.availableForm || [], 'form');
const questRequirements = formChange.questRequirement
?.map((requirement) => ({
questRequirement: requirement.questRequirementTemplateId,
description: requirement.description,
target: requirement.target,
}))
.filter((requirement) => requirement.questRequirement ||
requirement.description ||
requirement.target !== undefined) || [];
const componentLocationCardSettings = formChange.componentPokemonSettings?.locationCardSettings
?.map((settings) => ({
basePokemonLocationCard: LocationCards_1.default.resolveId(settings.basePokemonLocationCard, 'form change location card'),
componentPokemonLocationCard: LocationCards_1.default.resolveId(settings.componentPokemonLocationCard, 'form change location card'),
fusionPokemonLocationCard: LocationCards_1.default.resolveId(settings.fusionPokemonLocationCard, 'form change location card'),
}))
.filter((settings) => Object.values(settings).some((value) => value !== undefined)) || [];
const componentPokemonSettings = formChange.componentPokemonSettings
? {
pokedexId: (0, enum_1.resolveEnumId)(pogo_protos_1.Rpc.HoloPokemonId, formChange.componentPokemonSettings.pokedexId, 'pokemon'),
formId: (0, enum_1.resolveEnumId)(pogo_protos_1.Rpc.PokemonDisplayProto.Form, formChange.componentPokemonSettings.form, 'form'),
componentCandyCost: formChange.componentPokemonSettings.componentCandyCost,
formChangeType: (0, enum_1.enumName)(pogo_protos_1.Rpc.ComponentPokemonSettingsProto.FormChangeType, formChange.componentPokemonSettings.formChangeType, 'form change type'),
fusionMove1: (0, enum_1.resolveEnumId)(pogo_protos_1.Rpc.HoloPokemonMove, formChange.componentPokemonSettings.fusionMove1, 'move'),
fusionMove2: (0, enum_1.resolveEnumId)(pogo_protos_1.Rpc.HoloPokemonMove, formChange.componentPokemonSettings.fusionMove2, 'move'),
locationCardSettings: componentLocationCardSettings.length
? componentLocationCardSettings
: undefined,
familyId: (0, enum_1.resolveEnumId)(pogo_protos_1.Rpc.HoloPokemonFamilyId, formChange.componentPokemonSettings.familyId, 'family'),
}
: undefined;
const moveReassignment = formChange.moveReassignment
? {
quickMoves: formChange.moveReassignment.quickMoves
?.map((moves) => ({
existingMoves: (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.HoloPokemonMove, moves.existingMoves || [], 'move'),
replacementMoves: (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.HoloPokemonMove, moves.replacementMoves || [], 'move'),
}))
.filter((moves) => moves.existingMoves.length ||
moves.replacementMoves.length) || undefined,
chargedMoves: formChange.moveReassignment.cinematicMoves
?.map((moves) => ({
existingMoves: (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.HoloPokemonMove, moves.existingMoves || [], 'move'),
replacementMoves: (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.HoloPokemonMove, moves.replacementMoves || [], 'move'),
}))
.filter((moves) => moves.existingMoves.length ||
moves.replacementMoves.length) || undefined,
}
: undefined;
const requiredQuickMoves = formChange.requiredQuickMoves
?.map((moves) => ({
requiredMoves: (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.HoloPokemonMove, moves.requiredMoves || [], 'move'),
}))
.filter((moves) => moves.requiredMoves.length) || [];
const requiredChargedMoves = formChange.requiredCinematicMoves
?.map((moves) => ({
requiredMoves: (0, enum_1.resolveEnumIds)(pogo_protos_1.Rpc.HoloPokemonMove, moves.requiredMoves || [], 'move'),
}))
.filter((moves) => moves.requiredMoves.length) || [];
const requiredBreadMoves = formChange.requiredBreadMoves
?.map((moves) => {
const moveTypes = moves.moveTypes?.filter(Boolean) || [];
return {
moveTypes: moveTypes.length ? moveTypes : undefined,
moveLevel: (0, enum_1.enumName)(pogo_protos_1.Rpc.BreadMoveLevels, moves.moveLevel, 'bread move level'),
};
})
.filter((moves) => (moves.moveTypes?.length || 0) > 0 ||
moves.moveLevel !== undefined) || [];
const formChangeBonusAttributes = formChange.formChangeBonusAttributes
?.map((attributes) => {
const maxMoves = attributes.maxMoves
?.map((move) => ({
moveType: (0, enum_1.enumName)(pogo_protos_1.Rpc.BreadMoveSlotProto.BreadMoveType, move.moveType, 'bread move type'),
moveLevel: (0, enum_1.enumName)(pogo_protos_1.Rpc.BreadMoveLevels, move.moveLevel, 'bread move level'),
}))
.filter((move) => move.moveType !== undefined ||
move.moveLevel !== undefined) || [];
return {
targetForm: (0, enum_1.resolveEnumId)(pogo_protos_1.Rpc.PokemonDisplayProto.Form, attributes.targetForm, 'form'),
breadMode: (0, enum_1.enumName)(pogo_protos_1.Rpc.BreadModeEnum.Modifier, attributes.breadMode, 'bread mode'),
clearBreadMode: attributes.clearBreadMode,
maxMoves: maxMoves.length ? maxMoves : undefined,
};
})
.filter((attributes) => Object.entries(attributes).some(([key, value]) => key === 'maxMoves'
? Array.isArray(value)
? value.length > 0
: false
: value !== undefined)) || [];
const locationCardSettings = formChange.locationCardSettings
?.map((settings) => ({
existingLocationCard: LocationCards_1.default.resolveId(settings.existingLocationCard, 'form change location card'),
replacementLocationCard: LocationCards_1.default.resolveId(settings.replacementLocationCard, 'form change location card'),
}))
.filter((settings) => Object.values(settings).some((value) => value !== undefined)) || [];
const normalized = {
availableForms: availableForms.length ? availableForms : undefined,
candyCost: formChange.candyCost,
stardustCost: formChange.stardustCost,
itemRequirement: Item_1.default.resolveId(formChange.item, 'form change item'),
itemCostCount: formChange.itemCostCount,
questRequirements: questRequirements.length
? questRequirements
: undefined,
componentPokemonSettings: componentPokemonSettings &&
Object.entries(componentPokemonSettings).some(([key, value]) => key === 'locationCardSettings'
? Array.isArray(value)
? value.length > 0
: false
: value !== undefined)
? componentPokemonSettings
: undefined,
moveReassignment: moveReassignment &&
(moveReassignment.quickMoves?.length ||
moveReassignment.chargedMoves?.length)
? moveReassignment
: undefined,
requiredQuickMoves: requiredQuickMoves.length
? requiredQuickMoves
: undefined,
requiredChargedMoves: requiredChargedMoves.length
? requiredChargedMoves
: undefined,
requiredBreadMoves: requiredBreadMoves.length
? requiredBreadMoves
: undefined,
priority: formChange.priority,
formChangeBonusAttributes: formChangeBonusAttributes.length
? formChangeBonusAttributes
: undefined,
locationCardSettings: locationCardSettings.length
? locationCardSettings
: undefined,
};
return Object.values(normalized).some((value) => value !== undefined)
? normalized
: undefined;
})
.filter((formChange) => {
if (!formChange) {
return false;
}
const key = JSON.stringify(formChange);
if (seenFormChanges.has(key)) {
return false;
}
seenFormChanges.add(key);
return true;
});
}
catch (e) {
console.warn(e, `Failed to compile form changes for ${JSON.stringify(formChanges, null, 2)}`);
return [];
}
}
compileEvos(mfObject) {
const overrides = PokemonOverrides_1.default.checkEvos(this, mfObject);
if (overrides)
return overrides;
try {
const evolutions = [];
mfObject.forEach((branch) => {
if (branch.temporaryEvolution) {
return;
}
else if (branch.evolution) {
const id = pogo_protos_1.Rpc.HoloPokemonId[branch.evolution];
const formId = typeof branch.form === 'number'
? branch.form
: /^\d+$/.test(branch.form)
? +branch.form
: pogo_protos_1.Rpc.PokemonDisplayProto.Form[branch.form];
evolutions.push({
evoId: id,
formId: this.options.includeUnset ? formId || 0 : formId,
genderRequirement: this.options.genderString
? this.genders[pogo_protos_1.Rpc.PokemonDisplayProto.Gender[branch.genderRequirement]]
: pogo_protos_1.Rpc.PokemonDisplayProto.Gender[branch.genderRequirement],
candyCost: branch.candyCost,
itemRequirement: pogo_protos_1.Rpc.Item[branch.evolutionItemRequirement],
tradeBonus: branch.noCandyCostViaTrade,
mustBeBuddy: branch.mustBeBuddy,
onlyDaytime: branch.onlyDaytime,
onlyNighttime: branch.onlyNighttime,
questRequirement: branch.questDisplay
? branch.questDisplay[0].questRequirementTemplateId
: undefined,
});
this.evolvedPokemon.add(id);
}
});
return evolutions.sort((a, b) => a.evoId - b.evoId);
}
catch (e) {
console.warn(e, `Failed to compile evos for ${JSON.stringify(mfObject, null, 2)}`);
}
}
compileTempEvos(mfObject, evoBranch, primaryForm) {
try {
const tempEvolutions = mfObject
.filter((tempEvo) => tempEvo.stats)
.map((tempEvo) => {
const resolvedTempEvoId = pogo_protos_1.Rpc.HoloTemporaryEvolutionId[tempEvo.tempEvoId] ??
(tempEvo.tempEvoId === 'TEMP_EVOLUTION_MEGA_Z'
? 5
: tempEvo.tempEvoId);
const newTempEvolution = {
tempEvoId: resolvedTempEvoId,
};
switch (true) {
case tempEvo.stats.baseAttack !== primaryForm.attack:
case tempEvo.stats.baseDefense !== primaryForm.defense:
case tempEvo.stats.baseStamina !== primaryForm.stamina:
newTempEvolution.attack = tempEvo.stats.baseAttack;
newTempEvolution.defense = tempEvo.stats.baseDefense;
newTempEvolution.stamina = tempEvo.stats.baseStamina;
}
if (tempEvo.averageHeightM !== primaryForm.height) {
newTempEvolution.height = tempEvo.averageHeightM;
}
if (tempEvo.averageWeightKg !== primaryForm.weight) {
newTempEvolution.weight = tempEvo.averageWeightKg;
}
const types = this.getTypes([
tempEvo.typeOverride1,
tempEvo.typeOverride2,
]);
if (!this.compare(types, primaryForm.types)) {
newTempEvolution.types = types;
}
const energy = evoBranch.find((branch) => branch.temporaryEvolution === tempEvo.tempEvoId);
if (energy) {
newTempEvolution.firstEnergyCost =
energy.temporaryEvolutionEnergyCost;
newTempEvolution.subsequentEnergyCost =
energy.temporaryEvolutionEnergyCostSubsequent;
}
return newTempEvolution;
});
return (0, tempEvolutions_1.sortTempEvolutions)(tempEvolutions);
}
catch (e) {
console.warn(e, `Failed to compile temp evos for ${JSON.stringify(mfObject, null, 2)}`);
}
}
generateProtoForms() {
Object.entries(pogo_protos_1.Rpc.PokemonDisplayProto.Form).forEach((proto) => {
const [name, formId] = proto;
try {
const pokemon = name.startsWith('NIDORAN_')
? ['NIDORAN_FEMALE', 'NIDORAN_MALE']
: [this.formsRef[name] || this.lookupPokemon(name)];
for (const pkmn of pokemon) {
if (pkmn) {
const id = pogo_protos_1.Rpc.HoloPokemonId[pkmn];
const formName = this.formName(id, name);
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {
pokemonName: this.pokemonName(id),
forms: this.options.includeUnset && !this.options.noFormPlaceholders
? [0]
: [],
pokedexId: id,
defaultFormId: 0,
...this.getGeneration(+id),
};
}
if (!this.skipForms(formName) &&
!(this.parsedPokemon[id].defaultFormId === 0 &&
formName === 'Normal' &&
this.options.skipNormalIfUnset)) {
this.parsedForms[formId] = {
...this.parsedForms[formId],
formName,
proto: name,
formId: +formId,
};
PokemonOverrides_1.default.addFormData(this, formId);
if (!this.parsedPokemon[id].forms.includes(+formId)) {
if (!this.parsedPokemon[id].forms)
this.parsedPokemon[id].forms = [];
this.parsedPokemon[id].forms.push(+formId);
}
reconcileBaseFormChanges(this.parsedPokemon, this.parsedForms, id);
}
}
}
}
catch (e) {
console.warn(e, '\n', proto);
}
});
}
addExtendedStats(object) {
if ('pokemonExtendedSettings' in object.data) {
const id = pogo_protos_1.Rpc.HoloPokemonId[object.data.pokemonExtendedSettings.uniqueId];
if (id) {
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {};
}
const values = Object.entries(object.data.pokemonExtendedSettings.sizeSettings).map(([name, value]) => ({ name, value }));
const protoForm = object.data.pokemonExtendedSettings.form
? typeof object.data.pokemonExtendedSettings.form === 'number'
? object.data.pokemonExtendedSettings.form
: /^\d+$/.test(object.data.pokemonExtendedSettings.form)
? +object.data.pokemonExtendedSettings.form
: pogo_protos_1.Rpc.PokemonDisplayProto.Form[object.data.pokemonExtendedSettings.form]
: 0;
if (protoForm) {
if (!this.parsedForms[protoForm]) {
this.parsedForms[protoForm] = {};
}
this.parsedForms[protoForm].sizeSettings = values;
}
else {
this.parsedPokemon[id].sizeSettings = values;
}
}
}
}
cleanExtendedStats() {
Object.values(this.parsedPokemon).forEach((pkmn) => {
if (pkmn.sizeSettings && pkmn.forms) {
const pkmnSizeTree = Object.fromEntries(pkmn.sizeSettings.map(({ name, value }) => [name, value]));
pkmn.forms.forEach((formId) => {
if (this.parsedForms[formId]?.sizeSettings) {
if (this.parsedForms[formId].sizeSettings.every((size) => pkmnSizeTree[size.name] === size.value)) {
delete this.parsedForms[formId].sizeSettings;
}
}
});
}
});
}
addFormBaseStats(formId, hp, a, d, sa, sd, sp) {
if (this.parsedForms[formId].attack ||
this.parsedForms[formId].defense ||
this.parsedForms[formId].stamina) {
console.warn('Base stats already found for', pogo_protos_1.Rpc.PokemonDisplayProto.Form[formId]);
return;
}
this.parsedForms[formId].attack = PokeApi_1.default.attack(a, sa, sp);
this.parsedForms[formId].defense = PokeApi_1.default.defense(d, sd, sp);
this.parsedForms[formId].stamina = PokeApi_1.default.stamina(hp);
}
addEvolutionQuest(object) {
try {
const { evolutionQuestTemplate } = object.data;
this.evolutionQuests[object.templateId] = {
questType: pogo_protos_1.Rpc.QuestType[evolutionQuestTemplate.questType],
target: evolutionQuestTemplate.goals[0].target,
assetsRef: evolutionQuestTemplate.display.description.toLowerCase(),
};
if (this.evolutionQuests[object.templateId].target) {
this.evolutionQuests[object.templateId].assetsRef =
this.evolutionQuests[object.templateId].assetsRef.replace('single', 'plural');
}
if (evolutionQuestTemplate.goals[1]) {
console.warn(`Second quest goal detected, fix it. ${object.templateId}`);
}
}
catch (e) {
console.warn(e, `Failed to add evolution quest for ${JSON.stringify(object, null, 2)}`);
}
}
addForm(object) {
if (object.templateId.split('_')[1]) {
const id = Number(object.templateId.split('_')[1].slice(1));
try {
const forms = object.data.formSettings.forms;
if (forms) {
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {};
}
if (!this.parsedPokemon[id].forms) {
this.parsedPokemon[id].forms = [];
}
for (let i = 0; i < forms.length; i += 1) {
const value = forms[i].form;
if (!value)
continue;
const formId = typeof value === 'number'
? value
: pogo_protos_1.Rpc.PokemonDisplayProto.Form[value];
const form = typeof value === 'number' ? this.lookupForm(value) : value;
this.formsRef[form] = object.data.formSettings.pokemon;
const name = this.formName(id, form);
if (i === 0) {
this.parsedPokemon[id].defaultFormId = formId || 0;
}
if (!this.skipForms(name)) {
this.parsedForms[formId] = {
...this.parsedForms[formId],
formName: name,
proto: form,
formId,
isCostume: forms[i].isCostume,
};
}
}
}
else {
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {
pokemonName: this.pokemonName(id),
forms: [],
pokedexId: id,
...this.getGeneration(+id),
};
}
}
reconcileBaseFormChanges(this.parsedPokemon, this.parsedForms, id);
if (this.options.includeUnset && !this.options.noFormPlaceholders) {
this.parsedPokemon[id].forms.push(0);
}
}
catch (e) {
console.warn(e, '\n', JSON.stringify(object, null, 2));
}
}
}
addPokemon(object) {
const { templateId, data: { pokemonSettings }, } = object;
const split = templateId.split('_');
const id = Number(split[0].slice(1));
try {
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {};
}
const formId = /^V\d{4}_POKEMON_/.test(templateId)
? pogo_protos_1.Rpc.PokemonDisplayProto.Form[templateId.substring('V9999_POKEMON_'.length)]
: null;
if (formId) {
if (!this.parsedPokemon[id].forms) {
this.parsedPokemon[id].forms = [];
}
const primaryForm = this.parsedPokemon[id];
const formName = this.formName(id, split.filter((word, i) => i > 1 && word).join('_'));
if (!this.skipForms(formName)) {
this.parsedForms[formId] = {
...this.parsedForms[formId],
formName,
proto: templateId,
formId,
};
if (!this.parsedPokemon[id].forms.includes(formId)) {
this.parsedPokemon[id].forms.push(formId);
}
const form = this.parsedForms[formId];
switch (true) {
case pokemonSettings.stats.baseAttack !== primaryForm.attack:
case pokemonSettings.stats.baseDefense !== primaryForm.defense:
case pokemonSettings.stats.baseStamina !== primaryForm.stamina:
form.attack = pokemonSettings.stats.baseAttack;
form.defense = pokemonSettings.stats.baseDefense;
form.stamina = pokemonSettings.stats.baseStamina;
}
switch (true) {
case object.data.pokemonSettings.pokedexHeightM !==
primaryForm.height:
case object.data.pokemonSettings.pokedexWeightKg !==
primaryForm.weight:
form.height = object.data.pokemonSettings.pokedexHeightM;
form.weight = object.data.pokemonSettings.pokedexWeightKg;
}
const qMoves = this.getMoves(pokemonSettings.quickMoves);
if (!this.compare(qMoves, primaryForm.quickMoves)) {
form.quickMoves = qMoves;
}
const cMoves = this.getMoves(pokemonSettings.cinematicMoves);
if (!this.compare(cMoves, primaryForm.chargedMoves)) {
form.chargedMoves = cMoves;
}
const eqMoves = this.getMoves(pokemonSettings.eliteQuickMove);
if (!this.compare(eqMoves, primaryForm.eliteQuickMoves)) {
form.eliteQuickMoves = eqMoves;
}
const ecMoves = this.getMoves(pokemonSettings.eliteCinematicMove);
if (!this.compare(ecMoves, primaryForm.eliteChargedMoves)) {
form.eliteChargedMoves = ecMoves;
}
const types = this.getTypes([
pokemonSettings.type,
pokemonSettings.type2,
]);
if (!this.compare(types, primaryForm.types)) {
form.types = types;
}
const family = pogo_protos_1.Rpc.HoloPokemonFamilyId[pokemonSettings.familyId];
if (family !== primaryForm.family) {
form.family = family;
}
if (pokemonSettings.evolutionBranch?.some((evo) => evo.evolution)) {
if (!form.evolutions) {
form.evolutions = [];
}
form.evolutions.push(...this.compileEvos(pokemonSettings.evolutionBranch));
}
const formChanges = this.compileFormChanges(pokemonSettings.formChange);
if (formChanges.length) {
form.formChanges = formChanges;
}
else {
delete form.formChanges;
}
if (pokemonSettings.tempEvoOverrides) {
form.tempEvolutions = this.compileTempEvos(pokemonSettings.tempEvoOverrides, pokemonSettings.evolutionBranch, this.parsedPokemon[id]);
}
if ((form.formName === 'Normal' || form.formName === 'Purified') &&
primaryForm.tempEvolutions) {
form.tempEvolutions = Object.values(primaryForm.tempEvolutions).map((tempEvo) => tempEvo);
}
if (pokemonSettings.shadow) {
form.purificationDust =
pokemonSettings.shadow.purificationStardustNeeded;
form.purificationCandy =
pokemonSettings.shadow.purificationCandyNeeded;
}
if (pokemonSettings.allowNoevolveEvolution) {
form.costumeOverrideEvos = this.getCostumeOverrides(pokemonSettings.allowNoevolveEvolution);
}
reconcileBaseFormChanges(this.parsedPokemon, this.parsedForms, id);
}
}
else {
this.parsedPokemon[id] = {
pokemonName: this.pokemonName(id),
forms: this.parsedPokemon[id].forms || [],
...this.parsedPokemon[id],
pokedexId: id,
types: this.getTypes([pokemonSettings.type, pokemonSettings.type2]),
attack: pokemonSettings.stats.baseAttack,
defense: pokemonSettings.stats.baseDefense,
stamina: pokemonSettings.stats.baseStamina,
height: pokemonSettings.pokedexHeightM,
weight: pokemonSettings.pokedexWeightKg,
eliteQuickMoves: this.getMoves(pokemonSettings.eliteQuickMove),
eliteChargedMoves: this.getMoves(pokemonSettings.eliteCinematicMove),
family: pogo_protos_1.Rpc.HoloPokemonFamilyId[pokemonSettings.familyId],
fleeRate: pokemonSettings.encounter.baseFleeRate,
captureRate: pokemonSettings.encounter.baseCaptureRate,
bonusCandyCapture: pokemonSettings.encounter.bonusCandyCaptureReward,
bonusStardustCapture: pokemonSettings.encounter.bonusStardustCaptureReward,
legendary: pogo_protos_1.Rpc.HoloPokemonClass[pokemonSettings.pokemonClass] === 1,
mythic: pogo_protos_1.Rpc.HoloPokemonClass[pokemonSettings.pokemonClass] === 2,
ultraBeast: pogo_protos_1.Rpc.HoloPokemonClass[pokemonSettings.pokemonClass] === 3,
buddyGroupNumber: pokemonSettings.buddyGroupNumber,
buddyDistance: pokemonSettings.kmBuddyDistance,
buddyMegaEnergy: pokemonSettings.buddyWalkedMegaEnergyAward,
thirdMoveStardust: pokemonSettings.thirdMove.stardustToUnlock,
thirdMoveCandy: pokemonSettings.thirdMove.candyToUnlock,
gymDefenderEligible: pokemonSettings.isDeployable,
tradable: pokemonSettings.isTradable,
transferable: pokemonSettings.isTransferable,
...this.getGeneration(id),
};
if (id !== 235) {
this.parsedPokemon[id].quickMoves = this.getMoves(pokemonSettings.quickMoves);
this.parsedPokemon[id].chargedMoves = this.getMoves(pokemonSettings.cinematicMoves);
}
else {
if (pokemonSettings.quickMoves?.length)
console.warn('unexpected Smeargle quick moves', pokemonSettings.quickMoves);
if (pokemonSettings.cinematicMoves?.length)
console.warn('unexpected Smeargle charged moves', pokemonSettings.cinematicMoves);
}
if (pokemonSettings.evolutionBranch?.some((evo) => evo.evolution)) {
this.parsedPokemon[id].evolutions = this.compileEvos(pokemonSettings.evolutionBranch);
}
const formChanges = this.compileFormChanges(pokemonSettings.formChange);
if (formChanges.length) {
this.parsedPokemon[id].formChanges = formChanges;
reconcileBaseFormChanges(this.parsedPokemon, this.parsedForms, id);
}
if (pokemonSettings.tempEvoOverrides) {
this.parsedPokemon[id].tempEvolutions = this.compileTempEvos(pokemonSettings.tempEvoOverrides, pokemonSettings.evolutionBranch, this.parsedPokemon[id]);
}
if (pokemonSettings.allowNoevolveEvolution) {
this.parsedPokemon[id].costumeOverrideEvos = this.getCostumeOverrides(pokemonSettings.allowNoevolveEvolution);
}
if (pokemonSettings.shadow) {
this.parsedPokemon[id].purificationDust =
pokemonSettings.shadow.purificationStardustNeeded;
this.parsedPokemon[id].purificationCandy =
pokemonSettings.shadow.purificationCandyNeeded;
}
}
}
catch (e) {
console.warn(e, `Failed to parse Pokemon for ${id}`, JSON.stringify(object, null, 2));
}
}
addSourdoughMoveMappings({ data: { sourdoughMoveMappingSettings: { mappings }, }, }) {
for (let i = 0; i < mappings.length; i += 1)
try {
const id = pogo_protos_1.Rpc.HoloPokemonId[mappings[i].pokemonId];
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {};
}
let target = this.parsedPokemon[id];
if (mappings[i].form) {
const rawForm = mappings[i].form;
const formId = typeof rawForm === 'number'
? rawForm
: /^\d+$/.test(rawForm)
? +rawForm
: pogo_protos_1.Rpc.PokemonDisplayProto.Form[rawForm];
if (!this.parsedPokemon[id].forms) {
this.parsedPokemon[id].forms = [];
}
const formProto = typeof rawForm === 'number'
? this.lookupForm(rawForm)
: /^\d+$/.test(rawForm)
? this.lookupForm(+rawForm)
: rawForm;
const formName = this.formName(id, formProto);
if (!this.skipForms(formName)) {
this.parsedForms[formId] = {
...this.parsedForms[formId],
formName,
formId,
};
if (!this.parsedPokemon[id].forms.includes(formId)) {
this.parsedPokemon[id].forms.push(formId);
}
target = this.parsedForms[formId];
}
}
target.gmaxMove = pogo_protos_1.Rpc.HoloPokemonMove[mappings[i].move];
}
catch (e) {
console.warn(e, `Failed to parse gmax move mapping #${i}`, JSON.stringify(mappings[i], null, 2));
}
}
addSmeargleMovesSettings({ data: { smeargleMovesSettings: { quickMoves, cinematicMoves }, }, }) {
const id = 235;
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {};
}
try {
this.parsedPokemon[id].quickMoves = this.getMoves(quickMoves);
}
catch (e) {
console.warn(e, `Failed to parse smeargle quick move mapping`, JSON.stringify(quickMoves, null, 2));
}
try {
this.parsedPokemon[id].chargedMoves = this.getMoves(cinematicMoves);
}
catch (e) {
console.warn(e, `Failed to parse smeargle charged move mapping`, JSON.stringify(cinematicMoves, null, 2));
}
}
missingPokemon() {
Object.values(pogo_protos_1.Rpc.HoloPokemonId).forEach((id) => {
try {
if (id) {
this.parsedPokemon[id] = {
pokemonName: this.pokemonName(+id),
pokedexId: +id,
defaultFormId: 0,
types: [],
quickMoves: [],
chargedMoves: [],
eliteQuickMoves: [],
eliteChargedMoves: [],
...this.getGeneration(+id),
...this.parsedPokemon[id],
};
if (!this.parsedPokemon[id].forms) {
this.parsedPokemon[id].forms = this.options.noFormPlaceholders
? []
: [0];
}
else if ((this.parsedPokemon[id].forms.length === 0 &&
!this.options.noFormPlaceholders) ||
this.parsedPokemon[id].defaultFormId === 0) {
this.parsedPokemon[id].forms.push(0);
}
reconcileBaseFormChanges(this.parsedPokemon, this.parsedForms, +id);
}
}
catch (e) {
console.warn(e, `Failed to parse Future Pokemon for ${id}`);
}
});
}
sortForms() {
Object.values(this.parsedPokemon).forEach((pokemon) => {
try {
if (pokemon.forms) {
pokemon.forms.sort((a, b) => a - b);
}
}
catch (e) {
console.warn(e, `Failed to sort forms for ${pokemon.pokemonName}`);
}
});
}
littleCup() {
try {
if (this.lcBanList === undefined) {
console.warn('Missing little cup ban list from Masterfile');
}
else {
this.lcBanList.add('FARFETCHD');
this.parsedForms[pogo_protos_1.Rpc.PokemonDisplayProto.Form.FARFETCHD_GALARIAN].little = true;
}
for (const [id, pokemon] of Object.entries(this.parsedPokemon)) {
const allowed = !this.evolvedPokemon.has(+id) && pokemon.evolutions !== undefined;
if (allowed || +id === pogo_protos_1.Rpc.HoloPokemonId.DEERLING) {
pokemon.little = true;
}
}
}
catch (e) {
console.warn(e, `Failed to parse Little Cup`);
}
}
jungleCup(object) {
const { data: { combatLeague: { pokemonCondition, bannedPokemon }, }, } = object;
pokemonCondition.forEach((condition) => {
if (condition.type === 'WITH_POKEMON_TYPE') {
condition.withPokemonType.pokemonType.forEach((type) => {
this.jungleCupRules.types.push(pogo_protos_1.Rpc.HoloPokemonType[type]);
});
}
});
this.jungleCupRules.banned =
bannedPokemon?.map((poke) => {
return pogo_protos_1.Rpc.HoloPokemonId[poke];
}) ?? [];
}
jungleEligibility() {
Object.entries(this.parsedPokemon).forEach(([id, pokemon]) => {
const allowed = this.jungleCupRules.types.some((type) => pokemon.types.includes(type)) && !this.jungleCupRules.banned.includes(+id);
if (allowed)
pokemon.jungle = true;
});
}
makeFormsSeparate() {
try {
this.parsedPokeForms = {};
Object.values(this.parsedPokemon).forEach((pokemon) => {
if (pokemon.forms) {
pokemon.forms.forEach((form) => {
this.parsedPokeForms[`${pokemon.pokedexId}_${form}`] = {
...pokemon,
evolutions: form === 0 ? pokemon.evolutions : undefined,
formChanges: form === 0 ? pokemon.formChanges : undefined,
tempEvolutions: form === 0 ? pokemon.tempEvolutions : undefined,
...this.parsedForms[form],
forms: [form],
};
});
}
});
}
catch (e) {
console.warn(e, `Failed to make forms separate`);
}
}
parseCostumes() {
Object.entries(pogo_protos_1.Rpc.PokemonDisplayProto.Costume).forEach((proto) => {
const [name, id] = proto;
this.parsedCostumes[id] = {
id: +id,
name: this.capitalize(name),
proto: name,
noEvolve: name.endsWith('_NOEVOLVE'),
};
});
}
parsePokeApi(baseStats, tempEvos) {
if (this.options.includeEstimatedPokemon === true ||
this.options.includeEstimatedPokemon.baseStats) {
Object.keys(baseStats).forEach((id) => {
try {
if (!this.parsedPokemon[id]) {
this.parsedPokemon[id] = {
pokemonName: this.pokemonName(+id),
pokedexId: +id,
defaultFormId: 0,
forms: this.options.includeUnset && !this.options.noFormPlaceholders
? [0]
: [],