pvpokemon/parseGameMaster.ts
2019-02-09 18:21:29 -05:00

145 lines
5.9 KiB
TypeScript

import fs from 'fs';
import POGOProtos from 'pogo-protos';
import { ApiService } from 'api/ApiService';
import { IBaseStatsRank, IPokemon } from 'app/models/Pokemon';
interface ICalculateRelativeStats {
id : POGOProtos.Enums.PokemonId;
form : POGOProtos.Enums.Form;
value : number;
}
export const parseGameMaster = async () => {
const apiService = new ApiService();
// const pokemonForms : { [ key in keyof typeof POGOProtos.Enums.PokemonId ]? : Array<POGOProtos.Enums.Form> } = {};
const pokemonData : { [ key in keyof typeof POGOProtos.Enums.PokemonId ]? : Array<IPokemon> } = {};
const pokemonBaseStamina : Array<ICalculateRelativeStats> = [];
const pokemonBaseAttack : Array<ICalculateRelativeStats> = [];
const pokemonBaseDefense : Array<ICalculateRelativeStats> = [];
const pokemonOrderById : Record<string, IBaseStatsRank> = {};
const GameMasterProto = fs.readFileSync('externals/pokemongo-game-master/versions/latest/GAME_MASTER.protobuf');
const GameMaster = POGOProtos.Networking.Responses.DownloadItemTemplatesResponse.decode(GameMasterProto);
// fs.writeFileSync('gamemaster.json', JSON.stringify(decoded, null, 2));
const version = GameMaster.timestamp_ms;
for (const entry of GameMaster.item_templates) {
if (!entry.template_id) {
continue;
}
// ignore *_NORMAL entries
if (entry.template_id.match(/^V(\d{4})_POKEMON_(\w+)(?<!_NORMAL)$/) && entry.pokemon_settings) {
const pokemonId = entry.pokemon_settings.pokemon_id || POGOProtos.Enums.PokemonId.MISSINGNO;
const dex = parseInt(RegExp.$1, 10);
const speciesInfo = await apiService.getPokemonSpeciesInfo(pokemonId, dex, POGOProtos.Enums.PokemonId[pokemonId]);
pokemonData[pokemonId] = pokemonData[pokemonId] || [];
const mon = {
...speciesInfo,
id: pokemonId,
form: entry.pokemon_settings.form || POGOProtos.Enums.Form.FORM_UNSET,
family: entry.pokemon_settings.family_id || POGOProtos.Enums.PokemonFamilyId.FAMILY_UNSET,
types: {
type1: entry.pokemon_settings.type || POGOProtos.Enums.PokemonType.POKEMON_TYPE_NONE,
type2: entry.pokemon_settings.type_2 || null,
},
stats: {
baseAttack: (entry.pokemon_settings.stats || {}).base_attack || 0,
baseDefense: (entry.pokemon_settings.stats || {}).base_defense || 0,
baseStamina: (entry.pokemon_settings.stats || {}).base_stamina || 0,
},
statsRank: {
attackRank: -1,
defenseRank: -1,
staminaRank: -1,
},
};
pokemonData[pokemonId]!.push(mon);
const key = POGOProtos.Enums.PokemonId[mon.id] + POGOProtos.Enums.Form[mon.form];
pokemonBaseStamina.push({
id: mon.id,
form: mon.form,
value: mon.stats.baseStamina,
});
pokemonBaseAttack.push({
id: mon.id,
form: mon.form,
value: mon.stats.baseAttack,
});
pokemonBaseDefense.push({
id: mon.id,
form: mon.form,
value: mon.stats.baseDefense,
});
pokemonOrderById[key] = {
staminaRank: -1,
attackRank: -1,
defenseRank: -1,
};
// } else if (entry.template_id.indexOf('FORMS_V') === 0 && entry.form_settings) {
// const pokemonId = entry.form_settings.pokemon || POGOProtos.Enums.PokemonId.MISSINGNO;
// if (entry.form_settings.forms) {
// pokemonForms[pokemonId] = entry.form_settings.forms.reduce((output : Array<POGOProtos.Enums.Form>, form) => {
// if (form.form) {
// output.push(form.form);
// }
// return output;
// }, []);
// } else if (pokemonId) {
// pokemonForms[pokemonId] = [POGOProtos.Enums.Form.FORM_UNSET];
// }
}
}
// const orderedPokemon = Object.values(pokemonData).sort((a, b) => {
const orderedPokemon = (Object.values(pokemonData) as unknown as Array<Array<IPokemon>>).sort((a, b) => {
return a[0].order - b[0].order;
});
orderedPokemon.forEach((value) => {
value.sort((a, b) => {
return a.form - b.form;
});
});
let flatOrderedPokemon : Array<IPokemon> = [];
flatOrderedPokemon = flatOrderedPokemon.concat(...orderedPokemon);
// calculate pokemon relative rankings
pokemonBaseStamina.sort((a, b) => {
return a.value - b.value;
});
pokemonBaseStamina.forEach((stats, index, array) => {
const key = POGOProtos.Enums.PokemonId[stats.id] + POGOProtos.Enums.Form[stats.form];
pokemonOrderById[key].staminaRank = Math.floor((index / (array.length - 1)) * 100);
});
pokemonBaseAttack.sort((a, b) => {
return a.value - b.value;
});
pokemonBaseAttack.forEach((stats, index, array) => {
const key = POGOProtos.Enums.PokemonId[stats.id] + POGOProtos.Enums.Form[stats.form];
pokemonOrderById[key].attackRank = Math.floor((index / (array.length - 1)) * 100);
});
pokemonBaseDefense.sort((a, b) => {
return a.value - b.value;
});
pokemonBaseDefense.forEach((stats, index, array) => {
const key = POGOProtos.Enums.PokemonId[stats.id] + POGOProtos.Enums.Form[stats.form];
pokemonOrderById[key].defenseRank = Math.floor((index / (array.length - 1)) * 100);
});
flatOrderedPokemon.forEach((mon) => {
const key = POGOProtos.Enums.PokemonId[mon.id] + POGOProtos.Enums.Form[mon.form];
mon.statsRank = { ...pokemonOrderById[key] };
});
return flatOrderedPokemon;
};