415 lines
14 KiB
Rust
415 lines
14 KiB
Rust
use rpg_components::{
|
|
components::{
|
|
attributes::Attributes,
|
|
inventory::{Inventory, Storable},
|
|
item_slots::ItemSlotContainer,
|
|
statistics::Statistics,
|
|
},
|
|
config::{attributes::AttributeSettings, items::ItemSettings},
|
|
items::{Item, ItemAffix, ItemSystem, Jewel},
|
|
};
|
|
|
|
use crate::*;
|
|
|
|
use std::{
|
|
cmp::Reverse,
|
|
sync::{Arc, Weak},
|
|
};
|
|
|
|
use crate::CharacterWindow;
|
|
|
|
use super::super::traits::*;
|
|
|
|
pub enum ReferenceItemSource {
|
|
Inventory(usize),
|
|
Slots(Option<usize>),
|
|
}
|
|
|
|
pub enum ReferenceObject {
|
|
Item {
|
|
item: Item,
|
|
source: ReferenceItemSource,
|
|
},
|
|
Jewel {
|
|
jewel: Jewel,
|
|
index: usize,
|
|
},
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct LowerJewels {
|
|
pub jewels: [Option<(Jewel, usize)>; 3],
|
|
}
|
|
|
|
pub struct JewelRightSide {
|
|
snippet: Arc<GuiSnippet>,
|
|
}
|
|
|
|
impl JewelRightSide {
|
|
pub fn new<A: Ability + 'static>(
|
|
engine: &Arc<Engine>,
|
|
file: &str,
|
|
hero: Entity,
|
|
reference: &Weak<CharacterWindow>,
|
|
) -> Result<Self> {
|
|
let snippet = GuiSnippet::from_str(engine.gui_handler(), file)?;
|
|
|
|
let combine: Arc<Label> = snippet.element("combine")?;
|
|
combine.set_info_icon(&engine.controller_icon(ControllerButton::RightStick)?)?;
|
|
|
|
engine.on_scene_mut(|scene| {
|
|
scene
|
|
.resources
|
|
.insert_if_not_exists::<Option<ReferenceObject>>();
|
|
scene.resources.insert_if_not_exists::<LowerJewels>();
|
|
|
|
Ok(())
|
|
})?;
|
|
|
|
let me = Self { snippet };
|
|
me.setup_select::<A>(engine, hero, reference)?;
|
|
|
|
Ok(me)
|
|
}
|
|
|
|
fn setup_select<A: Ability + 'static>(
|
|
&self,
|
|
engine: &Arc<Engine>,
|
|
hero: Entity,
|
|
reference: &Weak<CharacterWindow>,
|
|
) -> Result<()> {
|
|
let (top, bottom) = self.elements()?;
|
|
|
|
top.set_select_callback({
|
|
let engine = engine.clone();
|
|
let weak_top = Arc::downgrade(&top);
|
|
let reference = reference.clone();
|
|
|
|
move |selected| {
|
|
let menu = reference.upgrade().unwrap();
|
|
|
|
if selected {
|
|
let Some(button) = weak_top.upgrade() else {
|
|
return Ok(());
|
|
};
|
|
|
|
let (x, y, w, _h) = button.position_extent();
|
|
|
|
let scene = engine.scene();
|
|
|
|
let reference_info = scene.resources.get::<Option<ReferenceObject>>();
|
|
|
|
if let Some(reference_info) = reference_info {
|
|
let tooltip = match reference_info {
|
|
ReferenceObject::Item { item, .. } => item.create_tooltip(
|
|
engine.gui_handler(),
|
|
scene.entity(hero)?.get_component::<Attributes>()?,
|
|
(x + w as i32, y),
|
|
)?,
|
|
ReferenceObject::Jewel { jewel, .. } => jewel.create_tooltip(
|
|
engine.gui_handler(),
|
|
scene.resources.get::<ItemSettings>(),
|
|
(x + w as i32, y),
|
|
)?,
|
|
};
|
|
|
|
tooltip.enable()?;
|
|
tooltip.perform_single_check(x, y)?;
|
|
|
|
menu.add_tooltip("upper", tooltip);
|
|
}
|
|
} else {
|
|
menu.remove_tooltip("upper");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
});
|
|
|
|
for (index, lower) in bottom.iter().enumerate() {
|
|
lower.set_select_callback({
|
|
let engine = engine.clone();
|
|
let weak_top = Arc::downgrade(&lower);
|
|
let reference = reference.clone();
|
|
|
|
move |selected| {
|
|
let menu = reference.upgrade().unwrap();
|
|
|
|
if selected {
|
|
let Some(button) = weak_top.upgrade() else {
|
|
return Ok(());
|
|
};
|
|
|
|
let (x, y, w, _h) = button.position_extent();
|
|
|
|
let scene = engine.scene();
|
|
|
|
let lower_info = scene.resources.get::<LowerJewels>();
|
|
|
|
if let Some((lower_jewel, _)) = &lower_info.jewels[index] {
|
|
let tooltip = lower_jewel.create_tooltip(
|
|
engine.gui_handler(),
|
|
scene.resources.get::<ItemSettings>(),
|
|
(x + w as i32, y),
|
|
)?;
|
|
|
|
tooltip.enable()?;
|
|
tooltip.perform_single_check(x, y)?;
|
|
|
|
menu.add_tooltip(format!("lower_{index}",), tooltip);
|
|
}
|
|
} else {
|
|
menu.remove_tooltip(format!("lower_{index}"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
});
|
|
|
|
lower.set_callback({
|
|
let engine = engine.clone();
|
|
let reference = reference.clone();
|
|
|
|
move || {
|
|
let scene = engine.scene_mut();
|
|
|
|
let lower_info = scene.resources.get_mut::<LowerJewels>();
|
|
|
|
if lower_info.jewels[index].is_some() {
|
|
lower_info.jewels[index] = None;
|
|
|
|
if let Some(menu) = reference.upgrade() {
|
|
let mut tabs = menu.tabs_mut();
|
|
let inventory = tabs.inventory::<A>();
|
|
|
|
inventory.update_page(true)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn elements(&self) -> Result<(Arc<Button>, [Arc<Button>; 3])> {
|
|
let reference_element: Arc<Button> = self.snippet.element("reference")?;
|
|
|
|
let first_element: Arc<Button> = self.snippet.element("first")?;
|
|
let second_element: Arc<Button> = self.snippet.element("second")?;
|
|
let third_element: Arc<Button> = self.snippet.element("third")?;
|
|
|
|
Ok((
|
|
reference_element,
|
|
[first_element, second_element, third_element],
|
|
))
|
|
}
|
|
|
|
pub fn clear(engine: &Arc<Engine>) {
|
|
let scene = engine.scene_mut();
|
|
|
|
*scene.resources.get_mut::<Option<ReferenceObject>>() = None;
|
|
scene
|
|
.resources
|
|
.get_mut::<LowerJewels>()
|
|
.jewels
|
|
.iter_mut()
|
|
.for_each(|j| *j = None);
|
|
}
|
|
|
|
pub fn combine<A: Ability + 'static>(engine: &Arc<Engine>, hero: Entity) -> Result<bool> {
|
|
let scene = engine.scene_mut();
|
|
|
|
let (resources, entity) = scene.entity_resource(hero)?;
|
|
let mut resources = resources.multi_mut();
|
|
|
|
let reference_info = resources.get::<Option<ReferenceObject>>();
|
|
|
|
if reference_info.is_none() {
|
|
return Ok(false);
|
|
}
|
|
|
|
let lower_info = resources.get::<LowerJewels>();
|
|
|
|
if let Some(upper_info) = reference_info {
|
|
match upper_info {
|
|
ReferenceObject::Item { item, source } => {
|
|
// check that is there something in lower
|
|
// that can be socketed into the item
|
|
if !lower_info.jewels.iter().any(|jewel| jewel.is_some()) {
|
|
return Ok(false);
|
|
}
|
|
|
|
match source {
|
|
ReferenceItemSource::Inventory(index) => {
|
|
let inventory = entity.get_component_mut::<Inventory<A>>()?;
|
|
|
|
let item = inventory.item_mut_at(*index);
|
|
|
|
match item.affixes.iter_mut().find(|affix| match affix {
|
|
ItemAffix::Socket(opt) => opt.is_none(),
|
|
ItemAffix::Stat(_) => false,
|
|
}) {
|
|
Some(ItemAffix::Socket(socket)) => {
|
|
// we already made sure that lower is not empty -> unwrap is safe
|
|
let (jewel, index) = lower_info.jewels[lower_info
|
|
.jewels
|
|
.iter()
|
|
.position(|jewel| jewel.is_some())
|
|
.unwrap()]
|
|
.take()
|
|
.unwrap();
|
|
|
|
*socket = Some(jewel);
|
|
inventory.remove_jewel(index);
|
|
}
|
|
_ => {
|
|
return Ok(false);
|
|
}
|
|
}
|
|
}
|
|
ReferenceItemSource::Slots(opt_index) => {
|
|
let attribute_settings = resources.get::<AttributeSettings>();
|
|
let item_settings = resources.get::<ItemSettings>();
|
|
|
|
let mut multi_mut = entity.multi_mut();
|
|
|
|
let inventory = multi_mut.get::<Inventory<A>>()?;
|
|
let item_slots = multi_mut.get::<ItemSlotContainer>()?;
|
|
|
|
let slot = item.slot;
|
|
let item = item_slots.item_mut(slot, *opt_index).as_mut().unwrap();
|
|
|
|
match item.affixes.iter_mut().find(|affix| match affix {
|
|
ItemAffix::Socket(opt) => opt.is_none(),
|
|
ItemAffix::Stat(_) => false,
|
|
}) {
|
|
Some(ItemAffix::Socket(socket)) => {
|
|
// we already made sure that lower is not empty -> unwrap is safe
|
|
let (jewel, index) = lower_info.jewels[lower_info
|
|
.jewels
|
|
.iter()
|
|
.position(|jewel| jewel.is_some())
|
|
.unwrap()]
|
|
.take()
|
|
.unwrap();
|
|
|
|
*socket = Some(jewel);
|
|
inventory.remove_jewel(index);
|
|
}
|
|
_ => {
|
|
return Ok(false);
|
|
}
|
|
}
|
|
|
|
let statistics = multi_mut.get::<Statistics>()?;
|
|
let attributes = multi_mut.get::<Attributes>()?;
|
|
|
|
statistics.update(
|
|
attributes,
|
|
attribute_settings,
|
|
(&*item_slots, &*item_settings),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
ReferenceObject::Jewel { jewel, index } => {
|
|
// check there are 3 jewels in lower
|
|
// and that all jewels match rarity and level
|
|
if !lower_info.jewels.iter().all(|j| match j {
|
|
Some((lower_jewel, _)) => {
|
|
jewel.rarity == lower_jewel.rarity && jewel.level == lower_jewel.level
|
|
}
|
|
None => false,
|
|
}) {
|
|
return Ok(false);
|
|
}
|
|
|
|
let item_settings = resources.get::<ItemSettings>();
|
|
let item_system = resources.get::<ItemSystem<A>>();
|
|
|
|
let inventory = entity.get_component_mut::<Inventory<A>>()?;
|
|
|
|
let upper_jewel = inventory.jewel_mut_at(*index);
|
|
|
|
if upper_jewel.level >= 4 {
|
|
return Ok(false);
|
|
}
|
|
|
|
upper_jewel.level += 1;
|
|
upper_jewel.update_stat(item_settings);
|
|
upper_jewel.icon = Some(item_system.jewel_icon(
|
|
upper_jewel.rarity,
|
|
upper_jewel.level,
|
|
upper_jewel.attribute,
|
|
));
|
|
|
|
let mut jewels: Vec<(Jewel, usize)> = lower_info
|
|
.jewels
|
|
.iter_mut()
|
|
.map(|j| j.take().unwrap())
|
|
.collect();
|
|
|
|
jewels.sort_by_key(|(_, index)| Reverse(*index));
|
|
jewels.iter().for_each(|(jewel, i)| {
|
|
debug_assert_eq!(*jewel, inventory.remove_jewel(*i))
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(true)
|
|
}
|
|
}
|
|
|
|
impl RightSide for JewelRightSide {
|
|
fn refresh(&mut self, engine: &Engine, _hero: Entity) -> Result<()> {
|
|
engine.on_scene(|scene| {
|
|
let (reference, lower) = self.elements()?;
|
|
|
|
let reference_info = scene.resources.get::<Option<ReferenceObject>>();
|
|
let lower_info = scene.resources.get::<LowerJewels>();
|
|
|
|
match reference_info.as_ref() {
|
|
Some(reference_info) => {
|
|
let icon = match reference_info {
|
|
ReferenceObject::Item { item, .. } => item.icon(),
|
|
ReferenceObject::Jewel { jewel, .. } => jewel.icon(),
|
|
};
|
|
|
|
reference.set_icon(&icon)?;
|
|
}
|
|
None => reference.clear_icon()?,
|
|
}
|
|
|
|
for (jewel, icon) in lower_info.jewels.iter().zip(lower.iter()) {
|
|
match jewel.as_ref() {
|
|
Some((jewel, _)) => icon.set_icon(&jewel.icon())?,
|
|
None => icon.clear_icon()?,
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
fn disable(&mut self, engine: &Engine, _hero: Entity) -> Result<()> {
|
|
let scene = engine.scene_mut();
|
|
|
|
*scene.resources.get_mut::<Option<ReferenceObject>>() = None;
|
|
scene
|
|
.resources
|
|
.get_mut::<LowerJewels>()
|
|
.jewels
|
|
.iter_mut()
|
|
.for_each(|j| *j = None);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn base(&self) -> &Arc<GuiSnippet> {
|
|
&self.snippet
|
|
}
|
|
}
|