rpg_base/character_window/src/inventory/content.rs

412 lines
13 KiB
Rust
Raw Normal View History

2025-02-28 07:43:35 +00:00
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,
2025-03-05 08:45:39 +00:00
content::{Content, ContentUpdate},
2025-02-28 07:43:35 +00:00
};
use super::jewel_right_side::{LowerJewels, ReferenceItemSource, ReferenceObject};
impl<A: Ability + 'static> Content<A, Item> {
2025-03-05 08:45:39 +00:00
fn salvage_item(world: &mut World, hero: Entity, item_index: usize) -> Result<()> {
CharacterWindow::salvage_from_inventory::<A, _, _>(world, hero, |inventory| {
2025-02-28 07:43:35 +00:00
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
})
}
2025-03-05 08:45:39 +00:00
fn select_to_socket(world: &mut World, hero: Entity, item_index: usize) -> Result<bool> {
2025-02-28 07:43:35 +00:00
let mut has_empty_sockets = true;
2025-03-05 08:45:39 +00:00
let entity = world.entity(hero)?;
let inventory = entity.get_component::<Inventory<A>>()?;
let item = inventory.item_at(item_index).clone();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
if item.affixes.iter().any(|affix| {
if let ItemAffix::Socket(None) = affix {
true
2025-02-28 07:43:35 +00:00
} else {
2025-03-05 08:45:39 +00:00
false
2025-02-28 07:43:35 +00:00
}
2025-03-05 08:45:39 +00:00
}) {
let socket_object = world.resources.get_mut::<Option<ReferenceObject>>();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
*socket_object = Some(ReferenceObject::Item {
item,
source: ReferenceItemSource::Inventory(item_index),
});
} else {
has_empty_sockets = false;
}
2025-02-28 07:43:35 +00:00
Ok(has_empty_sockets)
}
2025-03-05 08:45:39 +00:00
fn equip_item(world: &mut World, hero: Entity, item_index: usize) -> Result<()> {
let (entity, resources) = world.entity_resources(hero)?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let mut multi_mut = entity.multi_mut();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let hero_items = multi_mut.get::<ItemSlotContainer>()?;
let inventory = multi_mut.get::<Inventory<A>>()?;
let attributes = multi_mut.get::<Attributes>()?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
// remove item from inventory
let item = inventory.remove_item(item_index);
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
// add or swap items with equipment
if let Some(old_item) = hero_items.insert(item.clone(), attributes, &mut multi_mut)? {
inventory.insert_item(old_item, item_index);
}
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
// update hero stats
let statistics = multi_mut.get::<Statistics>()?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
statistics.update(
attributes,
resources.get::<AttributeSettings>(),
(&*hero_items, resources.get::<ItemSettings>()),
);
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
Ok(())
2025-02-28 07:43:35 +00:00
}
fn show_item_tooltip(
2025-03-05 08:45:39 +00:00
world: &mut World,
2025-02-28 07:43:35 +00:00
hero: Entity,
item_index: usize,
reference: &Weak<CharacterWindow>,
(x, y, w, _h): (i32, i32, u32, u32),
) -> Result<()> {
2025-03-05 08:45:39 +00:00
let entity = unsafe { remove_life_time(world.entity(hero)?) };
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let inventory = entity.get_component::<Inventory<A>>()?;
let attributes = entity.get_component::<Attributes>()?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let target_x = x + w as i32;
let target_y = y;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let item = inventory.item_at(item_index);
let gui = item.create_tooltip(world, attributes, (target_x, target_y))?;
gui.enable(world)?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let window = reference.upgrade().unwrap();
let items = entity.get_component::<ItemSlotContainer>()?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
match items.item_at(item.slot) {
Some(equipped) => {
let grid_pos = gui.position_extent();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
let spacing = 2;
let start_x = grid_pos.0 + grid_pos.2 as i32 + spacing;
let start_y = grid_pos.1;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
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::<GuiHandler>(),
x,
spacing as u32,
)?;
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
window.add_tooltip("equip", compare_gui);
2025-02-28 07:43:35 +00:00
}
2025-03-05 08:45:39 +00:00
None => {
gui.perform_single_check(world.resources.get_mut::<GuiHandler>(), x, y)?;
}
}
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
window.add_tooltip(format!("item_{item_index}"), gui);
2025-02-28 07:43:35 +00:00
Ok(())
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, Item> {
2025-03-05 08:45:39 +00:00
fn update(&mut self, world: &mut World, hero: Entity) -> Result<()> {
2025-02-28 07:43:35 +00:00
let reference = self.reference.clone();
2025-03-05 08:45:39 +00:00
self.update_base(world, |world, button, t, index| {
button.set_icon(world.resources.get_mut::<GuiHandler>(), &t.icon)?;
2025-02-28 07:43:35 +00:00
button.set_custom_callback({
let reference = reference.clone();
2025-03-05 08:45:39 +00:00
move |world, controller_button| {
2025-02-28 07:43:35 +00:00
Ok(match controller_button {
ControllerButton::X => {
2025-03-05 08:45:39 +00:00
Self::salvage_item(world, hero, index)?;
2025-02-28 07:43:35 +00:00
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
2025-03-05 08:45:39 +00:00
inventory.update_page(world, true)?;
2025-02-28 07:43:35 +00:00
}
true
}
ControllerButton::Y => {
2025-03-05 08:45:39 +00:00
if Self::select_to_socket(world, hero, index)? {
2025-02-28 07:43:35 +00:00
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
2025-03-05 08:45:39 +00:00
inventory.switch_to_jewels(world)?;
2025-02-28 07:43:35 +00:00
}
}
true
}
_ => false,
})
}
});
button.set_callback({
let reference = reference.clone();
2025-03-05 08:45:39 +00:00
move |world| {
Self::equip_item(world, hero, index)?;
2025-02-28 07:43:35 +00:00
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
2025-03-05 08:45:39 +00:00
inventory.update_page(world, true)?;
2025-02-28 07:43:35 +00:00
}
Ok(())
}
});
button.set_select_callback({
let weak_button = Arc::downgrade(&button);
let reference = reference.clone();
2025-03-05 08:45:39 +00:00
move |world, selected| {
2025-02-28 07:43:35 +00:00
if selected {
let button_pos = weak_button.upgrade().unwrap().position_extent();
2025-03-05 08:45:39 +00:00
Self::show_item_tooltip(world, hero, index, &reference, button_pos)?;
2025-02-28 07:43:35 +00:00
} else {
let window = reference.upgrade().unwrap();
window.remove_tooltip(format!("item_{index}"));
window.remove_tooltip("equip");
}
Ok(())
}
});
Ok(())
})
}
2025-03-05 08:45:39 +00:00
fn select(&self, world: &mut World) -> Result<()> {
self.select(world.resources.get_mut::<GuiHandler>())
2025-02-28 07:43:35 +00:00
}
}
impl<A: Ability + 'static> Content<A, Jewel> {
fn show_jewel_tooltip(
2025-03-05 08:45:39 +00:00
world: &mut World,
2025-02-28 07:43:35 +00:00
hero: Entity,
item_index: usize,
reference: &Weak<CharacterWindow>,
(x, y, w, _h): (i32, i32, u32, u32),
) -> Result<()> {
2025-03-05 08:45:39 +00:00
let entity = world.entity(hero)?;
let inventory = entity.get_component::<Inventory<A>>()?;
let target_x = x + w as i32;
let target_y = y;
let jewel = inventory.jewel_at(item_index);
let gui = jewel.create_tooltip(
world,
world.resources.get::<ItemSettings>(),
(target_x, target_y),
)?;
gui.enable(world)?;
gui.perform_single_check(world.resources.get_mut::<GuiHandler>(), x, y)?;
reference
.upgrade()
.unwrap()
.add_tooltip(format!("jewel_{item_index}"), gui);
2025-02-28 07:43:35 +00:00
Ok(())
}
2025-03-05 08:45:39 +00:00
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();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
// add to reference
let socket_object = world.resources.get_mut::<Option<ReferenceObject>>();
*socket_object = Some(ReferenceObject::Jewel {
jewel,
index: jewel_index,
});
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
// remove from lower if placed there
let lower_jewels = world.resources.get_mut::<LowerJewels>();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
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(())
2025-02-28 07:43:35 +00:00
}
2025-03-05 08:45:39 +00:00
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();
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
// remove from reference if placed there
let socket_object = world.resources.get_mut::<Option<ReferenceObject>>();
if let Some(ReferenceObject::Jewel { index, .. }) = socket_object {
if *index == jewel_index {
*socket_object = None;
}
}
let lower_jewels = world.resources.get_mut::<LowerJewels>();
// 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)),
2025-02-28 07:43:35 +00:00
}
2025-03-05 08:45:39 +00:00
}
2025-02-28 07:43:35 +00:00
2025-03-05 08:45:39 +00:00
Ok(())
2025-02-28 07:43:35 +00:00
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, Jewel> {
2025-03-05 08:45:39 +00:00
fn update(&mut self, world: &mut World, hero: Entity) -> Result<()> {
2025-02-28 07:43:35 +00:00
let reference = self.reference.clone();
2025-03-05 08:45:39 +00:00
self.update_base(world, |world, button, t, index| {
button.set_icon(world.resources.get_mut::<GuiHandler>(), &t.icon())?;
2025-02-28 07:43:35 +00:00
button.set_select_callback({
let weak_button = Arc::downgrade(&button);
let reference = reference.clone();
2025-03-05 08:45:39 +00:00
move |world, selected| {
2025-02-28 07:43:35 +00:00
if selected {
let button_pos = weak_button.upgrade().unwrap().position_extent();
2025-03-05 08:45:39 +00:00
Self::show_jewel_tooltip(world, hero, index, &reference, button_pos)?;
2025-02-28 07:43:35 +00:00
} else {
let window = reference.upgrade().unwrap();
window.remove_tooltip(format!("jewel_{index}"));
}
Ok(())
}
});
button.set_callback({
let reference = reference.clone();
2025-03-05 08:45:39 +00:00
move |world| {
Self::select_to_lower(world, hero, index)?;
2025-02-28 07:43:35 +00:00
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
2025-03-05 08:45:39 +00:00
inventory.update_page(world, true)?;
2025-02-28 07:43:35 +00:00
}
Ok(())
}
});
button.set_custom_callback({
let reference = reference.clone();
2025-03-05 08:45:39 +00:00
move |world, controller_button| {
2025-02-28 07:43:35 +00:00
Ok(match controller_button {
ControllerButton::Y => {
2025-03-05 08:45:39 +00:00
Self::select_to_combine(world, hero, index)?;
2025-02-28 07:43:35 +00:00
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
2025-03-05 08:45:39 +00:00
inventory.update_page(world, true)?;
2025-02-28 07:43:35 +00:00
}
true
}
_ => false,
})
}
});
Ok(())
})
}
2025-03-05 08:45:39 +00:00
fn select(&self, world: &mut World) -> Result<()> {
self.select(world.resources.get_mut::<GuiHandler>())
2025-02-28 07:43:35 +00:00
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, MapItem> {
2025-03-05 08:45:39 +00:00
fn update(&mut self, world: &mut World, _hero: Entity) -> Result<()> {
self.update_base(world, |_world, _button, _t, _index| {
2025-02-28 07:43:35 +00:00
// button.set_icon(&t.icon)?;
Ok(())
})
}
2025-03-05 08:45:39 +00:00
fn select(&self, world: &mut World) -> Result<()> {
self.select(world.resources.get_mut::<GuiHandler>())
2025-02-28 07:43:35 +00:00
}
}