145 lines
5.9 KiB
TypeScript
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;
|
|
};
|