Start moving character menu into crate

This commit is contained in:
hodasemi 2024-08-25 14:30:03 +02:00
parent eb543bab61
commit 1449a73d2c
24 changed files with 3800 additions and 41 deletions

View file

@ -19,7 +19,9 @@ members = [
"scene_update_macros",
"transaction_derive",
"map",
"rpg_components", "entity_manager",
"rpg_components",
"entity_manager",
"character_window",
]
[workspace.dependencies]

View 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" }

View 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>

View 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>

View 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
}
}

View 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()
}
}

View 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,
})
}
}

View 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)
}
}

View 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()
}
}

View 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()
}
}

View 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))
};
};
}
}

View 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
}
}

View 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
}
}

View 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
View 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(())
}
}

View 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
}
}

View 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>;
}

View file

@ -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,

View file

@ -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()))
}
}

View file

@ -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()))
}
}

View file

@ -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(),
))
}
}

View file

@ -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(),
))
}
}

View file

@ -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!()

View file

@ -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,