started implementing iv-peeker

master
hheik 2023-03-30 23:11:36 +03:00
parent 920cff3bbe
commit 635e67d6c7
16 changed files with 359 additions and 92 deletions

2
Cargo.lock generated
View File

@ -3286,7 +3286,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "smart-iv-calculator" name = "smart_iv_calculator"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bevy", "bevy",

View File

@ -1,5 +1,5 @@
[package] [package]
name = "smart-iv-calculator" name = "smart_iv_calculator"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

View File

@ -2,6 +2,7 @@
"name": "Hardy", "name": "Hardy",
"alias": "hardy", "alias": "hardy",
"id": 1, "id": 1,
"pid": 0,
"increased": null, "increased": null,
"decreased": null "decreased": null
}, },
@ -9,6 +10,7 @@
"name": "Bold", "name": "Bold",
"alias": "bold", "alias": "bold",
"id": 2, "id": 2,
"pid": 5,
"increased": "defense", "increased": "defense",
"decreased": "attack" "decreased": "attack"
}, },
@ -16,6 +18,7 @@
"name": "Modest", "name": "Modest",
"alias": "modest", "alias": "modest",
"id": 3, "id": 3,
"pid": 15,
"increased": "special_attack", "increased": "special_attack",
"decreased": "attack" "decreased": "attack"
}, },
@ -23,6 +26,7 @@
"name": "Calm", "name": "Calm",
"alias": "calm", "alias": "calm",
"id": 4, "id": 4,
"pid": 20,
"increased": "special_defense", "increased": "special_defense",
"decreased": "attack" "decreased": "attack"
}, },
@ -30,6 +34,7 @@
"name": "Timid", "name": "Timid",
"alias": "timid", "alias": "timid",
"id": 5, "id": 5,
"pid": 10,
"increased": "speed", "increased": "speed",
"decreased": "attack" "decreased": "attack"
}, },
@ -37,6 +42,7 @@
"name": "Lonely", "name": "Lonely",
"alias": "lonely", "alias": "lonely",
"id": 6, "id": 6,
"pid": 1,
"increased": "attack", "increased": "attack",
"decreased": "defense" "decreased": "defense"
}, },
@ -44,6 +50,7 @@
"name": "Docile", "name": "Docile",
"alias": "docile", "alias": "docile",
"id": 7, "id": 7,
"pid": 6,
"increased": null, "increased": null,
"decreased": null "decreased": null
}, },
@ -51,6 +58,7 @@
"name": "Mild", "name": "Mild",
"alias": "mild", "alias": "mild",
"id": 8, "id": 8,
"pid": 16,
"increased": "special_attack", "increased": "special_attack",
"decreased": "defense" "decreased": "defense"
}, },
@ -58,6 +66,7 @@
"name": "Gentle", "name": "Gentle",
"alias": "gentle", "alias": "gentle",
"id": 9, "id": 9,
"pid": 21,
"increased": "special_defense", "increased": "special_defense",
"decreased": "defense" "decreased": "defense"
}, },
@ -65,6 +74,7 @@
"name": "Hasty", "name": "Hasty",
"alias": "hasty", "alias": "hasty",
"id": 10, "id": 10,
"pid": 11,
"increased": "speed", "increased": "speed",
"decreased": "defense" "decreased": "defense"
}, },
@ -72,6 +82,7 @@
"name": "Adamant", "name": "Adamant",
"alias": "adamant", "alias": "adamant",
"id": 11, "id": 11,
"pid": 3,
"increased": "attack", "increased": "attack",
"decreased": "special_attack" "decreased": "special_attack"
}, },
@ -79,6 +90,7 @@
"name": "Impish", "name": "Impish",
"alias": "impish", "alias": "impish",
"id": 12, "id": 12,
"pid": 8,
"increased": "defense", "increased": "defense",
"decreased": "special_attack" "decreased": "special_attack"
}, },
@ -86,6 +98,7 @@
"name": "Bashful", "name": "Bashful",
"alias": "bashful", "alias": "bashful",
"id": 13, "id": 13,
"pid": 18,
"increased": null, "increased": null,
"decreased": null "decreased": null
}, },
@ -93,6 +106,7 @@
"name": "Careful", "name": "Careful",
"alias": "careful", "alias": "careful",
"id": 14, "id": 14,
"pid": 23,
"increased": "special_defense", "increased": "special_defense",
"decreased": "special_attack" "decreased": "special_attack"
}, },
@ -100,6 +114,7 @@
"name": "Rash", "name": "Rash",
"alias": "rash", "alias": "rash",
"id": 15, "id": 15,
"pid": 19,
"increased": "special_attack", "increased": "special_attack",
"decreased": "special_defense" "decreased": "special_defense"
}, },
@ -107,6 +122,7 @@
"name": "Jolly", "name": "Jolly",
"alias": "jolly", "alias": "jolly",
"id": 16, "id": 16,
"pid": 13,
"increased": "speed", "increased": "speed",
"decreased": "special_attack" "decreased": "special_attack"
}, },
@ -114,6 +130,7 @@
"name": "Naughty", "name": "Naughty",
"alias": "naughty", "alias": "naughty",
"id": 17, "id": 17,
"pid": 4,
"increased": "attack", "increased": "attack",
"decreased": "special_defense" "decreased": "special_defense"
}, },
@ -121,6 +138,7 @@
"name": "Lax", "name": "Lax",
"alias": "lax", "alias": "lax",
"id": 18, "id": 18,
"pid": 9,
"increased": "defense", "increased": "defense",
"decreased": "special_defense" "decreased": "special_defense"
}, },
@ -128,6 +146,7 @@
"name": "Quirky", "name": "Quirky",
"alias": "quirky", "alias": "quirky",
"id": 19, "id": 19,
"pid": 24,
"increased": null, "increased": null,
"decreased": null "decreased": null
}, },
@ -135,6 +154,7 @@
"name": "Naive", "name": "Naive",
"alias": "naive", "alias": "naive",
"id": 20, "id": 20,
"pid": 14,
"increased": "speed", "increased": "speed",
"decreased": "special_defense" "decreased": "special_defense"
}, },
@ -142,6 +162,7 @@
"name": "Brave", "name": "Brave",
"alias": "brave", "alias": "brave",
"id": 21, "id": 21,
"pid": 2,
"increased": "attack", "increased": "attack",
"decreased": "speed" "decreased": "speed"
}, },
@ -149,6 +170,7 @@
"name": "Relaxed", "name": "Relaxed",
"alias": "relaxed", "alias": "relaxed",
"id": 22, "id": 22,
"pid": 7,
"increased": "defense", "increased": "defense",
"decreased": "speed" "decreased": "speed"
}, },
@ -156,6 +178,7 @@
"name": "Quiet", "name": "Quiet",
"alias": "quiet", "alias": "quiet",
"id": 23, "id": 23,
"pid": 17,
"increased": "special_attack", "increased": "special_attack",
"decreased": "speed" "decreased": "speed"
}, },
@ -163,6 +186,7 @@
"name": "Sassy", "name": "Sassy",
"alias": "sassy", "alias": "sassy",
"id": 24, "id": 24,
"pid": 22,
"increased": "special_defense", "increased": "special_defense",
"decreased": "speed" "decreased": "speed"
}, },
@ -170,6 +194,7 @@
"name": "Serious", "name": "Serious",
"alias": "serious", "alias": "serious",
"id": 25, "id": 25,
"pid": 12,
"increased": null, "increased": null,
"decreased": null "decreased": null
} }

