use anyhow::Result; use assetpath::AssetPath; use engine::prelude::*; use serde::{Deserialize, Serialize}; use std::{ fmt, fmt::Debug, hash::{Hash, Hasher}, slice::Iter, str::FromStr, }; use std::sync::Arc; use crate::{components::inventory::Storable, config::abilities::AbilitySettings}; use super::{ability_book::Ability, ItemSystem, Rarities, Tooltip}; const COOL_DOWN_REDUCTION_CAP: f32 = 0.7; #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub enum AbilityAddonTypes { Damage(u32), ProjectileSpeed(f32), Bounce, Explosion(f32), // radius Size(f32), Projectiles(u32), CoolDown(f32), // % Distance(f32), // m // TODO: Pierce, // bool } impl AbilityAddonTypes { pub const COUNT: u32 = 8; pub fn iter() -> Iter<'static, Self> { use AbilityAddonTypes::*; static ADD_ON_TYPES: [AbilityAddonTypes; AbilityAddonTypes::COUNT as usize] = [ Damage(0), ProjectileSpeed(0.0), Bounce, Explosion(0.0), Size(0.0), Projectiles(0), CoolDown(0.0), Distance(0.0), ]; ADD_ON_TYPES.iter() } pub fn random() -> Self { let n = Random::range(0, Self::COUNT); Self::from(n) } pub fn val_as_str(&self) -> String { match self { Self::Damage(v) => format!("{} (*lvl)", v), Self::ProjectileSpeed(v) => format!("+ {:.1}", v), Self::Bounce => "Enabled".to_string(), Self::Explosion(v) => format!("+ {:.1}", v), Self::Size(v) => format!("+ {:.0}%", v * 100.0), Self::Projectiles(v) => format!("+ {}", v), Self::CoolDown(v) => format!("- {:.0}%", v * 100.0), Self::Distance(v) => format!("+ {}", v), } } pub fn apply_rarity(&mut self, rarity: Rarities, ability_settings: &AbilitySettings) { match self { Self::Damage(v) => *v = ability_settings.damage.from_rarity(rarity), Self::ProjectileSpeed(v) => *v = ability_settings.projectile_speed.from_rarity(rarity), Self::Bounce => (), Self::Explosion(v) => *v = ability_settings.explosion.from_rarity(rarity), Self::Size(v) => *v = ability_settings.size.from_rarity(rarity), Self::Projectiles(v) => { *v = ability_settings.additional_projectiles.from_rarity(rarity) } Self::CoolDown(v) => { *v = ability_settings.cool_down.from_rarity(rarity); } Self::Distance(v) => *v = ability_settings.distance.from_rarity(rarity), } } pub fn get_path<'a>(&self, ability_settings: &'a AbilitySettings) -> &'a AssetPath { match self { Self::Damage(_) => &ability_settings.icons.damage, Self::ProjectileSpeed(_) => &ability_settings.icons.projectile_speed, Self::Bounce => &ability_settings.icons.bounce, Self::Explosion(_) => &ability_settings.icons.explosion, Self::Size(_) => &ability_settings.icons.size, Self::Projectiles(_) => &ability_settings.icons.additional_projectiles, Self::CoolDown(_) => &ability_settings.icons.cool_down, Self::Distance(_) => &ability_settings.icons.distance, } } pub fn into_zero(self) -> Self { match self { AbilityAddonTypes::Damage(_) => AbilityAddonTypes::Damage(0), AbilityAddonTypes::ProjectileSpeed(_) => AbilityAddonTypes::ProjectileSpeed(0.0), AbilityAddonTypes::Bounce => AbilityAddonTypes::Bounce, AbilityAddonTypes::Explosion(_) => AbilityAddonTypes::Explosion(0.0), AbilityAddonTypes::Size(_) => AbilityAddonTypes::Size(0.0), AbilityAddonTypes::Projectiles(_) => AbilityAddonTypes::Projectiles(0), AbilityAddonTypes::CoolDown(_) => AbilityAddonTypes::CoolDown(0.0), AbilityAddonTypes::Distance(_) => AbilityAddonTypes::Distance(0.0), } } } impl std::str::FromStr for AbilityAddonTypes { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "Damage" => Ok(Self::Damage(0)), "Projectile Speed" => Ok(Self::ProjectileSpeed(0.0)), "Bounce" => Ok(Self::Bounce), "Explosion" => Ok(Self::Explosion(0.0)), "Size" => Ok(Self::Size(0.0)), "Projectiles" => Ok(Self::Projectiles(0)), "Cool Down" => Ok(Self::CoolDown(0.0)), "Distance" => Ok(Self::Distance(0.0)), _ => { return Err(anyhow::Error::msg(format!( "Failed parsing AbilityAddonTypes from {}", s ))) } } } } impl fmt::Display for AbilityAddonTypes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Damage(_) => write!(f, "Damage"), Self::ProjectileSpeed(_) => write!(f, "Projectile Speed"), Self::Bounce => write!(f, "Bounce"), Self::Explosion(_) => write!(f, "Explosion"), Self::Size(_) => write!(f, "Size"), Self::Projectiles(_) => write!(f, "Projectiles"), Self::CoolDown(_) => write!(f, "Cool Down"), Self::Distance(_) => write!(f, "Distance"), } } } impl From for AbilityAddonTypes { fn from(n: u32) -> Self { match n { 0 => Self::Damage(0), 1 => Self::ProjectileSpeed(0.0), 2 => Self::Bounce, 3 => Self::Explosion(0.0), 4 => Self::Size(0.0), 5 => Self::Projectiles(0), 6 => Self::CoolDown(0.0), 7 => Self::Distance(0.0), _ => panic!( "can't convert AbilityAddonTypes from bigger than {}", Self::COUNT ), } } } #[allow(clippy::derive_hash_xor_eq)] impl Hash for AbilityAddonTypes { fn hash(&self, state: &mut H) { match self { Self::Damage(_) => 0u32.hash(state), Self::ProjectileSpeed(_) => 1u32.hash(state), Self::Bounce => 2u32.hash(state), Self::Explosion(_) => 3u32.hash(state), Self::Size(_) => 4u32.hash(state), Self::Projectiles(_) => 5u32.hash(state), Self::CoolDown(_) => 6u32.hash(state), Self::Distance(_) => 7u32.hash(state), } } } impl Eq for AbilityAddonTypes {} #[derive(Debug, Clone)] pub struct AbilityAddon { icon: Arc, addon_type: AbilityAddonTypes, rarity: Rarities, } impl AbilityAddon { pub fn new(rarity: Rarities, addon_type: AbilityAddonTypes, icon: Arc) -> Self { AbilityAddon { addon_type, rarity, icon, } } pub fn load( mut addon_type: AbilityAddonTypes, rarity: Rarities, icon: Arc, ability_settings: &AbilitySettings, ) -> Self { addon_type.apply_rarity(rarity, ability_settings); AbilityAddon { addon_type, rarity, icon, } } pub fn addon_type(&self) -> &AbilityAddonTypes { &self.addon_type } pub fn into_persistent(&self) -> String { format!("{}|{}", self.addon_type(), self.rarity()) } pub fn from_persistent<'a, A: Ability>( mut split: impl Iterator, item_system: &ItemSystem, ) -> Result { let addon_type = AbilityAddonTypes::from_str(split.next().unwrap())?; let rarity = Rarities::from_str(split.next().unwrap())?; Ok(item_system.addon(rarity, addon_type)) } pub fn create_tooltip( &self, gui_handler: &Arc, position: (i32, i32), ) -> Result { let gui = GuiBuilder::from_str( gui_handler, include_str!("../../resources/addon_snippet.xml"), )?; let icon: Arc = gui.element("addon_icon")?; let rarity_label: Arc