diff --git a/CHANGELOG.md b/CHANGELOG.md index 42bff7edcc..f65454f9bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adjusted Stonework Defender loot table to remove mindflayer drops (bag, staff, glider). - Changed default controller key bindings - Improved network efficiency by ≈ factor 10 by using tokio. +- Added item tooltips to trade window. ### Removed diff --git a/assets/voxygen/i18n/en/hud/trade.ron b/assets/voxygen/i18n/en/hud/trade.ron index af1e09aaa6..cb2c0364f7 100644 --- a/assets/voxygen/i18n/en/hud/trade.ron +++ b/assets/voxygen/i18n/en/hud/trade.ron @@ -12,6 +12,10 @@ "hud.trade.has_accepted": "{playername}\nhas accepted", "hud.trade.accept": "Accept", "hud.trade.decline": "Decline", + "hud.trade.invite_sent": "Trade request sent to {playername}.", + "hud.trade.result.completed": "Trade completed successfully.", + "hud.trade.result.declined": "Trade declined.", + "hud.trade.result.nospace": "Not enough space to complete the trade.", }, diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index c736ab9782..fd9f8af8be 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -343,7 +343,7 @@ impl Inventory { } } - fn slot(&self, inv_slot_id: InvSlotId) -> Option<&InvSlot> { + pub fn slot(&self, inv_slot_id: InvSlotId) -> Option<&InvSlot> { match SlotId::from(inv_slot_id) { SlotId::Inventory(slot_idx) => self.slots.get(slot_idx), SlotId::Loadout(loadout_slot_id) => self.loadout.inv_slot(loadout_slot_id), diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index c52cdef988..01a530be68 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2131,6 +2131,7 @@ impl Hud { &self.imgs, &self.item_imgs, &self.fonts, + &self.rot_imgs, tooltip_manager, &mut self.slot_manager, i18n, diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 2765061a67..d5312e3ced 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -1,5 +1,6 @@ use super::{ - img_ids::Imgs, + get_quality_col, + img_ids::{Imgs, ImgsRot}, item_imgs::ItemImgs, slots::{SlotManager, TradeSlot}, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, @@ -9,12 +10,12 @@ use crate::{ ui::{ fonts::Fonts, slot::{ContentSize, SlotMaker}, - TooltipManager, + ImageFrame, Tooltip, TooltipManager, Tooltipable, }, }; use client::Client; use common::{ - comp::Inventory, + comp::{inventory::item::Quality, Inventory}, trade::{PendingTrade, TradeAction, TradePhase}, }; use common_net::sync::WorldSyncExt; @@ -54,9 +55,10 @@ pub struct Trade<'a> { imgs: &'a Imgs, item_imgs: &'a ItemImgs, fonts: &'a Fonts, + rot_imgs: &'a ImgsRot, + tooltip_manager: &'a mut TooltipManager, #[conrod(common_builder)] common: widget::CommonBuilder, - //tooltip_manager: &'a mut TooltipManager, slot_manager: &'a mut SlotManager, localized_strings: &'a Localization, pulse: f32, @@ -68,7 +70,8 @@ impl<'a> Trade<'a> { imgs: &'a Imgs, item_imgs: &'a ItemImgs, fonts: &'a Fonts, - _tooltip_manager: &'a mut TooltipManager, + rot_imgs: &'a ImgsRot, + tooltip_manager: &'a mut TooltipManager, slot_manager: &'a mut SlotManager, localized_strings: &'a Localization, pulse: f32, @@ -78,6 +81,8 @@ impl<'a> Trade<'a> { imgs, item_imgs, fonts, + rot_imgs, + tooltip_manager, common: widget::CommonBuilder::default(), //tooltip_manager, slot_manager, @@ -225,6 +230,24 @@ impl<'a> Trade<'a> { who: usize, tradeslots: &[TradeSlot], ) { + let item_tooltip = Tooltip::new({ + // Edge images [t, b, r, l] + // Corner images [tr, tl, br, bl] + let edge = &self.rot_imgs.tt_side; + let corner = &self.rot_imgs.tt_corner; + ImageFrame::new( + [edge.cw180, edge.none, edge.cw270, edge.cw90], + [corner.none, corner.cw270, corner.cw90, corner.cw180], + Color::Rgba(0.08, 0.07, 0.04, 1.0), + 5.0, + ) + }) + .title_font_size(self.fonts.cyri.scale(15)) + .parent(ui.window) + .desc_font_size(self.fonts.cyri.scale(12)) + .font_id(self.fonts.cyri.conrod_id) + .desc_text_color(TEXT_COLOR); + let mut slot_maker = SlotMaker { empty_slot: self.imgs.inv_slot, filled_slot: self.imgs.inv_slot, @@ -270,7 +293,33 @@ impl<'a> Trade<'a> { 0.0 + y as f64 * (40.0), 0.0 + x as f64 * (40.0), ); - slot_widget.set(state.ids.inv_slots[i + who * MAX_TRADE_SLOTS], ui); + let slot_id = state.ids.inv_slots[i + who * MAX_TRADE_SLOTS]; + if let Some(Some(item)) = slot.invslot.and_then(|slotid| inventory.slot(slotid)) { + let (title, desc) = super::util::item_text(item); + 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(slot_id, ui); + } else { + slot_widget.set(slot_id, ui); + } } } @@ -416,25 +465,6 @@ impl<'a> Widget for Trade<'a> { }); } - // TODO: item tooltips in trade preview - /*let trade_tooltip = Tooltip::new({ - // Edge images [t, b, r, l] - // Corner images [tr, tl, br, bl] - let edge = &self.rot_imgs.tt_side; - let corner = &self.rot_imgs.tt_corner; - ImageFrame::new( - [edge.cw180, edge.none, edge.cw270, edge.cw90], - [corner.none, corner.cw270, corner.cw90, corner.cw180], - Color::Rgba(0.08, 0.07, 0.04, 1.0), - 5.0, - ) - }) - .title_font_size(self.fonts.cyri.scale(15)) - .parent(ui.window) - .desc_font_size(self.fonts.cyri.scale(12)) - .font_id(self.fonts.cyri.conrod_id) - .desc_text_color(TEXT_COLOR);*/ - self.background(&mut state, ui); self.title(&mut state, ui); self.phase_indicator(&mut state, ui, &trade); diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 7eb334b345..d69068d270 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -133,14 +133,15 @@ impl SessionState { answer, kind, } => { - // TODO: i18n + // TODO: i18n (complicated since substituting phrases at this granularity may + // not be grammatical in some languages) let kind_str = match kind { InviteKind::Group => "Group", InviteKind::Trade => "Trade", }; let target_name = match client.player_list().get(&target) { Some(info) => info.player_alias.clone(), - None => "".to_string(), + None => format!("", target), }; let answer_str = match answer { InviteAnswer::Accepted => "accepted", @@ -151,11 +152,11 @@ impl SessionState { self.hud.new_message(ChatType::Meta.chat_msg(msg)); }, client::Event::TradeComplete { result, trade: _ } => { - // TODO: i18n, entity names + let i18n = global_state.i18n.read(); let msg = match result { - TradeResult::Completed => "Trade completed successfully.", - TradeResult::Declined => "Trade declined.", - TradeResult::NotEnoughSpace => "Not enough space to complete the trade.", + TradeResult::Completed => i18n.get("hud.trade.result.completed"), + TradeResult::Declined => i18n.get("hud.trade.result.declined"), + TradeResult::NotEnoughSpace => i18n.get("hud.trade.result.nospace"), }; self.hud.new_message(ChatType::Meta.chat_msg(msg)); }, @@ -573,11 +574,22 @@ impl PlayState for SessionState { match interactable { Interactable::Block(_, _) => {}, Interactable::Entity(entity) => { - client - .state() - .ecs() - .uid_from_entity(entity) - .map(|uid| client.send_invite(uid, InviteKind::Trade)); + if let Some(uid) = + client.state().ecs().uid_from_entity(entity) + { + let name = client + .player_list() + .get(&uid) + .map(|info| info.player_alias.clone()) + .unwrap_or_else(|| format!("", uid)); + let msg = global_state + .i18n + .read() + .get("hud.trade.invite_sent") + .replace("{playername}", &name); + self.hud.new_message(ChatType::Meta.chat_msg(msg)); + client.send_invite(uid, InviteKind::Trade) + }; }, } } diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index e92a7ec217..cd23e98113 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -142,8 +142,8 @@ impl ControlSettings { GameInput::ToggleLantern => KeyMouse::Key(VirtualKeyCode::G), GameInput::Mount => KeyMouse::Key(VirtualKeyCode::F), GameInput::Map => KeyMouse::Key(VirtualKeyCode::M), - GameInput::Bag => KeyMouse::Key(VirtualKeyCode::R), - GameInput::Trade => KeyMouse::Key(VirtualKeyCode::B), + GameInput::Bag => KeyMouse::Key(VirtualKeyCode::B), + GameInput::Trade => KeyMouse::Key(VirtualKeyCode::R), GameInput::Social => KeyMouse::Key(VirtualKeyCode::O), GameInput::Crafting => KeyMouse::Key(VirtualKeyCode::C), GameInput::Spellbook => KeyMouse::Key(VirtualKeyCode::P),