diff --git a/data/abilities.json b/data/abilities.json new file mode 100644 index 0000000..1c93bd5 --- /dev/null +++ b/data/abilities.json @@ -0,0 +1,1066 @@ +[ + { + "name": "Stench", + "id": 1 + }, + { + "name": "Drizzle", + "id": 2 + }, + { + "name": "Speed Boost", + "id": 3 + }, + { + "name": "Battle Armor", + "id": 4 + }, + { + "name": "Sturdy", + "id": 5 + }, + { + "name": "Damp", + "id": 6 + }, + { + "name": "Limber", + "id": 7 + }, + { + "name": "Sand Veil", + "id": 8 + }, + { + "name": "Static", + "id": 9 + }, + { + "name": "Volt Absorb", + "id": 10 + }, + { + "name": "Water Absorb", + "id": 11 + }, + { + "name": "Oblivious", + "id": 12 + }, + { + "name": "Cloud Nine", + "id": 13 + }, + { + "name": "Compound Eyes", + "id": 14 + }, + { + "name": "Insomnia", + "id": 15 + }, + { + "name": "Color Change", + "id": 16 + }, + { + "name": "Immunity", + "id": 17 + }, + { + "name": "Flash Fire", + "id": 18 + }, + { + "name": "Shield Dust", + "id": 19 + }, + { + "name": "Own Tempo", + "id": 20 + }, + { + "name": "Suction Cups", + "id": 21 + }, + { + "name": "Intimidate", + "id": 22 + }, + { + "name": "Shadow Tag", + "id": 23 + }, + { + "name": "Rough skin", + "id": 24 + }, + { + "name": "Wonder Guard", + "id": 25 + }, + { + "name": "Levitate", + "id": 26 + }, + { + "name": "Effect Spore", + "id": 27 + }, + { + "name": "Synchronize", + "id": 28 + }, + { + "name": "Clear Body", + "id": 29 + }, + { + "name": "Natural Cure", + "id": 30 + }, + { + "name": "Lightning Rod", + "id": 31 + }, + { + "name": "Serene Grace", + "id": 32 + }, + { + "name": "Swift Swim", + "id": 33 + }, + { + "name": "Chlorophyll", + "id": 34 + }, + { + "name": "Illuminate", + "id": 35 + }, + { + "name": "Trace", + "id": 36 + }, + { + "name": "Huge Power", + "id": 37 + }, + { + "name": "Poison Point", + "id": 38 + }, + { + "name": "Inner Focus", + "id": 39 + }, + { + "name": "Magma Armor", + "id": 40 + }, + { + "name": "Water Veil", + "id": 41 + }, + { + "name": "Magnet Pull", + "id": 42 + }, + { + "name": "Soundproof", + "id": 43 + }, + { + "name": "Rain Dish", + "id": 44 + }, + { + "name": "Sand Stream", + "id": 45 + }, + { + "name": "Pressure", + "id": 46 + }, + { + "name": "Thick Fat", + "id": 47 + }, + { + "name": "Early Bird", + "id": 48 + }, + { + "name": "Flame Body", + "id": 49 + }, + { + "name": "Run Away", + "id": 50 + }, + { + "name": "Keen Eye", + "id": 51 + }, + { + "name": "Hyper Cutter", + "id": 52 + }, + { + "name": "Pickup", + "id": 53 + }, + { + "name": "Truant", + "id": 54 + }, + { + "name": "Hustle", + "id": 55 + }, + { + "name": "Cute Charm", + "id": 56 + }, + { + "name": "Plus", + "id": 57 + }, + { + "name": "Minus", + "id": 58 + }, + { + "name": "Forecast", + "id": 59 + }, + { + "name": "Sticky Hold", + "id": 60 + }, + { + "name": "Shed Skin", + "id": 61 + }, + { + "name": "Guts", + "id": 62 + }, + { + "name": "Marvel Scale", + "id": 63 + }, + { + "name": "Liquid Ooze", + "id": 64 + }, + { + "name": "Overgrow", + "id": 65 + }, + { + "name": "Blaze", + "id": 66 + }, + { + "name": "Torrent", + "id": 67 + }, + { + "name": "Swarm", + "id": 68 + }, + { + "name": "Rock Head", + "id": 69 + }, + { + "name": "Drought", + "id": 70 + }, + { + "name": "Arena Trap", + "id": 71 + }, + { + "name": "Vital Spirit", + "id": 72 + }, + { + "name": "White Smoke", + "id": 73 + }, + { + "name": "Pure Power", + "id": 74 + }, + { + "name": "Shell Armor", + "id": 75 + }, + { + "name": "Air Lock", + "id": 76 + }, + { + "name": "Tangled Feet", + "id": 77 + }, + { + "name": "Motor Drive", + "id": 78 + }, + { + "name": "Rivalry", + "id": 79 + }, + { + "name": "Steadfast", + "id": 80 + }, + { + "name": "Snow Cloak", + "id": 81 + }, + { + "name": "Gluttony", + "id": 82 + }, + { + "name": "Anger Point", + "id": 83 + }, + { + "name": "Unburden", + "id": 84 + }, + { + "name": "Heatproof", + "id": 85 + }, + { + "name": "Simple", + "id": 86 + }, + { + "name": "Dry Skin", + "id": 87 + }, + { + "name": "Download", + "id": 88 + }, + { + "name": "Iron Fist", + "id": 89 + }, + { + "name": "Poison Heal", + "id": 90 + }, + { + "name": "Adaptability", + "id": 91 + }, + { + "name": "Skill Link", + "id": 92 + }, + { + "name": "Hydration", + "id": 93 + }, + { + "name": "Solar Power", + "id": 94 + }, + { + "name": "Quick Feet", + "id": 95 + }, + { + "name": "Normalize", + "id": 96 + }, + { + "name": "Sniper", + "id": 97 + }, + { + "name": "Magic Guard", + "id": 98 + }, + { + "name": "No Guard", + "id": 99 + }, + { + "name": "Stall", + "id": 100 + }, + { + "name": "Technician", + "id": 101 + }, + { + "name": "Leaf Guard", + "id": 102 + }, + { + "name": "Klutz", + "id": 103 + }, + { + "name": "Mold Breaker", + "id": 104 + }, + { + "name": "Super Luck", + "id": 105 + }, + { + "name": "Aftermath", + "id": 106 + }, + { + "name": "Anticipation", + "id": 107 + }, + { + "name": "Forewarn", + "id": 108 + }, + { + "name": "Unaware", + "id": 109 + }, + { + "name": "Tinted Lens", + "id": 110 + }, + { + "name": "Filter", + "id": 111 + }, + { + "name": "Slow Start", + "id": 112 + }, + { + "name": "Scrappy", + "id": 113 + }, + { + "name": "Storm Drain", + "id": 114 + }, + { + "name": "Ice Body", + "id": 115 + }, + { + "name": "Solid Rock", + "id": 116 + }, + { + "name": "Snow Warning", + "id": 117 + }, + { + "name": "Honey Gather", + "id": 118 + }, + { + "name": "Frisk", + "id": 119 + }, + { + "name": "Reckless", + "id": 120 + }, + { + "name": "Multitype", + "id": 121 + }, + { + "name": "Flower Gift", + "id": 122 + }, + { + "name": "Bad Dreams", + "id": 123 + }, + { + "name": "Pickpocket", + "id": 124 + }, + { + "name": "Sheer Force", + "id": 125 + }, + { + "name": "Contrary", + "id": 126 + }, + { + "name": "Unnerve", + "id": 127 + }, + { + "name": "Defiant", + "id": 128 + }, + { + "name": "Defeatist", + "id": 129 + }, + { + "name": "Cursed Body", + "id": 130 + }, + { + "name": "Healer", + "id": 131 + }, + { + "name": "Friend Guard", + "id": 132 + }, + { + "name": "Weak Armor", + "id": 133 + }, + { + "name": "Heavy Metal", + "id": 134 + }, + { + "name": "Light Metal", + "id": 135 + }, + { + "name": "Multiscale", + "id": 136 + }, + { + "name": "Toxic Boost", + "id": 137 + }, + { + "name": "Flare Boost", + "id": 138 + }, + { + "name": "Harvest", + "id": 139 + }, + { + "name": "Telepathy", + "id": 140 + }, + { + "name": "Moody", + "id": 141 + }, + { + "name": "Overcoat", + "id": 142 + }, + { + "name": "Poison Touch", + "id": 143 + }, + { + "name": "Regenerator", + "id": 144 + }, + { + "name": "Big Pecks", + "id": 145 + }, + { + "name": "Sand Rush", + "id": 146 + }, + { + "name": "Wonder Skin", + "id": 147 + }, + { + "name": "Analytic", + "id": 148 + }, + { + "name": "Illusion", + "id": 149 + }, + { + "name": "Imposter", + "id": 150 + }, + { + "name": "Infiltrator", + "id": 151 + }, + { + "name": "Mummy", + "id": 152 + }, + { + "name": "Moxie", + "id": 153 + }, + { + "name": "Justified", + "id": 154 + }, + { + "name": "Rattled", + "id": 155 + }, + { + "name": "Magic Bounce", + "id": 156 + }, + { + "name": "Sap Slipper", + "id": 157 + }, + { + "name": "Prankster", + "id": 158 + }, + { + "name": "Sand Force", + "id": 159 + }, + { + "name": "Iron Barbs", + "id": 160 + }, + { + "name": "Zen Mode", + "id": 161 + }, + { + "name": "Victory Star", + "id": 162 + }, + { + "name": "Turboblaze", + "id": 163 + }, + { + "name": "Teravolt", + "id": 164 + }, + { + "name": "Aroma Veil", + "id": 165 + }, + { + "name": "Flower Veil", + "id": 166 + }, + { + "name": "Cheek Pouch", + "id": 167 + }, + { + "name": "Protean", + "id": 168 + }, + { + "name": "Fur Coat", + "id": 169 + }, + { + "name": "Magician", + "id": 170 + }, + { + "name": "Bulletproof", + "id": 171 + }, + { + "name": "Competitive", + "id": 172 + }, + { + "name": "Strong Jaw", + "id": 173 + }, + { + "name": "Refrigerate", + "id": 174 + }, + { + "name": "Sweet Veil", + "id": 175 + }, + { + "name": "Stance Change", + "id": 176 + }, + { + "name": "Gale Wings", + "id": 177 + }, + { + "name": "Mega Launcher", + "id": 178 + }, + { + "name": "Grass Pelt", + "id": 179 + }, + { + "name": "Symbiosis", + "id": 180 + }, + { + "name": "Tough Claws", + "id": 181 + }, + { + "name": "Pixilate", + "id": 182 + }, + { + "name": "Gooey", + "id": 183 + }, + { + "name": "Aerilate", + "id": 184 + }, + { + "name": "Parental Bond", + "id": 185 + }, + { + "name": "Dark Aura", + "id": 186 + }, + { + "name": "Fairy Aura", + "id": 187 + }, + { + "name": "Primordial Sea", + "id": 188 + }, + { + "name": "Desolate Land", + "id": 189 + }, + { + "name": "Delta Stream", + "id": 190 + }, + { + "name": "Stamina", + "id": 191 + }, + { + "name": "Wimp Out", + "id": 192 + }, + { + "name": "Emergency Exit", + "id": 193 + }, + { + "name": "Water Compaction", + "id": 194 + }, + { + "name": "Merciless", + "id": 195 + }, + { + "name": "Shields Down", + "id": 196 + }, + { + "name": "Stakeout", + "id": 197 + }, + { + "name": "Water Bubble", + "id": 198 + }, + { + "name": "Steelworker", + "id": 199 + }, + { + "name": "Berserk", + "id": 200 + }, + { + "name": "Slush Rush", + "id": 201 + }, + { + "name": "Long Reach", + "id": 202 + }, + { + "name": "Liquid Voice", + "id": 203 + }, + { + "name": "Triage", + "id": 204 + }, + { + "name": "Galvanize", + "id": 205 + }, + { + "name": "Surge Surfer", + "id": 206 + }, + { + "name": "Schooling", + "id": 207 + }, + { + "name": "Disguise", + "id": 208 + }, + { + "name": "Battle Bond", + "id": 209 + }, + { + "name": "Power Construct", + "id": 210 + }, + { + "name": "Corrosion", + "id": 211 + }, + { + "name": "Comatose", + "id": 212 + }, + { + "name": "Queenly Majesty", + "id": 213 + }, + { + "name": "Innards Out", + "id": 214 + }, + { + "name": "Dancer", + "id": 215 + }, + { + "name": "Battery", + "id": 216 + }, + { + "name": "Fluffy", + "id": 217 + }, + { + "name": "Dazzling", + "id": 218 + }, + { + "name": "Soul-Heart", + "id": 219 + }, + { + "name": "Tangling Hair", + "id": 220 + }, + { + "name": "Receiver", + "id": 221 + }, + { + "name": "Power of Alchemy", + "id": 222 + }, + { + "name": "Beast Boost", + "id": 223 + }, + { + "name": "RKS System", + "id": 224 + }, + { + "name": "Electric Surge", + "id": 225 + }, + { + "name": "Psychic Surge", + "id": 226 + }, + { + "name": "Misty Surge", + "id": 227 + }, + { + "name": "Grassy Surge", + "id": 228 + }, + { + "name": "Full Metal Body", + "id": 229 + }, + { + "name": "Shadow Shield", + "id": 230 + }, + { + "name": "Prism Armor", + "id": 231 + }, + { + "name": "Neuroforce", + "id": 232 + }, + { + "name": "Intrepid Sword", + "id": 233 + }, + { + "name": "Dauntless Shield", + "id": 234 + }, + { + "name": "Libero", + "id": 235 + }, + { + "name": "Ball Fetch", + "id": 236 + }, + { + "name": "Cotton Down", + "id": 237 + }, + { + "name": "Propeller Tail", + "id": 238 + }, + { + "name": "Mirror Armor", + "id": 239 + }, + { + "name": "Gulp Missile", + "id": 240 + }, + { + "name": "Stalwart", + "id": 241 + }, + { + "name": "Steam Engine", + "id": 242 + }, + { + "name": "Punk Rock", + "id": 243 + }, + { + "name": "Sand Spit", + "id": 244 + }, + { + "name": "Ice Scales", + "id": 245 + }, + { + "name": "Ripen", + "id": 246 + }, + { + "name": "Ice Face", + "id": 247 + }, + { + "name": "Power Spot", + "id": 248 + }, + { + "name": "Mimicry", + "id": 249 + }, + { + "name": "Screen Cleaner", + "id": 250 + }, + { + "name": "Steely Spirit", + "id": 251 + }, + { + "name": "Perish Body", + "id": 252 + }, + { + "name": "Wandering Spirit", + "id": 253 + }, + { + "name": "Gorilla Tactics", + "id": 254 + }, + { + "name": "Neutralizing Gas", + "id": 255 + }, + { + "name": "Pastel Veil", + "id": 256 + }, + { + "name": "Hunger Switch", + "id": 257 + }, + { + "name": "Quick Draw", + "id": 258 + }, + { + "name": "Unseen Fist", + "id": 259 + }, + { + "name": "Curious Medecine", + "id": 260 + }, + { + "name": "Transistor", + "id": 261 + }, + { + "name": "Dragon's Maw", + "id": 262 + }, + { + "name": "Chiling Neigh", + "id": 263 + }, + { + "name": "Grim Neigh", + "id": 264 + }, + { + "name": "As One", + "id": 265 + }, + { + "name": "As One", + "id": 266 + } +] diff --git a/src/database.rs b/src/database.rs index aa34e96..76811fa 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,10 +7,7 @@ use std::{ use bevy::prelude::*; use json::JsonValue; -use crate::{ - json::{read_characteristics, read_natures, read_pokedex}, - pokemon::*, -}; +use crate::{json::*, pokemon::*}; pub struct DatabasePlugin; @@ -19,6 +16,7 @@ impl Plugin for DatabasePlugin { app.insert_resource(Database::::default()) .insert_resource(Database::::default()) .insert_resource(Database::::default()) + .insert_resource(Database::::default()) .add_startup_system(database_setup); } } @@ -46,9 +44,11 @@ where fn database_setup( mut pokemon_database: ResMut>, + mut ability_database: ResMut>, mut nature_database: ResMut>, mut characteristic_database: ResMut>, ) { + ability_database.populate_from_json(&read_abilities()); nature_database.populate_from_json(&read_natures()); pokemon_database.populate_from_json(&read_pokedex()); characteristic_database.populate_from_json(&read_characteristics()); diff --git a/src/ivpeek/battle.rs b/src/ivpeek/battle.rs index 141b6c7..80efa6d 100644 --- a/src/ivpeek/battle.rs +++ b/src/ivpeek/battle.rs @@ -1,4 +1,4 @@ -use super::inspector::{Battle, PokemonInstance}; +use super::inspector::{Battle, ParsedPokemonInstance}; use crate::pokemon::*; const PLAYER_PARTY_OFFSET: usize = 0x21E42C; @@ -29,7 +29,7 @@ struct Header { checksum: u16, } -pub fn parse_battle_party(data: &Vec) -> Result { +pub fn parse_battle_party(data: &Vec) -> Result, String> { let mut battle = Battle::default(); for i in 0..PARTY_MAX_SIZE { if let Some(instance) = try_parse(data, PLAYER_PARTY_OFFSET + i * POKEMON_SIZE) { @@ -42,13 +42,13 @@ pub fn parse_battle_party(data: &Vec) -> Result { Ok(battle) } -fn try_parse(data: &[u8], offset: usize) -> Option { +fn try_parse(data: &[u8], offset: usize) -> Option { let encrypted = &data[offset..offset + POKEMON_SIZE]; if !contains_pokemon(encrypted) { return None; } let decrypted = decrypt_pokemon(encrypted).ok()?; - // println!("{}", debug_pokemon_data(&decrypted)); + println!("{}", debug_pokemon_data(&decrypted)); let pokemon = parse_pokemon(&decrypted).ok()?; Some(pokemon) } @@ -113,40 +113,18 @@ fn decrypt_pokemon(encrypted: &[u8]) -> Result, String> { Ok(unshuffled) } -fn parse_pokemon(data: &[u8]) -> Result { +fn parse_pokemon(data: &[u8]) -> Result { validate_size(data)?; - let mut instance = PokemonInstance::default(); + let mut instance = ParsedPokemonInstance::default(); let offset_a: usize = 0x08 + 0 * BLOCK_SIZE; let offset_b: usize = 0x08 + 1 * BLOCK_SIZE; let offset_c: usize = 0x08 + 2 * BLOCK_SIZE; let offset_d: usize = 0x08 + 3 * BLOCK_SIZE; - instance.pokemon.national = read_short(data, offset_a + 0x00); - - let ivs = read_long(data, offset_b + 0x10); - instance.ivs.insert(BaseStat::Hp, ((ivs >> 0) & 31) as u8); - instance - .ivs - .insert(BaseStat::Attack, ((ivs >> 5) & 31) as u8); - instance - .ivs - .insert(BaseStat::Defense, ((ivs >> 10) & 31) as u8); - instance - .ivs - .insert(BaseStat::Speed, ((ivs >> 15) & 31) as u8); - instance - .ivs - .insert(BaseStat::SpecialAttack, ((ivs >> 20) & 31) as u8); - instance - .ivs - .insert(BaseStat::SpecialDefense, ((ivs >> 25) & 31) as u8); - - instance.nature = Some(Nature { - pid: read_byte(data, offset_b + 0x19), - ..Default::default() - }); + instance.pokemon = read_short(data, offset_a + 0x00); + instance.ability = read_byte(data, offset_a + 0x0D); instance .evs @@ -167,6 +145,26 @@ fn parse_pokemon(data: &[u8]) -> Result { .evs .insert(BaseStat::SpecialDefense, read_byte(data, offset_a + 0x15)); + let ivs = read_long(data, offset_b + 0x10); + instance.ivs.insert(BaseStat::Hp, ((ivs >> 0) & 31) as u8); + instance + .ivs + .insert(BaseStat::Attack, ((ivs >> 5) & 31) as u8); + instance + .ivs + .insert(BaseStat::Defense, ((ivs >> 10) & 31) as u8); + instance + .ivs + .insert(BaseStat::Speed, ((ivs >> 15) & 31) as u8); + instance + .ivs + .insert(BaseStat::SpecialAttack, ((ivs >> 20) & 31) as u8); + instance + .ivs + .insert(BaseStat::SpecialDefense, ((ivs >> 25) & 31) as u8); + + instance.nature = read_byte(data, offset_b + 0x19); + Ok(instance) } diff --git a/src/ivpeek/inspector.rs b/src/ivpeek/inspector.rs index ea0757c..cbd7df5 100644 --- a/src/ivpeek/inspector.rs +++ b/src/ivpeek/inspector.rs @@ -3,7 +3,10 @@ use std::collections::HashMap; use bevy::prelude::*; use bevy_egui::EguiContexts; -use crate::{database::load_url_asset, pokemon::*}; +use crate::{ + database::{load_url_asset, Database}, + pokemon::*, +}; use super::ui::{EguiAsset, UiAssets}; @@ -18,23 +21,82 @@ impl Plugin for InspectorPlugin { #[derive(Resource, Default)] pub struct Inspector { - pub battle: Battle, + pub battle: Battle, } #[derive(Default, Clone)] -pub struct Battle { - pub player_party: Vec, - pub enemy_party: Vec, +pub struct Battle { + pub player_party: Vec, + pub enemy_party: Vec, } #[derive(Default, Clone)] pub struct PokemonInstance { pub pokemon: Pokemon, + pub ability: Option, pub nature: Option, pub ivs: HashMap, pub evs: HashMap, } +impl PokemonInstance { + pub fn from_parsed( + parsed: &ParsedPokemonInstance, + pokedex: &Res>, + natures: &Res>, + abilities: &Res>, + ) -> Result { + let mut instance = PokemonInstance::default(); + + // pokemon data + if let Some(pokemon) = pokedex + .map + .values() + .find(|pokemon| pokemon.national == parsed.pokemon) + { + instance.pokemon = pokemon.clone(); + } else { + return Err(format!("National dex not found: {}", parsed.pokemon)); + } + + // ability data + if let Some(ability) = abilities + .map + .values() + .find(|ability| ability.id == parsed.ability as u16) + { + instance.ability = Some(ability.clone()); + } else { + return Err(format!("Ability id not found: {}", parsed.ability)); + } + + // nature data + if let Some(nature) = natures + .map + .values() + .find(|nature| nature.pid == parsed.nature) + { + instance.nature = Some(nature.clone()); + } else { + return Err(format!("Nature pid not found: {}", parsed.nature)); + } + + instance.ivs = parsed.ivs.clone(); + instance.evs = parsed.evs.clone(); + + Ok(instance) + } +} + +#[derive(Default, Clone)] +pub struct ParsedPokemonInstance { + pub pokemon: u16, + pub ability: u8, + pub nature: u8, + pub ivs: HashMap, + pub evs: HashMap, +} + fn state_changed( inspector: Res, assets: Res, diff --git a/src/ivpeek/memory_reader.rs b/src/ivpeek/memory_reader.rs index 81a8925..c3767cd 100644 --- a/src/ivpeek/memory_reader.rs +++ b/src/ivpeek/memory_reader.rs @@ -9,16 +9,13 @@ use std::{ use crate::{database::Database, pokemon::*}; -use super::{ - battle::parse_battle_party, - inspector::{Battle, Inspector}, -}; +use super::{battle::parse_battle_party, inspector::*}; pub struct MemoryReaderPlugin; impl Plugin for MemoryReaderPlugin { fn build(&self, app: &mut App) { - app.add_event::() + app.add_event::() .insert_resource(EventQueue::default()) .add_startup_system(init_memory_reader) .add_system(flush_updates) @@ -27,54 +24,40 @@ impl Plugin for MemoryReaderPlugin { } #[derive(Default)] -pub struct ParsedBattleEvent(Battle); +pub struct BattleEvent(Battle); #[derive(Resource, Default)] struct EventQueue { - updates: Arc>>, + updates: Arc>>>, } fn flush_updates( queue: Res, pokedex: Res>, + abilities: Res>, natures: Res>, - mut events: EventWriter, + mut events: EventWriter, ) { match queue.updates.lock() { Ok(mut updates) => { - for mut update in updates.drain(..) { - for mut instance in update - .0 - .player_party - .iter_mut() - .chain(update.0.enemy_party.iter_mut()) - { - // Fill pokemon data - if let Some(pokemon) = pokedex - .map - .values() - .find(|pokemon| pokemon.national == instance.pokemon.national) - { - instance.pokemon = pokemon.clone(); - } else { - warn!("National dex not found: {}", instance.pokemon.national); - } + for update in updates.drain(..) { + let mut battle = Battle::default(); - // Fill nature data - if let Some(instance_nature) = instance.nature.clone() { - if let Some(nature) = natures - .map - .values() - .find(|nature| nature.pid == instance_nature.pid) - { - instance.nature = Some(nature.clone()); - } else { - warn!("Nature pid not found: {}", instance_nature.pid); - } + for parsed in update.player_party.iter() { + match PokemonInstance::from_parsed(parsed, &pokedex, &natures, &abilities) { + Ok(instance) => battle.player_party.push(instance), + Err(err) => warn!("{err}"), } } - events.send(update) + for parsed in update.enemy_party.iter() { + match PokemonInstance::from_parsed(parsed, &pokedex, &natures, &abilities) { + Ok(instance) => battle.enemy_party.push(instance), + Err(err) => warn!("{err}"), + } + } + + events.send(BattleEvent(battle)) } } Err(err) => { @@ -83,7 +66,7 @@ fn flush_updates( } } -fn update_inspector(mut inspector: ResMut, mut events: EventReader) { +fn update_inspector(mut inspector: ResMut, mut events: EventReader) { for event in events.iter() { inspector.battle = event.0.clone(); } @@ -113,7 +96,7 @@ fn init_memory_reader(queue: Res) { match fs::read(&event.path) { Ok(data) => match parse_battle_party(&data) { Ok(battle) => match updates.lock() { - Ok(mut updates) => updates.push(ParsedBattleEvent(battle)), + Ok(mut updates) => updates.push(battle), Err(err) => error!("{err}"), }, Err(err) => error!("Failed to parse party data: ${err}"), diff --git a/src/ivpeek/ui.rs b/src/ivpeek/ui.rs index c81da11..2471b67 100644 --- a/src/ivpeek/ui.rs +++ b/src/ivpeek/ui.rs @@ -9,7 +9,7 @@ use super::inspector::{Inspector, PokemonInstance}; const BAR_HEIGHT: f32 = 12.0; const SPRITE_SIZE: f32 = 128.0; -const SPRITE_PADDING: f32 = 32.0; +const SPRITE_PADDING: f32 = 16.0; const BASE_STAT_WIDTH_MULT: f32 = 0.5; const IV_WIDTH_MULT: f32 = 4.0; const EV_WIDTH_MULT: f32 = 0.25; @@ -106,66 +106,69 @@ fn render_pokemon_instance( ) { let sprite_size = egui::Vec2::new(SPRITE_SIZE, SPRITE_SIZE); let pokemon = instance.pokemon.clone(); + let ability = instance.ability.clone(); let nature = instance.nature.clone(); ui.horizontal(|ui| { - ui.horizontal(|ui| { - ui.set_min_width(sprite_size.x + SPRITE_PADDING); - ui.set_max_width(sprite_size.x + SPRITE_PADDING); - // Name - ui.label(egui::RichText::new(&pokemon.full_name).color(egui::Color32::LIGHT_GRAY)); + ui.vertical(|ui| { + ui.horizontal(|ui| { + ui.set_min_width(sprite_size.x + SPRITE_PADDING); + ui.set_max_width(sprite_size.x + SPRITE_PADDING); + // Name + ui.label(egui::RichText::new(&pokemon.full_name).color(egui::Color32::LIGHT_GRAY)); - // Types - ui.label(format!( - "{} / {}", - pokemon.type1.as_ref().map_or("-", |t| t.name.as_str()), - pokemon.type2.as_ref().map_or("-", |t| t.name.as_str()) - )); - }); + // Types + ui.label(format!( + "{} / {}", + pokemon.type1.as_ref().map_or("-", |t| t.name.as_str()), + pokemon.type2.as_ref().map_or("-", |t| t.name.as_str()) + )); + }); - // Headings - ui.horizontal(|ui| { - for (i, width) in COLUMNS.iter().enumerate() { - ui.horizontal(|ui| { - ui.set_width(*width); - match i { - 1 => { - ui.label(egui::RichText::new("Base stat")); - } - 2 => { - ui.label(egui::RichText::new("EV")); - } - 3 => { - ui.label(egui::RichText::new(format!( - "{} / 510", - instance.evs.values().map(|v| *v as i32).sum::() - ))); - } - 5 => { - ui.label(egui::RichText::new("IV")); - } - _ => (), - } - }); - } - }); - }); + ui.label(ability.as_ref().map_or("-", |a| a.name.as_str())); - ui.horizontal(|ui| { - // Sprite - ui.horizontal(|ui| { - ui.set_min_size(sprite_size); - ui.set_max_size(sprite_size); - if let Some(EguiAsset(_, rendered_texture_id)) = - ui_assets.sprite_map.get(&pokemon.key()) - { - ui.add(egui::Image::new(*rendered_texture_id, sprite_size)); - } + // Sprite + ui.horizontal(|ui| { + ui.set_min_size(sprite_size); + ui.set_max_size(sprite_size); + if let Some(EguiAsset(_, rendered_texture_id)) = + ui_assets.sprite_map.get(&pokemon.key()) + { + ui.add(egui::Image::new(*rendered_texture_id, sprite_size)); + } + }); }); ui.add_space(SPRITE_PADDING); ui.vertical(|ui| { + ui.add_space(16.); + // Headings + ui.horizontal(|ui| { + for (i, width) in COLUMNS.iter().enumerate() { + ui.horizontal(|ui| { + ui.set_width(*width); + match i { + 1 => { + ui.label(egui::RichText::new("Base stat")); + } + 2 => { + ui.label(egui::RichText::new("EV")); + } + 3 => { + ui.label(egui::RichText::new(format!( + "{} / 510", + instance.evs.values().map(|v| *v as i32).sum::() + ))); + } + 5 => { + ui.label(egui::RichText::new("IV")); + } + _ => (), + } + }); + } + }); for stat in BaseStat::all() { ui.horizontal(|ui| { let mut column_size = COLUMNS.iter(); diff --git a/src/json.rs b/src/json.rs index 41b6df5..4bea199 100644 --- a/src/json.rs +++ b/src/json.rs @@ -26,6 +26,10 @@ pub fn parse_json_file>(path: P) -> JsonValue { json::parse(fs::read_to_string(path).unwrap().as_ref()).unwrap() } +pub fn read_abilities() -> JsonValue { + parse_json_file(DATA_DIR.join("abilities.json")) +} + pub fn read_natures() -> JsonValue { parse_json_file(DATA_DIR.join("natures.json")) } diff --git a/src/pokemon.rs b/src/pokemon.rs index 558faf4..0c52629 100644 --- a/src/pokemon.rs +++ b/src/pokemon.rs @@ -247,6 +247,30 @@ impl GetKey for Type { } } +#[derive(Default, Debug, Clone)] +pub struct Ability { + pub id: u16, + pub name: String, +} + +impl FromJson for Ability { + fn from_json(json: &json::JsonValue) -> Option + where + Self: Sized, + { + Some(Self { + id: json["id"].as_u16()?, + name: json["name"].as_str()?.to_string(), + }) + } +} + +impl GetKey for Ability { + fn key(&self) -> String { + self.name.clone() + } +} + #[derive(Default, Debug, Clone, Reflect, FromReflect, PartialEq)] pub struct Nature { pub id: u8,