rpg_base/rpg_components/src/components/item_slots.rs

913 lines
28 KiB
Rust
Raw Normal View History

2025-02-28 07:43:35 +00:00
use anyhow::Result;
use engine::prelude::*;
use paste;
use std::collections::HashMap;
use crate::{
config::{items::ItemSettings, save_game::SaveGame},
2025-04-05 10:16:34 +00:00
items::{Item, ItemAffix, ItemSlots, ItemSystem, Rarities, ability_book::Ability},
2025-02-28 07:43:35 +00:00
};
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! {
2025-04-06 07:23:11 +00:00
fn [<set_ $item>](&mut self, $item: Option<Item>) {
2025-02-28 07:43:35 +00:00
self.$item = $item;
}
}
};
}
pub const RING_COUNT: usize = 4;
pub const AMULET_COUNT: usize = 2;
pub struct ItemSlotContainer {
// main slots
helmet: Option<Item>,
chest_plate: Option<Item>,
belt: Option<Item>,
gloves: Option<Item>,
boots: Option<Item>,
// hand slots
main_hand: Option<Item>,
off_hand: Option<Item>,
// jewelry slots
rings: [Option<Item>; RING_COUNT],
amulets: [Option<Item>; 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<A: Ability>(save_game: &SaveGame, item_system: &ItemSystem<A>) -> Result<Self> {
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<Item>> {
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<ItemSlots, AssetMesh>,
item_settings: &ItemSettings,
) -> Result<HashMap<ItemSlots, HashMap<Rarities, AssetMesh>>> {
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::<Result<Vec<_>>>()?;
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<Item>, index: usize) {
self.rings[index] = ring;
}
fn set_amulets(&mut self, amulet: Option<Item>, index: usize) {
self.amulets[index] = amulet;
}
}
// getter
impl ItemSlotContainer {
pub fn helmet(&self) -> &Option<Item> {
&self.helmet
}
pub fn chest(&self) -> &Option<Item> {
&self.chest_plate
}
pub fn belt(&self) -> &Option<Item> {
&self.belt
}
pub fn gloves(&self) -> &Option<Item> {
&self.gloves
}
pub fn boots(&self) -> &Option<Item> {
&self.boots
}
pub fn primary_hand(&self) -> &Option<Item> {
&self.main_hand
}
pub fn secondary_hand(&self) -> &Option<Item> {
&self.off_hand
}
pub fn ring(&self, index: usize) -> &Option<Item> {
&self.rings[index]
}
pub fn amulet(&self, index: usize) -> &Option<Item> {
&self.amulets[index]
}
pub fn item_mut(&mut self, slot: ItemSlots, index: Option<usize>) -> &mut Option<Item> {
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 {
2025-04-06 07:23:11 +00:00
fn _update_helmet(&mut self, helmet: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(helmet.slot == ItemSlots::Helmet);
2025-04-06 07:23:11 +00:00
self.set_helmet(Some(helmet));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_helmet(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.helmet.is_some() {
2025-04-06 07:23:11 +00:00
self.set_helmet(None);
2025-02-28 07:43:35 +00:00
}
Ok(())
}
2025-04-06 07:23:11 +00:00
fn update_chest(&mut self, chest: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(chest.slot == ItemSlots::ChestPlate);
2025-04-06 07:23:11 +00:00
self.set_chest_plate(Some(chest));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_chest(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.chest_plate.is_some() {
2025-04-06 07:23:11 +00:00
self.set_chest_plate(None);
2025-02-28 07:43:35 +00:00
}
Ok(())
}
2025-04-06 07:23:11 +00:00
fn _update_belt(&mut self, belt: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(belt.slot == ItemSlots::Belt);
2025-04-06 07:23:11 +00:00
self.set_belt(Some(belt));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_belt(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.belt.is_some() {
2025-04-06 07:23:11 +00:00
self.set_belt(None);
2025-02-28 07:43:35 +00:00
}
Ok(())
}
2025-04-06 07:23:11 +00:00
fn _update_gloves(&mut self, gloves: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(gloves.slot == ItemSlots::Gloves);
2025-04-06 07:23:11 +00:00
self.set_gloves(Some(gloves));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_gloves(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.gloves.is_some() {
2025-04-06 07:23:11 +00:00
self.set_gloves(None);
2025-02-28 07:43:35 +00:00
}
Ok(())
}
2025-04-06 07:23:11 +00:00
fn _update_boots(&mut self, boots: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(boots.slot == ItemSlots::Boots);
2025-04-06 07:23:11 +00:00
self.set_boots(Some(boots));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_boots(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.boots.is_some() {
2025-04-06 07:23:11 +00:00
self.set_boots(None);
2025-02-28 07:43:35 +00:00
}
Ok(())
}
2025-04-06 07:23:11 +00:00
fn update_primary_hand(&mut self, primary_hand: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(primary_hand.slot == ItemSlots::MainHand);
2025-04-06 07:23:11 +00:00
self.set_main_hand(Some(primary_hand));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_primary_hand(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.main_hand.is_some() {
2025-04-06 07:23:11 +00:00
self.set_main_hand(None);
2025-02-28 07:43:35 +00:00
}
Ok(())
}
2025-04-06 07:23:11 +00:00
fn update_secondary_hand(&mut self, secondary_hand: Item) {
2025-02-28 07:43:35 +00:00
debug_assert!(secondary_hand.slot == ItemSlots::OffHand);
2025-04-06 07:23:11 +00:00
self.set_off_hand(Some(secondary_hand));
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn unset_secondary_hand(&mut self) -> Result<()> {
2025-02-28 07:43:35 +00:00
if self.off_hand.is_some() {
2025-04-06 07:23:11 +00:00
self.set_off_hand(None);
2025-02-28 07:43:35 +00:00
}
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<ItemSlots, AssetMesh>,
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<HashMap<ItemSlots, AssetMesh>> {
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<ItemSlots, HashMap<Rarities, AssetMesh>>,
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(
2025-04-06 07:23:11 +00:00
&self,
2025-02-28 07:43:35 +00:00
draw: &mut Draw,
location: &Location,
item_meshes: &HashMap<ItemSlots, HashMap<Rarities, AssetMesh>>,
) -> Result<()> {
macro_rules! check {
($func:ident, $name:expr) => {
2025-04-06 07:23:11 +00:00
match self.$func() {
2025-02-28 07:43:35 +00:00
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(
2025-04-05 10:16:34 +00:00
entity: &mut EntityObject,
2025-02-28 07:43:35 +00:00
item_meshes: &HashMap<ItemSlots, HashMap<Rarities, AssetMesh>>,
slot: ItemSlots,
rarity: Rarities,
is_some: bool,
) -> Result<()> {
2025-04-05 10:16:34 +00:00
let (draw, location): (&mut Draw, &mut Location) = entity.get_components_mut()?;
2025-02-28 07:43:35 +00:00
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
2025-04-06 07:23:11 +00:00
pub fn insert(&mut self, item: Item, attributes: &Attributes) -> Result<Option<Item>> {
2025-02-28 07:43:35 +00:00
// check attribute requirements
if attributes < &item.attributes {
return Ok(Some(item));
}
2025-04-06 07:23:11 +00:00
self.insert_unchecked(item)
2025-02-28 07:43:35 +00:00
}
2025-04-06 07:23:11 +00:00
pub fn insert_unchecked(&mut self, item: Item) -> Result<Option<Item>> {
2025-02-28 07:43:35 +00:00
Ok(match item.slot {
ItemSlots::Helmet => {
let current_helmet = self.helmet.clone();
2025-04-06 07:23:11 +00:00
self._update_helmet(item);
2025-02-28 07:43:35 +00:00
current_helmet
}
ItemSlots::ChestPlate => {
let current_chest_plate = self.chest_plate.clone();
2025-04-06 07:23:11 +00:00
self.update_chest(item);
2025-02-28 07:43:35 +00:00
current_chest_plate
}
ItemSlots::Belt => {
let current_belt = self.belt.clone();
2025-04-06 07:23:11 +00:00
self._update_belt(item);
2025-02-28 07:43:35 +00:00
current_belt
}
ItemSlots::Gloves => {
let current_gloves = self.gloves.clone();
2025-04-06 07:23:11 +00:00
self._update_gloves(item);
2025-02-28 07:43:35 +00:00
current_gloves
}
ItemSlots::Boots => {
let current_boots = self.boots.clone();
2025-04-06 07:23:11 +00:00
self._update_boots(item);
2025-02-28 07:43:35 +00:00
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();
2025-04-06 07:23:11 +00:00
self.update_primary_hand(item);
2025-02-28 07:43:35 +00:00
current_main_hand
}
ItemSlots::OffHand => {
let current_off_hand = self.off_hand.clone();
2025-04-06 07:23:11 +00:00
self.update_secondary_hand(item);
2025-02-28 07:43:35 +00:00
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<StatisticType> {
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<ItemAffix>) -> Vec<StatisticType> {
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<StatisticType>) {
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(())
}
}