435 lines
14 KiB
Rust
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()
|
|
}
|
|
}
|