rpg_base/character_window/src/inventory/content.rs
2025-04-06 09:23:11 +02:00

407 lines
13 KiB
Rust

use std::sync::{Arc, Weak};
use rpg_components::components::attributes::Attributes;
use rpg_components::components::inventory::{Inventory, Storable};
use rpg_components::components::item_slots::ItemSlotContainer;
use rpg_components::components::statistics::Statistics;
use rpg_components::config::attributes::AttributeSettings;
use rpg_components::config::items::ItemSettings;
use rpg_components::items::{Item, ItemAffix, Jewel, MapItem};
use crate::*;
use crate::{
CharacterWindow,
content::{Content, ContentUpdate},
};
use super::jewel_right_side::{LowerJewels, ReferenceItemSource, ReferenceObject};
impl<A: Ability + 'static> Content<A, Item> {
fn salvage_item(world: &mut World, hero: Entity, item_index: usize) -> Result<()> {
CharacterWindow::salvage_from_inventory::<A, _, _>(world, hero, |inventory| {
let mut item = inventory.remove_item(item_index);
// unsocket jewels and add them into inventory
item.affixes
.iter_mut()
.filter_map(|affix| match affix {
ItemAffix::Socket(j) => j.take(),
ItemAffix::Stat(_) => None,
})
.for_each(|jewel| {
inventory.add_jewel(jewel);
});
item
})
}
fn select_to_socket(world: &mut World, hero: Entity, item_index: usize) -> Result<bool> {
let mut has_empty_sockets = true;
let entity = world.entity(hero)?;
let inventory = entity.get_component::<Inventory<A>>()?;
let item = inventory.item_at(item_index).clone();
if item.affixes.iter().any(|affix| {
if let ItemAffix::Socket(None) = affix {
true
} else {
false
}
}) {
let socket_object: &mut ReferenceObject = world.resources.get_mut()?;
*socket_object = ReferenceObject::Item {
item,
source: ReferenceItemSource::Inventory(item_index),
};
} else {
has_empty_sockets = false;
}
Ok(has_empty_sockets)
}
fn equip_item(world: &mut World, hero: Entity, item_index: usize) -> Result<()> {
let (entity, resources) = world.entity_resources(hero)?;
let (hero_items, inventory, attributes, statistics): (
&mut ItemSlotContainer,
&mut Inventory<A>,
&mut Attributes,
&mut Statistics,
) = entity.get_components_mut()?;
// remove item from inventory
let item = inventory.remove_item(item_index);
// add or swap items with equipment
if let Some(old_item) = hero_items.insert(item.clone(), attributes)? {
inventory.insert_item(old_item, item_index);
}
// update hero stats
statistics.update(
attributes,
resources.get::<AttributeSettings>(),
(&*hero_items, resources.get::<ItemSettings>()),
);
Ok(())
}
fn show_item_tooltip(
world: &mut World,
hero: Entity,
item_index: usize,
reference: &Weak<CharacterWindow>,
(x, y, w, _h): (i32, i32, u32, u32),
) -> Result<()> {
let entity = unsafe { remove_life_time(world.entity(hero)?) };
let inventory = entity.get_component::<Inventory<A>>()?;
let attributes = entity.get_component::<Attributes>()?;
let target_x = x + w as i32;
let target_y = y;
let item = inventory.item_at(item_index);
let gui = item.create_tooltip(world, attributes, (target_x, target_y))?;
gui.enable(world)?;
let window = reference.upgrade().unwrap();
let items = entity.get_component::<ItemSlotContainer>()?;
match items.item_at(item.slot) {
Some(equipped) => {
let grid_pos = gui.position_extent();
let spacing = 2;
let start_x = grid_pos.0 + grid_pos.2 as i32 + spacing;
let start_y = grid_pos.1;
let compare_gui = equipped.create_tooltip(world, attributes, (start_x, start_y))?;
compare_gui.enable(world)?;
gui.perform_double_check(
&compare_gui,
world.resources.get_mut()?,
x,
spacing as u32,
)?;
window.add_tooltip("equip", compare_gui);
}
None => {
gui.perform_single_check(world.resources.get_mut()?, x, y)?;
}
}
window.add_tooltip(format!("item_{item_index}"), gui);
Ok(())
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, Item> {
fn update(&mut self, world: &mut World, hero: Entity) -> Result<()> {
let reference = self.reference.clone();
self.update_base(world, |world, button, t, index| {
button.set_icon(world.resources.get_mut()?, &t.icon)?;
button.set_custom_callback({
let reference = reference.clone();
move |world, controller_button| {
Ok(match controller_button {
ControllerButton::X => {
Self::salvage_item(world, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(world, true)?;
}
true
}
ControllerButton::Y => {
if Self::select_to_socket(world, hero, index)? {
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.switch_to_jewels(world)?;
}
}
true
}
_ => false,
})
}
});
button.set_callback({
let reference = reference.clone();
move |world| {
Self::equip_item(world, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(world, true)?;
}
Ok(())
}
});
button.set_select_callback({
let weak_button = Arc::downgrade(&button);
let reference = reference.clone();
move |world, selected| {
if selected {
let button_pos = weak_button.upgrade().unwrap().position_extent();
Self::show_item_tooltip(world, hero, index, &reference, button_pos)?;
} else {
let window = reference.upgrade().unwrap();
window.remove_tooltip(world, format!("item_{index}"))?;
window.remove_tooltip(world, "equip")?;
}
Ok(())
}
});
Ok(())
})
}
fn select(&self, world: &mut World) -> Result<()> {
self.select(world.resources.get_mut()?)
}
}
impl<A: Ability + 'static> Content<A, Jewel> {
fn show_jewel_tooltip(
world: &mut World,
hero: Entity,
item_index: usize,
reference: &Weak<CharacterWindow>,
(x, y, w, _h): (i32, i32, u32, u32),
) -> Result<()> {
let entity = unsafe { remove_life_time(world.entity(hero)?) };
let inventory = entity.get_component::<Inventory<A>>()?;
let target_x = x + w as i32;
let target_y = y;
let item_settings = world.resources.get_unchecked::<ItemSettings>();
let jewel = inventory.jewel_at(item_index);
let gui = jewel.create_tooltip(world, item_settings, (target_x, target_y))?;
gui.enable(world)?;
gui.perform_single_check(world.resources.get_mut()?, x, y)?;
reference
.upgrade()
.unwrap()
.add_tooltip(format!("jewel_{item_index}"), gui);
Ok(())
}
fn select_to_combine(world: &mut World, hero: Entity, jewel_index: usize) -> Result<()> {
let entity = world.entity(hero)?;
let inventory = entity.get_component::<Inventory<A>>()?;
let jewel = inventory.jewel_at(jewel_index).clone();
// add to reference
let socket_object: &mut ReferenceObject = world.resources.get_mut()?;
*socket_object = ReferenceObject::Jewel {
jewel,
index: jewel_index,
};
// remove from lower if placed there
let lower_jewels: &mut LowerJewels = world.resources.get_mut()?;
if let Some(position) = lower_jewels.jewels.iter().position(|jewel| match jewel {
Some((_, index)) => *index == jewel_index,
None => false,
}) {
lower_jewels.jewels[position] = None;
}
Ok(())
}
fn select_to_lower(world: &mut World, hero: Entity, jewel_index: usize) -> Result<()> {
let entity = world.entity(hero)?;
let inventory = entity.get_component::<Inventory<A>>()?;
let jewel = inventory.jewel_at(jewel_index).clone();
// remove from reference if placed there
let socket_object: &mut ReferenceObject = world.resources.get_mut()?;
if let Some(ReferenceObject::Jewel { index, .. }) = socket_object.some() {
if *index == jewel_index {
*socket_object = ReferenceObject::Empty;
}
}
let lower_jewels: &mut LowerJewels = world.resources.get_mut()?;
// check if that jewel is already added
if !lower_jewels.jewels.iter().any(|content| match content {
Some((_, index)) => *index == jewel_index,
None => false,
}) {
// search for an empty position in lower jewels
match lower_jewels.jewels.iter().position(|jewel| jewel.is_none()) {
Some(position) => lower_jewels.jewels[position] = Some((jewel, jewel_index)),
None => lower_jewels.jewels[0] = Some((jewel, jewel_index)),
}
}
Ok(())
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, Jewel> {
fn update(&mut self, world: &mut World, hero: Entity) -> Result<()> {
let reference = self.reference.clone();
self.update_base(world, |world, button, t, index| {
button.set_icon(world.resources.get_mut()?, &t.icon())?;
button.set_select_callback({
let weak_button = Arc::downgrade(&button);
let reference = reference.clone();
move |world, selected| {
if selected {
let button_pos = weak_button.upgrade().unwrap().position_extent();
Self::show_jewel_tooltip(world, hero, index, &reference, button_pos)?;
} else {
let window = reference.upgrade().unwrap();
window.remove_tooltip(world, format!("jewel_{index}"))?;
}
Ok(())
}
});
button.set_callback({
let reference = reference.clone();
move |world| {
Self::select_to_lower(world, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(world, true)?;
}
Ok(())
}
});
button.set_custom_callback({
let reference = reference.clone();
move |world, controller_button| {
Ok(match controller_button {
ControllerButton::Y => {
Self::select_to_combine(world, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(world, true)?;
}
true
}
_ => false,
})
}
});
Ok(())
})
}
fn select(&self, world: &mut World) -> Result<()> {
self.select(world.resources.get_mut()?)
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, MapItem> {
fn update(&mut self, world: &mut World, _hero: Entity) -> Result<()> {
self.update_base(world, |_world, _button, _t, _index| {
// button.set_icon(&t.icon)?;
Ok(())
})
}
fn select(&self, world: &mut World) -> Result<()> {
self.select(world.resources.get_mut()?)
}
}