532 lines
21 KiB
Rust
532 lines
21 KiB
Rust
|
use rpg_components::{
|
||
|
components::{
|
||
|
attributes::Attributes, character_status::CharacterStatus, inventory::Inventory,
|
||
|
item_slots::ItemSlotContainer, statistics::Statistics,
|
||
|
},
|
||
|
config::{attributes::AttributeSettings, items::ItemSettings},
|
||
|
items::{Item, ItemAffix, Tooltip},
|
||
|
};
|
||
|
|
||
|
use crate::*;
|
||
|
|
||
|
use std::sync::{Arc, Weak};
|
||
|
|
||
|
use super::{
|
||
|
super::traits::*,
|
||
|
jewel_right_side::{ReferenceItemSource, ReferenceObject},
|
||
|
};
|
||
|
|
||
|
pub struct ItemRightSide {
|
||
|
snippet: Arc<GuiSnippet>,
|
||
|
|
||
|
empty_icons: InventoryEmptyIcons,
|
||
|
}
|
||
|
|
||
|
impl ItemRightSide {
|
||
|
pub fn new<A: Ability + 'static>(
|
||
|
engine: &Arc<Engine>,
|
||
|
file: &str,
|
||
|
reference: &Weak<CharacterWindow>,
|
||
|
hero: Entity,
|
||
|
) -> Result<Self> {
|
||
|
let snippet = GuiSnippet::from_str(engine.gui_handler(), file)?;
|
||
|
let icons = InventoryEmptyIcons::new(engine)?;
|
||
|
|
||
|
let me = Self {
|
||
|
snippet,
|
||
|
empty_icons: icons,
|
||
|
};
|
||
|
|
||
|
me.setup::<A>(engine, reference, hero)?;
|
||
|
|
||
|
Ok(me)
|
||
|
}
|
||
|
|
||
|
fn setup<A: Ability + 'static>(
|
||
|
&self,
|
||
|
engine: &Arc<Engine>,
|
||
|
reference: &Weak<CharacterWindow>,
|
||
|
hero: Entity,
|
||
|
) -> Result<()> {
|
||
|
button_setup!(self, engine, reference, hero, helmet, "helmet");
|
||
|
button_setup!(self, engine, reference, hero, chest, "chest");
|
||
|
button_setup!(self, engine, reference, hero, gloves, "gloves");
|
||
|
button_setup!(self, engine, reference, hero, belt, "belt");
|
||
|
button_setup!(self, engine, reference, hero, boots, "boots");
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, primary_hand, "main hand");
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, secondary_hand, "off hand");
|
||
|
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, amulet, "amulet_0", 0);
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, amulet, "amulet_1", 1);
|
||
|
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, ring, "ring_0", 0);
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, ring, "ring_1", 1);
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, ring, "ring_2", 2);
|
||
|
#[rustfmt::skip]
|
||
|
button_setup!(self, engine, reference, hero, ring, "ring_3", 3);
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn update_icons(&self, items: &ItemSlotContainer) -> Result<()> {
|
||
|
let ui = &self.snippet;
|
||
|
let empty_icons = &self.empty_icons;
|
||
|
|
||
|
equip_update!(ui, items, helmet, empty_icons);
|
||
|
equip_update!(ui, items, chest, empty_icons);
|
||
|
equip_update!(ui, items, boots, empty_icons);
|
||
|
equip_update!(ui, items, gloves, empty_icons);
|
||
|
equip_update!(ui, items, belt, empty_icons);
|
||
|
|
||
|
equip_update!(ui, items, primary_hand, empty_icons, "main hand");
|
||
|
equip_update!(ui, items, secondary_hand, empty_icons, "off hand");
|
||
|
|
||
|
equip_update!(ui, items, ring, empty_icons, "ring_0", 0);
|
||
|
equip_update!(ui, items, ring, empty_icons, "ring_1", 1);
|
||
|
equip_update!(ui, items, ring, empty_icons, "ring_2", 2);
|
||
|
equip_update!(ui, items, ring, empty_icons, "ring_3", 3);
|
||
|
|
||
|
equip_update!(ui, items, amulet, empty_icons, "amulet_0", 0);
|
||
|
equip_update!(ui, items, amulet, empty_icons, "amulet_1", 1);
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_tooltip(
|
||
|
engine: &Arc<Engine>,
|
||
|
item: &Item,
|
||
|
attributes: &Attributes,
|
||
|
(x, y, w, _h): (i32, i32, u32, u32),
|
||
|
) -> Result<Tooltip> {
|
||
|
let target_x = x + w as i32;
|
||
|
let target_y = y;
|
||
|
|
||
|
let gui = item.create_tooltip(engine.gui_handler(), attributes, (target_x, target_y))?;
|
||
|
gui.enable()?;
|
||
|
gui.perform_single_check(x, y)?;
|
||
|
|
||
|
Ok(gui)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl RightSide for ItemRightSide {
|
||
|
fn refresh(&mut self, engine: &Engine, hero: Entity) -> Result<()> {
|
||
|
engine.on_scene(|scene| {
|
||
|
let hero_object = scene.entity(hero)?;
|
||
|
|
||
|
let items = hero_object.get_component::<ItemSlotContainer>()?;
|
||
|
|
||
|
self.update_icons(items)?;
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn base(&self) -> &Arc<GuiSnippet> {
|
||
|
&self.snippet
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct InventoryEmptyIcons {
|
||
|
helmet: Arc<Image>,
|
||
|
chest: Arc<Image>,
|
||
|
belt: Arc<Image>,
|
||
|
boots: Arc<Image>,
|
||
|
gloves: Arc<Image>,
|
||
|
|
||
|
primary_hand: Arc<Image>,
|
||
|
secondary_hand: Arc<Image>,
|
||
|
|
||
|
ring: Arc<Image>,
|
||
|
amulet: Arc<Image>,
|
||
|
}
|
||
|
|
||
|
impl InventoryEmptyIcons {
|
||
|
fn new(engine: &Engine) -> Result<Self> {
|
||
|
let place_holder_settings = &engine
|
||
|
.scene()
|
||
|
.resources
|
||
|
.get::<ItemSettings>()
|
||
|
.icon_place_holder_paths;
|
||
|
|
||
|
Ok(Self {
|
||
|
helmet: Image::from_file(place_holder_settings.helmet.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
chest: Image::from_file(place_holder_settings.chest.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
belt: Image::from_file(place_holder_settings.belt.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
boots: Image::from_file(place_holder_settings.boots.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
gloves: Image::from_file(place_holder_settings.gloves.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
|
||
|
primary_hand: Image::from_file(place_holder_settings.main_hand.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
secondary_hand: Image::from_file(place_holder_settings.off_hand.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
|
||
|
ring: Image::from_file(place_holder_settings.ring.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
amulet: Image::from_file(place_holder_settings.amulet.clone())?
|
||
|
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||
|
.build(engine.device(), engine.queue())?,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mod macros {
|
||
|
#[macro_export]
|
||
|
macro_rules! button_setup {
|
||
|
($self:ident, $engine:ident, $reference:ident, $hero:ident, $item:ident, $button:literal) => {
|
||
|
paste::expr! {
|
||
|
let [<$item _button>]: Arc<Button> = $self.snippet.element($button)?;
|
||
|
|
||
|
[<$item _button>].set_select_callback({
|
||
|
let engine = $engine.clone();
|
||
|
let reference = $reference.clone();
|
||
|
let weak_button = Arc::downgrade(&[<$item _button>]);
|
||
|
|
||
|
move |selected| {
|
||
|
if selected {
|
||
|
engine.on_scene(|scene| {
|
||
|
let entity = scene.entity($hero)?;
|
||
|
|
||
|
let attributes = entity.get_component::<Attributes>()?;
|
||
|
let items = entity.get_component::<ItemSlotContainer>()?;
|
||
|
|
||
|
match items.$item() {
|
||
|
Some($item) => {
|
||
|
let button_pos =
|
||
|
weak_button.upgrade().unwrap().position_extent();
|
||
|
|
||
|
let gui = Self::create_tooltip(
|
||
|
&engine,
|
||
|
$item,
|
||
|
attributes,
|
||
|
button_pos,
|
||
|
)?;
|
||
|
|
||
|
reference.upgrade().unwrap().add_tooltip("equip", gui);
|
||
|
}
|
||
|
None => {
|
||
|
reference.upgrade().unwrap().remove_tooltip("equip");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
} else {
|
||
|
reference.upgrade().unwrap().remove_tooltip("equip");
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
});
|
||
|
|
||
|
[<$item _button>].set_callback({
|
||
|
let engine = $engine.clone();
|
||
|
let reference = $reference.clone();
|
||
|
|
||
|
move || {
|
||
|
let mut found_item = false;
|
||
|
|
||
|
engine.on_scene_mut(|scene| {
|
||
|
let (resources, entity) = scene.entity_resource($hero)?;
|
||
|
let mut multi_mut = entity.multi_mut();
|
||
|
|
||
|
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||
|
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||
|
|
||
|
if let Some($item) = items.[<$item>]() {
|
||
|
inventory.add_item($item.clone());
|
||
|
found_item = true;
|
||
|
}
|
||
|
|
||
|
if found_item {
|
||
|
items.[<unset_ $item>](&mut multi_mut)?;
|
||
|
|
||
|
let statistics = multi_mut.get::<Statistics>()?;
|
||
|
let attributes = multi_mut.get::<Attributes>()?;
|
||
|
|
||
|
statistics.update(
|
||
|
attributes,
|
||
|
resources.get::<AttributeSettings>(),
|
||
|
(&*items, resources.get::<ItemSettings>())
|
||
|
);
|
||
|
|
||
|
let status = multi_mut.get::<CharacterStatus>()?;
|
||
|
|
||
|
if status.current_health > statistics.health {
|
||
|
status.current_health = statistics.health.clone();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
|
||
|
if found_item {
|
||
|
if let Some(menu) = reference.upgrade() {
|
||
|
let mut tabs = menu.tabs_mut();
|
||
|
let inventory = tabs.inventory::<A>();
|
||
|
|
||
|
inventory.update_page(true)?;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
});
|
||
|
|
||
|
[<$item _button>].set_custom_callback({
|
||
|
let engine = $engine.clone();
|
||
|
let reference = $reference.clone();
|
||
|
|
||
|
move |controller_button| {
|
||
|
Ok(match controller_button {
|
||
|
ControllerButton::Y => {
|
||
|
let mut empty_affixes_found = false;
|
||
|
|
||
|
engine.on_scene_mut(|scene| {
|
||
|
let entity = scene.entity_mut($hero)?;
|
||
|
let items = entity.get_component::<ItemSlotContainer>()?;
|
||
|
|
||
|
if let Some(item) = items.$item().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::Slots(None),
|
||
|
});
|
||
|
|
||
|
empty_affixes_found = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
|
||
|
if empty_affixes_found {
|
||
|
let window = reference.upgrade().unwrap();
|
||
|
let mut tabs = window.tabs_mut();
|
||
|
let inventory = tabs.inventory::<A>();
|
||
|
|
||
|
inventory.switch_to_jewels()?;
|
||
|
}
|
||
|
|
||
|
true
|
||
|
}
|
||
|
|
||
|
_ => false,
|
||
|
})
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
($self:ident, $engine:ident, $reference:ident, $hero:ident, $item:ident, $button:literal, $index:literal) => {
|
||
|
paste::expr! {
|
||
|
let [<$item _button>]: Arc<Button> = $self.snippet.element($button)?;
|
||
|
|
||
|
[<$item _button>].set_select_callback({
|
||
|
let engine = $engine.clone();
|
||
|
let reference = $reference.clone();
|
||
|
let weak_button = Arc::downgrade(&[<$item _button>]);
|
||
|
|
||
|
move |selected| {
|
||
|
if selected {
|
||
|
engine.on_scene(|scene| {
|
||
|
let entity = scene.entity($hero)?;
|
||
|
|
||
|
let attributes = entity.get_component::<Attributes>()?;
|
||
|
let items = entity.get_component::<ItemSlotContainer>()?;
|
||
|
|
||
|
match items.$item($index) {
|
||
|
Some($item) => {
|
||
|
let button_pos =
|
||
|
weak_button.upgrade().unwrap().position_extent();
|
||
|
|
||
|
let gui = Self::create_tooltip(
|
||
|
&engine,
|
||
|
$item,
|
||
|
attributes,
|
||
|
button_pos,
|
||
|
)?;
|
||
|
|
||
|
reference.upgrade().unwrap().add_tooltip("equip", gui);
|
||
|
}
|
||
|
None => {
|
||
|
reference.upgrade().unwrap().remove_tooltip("equip");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
} else {
|
||
|
reference.upgrade().unwrap().remove_tooltip("equip");
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
});
|
||
|
|
||
|
[<$item _button>].set_callback({
|
||
|
let engine = $engine.clone();
|
||
|
let reference = $reference.clone();
|
||
|
|
||
|
move || {
|
||
|
let mut found_item = false;
|
||
|
|
||
|
engine.on_scene_mut(|scene| {
|
||
|
let (resources, entity) = scene.entity_resource($hero)?;
|
||
|
let mut multi_mut = entity.multi_mut();
|
||
|
|
||
|
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||
|
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||
|
|
||
|
if let Some($item) = items.[<$item>]($index) {
|
||
|
inventory.add_item($item.clone());
|
||
|
found_item = true;
|
||
|
}
|
||
|
|
||
|
if found_item {
|
||
|
items.[<unset_ $item>]($index)?;
|
||
|
|
||
|
let statistics = multi_mut.get::<Statistics>()?;
|
||
|
let attributes = multi_mut.get::<Attributes>()?;
|
||
|
|
||
|
statistics.update(
|
||
|
attributes,
|
||
|
resources.get::<AttributeSettings>(),
|
||
|
(&*items, resources.get::<ItemSettings>())
|
||
|
);
|
||
|
|
||
|
let status = multi_mut.get::<CharacterStatus>()?;
|
||
|
|
||
|
if status.current_health > statistics.health {
|
||
|
status.current_health = statistics.health.clone();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
|
||
|
if found_item {
|
||
|
if let Some(menu) = reference.upgrade() {
|
||
|
let mut tabs = menu.tabs_mut();
|
||
|
let inventory = tabs.inventory::<A>();
|
||
|
|
||
|
inventory.update_page(true)?;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
});
|
||
|
|
||
|
[<$item _button>].set_custom_callback({
|
||
|
let engine = $engine.clone();
|
||
|
let reference = $reference.clone();
|
||
|
|
||
|
move |controller_button| {
|
||
|
Ok(match controller_button {
|
||
|
ControllerButton::Y => {
|
||
|
let mut empty_affixes_found = false;
|
||
|
|
||
|
engine.on_scene_mut(|scene| {
|
||
|
let entity = scene.entity_mut($hero)?;
|
||
|
let items = entity.get_component::<ItemSlotContainer>()?;
|
||
|
|
||
|
if let Some(item) = items.$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::Slots(Some($index)),
|
||
|
});
|
||
|
|
||
|
empty_affixes_found = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
})?;
|
||
|
|
||
|
if empty_affixes_found {
|
||
|
let window = reference.upgrade().unwrap();
|
||
|
let mut tabs = window.tabs_mut();
|
||
|
let inventory = tabs.inventory::<A>();
|
||
|
|
||
|
inventory.switch_to_jewels()?;
|
||
|
}
|
||
|
|
||
|
true
|
||
|
}
|
||
|
|
||
|
_ => false,
|
||
|
})
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
#[macro_export]
|
||
|
macro_rules! equip_update {
|
||
|
($gui:ident, $items:ident, $part:ident, $icons:ident) => {{
|
||
|
let button: Arc<Button> = $gui.element(stringify!($part))?;
|
||
|
|
||
|
match $items.$part() {
|
||
|
Some($part) => button.set_icon(&$part.icon)?,
|
||
|
None => button.set_icon(&$icons.$part)?,
|
||
|
}
|
||
|
}};
|
||
|
($gui:ident, $items:ident, $part:ident, $icons:ident, $name:literal) => {{
|
||
|
let button: Arc<Button> = $gui.element($name)?;
|
||
|
|
||
|
match $items.$part() {
|
||
|
Some($part) => button.set_icon(&$part.icon)?,
|
||
|
None => button.set_icon(&$icons.$part)?,
|
||
|
}
|
||
|
}};
|
||
|
($gui:ident, $items:ident, $part:ident, $icons:ident, $name:literal, $index:literal) => {{
|
||
|
let button: Arc<Button> = $gui.element($name)?;
|
||
|
|
||
|
match $items.$part($index) {
|
||
|
Some($part) => button.set_icon(&$part.icon)?,
|
||
|
None => button.set_icon(&$icons.$part)?,
|
||
|
}
|
||
|
}};
|
||
|
}
|
||
|
}
|