feat: ivpeek ui
parent
ffbcea507b
commit
baf64181ec
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
201
src/ivpeek/ui.rs
201
src/ivpeek/ui.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue