Client-side trade improvements.

- Add item tooltips in trade.
- More localization support.
- Fix bindings (R for trade, B for bag).
This commit is contained in:
Avi Weinstock 2021-02-19 15:20:27 -05:00
parent b4adc5369a
commit 559ad7b7f5
7 changed files with 87 additions and 39 deletions

View File

@ -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). - Adjusted Stonework Defender loot table to remove mindflayer drops (bag, staff, glider).
- Changed default controller key bindings - Changed default controller key bindings
- Improved network efficiency by ≈ factor 10 by using tokio. - Improved network efficiency by ≈ factor 10 by using tokio.
- Added item tooltips to trade window.
### Removed ### Removed

View File

@ -12,6 +12,10 @@
"hud.trade.has_accepted": "{playername}\nhas accepted", "hud.trade.has_accepted": "{playername}\nhas accepted",
"hud.trade.accept": "Accept", "hud.trade.accept": "Accept",
"hud.trade.decline": "Decline", "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.",
}, },

View File

@ -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) { match SlotId::from(inv_slot_id) {
SlotId::Inventory(slot_idx) => self.slots.get(slot_idx), SlotId::Inventory(slot_idx) => self.slots.get(slot_idx),
SlotId::Loadout(loadout_slot_id) => self.loadout.inv_slot(loadout_slot_id), SlotId::Loadout(loadout_slot_id) => self.loadout.inv_slot(loadout_slot_id),

View File

@ -2131,6 +2131,7 @@ impl Hud {
&self.imgs, &self.imgs,
&self.item_imgs, &self.item_imgs,
&self.fonts, &self.fonts,
&self.rot_imgs,
tooltip_manager, tooltip_manager,
&mut self.slot_manager, &mut self.slot_manager,
i18n, i18n,

View File

@ -1,5 +1,6 @@
use super::{ use super::{
img_ids::Imgs, get_quality_col,
img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs, item_imgs::ItemImgs,
slots::{SlotManager, TradeSlot}, slots::{SlotManager, TradeSlot},
TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
@ -9,12 +10,12 @@ use crate::{
ui::{ ui::{
fonts::Fonts, fonts::Fonts,
slot::{ContentSize, SlotMaker}, slot::{ContentSize, SlotMaker},
TooltipManager, ImageFrame, Tooltip, TooltipManager, Tooltipable,
}, },
}; };
use client::Client; use client::Client;
use common::{ use common::{
comp::Inventory, comp::{inventory::item::Quality, Inventory},
trade::{PendingTrade, TradeAction, TradePhase}, trade::{PendingTrade, TradeAction, TradePhase},
}; };
use common_net::sync::WorldSyncExt; use common_net::sync::WorldSyncExt;
@ -54,9 +55,10 @@ pub struct Trade<'a> {
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
fonts: &'a Fonts, fonts: &'a Fonts,
rot_imgs: &'a ImgsRot,
tooltip_manager: &'a mut TooltipManager,
#[conrod(common_builder)] #[conrod(common_builder)]
common: widget::CommonBuilder, common: widget::CommonBuilder,
//tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager, slot_manager: &'a mut SlotManager,
localized_strings: &'a Localization, localized_strings: &'a Localization,
pulse: f32, pulse: f32,
@ -68,7 +70,8 @@ impl<'a> Trade<'a> {
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
fonts: &'a Fonts, fonts: &'a Fonts,
_tooltip_manager: &'a mut TooltipManager, rot_imgs: &'a ImgsRot,
tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager, slot_manager: &'a mut SlotManager,
localized_strings: &'a Localization, localized_strings: &'a Localization,
pulse: f32, pulse: f32,
@ -78,6 +81,8 @@ impl<'a> Trade<'a> {
imgs, imgs,
item_imgs, item_imgs,
fonts, fonts,
rot_imgs,
tooltip_manager,
common: widget::CommonBuilder::default(), common: widget::CommonBuilder::default(),
//tooltip_manager, //tooltip_manager,
slot_manager, slot_manager,
@ -225,6 +230,24 @@ impl<'a> Trade<'a> {
who: usize, who: usize,
tradeslots: &[TradeSlot], 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 { 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,
@ -270,7 +293,33 @@ impl<'a> Trade<'a> {
0.0 + y as f64 * (40.0), 0.0 + y as f64 * (40.0),
0.0 + x 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.background(&mut state, ui);
self.title(&mut state, ui); self.title(&mut state, ui);
self.phase_indicator(&mut state, ui, &trade); self.phase_indicator(&mut state, ui, &trade);

View File

@ -133,14 +133,15 @@ impl SessionState {
answer, answer,
kind, kind,
} => { } => {
// TODO: i18n // TODO: i18n (complicated since substituting phrases at this granularity may
// not be grammatical in some languages)
let kind_str = match kind { let kind_str = match kind {
InviteKind::Group => "Group", InviteKind::Group => "Group",
InviteKind::Trade => "Trade", InviteKind::Trade => "Trade",
}; };
let target_name = match client.player_list().get(&target) { let target_name = match client.player_list().get(&target) {
Some(info) => info.player_alias.clone(), Some(info) => info.player_alias.clone(),
None => "<unknown>".to_string(), None => format!("<entity {}>", target),
}; };
let answer_str = match answer { let answer_str = match answer {
InviteAnswer::Accepted => "accepted", InviteAnswer::Accepted => "accepted",
@ -151,11 +152,11 @@ impl SessionState {
self.hud.new_message(ChatType::Meta.chat_msg(msg)); self.hud.new_message(ChatType::Meta.chat_msg(msg));
}, },
client::Event::TradeComplete { result, trade: _ } => { client::Event::TradeComplete { result, trade: _ } => {
// TODO: i18n, entity names let i18n = global_state.i18n.read();
let msg = match result { let msg = match result {
TradeResult::Completed => "Trade completed successfully.", TradeResult::Completed => i18n.get("hud.trade.result.completed"),
TradeResult::Declined => "Trade declined.", TradeResult::Declined => i18n.get("hud.trade.result.declined"),
TradeResult::NotEnoughSpace => "Not enough space to complete the trade.", TradeResult::NotEnoughSpace => i18n.get("hud.trade.result.nospace"),
}; };
self.hud.new_message(ChatType::Meta.chat_msg(msg)); self.hud.new_message(ChatType::Meta.chat_msg(msg));
}, },
@ -573,11 +574,22 @@ impl PlayState for SessionState {
match interactable { match interactable {
Interactable::Block(_, _) => {}, Interactable::Block(_, _) => {},
Interactable::Entity(entity) => { Interactable::Entity(entity) => {
client if let Some(uid) =
.state() client.state().ecs().uid_from_entity(entity)
.ecs() {
.uid_from_entity(entity) let name = client
.map(|uid| client.send_invite(uid, InviteKind::Trade)); .player_list()
.get(&uid)
.map(|info| info.player_alias.clone())
.unwrap_or_else(|| format!("<entity {:?}>", 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)
};
}, },
} }
} }

View File

@ -142,8 +142,8 @@ impl ControlSettings {
GameInput::ToggleLantern => KeyMouse::Key(VirtualKeyCode::G), GameInput::ToggleLantern => KeyMouse::Key(VirtualKeyCode::G),
GameInput::Mount => KeyMouse::Key(VirtualKeyCode::F), GameInput::Mount => KeyMouse::Key(VirtualKeyCode::F),
GameInput::Map => KeyMouse::Key(VirtualKeyCode::M), GameInput::Map => KeyMouse::Key(VirtualKeyCode::M),
GameInput::Bag => KeyMouse::Key(VirtualKeyCode::R), GameInput::Bag => KeyMouse::Key(VirtualKeyCode::B),
GameInput::Trade => KeyMouse::Key(VirtualKeyCode::B), GameInput::Trade => KeyMouse::Key(VirtualKeyCode::R),
GameInput::Social => KeyMouse::Key(VirtualKeyCode::O), GameInput::Social => KeyMouse::Key(VirtualKeyCode::O),
GameInput::Crafting => KeyMouse::Key(VirtualKeyCode::C), GameInput::Crafting => KeyMouse::Key(VirtualKeyCode::C),
GameInput::Spellbook => KeyMouse::Key(VirtualKeyCode::P), GameInput::Spellbook => KeyMouse::Key(VirtualKeyCode::P),