mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
During a trade, allow requesting items from the counterparty's inventory (prequisite for NPC trading).
This commit is contained in:
parent
b9b36e0bf5
commit
7e458ecd40
@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Improved network efficiency by ≈ factor 10 by using tokio.
|
- Improved network efficiency by ≈ factor 10 by using tokio.
|
||||||
- Added item tooltips to trade window.
|
- Added item tooltips to trade window.
|
||||||
- "Quest" given to new players converted to being a short tutorial
|
- "Quest" given to new players converted to being a short tutorial
|
||||||
|
- Items can be requested from the counterparty's inventory during trade.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
(
|
(
|
||||||
string_map: {
|
string_map: {
|
||||||
"hud.trade.trade_window": "Trade window",
|
"hud.trade.trade_window": "Trade window",
|
||||||
"hud.trade.phase1_description": "Drag the items you want to trade\n into your area.",
|
"hud.trade.phase1_description": "Drag the items you want to trade\n into the corresponding area.",
|
||||||
"hud.trade.phase2_description": "The trade is now locked to give you\n time to review it.",
|
"hud.trade.phase2_description": "The trade is now locked to give you\n time to review it.",
|
||||||
/// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness
|
/// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness
|
||||||
"hud.trade.phase3_description": "Trade is being processed.",
|
"hud.trade.phase3_description": "Trade is being processed.",
|
||||||
|
@ -21,10 +21,12 @@ pub enum TradeAction {
|
|||||||
AddItem {
|
AddItem {
|
||||||
item: InvSlotId,
|
item: InvSlotId,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
|
ours: bool,
|
||||||
},
|
},
|
||||||
RemoveItem {
|
RemoveItem {
|
||||||
item: InvSlotId,
|
item: InvSlotId,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
|
ours: bool,
|
||||||
},
|
},
|
||||||
/// Accept needs the phase indicator to avoid progressing too far in the
|
/// Accept needs the phase indicator to avoid progressing too far in the
|
||||||
/// trade if there's latency and a player presses the accept button
|
/// trade if there's latency and a player presses the accept button
|
||||||
@ -118,16 +120,26 @@ impl PendingTrade {
|
|||||||
/// - Modifications can only happen in phase 1
|
/// - Modifications can only happen in phase 1
|
||||||
/// - Whenever a trade is modified, both accept flags get reset
|
/// - Whenever a trade is modified, both accept flags get reset
|
||||||
/// - Accept flags only get set for the current phase
|
/// - Accept flags only get set for the current phase
|
||||||
pub fn process_trade_action(&mut self, who: usize, action: TradeAction, inventory: &Inventory) {
|
pub fn process_trade_action(
|
||||||
|
&mut self,
|
||||||
|
mut who: usize,
|
||||||
|
action: TradeAction,
|
||||||
|
inventories: &[&Inventory],
|
||||||
|
) {
|
||||||
use TradeAction::*;
|
use TradeAction::*;
|
||||||
match action {
|
match action {
|
||||||
AddItem {
|
AddItem {
|
||||||
item,
|
item,
|
||||||
quantity: delta,
|
quantity: delta,
|
||||||
|
ours,
|
||||||
} => {
|
} => {
|
||||||
if self.phase() == TradePhase::Mutate && delta > 0 {
|
if self.phase() == TradePhase::Mutate && delta > 0 {
|
||||||
|
if !ours {
|
||||||
|
who = 1 - who;
|
||||||
|
}
|
||||||
let total = self.offers[who].entry(item).or_insert(0);
|
let total = self.offers[who].entry(item).or_insert(0);
|
||||||
let owned_quantity = inventory.get(item).map(|i| i.amount()).unwrap_or(0);
|
let owned_quantity =
|
||||||
|
inventories[who].get(item).map(|i| i.amount()).unwrap_or(0);
|
||||||
*total = total.saturating_add(delta).min(owned_quantity);
|
*total = total.saturating_add(delta).min(owned_quantity);
|
||||||
self.accept_flags = [false, false];
|
self.accept_flags = [false, false];
|
||||||
}
|
}
|
||||||
@ -135,8 +147,12 @@ impl PendingTrade {
|
|||||||
RemoveItem {
|
RemoveItem {
|
||||||
item,
|
item,
|
||||||
quantity: delta,
|
quantity: delta,
|
||||||
|
ours,
|
||||||
} => {
|
} => {
|
||||||
if self.phase() == TradePhase::Mutate {
|
if self.phase() == TradePhase::Mutate {
|
||||||
|
if !ours {
|
||||||
|
who = 1 - who;
|
||||||
|
}
|
||||||
self.offers[who]
|
self.offers[who]
|
||||||
.entry(item)
|
.entry(item)
|
||||||
.and_replace_entry_with(|_, mut total| {
|
.and_replace_entry_with(|_, mut total| {
|
||||||
@ -180,17 +196,24 @@ impl Trades {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_trade_action(
|
pub fn process_trade_action<'a, F: Fn(Uid) -> Option<&'a Inventory>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: TradeId,
|
id: TradeId,
|
||||||
who: Uid,
|
who: Uid,
|
||||||
action: TradeAction,
|
action: TradeAction,
|
||||||
inventory: &Inventory,
|
get_inventory: F,
|
||||||
) {
|
) {
|
||||||
trace!("for trade id {:?}, message {:?}", id, action);
|
trace!("for trade id {:?}, message {:?}", id, action);
|
||||||
if let Some(trade) = self.trades.get_mut(&id) {
|
if let Some(trade) = self.trades.get_mut(&id) {
|
||||||
if let Some(party) = trade.which_party(who) {
|
if let Some(party) = trade.which_party(who) {
|
||||||
trade.process_trade_action(party, action, inventory);
|
let mut inventories = Vec::new();
|
||||||
|
for party in trade.parties.iter() {
|
||||||
|
match get_inventory(*party) {
|
||||||
|
Some(inventory) => inventories.push(inventory),
|
||||||
|
None => return,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trade.process_trade_action(party, action, &*inventories);
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"An entity who is not a party to trade {:?} tried to modify it",
|
"An entity who is not a party to trade {:?} tried to modify it",
|
||||||
|
@ -3,7 +3,10 @@ use common::{
|
|||||||
comp::inventory::{item::MaterialStatManifest, Inventory},
|
comp::inventory::{item::MaterialStatManifest, Inventory},
|
||||||
trade::{PendingTrade, TradeAction, TradeId, TradeResult, Trades},
|
trade::{PendingTrade, TradeAction, TradeId, TradeResult, Trades},
|
||||||
};
|
};
|
||||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
use common_net::{
|
||||||
|
msg::ServerGeneral,
|
||||||
|
sync::{Uid, WorldSyncExt},
|
||||||
|
};
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use specs::{world::WorldExt, Entity as EcsEntity};
|
use specs::{world::WorldExt, Entity as EcsEntity};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@ -26,8 +29,17 @@ pub fn handle_process_trade_action(
|
|||||||
server.notify_client(e, ServerGeneral::FinishedTrade(TradeResult::Declined))
|
server.notify_client(e, ServerGeneral::FinishedTrade(TradeResult::Declined))
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if let Some(inv) = server.state.ecs().read_component::<Inventory>().get(entity) {
|
{
|
||||||
trades.process_trade_action(trade_id, uid, action, inv);
|
let ecs = server.state.ecs();
|
||||||
|
let inventories = ecs.read_component::<Inventory>();
|
||||||
|
let get_inventory = |uid: Uid| {
|
||||||
|
if let Some(entity) = ecs.entity_from_uid(uid.0) {
|
||||||
|
inventories.get(entity)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trades.process_trade_action(trade_id, uid, action, get_inventory);
|
||||||
}
|
}
|
||||||
if let Entry::Occupied(entry) = trades.trades.entry(trade_id) {
|
if let Entry::Occupied(entry) = trades.trades.entry(trade_id) {
|
||||||
let parties = entry.get().parties;
|
let parties = entry.get().parties;
|
||||||
|
@ -17,23 +17,25 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
|
assets::AssetExt,
|
||||||
combat::{combat_rating, Damage},
|
combat::{combat_rating, Damage},
|
||||||
comp::{
|
comp::{
|
||||||
item::{MaterialStatManifest, Quality},
|
item::{ItemDef, MaterialStatManifest, Quality},
|
||||||
Body, Energy, Health, Stats,
|
Body, Energy, Health, Inventory, Stats,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
|
widget::{self, Button, Image, Rectangle, Scrollbar, State as ConrodState, Text},
|
||||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::hud::slots::SlotKind;
|
use crate::hud::slots::SlotKind;
|
||||||
|
use std::sync::Arc;
|
||||||
use vek::Vec2;
|
use vek::Vec2;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
pub struct Ids {
|
pub struct InventoryScrollerIds {
|
||||||
test,
|
test,
|
||||||
bag_close,
|
bag_close,
|
||||||
inv_alignment,
|
inv_alignment,
|
||||||
@ -53,6 +55,351 @@ widget_ids! {
|
|||||||
inventory_title_bg,
|
inventory_title_bg,
|
||||||
scrollbar_bg,
|
scrollbar_bg,
|
||||||
scrollbar_slots,
|
scrollbar_slots,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InventoryScrollerState {
|
||||||
|
ids: InventoryScrollerIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(WidgetCommon)]
|
||||||
|
pub struct InventoryScroller<'a> {
|
||||||
|
imgs: &'a Imgs,
|
||||||
|
item_imgs: &'a ItemImgs,
|
||||||
|
fonts: &'a Fonts,
|
||||||
|
#[conrod(common_builder)]
|
||||||
|
common: widget::CommonBuilder,
|
||||||
|
tooltip_manager: &'a mut TooltipManager,
|
||||||
|
slot_manager: &'a mut SlotManager,
|
||||||
|
pulse: f32,
|
||||||
|
localized_strings: &'a Localization,
|
||||||
|
show_stats: bool,
|
||||||
|
show_bag_inv: bool,
|
||||||
|
msm: &'a MaterialStatManifest,
|
||||||
|
on_right: bool,
|
||||||
|
item_tooltip: &'a Tooltip<'a>,
|
||||||
|
playername: String,
|
||||||
|
is_us: bool,
|
||||||
|
inventory: &'a Inventory,
|
||||||
|
bg_ids: &'a BackgroundIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> InventoryScroller<'a> {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn new(
|
||||||
|
imgs: &'a Imgs,
|
||||||
|
item_imgs: &'a ItemImgs,
|
||||||
|
fonts: &'a Fonts,
|
||||||
|
tooltip_manager: &'a mut TooltipManager,
|
||||||
|
slot_manager: &'a mut SlotManager,
|
||||||
|
pulse: f32,
|
||||||
|
localized_strings: &'a Localization,
|
||||||
|
show_stats: bool,
|
||||||
|
show_bag_inv: bool,
|
||||||
|
msm: &'a MaterialStatManifest,
|
||||||
|
on_right: bool,
|
||||||
|
item_tooltip: &'a Tooltip<'a>,
|
||||||
|
playername: String,
|
||||||
|
is_us: bool,
|
||||||
|
inventory: &'a Inventory,
|
||||||
|
bg_ids: &'a BackgroundIds,
|
||||||
|
) -> Self {
|
||||||
|
InventoryScroller {
|
||||||
|
imgs,
|
||||||
|
item_imgs,
|
||||||
|
fonts,
|
||||||
|
common: widget::CommonBuilder::default(),
|
||||||
|
tooltip_manager,
|
||||||
|
slot_manager,
|
||||||
|
pulse,
|
||||||
|
localized_strings,
|
||||||
|
show_stats,
|
||||||
|
show_bag_inv,
|
||||||
|
msm,
|
||||||
|
on_right,
|
||||||
|
item_tooltip,
|
||||||
|
playername,
|
||||||
|
is_us,
|
||||||
|
inventory,
|
||||||
|
bg_ids,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn background(&mut self, ui: &mut UiCell<'_>) {
|
||||||
|
let mut bg = Image::new(if self.show_stats {
|
||||||
|
self.imgs.inv_bg_stats
|
||||||
|
} else if self.show_bag_inv {
|
||||||
|
self.imgs.inv_bg_bag
|
||||||
|
} else {
|
||||||
|
self.imgs.inv_bg_armor
|
||||||
|
})
|
||||||
|
.w_h(424.0, 708.0);
|
||||||
|
if self.on_right {
|
||||||
|
bg = bg.bottom_right_with_margins_on(ui.window, 60.0, 5.0);
|
||||||
|
} else {
|
||||||
|
bg = bg.bottom_left_with_margins_on(ui.window, 60.0, 5.0);
|
||||||
|
}
|
||||||
|
bg.color(Some(UI_MAIN)).set(self.bg_ids.bg, ui);
|
||||||
|
Image::new(if self.show_bag_inv {
|
||||||
|
self.imgs.inv_frame_bag
|
||||||
|
} else {
|
||||||
|
self.imgs.inv_frame
|
||||||
|
})
|
||||||
|
.w_h(424.0, 708.0)
|
||||||
|
.middle_of(self.bg_ids.bg)
|
||||||
|
.color(Some(UI_HIGHLIGHT_0))
|
||||||
|
.set(self.bg_ids.bg_frame, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(&mut self, state: &mut ConrodState<'_, InventoryScrollerState>, ui: &mut UiCell<'_>) {
|
||||||
|
Text::new(
|
||||||
|
&self
|
||||||
|
.localized_strings
|
||||||
|
.get("hud.bag.inventory")
|
||||||
|
.replace("{playername}", &*self.playername),
|
||||||
|
)
|
||||||
|
.mid_top_with_margin_on(self.bg_ids.bg_frame, 9.0)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(self.fonts.cyri.scale(22))
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.set(state.ids.inventory_title_bg, ui);
|
||||||
|
Text::new(
|
||||||
|
&self
|
||||||
|
.localized_strings
|
||||||
|
.get("hud.bag.inventory")
|
||||||
|
.replace("{playername}", &*self.playername),
|
||||||
|
)
|
||||||
|
.top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(self.fonts.cyri.scale(22))
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.inventory_title, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scrollbar_and_slots(
|
||||||
|
&mut self,
|
||||||
|
state: &mut ConrodState<'_, InventoryScrollerState>,
|
||||||
|
ui: &mut UiCell<'_>,
|
||||||
|
) {
|
||||||
|
let space_max = self.inventory.slots().count();
|
||||||
|
// Slots Scrollbar
|
||||||
|
if space_max > 45 && !self.show_bag_inv {
|
||||||
|
// Scrollbar-BG
|
||||||
|
Image::new(self.imgs.scrollbar_bg)
|
||||||
|
.w_h(9.0, 173.0)
|
||||||
|
.bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
|
||||||
|
.color(Some(UI_HIGHLIGHT_0))
|
||||||
|
.set(state.ids.scrollbar_bg, ui);
|
||||||
|
// Scrollbar
|
||||||
|
Scrollbar::y_axis(state.ids.inv_alignment)
|
||||||
|
.thickness(5.0)
|
||||||
|
.h(123.0)
|
||||||
|
.color(UI_MAIN)
|
||||||
|
.middle_of(state.ids.scrollbar_bg)
|
||||||
|
.set(state.ids.scrollbar_slots, ui);
|
||||||
|
} else if space_max > 135 {
|
||||||
|
// Scrollbar-BG
|
||||||
|
Image::new(self.imgs.scrollbar_bg_big)
|
||||||
|
.w_h(9.0, 592.0)
|
||||||
|
.bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
|
||||||
|
.color(Some(UI_HIGHLIGHT_0))
|
||||||
|
.set(state.ids.scrollbar_bg, ui);
|
||||||
|
// Scrollbar
|
||||||
|
Scrollbar::y_axis(state.ids.inv_alignment)
|
||||||
|
.thickness(5.0)
|
||||||
|
.h(542.0)
|
||||||
|
.color(UI_MAIN)
|
||||||
|
.middle_of(state.ids.scrollbar_bg)
|
||||||
|
.set(state.ids.scrollbar_slots, ui);
|
||||||
|
};
|
||||||
|
// Alignment for Grid
|
||||||
|
Rectangle::fill_with(
|
||||||
|
[362.0, if self.show_bag_inv { 600.0 } else { 200.0 }],
|
||||||
|
color::TRANSPARENT,
|
||||||
|
)
|
||||||
|
.bottom_left_with_margins_on(self.bg_ids.bg_frame, 29.0, 46.5)
|
||||||
|
.scroll_kids_vertically()
|
||||||
|
.set(state.ids.inv_alignment, ui);
|
||||||
|
|
||||||
|
// Bag Slots
|
||||||
|
// Create available inventory slot widgets
|
||||||
|
if state.ids.inv_slots.len() < self.inventory.capacity() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.inv_slots
|
||||||
|
.resize(self.inventory.capacity(), &mut ui.widget_id_generator());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Determine the range of inventory slots that are provided by the loadout item
|
||||||
|
// that the mouse is over
|
||||||
|
let mouseover_loadout_slots = self
|
||||||
|
.slot_manager
|
||||||
|
.mouse_over_slot
|
||||||
|
.and_then(|x| {
|
||||||
|
if let SlotKind::Equip(e) = x {
|
||||||
|
self.inventory.get_slot_range_for_equip_slot(e)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(0usize..0usize);
|
||||||
|
|
||||||
|
// Display inventory contents
|
||||||
|
let mut slot_maker = SlotMaker {
|
||||||
|
empty_slot: self.imgs.inv_slot,
|
||||||
|
filled_slot: self.imgs.inv_slot,
|
||||||
|
selected_slot: self.imgs.inv_slot_sel,
|
||||||
|
background_color: Some(UI_MAIN),
|
||||||
|
content_size: ContentSize {
|
||||||
|
width_height_ratio: 1.0,
|
||||||
|
max_fraction: 0.75,
|
||||||
|
},
|
||||||
|
selected_content_scale: 1.067,
|
||||||
|
amount_font: self.fonts.cyri.conrod_id,
|
||||||
|
amount_margins: Vec2::new(-4.0, 0.0),
|
||||||
|
amount_font_size: self.fonts.cyri.scale(12),
|
||||||
|
amount_text_color: TEXT_COLOR,
|
||||||
|
content_source: self.inventory,
|
||||||
|
image_source: self.item_imgs,
|
||||||
|
slot_manager: Some(self.slot_manager),
|
||||||
|
pulse: self.pulse,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, (pos, item)) in self.inventory.slots_with_id().enumerate() {
|
||||||
|
let x = i % 9;
|
||||||
|
let y = i / 9;
|
||||||
|
|
||||||
|
// Slot
|
||||||
|
let mut slot_widget = slot_maker
|
||||||
|
.fabricate(
|
||||||
|
InventorySlot {
|
||||||
|
slot: pos,
|
||||||
|
ours: self.is_us,
|
||||||
|
},
|
||||||
|
[40.0; 2],
|
||||||
|
)
|
||||||
|
.top_left_with_margins_on(
|
||||||
|
state.ids.inv_alignment,
|
||||||
|
0.0 + y as f64 * (40.0),
|
||||||
|
0.0 + x as f64 * (40.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Highlight slots are provided by the loadout item that the mouse is over
|
||||||
|
if mouseover_loadout_slots.contains(&i) {
|
||||||
|
slot_widget = slot_widget.with_background_color(Color::Rgba(1.0, 1.0, 1.0, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item) = item {
|
||||||
|
let (title, desc) = super::util::item_text(item, &self.msm);
|
||||||
|
let quality_col = get_quality_col(item);
|
||||||
|
let quality_col_img = match item.quality() {
|
||||||
|
Quality::Low => self.imgs.inv_slot_grey,
|
||||||
|
Quality::Common => self.imgs.inv_slot,
|
||||||
|
Quality::Moderate => self.imgs.inv_slot_green,
|
||||||
|
Quality::High => self.imgs.inv_slot_blue,
|
||||||
|
Quality::Epic => self.imgs.inv_slot_purple,
|
||||||
|
Quality::Legendary => self.imgs.inv_slot_gold,
|
||||||
|
Quality::Artifact => self.imgs.inv_slot_orange,
|
||||||
|
_ => self.imgs.inv_slot_red,
|
||||||
|
};
|
||||||
|
slot_widget
|
||||||
|
.filled_slot(quality_col_img)
|
||||||
|
.with_tooltip(
|
||||||
|
self.tooltip_manager,
|
||||||
|
title,
|
||||||
|
&*desc,
|
||||||
|
self.item_tooltip,
|
||||||
|
quality_col,
|
||||||
|
)
|
||||||
|
.set(state.ids.inv_slots[i], ui);
|
||||||
|
} else {
|
||||||
|
slot_widget.set(state.ids.inv_slots[i], ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn footer_metrics(
|
||||||
|
&mut self,
|
||||||
|
state: &mut ConrodState<'_, InventoryScrollerState>,
|
||||||
|
ui: &mut UiCell<'_>,
|
||||||
|
) {
|
||||||
|
let space_used = self.inventory.populated_slots();
|
||||||
|
let space_max = self.inventory.slots().count();
|
||||||
|
let bag_space = format!("{}/{}", space_used, space_max);
|
||||||
|
let bag_space_percentage = space_used as f32 / space_max as f32;
|
||||||
|
let coin_itemdef = Arc::<ItemDef>::load_expect_cloned("common.items.utility.coins");
|
||||||
|
let currency = self.inventory.item_count(&coin_itemdef);
|
||||||
|
|
||||||
|
// Coin Icon and Currency Text
|
||||||
|
Image::new(self.imgs.coin_ico)
|
||||||
|
.w_h(16.0, 17.0)
|
||||||
|
.bottom_left_with_margins_on(self.bg_ids.bg_frame, 2.0, 43.0)
|
||||||
|
.set(state.ids.coin_ico, ui);
|
||||||
|
Text::new(&format!("{}", currency))
|
||||||
|
.bottom_left_with_margins_on(self.bg_ids.bg_frame, 6.0, 64.0)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
|
.color(Color::Rgba(0.871, 0.863, 0.05, 1.0))
|
||||||
|
.set(state.ids.currency_txt, ui);
|
||||||
|
//Free Bag-Space
|
||||||
|
Text::new(&bag_space)
|
||||||
|
.bottom_right_with_margins_on(self.bg_ids.bg_frame, 6.0, 43.0)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
|
.color(if bag_space_percentage < 0.8 {
|
||||||
|
TEXT_COLOR
|
||||||
|
} else if bag_space_percentage < 1.0 {
|
||||||
|
LOW_HP_COLOR
|
||||||
|
} else {
|
||||||
|
CRITICAL_HP_COLOR
|
||||||
|
})
|
||||||
|
.set(state.ids.space_txt, ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Widget for InventoryScroller<'a> {
|
||||||
|
type Event = ();
|
||||||
|
type State = InventoryScrollerState;
|
||||||
|
type Style = ();
|
||||||
|
|
||||||
|
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||||
|
InventoryScrollerState {
|
||||||
|
ids: InventoryScrollerIds::new(id_gen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self) -> Self::Style {}
|
||||||
|
|
||||||
|
fn update(mut self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
|
let widget::UpdateArgs { state, ui, .. } = args;
|
||||||
|
self.background(ui);
|
||||||
|
self.title(state, ui);
|
||||||
|
self.scrollbar_and_slots(state, ui);
|
||||||
|
self.footer_metrics(state, ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
widget_ids! {
|
||||||
|
pub struct BackgroundIds {
|
||||||
|
bg,
|
||||||
|
bg_frame,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
widget_ids! {
|
||||||
|
pub struct BagIds {
|
||||||
|
test,
|
||||||
|
inventory_scroller,
|
||||||
|
bag_close,
|
||||||
|
//tooltip[],
|
||||||
|
char_ico,
|
||||||
|
coin_ico,
|
||||||
|
space_txt,
|
||||||
|
currency_txt,
|
||||||
|
inventory_title,
|
||||||
|
inventory_title_bg,
|
||||||
|
scrollbar_bg,
|
||||||
|
scrollbar_slots,
|
||||||
tab_1,
|
tab_1,
|
||||||
tab_2,
|
tab_2,
|
||||||
tab_3,
|
tab_3,
|
||||||
@ -148,8 +495,9 @@ impl<'a> Bag<'a> {
|
|||||||
}
|
}
|
||||||
const STATS: [&str; 4] = ["Health", "Stamina", "Protection", "Combat Rating"];
|
const STATS: [&str; 4] = ["Health", "Stamina", "Protection", "Combat Rating"];
|
||||||
|
|
||||||
pub struct State {
|
pub struct BagState {
|
||||||
ids: Ids,
|
ids: BagIds,
|
||||||
|
bg_ids: BackgroundIds,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
@ -159,12 +507,16 @@ pub enum Event {
|
|||||||
|
|
||||||
impl<'a> Widget for Bag<'a> {
|
impl<'a> Widget for Bag<'a> {
|
||||||
type Event = Option<Event>;
|
type Event = Option<Event>;
|
||||||
type State = State;
|
type State = BagState;
|
||||||
type Style = ();
|
type Style = ();
|
||||||
|
|
||||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
|
||||||
State {
|
BagState {
|
||||||
ids: Ids::new(id_gen),
|
bg_ids: BackgroundIds {
|
||||||
|
bg: id_gen.next(),
|
||||||
|
bg_frame: id_gen.next(),
|
||||||
|
},
|
||||||
|
ids: BagIds::new(id_gen),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,12 +551,6 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let space_used = inventory.populated_slots();
|
|
||||||
let space_max = inventory.slots().count();
|
|
||||||
let bag_space = format!("{}/{}", space_used, space_max);
|
|
||||||
let bag_space_percentage = space_used as f32 / space_max as f32;
|
|
||||||
let currency = 0; // TODO: Add as a Stat
|
|
||||||
|
|
||||||
// Tooltips
|
// Tooltips
|
||||||
let item_tooltip = Tooltip::new({
|
let item_tooltip = Tooltip::new({
|
||||||
// Edge images [t, b, r, l]
|
// Edge images [t, b, r, l]
|
||||||
@ -223,117 +569,32 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
.desc_font_size(self.fonts.cyri.scale(12))
|
.desc_font_size(self.fonts.cyri.scale(12))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.desc_text_color(TEXT_COLOR);
|
.desc_text_color(TEXT_COLOR);
|
||||||
// BG
|
|
||||||
Image::new(if self.show.stats {
|
InventoryScroller::new(
|
||||||
self.imgs.inv_bg_stats
|
self.imgs,
|
||||||
} else if self.show.bag_inv {
|
self.item_imgs,
|
||||||
self.imgs.inv_bg_bag
|
self.fonts,
|
||||||
} else {
|
self.tooltip_manager,
|
||||||
self.imgs.inv_bg_armor
|
self.slot_manager,
|
||||||
})
|
self.pulse,
|
||||||
.w_h(424.0, 708.0)
|
self.localized_strings,
|
||||||
.bottom_right_with_margins_on(ui.window, 60.0, 5.0)
|
self.show.stats,
|
||||||
.color(Some(UI_MAIN))
|
self.show.bag_inv,
|
||||||
.set(state.ids.bg, ui);
|
self.msm,
|
||||||
Image::new(if self.show.bag_inv {
|
true,
|
||||||
self.imgs.inv_frame_bag
|
&item_tooltip,
|
||||||
} else {
|
self.stats.name.to_string(),
|
||||||
self.imgs.inv_frame
|
true,
|
||||||
})
|
&inventory,
|
||||||
.w_h(424.0, 708.0)
|
&state.bg_ids,
|
||||||
.middle_of(state.ids.bg)
|
|
||||||
.color(Some(UI_HIGHLIGHT_0))
|
|
||||||
.set(state.ids.bg_frame, ui);
|
|
||||||
// Title
|
|
||||||
Text::new(
|
|
||||||
&self
|
|
||||||
.localized_strings
|
|
||||||
.get("hud.bag.inventory")
|
|
||||||
.replace("{playername}", &self.stats.name.to_string().as_str()),
|
|
||||||
)
|
)
|
||||||
.mid_top_with_margin_on(state.ids.bg_frame, 9.0)
|
.set(state.ids.inventory_scroller, ui);
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(self.fonts.cyri.scale(20))
|
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
|
||||||
.set(state.ids.inventory_title_bg, ui);
|
|
||||||
Text::new(
|
|
||||||
&self
|
|
||||||
.localized_strings
|
|
||||||
.get("hud.bag.inventory")
|
|
||||||
.replace("{playername}", &self.stats.name.to_string().as_str()),
|
|
||||||
)
|
|
||||||
.top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(self.fonts.cyri.scale(20))
|
|
||||||
.color(TEXT_COLOR)
|
|
||||||
.set(state.ids.inventory_title, ui);
|
|
||||||
// Slots Scrollbar
|
|
||||||
if space_max > 45 && !self.show.bag_inv {
|
|
||||||
// Scrollbar-BG
|
|
||||||
Image::new(self.imgs.scrollbar_bg)
|
|
||||||
.w_h(9.0, 173.0)
|
|
||||||
.bottom_right_with_margins_on(state.ids.bg_frame, 42.0, 3.0)
|
|
||||||
.color(Some(UI_HIGHLIGHT_0))
|
|
||||||
.set(state.ids.scrollbar_bg, ui);
|
|
||||||
// Scrollbar
|
|
||||||
Scrollbar::y_axis(state.ids.inv_alignment)
|
|
||||||
.thickness(5.0)
|
|
||||||
.h(123.0)
|
|
||||||
.color(UI_MAIN)
|
|
||||||
.middle_of(state.ids.scrollbar_bg)
|
|
||||||
.set(state.ids.scrollbar_slots, ui);
|
|
||||||
} else if space_max > 135 {
|
|
||||||
// Scrollbar-BG
|
|
||||||
Image::new(self.imgs.scrollbar_bg_big)
|
|
||||||
.w_h(9.0, 592.0)
|
|
||||||
.bottom_right_with_margins_on(state.ids.bg_frame, 42.0, 3.0)
|
|
||||||
.color(Some(UI_HIGHLIGHT_0))
|
|
||||||
.set(state.ids.scrollbar_bg, ui);
|
|
||||||
// Scrollbar
|
|
||||||
Scrollbar::y_axis(state.ids.inv_alignment)
|
|
||||||
.thickness(5.0)
|
|
||||||
.h(542.0)
|
|
||||||
.color(UI_MAIN)
|
|
||||||
.middle_of(state.ids.scrollbar_bg)
|
|
||||||
.set(state.ids.scrollbar_slots, ui);
|
|
||||||
};
|
|
||||||
// Char Pixel-Art
|
// Char Pixel-Art
|
||||||
Image::new(self.imgs.char_art)
|
Image::new(self.imgs.char_art)
|
||||||
.w_h(40.0, 37.0)
|
.w_h(40.0, 37.0)
|
||||||
.top_left_with_margins_on(state.ids.bg, 4.0, 2.0)
|
.top_left_with_margins_on(state.bg_ids.bg, 4.0, 2.0)
|
||||||
.set(state.ids.char_ico, ui);
|
.set(state.ids.char_ico, ui);
|
||||||
// Coin Icon and Currency Text
|
|
||||||
Image::new(self.imgs.coin_ico)
|
|
||||||
.w_h(16.0, 17.0)
|
|
||||||
.bottom_left_with_margins_on(state.ids.bg_frame, 2.0, 43.0)
|
|
||||||
.set(state.ids.coin_ico, ui);
|
|
||||||
Text::new(&format!("{}", currency))
|
|
||||||
.bottom_left_with_margins_on(state.ids.bg_frame, 6.0, 64.0)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(self.fonts.cyri.scale(14))
|
|
||||||
.color(Color::Rgba(0.871, 0.863, 0.05, 1.0))
|
|
||||||
.set(state.ids.currency_txt, ui);
|
|
||||||
//Free Bag-Space
|
|
||||||
Text::new(&bag_space)
|
|
||||||
.bottom_right_with_margins_on(state.ids.bg_frame, 6.0, 43.0)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(self.fonts.cyri.scale(14))
|
|
||||||
.color(if bag_space_percentage < 0.8 {
|
|
||||||
TEXT_COLOR
|
|
||||||
} else if bag_space_percentage < 1.0 {
|
|
||||||
LOW_HP_COLOR
|
|
||||||
} else {
|
|
||||||
CRITICAL_HP_COLOR
|
|
||||||
})
|
|
||||||
.set(state.ids.space_txt, ui);
|
|
||||||
// Alignment for Grid
|
|
||||||
Rectangle::fill_with(
|
|
||||||
[362.0, if self.show.bag_inv { 600.0 } else { 200.0 }],
|
|
||||||
color::TRANSPARENT,
|
|
||||||
)
|
|
||||||
.bottom_left_with_margins_on(state.ids.bg_frame, 29.0, 46.5)
|
|
||||||
.scroll_kids_vertically()
|
|
||||||
.set(state.ids.inv_alignment, ui);
|
|
||||||
// Button to expand bag
|
// Button to expand bag
|
||||||
let txt = if self.show.bag_inv {
|
let txt = if self.show.bag_inv {
|
||||||
"Show Loadout"
|
"Show Loadout"
|
||||||
@ -357,9 +618,10 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
self.imgs.expand_btn_press
|
self.imgs.expand_btn_press
|
||||||
});
|
});
|
||||||
// Only show expand button when it's needed...
|
// Only show expand button when it's needed...
|
||||||
|
let space_max = inventory.slots().count();
|
||||||
if space_max > 45 && !self.show.bag_inv {
|
if space_max > 45 && !self.show.bag_inv {
|
||||||
if expand_btn
|
if expand_btn
|
||||||
.top_left_with_margins_on(state.ids.bg_frame, 460.0, 211.5)
|
.top_left_with_margins_on(state.bg_ids.bg_frame, 460.0, 211.5)
|
||||||
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
||||||
.set(state.ids.bag_expand_btn, ui)
|
.set(state.ids.bag_expand_btn, ui)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
@ -369,7 +631,7 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
} else if self.show.bag_inv {
|
} else if self.show.bag_inv {
|
||||||
//... but always show it when the bag is expanded
|
//... but always show it when the bag is expanded
|
||||||
if expand_btn
|
if expand_btn
|
||||||
.top_left_with_margins_on(state.ids.bg_frame, 53.0, 211.5)
|
.top_left_with_margins_on(state.bg_ids.bg_frame, 53.0, 211.5)
|
||||||
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
||||||
.set(state.ids.bag_expand_btn, ui)
|
.set(state.ids.bag_expand_btn, ui)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
@ -378,29 +640,6 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title
|
|
||||||
Text::new(
|
|
||||||
&self
|
|
||||||
.localized_strings
|
|
||||||
.get("hud.bag.inventory")
|
|
||||||
.replace("{playername}", &self.stats.name.to_string().as_str()),
|
|
||||||
)
|
|
||||||
.mid_top_with_margin_on(state.ids.bg_frame, 9.0)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(self.fonts.cyri.scale(22))
|
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
|
||||||
.set(state.ids.inventory_title_bg, ui);
|
|
||||||
Text::new(
|
|
||||||
&self
|
|
||||||
.localized_strings
|
|
||||||
.get("hud.bag.inventory")
|
|
||||||
.replace("{playername}", &self.stats.name.to_string().as_str()),
|
|
||||||
)
|
|
||||||
.top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(self.fonts.cyri.scale(22))
|
|
||||||
.color(TEXT_COLOR)
|
|
||||||
.set(state.ids.inventory_title, ui);
|
|
||||||
// Armor Slots
|
// Armor Slots
|
||||||
let mut slot_maker = SlotMaker {
|
let mut slot_maker = SlotMaker {
|
||||||
empty_slot: self.imgs.armor_slot_empty,
|
empty_slot: self.imgs.armor_slot_empty,
|
||||||
@ -464,7 +703,7 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
|
let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
|
||||||
|
|
||||||
let btn = if i.0 == 0 {
|
let btn = if i.0 == 0 {
|
||||||
btn.top_left_with_margins_on(state.ids.bg_frame, 55.0, 10.0)
|
btn.top_left_with_margins_on(state.bg_ids.bg_frame, 55.0, 10.0)
|
||||||
} else {
|
} else {
|
||||||
btn.down_from(state.ids.stat_icons[i.0 - 1], 7.0)
|
btn.down_from(state.ids.stat_icons[i.0 - 1], 7.0)
|
||||||
};
|
};
|
||||||
@ -517,7 +756,7 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
.unwrap_or(QUALITY_COMMON);
|
.unwrap_or(QUALITY_COMMON);
|
||||||
slot_maker
|
slot_maker
|
||||||
.fabricate(EquipSlot::Armor(ArmorSlot::Head), [45.0; 2])
|
.fabricate(EquipSlot::Armor(ArmorSlot::Head), [45.0; 2])
|
||||||
.mid_top_with_margin_on(state.ids.bg_frame, 60.0)
|
.mid_top_with_margin_on(state.bg_ids.bg_frame, 60.0)
|
||||||
.with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
|
.with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
|
||||||
.filled_slot(filled_slot)
|
.filled_slot(filled_slot)
|
||||||
.with_tooltip(
|
.with_tooltip(
|
||||||
@ -771,7 +1010,7 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
.unwrap_or(QUALITY_COMMON);
|
.unwrap_or(QUALITY_COMMON);
|
||||||
slot_maker
|
slot_maker
|
||||||
.fabricate(EquipSlot::Lantern, [45.0; 2])
|
.fabricate(EquipSlot::Lantern, [45.0; 2])
|
||||||
.top_right_with_margins_on(state.ids.bg_frame, 60.0, 5.0)
|
.top_right_with_margins_on(state.bg_ids.bg_frame, 60.0, 5.0)
|
||||||
.with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN))
|
.with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN))
|
||||||
.filled_slot(filled_slot)
|
.filled_slot(filled_slot)
|
||||||
.with_tooltip(
|
.with_tooltip(
|
||||||
@ -888,7 +1127,7 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
slot_maker
|
slot_maker
|
||||||
.fabricate(EquipSlot::Armor(ArmorSlot::Bag1), [35.0; 2])
|
.fabricate(EquipSlot::Armor(ArmorSlot::Bag1), [35.0; 2])
|
||||||
.bottom_left_with_margins_on(
|
.bottom_left_with_margins_on(
|
||||||
state.ids.bg_frame,
|
state.bg_ids.bg_frame,
|
||||||
if self.show.bag_inv { 600.0 } else { 167.0 },
|
if self.show.bag_inv { 600.0 } else { 167.0 },
|
||||||
3.0,
|
3.0,
|
||||||
)
|
)
|
||||||
@ -971,102 +1210,13 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
bag4_q_col,
|
bag4_q_col,
|
||||||
)
|
)
|
||||||
.set(state.ids.bag4_slot, ui);
|
.set(state.ids.bag4_slot, ui);
|
||||||
// Bag Slots
|
|
||||||
// Create available inventory slot widgets
|
|
||||||
if state.ids.inv_slots.len() < inventory.capacity() {
|
|
||||||
state.update(|s| {
|
|
||||||
s.ids
|
|
||||||
.inv_slots
|
|
||||||
.resize(inventory.capacity(), &mut ui.widget_id_generator());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the range of inventory slots that are provided by the loadout item
|
|
||||||
// that the mouse is over
|
|
||||||
let mouseover_loadout_slots = self
|
|
||||||
.slot_manager
|
|
||||||
.mouse_over_slot
|
|
||||||
.and_then(|x| {
|
|
||||||
if let SlotKind::Equip(e) = x {
|
|
||||||
inventory.get_slot_range_for_equip_slot(e)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(0usize..0usize);
|
|
||||||
|
|
||||||
// Display inventory contents
|
|
||||||
let mut slot_maker = SlotMaker {
|
|
||||||
empty_slot: self.imgs.inv_slot,
|
|
||||||
filled_slot: self.imgs.inv_slot,
|
|
||||||
selected_slot: self.imgs.inv_slot_sel,
|
|
||||||
background_color: Some(UI_MAIN),
|
|
||||||
content_size: ContentSize {
|
|
||||||
width_height_ratio: 1.0,
|
|
||||||
max_fraction: 0.75,
|
|
||||||
},
|
|
||||||
selected_content_scale: 1.067,
|
|
||||||
amount_font: self.fonts.cyri.conrod_id,
|
|
||||||
amount_margins: Vec2::new(-4.0, 0.0),
|
|
||||||
amount_font_size: self.fonts.cyri.scale(12),
|
|
||||||
amount_text_color: TEXT_COLOR,
|
|
||||||
content_source: inventory,
|
|
||||||
image_source: self.item_imgs,
|
|
||||||
slot_manager: Some(self.slot_manager),
|
|
||||||
pulse: self.pulse,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i, (pos, item)) in inventory.slots_with_id().enumerate() {
|
|
||||||
let x = i % 9;
|
|
||||||
let y = i / 9;
|
|
||||||
|
|
||||||
// Slot
|
|
||||||
let mut slot_widget = slot_maker
|
|
||||||
.fabricate(InventorySlot(pos), [40.0; 2])
|
|
||||||
.top_left_with_margins_on(
|
|
||||||
state.ids.inv_alignment,
|
|
||||||
0.0 + y as f64 * (40.0),
|
|
||||||
0.0 + x as f64 * (40.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Highlight slots are provided by the loadout item that the mouse is over
|
|
||||||
if mouseover_loadout_slots.contains(&i) {
|
|
||||||
slot_widget = slot_widget.with_background_color(Color::Rgba(1.0, 1.0, 1.0, 1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(item) = item {
|
|
||||||
let (title, desc) = super::util::item_text(item, &self.msm);
|
|
||||||
let quality_col = get_quality_col(item);
|
|
||||||
let quality_col_img = match item.quality() {
|
|
||||||
Quality::Low => self.imgs.inv_slot_grey,
|
|
||||||
Quality::Common => self.imgs.inv_slot,
|
|
||||||
Quality::Moderate => self.imgs.inv_slot_green,
|
|
||||||
Quality::High => self.imgs.inv_slot_blue,
|
|
||||||
Quality::Epic => self.imgs.inv_slot_purple,
|
|
||||||
Quality::Legendary => self.imgs.inv_slot_gold,
|
|
||||||
Quality::Artifact => self.imgs.inv_slot_orange,
|
|
||||||
_ => self.imgs.inv_slot_red,
|
|
||||||
};
|
|
||||||
slot_widget
|
|
||||||
.filled_slot(quality_col_img)
|
|
||||||
.with_tooltip(
|
|
||||||
self.tooltip_manager,
|
|
||||||
title,
|
|
||||||
&*desc,
|
|
||||||
&item_tooltip,
|
|
||||||
quality_col,
|
|
||||||
)
|
|
||||||
.set(state.ids.inv_slots[i], ui);
|
|
||||||
} else {
|
|
||||||
slot_widget.set(state.ids.inv_slots[i], ui);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Close button
|
// Close button
|
||||||
if Button::image(self.imgs.close_btn)
|
if Button::image(self.imgs.close_btn)
|
||||||
.w_h(24.0, 25.0)
|
.w_h(24.0, 25.0)
|
||||||
.hover_image(self.imgs.close_btn_hover)
|
.hover_image(self.imgs.close_btn_hover)
|
||||||
.press_image(self.imgs.close_btn_press)
|
.press_image(self.imgs.close_btn_press)
|
||||||
.top_right_with_margins_on(state.ids.bg, 0.0, 0.0)
|
.top_right_with_margins_on(state.bg_ids.bg, 0.0, 0.0)
|
||||||
.set(state.ids.bag_close, ui)
|
.set(state.ids.bag_close, ui)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
|
@ -2716,9 +2716,10 @@ impl Hud {
|
|||||||
// Maintain slot manager
|
// Maintain slot manager
|
||||||
for event in self.slot_manager.maintain(ui_widgets) {
|
for event in self.slot_manager.maintain(ui_widgets) {
|
||||||
use comp::slot::Slot;
|
use comp::slot::Slot;
|
||||||
use slots::SlotKind::*;
|
use slots::{InventorySlot, SlotKind::*};
|
||||||
let to_slot = |slot_kind| match slot_kind {
|
let to_slot = |slot_kind| match slot_kind {
|
||||||
Inventory(i) => Some(Slot::Inventory(i.0)),
|
Inventory(InventorySlot { slot, ours: true }) => Some(Slot::Inventory(slot)),
|
||||||
|
Inventory(InventorySlot { ours: false, .. }) => None,
|
||||||
Equip(e) => Some(Slot::Equip(e)),
|
Equip(e) => Some(Slot::Equip(e)),
|
||||||
Hotbar(_) => None,
|
Hotbar(_) => None,
|
||||||
Trade(_) => None,
|
Trade(_) => None,
|
||||||
@ -2732,29 +2733,37 @@ impl Hud {
|
|||||||
slot_b: b,
|
slot_b: b,
|
||||||
bypass_dialog: false,
|
bypass_dialog: false,
|
||||||
});
|
});
|
||||||
} else if let (Inventory(i), Hotbar(h)) = (a, b) {
|
} else if let (Inventory(InventorySlot { slot, ours: true }), Hotbar(h)) =
|
||||||
self.hotbar.add_inventory_link(h, i.0);
|
(a, b)
|
||||||
|
{
|
||||||
|
self.hotbar.add_inventory_link(h, slot);
|
||||||
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
||||||
} else if let (Hotbar(a), Hotbar(b)) = (a, b) {
|
} else if let (Hotbar(a), Hotbar(b)) = (a, b) {
|
||||||
self.hotbar.swap(a, b);
|
self.hotbar.swap(a, b);
|
||||||
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
||||||
} else if let (Inventory(i), Trade(_)) = (a, b) {
|
} else if let (Inventory(i), Trade(t)) = (a, b) {
|
||||||
if let Some(inventory) = inventories.get(entity) {
|
if i.ours == t.ours {
|
||||||
|
if let Some(inventory) = inventories.get(t.entity) {
|
||||||
events.push(Event::TradeAction(TradeAction::AddItem {
|
events.push(Event::TradeAction(TradeAction::AddItem {
|
||||||
item: i.0,
|
item: i.slot,
|
||||||
quantity: i.amount(inventory).unwrap_or(1),
|
quantity: i.amount(inventory).unwrap_or(1),
|
||||||
|
ours: i.ours,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else if let (Trade(t), Inventory(_)) = (a, b) {
|
}
|
||||||
if let Some(inventory) = inventories.get(entity) {
|
} else if let (Trade(t), Inventory(i)) = (a, b) {
|
||||||
|
if i.ours == t.ours {
|
||||||
|
if let Some(inventory) = inventories.get(t.entity) {
|
||||||
if let Some(invslot) = t.invslot {
|
if let Some(invslot) = t.invslot {
|
||||||
events.push(Event::TradeAction(TradeAction::RemoveItem {
|
events.push(Event::TradeAction(TradeAction::RemoveItem {
|
||||||
item: invslot,
|
item: invslot,
|
||||||
quantity: t.amount(inventory).unwrap_or(1),
|
quantity: t.amount(inventory).unwrap_or(1),
|
||||||
|
ours: t.ours,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
slot::Event::Dropped(from) => {
|
slot::Event::Dropped(from) => {
|
||||||
// Drop item
|
// Drop item
|
||||||
@ -2763,6 +2772,16 @@ impl Hud {
|
|||||||
} else if let Hotbar(h) = from {
|
} else if let Hotbar(h) = from {
|
||||||
self.hotbar.clear_slot(h);
|
self.hotbar.clear_slot(h);
|
||||||
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
||||||
|
} else if let Trade(t) = from {
|
||||||
|
if let Some(inventory) = inventories.get(t.entity) {
|
||||||
|
if let Some(invslot) = t.invslot {
|
||||||
|
events.push(Event::TradeAction(TradeAction::RemoveItem {
|
||||||
|
item: invslot,
|
||||||
|
quantity: t.amount(inventory).unwrap_or(1),
|
||||||
|
ours: t.ours,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
slot::Event::Used(from) => {
|
slot::Event::Used(from) => {
|
||||||
@ -2835,8 +2854,13 @@ impl Hud {
|
|||||||
slot_manager: &mut slots::SlotManager,
|
slot_manager: &mut slots::SlotManager,
|
||||||
hotbar: &mut hotbar::State,
|
hotbar: &mut hotbar::State,
|
||||||
) {
|
) {
|
||||||
if let Some(slots::SlotKind::Inventory(i)) = slot_manager.selected() {
|
use slots::InventorySlot;
|
||||||
hotbar.add_inventory_link(slot, i.0);
|
if let Some(slots::SlotKind::Inventory(InventorySlot {
|
||||||
|
slot: i,
|
||||||
|
ours: true,
|
||||||
|
})) = slot_manager.selected()
|
||||||
|
{
|
||||||
|
hotbar.add_inventory_link(slot, i);
|
||||||
events.push(Event::ChangeHotbarState(Box::new(hotbar.to_owned())));
|
events.push(Event::ChangeHotbarState(Box::new(hotbar.to_owned())));
|
||||||
slot_manager.idle();
|
slot_manager.idle();
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,6 +13,7 @@ use common::comp::{
|
|||||||
Energy, Inventory,
|
Energy, Inventory,
|
||||||
};
|
};
|
||||||
use conrod_core::{image, Color};
|
use conrod_core::{image, Color};
|
||||||
|
use specs::Entity as EcsEntity;
|
||||||
|
|
||||||
pub use common::comp::slot::{ArmorSlot, EquipSlot};
|
pub use common::comp::slot::{ArmorSlot, EquipSlot};
|
||||||
|
|
||||||
@ -28,18 +29,21 @@ pub enum SlotKind {
|
|||||||
pub type SlotManager = slot::SlotManager<SlotKind>;
|
pub type SlotManager = slot::SlotManager<SlotKind>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct InventorySlot(pub InvSlotId);
|
pub struct InventorySlot {
|
||||||
|
pub slot: InvSlotId,
|
||||||
|
pub ours: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl SlotKey<Inventory, ItemImgs> for InventorySlot {
|
impl SlotKey<Inventory, ItemImgs> for InventorySlot {
|
||||||
type ImageKey = ItemKey;
|
type ImageKey = ItemKey;
|
||||||
|
|
||||||
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
||||||
source.get(self.0).map(|i| (i.into(), None))
|
source.get(self.slot).map(|i| (i.into(), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amount(&self, source: &Inventory) -> Option<u32> {
|
fn amount(&self, source: &Inventory) -> Option<u32> {
|
||||||
source
|
source
|
||||||
.get(self.0)
|
.get(self.slot)
|
||||||
.map(|item| item.amount())
|
.map(|item| item.amount())
|
||||||
.filter(|amount| *amount > 1)
|
.filter(|amount| *amount > 1)
|
||||||
}
|
}
|
||||||
@ -69,19 +73,32 @@ pub struct TradeSlot {
|
|||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
pub invslot: Option<InvSlotId>,
|
pub invslot: Option<InvSlotId>,
|
||||||
|
pub entity: EcsEntity,
|
||||||
|
pub ours: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlotKey<Inventory, ItemImgs> for TradeSlot {
|
impl SlotKey<Inventory, ItemImgs> for TradeSlot {
|
||||||
type ImageKey = ItemKey;
|
type ImageKey = ItemKey;
|
||||||
|
|
||||||
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
||||||
self.invslot
|
self.invslot.and_then(|inv_id| {
|
||||||
.and_then(|inv_id| InventorySlot(inv_id).image_key(source))
|
InventorySlot {
|
||||||
|
slot: inv_id,
|
||||||
|
ours: self.ours,
|
||||||
|
}
|
||||||
|
.image_key(source)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amount(&self, source: &Inventory) -> Option<u32> {
|
fn amount(&self, source: &Inventory) -> Option<u32> {
|
||||||
self.invslot
|
self.invslot
|
||||||
.and_then(|inv_id| InventorySlot(inv_id).amount(source))
|
.and_then(|inv_id| {
|
||||||
|
InventorySlot {
|
||||||
|
slot: inv_id,
|
||||||
|
ours: self.ours,
|
||||||
|
}
|
||||||
|
.amount(source)
|
||||||
|
})
|
||||||
.map(|x| x.min(self.quantity))
|
.map(|x| x.min(self.quantity))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use super::{
|
|||||||
TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
|
TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hud::bag::{BackgroundIds, InventoryScroller},
|
||||||
i18n::Localization,
|
i18n::Localization,
|
||||||
ui::{
|
ui::{
|
||||||
fonts::Fonts,
|
fonts::Fonts,
|
||||||
@ -28,10 +29,12 @@ use conrod_core::{
|
|||||||
widget::{self, Button, Image, Rectangle, State as ConrodState, Text},
|
widget::{self, Button, Image, Rectangle, State as ConrodState, Text},
|
||||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
|
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
|
||||||
};
|
};
|
||||||
|
use specs::Entity as EcsEntity;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
ids: Ids,
|
ids: Ids,
|
||||||
|
bg_ids: BackgroundIds,
|
||||||
}
|
}
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
@ -49,6 +52,7 @@ widget_ids! {
|
|||||||
phase_indicator,
|
phase_indicator,
|
||||||
accept_button,
|
accept_button,
|
||||||
decline_button,
|
decline_button,
|
||||||
|
inventory_scroller,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +161,7 @@ impl<'a> Trade<'a> {
|
|||||||
let inventories = self.client.inventories();
|
let inventories = self.client.inventories();
|
||||||
let uid = trade.parties[who];
|
let uid = trade.parties[who];
|
||||||
let entity = self.client.state().ecs().entity_from_uid(uid.0)?;
|
let entity = self.client.state().ecs().entity_from_uid(uid.0)?;
|
||||||
|
let ours = entity == self.client.entity();
|
||||||
// TODO: update in accordence with https://gitlab.com/veloren/veloren/-/issues/960
|
// TODO: update in accordence with https://gitlab.com/veloren/veloren/-/issues/960
|
||||||
let inventory = inventories.get(entity)?;
|
let inventory = inventories.get(entity)?;
|
||||||
|
|
||||||
@ -215,13 +220,15 @@ impl<'a> Trade<'a> {
|
|||||||
index,
|
index,
|
||||||
quantity,
|
quantity,
|
||||||
invslot: Some(k),
|
invslot: Some(k),
|
||||||
|
ours,
|
||||||
|
entity,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if matches!(trade.phase(), TradePhase::Mutate) {
|
if matches!(trade.phase(), TradePhase::Mutate) {
|
||||||
self.phase1_itemwidget(state, ui, inventory, who, &tradeslots);
|
self.phase1_itemwidget(state, ui, inventory, who, ours, entity, name, &tradeslots);
|
||||||
} else {
|
} else {
|
||||||
self.phase2_itemwidget(state, ui, inventory, who, &tradeslots);
|
self.phase2_itemwidget(state, ui, inventory, who, ours, entity, &tradeslots);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@ -233,6 +240,9 @@ impl<'a> Trade<'a> {
|
|||||||
ui: &mut UiCell<'_>,
|
ui: &mut UiCell<'_>,
|
||||||
inventory: &Inventory,
|
inventory: &Inventory,
|
||||||
who: usize,
|
who: usize,
|
||||||
|
ours: bool,
|
||||||
|
entity: EcsEntity,
|
||||||
|
name: String,
|
||||||
tradeslots: &[TradeSlot],
|
tradeslots: &[TradeSlot],
|
||||||
) {
|
) {
|
||||||
let item_tooltip = Tooltip::new({
|
let item_tooltip = Tooltip::new({
|
||||||
@ -253,6 +263,28 @@ impl<'a> Trade<'a> {
|
|||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.desc_text_color(TEXT_COLOR);
|
.desc_text_color(TEXT_COLOR);
|
||||||
|
|
||||||
|
if !ours {
|
||||||
|
InventoryScroller::new(
|
||||||
|
self.imgs,
|
||||||
|
self.item_imgs,
|
||||||
|
self.fonts,
|
||||||
|
self.tooltip_manager,
|
||||||
|
self.slot_manager,
|
||||||
|
self.pulse,
|
||||||
|
self.localized_strings,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
self.msm,
|
||||||
|
false,
|
||||||
|
&item_tooltip,
|
||||||
|
name,
|
||||||
|
false,
|
||||||
|
&inventory,
|
||||||
|
&state.bg_ids,
|
||||||
|
)
|
||||||
|
.set(state.ids.inventory_scroller, ui);
|
||||||
|
}
|
||||||
|
|
||||||
let mut slot_maker = SlotMaker {
|
let mut slot_maker = SlotMaker {
|
||||||
empty_slot: self.imgs.inv_slot,
|
empty_slot: self.imgs.inv_slot,
|
||||||
filled_slot: self.imgs.inv_slot,
|
filled_slot: self.imgs.inv_slot,
|
||||||
@ -289,6 +321,8 @@ impl<'a> Trade<'a> {
|
|||||||
index: i,
|
index: i,
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
invslot: None,
|
invslot: None,
|
||||||
|
ours,
|
||||||
|
entity,
|
||||||
});
|
});
|
||||||
// Slot
|
// Slot
|
||||||
let slot_widget = slot_maker
|
let slot_widget = slot_maker
|
||||||
@ -334,6 +368,8 @@ impl<'a> Trade<'a> {
|
|||||||
ui: &mut UiCell<'_>,
|
ui: &mut UiCell<'_>,
|
||||||
inventory: &Inventory,
|
inventory: &Inventory,
|
||||||
who: usize,
|
who: usize,
|
||||||
|
ours: bool,
|
||||||
|
entity: EcsEntity,
|
||||||
tradeslots: &[TradeSlot],
|
tradeslots: &[TradeSlot],
|
||||||
) {
|
) {
|
||||||
if state.ids.inv_textslots.len() < 2 * MAX_TRADE_SLOTS {
|
if state.ids.inv_textslots.len() < 2 * MAX_TRADE_SLOTS {
|
||||||
@ -348,6 +384,8 @@ impl<'a> Trade<'a> {
|
|||||||
index: i,
|
index: i,
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
invslot: None,
|
invslot: None,
|
||||||
|
ours,
|
||||||
|
entity,
|
||||||
});
|
});
|
||||||
let itemname = slot
|
let itemname = slot
|
||||||
.invslot
|
.invslot
|
||||||
@ -435,8 +473,12 @@ impl<'a> Widget for Trade<'a> {
|
|||||||
type State = State;
|
type State = State;
|
||||||
type Style = ();
|
type Style = ();
|
||||||
|
|
||||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
|
||||||
State {
|
State {
|
||||||
|
bg_ids: BackgroundIds {
|
||||||
|
bg: id_gen.next(),
|
||||||
|
bg_frame: id_gen.next(),
|
||||||
|
},
|
||||||
ids: Ids::new(id_gen),
|
ids: Ids::new(id_gen),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user