5
src/bin/ivcalc.rs Normal file
View File

@ -0,0 +1,5 @@
use smart_iv_calculator::ivcalc;
fn main() {
ivcalc::run()
}

5
src/bin/ivpeek.rs Normal file
View File

@ -0,0 +1,5 @@
use smart_iv_calculator::ivpeek;
fn main() {
ivpeek::run()
}

View File

@ -1,28 +1,13 @@
use std::collections::HashMap;
use bevy::prelude::*; use bevy::prelude::*;
use json::JsonValue; use json::JsonValue;
use lazy_static::lazy_static;
use std::{ use crate::{
collections::HashMap, json::{read_characteristics, read_natures, read_pokedex},
fs, pokemon::*,
path::{Path, PathBuf},
}; };
use crate::pokemon::*;
lazy_static! {
static ref DATA_DIR: PathBuf = "./data".into();
pub static ref TYPE_MAP: HashMap<u8, Type> = {
let types = parse_json_file(DATA_DIR.join("types.json"));
let mut map = HashMap::new();
for type_data in types.members() {
if let Some(type_data) = Type::from_json(type_data) {
map.insert(type_data.id, type_data);
}
}
map
};
}
pub struct DatabasePlugin; pub struct DatabasePlugin;
impl Plugin for DatabasePlugin { impl Plugin for DatabasePlugin {
@ -60,28 +45,7 @@ fn database_setup(
mut nature_database: ResMut<Database<Nature>>, mut nature_database: ResMut<Database<Nature>>,
mut characteristic_database: ResMut<Database<Characteristic>>, mut characteristic_database: ResMut<Database<Characteristic>>,
) { ) {
let natures = parse_json_file(DATA_DIR.join("natures.json")); nature_database.populate_from_json(&read_natures());
nature_database.populate_from_json(&natures); pokemon_database.populate_from_json(&read_pokedex());
let pokedex = parse_json_file(DATA_DIR.join("pokedex.json")); characteristic_database.populate_from_json(&read_characteristics());
pokemon_database.populate_from_json(&pokedex);
let characteristics = parse_json_file(DATA_DIR.join("characteristics.json"));
characteristic_database.populate_from_json(&characteristics);
}
pub fn parse_json_file<P: AsRef<Path>>(path: P) -> JsonValue {
json::parse(fs::read_to_string(path).unwrap().as_ref()).unwrap()
}
pub fn load_url_asset(
url: String,
path: PathBuf,
assets: &Res<AssetServer>,
) -> Result<Handle<Image>, Box<dyn std::error::Error>> {
let system_path = Path::new("assets").join(&path);
if Path::exists(&path) {
return Ok(assets.load(path));
}
let data = reqwest::blocking::get(&url)?.bytes()?;
fs::write(&system_path, data).unwrap();
Ok(assets.load(path))
} }

View File

@ -1,12 +1,10 @@
use bevy::{prelude::*, window::WindowResolution}; use bevy::{prelude::*, window::WindowResolution};
pub mod database; mod calculator;
pub mod inspector; mod server;
pub mod pokemon; mod ui;
pub mod server;
pub mod ui;
fn main() { pub fn run() {
App::new() App::new()
.add_plugins( .add_plugins(
DefaultPlugins DefaultPlugins
@ -21,8 +19,8 @@ fn main() {
}) })
.set(ImagePlugin::default_nearest()), .set(ImagePlugin::default_nearest()),
) )
.add_plugin(database::DatabasePlugin) .add_plugin(crate::database::DatabasePlugin)
.add_plugin(inspector::InspectorPlugin) .add_plugin(calculator::CalculatorPlugin)
.add_plugin(ui::UiPlugin) .add_plugin(ui::UiPlugin)
.add_plugin(server::ServerPlugin) .add_plugin(server::ServerPlugin)
.run(); .run();

