feat: ivpeek ui

master
hheik 2023-03-31 20:18:41 +03:00
parent ffbcea507b
commit baf64181ec
5 changed files with 267 additions and 49 deletions

View File

@ -1,4 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::{
fs,
path::{Path, PathBuf},
};
use bevy::prelude::*; use bevy::prelude::*;
use json::JsonValue; use json::JsonValue;
@ -49,3 +53,17 @@ fn database_setup(
pokemon_database.populate_from_json(&read_pokedex()); pokemon_database.populate_from_json(&read_pokedex());
characteristic_database.populate_from_json(&read_characteristics()); characteristic_database.populate_from_json(&read_characteristics());
} }
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,8 +1,3 @@
use std::{
fs,
path::{Path, PathBuf},
};
use bevy::prelude::*; use bevy::prelude::*;
use bevy_egui::EguiContexts; use bevy_egui::EguiContexts;
@ -112,17 +107,3 @@ 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

@ -19,6 +19,7 @@ pub fn run() {
.set(ImagePlugin::default_nearest()), .set(ImagePlugin::default_nearest()),
) )
.add_plugin(crate::database::DatabasePlugin) .add_plugin(crate::database::DatabasePlugin)
.add_plugin(inspector::InspectorPlugin)
.add_plugin(ui::UiPlugin) .add_plugin(ui::UiPlugin)
.run() .run()
} }

View File

@ -1,14 +1,22 @@
use std::collections::HashMap; use std::collections::HashMap;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_egui::EguiContexts;
use crate::pokemon::*; use crate::{
database::{load_url_asset, Database},
pokemon::*,
};
use super::ui::UiAssets;
pub struct InspectorPlugin; pub struct InspectorPlugin;
impl Plugin for InspectorPlugin { impl Plugin for InspectorPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(Inspector::default()); app.insert_resource(Inspector::default())
.add_system(state_changed)
.add_startup_system(test_init);
} }
} }
@ -32,3 +40,68 @@ pub struct PokemonInstance {
pub ivs: HashMap<BaseStat, u8>, pub ivs: HashMap<BaseStat, u8>,
pub evs: HashMap<BaseStat, u8>, pub evs: HashMap<BaseStat, u8>,
} }
fn test_init(mut inspector: ResMut<Inspector>, pokemon_database: Res<Database<Pokemon>>) {
for name in vec![
"baltoy",
"claydol",
"regirock",
"flygon",
"shuckle",
"swampert",
"forretress",
"shedinja",
"blissey",
]
.iter()
{
inspector.enemies.push(PokemonInstance {
pokemon: pokemon_database.map.get(*name).unwrap().clone(),
ivs: {
let mut result = HashMap::new();
for stat in BaseStat::all().iter() {
result.insert(
*stat,
match stat {
BaseStat::Hp => 31,
BaseStat::Attack => 27,
BaseStat::Defense => 26,
BaseStat::SpecialAttack => 0,
BaseStat::SpecialDefense => 21,
BaseStat::Speed => 15,
},
);
}
result
},
..default()
});
}
}
fn state_changed(
inspector: Res<Inspector>,
assets: Res<AssetServer>,
mut ui_assets: ResMut<UiAssets>,
mut contexts: EguiContexts,
) {
if inspector.is_changed() {
for instance in inspector.enemies.iter().chain(inspector.allies.iter()) {
let pokemon = instance.pokemon.clone();
if !ui_assets.sprite_map.contains_key(&pokemon.key()) {
let handle = load_url_asset(
pokemon.sprite_url(),
format!("cache/{name}.png", name = pokemon.key()).into(),
&assets,
);
if let Ok(handle) = handle {
let weak = handle.clone_weak();
ui_assets
.sprite_map
.insert(pokemon.key(), (handle, contexts.add_image(weak)));
}
}
}
}
}

View File

