rpg_base/character_window/src/inventory/content.rs
2025-02-28 08:43:35 +01:00

435 lines
14 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::{
content::{Content, ContentUpdate},
CharacterWindow,
};
use super::jewel_right_side::{LowerJewels, ReferenceItemSource, ReferenceObject};
impl<A: Ability + 'static> Content<A, Item> {
fn salvage_item(engine: &Arc<Engine>, hero: Entity, item_index: usize) -> Result<()> {
CharacterWindow::salvage_from_inventory::<A, _, _>(engine, 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(engine: &Arc<Engine>, hero: Entity, item_index: usize) -> Result<bool> {
let mut has_empty_sockets = true;
engine.on_scene_mut(|scene| {
let entity = scene.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 = scene.resources.get_mut::<Option<ReferenceObject>>();
*socket_object = Some(ReferenceObject::Item {
item,
source: ReferenceItemSource::Inventory(item_index),
});
} else {
has_empty_sockets = false;
}
Ok(())
})?;
Ok(has_empty_sockets)
}
fn equip_item(engine: &Arc<Engine>, hero: Entity, item_index: usize) -> Result<()> {
engine.on_scene_mut(|scene| {
let (resources, entity) = scene.entity_resource(hero)?;
let mut multi_mut = entity.multi_mut();
let hero_items = multi_mut.get::<ItemSlotContainer>()?;
let inventory = multi_mut.get::<Inventory<A>>()?;
let attributes = multi_mut.get::<Attributes>()?;
// 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, &mut multi_mut)? {
inventory.insert_item(old_item, item_index);
}
// update hero stats
let statistics = multi_mut.get::<Statistics>()?;
statistics.update(
attributes,
resources.get::<AttributeSettings>(),
(&*hero_items, resources.get::<ItemSettings>()),
);
Ok(())
})
}
fn show_item_tooltip(
engine: &Arc<Engine>,
hero: Entity,
item_index: usize,
reference: &Weak<CharacterWindow>,
(x, y, w, _h): (i32, i32, u32, u32),
) -> Result<()> {
engine.on_scene(|scene| {
let entity = scene.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(engine.gui_handler(), attributes, (target_x, target_y))?;
gui.enable()?;
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(
engine.gui_handler(),
attributes,
(start_x, start_y),
)?;
compare_gui.enable()?;
gui.perform_double_check(&compare_gui, x, spacing as u32)?;
window.add_tooltip("equip", compare_gui);
}
None => {
gui.perform_single_check(x, y)?;
}
}
window.add_tooltip(format!("item_{item_index}"), gui);
Ok(())
})?;
Ok(())
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, Item> {
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
let reference = self.reference.clone();
self.update_base(engine, |button, t, index| {
button.set_icon(&t.icon)?;
button.set_custom_callback({
let reference = reference.clone();
let engine = engine.clone();
move |controller_button| {
Ok(match controller_button {
ControllerButton::X => {
Self::salvage_item(&engine, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(true)?;
}
true
}
ControllerButton::Y => {
if Self::select_to_socket(&engine, hero, index)? {
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.switch_to_jewels()?;
}
}
true
}
_ => false,
})
}
});
button.set_callback({
let reference = reference.clone();
let engine = engine.clone();
move || {
Self::equip_item(&engine, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(true)?;
}
Ok(())
}
});
button.set_select_callback({
let weak_button = Arc::downgrade(&button);
let reference = reference.clone();
let engine = engine.clone();
move |selected| {
if selected {
let button_pos = weak_button.upgrade().unwrap().position_extent();
Self::show_item_tooltip(&engine, hero, index, &reference, button_pos)?;
} else {
let window = reference.upgrade().unwrap();
window.remove_tooltip(format!("item_{index}"));
window.remove_tooltip("equip");
}
Ok(())
}
});
Ok(())
})
}
fn select(&self) -> Result<()> {
self.select()
}
}
impl<A: Ability + 'static> Content<A, Jewel> {
fn show_jewel_tooltip(
engine: &Arc<Engine>,
hero: Entity,
item_index: usize,
reference: &Weak<CharacterWindow>,
(x, y, w, _h): (i32, i32, u32, u32),
) -> Result<()> {
engine.on_scene(|scene| {
let entity = scene.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(
engine.gui_handler(),
scene.resources.get::<ItemSettings>(),
(target_x, target_y),
)?;
gui.enable()?;
gui.perform_single_check(x, y)?;
reference
.upgrade()
.unwrap()
.add_tooltip(format!("jewel_{item_index}"), gui);
Ok(())
})?;
Ok(())
}
fn select_to_combine(engine: &Arc<Engine>, hero: Entity, jewel_index: usize) -> Result<()> {
engine.on_scene_mut(|scene| {
let entity = scene.entity(hero)?;
let inventory = entity.get_component::<Inventory<A>>()?;
let jewel = inventory.jewel_at(jewel_index).clone();
// add to reference
let socket_object = scene.resources.get_mut::<Option<ReferenceObject>>();
*socket_object = Some(ReferenceObject::Jewel {
jewel,
index: jewel_index,
});
// remove from lower if placed there
let lower_jewels = scene.resources.get_mut::<LowerJewels>();
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(engine: &Arc<Engine>, hero: Entity, jewel_index: usize) -> Result<()> {
engine.on_scene_mut(|scene| {
let entity = scene.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 = scene.resources.get_mut::<Option<ReferenceObject>>();
if let Some(ReferenceObject::Jewel { index, .. }) = socket_object {
if *index == jewel_index {
*socket_object = None;
}
}
let lower_jewels = scene.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)),
}
}
Ok(())
})
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, Jewel> {
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
let reference = self.reference.clone();
self.update_base(engine, |button, t, index| {
button.set_icon(&t.icon())?;
button.set_select_callback({
let weak_button = Arc::downgrade(&button);
let engine = engine.clone();
let reference = reference.clone();
move |selected| {
if selected {
let button_pos = weak_button.upgrade().unwrap().position_extent();
Self::show_jewel_tooltip(&engine, hero, index, &reference, button_pos)?;
} else {
let window = reference.upgrade().unwrap();
window.remove_tooltip(format!("jewel_{index}"));
}
Ok(())
}
});
button.set_callback({
let engine = engine.clone();
let reference = reference.clone();
move || {
Self::select_to_lower(&engine, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(true)?;
}
Ok(())
}
});
button.set_custom_callback({
let engine = engine.clone();
let reference = reference.clone();
move |controller_button| {
Ok(match controller_button {
ControllerButton::Y => {
Self::select_to_combine(&engine, hero, index)?;
if let Some(menu) = reference.upgrade() {
let mut tabs = menu.tabs_mut();
let inventory = tabs.inventory::<A>();
inventory.update_page(true)?;
}
true
}
_ => false,
})
}
});
Ok(())
})
}
fn select(&self) -> Result<()> {
self.select()
}
}
impl<A: Ability + 'static> ContentUpdate for Content<A, MapItem> {
fn update(&mut self, engine: &Arc<Engine>, _hero: Entity) -> Result<()> {
self.update_base(engine, |_button, _t, _index| {
// button.set_icon(&t.icon)?;
Ok(())
})
}
fn select(&self) -> Result<()> {
self.select()
}
}