use std::{collections::HashMap, fmt::Display, fs, path}; use serde::Deserialize; use crate::ASSETS_FOLDER_PATH; use super::{ Game, enemy::{Enemy, EnemyDrop}, }; pub const GENERIC_MATERIAL_ICON: &str = "generic"; #[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct MaterialDetails { pub category: MaterialCategory, pub kind: MaterialKind, } #[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "lowercase")] pub enum MaterialCategory { // Common #[serde(alias = "blaze")] Blazing, #[serde(alias = "soothing")] Bright, #[serde(alias = "betwixt")] Dense, #[serde(alias = "writhing")] Dark, #[serde(alias = "wellspring")] Energy, Frost, #[serde(alias = "thunder")] Lightning, Lucid, Mythril, Orichalcum, Power, #[serde(alias = "shiny")] Shimmering, #[serde(alias = "mystery", alias = "hungry")] Serenity, Twilight, // KH1 Only Spirit, Stormy, // KH2 Only Remembrance, } impl MaterialCategory { pub fn get_texture_group(&self) -> String { match self { MaterialCategory::Blazing => "blazing".to_string(), MaterialCategory::Bright => "bright".to_string(), MaterialCategory::Dense => "dense".to_string(), MaterialCategory::Dark => "dark".to_string(), MaterialCategory::Energy => "energy".to_string(), MaterialCategory::Frost => "frost".to_string(), MaterialCategory::Lightning => "lightning".to_string(), MaterialCategory::Lucid => "lucid".to_string(), MaterialCategory::Mythril => "mythril".to_string(), MaterialCategory::Orichalcum => "orichalcum".to_string(), MaterialCategory::Power => "power".to_string(), MaterialCategory::Shimmering => "shimmering".to_string(), MaterialCategory::Serenity => "serenity".to_string(), MaterialCategory::Twilight => "twilight".to_string(), MaterialCategory::Spirit => "spirit".to_string(), MaterialCategory::Stormy => "stormy".to_string(), MaterialCategory::Remembrance => "remembrance".to_string(), } } pub fn get_category(&self, game: &Game) -> String { match self { MaterialCategory::Blazing => match game { Game::Kh1 => "blaze".to_string(), _ => "blazing".to_string(), }, MaterialCategory::Bright => match game { Game::Kh1 | Game::Kh2 => "bright".to_string(), _ => "soothing".to_string(), }, MaterialCategory::Dense => match game { Game::Kh2 => "dense".to_string(), _ => "betwixt".to_string(), }, MaterialCategory::Dark => match game { Game::Kh2 => "dark".to_string(), _ => "writhing".to_string(), }, MaterialCategory::Energy => match game { Game::Kh1 | Game::Kh2 => "energy".to_string(), _ => "wellspring".to_string(), }, MaterialCategory::Frost => "frost".to_string(), MaterialCategory::Lightning => match game { Game::Kh1 => "thunder".to_string(), _ => "lightning".to_string(), }, MaterialCategory::Lucid => "lucid".to_string(), MaterialCategory::Mythril => "mythril".to_string(), MaterialCategory::Orichalcum => "orichalcum".to_string(), MaterialCategory::Power => "power".to_string(), MaterialCategory::Shimmering => match game { Game::Kh1 => "shiny".to_string(), _ => "shimmering".to_string(), }, MaterialCategory::Serenity => match game { Game::Kh1 => "mystery".to_string(), Game::Bbs => "hungry".to_string(), _ => "serenity".to_string(), }, MaterialCategory::Twilight => "twilight".to_string(), MaterialCategory::Spirit => "spirit".to_string(), MaterialCategory::Stormy => "stormy".to_string(), MaterialCategory::Remembrance => "remembrance".to_string(), } } } #[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "lowercase")] pub enum MaterialKind { Shard, Stone, Gem, Crystal, } impl Display for MaterialKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { MaterialKind::Shard => f.write_str("shard"), MaterialKind::Stone => f.write_str("stone"), MaterialKind::Gem => f.write_str("gem"), MaterialKind::Crystal => f.write_str("crystal"), } } } #[derive(Debug, PartialEq, Eq)] pub struct MaterialDrops { pub name: String, pub icon: String, pub category: MaterialCategory, pub kind: MaterialKind, pub drops: Vec, } impl PartialOrd for MaterialDrops { fn partial_cmp(&self, other: &Self) -> Option { if self.category.cmp(&other.category) == std::cmp::Ordering::Equal { return Some(self.kind.cmp(&other.kind)); } Some(self.category.cmp(&other.category)) } } impl MaterialDrops { pub fn new(enemies: Vec) -> Vec { let mut mat_map = HashMap::<(MaterialCategory, MaterialKind), MaterialDrops>::new(); for enemy in enemies { for drop in &enemy.drops { let Some(material) = &drop.material else { continue; }; let key = (material.category.clone(), material.kind.clone()); mat_map .entry(key) .and_modify(|d| d.drops.push(drop.clone())) .or_insert(MaterialDrops { name: drop.name.to_string(), icon: "".to_string(), category: material.category.clone(), kind: material.kind.clone(), drops: vec![drop.clone()], }); } } let mut values: Vec = mat_map.into_values().collect(); values.sort_by(|a, b| a.partial_cmp(b).unwrap()); values } pub fn drops(&self, kind: &str) -> Vec<&EnemyDrop> { match kind { "shard" => self.get_drop_kind(MaterialKind::Shard), "stone" => self.get_drop_kind(MaterialKind::Stone), "gem" => self.get_drop_kind(MaterialKind::Gem), "crystal" => self.get_drop_kind(MaterialKind::Crystal), _ => vec![], } } fn get_drop_kind(&self, kind: MaterialKind) -> Vec<&EnemyDrop> { self.drops .iter() .filter(|d| d.material.as_ref().map(|m| m.kind == kind).unwrap_or(false)) .collect::>() } pub fn texture(&self, game: &Game) -> String { if *game == Game::Kh1 { return GENERIC_MATERIAL_ICON.to_string(); } let category = self.category.get_category(game); let kind = self.kind.to_string(); let path = format!( "{}/materials/{}/{}.webp", ASSETS_FOLDER_PATH, category, kind ); let path = path::Path::new(&path); if path.exists() { format!("{}/{}", self.category.get_category(game), self.kind) } else { GENERIC_MATERIAL_ICON.to_string() } } }