@ -3,14 +3,14 @@ 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, pokemon::*}; use crate::pokemon::*;
use super::inspector::{Inspector, PokemonInstance}; use super::inspector::Inspector;
const BAR_HEIGHT: f32 = 12.0; const BAR_HEIGHT: f32 = 12.0;
const BASE_STAT_WIDTH_MULT: f32 = 1.0; const BASE_STAT_WIDTH_MULT: f32 = 0.8;
const IV_WIDTH_MULT: f32 = 4.0; const IV_WIDTH_MULT: f32 = 4.0;
const EV_WIDTH_MULT: f32 = 1.0; // const EV_WIDTH_MULT: f32 = 1.0;
lazy_static! { lazy_static! {
static ref BASE_STAT_COLOR_RANGES: Vec<(u8, egui::Color32)> = vec![ static ref BASE_STAT_COLOR_RANGES: Vec<(u8, egui::Color32)> = vec![
@ -33,10 +33,10 @@ lazy_static! {
25.0, // Base stat 25.0, // Base stat
255.0 * BASE_STAT_WIDTH_MULT, // Base stat bar 255.0 * BASE_STAT_WIDTH_MULT, // Base stat bar
60.0, // Stat name 60.0, // Stat name
40.0, // IV value 25.0, // IV value
32.0 * IV_WIDTH_MULT, // IV bar 32.0 * IV_WIDTH_MULT, // IV bar
40.0, // EV value // 40.0, // EV value
255.0 * EV_WIDTH_MULT, // EV bar // 255.0 * EV_WIDTH_MULT, // EV bar
]; ];
} }
@ -45,21 +45,12 @@ pub struct UiPlugin;
impl Plugin for UiPlugin { impl Plugin for UiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugin(EguiPlugin) app.add_plugin(EguiPlugin)
.insert_resource(Inspector::default())
.insert_resource(UiAssets::default()) .insert_resource(UiAssets::default())
.add_startup_system(load_assets) .add_startup_system(load_assets)
.add_system(ui_system) .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)] #[derive(Resource, Default)]
pub struct UiAssets { pub struct UiAssets {
pub bar_handle: Handle<Image>, pub bar_handle: Handle<Image>,
@ -84,20 +75,163 @@ 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.);
for instance in inspector.enemies.iter().chain(inspector.allies.iter()) {
let pokemon = instance.pokemon.clone();
// Sprite egui::ScrollArea::vertical().show(ui, |ui| {
ui.horizontal(|ui| { for instance in inspector.enemies.iter().chain(inspector.allies.iter()) {
let pokemon = instance.pokemon.clone();
let nature = instance.nature.clone();
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.set_min_size(sprite_size); ui.horizontal(|ui| {
if let Some((_, rendered_texture_id)) = ui_assets.sprite_map.get(&pokemon.key()) ui.set_min_width(sprite_size.x + 32.);
{ ui.set_max_width(sprite_size.x + 32.);
ui.add(egui::Image::new(*rendered_texture_id, sprite_size)); // 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())
));
});
// 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"));
}
3 => {
ui.label(egui::RichText::new("IV"));
}
4 => {
ui.label(egui::RichText::new("IV range"));
}
_ => (),
}
});
}
});
}); });
});
} ui.horizontal(|ui| {
// Sprite
ui.horizontal(|ui| {
ui.set_min_size(sprite_size);
ui.set_max_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));
}
});
ui.add_space(32.);
ui.vertical(|ui| {
for stat in BaseStat::all() {
ui.horizontal(|ui| {
let mut column_size = COLUMNS.iter();
// Base stat number
ui.horizontal(|ui| {
if let Some(width) = column_size.next() {
ui.set_width(*width);
}
let base_stat = &pokemon.base_value(stat).to_string();
ui.label(egui::RichText::new(base_stat));
});
// Base stat bar
ui.horizontal(|ui| {
if let Some(width) = column_size.next() {
ui.set_width(*width);
}
let base_value = pokemon.base_value(stat);
let bar_length = base_value as f32 * BASE_STAT_WIDTH_MULT;
let color = base_stat_color(base_value);
let image = egui::Image::new(
*rendered_texture_id,
[bar_length, BAR_HEIGHT],
)
.tint(color);
ui.add(image);
});
// Stat name
ui.horizontal(|ui| {
if let Some(width) = column_size.next() {
ui.set_width(*width);
}
ui.label(
egui::RichText::new(format!("{stat}"))
.color(nature_color(stat, nature.as_ref())),
);
});
// IV stat
ui.horizontal(|ui| {
if let Some(width) = column_size.next() {
ui.set_width(*width);
}
if let Some(iv) = instance.ivs.get(&stat) {
ui.label(
egui::RichText::new(format!("{iv}"))
.color(iv_color(*iv)),
);
} else {
ui.label("-");
}
});
// IV stat bar
ui.horizontal(|ui| {
if let Some(width) = column_size.next() {
ui.set_width(*width);
}
if let Some(iv) = instance.ivs.get(&stat) {
let bar_length = *iv as f32 * IV_WIDTH_MULT;
let track_length = 31. * IV_WIDTH_MULT - bar_length;
let color = iv_color(*iv);
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::ZERO;
// Bar
ui.add(
egui::Image::new(
*rendered_texture_id,
[bar_length, BAR_HEIGHT],
)
.tint(color),
);
// Track
ui.add(
egui::Image::new(
*rendered_texture_id,
[track_length, BAR_HEIGHT],
)
.tint(egui::Color32::from_rgb(64, 64, 64)),
);
});
}
});
});
}
});
});
ui.separator();
}
});
}); });
} }
@ -115,6 +249,17 @@ fn nature_color(stat: BaseStat, nature: Option<&Nature>) -> egui::Color32 {
} }
} }
fn base_stat_color(base_stat: u8) -> egui::Color32 {
for (treshold, color) in BASE_STAT_COLOR_RANGES.iter().rev() {
if base_stat >= *treshold {
return color.clone();
}
}
BASE_STAT_COLOR_RANGES
.first()
.map_or(egui::Color32::LIGHT_GRAY, |(_, color)| color.clone())
}
fn iv_color(iv: u8) -> egui::Color32 { fn iv_color(iv: u8) -> egui::Color32 {
for (treshold, color) in IV_COLOR_RANGES.iter().rev() { for (treshold, color) in IV_COLOR_RANGES.iter().rev() {
if iv >= *treshold { if iv >= *treshold {