use anyhow::Result; use engine::prelude::*; use paste; use std::collections::HashMap; use crate::{ config::{items::ItemSettings, save_game::SaveGame}, items::{Item, ItemAffix, ItemSlots, ItemSystem, Rarities, ability_book::Ability}, }; use super::{ attributes::{Agility, Attribute, Attributes, Intelligence, Strength}, statistic_types::StatisticType, }; macro_rules! load_item { ($me:ident, $var_name:ident, $slot:ident, $save_game:ident, $item_system:ident) => { if $save_game.$var_name.used { let attributes = Attributes::load( $save_game.$var_name.strength, $save_game.$var_name.agility, $save_game.$var_name.intelligence, ); let mut $var_name = $item_system.item( $save_game.$var_name.rarity, ItemSlots::$slot, $save_game.$var_name.level, attributes, $save_game.$var_name.affixes.clone(), ); $var_name.affixes.iter_mut().for_each(|affix| { if let ItemAffix::Socket(Some(jewel)) = affix { jewel.icon = Some($item_system.jewel_icon(jewel.rarity, jewel.level, jewel.attribute)); jewel.update_stat(&$item_system.item_settings); } }); $me.$var_name = Some($var_name); } }; ($me:ident, $var_name:ident, $index:expr, $slot:ident, $save_game:ident, $item_system:ident) => { paste::expr! { if $save_game.[<$var_name _ $index>].used { let attributes = Attributes::load( $save_game.[<$var_name _ $index>].strength, $save_game.[<$var_name _ $index>].agility, $save_game.[<$var_name _ $index>].intelligence, ); let mut $var_name = $item_system.item( $save_game.[<$var_name _ $index>].rarity, ItemSlots::$slot, $save_game.[<$var_name _ $index>].level, attributes, $save_game.[<$var_name _ $index>].affixes.clone(), ); $var_name.affixes.iter_mut().for_each(|affix| { if let ItemAffix::Socket(Some(jewel)) = affix { jewel.icon = Some($item_system.jewel_icon(jewel.rarity, jewel.level, jewel.attribute)); jewel.update_stat(&$item_system.item_settings); } }); $me.[<$var_name s>][$index] = Some($var_name); } } }; } macro_rules! store_item { ($me:ident, $save_game:ident, $var_name:ident) => { match &$me.$var_name { Some($var_name) => { $save_game.$var_name.used = true; $save_game.$var_name.level = $var_name.level; $save_game.$var_name.strength = $var_name.attributes.strength().raw(); $save_game.$var_name.agility = $var_name.attributes.agility().raw(); $save_game.$var_name.intelligence = $var_name.attributes.intelligence().raw(); $save_game.$var_name.rarity = $var_name.rarity; $save_game.$var_name.affixes = $var_name.affixes.clone(); } None => { $save_game.$var_name.used = false; $save_game.$var_name.level = 0; $save_game.$var_name.strength = 0; $save_game.$var_name.agility = 0; $save_game.$var_name.intelligence = 0; $save_game.$var_name.rarity = Rarities::default(); $save_game.$var_name.affixes = Vec::new(); } } }; ($me:ident, $save_game:ident, $var_name:ident, $index:expr) => { paste::expr! { match &$me.[<$var_name s>][$index] { Some($var_name) => { $save_game.[<$var_name _ $index>].used = true; $save_game.[<$var_name _ $index>].level = $var_name.level; $save_game.[<$var_name _ $index>].strength = $var_name.attributes.strength().raw(); $save_game.[<$var_name _ $index>].agility = $var_name.attributes.agility().raw(); $save_game.[<$var_name _ $index>].intelligence = $var_name.attributes.intelligence().raw(); $save_game.[<$var_name _ $index>].rarity = $var_name.rarity; $save_game.[<$var_name _ $index>].affixes = $var_name.affixes.clone(); } None => { $save_game.[<$var_name _ $index>].used = false; $save_game.[<$var_name _ $index>].level = 0; $save_game.[<$var_name _ $index>].strength = 0; $save_game.[<$var_name _ $index>].agility = 0; $save_game.[<$var_name _ $index>].intelligence = 0; $save_game.[<$var_name _ $index>].rarity = Rarities::default(); $save_game.[<$var_name _ $index>].affixes = Vec::new(); } } } }; } macro_rules! setter { ($item:ident) => { paste::paste! { fn [](&mut self, $item: Option) { self.$item = $item; } } }; } pub const RING_COUNT: usize = 4; pub const AMULET_COUNT: usize = 2; pub struct ItemSlotContainer { // main slots helmet: Option, chest_plate: Option, belt: Option, gloves: Option, boots: Option, // hand slots main_hand: Option, off_hand: Option, // jewelry slots rings: [Option; RING_COUNT], amulets: [Option; AMULET_COUNT], } impl ItemSlotContainer { pub fn new() -> Self { Self { helmet: None, chest_plate: None, belt: None, gloves: None, boots: None, main_hand: None, off_hand: None, rings: [None, None, None, None], amulets: [None, None], } } pub fn load(save_game: &SaveGame, item_system: &ItemSystem) -> Result { let mut me = Self::new(); load_item!(me, chest_plate, ChestPlate, save_game, item_system); load_item!(me, helmet, Helmet, save_game, item_system); load_item!(me, belt, Belt, save_game, item_system); load_item!(me, gloves, Gloves, save_game, item_system); load_item!(me, boots, Boots, save_game, item_system); load_item!(me, main_hand, MainHand, save_game, item_system); load_item!(me, off_hand, OffHand, save_game, item_system); load_item!(me, ring, 0, Ring, save_game, item_system); load_item!(me, ring, 1, Ring, save_game, item_system); load_item!(me, ring, 2, Ring, save_game, item_system); load_item!(me, ring, 3, Ring, save_game, item_system); load_item!(me, amulet, 0, Amulet, save_game, item_system); load_item!(me, amulet, 1, Amulet, save_game, item_system); Ok(me) } pub fn store(&self, save_game: &mut SaveGame) { store_item!(self, save_game, helmet); store_item!(self, save_game, chest_plate); store_item!(self, save_game, belt); store_item!(self, save_game, gloves); store_item!(self, save_game, boots); store_item!(self, save_game, main_hand); store_item!(self, save_game, off_hand); store_item!(self, save_game, ring, 0); store_item!(self, save_game, ring, 1); store_item!(self, save_game, ring, 2); store_item!(self, save_game, ring, 3); store_item!(self, save_game, amulet, 0); store_item!(self, save_game, amulet, 1); } pub fn as_vec(&self) -> Vec<&Option> { vec![ &self.helmet, &self.chest_plate, &self.belt, &self.gloves, &self.boots, &self.main_hand, &self.off_hand, &self.rings[0], &self.rings[1], &self.rings[2], &self.rings[3], &self.amulets[0], &self.amulets[1], ] } fn _create_rarity_color_items( item_meshes: HashMap, item_settings: &ItemSettings, ) -> Result>> { let mut slot_meshes = HashMap::new(); for (slot, mesh) in item_meshes.into_iter() { let mut rarity_meshes = HashMap::new(); for rarity in Rarities::iter() { let mut copied_mesh = mesh.separated_copy()?; let rarity_color = item_settings.rarity_color_settings.from_rarity(*rarity); let buffers = copied_mesh .primitive_types .iter() .map(|primitive| match primitive { PrimitiveDataTypes::PositionNormalColorUVBone(data) => { data.remove_uv_from_buffer() } PrimitiveDataTypes::PositionNormalBone(d) => Ok(d.vertex_buffer.clone()), PrimitiveDataTypes::PositionOnly(_) => todo!(), PrimitiveDataTypes::PositionNormal(_) => todo!(), PrimitiveDataTypes::PositionNormalColor(_) => todo!(), PrimitiveDataTypes::PositionColorUV(_) => todo!(), PrimitiveDataTypes::PositionNormalColorUV(_) => todo!(), PrimitiveDataTypes::PositionNormalColorUVNormalUV(_) => todo!(), PrimitiveDataTypes::PositionNormalColorUVNormalUVBone(_) => todo!(), }) .collect::>>()?; copied_mesh.primitive_types.clear(); buffers.into_iter().try_for_each(|buffer| { copied_mesh.add_primitive( buffer, None, None, PrimitiveMaterial { color: { let c: [f32; 3] = rarity_color.into(); [c[0], c[1], c[2], 1.0] }, ..Default::default() }, true, ) })?; rarity_meshes.insert(*rarity, copied_mesh); } slot_meshes.insert(slot, rarity_meshes); } Ok(slot_meshes) } pub fn set_item_change_callback( &mut self, _draw: &mut Draw, _location: &Location, ) -> Result<()> { // TODO // item_meshes: if query_meshes { // Arc::new( // Self::find_items(draw) // .map({ // let item_settings = item_settings.clone(); // move |item_meshes| { // Self::create_rarity_color_items(item_meshes, &item_settings) // } // }) // .transpose()? // .unwrap_or_default(), // ) // } else { // Default::default() // }, // let item_rarity_meshes = self.item_meshes.clone(); // // only enable items that are equipped // self.slot_changed_callback = Some(Box::new(move |slot, rarity, is_some, multi_mut| { // Self::equipment_changed(multi_mut, &item_rarity_meshes, slot, rarity, is_some) // })); // Self::check_items(draw, location, self, &self.item_meshes)?; Ok(()) } setter!(helmet); setter!(belt); setter!(chest_plate); setter!(gloves); setter!(boots); setter!(main_hand); setter!(off_hand); fn set_rings(&mut self, ring: Option, index: usize) { self.rings[index] = ring; } fn set_amulets(&mut self, amulet: Option, index: usize) { self.amulets[index] = amulet; } } // getter impl ItemSlotContainer { pub fn helmet(&self) -> &Option { &self.helmet } pub fn chest(&self) -> &Option { &self.chest_plate } pub fn belt(&self) -> &Option { &self.belt } pub fn gloves(&self) -> &Option { &self.gloves } pub fn boots(&self) -> &Option { &self.boots } pub fn primary_hand(&self) -> &Option { &self.main_hand } pub fn secondary_hand(&self) -> &Option { &self.off_hand } pub fn ring(&self, index: usize) -> &Option { &self.rings[index] } pub fn amulet(&self, index: usize) -> &Option { &self.amulets[index] } pub fn item_mut(&mut self, slot: ItemSlots, index: Option) -> &mut Option { match slot { ItemSlots::Helmet => &mut self.helmet, ItemSlots::ChestPlate => &mut self.chest_plate, ItemSlots::Belt => &mut self.belt, ItemSlots::Gloves => &mut self.gloves, ItemSlots::Boots => &mut self.boots, ItemSlots::Ring => &mut self.rings[index.unwrap()], ItemSlots::Amulet => &mut self.amulets[index.unwrap()], ItemSlots::MainHand => &mut self.main_hand, ItemSlots::OffHand => &mut self.off_hand, } } } // setter impl ItemSlotContainer { fn _update_helmet(&mut self, helmet: Item) { debug_assert!(helmet.slot == ItemSlots::Helmet); self.set_helmet(Some(helmet)); } pub fn unset_helmet(&mut self) -> Result<()> { if self.helmet.is_some() { self.set_helmet(None); } Ok(()) } fn update_chest(&mut self, chest: Item) { debug_assert!(chest.slot == ItemSlots::ChestPlate); self.set_chest_plate(Some(chest)); } pub fn unset_chest(&mut self) -> Result<()> { if self.chest_plate.is_some() { self.set_chest_plate(None); } Ok(()) } fn _update_belt(&mut self, belt: Item) { debug_assert!(belt.slot == ItemSlots::Belt); self.set_belt(Some(belt)); } pub fn unset_belt(&mut self) -> Result<()> { if self.belt.is_some() { self.set_belt(None); } Ok(()) } fn _update_gloves(&mut self, gloves: Item) { debug_assert!(gloves.slot == ItemSlots::Gloves); self.set_gloves(Some(gloves)); } pub fn unset_gloves(&mut self) -> Result<()> { if self.gloves.is_some() { self.set_gloves(None); } Ok(()) } fn _update_boots(&mut self, boots: Item) { debug_assert!(boots.slot == ItemSlots::Boots); self.set_boots(Some(boots)); } pub fn unset_boots(&mut self) -> Result<()> { if self.boots.is_some() { self.set_boots(None); } Ok(()) } fn update_primary_hand(&mut self, primary_hand: Item) { debug_assert!(primary_hand.slot == ItemSlots::MainHand); self.set_main_hand(Some(primary_hand)); } pub fn unset_primary_hand(&mut self) -> Result<()> { if self.main_hand.is_some() { self.set_main_hand(None); } Ok(()) } fn update_secondary_hand(&mut self, secondary_hand: Item) { debug_assert!(secondary_hand.slot == ItemSlots::OffHand); self.set_off_hand(Some(secondary_hand)); } pub fn unset_secondary_hand(&mut self) -> Result<()> { if self.off_hand.is_some() { self.set_off_hand(None); } Ok(()) } pub fn unset_ring(&mut self, index: usize) -> Result<()> { if self.rings[index].is_some() { self.set_rings(None, index); } Ok(()) } pub fn unset_amulet(&mut self, index: usize) -> Result<()> { if self.amulets[index].is_some() { self.set_amulets(None, index); } Ok(()) } } impl ItemSlotContainer { #[inline] fn _item( item_meshes: &mut HashMap, mesh: &AssetMesh, name: &str, slot: ItemSlots, ) { if let Some(mesh_name) = &mesh.name { if name == mesh_name { assert!(item_meshes.insert(slot, mesh.clone()).is_none()); } } } #[inline] fn _find_items(draw: &Draw) -> Option> { let mut item_meshes = HashMap::new(); for mesh in draw.iter() { Self::_item(&mut item_meshes, mesh, "Helmet", ItemSlots::Helmet); Self::_item(&mut item_meshes, mesh, "Shield", ItemSlots::OffHand); Self::_item(&mut item_meshes, mesh, "Sword", ItemSlots::MainHand); Self::_item(&mut item_meshes, mesh, "ChestPlate", ItemSlots::ChestPlate); Self::_item(&mut item_meshes, mesh, "Boots", ItemSlots::Boots); Self::_item(&mut item_meshes, mesh, "Gloves", ItemSlots::Gloves); } if !item_meshes.is_empty() { Some(item_meshes) } else { None } } #[inline] fn _undress(draw: &mut Draw, name: &str) { draw.remove_by_name(name); } #[inline] fn _dress( draw: &mut Draw, location: &Location, item_meshes: &HashMap>, name: &str, slot: ItemSlots, rarity: Rarities, ) -> Result<()> { // check that the item is not already equipped if !draw.contains(name) { match item_meshes .get(&slot) .map(|rarity_items| rarity_items.get(&rarity)) .flatten() { Some(mesh) => { mesh.model_buffer() .fill(&[(location.transformation_matrix() * mesh.model_matrix()).into()])?; draw.push(mesh.clone()); } None => println!("couldnt find {:?} in item_meshes", slot), } } Ok(()) } #[inline] fn _check_items( &self, draw: &mut Draw, location: &Location, item_meshes: &HashMap>, ) -> Result<()> { macro_rules! check { ($func:ident, $name:expr) => { match self.$func() { Some(item) => { Self::_dress(draw, location, item_meshes, $name, item.slot, item.rarity)?; } None => Self::_undress(draw, $name), } }; } check!(primary_hand, "Sword"); check!(secondary_hand, "Shield"); check!(helmet, "Helmet"); check!(chest, "ChestPlate"); check!(boots, "Boots"); check!(gloves, "Gloves"); Ok(()) } fn _equipment_changed( entity: &mut EntityObject, item_meshes: &HashMap>, slot: ItemSlots, rarity: Rarities, is_some: bool, ) -> Result<()> { let (draw, location): (&mut Draw, &mut Location) = entity.get_components_mut()?; let item_name = match slot { ItemSlots::Helmet => "Helmet", ItemSlots::ChestPlate => "ChestPlate", ItemSlots::Gloves => "Gloves", ItemSlots::MainHand => "Sword", ItemSlots::OffHand => "Shield", ItemSlots::Boots => "Boots", ItemSlots::Belt => return Ok(()), ItemSlots::Ring => return Ok(()), ItemSlots::Amulet => return Ok(()), }; if is_some { Self::_dress(draw, location, item_meshes, item_name, slot, rarity)?; } else { Self::_undress(draw, item_name); } Ok(()) } } impl ItemSlotContainer { pub fn has_space_for(&self, item: &Item) -> bool { match item.slot { ItemSlots::Helmet => self.helmet.is_none(), ItemSlots::ChestPlate => self.chest_plate.is_none(), ItemSlots::Belt => self.belt.is_none(), ItemSlots::Gloves => self.gloves.is_none(), ItemSlots::Boots => self.boots.is_none(), ItemSlots::Ring => { for i in 0..RING_COUNT { if self.rings[i].is_none() { return true; } } false } ItemSlots::Amulet => { for i in 0..AMULET_COUNT { if self.amulets[i].is_none() { return true; } } false } ItemSlots::MainHand => self.main_hand.is_none(), ItemSlots::OffHand => self.off_hand.is_none(), } } /// inserts the item, returns the current item at this slot pub fn insert(&mut self, item: Item, attributes: &Attributes) -> Result> { // check attribute requirements if attributes < &item.attributes { return Ok(Some(item)); } self.insert_unchecked(item) } pub fn insert_unchecked(&mut self, item: Item) -> Result> { Ok(match item.slot { ItemSlots::Helmet => { let current_helmet = self.helmet.clone(); self._update_helmet(item); current_helmet } ItemSlots::ChestPlate => { let current_chest_plate = self.chest_plate.clone(); self.update_chest(item); current_chest_plate } ItemSlots::Belt => { let current_belt = self.belt.clone(); self._update_belt(item); current_belt } ItemSlots::Gloves => { let current_gloves = self.gloves.clone(); self._update_gloves(item); current_gloves } ItemSlots::Boots => { let current_boots = self.boots.clone(); self._update_boots(item); current_boots } ItemSlots::Ring => { if let Some(index) = self.rings.iter().position(|ring| ring.is_none()) { self.set_rings(Some(item), index); return Ok(None); } // return first ring, if there is no slot left let current_ring = self.rings[0].clone(); self.set_rings(Some(item), 0); return Ok(current_ring); } ItemSlots::Amulet => { if let Some(index) = self.amulets.iter().position(|amulet| amulet.is_none()) { self.set_amulets(Some(item), index); return Ok(None); } // return first amulet, if there is no slot left let current_amulet = self.amulets[0].clone(); self.set_amulets(Some(item), 0); return Ok(current_amulet); } ItemSlots::MainHand => { let current_main_hand = self.main_hand.clone(); self.update_primary_hand(item); current_main_hand } ItemSlots::OffHand => { let current_off_hand = self.off_hand.clone(); self.update_secondary_hand(item); current_off_hand } }) } pub fn item_at(&self, slot: ItemSlots) -> Option<&Item> { match slot { ItemSlots::Helmet => self.helmet().as_ref(), ItemSlots::ChestPlate => self.chest().as_ref(), ItemSlots::Belt => self.belt().as_ref(), ItemSlots::Gloves => self.gloves().as_ref(), ItemSlots::Boots => self.boots().as_ref(), ItemSlots::Ring => { for ring in self.rings.iter() { if ring.is_some() { return ring.as_ref(); } } None } ItemSlots::Amulet => { for amulet in self.amulets.iter() { if amulet.is_some() { return amulet.as_ref(); } } None } ItemSlots::MainHand => self.primary_hand().as_ref(), ItemSlots::OffHand => self.secondary_hand().as_ref(), } } pub fn collect_attribute_bonuses( &self, item_settings: &ItemSettings, ) -> (Strength, Agility, Intelligence) { let mut strength = Strength::default(); let mut agility = Agility::default(); let mut intelligence = Intelligence::default(); for item in self.as_vec().iter().copied().flatten() { for affix in item.affixes.iter() { if let ItemAffix::Socket(Some(jewel)) = affix { let value = item_settings .jewel_rarity_multiplier .from_rarity(jewel.rarity) * jewel.level * item_settings.general.jewel_level_multiplier; match jewel.attribute { Attribute::Agility => agility += Agility::from(value), Attribute::Intelligence => intelligence += Intelligence::from(value), Attribute::Strength => strength += Strength::from(value), } } } } (strength, agility, intelligence) } pub fn collect_stat_bonuses(&self) -> Vec { let mut affixes = Vec::new(); let items = self.as_vec(); for item in items.iter().copied().flatten() { for affix in item.affixes.iter() { affixes.push(affix.clone()); } } Self::squash_stats(affixes) } fn squash_stats(affixes: Vec) -> Vec { let mut result = Vec::new(); for stat in affixes.iter() { match stat { ItemAffix::Socket(jewel) => { if let Some(jewel) = jewel { Self::insert_stat(&jewel.stat, &mut result); } } ItemAffix::Stat(stat) => Self::insert_stat(stat, &mut result), } } result } fn insert_stat(val: &StatisticType, vec: &mut Vec) { match vec .iter_mut() .find(|stat| StatisticType::same_type(stat, val)) { Some(stat) => { *stat += val.clone(); } None => vec.push(val.clone()), } } } impl EntityComponent for ItemSlotContainer { fn name(&self) -> &str { Self::debug_name() } } impl ComponentDebug for ItemSlotContainer { fn debug_name() -> &'static str { "ItemSlotContainer" } } #[cfg(test)] mod test { use super::*; use anyhow::Result; use assetpath::AssetPath; #[test] fn create_rarity_textures() -> Result<()> { const OBJECT_PATH: &str = "gltf_objects/blender_test/blender_test.glb"; let path = AssetPath::from((std::env::var("GAVANIA_DATA_DIR").unwrap(), OBJECT_PATH)); let (device, queue) = create_vk_handles()?; let gltf_asset = GltfAsset::new(&path)?; let asset = Asset::new( &device, &queue, SceneType::Rasterizer, gltf_asset, path.path_without_prefix(), )?; if std::path::Path::new("mesh_images").exists() { std::fs::remove_dir_all("mesh_images")?; } if !std::path::Path::new("mesh_images").exists() { std::fs::create_dir("mesh_images")?; } for (x, mesh) in asset.meshes.iter().enumerate() { for (y, primitive_type) in mesh.primitive_types.iter().enumerate() { if let Some(image) = primitive_type.color_texture() { image.to_file(&format!("mesh_images/{}_{}_source.png", x, y))?; } } } Ok(()) } }