Start moving character menu into crate
This commit is contained in:
parent
eb543bab61
commit
1449a73d2c
24 changed files with 3800 additions and 41 deletions
|
@ -19,7 +19,9 @@ members = [
|
|||
"scene_update_macros",
|
||||
"transaction_derive",
|
||||
"map",
|
||||
"rpg_components", "entity_manager",
|
||||
"rpg_components",
|
||||
"entity_manager",
|
||||
"character_window",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
|
|
12
character_window/Cargo.toml
Normal file
12
character_window/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "character_window"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
destructure_traitobject = { workspace = true }
|
||||
|
||||
engine = { path = "../engine" }
|
||||
rpg_components = { path = "../rpg_components" }
|
29
character_window/resources/menu.xml
Normal file
29
character_window/resources/menu.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?xml-model href="../../../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="10">
|
||||
<!-- header -->
|
||||
<grid x_dim="7" y_dim="1" x_offset="-420" y_offset="-310" width="840" height="50"
|
||||
vert_align="middle" hori_align="middle" margin="10" padding="10"
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'
|
||||
background='{"background_color":"#9c610a","border_color":"#543919","border_thickness":{"Pixel":5}}'>
|
||||
|
||||
<icon id="left_info" x_slot="1" y_slot="0"></icon>
|
||||
|
||||
<button id="open_statistics" x_slot="2" y_slot="0" text_ratio="0.7" text_color="black" select_mode="none" isolate="true">Character</button>
|
||||
<button id="open_inventory" x_slot="3" y_slot="0" text_ratio="0.7" text_color="black" select_mode="none" isolate="true">Inventory</button>
|
||||
<button id="open_abilities" x_slot="4" y_slot="0" text_ratio="0.7" text_color="black" select_mode="none" isolate="true">Abilities</button>
|
||||
|
||||
<icon id="right_info" x_slot="5" y_slot="0"></icon>
|
||||
|
||||
<grid x_slot="6" y_slot="0" x_dim="3" y_dim="1" padding="0" margin="0">
|
||||
<button id="close" x_slot="2" y_slot="0" text_ratio="0.6" text_color="black" isolate="true"
|
||||
normal='{"background_color":"#a00000","border_color":"#000000","border_thickness":{"Pixel":2}}'
|
||||
selected='{"background_color":"#df0707","border_color":"#000000","border_thickness":{"Pixel":2}}'
|
||||
select="true" select_mode="none">X</button>
|
||||
</grid>
|
||||
</grid>
|
||||
|
||||
<grid id="tab_content" x_dim="1" y_dim="1" x_offset="-400" y_offset="-260" width="800"
|
||||
height="500" vert_align="middle" hori_align="middle" margin="10" padding="10"
|
||||
background='{"background_color":"#B26F0C","border_color":"#543919","border_thickness":{"Pixel":5}}'> </grid>
|
||||
</root>
|
76
character_window/resources/statistics.xml
Normal file
76
character_window/resources/statistics.xml
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml-model href="../../../../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid id="statistic_tab" x_dim="2" y_dim="1" padding="10">
|
||||
<!-- stats -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="2" y_dim="1" margin="0" padding="0"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<grid x_slot="0" y_slot="0" x_dim="1" y_dim="17" margin="2" padding="2">
|
||||
<label x_slot="0" y_slot="0" text_ratio="0.9">Air Resistance</label>
|
||||
<label x_slot="0" y_slot="1" text_ratio="0.9">Fire Resistance</label>
|
||||
<label x_slot="0" y_slot="2" text_ratio="0.9">Water Resistance</label>
|
||||
<label x_slot="0" y_slot="3" text_ratio="0.9">Armor</label>
|
||||
|
||||
<label x_slot="0" y_slot="5" text_ratio="0.9">Air Damage</label>
|
||||
<label x_slot="0" y_slot="6" text_ratio="0.9">Fire Damage</label>
|
||||
<label x_slot="0" y_slot="7" text_ratio="0.9">Water Damage</label>
|
||||
<label x_slot="0" y_slot="8" text_ratio="0.9">Physical Damage</label>
|
||||
|
||||
<label x_slot="0" y_slot="10" text_ratio="0.9">Crit Chance</label>
|
||||
<label x_slot="0" y_slot="11" text_ratio="0.9">Crit Damage</label>
|
||||
|
||||
<label x_slot="0" y_slot="13" text_ratio="0.9">Health</label>
|
||||
<label x_slot="0" y_slot="14" text_ratio="0.9">Mana</label>
|
||||
<label x_slot="0" y_slot="15" text_ratio="0.9">Health Regen</label>
|
||||
<label x_slot="0" y_slot="16" text_ratio="0.9">Mana Regen</label>
|
||||
</grid>
|
||||
<grid x_slot="1" y_slot="0" x_dim="1" y_dim="17" background="#aaaaaa" margin="2" padding="2">
|
||||
<label x_slot="0" y_slot="0" text_ratio="0.9" id="air_def_info">0</label>
|
||||
<label x_slot="0" y_slot="1" text_ratio="0.9" id="fire_def_info">0</label>
|
||||
<label x_slot="0" y_slot="2" text_ratio="0.9" id="water_def_info">0</label>
|
||||
<label x_slot="0" y_slot="3" text_ratio="0.9" id="armor_info">0</label>
|
||||
|
||||
<label x_slot="0" y_slot="5" text_ratio="0.9" id="air_dmg_info">0</label>
|
||||
<label x_slot="0" y_slot="6" text_ratio="0.9" id="fire_dmg_info">0</label>
|
||||
<label x_slot="0" y_slot="7" text_ratio="0.9" id="water_dmg_info"> 0</label>
|
||||
<label x_slot="0" y_slot="8" text_ratio="0.9" id="phys_dmg_info">0</label>
|
||||
|
||||
<label x_slot="0" y_slot="10" text_ratio="0.9" id="crit_chance_info">0</label>
|
||||
<label x_slot="0" y_slot="11" text_ratio="0.9" id="crit_dmg_info">0</label>
|
||||
|
||||
<label x_slot="0" y_slot="13" text_ratio="0.9" id="health_info">0</label>
|
||||
<label x_slot="0" y_slot="14" text_ratio="0.9" id="mana_info">0</label>
|
||||
<label x_slot="0" y_slot="15" text_ratio="0.9" id="health_regen_info">0</label>
|
||||
<label x_slot="0" y_slot="16" text_ratio="0.9" id="mana_regen_info">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="1" y_slot="0" x_dim="1" y_dim="17" margin="0" padding="0"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'>
|
||||
|
||||
<label id="character_name" x_slot="0" y_slot="0" text_ratio="0.8">Name</label>
|
||||
|
||||
<grid x_slot="0" y_slot="1" x_dim="8" y_dim="1">
|
||||
<label id="level" x_slot="0" y_slot="0" text_ratio="0.8">0</label>
|
||||
<progressbar id="level_progress" x_slot="1" y_slot="0" x_size="7" background="#A69614" foreground="#E9D429">Test</progressbar>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="1" y_dim="4" y_size="4">
|
||||
<label id="attributes" x_slot="0" y_slot="0" text_color="black">Attributes</label>
|
||||
|
||||
<grid x_slot="0" y_slot="1" x_dim="4" y_dim="3" y_size="3">
|
||||
<label x_slot="0" y_slot="0" x_size="3" text_color="#C23519">Strength</label>
|
||||
<button id="strength_field" x_slot="3" y_slot="0" text_color="black" select="true">0</button>
|
||||
|
||||
<label x_slot="0" y_slot="1" x_size="3" text_ratio="0.4" text_color="#65D01E">Agility</label>
|
||||
<button id="agility_field" x_slot="3" y_slot="1" text_color="black">0</button>
|
||||
|
||||
<label x_slot="0" y_slot="2" x_size="3" text_ratio="0.4" text_color="#1D63B3">Intelligence</label>
|
||||
<button id="intelligence_field" x_slot="3" y_slot="2" text_color="black">0</button>
|
||||
</grid>
|
||||
</grid>
|
||||
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
265
character_window/src/abilities/ability_right_side.rs
Normal file
265
character_window/src/abilities/ability_right_side.rs
Normal file
|
@ -0,0 +1,265 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use rpg_components::components::ability_slots::AbilitySlots;
|
||||
use rpg_components::components::crafting_materials::CraftingMaterials;
|
||||
use rpg_components::components::inventory::Storable;
|
||||
use rpg_components::components::statistics::Statistics;
|
||||
use rpg_components::items::Rarities;
|
||||
|
||||
use crate::*;
|
||||
|
||||
use crate::{traits::RightSide, CharacterWindow};
|
||||
|
||||
pub struct AbilityPageRightSide {
|
||||
snippet: Arc<GuiSnippet>,
|
||||
|
||||
ability_index: usize,
|
||||
}
|
||||
|
||||
impl AbilityPageRightSide {
|
||||
const ABILITY_BUTTON_NAMES: [&'static str; 4] = [
|
||||
"first_ability",
|
||||
"second_ability",
|
||||
"third_ability",
|
||||
"fourth_ability",
|
||||
];
|
||||
|
||||
pub fn new(
|
||||
game_handle: &GameHandle,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
hero: Entity,
|
||||
) -> Result<Self> {
|
||||
let snippet =
|
||||
game_handle.gui_snippet("gui/xml/ingame/character_menu/abilities/right_side.xml")?;
|
||||
|
||||
let game = game_handle.upgrade();
|
||||
let color_settings = &game.item_settings.rarity_color_settings;
|
||||
|
||||
Self::rarity_icon_background(&snippet, "common", color_settings.common)?;
|
||||
Self::rarity_icon_background(&snippet, "uncommon", color_settings.uncommon)?;
|
||||
Self::rarity_icon_background(&snippet, "magical", color_settings.magical)?;
|
||||
Self::rarity_icon_background(&snippet, "rare", color_settings.rare)?;
|
||||
Self::rarity_icon_background(&snippet, "epic", color_settings.epic)?;
|
||||
Self::rarity_icon_background(&snippet, "legendary", color_settings.legendary)?;
|
||||
|
||||
for (index, name) in Self::ABILITY_BUTTON_NAMES.iter().enumerate() {
|
||||
let button: Arc<Button> = snippet.element(name)?;
|
||||
|
||||
button.set_info_icon(&game_handle.controller_icon(ControllerButton::RightStick)?)?;
|
||||
|
||||
button.set_callback({
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
abilities.right_side.ability_index = index;
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
|
||||
move |selected| {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
if selected {
|
||||
game_handle.upgrade().engine().on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let abilities = entity.get_component::<AbilitySlots>()?;
|
||||
|
||||
if let Some(book) = abilities.book(index) {
|
||||
let button = weak_button.upgrade().unwrap();
|
||||
let button_pos = button.position_extent();
|
||||
|
||||
let target_x = button_pos.0 + button_pos.2 as i32;
|
||||
let target_y = button_pos.1;
|
||||
|
||||
let statistics = entity.get_component::<Statistics>()?;
|
||||
|
||||
let gui = book.create_tooltip(
|
||||
&game_handle,
|
||||
statistics,
|
||||
(target_x, target_y),
|
||||
)?;
|
||||
gui.enable()?;
|
||||
gui.perform_single_check(button_pos.0, button_pos.1)?;
|
||||
|
||||
menu.add_tooltip("active_ability", gui);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
menu.remove_tooltip("active_ability");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_custom_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |button| match button {
|
||||
ControllerButton::Y => {
|
||||
game_handle.upgrade().engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let abilities = multi_mut.get::<AbilitySlots>()?;
|
||||
|
||||
if let Some(ability) = abilities.book_mut(index) {
|
||||
let materials = multi_mut.get::<CraftingMaterials>()?;
|
||||
|
||||
ability.upgrade(materials);
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
menu.tabs_mut()
|
||||
.abilities()
|
||||
.right_side
|
||||
.refresh(&game_handle, hero)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
_ => Ok(false),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
snippet,
|
||||
ability_index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn selected_ability(&self) -> usize {
|
||||
self.ability_index
|
||||
}
|
||||
|
||||
fn rarity_icon_background(gui: &GuiSnippet, element: &str, color: Color) -> Result<()> {
|
||||
let icon: Arc<Icon> = gui.element(element)?;
|
||||
|
||||
icon.set_background(FillTypeInfo::Element(
|
||||
ElementDescriptor::new(color, Color::Black, 2),
|
||||
DisplayableFillType::Square,
|
||||
))
|
||||
}
|
||||
|
||||
fn update_crafting_count(&self, element: &str, value: u32) -> Result<()> {
|
||||
let icon: Arc<Icon> = self.snippet.element(element)?;
|
||||
icon.set_text(value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_ability_icon(&self, element: &str, image: Option<Arc<Image>>) -> Result<()> {
|
||||
let button: Arc<Button> = self.snippet.element(element)?;
|
||||
|
||||
match image {
|
||||
Some(image) => {
|
||||
button.set_icon(&image)?;
|
||||
}
|
||||
None => {
|
||||
button.clear_icon()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_active_ability(
|
||||
&self,
|
||||
game_handle: &GameHandle,
|
||||
abilities: &AbilitySlots,
|
||||
) -> Result<()> {
|
||||
let grid: Arc<Grid> = self.snippet.element("ability_content")?;
|
||||
let (_, rows) = grid.dimensions();
|
||||
|
||||
for y in 0..rows {
|
||||
grid.detach(0, y)?;
|
||||
}
|
||||
|
||||
if let Some(ability) = abilities.book(self.ability_index) {
|
||||
for (index, addon) in ability.addons().iter().enumerate() {
|
||||
match addon.as_ref() {
|
||||
Some(addon) => {
|
||||
let addon_type_snippet = game_handle.gui_snippet(
|
||||
"gui/xml/ingame/character_menu/abilities/addon_type_snippet.xml",
|
||||
)?;
|
||||
|
||||
let addon_icon: Arc<Icon> = addon_type_snippet.element("addon_icon")?;
|
||||
let addon_type: Arc<Label> = addon_type_snippet.element("addon_type")?;
|
||||
let addon_value: Arc<Label> = addon_type_snippet.element("addon_value")?;
|
||||
|
||||
addon_icon.set_icon(&addon.icon())?;
|
||||
addon_type.set_text(&format!("{}", addon.addon_type()))?;
|
||||
addon_value.set_text(&addon.addon_type().val_as_str())?;
|
||||
|
||||
grid.attach(addon_type_snippet, 0, index, 1, 1)?;
|
||||
}
|
||||
None => {
|
||||
let empty_addon = game_handle.gui_snippet(
|
||||
"gui/xml/ingame/character_menu/abilities/empty_addon_snippet.xml",
|
||||
)?;
|
||||
|
||||
grid.attach(empty_addon, 0, index, 1, 1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn next_ability(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
self.ability_index = (self.ability_index + 1) % 4;
|
||||
self.refresh(game_handle, hero)
|
||||
}
|
||||
}
|
||||
|
||||
impl RightSide for AbilityPageRightSide {
|
||||
fn refresh(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
|
||||
let crafting = entity.get_component::<CraftingMaterials>()?;
|
||||
|
||||
self.update_crafting_count("common", crafting.count(Rarities::Common))?;
|
||||
self.update_crafting_count("uncommon", crafting.count(Rarities::Uncommon))?;
|
||||
self.update_crafting_count("magical", crafting.count(Rarities::Magical))?;
|
||||
self.update_crafting_count("rare", crafting.count(Rarities::Rare))?;
|
||||
self.update_crafting_count("epic", crafting.count(Rarities::Epic))?;
|
||||
self.update_crafting_count("legendary", crafting.count(Rarities::Legendary))?;
|
||||
|
||||
let abilities = entity.get_component::<AbilitySlots>()?;
|
||||
|
||||
for (index, name) in Self::ABILITY_BUTTON_NAMES.iter().enumerate() {
|
||||
self.update_ability_icon(name, abilities.book(index).map(|book| book.icon()))?;
|
||||
}
|
||||
|
||||
self.update_active_ability(game_handle, abilities)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.snippet
|
||||
}
|
||||
}
|
311
character_window/src/abilities/content.rs
Normal file
311
character_window/src/abilities/content.rs
Normal file
|
@ -0,0 +1,311 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use rpg_components::components::ability_slots::AbilitySlots;
|
||||
use rpg_components::components::inventory::{Inventory, Storable};
|
||||
use rpg_components::components::statistics::Statistics;
|
||||
use rpg_components::items::ability_addon::AbilityAddon;
|
||||
use rpg_components::items::ability_book::AbilityBook;
|
||||
|
||||
use crate::*;
|
||||
|
||||
use crate::{
|
||||
content::{Content, ContentUpdate},
|
||||
CharacterWindow,
|
||||
};
|
||||
|
||||
use super::AbilityPage;
|
||||
|
||||
impl Content<AbilityAddon> {
|
||||
fn show_addon_tooltip(
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
addon_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let addon = inventory.addon_at(addon_index);
|
||||
let gui = addon.create_tooltip(game_handle, (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
gui.perform_single_check(x, y)?;
|
||||
|
||||
let window = reference.upgrade().unwrap();
|
||||
window.add_tooltip(format!("addon_{addon_index}"), gui);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_addon(
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
addon_index: usize,
|
||||
ability_page: &AbilityPage,
|
||||
) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
let abilities = multi_mut.get::<AbilitySlots>()?;
|
||||
|
||||
if let Some(book) = abilities.book_mut(ability_page.right_side.selected_ability()) {
|
||||
if book.has_free_addon_slots() {
|
||||
book.addons_mut()
|
||||
.insert_addon(inventory.remove_addon(addon_index));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentUpdate for Content<AbilityAddon> {
|
||||
fn update(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(game_handle, |button, t, index| {
|
||||
button.set_icon(&t.icon())?;
|
||||
|
||||
button.set_custom_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
if let ControllerButton::X = controller_button {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
CharacterWindow::salvage_from_inventory(&game, hero, |inventory| {
|
||||
inventory.remove_addon(index)
|
||||
})?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
Self::insert_addon(&game_handle, hero, index, abilities)?;
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_addon_tooltip(
|
||||
&game_handle,
|
||||
hero,
|
||||
index,
|
||||
&reference,
|
||||
button_pos,
|
||||
)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("addon_{index}"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
||||
|
||||
impl Content<AbilityBook> {
|
||||
fn equip_book(
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
book_index: usize,
|
||||
ability_page: &AbilityPage,
|
||||
) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
let abilitiy_slots = multi_mut.get::<AbilitySlots>()?;
|
||||
|
||||
if let Some(old_book) = abilitiy_slots.insert_book(
|
||||
inventory.remove_book(book_index),
|
||||
ability_page.right_side.selected_ability(),
|
||||
) {
|
||||
inventory.insert_book(old_book, book_index);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn show_book_tooltip(
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
book_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
let statistics = entity.get_component::<Statistics>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let book = inventory.book_at(book_index);
|
||||
let gui = book.create_tooltip(game_handle, statistics, (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
|
||||
let window = reference.upgrade().unwrap();
|
||||
let abilities = entity.get_component::<AbilitySlots>()?;
|
||||
|
||||
match abilities.book(window.tabs().abilities().right_side.selected_ability()) {
|
||||
Some(selected_book) => {
|
||||
let button_pos = gui.position_extent();
|
||||
|
||||
let target_x = button_pos.0 + button_pos.2 as i32 + 2;
|
||||
let target_y = button_pos.1;
|
||||
|
||||
let compare_gui = selected_book.create_tooltip(
|
||||
game_handle,
|
||||
statistics,
|
||||
(target_x, target_y),
|
||||
)?;
|
||||
compare_gui.enable()?;
|
||||
gui.perform_double_check(&compare_gui, x, 2)?;
|
||||
|
||||
window.add_tooltip("active_book", compare_gui);
|
||||
}
|
||||
None => {
|
||||
gui.perform_single_check(x, y)?;
|
||||
}
|
||||
}
|
||||
|
||||
window.add_tooltip(format!("book_{book_index}"), gui);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentUpdate for Content<AbilityBook> {
|
||||
fn update(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(game_handle, |button, t, index| {
|
||||
button.set_icon(&t.icon())?;
|
||||
|
||||
button.set_custom_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
if let ControllerButton::X = controller_button {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
CharacterWindow::salvage_from_inventory(&game, hero, |inventory| {
|
||||
inventory.remove_book(index)
|
||||
})?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
Self::equip_book(&game_handle, hero, index, abilities)?;
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_book_tooltip(&game_handle, hero, index, &reference, button_pos)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("book_{index}"));
|
||||
window.remove_tooltip("active_book");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
281
character_window/src/abilities/mod.rs
Normal file
281
character_window/src/abilities/mod.rs
Normal file
|
@ -0,0 +1,281 @@
|
|||
mod ability_right_side;
|
||||
mod content;
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Result;
|
||||
use rpg_components::components::inventory::Inventory;
|
||||
|
||||
use self::ability_right_side::AbilityPageRightSide;
|
||||
|
||||
use super::{
|
||||
content::Content,
|
||||
page_content::{EmptyRightSide, PageContent},
|
||||
traits::{PageContentWrapper, RightSide},
|
||||
CharacterWindow, Page,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
pub struct AbilityPage {
|
||||
close: Weak<Button>,
|
||||
|
||||
game_handle: GameHandle,
|
||||
hero: Entity,
|
||||
|
||||
grid: Arc<Grid>,
|
||||
|
||||
tooltip: Arc<Grid>,
|
||||
content: Arc<Grid>,
|
||||
|
||||
modes: [Box<dyn PageContentWrapper>; 2],
|
||||
|
||||
current_mode: usize,
|
||||
|
||||
right_side: AbilityPageRightSide,
|
||||
}
|
||||
|
||||
impl AbilityPage {
|
||||
pub fn new(
|
||||
game: GameHandle,
|
||||
hero: Entity,
|
||||
reference: Weak<CharacterWindow>,
|
||||
close: &Arc<Button>,
|
||||
) -> Result<Self> {
|
||||
let base = "gui/xml/ingame/character_menu/abilities/".to_string();
|
||||
|
||||
let grid = Grid::new(game.upgrade().engine().gui_handler().clone(), 2, 1, false)?;
|
||||
|
||||
let left_base = game.gui_snippet((base.clone() + "left_side.xml").as_str())?;
|
||||
grid.attach(left_base.clone(), 0, 0, 1, 1)?;
|
||||
|
||||
Self::setup_content_switch(&left_base, reference.clone())?;
|
||||
|
||||
let tooltip = left_base.element("tooltip")?;
|
||||
let content = left_base.element("content")?;
|
||||
|
||||
// abilities
|
||||
let ability_mode = PageContent::new(
|
||||
Content::new(&game, reference.clone(), {
|
||||
let game_handle = game.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
let mut data = Vec::new();
|
||||
|
||||
game.engine().on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory>()?;
|
||||
|
||||
data = inventory.iter_books().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = game.gui_snippet((base.clone() + "ability_tooltip.xml").as_str())?;
|
||||
|
||||
let equip: Arc<Label> = ui.element("equip")?;
|
||||
equip.set_info_icon(&game.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let upgrade: Arc<Label> = ui.element("upgrade")?;
|
||||
upgrade.set_info_icon(&game.controller_icon(ControllerButton::Y)?)?;
|
||||
|
||||
let salvage: Arc<Label> = ui.element("salvage")?;
|
||||
salvage.set_info_icon(&game.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&game.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
EmptyRightSide,
|
||||
);
|
||||
|
||||
// addons
|
||||
let addons_mode = PageContent::new(
|
||||
Content::new(&game, reference.clone(), {
|
||||
let game_handle = game.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
let mut data = Vec::new();
|
||||
|
||||
game.engine().on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory>()?;
|
||||
|
||||
data = inventory.iter_addons().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = game.gui_snippet((base.clone() + "addon_tooltip.xml").as_str())?;
|
||||
|
||||
let equip: Arc<Label> = ui.element("socket")?;
|
||||
equip.set_info_icon(&game.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let salvage: Arc<Label> = ui.element("salvage")?;
|
||||
salvage.set_info_icon(&game.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&game.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
EmptyRightSide,
|
||||
);
|
||||
|
||||
let right_side = AbilityPageRightSide::new(&game, &reference, hero)?;
|
||||
|
||||
Ok(Self {
|
||||
close: Arc::downgrade(close),
|
||||
|
||||
game_handle: game,
|
||||
hero,
|
||||
|
||||
grid,
|
||||
|
||||
tooltip,
|
||||
content,
|
||||
|
||||
modes: [Box::new(ability_mode), Box::new(addons_mode)],
|
||||
|
||||
current_mode: 0,
|
||||
|
||||
right_side,
|
||||
})
|
||||
}
|
||||
|
||||
fn update_page(&mut self) -> Result<()> {
|
||||
match self.current_mode {
|
||||
0 => println!("update ability view"),
|
||||
1 => println!("update addon view"),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
mode.content_mut().update(&self.game_handle, self.hero)?;
|
||||
|
||||
self.tooltip.attach(mode.tooltip().clone(), 0, 0, 1, 1)?;
|
||||
self.content
|
||||
.attach(mode.content_mut().base().clone(), 0, 0, 1, 1)?;
|
||||
|
||||
self.right_side.refresh(&self.game_handle, self.hero)?;
|
||||
self.grid
|
||||
.attach(self.right_side.base().clone(), 1, 0, 1, 1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_content_switch(
|
||||
left_base: &GuiSnippet,
|
||||
reference: Weak<CharacterWindow>,
|
||||
) -> Result<()> {
|
||||
let switch = {
|
||||
let reference = reference.clone();
|
||||
|
||||
move |index| {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let me = tabs.abilities();
|
||||
|
||||
if me.current_mode != index {
|
||||
me.current_mode = index;
|
||||
me.update_page()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
let switch_to_abilities = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(0)
|
||||
});
|
||||
|
||||
let switch_to_addons = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(1)
|
||||
});
|
||||
|
||||
left_base.set_click_callbacks(vec![
|
||||
("abilities", switch_to_abilities),
|
||||
("addons", switch_to_addons),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Page for AbilityPage {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>> {
|
||||
println!("enable AbilityPage");
|
||||
|
||||
for mode in self.modes.iter_mut() {
|
||||
mode.content_mut().refresh()?;
|
||||
}
|
||||
|
||||
self.update_page()?;
|
||||
|
||||
Ok(self.grid.clone())
|
||||
}
|
||||
|
||||
fn disable(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
let mode = &self.modes[self.current_mode];
|
||||
|
||||
mode.content().select()?;
|
||||
|
||||
if mode.content().is_empty() {
|
||||
if let Some(close) = self.close.upgrade() {
|
||||
close.select()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) -> Result<()> {
|
||||
self.modes[self.current_mode]
|
||||
.content_mut()
|
||||
.next_tab(&self.game_handle, self.hero)
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) -> Result<()> {
|
||||
self.modes[self.current_mode]
|
||||
.content_mut()
|
||||
.previous_tab(&self.game_handle, self.hero)
|
||||
}
|
||||
|
||||
fn event(&mut self, button: ControllerButton) -> Result<bool> {
|
||||
Ok(match button {
|
||||
ControllerButton::LeftStick => {
|
||||
self.current_mode = (self.current_mode + 1) % self.modes.len();
|
||||
self.update_page()?;
|
||||
self.select()?;
|
||||
true
|
||||
}
|
||||
|
||||
ControllerButton::RightStick => {
|
||||
self.right_side.next_ability(&self.game_handle, self.hero)?;
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
257
character_window/src/character/mod.rs
Normal file
257
character_window/src/character/mod.rs
Normal file
|
@ -0,0 +1,257 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Result;
|
||||
use rpg_components::components::{
|
||||
attributes::{Agility, Attributes, Intelligence, Strength},
|
||||
item_slots::ItemSlotContainer,
|
||||
level::Level,
|
||||
statistics::Statistics,
|
||||
};
|
||||
|
||||
use super::{CharacterWindow, Page};
|
||||
use crate::*;
|
||||
|
||||
pub struct CharacterPage {
|
||||
engine: Arc<Engine>,
|
||||
hero: Entity,
|
||||
|
||||
snippet: Arc<GuiSnippet>,
|
||||
grid: Arc<Grid>,
|
||||
}
|
||||
|
||||
impl CharacterPage {
|
||||
pub fn new(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
hero_name: &str,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> Result<Self> {
|
||||
let snippet = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/statistics.xml"),
|
||||
)?;
|
||||
|
||||
let grid: Arc<Grid> = snippet.element("statistic_tab")?;
|
||||
|
||||
let name: Arc<Label> = snippet.element("character_name")?;
|
||||
name.set_text(hero_name)?;
|
||||
|
||||
let strength: Arc<Button> = snippet.element("strength_field")?;
|
||||
strength.set_callback({
|
||||
let update_stats = Self::create_update_stats(hero, game_handle, reference);
|
||||
|
||||
move || {
|
||||
update_stats(|attributes| {
|
||||
attributes.add_strength(Strength::from(1));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let agility: Arc<Button> = snippet.element("agility_field")?;
|
||||
agility.set_callback({
|
||||
let update_stats = Self::create_update_stats(hero, game_handle, reference);
|
||||
|
||||
move || {
|
||||
update_stats(|attributes| {
|
||||
attributes.add_agility(Agility::from(1));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let intelligence: Arc<Button> = snippet.element("intelligence_field")?;
|
||||
intelligence.set_callback({
|
||||
let update_stats = Self::create_update_stats(hero, game_handle, reference);
|
||||
|
||||
move || {
|
||||
update_stats(|attributes| {
|
||||
attributes.add_intelligence(Intelligence::from(1));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
engine,
|
||||
hero,
|
||||
|
||||
snippet,
|
||||
grid,
|
||||
})
|
||||
}
|
||||
|
||||
fn refresh(&self) -> Result<()> {
|
||||
self.update_stats()?;
|
||||
self.update_attributes()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_stats(&self) -> Result<()> {
|
||||
let air_def: Arc<Label> = self.snippet.element("air_def_info")?;
|
||||
let fire_def: Arc<Label> = self.snippet.element("fire_def_info")?;
|
||||
let water_def: Arc<Label> = self.snippet.element("water_def_info")?;
|
||||
let armor: Arc<Label> = self.snippet.element("armor_info")?;
|
||||
|
||||
let air_dmg: Arc<Label> = self.snippet.element("air_dmg_info")?;
|
||||
let fire_dmg: Arc<Label> = self.snippet.element("fire_dmg_info")?;
|
||||
let water_dmg: Arc<Label> = self.snippet.element("water_dmg_info")?;
|
||||
let phys_dmg: Arc<Label> = self.snippet.element("phys_dmg_info")?;
|
||||
|
||||
let crit_chance: Arc<Label> = self.snippet.element("crit_chance_info")?;
|
||||
let crit_dmg: Arc<Label> = self.snippet.element("crit_dmg_info")?;
|
||||
|
||||
let health: Arc<Label> = self.snippet.element("health_info")?;
|
||||
let health_regen: Arc<Label> = self.snippet.element("health_regen_info")?;
|
||||
let mana: Arc<Label> = self.snippet.element("mana_info")?;
|
||||
let mana_regen: Arc<Label> = self.snippet.element("mana_regen_info")?;
|
||||
|
||||
self.engine.on_scene(|scene| {
|
||||
let entity = scene.entity(self.hero)?;
|
||||
let statistics = entity.get_component::<Statistics>()?;
|
||||
|
||||
air_def.set_text(&format!("{}", statistics.air_resistance.raw()))?;
|
||||
fire_def.set_text(&format!("{}", statistics.fire_resistance.raw()))?;
|
||||
water_def.set_text(&format!("{}", statistics.water_resistance.raw()))?;
|
||||
armor.set_text(&format!("{}", statistics.armor.raw()))?;
|
||||
|
||||
air_dmg.set_text(&format!("{}", statistics.air_damage.raw()))?;
|
||||
fire_dmg.set_text(&format!("{}", statistics.fire_damage.raw()))?;
|
||||
water_dmg.set_text(&format!("{}", statistics.water_damage.raw()))?;
|
||||
phys_dmg.set_text(&format!("{}", statistics.physical_damage.raw()))?;
|
||||
|
||||
crit_chance.set_text(&format!("{:.2} %", statistics.critical_hit_chance.raw()))?;
|
||||
crit_dmg.set_text(&format!(
|
||||
"{:.2} %",
|
||||
statistics.critical_hit_damage.raw() + 100.0
|
||||
))?;
|
||||
|
||||
health.set_text(&format!("{:.0}", statistics.health.raw()))?;
|
||||
health_regen.set_text(&format!("{:.2}", statistics.health_regeneration.raw()))?;
|
||||
mana.set_text(&format!("{:.0}", statistics.mana.raw()))?;
|
||||
mana_regen.set_text(&format!("{:.2}", statistics.mana_regeneration.raw()))?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn update_attributes(&self) -> Result<()> {
|
||||
let level_label: Arc<Label> = self.snippet.element("level")?;
|
||||
let level_progress: Arc<ProgressBar> = self.snippet.element("level_progress")?;
|
||||
|
||||
let attributes_label: Arc<Label> = self.snippet.element("attributes")?;
|
||||
let strength: Arc<Button> = self.snippet.element("strength_field")?;
|
||||
let agility: Arc<Button> = self.snippet.element("agility_field")?;
|
||||
let intelligence: Arc<Button> = self.snippet.element("intelligence_field")?;
|
||||
|
||||
self.engine.on_scene(|scene| {
|
||||
let entity = scene.entity(self.hero)?;
|
||||
|
||||
let level = entity.get_component::<Level>()?;
|
||||
let attributes = entity.get_component::<Attributes>()?;
|
||||
|
||||
level_label.set_text(format!("Level: {}", level.level()))?;
|
||||
level_progress.set_text(format!(
|
||||
"{} / {}",
|
||||
level.current_experience, level.experience_needed
|
||||
))?;
|
||||
|
||||
attributes_label.set_text(format!(
|
||||
"Attributes ({})",
|
||||
Self::available_attribute_points(&game, attributes, level)
|
||||
))?;
|
||||
|
||||
strength.set_text(attributes.strength().raw())?;
|
||||
agility.set_text(attributes.agility().raw())?;
|
||||
intelligence.set_text(attributes.intelligence().raw())?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn available_attribute_points(game: &Game, attributes: &Attributes, level: &Level) -> u32 {
|
||||
let total_attribute_points = game.attribute_settings.meta_settings.starting_skill_points
|
||||
+ level.level() * game.attribute_settings.meta_settings.skill_points_per_level
|
||||
+ game.attribute_settings.starting_attributes.sum();
|
||||
|
||||
let attributes_spent = attributes.sum();
|
||||
|
||||
total_attribute_points - attributes_spent
|
||||
}
|
||||
|
||||
fn create_update_stats<F>(
|
||||
hero: Entity,
|
||||
engine: &Arc<Engine>,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> impl Fn(F) -> Result<()> + Clone
|
||||
where
|
||||
F: Fn(&mut Attributes),
|
||||
{
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |upgrade: F| {
|
||||
let mut upgraded = false;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let level = multi_mut.get::<Level>()?;
|
||||
let attributes = multi_mut.get::<Attributes>()?;
|
||||
|
||||
if Self::available_attribute_points(&game, attributes, level) > 0 {
|
||||
upgrade(attributes);
|
||||
|
||||
let statistics = multi_mut.get::<Statistics>()?;
|
||||
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
|
||||
statistics.update(attributes, &game.attribute_settings, &*items);
|
||||
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if upgraded {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
menu.tabs().character().refresh()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Page for CharacterPage {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>> {
|
||||
println!("enable CharacterPage");
|
||||
|
||||
self.refresh()?;
|
||||
|
||||
Ok(self.grid.clone())
|
||||
}
|
||||
|
||||
fn disable(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
let strength: Arc<Button> = self.snippet.element("strength_field")?;
|
||||
strength.select()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn event(&mut self, _button: ControllerButton) -> Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
158
character_window/src/content.rs
Normal file
158
character_window/src/content.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use crate::*;
|
||||
|
||||
pub trait ContentWrapper: ContentUpdate + Send + Sync {
|
||||
fn refresh(&mut self) -> Result<()>;
|
||||
fn next_tab(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()>;
|
||||
fn previous_tab(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()>;
|
||||
fn base(&self) -> &Arc<GuiSnippet>;
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait ContentUpdate {
|
||||
fn update(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()>;
|
||||
fn select(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct Content<T: Send + Sync> {
|
||||
pub reference: Weak<CharacterWindow>,
|
||||
base: Arc<GuiSnippet>,
|
||||
data: Vec<T>,
|
||||
|
||||
on_enable: Box<dyn Fn() -> Result<Vec<T>> + Send + Sync + 'static>,
|
||||
|
||||
page: usize,
|
||||
pages: usize,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> Content<T> {
|
||||
pub fn new<F>(game: &GameHandle, reference: Weak<CharacterWindow>, on_enable: F) -> Result<Self>
|
||||
where
|
||||
F: Fn() -> Result<Vec<T>> + Send + Sync + 'static,
|
||||
{
|
||||
let base = game.gui_snippet("gui/xml/ingame/character_menu/content.xml")?;
|
||||
|
||||
let left: Arc<Button> = base.element("left")?;
|
||||
left.set_text("<")?;
|
||||
left.set_info_icon(&game.controller_icon(ControllerButton::LeftTrigger)?)?;
|
||||
let right: Arc<Button> = base.element("right")?;
|
||||
right.set_text(">")?;
|
||||
right.set_info_icon(&game.controller_icon(ControllerButton::RightTrigger)?)?;
|
||||
|
||||
Ok(Self {
|
||||
reference,
|
||||
base,
|
||||
data: Vec::new(),
|
||||
|
||||
on_enable: Box::new(on_enable),
|
||||
|
||||
page: 0,
|
||||
pages: 1,
|
||||
})
|
||||
}
|
||||
|
||||
fn clear_grid(grid: &Arc<Grid>) -> Result<()> {
|
||||
let (rows, columns) = grid.dimensions();
|
||||
|
||||
for x in 0..columns {
|
||||
for y in 0..rows {
|
||||
grid.detach(x, y)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_tab(&self, label: &Arc<Label>) -> Result<()> {
|
||||
label.set_text(format!("{} / {}", self.page + 1, self.pages))
|
||||
}
|
||||
|
||||
pub fn update_base<F>(&mut self, game_handle: &GameHandle, setup: F) -> Result<()>
|
||||
where
|
||||
Self: ContentWrapper,
|
||||
F: Fn(&Arc<Button>, &T, usize) -> Result<()>,
|
||||
{
|
||||
self.refresh()?;
|
||||
|
||||
let grid: Arc<Grid> = self.base.element("content")?;
|
||||
let label: Arc<Label> = self.base.element("tab_info")?;
|
||||
|
||||
Self::clear_grid(&grid)?;
|
||||
self.set_tab(&label)?;
|
||||
|
||||
let (rows, columns) = grid.dimensions();
|
||||
|
||||
'outer: for y in 0..rows {
|
||||
for x in 0..columns {
|
||||
let index = (self.page * columns * rows) + y * columns + x;
|
||||
|
||||
match self.data.get(index) {
|
||||
Some(t) => {
|
||||
let snippet = game_handle
|
||||
.gui_snippet("gui/xml/ingame/character_menu/content_button.xml")?;
|
||||
|
||||
let button: Arc<Button> = snippet.element("button")?;
|
||||
setup(&button, t, index)?;
|
||||
grid.attach(button, x, y, 1, 1)?;
|
||||
}
|
||||
None => break 'outer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select(&self) -> Result<()> {
|
||||
let grid: Arc<Grid> = self.base.element("content")?;
|
||||
|
||||
if let Some(child) = grid.child_at(0, 0)? {
|
||||
child.gridable().unwrap().selectable().unwrap().select()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> ContentWrapper for Content<T>
|
||||
where
|
||||
Content<T>: ContentUpdate,
|
||||
{
|
||||
fn refresh(&mut self) -> Result<()> {
|
||||
self.data = (self.on_enable)()?;
|
||||
|
||||
let grid: Arc<Grid> = self.base.element("content")?;
|
||||
let (rows, columns) = grid.dimensions();
|
||||
|
||||
self.pages = 1.max((self.data.len() as f32 / (rows * columns) as f32).ceil() as usize);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
if self.page < (self.pages - 1) {
|
||||
self.page += 1;
|
||||
self.update(game_handle, hero)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
if self.page > 0 {
|
||||
self.page -= 1;
|
||||
self.update(game_handle, hero)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
441
character_window/src/inventory/content.rs
Normal file
441
character_window/src/inventory/content.rs
Normal file
|
@ -0,0 +1,441 @@
|
|||
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::items::{Item, ItemAffix, Jewel, MapItem};
|
||||
|
||||
use crate::*;
|
||||
|
||||
use crate::{
|
||||
content::{Content, ContentUpdate},
|
||||
CharacterWindow,
|
||||
};
|
||||
|
||||
use super::jewel_right_side::{LowerJewels, ReferenceItemSource, ReferenceObject};
|
||||
|
||||
impl Content<Item> {
|
||||
fn salvage_item(game: &Game, hero: Entity, item_index: usize) -> Result<()> {
|
||||
CharacterWindow::salvage_from_inventory(game, 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(game: &Game, hero: Entity, item_index: usize) -> Result<bool> {
|
||||
let mut has_empty_sockets = true;
|
||||
|
||||
game.engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
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(game: &Game, hero: Entity, item_index: usize) -> Result<()> {
|
||||
game.engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let hero_items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
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, &game.attribute_settings, &*hero_items);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn show_item_tooltip(
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
item_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
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(game_handle, 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(game_handle, 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 ContentUpdate for Content<Item> {
|
||||
fn update(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(game_handle, |button, t, index| {
|
||||
button.set_icon(&t.icon)?;
|
||||
|
||||
button.set_custom_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::X => {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
Self::salvage_item(&game, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
ControllerButton::Y => {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
if Self::select_to_socket(&game, hero, index)? {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory();
|
||||
|
||||
inventory.switch_to_jewels()?;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
Self::equip_item(&game, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_item_tooltip(&game_handle, 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 Content<Jewel> {
|
||||
fn show_jewel_tooltip(
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
item_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
game_handle.upgrade().engine().on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let jewel = inventory.jewel_at(item_index);
|
||||
let gui = jewel.create_tooltip(
|
||||
game_handle,
|
||||
&game_handle.upgrade().item_settings,
|
||||
(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(game: &Game, hero: Entity, jewel_index: usize) -> Result<()> {
|
||||
game.engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
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(game: &Game, hero: Entity, jewel_index: usize) -> Result<()> {
|
||||
game.engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
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 ContentUpdate for Content<Jewel> {
|
||||
fn update(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(game_handle, |button, t, index| {
|
||||
button.set_icon(&t.icon())?;
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_jewel_tooltip(
|
||||
&game_handle,
|
||||
hero,
|
||||
index,
|
||||
&reference,
|
||||
button_pos,
|
||||
)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("jewel_{index}"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
Self::select_to_lower(&game, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_custom_callback({
|
||||
let game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::Y => {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
Self::select_to_combine(&game, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentUpdate for Content<MapItem> {
|
||||
fn update(&mut self, game_handle: &GameHandle, _hero: Entity) -> Result<()> {
|
||||
self.update_base(game_handle, |_button, _t, _index| {
|
||||
// button.set_icon(&t.icon)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
540
character_window/src/inventory/item_right_side.rs
Normal file
540
character_window/src/inventory/item_right_side.rs
Normal file
|
@ -0,0 +1,540 @@
|
|||
use rpg_components::{
|
||||
components::{
|
||||
attributes::Attributes, character_status::CharacterStatus, inventory::Inventory,
|
||||
item_slots::ItemSlotContainer, statistics::Statistics,
|
||||
},
|
||||
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(
|
||||
game_handle: &GameHandle,
|
||||
path: &str,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
hero: Entity,
|
||||
) -> Result<Self> {
|
||||
let snippet = game_handle.gui_snippet(path)?;
|
||||
let icons = InventoryEmptyIcons::new(&snippet)?;
|
||||
|
||||
let me = Self {
|
||||
snippet,
|
||||
empty_icons: icons,
|
||||
};
|
||||
|
||||
me.setup(game_handle, reference, hero)?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
fn setup(
|
||||
&self,
|
||||
game_handle: &GameHandle,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
hero: Entity,
|
||||
) -> Result<()> {
|
||||
button_setup!(self, game_handle, reference, hero, helmet, "helmet");
|
||||
button_setup!(self, game_handle, reference, hero, chest, "chest");
|
||||
button_setup!(self, game_handle, reference, hero, gloves, "gloves");
|
||||
button_setup!(self, game_handle, reference, hero, belt, "belt");
|
||||
button_setup!(self, game_handle, reference, hero, boots, "boots");
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, primary_hand, "main hand");
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, secondary_hand, "off hand");
|
||||
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, amulet, "amulet_0", 0);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, amulet, "amulet_1", 1);
|
||||
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, ring, "ring_0", 0);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, ring, "ring_1", 1);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, reference, hero, ring, "ring_2", 2);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, game_handle, 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(
|
||||
game_handle: &GameHandle,
|
||||
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(game_handle, attributes, (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
gui.perform_single_check(x, y)?;
|
||||
|
||||
Ok(gui)
|
||||
}
|
||||
}
|
||||
|
||||
impl RightSide for ItemRightSide {
|
||||
fn refresh(&mut self, game_handle: &GameHandle, hero: Entity) -> Result<()> {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
game.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(ui: &GuiSnippet) -> Result<Self> {
|
||||
get_place_holder!(ui, helmet);
|
||||
get_place_holder!(ui, chest);
|
||||
get_place_holder!(ui, boots);
|
||||
get_place_holder!(ui, belt);
|
||||
get_place_holder!(ui, gloves);
|
||||
|
||||
get_place_holder!(ui, primary_hand, "main hand");
|
||||
get_place_holder!(ui, secondary_hand, "off hand");
|
||||
|
||||
get_place_holder!(ui, ring, "ring_0");
|
||||
get_place_holder!(ui, amulet, "amulet_0");
|
||||
|
||||
Ok(Self {
|
||||
helmet,
|
||||
chest,
|
||||
belt,
|
||||
boots,
|
||||
gloves,
|
||||
|
||||
primary_hand,
|
||||
secondary_hand,
|
||||
|
||||
ring,
|
||||
amulet,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mod macros {
|
||||
#[macro_export]
|
||||
macro_rules! button_setup {
|
||||
($self:ident, $game_handle: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 game_handle = $game_handle.clone();
|
||||
let reference = $reference.clone();
|
||||
let weak_button = Arc::downgrade(&[<$item _button>]);
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
game_handle.upgrade().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(
|
||||
&game_handle,
|
||||
$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 game_handle = $game_handle.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move || {
|
||||
let mut found_item = false;
|
||||
|
||||
game_handle.upgrade().engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut($hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
|
||||
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,
|
||||
&game_handle.upgrade().attribute_settings,
|
||||
&*items
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
[<$item _button>].set_custom_callback({
|
||||
let game_handle = $game_handle.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::Y => {
|
||||
let mut empty_affixes_found = false;
|
||||
|
||||
game_handle.upgrade().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();
|
||||
|
||||
inventory.switch_to_jewels()?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
($self:ident, $game_handle: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 game_handle = $game_handle.clone();
|
||||
let reference = $reference.clone();
|
||||
let weak_button = Arc::downgrade(&[<$item _button>]);
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
game_handle.upgrade().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(
|
||||
&game_handle,
|
||||
$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 game_handle = $game_handle.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move || {
|
||||
let mut found_item = false;
|
||||
|
||||
game_handle.upgrade().engine().on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut($hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
|
||||
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,
|
||||
&game_handle.upgrade().attribute_settings,
|
||||
&*items
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
[<$item _button>].set_custom_callback({
|
||||
let game_handle = $game_handle.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::Y => {
|
||||
let mut empty_affixes_found = false;
|
||||
|
||||
game_handle.upgrade().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();
|
||||
|
||||
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)?,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_place_holder {
|
||||
($gui:ident, $name:ident) => {
|
||||
let $name = {
|
||||
let button: Arc<Button> = $gui.element(stringify!($name))?;
|
||||
button
|
||||
.icon()?
|
||||
.expect(&format!("missing {} placeholder", stringify!($name)))
|
||||
};
|
||||
};
|
||||
($gui:ident, $name:ident, $id:literal) => {
|
||||
let $name = {
|
||||
let button: Arc<Button> = $gui.element($id)?;
|
||||
button
|
||||
.icon()?
|
||||
.expect(&format!("missing {} placeholder", $id))
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
411
character_window/src/inventory/jewel_right_side.rs
Normal file
411
character_window/src/inventory/jewel_right_side.rs
Normal file
|
@ -0,0 +1,411 @@
|
|||
use rpg_components::{
|
||||
components::{
|
||||
attributes::Attributes,
|
||||
inventory::{Inventory, Storable},
|
||||
item_slots::ItemSlotContainer,
|
||||
statistics::Statistics,
|
||||
},
|
||||
items::{Item, ItemAffix, 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(
|
||||
game_handle: &GameHandle,
|
||||
path: &str,
|
||||
hero: Entity,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> Result<Self> {
|
||||
let snippet = game_handle.gui_snippet(path)?;
|
||||
|
||||
let combine: Arc<Label> = snippet.element("combine")?;
|
||||
combine.set_info_icon(&game_handle.controller_icon(ControllerButton::RightStick)?)?;
|
||||
|
||||
game_handle.upgrade().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(game_handle, hero, reference)?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
fn setup_select(
|
||||
&self,
|
||||
game_handle: &GameHandle,
|
||||
hero: Entity,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> Result<()> {
|
||||
let (top, bottom) = self.elements()?;
|
||||
|
||||
top.set_select_callback({
|
||||
let game_handle = game_handle.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 game = game_handle.upgrade();
|
||||
let scene = game.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(
|
||||
&game_handle,
|
||||
scene.entity(hero)?.get_component::<Attributes>()?,
|
||||
(x + w as i32, y),
|
||||
)?,
|
||||
ReferenceObject::Jewel { jewel, .. } => jewel.create_tooltip(
|
||||
&game_handle,
|
||||
&game_handle.upgrade().item_settings,
|
||||
(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 game_handle = game_handle.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 game = game_handle.upgrade();
|
||||
let scene = game.engine().scene();
|
||||
|
||||
let lower_info = scene.resources.get::<LowerJewels>();
|
||||
|
||||
if let Some((lower_jewel, _)) = &lower_info.jewels[index] {
|
||||
let tooltip = lower_jewel.create_tooltip(
|
||||
&game_handle,
|
||||
&game_handle.upgrade().item_settings,
|
||||
(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 game_handle = game_handle.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
let scene = game.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();
|
||||
|
||||
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(game_handle: &GameHandle) {
|
||||
let game = game_handle.upgrade();
|
||||
let scene = game.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(game_handle: &GameHandle, hero: Entity) -> Result<bool> {
|
||||
let game = game_handle.upgrade();
|
||||
let scene = game.engine().scene_mut();
|
||||
let mut resources = unsafe { remove_life_time_mut(scene) }.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 =
|
||||
scene.entity_mut(hero)?.get_component_mut::<Inventory>()?;
|
||||
|
||||
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 entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
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, &game.attribute_settings, &*item_slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 entity = scene.entity_mut(hero)?;
|
||||
let inventory = entity.get_component_mut::<Inventory>()?;
|
||||
|
||||
let upper_jewel = inventory.jewel_mut_at(*index);
|
||||
|
||||
if upper_jewel.level >= 4 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
upper_jewel.level += 1;
|
||||
upper_jewel.update_stat(&game.item_settings);
|
||||
upper_jewel.icon = Some(game.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, game_handle: &GameHandle, _hero: Entity) -> Result<()> {
|
||||
game_handle.upgrade().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, game_handle: &GameHandle, _hero: Entity) -> Result<()> {
|
||||
let game = game_handle.upgrade();
|
||||
let scene = game.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
|
||||
}
|
||||
}
|
27
character_window/src/inventory/map_right_side.rs
Normal file
27
character_window/src/inventory/map_right_side.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use crate::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::super::traits::*;
|
||||
|
||||
pub struct MapRightSide {
|
||||
snippet: Arc<GuiSnippet>,
|
||||
}
|
||||
|
||||
impl MapRightSide {
|
||||
pub fn new(game_handle: &GameHandle, path: &str) -> Result<Self> {
|
||||
let snippet = game_handle.gui_snippet(path)?;
|
||||
|
||||
Ok(Self { snippet })
|
||||
}
|
||||
}
|
||||
|
||||
impl RightSide for MapRightSide {
|
||||
fn refresh(&mut self, _game_handle: &GameHandle, _hero: Entity) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.snippet
|
||||
}
|
||||
}
|
433
character_window/src/inventory/mod.rs
Normal file
433
character_window/src/inventory/mod.rs
Normal file
|
@ -0,0 +1,433 @@
|
|||
mod content;
|
||||
mod item_right_side;
|
||||
mod jewel_right_side;
|
||||
mod map_right_side;
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use rpg_components::components::inventory::Inventory;
|
||||
|
||||
use super::page_content::PageContent;
|
||||
use super::traits::*;
|
||||
use super::{content::Content, CharacterWindow, Page};
|
||||
use crate::GameHandle;
|
||||
use item_right_side::ItemRightSide;
|
||||
use jewel_right_side::JewelRightSide;
|
||||
use map_right_side::MapRightSide;
|
||||
|
||||
pub struct InventoryPage {
|
||||
close: Weak<Button>,
|
||||
|
||||
game_handle: GameHandle,
|
||||
hero: Entity,
|
||||
|
||||
grid: Arc<Grid>,
|
||||
|
||||
tooltip: Arc<Grid>,
|
||||
content: Arc<Grid>,
|
||||
|
||||
modes: [Box<dyn PageContentWrapper>; 3],
|
||||
|
||||
current_mode: usize,
|
||||
}
|
||||
|
||||
impl InventoryPage {
|
||||
pub fn new(
|
||||
game: GameHandle,
|
||||
hero: Entity,
|
||||
reference: Weak<CharacterWindow>,
|
||||
close: &Arc<Button>,
|
||||
) -> Result<Self> {
|
||||
let base = "gui/xml/ingame/character_menu/inventory/".to_string();
|
||||
|
||||
let grid = Grid::new(game.upgrade().engine().gui_handler().clone(), 2, 1, false)?;
|
||||
|
||||
let left_base = game.gui_snippet((base.clone() + "left_side.xml").as_str())?;
|
||||
grid.attach(left_base.clone(), 0, 0, 1, 1)?;
|
||||
|
||||
Self::setup_content_switch(&left_base, reference.clone())?;
|
||||
|
||||
let tooltip = left_base.element("tooltip")?;
|
||||
let content = left_base.element("content")?;
|
||||
|
||||
// items
|
||||
let item_mode = PageContent::new(
|
||||
Content::new(&game, reference.clone(), {
|
||||
let game_handle = game.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
let mut data = Vec::new();
|
||||
|
||||
game.engine().on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory>()?;
|
||||
|
||||
data = inventory.iter_items().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = game.gui_snippet((base.clone() + "items/tooltip.xml").as_str())?;
|
||||
|
||||
let equip: Arc<Label> = ui.element("equip")?;
|
||||
equip.set_info_icon(&game.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let salvage: Arc<Label> = ui.element("salvage")?;
|
||||
salvage.set_info_icon(&game.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let socket: Arc<Label> = ui.element("socket")?;
|
||||
socket.set_info_icon(&game.controller_icon(ControllerButton::Y)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&game.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
ItemRightSide::new(
|
||||
&game,
|
||||
(base.clone() + "items/right_side.xml").as_str(),
|
||||
&reference,
|
||||
hero,
|
||||
)?,
|
||||
);
|
||||
|
||||
// jewels
|
||||
let jewel_mode = PageContent::new(
|
||||
Content::new(&game, reference.clone(), {
|
||||
let game_handle = game.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
let mut data = Vec::new();
|
||||
|
||||
game.engine().on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory>()?;
|
||||
|
||||
data = inventory.iter_jewels().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = game.gui_snippet((base.clone() + "jewels/tooltip.xml").as_str())?;
|
||||
|
||||
let socket: Arc<Label> = ui.element("socket")?;
|
||||
socket.set_info_icon(&game.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let combine: Arc<Label> = ui.element("combine")?;
|
||||
combine.set_info_icon(&game.controller_icon(ControllerButton::Y)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&game.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
JewelRightSide::new(
|
||||
&game,
|
||||
(base.clone() + "jewels/right_side.xml").as_str(),
|
||||
hero,
|
||||
&reference,
|
||||
)?,
|
||||
);
|
||||
|
||||
// maps
|
||||
let map_mode = PageContent::new(
|
||||
Content::new(&game, reference.clone(), {
|
||||
let game_handle = game.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let game = game_handle.upgrade();
|
||||
let mut data = Vec::new();
|
||||
|
||||
game.engine().on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory>()?;
|
||||
|
||||
data = inventory.iter_maps().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = game.gui_snippet((base.clone() + "maps/tooltip.xml").as_str())?;
|
||||
|
||||
let select: Arc<Label> = ui.element("select")?;
|
||||
select.set_info_icon(&game.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let start: Arc<Label> = ui.element("start")?;
|
||||
start.set_info_icon(&game.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&game.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
MapRightSide::new(&game, (base.clone() + "maps/right_side.xml").as_str())?,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
close: Arc::downgrade(close),
|
||||
|
||||
game_handle: game,
|
||||
hero,
|
||||
|
||||
grid,
|
||||
|
||||
tooltip,
|
||||
content,
|
||||
|
||||
modes: [
|
||||
Box::new(item_mode),
|
||||
Box::new(jewel_mode),
|
||||
Box::new(map_mode),
|
||||
],
|
||||
|
||||
current_mode: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn switch_to_jewels(&mut self) -> Result<()> {
|
||||
self.current_mode = 1;
|
||||
self.update_page(true)
|
||||
}
|
||||
|
||||
fn update_page(&mut self, select: bool) -> Result<()> {
|
||||
match self.current_mode {
|
||||
0 => println!("update item view"),
|
||||
1 => println!("update jewel view"),
|
||||
2 => println!("update map view"),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
{
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
self.tooltip.attach(mode.tooltip().clone(), 0, 0, 1, 1)?;
|
||||
self.content
|
||||
.attach(mode.content_mut().base().clone(), 0, 0, 1, 1)?;
|
||||
self.grid
|
||||
.attach(mode.right_side_mut().base().clone(), 1, 0, 1, 1)?;
|
||||
|
||||
mode.content_mut().update(&self.game_handle, self.hero)?;
|
||||
mode.right_side_mut()
|
||||
.refresh(&self.game_handle, self.hero)?;
|
||||
|
||||
if select {
|
||||
mode.content_mut().select()?;
|
||||
|
||||
if mode.content_mut().is_empty() {
|
||||
if let Some(close) = self.close.upgrade() {
|
||||
close.select()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.connect_character_page()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn connect_character_page(&mut self) -> Result<()> {
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
match self.current_mode {
|
||||
0 => {
|
||||
let right_side = mode.right_side_mut().base();
|
||||
let gloves: Arc<Button> = right_side.element("gloves")?;
|
||||
let ring_0: Arc<Button> = right_side.element("ring_0")?;
|
||||
let ring_2: Arc<Button> = right_side.element("ring_2")?;
|
||||
let main_hand: Arc<Button> = right_side.element("main hand")?;
|
||||
|
||||
let content_grid: Arc<Grid> = mode.content_mut().base().element("content")?;
|
||||
|
||||
content_grid.connect(
|
||||
ConnectDirection::East,
|
||||
[
|
||||
(0, gloves.selectable().unwrap()),
|
||||
(1, ring_0.selectable().unwrap()),
|
||||
(2, ring_2.selectable().unwrap()),
|
||||
(3, main_hand.selectable().unwrap()),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
1 => {
|
||||
let right_side = mode.right_side_mut().base();
|
||||
|
||||
let reference: Arc<Button> = right_side.element("reference")?;
|
||||
let first: Arc<Button> = right_side.element("first")?;
|
||||
|
||||
let content_grid: Arc<Grid> = mode.content_mut().base().element("content")?;
|
||||
|
||||
content_grid.connect(
|
||||
ConnectDirection::East,
|
||||
[
|
||||
(1, reference.selectable().unwrap()),
|
||||
(0, reference.selectable().unwrap()),
|
||||
(3, first.selectable().unwrap()),
|
||||
(2, first.selectable().unwrap()),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
2 => {
|
||||
// map tab is not implemented now
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_content_switch(
|
||||
left_base: &GuiSnippet,
|
||||
reference: Weak<CharacterWindow>,
|
||||
) -> Result<()> {
|
||||
let switch = {
|
||||
let reference = reference.clone();
|
||||
|
||||
move |index| {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let me = tabs.inventory();
|
||||
|
||||
if me.current_mode != index {
|
||||
me.current_mode = index;
|
||||
me.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
let switch_to_items = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(0)
|
||||
});
|
||||
|
||||
let switch_to_jewels = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(1)
|
||||
});
|
||||
|
||||
let switch_to_maps = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(2)
|
||||
});
|
||||
|
||||
left_base.set_click_callbacks(vec![
|
||||
("items", switch_to_items),
|
||||
("jewels", switch_to_jewels),
|
||||
("maps", switch_to_maps),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Page for InventoryPage {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>> {
|
||||
println!("enable InventoryPage");
|
||||
|
||||
for mode in self.modes.iter_mut() {
|
||||
mode.content_mut().refresh()?;
|
||||
}
|
||||
|
||||
self.update_page(false)?;
|
||||
|
||||
Ok(self.grid.clone())
|
||||
}
|
||||
|
||||
fn disable(&mut self) -> Result<()> {
|
||||
self.modes[self.current_mode]
|
||||
.right_side_mut()
|
||||
.disable(&self.game_handle, self.hero)
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
let mode = &self.modes[self.current_mode];
|
||||
|
||||
mode.content().select()?;
|
||||
|
||||
if mode.content().is_empty() {
|
||||
if let Some(close) = self.close.upgrade() {
|
||||
close.select()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) -> Result<()> {
|
||||
{
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
mode.content_mut().next_tab(&self.game_handle, self.hero)?;
|
||||
mode.content_mut().select()?;
|
||||
}
|
||||
|
||||
self.connect_character_page()
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) -> Result<()> {
|
||||
{
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
mode.content_mut()
|
||||
.previous_tab(&self.game_handle, self.hero)?;
|
||||
mode.content_mut().select()?;
|
||||
}
|
||||
|
||||
self.connect_character_page()
|
||||
}
|
||||
|
||||
fn event(&mut self, button: ControllerButton) -> Result<bool> {
|
||||
Ok(match button {
|
||||
ControllerButton::LeftStick => {
|
||||
self.modes[self.current_mode]
|
||||
.right_side_mut()
|
||||
.disable(&self.game_handle, self.hero)?;
|
||||
|
||||
self.current_mode = (self.current_mode + 1) % self.modes.len();
|
||||
|
||||
self.update_page(true)?;
|
||||
true
|
||||
}
|
||||
ControllerButton::RightStick => {
|
||||
// check if jewel page is open
|
||||
if self.current_mode == 1 {
|
||||
if JewelRightSide::combine(&self.game_handle, self.hero)? {
|
||||
JewelRightSide::clear(&self.game_handle);
|
||||
self.update_page(true)?;
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
448
character_window/src/lib.rs
Normal file
448
character_window/src/lib.rs
Normal file
|
@ -0,0 +1,448 @@
|
|||
mod abilities;
|
||||
mod character;
|
||||
mod content;
|
||||
mod inventory;
|
||||
mod page_content;
|
||||
mod traits;
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use rpg_components::components::{
|
||||
crafting_materials::CraftingMaterials,
|
||||
inventory::{Inventory, Storable},
|
||||
};
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::HashMap,
|
||||
mem::transmute,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||
},
|
||||
};
|
||||
|
||||
use self::{abilities::AbilityPage, character::CharacterPage, inventory::InventoryPage};
|
||||
|
||||
trait Page: Any + Send + Sync {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>>;
|
||||
fn disable(&mut self) -> Result<()>;
|
||||
fn select(&self) -> Result<()>;
|
||||
fn next_tab(&mut self) -> Result<()>;
|
||||
fn previous_tab(&mut self) -> Result<()>;
|
||||
fn event(&mut self, button: ControllerButton) -> Result<bool>;
|
||||
}
|
||||
|
||||
struct Tab<'a> {
|
||||
index: usize,
|
||||
tabs: RwLockReadGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for Tab<'a> {
|
||||
type Target = Box<dyn Page>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tabs[self.index]
|
||||
}
|
||||
}
|
||||
|
||||
struct TabMut<'a> {
|
||||
index: usize,
|
||||
tabs: RwLockWriteGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for TabMut<'a> {
|
||||
type Target = Box<dyn Page>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tabs[self.index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for TabMut<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.tabs[self.index]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tabs<'a> {
|
||||
tabs: RwLockReadGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<'a> Tabs<'a> {
|
||||
pub fn character(&mut self) -> &'a CharacterPage {
|
||||
Self::downcast_unchecked(&self.tabs[0])
|
||||
}
|
||||
|
||||
pub fn inventory(&mut self) -> &'a InventoryPage {
|
||||
Self::downcast_unchecked(&self.tabs[1])
|
||||
}
|
||||
|
||||
pub fn abilities(&mut self) -> &'a AbilityPage {
|
||||
Self::downcast_unchecked(&self.tabs[2])
|
||||
}
|
||||
|
||||
fn downcast_unchecked<T: Page>(boxed_ref: &Box<dyn Page>) -> &'a T {
|
||||
unsafe {
|
||||
let ptr_to_ptr: *const *const T =
|
||||
transmute(destructure_traitobject::data(boxed_ref as *const _));
|
||||
|
||||
&**ptr_to_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TabsMut<'a> {
|
||||
tabs: RwLockWriteGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<'a> TabsMut<'a> {
|
||||
pub fn character(&mut self) -> &'a mut CharacterPage {
|
||||
Self::downcast_mut_unchecked(&mut self.tabs[0])
|
||||
}
|
||||
|
||||
pub fn inventory(&mut self) -> &'a mut InventoryPage {
|
||||
Self::downcast_mut_unchecked(&mut self.tabs[1])
|
||||
}
|
||||
|
||||
pub fn abilities(&mut self) -> &'a mut AbilityPage {
|
||||
Self::downcast_mut_unchecked(&mut self.tabs[2])
|
||||
}
|
||||
|
||||
fn downcast_mut_unchecked<T: Page>(boxed_ref: &mut Box<dyn Page>) -> &'a mut T {
|
||||
unsafe {
|
||||
let ptr_to_ptr: *mut *mut T =
|
||||
transmute(destructure_traitobject::data(boxed_ref as *mut _));
|
||||
|
||||
&mut **ptr_to_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CharacterWindow {
|
||||
hud: Box<dyn FutureStateChange>,
|
||||
|
||||
menu_gui: Arc<GuiBuilder>,
|
||||
tab_content_grid: Arc<Grid>,
|
||||
|
||||
tooltips: Mutex<HashMap<String, Arc<GuiBuilder>>>,
|
||||
|
||||
tabs: RwLock<[Box<dyn Page>; 3]>,
|
||||
tab: AtomicUsize,
|
||||
|
||||
engine: Arc<Engine>,
|
||||
}
|
||||
|
||||
impl CharacterWindow {
|
||||
pub fn new(engine: Arc<Engine>, hero: Entity, name: &str) -> Result<Arc<Self>> {
|
||||
let menu_gui =
|
||||
GuiBuilder::from_str(engine.gui_handler(), include_str!("../resources/menu.xml"))?;
|
||||
|
||||
let content_grid = menu_gui.element("tab_content")?;
|
||||
let open_character_page: Arc<Button> = menu_gui.element("open_statistics")?;
|
||||
let open_inventory_page: Arc<Button> = menu_gui.element("open_inventory")?;
|
||||
let open_ability_page: Arc<Button> = menu_gui.element("open_abilities")?;
|
||||
let close_button: Arc<Button> = menu_gui.element("close")?;
|
||||
|
||||
let hud = ingame.ui().future_state_change("hud")?;
|
||||
|
||||
let character_window = Arc::new_cyclic(|me| CharacterWindow {
|
||||
hud: Box::new(hud),
|
||||
|
||||
menu_gui,
|
||||
tab_content_grid: content_grid,
|
||||
|
||||
tooltips: Mutex::default(),
|
||||
|
||||
tabs: RwLock::new([
|
||||
Box::new(
|
||||
CharacterPage::new(&ingame.game, ingame.hero.entity(), ingame.hero.name(), me)
|
||||
.unwrap(),
|
||||
),
|
||||
Box::new(
|
||||
InventoryPage::new(
|
||||
ingame.game.clone(),
|
||||
ingame.hero.entity(),
|
||||
me.clone(),
|
||||
&close_button,
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
Box::new(
|
||||
AbilityPage::new(
|
||||
ingame.game.clone(),
|
||||
ingame.hero.entity(),
|
||||
me.clone(),
|
||||
&close_button,
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
]),
|
||||
tab: AtomicUsize::new(0),
|
||||
|
||||
engine,
|
||||
});
|
||||
|
||||
let open_tab = {
|
||||
let weak_me = Arc::downgrade(&character_window);
|
||||
|
||||
move |index| {
|
||||
if let Some(me) = weak_me.upgrade() {
|
||||
me.tab.store(index, SeqCst);
|
||||
|
||||
me.tab_content_grid
|
||||
.attach(me.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
me.tab().select()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
open_character_page.set_callback({
|
||||
let open_tab = open_tab.clone();
|
||||
|
||||
move || open_tab(0)
|
||||
});
|
||||
|
||||
open_inventory_page.set_callback({
|
||||
let open_tab = open_tab.clone();
|
||||
|
||||
move || open_tab(1)
|
||||
});
|
||||
|
||||
open_ability_page.set_callback({
|
||||
let open_tab = open_tab.clone();
|
||||
|
||||
move || open_tab(2)
|
||||
});
|
||||
|
||||
Self::setup_menu(&character_window)?;
|
||||
|
||||
Ok(character_window)
|
||||
}
|
||||
|
||||
pub fn event(&self, button: ControllerButton) -> Result<bool> {
|
||||
self.tabs.write().unwrap()[self.tab.load(SeqCst)].event(button)
|
||||
}
|
||||
|
||||
pub fn tabs<'a>(&'a self) -> Tabs<'a> {
|
||||
Tabs {
|
||||
tabs: self.tabs.read().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tabs_mut<'a>(&'a self) -> TabsMut<'a> {
|
||||
TabsMut {
|
||||
tabs: self.tabs.write().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tab(&self) -> Tab<'_> {
|
||||
Tab {
|
||||
index: self.tab.load(SeqCst),
|
||||
tabs: self.tabs.read().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_mut(&self) -> TabMut<'_> {
|
||||
TabMut {
|
||||
index: self.tab.load(SeqCst),
|
||||
tabs: self.tabs.write().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tooltip(&self, name: impl ToString, gui: impl Into<Arc<GuiBuilder>>) {
|
||||
self.tooltips
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(name.to_string(), gui.into());
|
||||
}
|
||||
|
||||
pub fn remove_tooltip(&self, name: impl ToString) {
|
||||
self.tooltips.lock().unwrap().remove(&name.to_string());
|
||||
}
|
||||
|
||||
pub fn salvage_from_inventory<F, S>(engine: &Engine, hero: Entity, f: F) -> Result<()>
|
||||
where
|
||||
F: FnOnce(&mut Inventory) -> S,
|
||||
S: Storable,
|
||||
{
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let crafting_materials = multi_mut.get::<CraftingMaterials>()?;
|
||||
let inventory = multi_mut.get::<Inventory>()?;
|
||||
|
||||
// remove callback
|
||||
let storable = f(inventory);
|
||||
|
||||
crafting_materials.increment(storable.rarity());
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TopLevelGui for CharacterWindow {
|
||||
fn gui_traits(&self) -> &dyn GuiElementTraits {
|
||||
self
|
||||
}
|
||||
|
||||
fn top_gui(&self) -> Option<&dyn TopGui> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn elements(&self) -> Option<&HashMap<String, UiElement>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn functionality(&self) -> Option<&dyn Functionality> {
|
||||
None
|
||||
}
|
||||
|
||||
fn enable(&self) -> Result<()> {
|
||||
self.menu_gui.enable()?;
|
||||
|
||||
self.tab_content_grid
|
||||
.attach(self.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
self.tab().select()?;
|
||||
|
||||
let close_button: Arc<Button> = self.menu_gui.element("close")?;
|
||||
close_button.set_info_icon(&self.game_handle.controller_icon(ControllerButton::B)?)?;
|
||||
|
||||
let left_info: Arc<Icon> = self.menu_gui.element("left_info")?;
|
||||
match self
|
||||
.engine
|
||||
.settings()
|
||||
.controller_button(&self.engine, ControllerButton::LeftButton)?
|
||||
{
|
||||
Some(icon) => {
|
||||
left_info.set_icon(&icon)?;
|
||||
left_info.set_text("")?;
|
||||
}
|
||||
None => {
|
||||
left_info.clear_icon()?;
|
||||
left_info.set_text("")?;
|
||||
}
|
||||
};
|
||||
|
||||
let right_info: Arc<Icon> = self.menu_gui.element("right_info")?;
|
||||
match self
|
||||
.engine
|
||||
.settings()
|
||||
.controller_button(&self.engine, ControllerButton::RightButton)?
|
||||
{
|
||||
Some(icon) => {
|
||||
right_info.set_icon(&icon)?;
|
||||
right_info.set_text("")?;
|
||||
}
|
||||
None => {
|
||||
right_info.clear_icon()?;
|
||||
right_info.set_text("")?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&self) -> Result<()> {
|
||||
self.menu_gui.disable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiElementTraits for CharacterWindow {
|
||||
fn gridable(&self) -> Option<&dyn Gridable> {
|
||||
None
|
||||
}
|
||||
|
||||
fn visibility(&self) -> Option<&dyn Visibility> {
|
||||
None
|
||||
}
|
||||
|
||||
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl TopGui for CharacterWindow {
|
||||
fn decline(&self) -> Result<()> {
|
||||
(self.hud)()
|
||||
}
|
||||
|
||||
fn next_tab(&self, second_level: bool) -> Result<()> {
|
||||
match second_level {
|
||||
false => {
|
||||
// disable old tab
|
||||
self.tab_mut().disable()?;
|
||||
|
||||
// add to tab index
|
||||
self.tab.store((self.tab.load(SeqCst) + 1) % 3, SeqCst);
|
||||
|
||||
// update tab content
|
||||
self.tab_content_grid
|
||||
.attach(self.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
self.tab().select()?;
|
||||
}
|
||||
true => {
|
||||
self.tab_mut().next_tab()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous_tab(&self, second_level: bool) -> Result<()> {
|
||||
match second_level {
|
||||
false => {
|
||||
// disable old tab
|
||||
self.tab_mut().disable()?;
|
||||
|
||||
// subtract from tab index
|
||||
if self.tab.load(SeqCst) == 0 {
|
||||
self.tab.store(2, SeqCst);
|
||||
} else {
|
||||
self.tab.store(self.tab.load(SeqCst) - 1, SeqCst);
|
||||
}
|
||||
|
||||
// update tab content
|
||||
self.tab_content_grid
|
||||
.attach(self.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
self.tab().select()?;
|
||||
}
|
||||
true => {
|
||||
self.tab_mut().previous_tab()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CharacterWindow {
|
||||
fn setup_menu(&self) -> Result<()> {
|
||||
// let open_statistics = self.switch_tab(STATISTICS_TAB);
|
||||
// let open_abilities = self.switch_tab(ABILITY_TAB);
|
||||
// let open_inventory = self.switch_tab(INVENTORY_TAB);
|
||||
|
||||
let close = self.hud.clone();
|
||||
|
||||
self.menu_gui.set_click_callbacks(
|
||||
ClickCallbacks::default()
|
||||
// .add("open_statistics", open_statistics)
|
||||
// .add("open_abilities", open_abilities)
|
||||
// .add("open_inventory", open_inventory)
|
||||
.add("close", close)
|
||||
.into(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
63
character_window/src/page_content.rs
Normal file
63
character_window/src/page_content.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use crate::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::content::{Content, ContentUpdate, ContentWrapper};
|
||||
|
||||
use super::traits::*;
|
||||
|
||||
pub struct EmptyRightSide;
|
||||
|
||||
impl RightSide for EmptyRightSide {
|
||||
fn refresh(&mut self, _engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PageContent<T: Send + Sync> {
|
||||
content: Content<T>,
|
||||
tooltip: Arc<GuiSnippet>,
|
||||
|
||||
right_side: Box<dyn RightSide>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> PageContent<T> {
|
||||
pub fn new<R>(content: Content<T>, tooltip: Arc<GuiSnippet>, right_side: R) -> Self
|
||||
where
|
||||
R: RightSide + 'static,
|
||||
{
|
||||
Self {
|
||||
content,
|
||||
tooltip,
|
||||
right_side: Box::new(right_side),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> PageContentWrapper for PageContent<T>
|
||||
where
|
||||
Content<T>: ContentUpdate,
|
||||
{
|
||||
fn content(&self) -> &dyn ContentWrapper {
|
||||
&self.content
|
||||
}
|
||||
|
||||
fn content_mut(&mut self) -> &mut dyn ContentWrapper {
|
||||
&mut self.content
|
||||
}
|
||||
|
||||
fn tooltip(&self) -> &Arc<GuiSnippet> {
|
||||
&self.tooltip
|
||||
}
|
||||
|
||||
fn right_side(&self) -> &dyn RightSide {
|
||||
&*self.right_side
|
||||
}
|
||||
|
||||
fn right_side_mut(&mut self) -> &mut dyn RightSide {
|
||||
&mut *self.right_side
|
||||
}
|
||||
}
|
24
character_window/src/traits.rs
Normal file
24
character_window/src/traits.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::content::ContentWrapper;
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait PageContentWrapper: Send + Sync {
|
||||
fn content(&self) -> &dyn ContentWrapper;
|
||||
fn content_mut(&mut self) -> &mut dyn ContentWrapper;
|
||||
fn tooltip(&self) -> &Arc<GuiSnippet>;
|
||||
fn right_side(&self) -> &dyn RightSide;
|
||||
fn right_side_mut(&mut self) -> &mut dyn RightSide;
|
||||
}
|
||||
|
||||
pub trait RightSide: Send + Sync {
|
||||
fn refresh(&mut self, engine: &Engine, hero: Entity) -> Result<()>;
|
||||
|
||||
fn disable(&mut self, _engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet>;
|
||||
}
|
|
@ -12,7 +12,7 @@ use rpg_components::config::abilities::AbilitySettings;
|
|||
use rpg_components::config::attributes::AttributeSettings;
|
||||
use rpg_components::config::experience::ExperienceSettings;
|
||||
use rpg_components::config::items::ItemSettings;
|
||||
use rpg_components::items::{ItemSystem, ToolTipBuilder};
|
||||
use rpg_components::items::ItemSystem;
|
||||
|
||||
// std
|
||||
use std::collections::HashMap;
|
||||
|
@ -101,12 +101,6 @@ impl GameHandle {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToolTipBuilder for GameHandle {
|
||||
fn gui_handler(&self) -> Arc<GuiHandler> {
|
||||
self.upgrade().engine().gui_handler().clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Settings {
|
||||
pub core_settings: CoreSettings,
|
||||
pub user_settings: UserSettings,
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
ability_type::AbilityType, components::inventory::Storable, config::abilities::AbilitySettings,
|
||||
};
|
||||
|
||||
use super::{ItemSystem, Rarities, ToolTipBuilder, Tooltip};
|
||||
use super::{ItemSystem, Rarities, Tooltip};
|
||||
|
||||
const COOL_DOWN_REDUCTION_CAP: f32 = 0.7;
|
||||
|
||||
|
@ -274,11 +274,11 @@ impl AbilityAddon {
|
|||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
tooltip_builder: &impl ToolTipBuilder,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let gui = GuiBuilder::from_str(
|
||||
&tooltip_builder.gui_handler(),
|
||||
gui_handler,
|
||||
include_str!("../../resources/addon_snippet.xml"),
|
||||
)?;
|
||||
|
||||
|
@ -296,11 +296,7 @@ impl AbilityAddon {
|
|||
type_label.set_text(&format!("{}", self.addon_type()))?;
|
||||
value_label.set_text(&self.addon_type().val_as_str())?;
|
||||
|
||||
Ok(Tooltip::new(
|
||||
grid,
|
||||
gui,
|
||||
tooltip_builder.gui_handler().clone(),
|
||||
))
|
||||
Ok(Tooltip::new(grid, gui, gui_handler.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
|
||||
use super::{
|
||||
ability_addon::{AbilityAddon, AbilityAddonCollection, AbilityAddonTypes},
|
||||
ItemSystem, Rarities, ToolTipBuilder, Tooltip,
|
||||
ItemSystem, Rarities, Tooltip,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -282,12 +282,12 @@ impl AbilityBook {
|
|||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
tooltip_builder: &impl ToolTipBuilder,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
statistics: &Statistics,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let gui = GuiBuilder::from_str(
|
||||
&tooltip_builder.gui_handler(),
|
||||
gui_handler,
|
||||
include_str!("../../resources/book_snippet.xml"),
|
||||
)?;
|
||||
|
||||
|
@ -326,11 +326,7 @@ impl AbilityBook {
|
|||
* (1.0 - self.addons().cool_down_reduction())
|
||||
))?;
|
||||
|
||||
Ok(Tooltip::new(
|
||||
inspector_grid,
|
||||
gui,
|
||||
tooltip_builder.gui_handler().clone(),
|
||||
))
|
||||
Ok(Tooltip::new(inspector_grid, gui, gui_handler.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
config::items::ItemSettings,
|
||||
};
|
||||
|
||||
use super::{ItemSlots, ItemSystem, Jewel, Rarities, ToolTipBuilder, Tooltip};
|
||||
use super::{ItemSlots, ItemSystem, Jewel, Rarities, Tooltip};
|
||||
|
||||
const ITEM_SNIPPETS: [&'static str; 8] = [
|
||||
include_str!("../../resources/items/slots_0.xml"),
|
||||
|
@ -233,15 +233,14 @@ impl Item {
|
|||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
tooltip_builder: &impl ToolTipBuilder,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
attributes: &Attributes,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let (stats, jewels) = ItemAffix::squash(self.affixes.iter());
|
||||
let count = stats.len() + jewels.len();
|
||||
|
||||
let inspector_snippet =
|
||||
GuiBuilder::from_str(&tooltip_builder.gui_handler(), &ITEM_SNIPPETS[count])?;
|
||||
let inspector_snippet = GuiBuilder::from_str(gui_handler, &ITEM_SNIPPETS[count])?;
|
||||
|
||||
let item_icon: Arc<Icon> = inspector_snippet.element("item_icon")?;
|
||||
let slot_label: Arc<Label> = inspector_snippet.element("slot_label")?;
|
||||
|
@ -278,7 +277,7 @@ impl Item {
|
|||
|
||||
for stat in stats {
|
||||
let stat_type_snippet = GuiSnippet::from_str(
|
||||
&tooltip_builder.gui_handler(),
|
||||
gui_handler,
|
||||
include_str!("../../resources/stat_type_snippet.xml"),
|
||||
)?;
|
||||
|
||||
|
@ -295,7 +294,7 @@ impl Item {
|
|||
|
||||
for jewel in jewels {
|
||||
let socket_snippet = GuiSnippet::from_str(
|
||||
&tooltip_builder.gui_handler(),
|
||||
gui_handler,
|
||||
include_str!("../../resources/item_socket_snippet.xml"),
|
||||
)?;
|
||||
|
||||
|
@ -318,7 +317,7 @@ impl Item {
|
|||
Ok(Tooltip::new(
|
||||
inspector_grid,
|
||||
inspector_snippet,
|
||||
tooltip_builder.gui_handler().clone(),
|
||||
gui_handler.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use super::{Rarities, ToolTipBuilder, Tooltip};
|
||||
use super::{Rarities, Tooltip};
|
||||
use crate::{
|
||||
components::{attributes::Attribute, inventory::Storable, statistic_types::StatisticType},
|
||||
config::items::ItemSettings,
|
||||
|
@ -60,12 +60,12 @@ impl Jewel {
|
|||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
tooltip_builder: &impl ToolTipBuilder,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
item_settings: &ItemSettings,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let inspector_snippet: Arc<GuiBuilder> = GuiBuilder::from_str(
|
||||
&tooltip_builder.gui_handler(),
|
||||
gui_handler,
|
||||
include_str!("../../resources/jewel_tooltip.xml"),
|
||||
)?;
|
||||
|
||||
|
@ -108,7 +108,7 @@ impl Jewel {
|
|||
Ok(Tooltip::new(
|
||||
main_grid,
|
||||
inspector_snippet,
|
||||
tooltip_builder.gui_handler().clone(),
|
||||
gui_handler.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use engine::prelude::*;
|
|||
|
||||
use crate::components::inventory::Storable;
|
||||
|
||||
use super::{ItemSystem, Rarities, ToolTipBuilder};
|
||||
use super::{ItemSystem, Rarities};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MapItem {
|
||||
|
@ -27,7 +27,7 @@ impl MapItem {
|
|||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
_tooltip_builder: &impl ToolTipBuilder,
|
||||
_gui_handler: &Arc<GuiHandler>,
|
||||
_position: (i32, i32),
|
||||
) -> Result<Arc<GuiBuilder>> {
|
||||
todo!()
|
||||
|
|
|
@ -2,10 +2,6 @@ use anyhow::{bail, Result};
|
|||
use engine::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait ToolTipBuilder {
|
||||
fn gui_handler(&self) -> Arc<GuiHandler>;
|
||||
}
|
||||
|
||||
pub struct FittingResult {
|
||||
pub fit: bool,
|
||||
pub start: i32,
|
||||
|
|
Loading…
Reference in a new issue