View File

@ -1,19 +1,25 @@
use std::{
fs,
path::{Path, PathBuf},
};
use bevy::prelude::*; use bevy::prelude::*;
use bevy_egui::EguiContexts; use bevy_egui::EguiContexts;
use crate::{ use crate::database::*;
database::{load_url_asset, Database}, use crate::pokemon::*;
pokemon::*,
use super::{
server::Request, server::Request,
ui::{UiAssets, UiState}, ui::{UiAssets, UiState},
}; };
pub struct InspectorPlugin; pub struct CalculatorPlugin;
impl Plugin for InspectorPlugin { impl Plugin for CalculatorPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.register_type::<Inspector>() app.register_type::<Calculator>()
.insert_resource(Inspector::default()) .insert_resource(Calculator::default())
.add_system(state_changed) .add_system(state_changed)
.add_system(request_handler.in_base_set(CoreSet::PreUpdate)); .add_system(request_handler.in_base_set(CoreSet::PreUpdate));
} }
@ -21,7 +27,7 @@ impl Plugin for InspectorPlugin {
#[derive(Reflect, Resource, Default, Debug, Clone)] #[derive(Reflect, Resource, Default, Debug, Clone)]
#[reflect(Resource)] #[reflect(Resource)]
pub struct Inspector { pub struct Calculator {
pub pokemon: Option<Pokemon>, pub pokemon: Option<Pokemon>,
pub nature: Option<Nature>, pub nature: Option<Nature>,
pub characteristic: Option<Characteristic>, pub characteristic: Option<Characteristic>,
@ -29,15 +35,15 @@ pub struct Inspector {
} }
fn state_changed( fn state_changed(
inspector: Res<Inspector>, calculator: Res<Calculator>,
assets: Res<AssetServer>, assets: Res<AssetServer>,
mut ui_assets: ResMut<UiAssets>, mut ui_assets: ResMut<UiAssets>,
mut contexts: EguiContexts, mut contexts: EguiContexts,
) { ) {
if let (Some(pokemon), true) = (inspector.pokemon.as_ref(), inspector.is_changed()) { if let (Some(pokemon), true) = (calculator.pokemon.as_ref(), calculator.is_changed()) {
if !ui_assets.sprite_map.contains_key(&pokemon.key()) { if !ui_assets.sprite_map.contains_key(&pokemon.key()) {
let handle = load_url_asset( let handle = load_url_asset(
pokemon.get_url(), pokemon.sprite_url(),
format!("cache/{name}.png", name = pokemon.key()).into(), format!("cache/{name}.png", name = pokemon.key()).into(),
&assets, &assets,
); );
@ -53,7 +59,7 @@ fn state_changed(
fn request_handler( fn request_handler(
mut request_events: EventReader<Request>, mut request_events: EventReader<Request>,
mut inspector: ResMut<Inspector>, mut calculator: ResMut<Calculator>,
mut ui: ResMut<UiState>, mut ui: ResMut<UiState>,
characteristics: Res<Database<Characteristic>>, characteristics: Res<Database<Characteristic>>,
natures: Res<Database<Nature>>, natures: Res<Database<Nature>>,
@ -62,10 +68,10 @@ fn request_handler(
info!("{:#}", request); info!("{:#}", request);
match request.urn.as_str() { match request.urn.as_str() {
"clear" => { "clear" => {
inspector.pokemon = None; calculator.pokemon = None;
inspector.nature = None; calculator.nature = None;
inspector.characteristic = None; calculator.characteristic = None;
inspector.derived_stats = DerivedStats::default(); calculator.derived_stats = DerivedStats::default();
ui.name = String::from(""); ui.name = String::from("");
ui.level = String::from(""); ui.level = String::from("");
ui.derived_stats.clear(); ui.derived_stats.clear();
@ -77,11 +83,11 @@ fn request_handler(
} }
if let Some(level) = data["level"].as_i32() { if let Some(level) = data["level"].as_i32() {
ui.level = level.to_string(); ui.level = level.to_string();
inspector.derived_stats.level = Some(level); calculator.derived_stats.level = Some(level);
} }
if let Some(nature) = data["nature"].as_str() { if let Some(nature) = data["nature"].as_str() {
if let Some(nature) = natures.map.get(&nature.to_lowercase()).cloned() { if let Some(nature) = natures.map.get(&nature.to_lowercase()).cloned() {
inspector.nature = Some(nature); calculator.nature = Some(nature);
} }
} }
if let Some(characteristic) = data["characteristic"].as_str() { if let Some(characteristic) = data["characteristic"].as_str() {
@ -90,13 +96,13 @@ fn request_handler(
.get(&characteristic.to_lowercase()) .get(&characteristic.to_lowercase())
.cloned() .cloned()
{ {
inspector.characteristic = Some(characteristic); calculator.characteristic = Some(characteristic);
} }
} }
for stat in BaseStat::all() { for stat in BaseStat::all() {
if let Some(value) = data[stat.key()].as_i32() { if let Some(value) = data[stat.key()].as_i32() {
ui.derived_stats.insert(stat, value.to_string()); ui.derived_stats.insert(stat, value.to_string());
inspector.derived_stats.set_stat_value(stat, Some(value)); calculator.derived_stats.set_stat_value(stat, Some(value));
} }
} }
} }
@ -106,3 +112,17 @@ fn request_handler(
} }
} }
} }
pub fn load_url_asset(
url: String,
path: PathBuf,
assets: &Res<AssetServer>,
) -> Result<Handle<Image>, Box<dyn std::error::Error>> {
let system_path = Path::new("assets").join(&path);
if Path::exists(&path) {
return Ok(assets.load(path));
}
let data = reqwest::blocking::get(&url)?.bytes()?;
fs::write(&system_path, data).unwrap();
Ok(assets.load(path))
}

View File

@ -3,7 +3,10 @@ use bevy_egui::{egui, EguiContexts, EguiPlugin};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::collections::HashMap; use std::collections::HashMap;
use crate::{database::Database, inspector::Inspector, pokemon::*}; use crate::database::Database;
use crate::pokemon::*;
use super::calculator::Calculator;
const INPUT_LABEL_WIDTH: f32 = 100.0; const INPUT_LABEL_WIDTH: f32 = 100.0;
const INPUT_TEXT_WIDTH: f32 = 150.0; const INPUT_TEXT_WIDTH: f32 = 150.0;
@ -69,18 +72,18 @@ fn load_assets(mut ui_assets: ResMut<UiAssets>, assets: Res<AssetServer>) {
fn handle_state( fn handle_state(
ui_state: Res<UiState>, ui_state: Res<UiState>,
pokemon: Res<Database<Pokemon>>, pokemon: Res<Database<Pokemon>>,
mut inspector: ResMut<Inspector>, mut calculator: ResMut<Calculator>,
) { ) {
if ui_state.is_changed() { if ui_state.is_changed() {
if let Some(pokemon) = pokemon.map.get(ui_state.name.to_lowercase().as_str()) { if let Some(pokemon) = pokemon.map.get(ui_state.name.to_lowercase().as_str()) {
inspector.pokemon = Some(pokemon.clone()); calculator.pokemon = Some(pokemon.clone());
} }
} }
} }
fn ui_system( fn ui_system(
mut contexts: EguiContexts, mut contexts: EguiContexts,
mut inspector: ResMut<Inspector>, mut calculator: ResMut<Calculator>,
ui_assets: Res<UiAssets>, ui_assets: Res<UiAssets>,
mut ui_state: ResMut<UiState>, mut ui_state: ResMut<UiState>,
mut rendered_texture_id: Local<egui::TextureId>, mut rendered_texture_id: Local<egui::TextureId>,
@ -96,11 +99,11 @@ fn ui_system(
egui::CentralPanel::default().show(contexts.ctx_mut(), |ui| { egui::CentralPanel::default().show(contexts.ctx_mut(), |ui| {
let sprite_size = egui::Vec2::new(128., 128.); let sprite_size = egui::Vec2::new(128., 128.);
let pokemon = inspector.pokemon.clone(); let pokemon = calculator.pokemon.clone();
let pokemon = pokemon.as_ref(); let pokemon = pokemon.as_ref();
let nature = inspector.nature.clone(); let nature = calculator.nature.clone();
let nature = nature.as_ref(); let nature = nature.as_ref();
let characteristic = inspector.characteristic.clone(); let characteristic = calculator.characteristic.clone();
let characteristic = characteristic.as_ref(); let characteristic = characteristic.as_ref();
// Pokemon // Pokemon
@ -128,11 +131,11 @@ fn ui_system(
egui::ComboBox::new("nature", "") egui::ComboBox::new("nature", "")
.selected_text(format!("{}", nature.map_or("-", |c| &c.name))) .selected_text(format!("{}", nature.map_or("-", |c| &c.name)))
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
ui.selectable_value(&mut inspector.nature, None, "-"); ui.selectable_value(&mut calculator.nature, None, "-");
let mut chars: Vec<_> = natures.map.values().collect(); let mut chars: Vec<_> = natures.map.values().collect();
chars.sort_by_key(|c| c.name.clone()); chars.sort_by_key(|c| c.name.clone());
for char in chars { for char in chars {
ui.selectable_value(&mut inspector.nature, Some(char.clone()), &char.name); ui.selectable_value(&mut calculator.nature, Some(char.clone()), &char.name);
} }
}); });
}); });
@ -147,12 +150,12 @@ fn ui_system(
egui::ComboBox::new("characteristic", "") egui::ComboBox::new("characteristic", "")
.selected_text(format!("{}", characteristic.map_or("-", |c| &c.name))) .selected_text(format!("{}", characteristic.map_or("-", |c| &c.name)))
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
ui.selectable_value(&mut inspector.characteristic, None, "-"); ui.selectable_value(&mut calculator.characteristic, None, "-");
let mut chars: Vec<_> = characteristics.map.values().collect(); let mut chars: Vec<_> = characteristics.map.values().collect();
chars.sort_by_key(|c| c.name.clone()); chars.sort_by_key(|c| c.name.clone());
for char in chars { for char in chars {
ui.selectable_value( ui.selectable_value(
&mut inspector.characteristic, &mut calculator.characteristic,
Some(char.clone()), Some(char.clone()),
&char.name, &char.name,
); );
@ -212,7 +215,7 @@ fn ui_system(
if input.changed() { if input.changed() {
*value = digit_filter(value); *value = digit_filter(value);
if let Ok(value) = value.parse::<StatValue>() { if let Ok(value) = value.parse::<StatValue>() {
inspector.derived_stats.level = Some(value); calculator.derived_stats.level = Some(value);
} }
} }
if input.gained_focus() { if input.gained_focus() {
@ -318,7 +321,7 @@ fn ui_system(
*value = digit_filter(value); *value = digit_filter(value);
if let (Ok(value), derived) = ( if let (Ok(value), derived) = (
value.parse::<StatValue>(), value.parse::<StatValue>(),
inspector.derived_stats.value_mut(stat), calculator.derived_stats.value_mut(stat),
) { ) {
*derived = Some(value); *derived = Some(value);
} }
@ -330,8 +333,8 @@ fn ui_system(
let possible_ivs = if let (Some(pokemon), Some(level), Some(derived_stat)) = ( let possible_ivs = if let (Some(pokemon), Some(level), Some(derived_stat)) = (
pokemon, pokemon,
inspector.derived_stats.level, calculator.derived_stats.level,
inspector.derived_stats.value(stat), calculator.derived_stats.value(stat),
) { ) {
calculate_possible_ivs( calculate_possible_ivs(
stat, stat,

24
src/ivpeek.rs Normal file
View File

@ -0,0 +1,24 @@
use bevy::{prelude::*, window::WindowResolution};
mod inspector;
mod ui;
pub fn run() {
App::new()
.add_plugins(
DefaultPlugins
.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(720., 540.),
resizable: false,
title: "Smart IV peeker".to_string(),
..default()
}),
..default()
})
.set(ImagePlugin::default_nearest()),
)
.add_plugin(crate::database::DatabasePlugin)
.add_plugin(ui::UiPlugin)
.run()
}

34
src/ivpeek/inspector.rs Normal file
View File

@ -0,0 +1,34 @@
use std::collections::HashMap;
use bevy::prelude::*;
use crate::pokemon::*;
pub struct InspectorPlugin;
impl Plugin for InspectorPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(Inspector::default());
}
}
#[derive(Resource, Default)]
pub struct Inspector {
pub allies: Vec<PokemonInstance>,
pub enemies: Vec<PokemonInstance>,
}
impl Inspector {
pub fn clear(&mut self) {
self.allies.clear();
self.enemies.clear();
}
}
#[derive(Default)]
pub struct PokemonInstance {
pub pokemon: Pokemon,
pub nature: Option<Nature>,
pub ivs: HashMap<BaseStat, u8>,
pub evs: HashMap<BaseStat, u8>,
}

127
src/ivpeek/ui.rs Normal file
View File

@ -0,0 +1,127 @@
use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts, EguiPlugin};
use lazy_static::lazy_static;
use std::collections::HashMap;
use crate::{database::Database, pokemon::*};
use super::inspector::{Inspector, PokemonInstance};
const BAR_HEIGHT: f32 = 12.0;
const BASE_STAT_WIDTH_MULT: f32 = 1.0;
const IV_WIDTH_MULT: f32 = 4.0;
const EV_WIDTH_MULT: f32 = 1.0;
lazy_static! {
static ref BASE_STAT_COLOR_RANGES: Vec<(u8, egui::Color32)> = vec![
(0, egui::Color32::from_rgb(255, 0, 0)),
(30, egui::Color32::from_rgb(255, 128, 0)),
(60, egui::Color32::from_rgb(255, 255, 0)),
(90, egui::Color32::from_rgb(128, 255, 0)),
(120, egui::Color32::from_rgb(0, 255, 0)),
(150, egui::Color32::from_rgb(0, 255, 128)),
(180, egui::Color32::from_rgb(0, 255, 255)),
];
static ref IV_COLOR_RANGES: Vec<(u8, egui::Color32)> = vec![
(0, egui::Color32::from_rgb(255, 0, 0)),
(16, egui::Color32::from_rgb(255, 128, 0)),
(22, egui::Color32::from_rgb(255, 255, 0)),
(27, egui::Color32::from_rgb(128, 255, 0)),
(30, egui::Color32::from_rgb(0, 255, 0)),
];
static ref COLUMNS: Vec<f32> = vec![
25.0, // Base stat
255.0 * BASE_STAT_WIDTH_MULT, // Base stat bar
60.0, // Stat name
40.0, // IV value
32.0 * IV_WIDTH_MULT, // IV bar
40.0, // EV value
255.0 * EV_WIDTH_MULT, // EV bar
];
}
pub struct UiPlugin;
impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.add_plugin(EguiPlugin)
.insert_resource(Inspector::default())
.insert_resource(UiAssets::default())
.add_startup_system(load_assets)
.add_system(ui_system)
.add_startup_system(test_init);
}
}
fn test_init(mut inspector: ResMut<Inspector>, pokemon_database: Res<Database<Pokemon>>) {
inspector.enemies.push(PokemonInstance {
pokemon: pokemon_database.map.get("baltoy").unwrap().clone(),
..default()
});
}
#[derive(Resource, Default)]
pub struct UiAssets {
pub bar_handle: Handle<Image>,
pub sprite_map: HashMap<String, (Handle<Image>, egui::TextureId)>,
}
fn load_assets(mut ui_assets: ResMut<UiAssets>, assets: Res<AssetServer>) {
ui_assets.bar_handle = assets.load("ui/bar.png");
}
fn ui_system(
mut contexts: EguiContexts,
inspector: Res<Inspector>,
ui_assets: Res<UiAssets>,
mut rendered_texture_id: Local<egui::TextureId>,
mut is_initialized: Local<bool>,
) {
if !*is_initialized {
*is_initialized = true;
*rendered_texture_id = contexts.add_image(ui_assets.bar_handle.clone_weak());
}
egui::CentralPanel::default().show(contexts.ctx_mut(), |ui| {
let sprite_size = egui::Vec2::new(128., 128.);
for instance in inspector.enemies.iter().chain(inspector.allies.iter()) {
let pokemon = instance.pokemon.clone();
// Sprite
ui.horizontal(|ui| {
ui.horizontal(|ui| {
ui.set_min_size(sprite_size);
if let Some((_, rendered_texture_id)) = ui_assets.sprite_map.get(&pokemon.key())
{
ui.add(egui::Image::new(*rendered_texture_id, sprite_size));
}
});
});
}
});
}
fn nature_color(stat: BaseStat, nature: Option<&Nature>) -> egui::Color32 {
if let Some(nature) = nature {
if nature.increased == Some(stat) {
egui::Color32::from_rgb(255, 64, 192)
} else if nature.decreased == Some(stat) {
egui::Color32::from_rgb(64, 192, 255)
} else {
egui::Color32::LIGHT_GRAY
}
} else {
egui::Color32::LIGHT_GRAY
}
}
fn iv_color(iv: u8) -> egui::Color32 {
for (treshold, color) in IV_COLOR_RANGES.iter().rev() {
if iv >= *treshold {
return color.clone();
}
}
IV_COLOR_RANGES
.first()
.map_or(egui::Color32::LIGHT_GRAY, |(_, color)| color.clone())
}

43
src/json.rs Normal file
View File

@ -0,0 +1,43 @@
use json::JsonValue;
use lazy_static::lazy_static;
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
};
use crate::pokemon::*;
lazy_static! {
static ref DATA_DIR: PathBuf = "./data".into();
pub static ref TYPE_MAP: HashMap<u8, Type> = {
let types = read_pokedex();
let mut map = HashMap::new();
for type_data in types.members() {
if let Some(type_data) = Type::from_json(type_data) {
map.insert(type_data.id, type_data);
}
}
map
};
}
pub fn parse_json_file<P: AsRef<Path>>(path: P) -> JsonValue {
json::parse(fs::read_to_string(path).unwrap().as_ref()).unwrap()
}
pub fn read_natures() -> JsonValue {
parse_json_file(DATA_DIR.join("natures.json"))
}
pub fn read_pokedex() -> JsonValue {
parse_json_file(DATA_DIR.join("pokedex.json"))
}
pub fn read_types() -> JsonValue {
parse_json_file(DATA_DIR.join("types.json"))
}
pub fn read_characteristics() -> JsonValue {
parse_json_file(DATA_DIR.join("characteristics.json"))
}

5
src/lib.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod database;
pub mod ivcalc;
pub mod ivpeek;
pub mod json;
pub mod pokemon;

View File

@ -2,7 +2,7 @@ use bevy::reflect::{FromReflect, Reflect};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::{collections::HashMap, fmt}; use std::{collections::HashMap, fmt};
use crate::database::TYPE_MAP; use crate::json::TYPE_MAP;
pub type BaseValue = u8; pub type BaseValue = u8;
pub type StatValue = i32; pub type StatValue = i32;
@ -103,7 +103,7 @@ impl Pokemon {
} }
} }
pub fn get_url(&self) -> String { pub fn sprite_url(&self) -> String {
format!( format!(
"https://img.pokemondb.net/sprites/{gen}/normal/{name}.png", "https://img.pokemondb.net/sprites/{gen}/normal/{name}.png",
gen = self.get_sprite_gen(), gen = self.get_sprite_gen(),
@ -183,6 +183,18 @@ impl BaseStat {
BaseStat::Speed, BaseStat::Speed,
] ]
} }
pub fn from_str(str: &str) -> Option<Self> {
match str {
"hp" => Some(BaseStat::Hp),
"attack" => Some(BaseStat::Attack),
"defense" => Some(BaseStat::Defense),
"special_attack" => Some(BaseStat::SpecialAttack),
"special_defense" => Some(BaseStat::SpecialDefense),
"speed" => Some(BaseStat::Speed),
_ => None,
}
}
} }
impl GetKey for BaseStat { impl GetKey for BaseStat {
@ -238,6 +250,7 @@ impl GetKey for Type {
#[derive(Default, Debug, Clone, Reflect, FromReflect, PartialEq)] #[derive(Default, Debug, Clone, Reflect, FromReflect, PartialEq)]
pub struct Nature { pub struct Nature {
pub id: u8, pub id: u8,
pub pid: u8,
pub name: String, pub name: String,
pub alias: String, pub alias: String,
pub increased: Option<BaseStat>, pub increased: Option<BaseStat>,
@ -260,6 +273,7 @@ impl FromJson for Nature {
fn from_json(json: &json::JsonValue) -> Option<Self> { fn from_json(json: &json::JsonValue) -> Option<Self> {
Some(Self { Some(Self {
id: json["id"].as_u8()?, id: json["id"].as_u8()?,
pid: json["pid"].as_u8()?,
name: json["name"].as_str()?.to_string(), name: json["name"].as_str()?.to_string(),
alias: json["alias"].as_str()?.to_string(), alias: json["alias"].as_str()?.to_string(),
increased: json["increased"] increased: json["increased"]