mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Item pickups are shown in separate window and "inventory-full" messages are shown above the item attempted to be picked up
This commit is contained in:
parent
7488d8692b
commit
854930bc1a
@ -108,6 +108,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Merchants now use `/tell` instead of `/say` to communicate prices
|
- Merchants now use `/tell` instead of `/say` to communicate prices
|
||||||
- Entities catch on fire if they stand too close to campfires
|
- Entities catch on fire if they stand too close to campfires
|
||||||
- Water extinguishes entities on fire
|
- Water extinguishes entities on fire
|
||||||
|
- Item pickups are shown in separate window and inventory-full shows above item
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
BIN
assets/voxygen/element/ui/chat/icons/loot_small.png
(Stored with Git LFS)
BIN
assets/voxygen/element/ui/chat/icons/loot_small.png
(Stored with Git LFS)
Binary file not shown.
@ -9,6 +9,7 @@
|
|||||||
"hud.you_died": "You Died",
|
"hud.you_died": "You Died",
|
||||||
"hud.waypoint_saved": "Waypoint Saved",
|
"hud.waypoint_saved": "Waypoint Saved",
|
||||||
"hud.sp_arrow_txt": "SP",
|
"hud.sp_arrow_txt": "SP",
|
||||||
|
"hud.inventory_full": "Inventory Full",
|
||||||
|
|
||||||
"hud.press_key_to_show_keybindings_fmt": "[{key}] Keybindings",
|
"hud.press_key_to_show_keybindings_fmt": "[{key}] Keybindings",
|
||||||
"hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantern",
|
"hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantern",
|
||||||
|
@ -1900,7 +1900,8 @@ impl Client {
|
|||||||
},
|
},
|
||||||
ServerGeneral::InventoryUpdate(inventory, event) => {
|
ServerGeneral::InventoryUpdate(inventory, event) => {
|
||||||
match event {
|
match event {
|
||||||
InventoryUpdateEvent::CollectFailed => {},
|
InventoryUpdateEvent::BlockCollectFailed(_) => {},
|
||||||
|
InventoryUpdateEvent::EntityCollectFailed(_) => {},
|
||||||
_ => {
|
_ => {
|
||||||
// Push the updated inventory component to the client
|
// Push the updated inventory component to the client
|
||||||
// FIXME: Figure out whether this error can happen under normal gameplay,
|
// FIXME: Figure out whether this error can happen under normal gameplay,
|
||||||
@ -2287,7 +2288,6 @@ impl Client {
|
|||||||
},
|
},
|
||||||
comp::ChatType::CommandError => message.to_string(),
|
comp::ChatType::CommandError => message.to_string(),
|
||||||
comp::ChatType::CommandInfo => message.to_string(),
|
comp::ChatType::CommandInfo => message.to_string(),
|
||||||
comp::ChatType::Loot => message.to_string(),
|
|
||||||
comp::ChatType::FactionMeta(_) => message.to_string(),
|
comp::ChatType::FactionMeta(_) => message.to_string(),
|
||||||
comp::ChatType::GroupMeta(_) => message.to_string(),
|
comp::ChatType::GroupMeta(_) => message.to_string(),
|
||||||
comp::ChatType::Kill(kill_source, victim) => {
|
comp::ChatType::Kill(kill_source, victim) => {
|
||||||
|
@ -113,8 +113,6 @@ pub enum ChatType<G> {
|
|||||||
NpcTell(Uid, Uid, u16),
|
NpcTell(Uid, Uid, u16),
|
||||||
/// Anything else
|
/// Anything else
|
||||||
Meta,
|
Meta,
|
||||||
// Looted items
|
|
||||||
Loot,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G> ChatType<G> {
|
impl<G> ChatType<G> {
|
||||||
@ -166,7 +164,6 @@ impl<G> GenericChatMsg<G> {
|
|||||||
ChatType::Offline(a) => ChatType::Offline(a),
|
ChatType::Offline(a) => ChatType::Offline(a),
|
||||||
ChatType::CommandInfo => ChatType::CommandInfo,
|
ChatType::CommandInfo => ChatType::CommandInfo,
|
||||||
ChatType::CommandError => ChatType::CommandError,
|
ChatType::CommandError => ChatType::CommandError,
|
||||||
ChatType::Loot => ChatType::Loot,
|
|
||||||
ChatType::FactionMeta(a) => ChatType::FactionMeta(a),
|
ChatType::FactionMeta(a) => ChatType::FactionMeta(a),
|
||||||
ChatType::GroupMeta(g) => ChatType::GroupMeta(f(g)),
|
ChatType::GroupMeta(g) => ChatType::GroupMeta(f(g)),
|
||||||
ChatType::Kill(a, b) => ChatType::Kill(a, b),
|
ChatType::Kill(a, b) => ChatType::Kill(a, b),
|
||||||
@ -214,7 +211,6 @@ impl<G> GenericChatMsg<G> {
|
|||||||
ChatType::Offline(_) => SpeechBubbleType::None,
|
ChatType::Offline(_) => SpeechBubbleType::None,
|
||||||
ChatType::CommandInfo => SpeechBubbleType::None,
|
ChatType::CommandInfo => SpeechBubbleType::None,
|
||||||
ChatType::CommandError => SpeechBubbleType::None,
|
ChatType::CommandError => SpeechBubbleType::None,
|
||||||
ChatType::Loot => SpeechBubbleType::None,
|
|
||||||
ChatType::FactionMeta(_) => SpeechBubbleType::None,
|
ChatType::FactionMeta(_) => SpeechBubbleType::None,
|
||||||
ChatType::GroupMeta(_) => SpeechBubbleType::None,
|
ChatType::GroupMeta(_) => SpeechBubbleType::None,
|
||||||
ChatType::Kill(_, _) => SpeechBubbleType::None,
|
ChatType::Kill(_, _) => SpeechBubbleType::None,
|
||||||
@ -237,7 +233,6 @@ impl<G> GenericChatMsg<G> {
|
|||||||
ChatType::Offline(_) => None,
|
ChatType::Offline(_) => None,
|
||||||
ChatType::CommandInfo => None,
|
ChatType::CommandInfo => None,
|
||||||
ChatType::CommandError => None,
|
ChatType::CommandError => None,
|
||||||
ChatType::Loot => None,
|
|
||||||
ChatType::FactionMeta(_) => None,
|
ChatType::FactionMeta(_) => None,
|
||||||
ChatType::GroupMeta(_) => None,
|
ChatType::GroupMeta(_) => None,
|
||||||
ChatType::Kill(_, _) => None,
|
ChatType::Kill(_, _) => None,
|
||||||
|
@ -4,6 +4,7 @@ use specs::{Component, DerefFlaggedStorage};
|
|||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::{collections::HashMap, convert::TryFrom, mem, ops::Range};
|
use std::{collections::HashMap, convert::TryFrom, mem, ops::Range};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
@ -16,6 +17,7 @@ use crate::{
|
|||||||
Item,
|
Item,
|
||||||
},
|
},
|
||||||
recipe::{Recipe, RecipeInput},
|
recipe::{Recipe, RecipeInput},
|
||||||
|
uid::Uid,
|
||||||
LoadoutBuilder,
|
LoadoutBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -817,7 +819,8 @@ pub enum InventoryUpdateEvent {
|
|||||||
Swapped,
|
Swapped,
|
||||||
Dropped,
|
Dropped,
|
||||||
Collected(Item),
|
Collected(Item),
|
||||||
CollectFailed,
|
BlockCollectFailed(Vec3<i32>),
|
||||||
|
EntityCollectFailed(Uid),
|
||||||
Possession,
|
Possession,
|
||||||
Debug,
|
Debug,
|
||||||
Craft,
|
Craft,
|
||||||
|
@ -158,7 +158,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
);
|
);
|
||||||
drop(item_storage);
|
drop(item_storage);
|
||||||
drop(inventories);
|
drop(inventories);
|
||||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::CollectFailed)
|
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::EntityCollectFailed(uid))
|
||||||
},
|
},
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// We succeeded in picking up the item, so we may now delete its old entity
|
// We succeeded in picking up the item, so we may now delete its old entity
|
||||||
@ -216,7 +216,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
// drop it.
|
// drop it.
|
||||||
Err(_) => (
|
Err(_) => (
|
||||||
Some(comp::InventoryUpdate::new(
|
Some(comp::InventoryUpdate::new(
|
||||||
comp::InventoryUpdateEvent::CollectFailed,
|
comp::InventoryUpdateEvent::BlockCollectFailed(pos),
|
||||||
)),
|
)),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
@ -543,7 +543,6 @@ impl StateExt for State {
|
|||||||
comp::ChatType::Offline(_)
|
comp::ChatType::Offline(_)
|
||||||
| comp::ChatType::CommandInfo
|
| comp::ChatType::CommandInfo
|
||||||
| comp::ChatType::CommandError
|
| comp::ChatType::CommandError
|
||||||
| comp::ChatType::Loot
|
|
||||||
| comp::ChatType::Meta
|
| comp::ChatType::Meta
|
||||||
| comp::ChatType::World(_) => self.notify_players(ServerGeneral::ChatMsg(resolved_msg)),
|
| comp::ChatType::World(_) => self.notify_players(ServerGeneral::ChatMsg(resolved_msg)),
|
||||||
comp::ChatType::Online(u) => {
|
comp::ChatType::Online(u) => {
|
||||||
|
@ -217,7 +217,8 @@ impl From<&InventoryUpdateEvent> for SfxEvent {
|
|||||||
_ => SfxEvent::Inventory(SfxInventoryEvent::Collected),
|
_ => SfxEvent::Inventory(SfxInventoryEvent::Collected),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InventoryUpdateEvent::CollectFailed => {
|
InventoryUpdateEvent::BlockCollectFailed(_)
|
||||||
|
| InventoryUpdateEvent::EntityCollectFailed(_) => {
|
||||||
SfxEvent::Inventory(SfxInventoryEvent::CollectFailed)
|
SfxEvent::Inventory(SfxInventoryEvent::CollectFailed)
|
||||||
},
|
},
|
||||||
InventoryUpdateEvent::Consumed(consumable) => {
|
InventoryUpdateEvent::Consumed(consumable) => {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
img_ids::Imgs, ChatTab, ERROR_COLOR, FACTION_COLOR, GROUP_COLOR, INFO_COLOR, KILL_COLOR,
|
img_ids::Imgs, ChatTab, ERROR_COLOR, FACTION_COLOR, GROUP_COLOR, INFO_COLOR, KILL_COLOR,
|
||||||
LOOT_COLOR, OFFLINE_COLOR, ONLINE_COLOR, REGION_COLOR, SAY_COLOR, TELL_COLOR, TEXT_COLOR,
|
OFFLINE_COLOR, ONLINE_COLOR, REGION_COLOR, SAY_COLOR, TELL_COLOR, TEXT_COLOR, WORLD_COLOR,
|
||||||
WORLD_COLOR,
|
|
||||||
};
|
};
|
||||||
use crate::{i18n::Localization, settings::chat::MAX_CHAT_TABS, ui::fonts::Fonts, GlobalState};
|
use crate::{i18n::Localization, settings::chat::MAX_CHAT_TABS, ui::fonts::Fonts, GlobalState};
|
||||||
use client::{cmd, Client};
|
use client::{cmd, Client};
|
||||||
@ -722,7 +721,6 @@ fn render_chat_line(chat_type: &ChatType<String>, imgs: &Imgs) -> (Color, conrod
|
|||||||
ChatType::Offline(_) => (OFFLINE_COLOR, imgs.chat_offline_small),
|
ChatType::Offline(_) => (OFFLINE_COLOR, imgs.chat_offline_small),
|
||||||
ChatType::CommandError => (ERROR_COLOR, imgs.chat_command_error_small),
|
ChatType::CommandError => (ERROR_COLOR, imgs.chat_command_error_small),
|
||||||
ChatType::CommandInfo => (INFO_COLOR, imgs.chat_command_info_small),
|
ChatType::CommandInfo => (INFO_COLOR, imgs.chat_command_info_small),
|
||||||
ChatType::Loot => (LOOT_COLOR, imgs.chat_loot_small),
|
|
||||||
ChatType::GroupMeta(_) => (GROUP_COLOR, imgs.chat_group_small),
|
ChatType::GroupMeta(_) => (GROUP_COLOR, imgs.chat_group_small),
|
||||||
ChatType::FactionMeta(_) => (FACTION_COLOR, imgs.chat_faction_small),
|
ChatType::FactionMeta(_) => (FACTION_COLOR, imgs.chat_faction_small),
|
||||||
ChatType::Kill(_, _) => (KILL_COLOR, imgs.chat_kill_small),
|
ChatType::Kill(_, _) => (KILL_COLOR, imgs.chat_kill_small),
|
||||||
|
@ -561,7 +561,6 @@ image_ids! {
|
|||||||
chat_command_info_small: "voxygen.element.ui.chat.icons.command_info_small",
|
chat_command_info_small: "voxygen.element.ui.chat.icons.command_info_small",
|
||||||
chat_online_small: "voxygen.element.ui.chat.icons.online_small",
|
chat_online_small: "voxygen.element.ui.chat.icons.online_small",
|
||||||
chat_offline_small: "voxygen.element.ui.chat.icons.offline_small",
|
chat_offline_small: "voxygen.element.ui.chat.icons.offline_small",
|
||||||
chat_loot_small: "voxygen.element.ui.chat.icons.loot_small",
|
|
||||||
|
|
||||||
chat_faction: "voxygen.element.ui.chat.icons.faction",
|
chat_faction: "voxygen.element.ui.chat.icons.faction",
|
||||||
chat_group: "voxygen.element.ui.chat.icons.group",
|
chat_group: "voxygen.element.ui.chat.icons.group",
|
||||||
|
384
voxygen/src/hud/loot_scroller.rs
Normal file
384
voxygen/src/hud/loot_scroller.rs
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
use super::{
|
||||||
|
animate_by_pulse, get_quality_col,
|
||||||
|
img_ids::{Imgs, ImgsRot},
|
||||||
|
item_imgs::ItemImgs,
|
||||||
|
Show, TEXT_COLOR,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
i18n::Localization,
|
||||||
|
ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable},
|
||||||
|
};
|
||||||
|
use client::Client;
|
||||||
|
use common::comp::inventory::item::{ItemDef, MaterialStatManifest, Quality};
|
||||||
|
use conrod_core::{
|
||||||
|
color,
|
||||||
|
position::Dimension,
|
||||||
|
widget::{self, Image, List, Rectangle, Scrollbar, Text},
|
||||||
|
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||||
|
};
|
||||||
|
use std::{collections::VecDeque, sync::Arc};
|
||||||
|
|
||||||
|
widget_ids! {
|
||||||
|
struct Ids{
|
||||||
|
frame,
|
||||||
|
message_box,
|
||||||
|
scrollbar,
|
||||||
|
message_icons[],
|
||||||
|
message_icon_bgs[],
|
||||||
|
message_icon_frames[],
|
||||||
|
message_texts[],
|
||||||
|
message_text_shadows[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_MESSAGES: usize = 50;
|
||||||
|
|
||||||
|
const BOX_WIDTH: f64 = 300.0;
|
||||||
|
const BOX_HEIGHT: f64 = 350.0;
|
||||||
|
|
||||||
|
const ICON_BG_SIZE: f64 = 33.0;
|
||||||
|
const ICON_SIZE: f64 = 30.0;
|
||||||
|
const ICON_LABEL_SPACER: f64 = 7.0;
|
||||||
|
|
||||||
|
const MESSAGE_VERTICAL_PADDING: f64 = 1.0;
|
||||||
|
|
||||||
|
const HOVER_FADE_OUT_TIME: f32 = 2.0;
|
||||||
|
const MESSAGE_FADE_OUT_TIME: f32 = 4.5;
|
||||||
|
const AUTO_SHOW_FADE_OUT_TIME: f32 = 1.0;
|
||||||
|
|
||||||
|
const MAX_MERGE_TIME: f32 = MESSAGE_FADE_OUT_TIME;
|
||||||
|
|
||||||
|
#[derive(WidgetCommon)]
|
||||||
|
pub struct LootScroller<'a> {
|
||||||
|
new_messages: &'a mut VecDeque<LootMessage>,
|
||||||
|
|
||||||
|
client: &'a Client,
|
||||||
|
show: &'a Show,
|
||||||
|
imgs: &'a Imgs,
|
||||||
|
item_imgs: &'a ItemImgs,
|
||||||
|
rot_imgs: &'a ImgsRot,
|
||||||
|
fonts: &'a Fonts,
|
||||||
|
localized_strings: &'a Localization,
|
||||||
|
msm: &'a MaterialStatManifest,
|
||||||
|
item_tooltip_manager: &'a mut ItemTooltipManager,
|
||||||
|
pulse: f32,
|
||||||
|
|
||||||
|
#[conrod(common_builder)]
|
||||||
|
common: widget::CommonBuilder,
|
||||||
|
}
|
||||||
|
impl<'a> LootScroller<'a> {
|
||||||
|
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
|
||||||
|
pub fn new(
|
||||||
|
new_messages: &'a mut VecDeque<LootMessage>,
|
||||||
|
client: &'a Client,
|
||||||
|
show: &'a Show,
|
||||||
|
imgs: &'a Imgs,
|
||||||
|
item_imgs: &'a ItemImgs,
|
||||||
|
rot_imgs: &'a ImgsRot,
|
||||||
|
fonts: &'a Fonts,
|
||||||
|
localized_strings: &'a Localization,
|
||||||
|
msm: &'a MaterialStatManifest,
|
||||||
|
item_tooltip_manager: &'a mut ItemTooltipManager,
|
||||||
|
pulse: f32,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
new_messages,
|
||||||
|
client,
|
||||||
|
show,
|
||||||
|
imgs,
|
||||||
|
item_imgs,
|
||||||
|
rot_imgs,
|
||||||
|
fonts,
|
||||||
|
localized_strings,
|
||||||
|
msm,
|
||||||
|
item_tooltip_manager,
|
||||||
|
pulse,
|
||||||
|
common: widget::CommonBuilder::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct LootMessage {
|
||||||
|
pub item: Arc<ItemDef>,
|
||||||
|
pub amount: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
ids: Ids,
|
||||||
|
messages: VecDeque<(LootMessage, f32)>, // (message, timestamp)
|
||||||
|
|
||||||
|
last_hover_pulse: Option<f32>,
|
||||||
|
last_auto_show_pulse: Option<f32>, // auto show if (for example) bag is open
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Widget for LootScroller<'a> {
|
||||||
|
type Event = ();
|
||||||
|
type State = State;
|
||||||
|
type Style = ();
|
||||||
|
|
||||||
|
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||||
|
State {
|
||||||
|
ids: Ids::new(id_gen),
|
||||||
|
messages: VecDeque::new(),
|
||||||
|
last_hover_pulse: None,
|
||||||
|
last_auto_show_pulse: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
|
||||||
|
fn style(&self) -> Self::Style { () }
|
||||||
|
|
||||||
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
|
let widget::UpdateArgs { state, ui, .. } = args;
|
||||||
|
|
||||||
|
// Tooltips
|
||||||
|
let item_tooltip = ItemTooltip::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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
self.client,
|
||||||
|
self.imgs,
|
||||||
|
self.item_imgs,
|
||||||
|
self.pulse,
|
||||||
|
self.msm,
|
||||||
|
self.localized_strings,
|
||||||
|
)
|
||||||
|
.title_font_size(self.fonts.cyri.scale(20))
|
||||||
|
.parent(ui.window)
|
||||||
|
.desc_font_size(self.fonts.cyri.scale(12))
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.desc_text_color(TEXT_COLOR);
|
||||||
|
|
||||||
|
if !self.new_messages.is_empty() {
|
||||||
|
let pulse = self.pulse;
|
||||||
|
let oldest_merge_pulse = pulse - MAX_MERGE_TIME;
|
||||||
|
|
||||||
|
state.update(|s| {
|
||||||
|
s.messages.retain(|(message, t)| {
|
||||||
|
if *t >= oldest_merge_pulse {
|
||||||
|
if let Some(i) = self
|
||||||
|
.new_messages
|
||||||
|
.iter()
|
||||||
|
.position(|m| m.item.id() == message.item.id())
|
||||||
|
{
|
||||||
|
self.new_messages[i].amount += message.amount;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
s.messages
|
||||||
|
.extend(self.new_messages.drain(..).map(|message| (message, pulse)));
|
||||||
|
while s.messages.len() > MAX_MESSAGES {
|
||||||
|
s.messages.pop_front();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.scroll_widget(state.ids.message_box, [0.0, std::f64::MAX]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.show.social || self.show.trade {
|
||||||
|
if state.last_hover_pulse.is_some() || state.last_auto_show_pulse.is_some() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.last_hover_pulse = None;
|
||||||
|
s.last_auto_show_pulse = None;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ui
|
||||||
|
.rect_of(state.ids.message_box)
|
||||||
|
.map_or(false, |r| r.is_over(ui.global_input().current.mouse.xy))
|
||||||
|
{
|
||||||
|
state.update(|s| s.last_hover_pulse = Some(self.pulse));
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.ids.message_icons.len() < state.messages.len() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.message_icons
|
||||||
|
.resize(s.messages.len(), &mut ui.widget_id_generator())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state.ids.message_icon_bgs.len() < state.messages.len() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.message_icon_bgs
|
||||||
|
.resize(s.messages.len(), &mut ui.widget_id_generator())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state.ids.message_icon_frames.len() < state.messages.len() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.message_icon_frames
|
||||||
|
.resize(s.messages.len(), &mut ui.widget_id_generator())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state.ids.message_texts.len() < state.messages.len() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.message_texts
|
||||||
|
.resize(s.messages.len(), &mut ui.widget_id_generator())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state.ids.message_text_shadows.len() < state.messages.len() {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.message_text_shadows
|
||||||
|
.resize(s.messages.len(), &mut ui.widget_id_generator())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let hover_age = state
|
||||||
|
.last_hover_pulse
|
||||||
|
.map_or(1.0, |t| (self.pulse - t) / HOVER_FADE_OUT_TIME);
|
||||||
|
let auto_show_age = state
|
||||||
|
.last_auto_show_pulse
|
||||||
|
.map_or(1.0, |t| (self.pulse - t) / AUTO_SHOW_FADE_OUT_TIME);
|
||||||
|
|
||||||
|
let show_all_age = hover_age.min(auto_show_age);
|
||||||
|
|
||||||
|
let messages_to_display = state
|
||||||
|
.messages
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|(message, t)| {
|
||||||
|
let age = (self.pulse - t) / MESSAGE_FADE_OUT_TIME;
|
||||||
|
(message, age)
|
||||||
|
})
|
||||||
|
.filter(|(_, age)| age.min(show_all_age) < 1.0)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let (mut list_messages, _) = List::flow_up(messages_to_display.len())
|
||||||
|
.w_h(BOX_WIDTH, BOX_HEIGHT)
|
||||||
|
.scroll_kids_vertically()
|
||||||
|
.bottom_left_with_margins_on(ui.window, 308.0, 20.0)
|
||||||
|
.set(state.ids.message_box, ui);
|
||||||
|
|
||||||
|
if show_all_age < 1.0
|
||||||
|
&& ui
|
||||||
|
.widget_graph()
|
||||||
|
.widget(state.ids.message_box)
|
||||||
|
.map(|w| w.maybe_y_scroll_state)
|
||||||
|
.flatten()
|
||||||
|
.map_or(false, |s| s.scrollable_range_len > BOX_HEIGHT)
|
||||||
|
{
|
||||||
|
Scrollbar::y_axis(state.ids.message_box)
|
||||||
|
.thickness(5.0)
|
||||||
|
.rgba(0.33, 0.33, 0.33, 1.0 - show_all_age.powi(4))
|
||||||
|
.left_from(state.ids.message_box, 1.0)
|
||||||
|
.set(state.ids.scrollbar, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(list_message) = list_messages.next(ui) {
|
||||||
|
let i = list_message.i;
|
||||||
|
|
||||||
|
let (message, age) = messages_to_display[i];
|
||||||
|
let LootMessage { item, amount } = message;
|
||||||
|
|
||||||
|
let alpha = 1.0 - age.min(show_all_age).powi(4);
|
||||||
|
|
||||||
|
let brightness = 1.0 / (age / 0.05 - 1.0).abs().clamp(0.01, 1.0);
|
||||||
|
|
||||||
|
let shade_color = |color: Color| {
|
||||||
|
let color::Hsla(hue, sat, lum, alp) = color.to_hsl();
|
||||||
|
color::hsla(hue, sat / brightness, lum * brightness.sqrt(), alp * alpha)
|
||||||
|
};
|
||||||
|
|
||||||
|
let quality_col_image = 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,
|
||||||
|
};
|
||||||
|
let quality_col = get_quality_col(&**item);
|
||||||
|
|
||||||
|
Image::new(self.imgs.pixel)
|
||||||
|
.color(Some(shade_color(quality_col.alpha(0.7))))
|
||||||
|
.w_h(ICON_BG_SIZE, ICON_BG_SIZE)
|
||||||
|
.top_left_with_margins_on(list_message.widget_id, MESSAGE_VERTICAL_PADDING, 0.0)
|
||||||
|
.set(state.ids.message_icon_bgs[i], ui);
|
||||||
|
|
||||||
|
Image::new(quality_col_image)
|
||||||
|
.color(Some(shade_color(color::hsla(0.0, 0.0, 1.0, 1.0))))
|
||||||
|
.wh_of(state.ids.message_icon_bgs[i])
|
||||||
|
.middle_of(state.ids.message_icon_bgs[i])
|
||||||
|
.set(state.ids.message_icon_frames[i], ui);
|
||||||
|
|
||||||
|
Image::new(animate_by_pulse(
|
||||||
|
&self.item_imgs.img_ids_or_not_found_img((&**item).into()),
|
||||||
|
self.pulse,
|
||||||
|
))
|
||||||
|
.color(Some(shade_color(color::hsla(0.0, 0.0, 1.0, 1.0))))
|
||||||
|
.w_h(ICON_SIZE, ICON_SIZE)
|
||||||
|
.middle_of(state.ids.message_icon_bgs[i])
|
||||||
|
.with_item_tooltip(self.item_tooltip_manager, &**item, &None, &item_tooltip)
|
||||||
|
.set(state.ids.message_icons[i], ui);
|
||||||
|
|
||||||
|
let label = if *amount == 1 {
|
||||||
|
item.name.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}x {}", amount, item.name)
|
||||||
|
};
|
||||||
|
let label_font_size = 20;
|
||||||
|
|
||||||
|
Text::new(&label)
|
||||||
|
.top_left_with_margins_on(
|
||||||
|
list_message.widget_id,
|
||||||
|
MESSAGE_VERTICAL_PADDING + 1.0,
|
||||||
|
ICON_BG_SIZE + ICON_LABEL_SPACER,
|
||||||
|
)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(self.fonts.cyri.scale(label_font_size))
|
||||||
|
.color(shade_color(quality_col))
|
||||||
|
.graphics_for(state.ids.message_icons[i])
|
||||||
|
.and(|text| {
|
||||||
|
let text_width = match text.get_x_dimension(ui) {
|
||||||
|
Dimension::Absolute(x) => x,
|
||||||
|
_ => std::f64::MAX,
|
||||||
|
}
|
||||||
|
.min(BOX_WIDTH - (ICON_BG_SIZE + ICON_LABEL_SPACER));
|
||||||
|
text.w(text_width)
|
||||||
|
})
|
||||||
|
.set(state.ids.message_texts[i], ui);
|
||||||
|
Text::new(&label)
|
||||||
|
.depth(1.0)
|
||||||
|
.parent(list_message.widget_id)
|
||||||
|
.x_y_relative_to(state.ids.message_texts[i], -1.0, -1.0)
|
||||||
|
.wh_of(state.ids.message_texts[i])
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(self.fonts.cyri.scale(label_font_size))
|
||||||
|
.color(shade_color(color::rgba(0.0, 0.0, 0.0, 1.0)))
|
||||||
|
.set(state.ids.message_text_shadows[i], ui);
|
||||||
|
|
||||||
|
let height = 2.0 * MESSAGE_VERTICAL_PADDING
|
||||||
|
+ ICON_BG_SIZE.max(
|
||||||
|
1.0 + ui
|
||||||
|
.rect_of(state.ids.message_texts[i])
|
||||||
|
.map_or(0.0, |r| r.h() + label_font_size as f64 / 3.0),
|
||||||
|
/* add to height since rect height does not account for lower parts of
|
||||||
|
* letters */
|
||||||
|
);
|
||||||
|
|
||||||
|
let rect = Rectangle::fill_with([BOX_WIDTH, height], color::TRANSPARENT);
|
||||||
|
|
||||||
|
list_message.set(rect, ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ mod group;
|
|||||||
mod hotbar;
|
mod hotbar;
|
||||||
pub mod img_ids;
|
pub mod img_ids;
|
||||||
pub mod item_imgs;
|
pub mod item_imgs;
|
||||||
|
mod loot_scroller;
|
||||||
mod map;
|
mod map;
|
||||||
mod minimap;
|
mod minimap;
|
||||||
mod overhead;
|
mod overhead;
|
||||||
@ -25,6 +26,7 @@ pub mod util;
|
|||||||
pub use crafting::CraftingTab;
|
pub use crafting::CraftingTab;
|
||||||
pub use hotbar::{SlotContents as HotbarSlotContents, State as HotbarState};
|
pub use hotbar::{SlotContents as HotbarSlotContents, State as HotbarState};
|
||||||
pub use item_imgs::animate_by_pulse;
|
pub use item_imgs::animate_by_pulse;
|
||||||
|
pub use loot_scroller::LootMessage;
|
||||||
pub use settings_window::ScaleChange;
|
pub use settings_window::ScaleChange;
|
||||||
|
|
||||||
use bag::Bag;
|
use bag::Bag;
|
||||||
@ -38,6 +40,7 @@ use esc_menu::EscMenu;
|
|||||||
use group::Group;
|
use group::Group;
|
||||||
use img_ids::Imgs;
|
use img_ids::Imgs;
|
||||||
use item_imgs::ItemImgs;
|
use item_imgs::ItemImgs;
|
||||||
|
use loot_scroller::LootScroller;
|
||||||
use map::Map;
|
use map::Map;
|
||||||
use minimap::MiniMap;
|
use minimap::MiniMap;
|
||||||
use popup::Popup;
|
use popup::Popup;
|
||||||
@ -60,7 +63,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
settings::chat::ChatFilter,
|
settings::chat::ChatFilter,
|
||||||
ui::{
|
ui::{
|
||||||
fonts::Fonts, img_ids::Rotations, slot, slot::SlotKey, Graphic, Ingameable, ScaleMode, Ui,
|
self, fonts::Fonts, img_ids::Rotations, slot, slot::SlotKey, Graphic, Ingameable,
|
||||||
|
ScaleMode, Ui,
|
||||||
},
|
},
|
||||||
window::{Event as WinEvent, GameInput},
|
window::{Event as WinEvent, GameInput},
|
||||||
GlobalState,
|
GlobalState,
|
||||||
@ -95,7 +99,7 @@ use conrod_core::{
|
|||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
@ -161,8 +165,6 @@ const REGION_COLOR: Color = Color::Rgba(0.8, 1.0, 0.8, 1.0);
|
|||||||
const KILL_COLOR: Color = Color::Rgba(1.0, 0.17, 0.17, 1.0);
|
const KILL_COLOR: Color = Color::Rgba(1.0, 0.17, 0.17, 1.0);
|
||||||
/// Color for global messages
|
/// Color for global messages
|
||||||
const WORLD_COLOR: Color = Color::Rgba(0.95, 1.0, 0.95, 1.0);
|
const WORLD_COLOR: Color = Color::Rgba(0.95, 1.0, 0.95, 1.0);
|
||||||
/// Color for collected loot messages
|
|
||||||
const LOOT_COLOR: Color = Color::Rgba(0.69, 0.57, 1.0, 1.0);
|
|
||||||
|
|
||||||
//Nametags
|
//Nametags
|
||||||
const GROUP_MEMBER: Color = Color::Rgba(0.47, 0.84, 1.0, 1.0);
|
const GROUP_MEMBER: Color = Color::Rgba(0.47, 0.84, 1.0, 1.0);
|
||||||
@ -258,6 +260,7 @@ widget_ids! {
|
|||||||
|
|
||||||
// External
|
// External
|
||||||
chat,
|
chat,
|
||||||
|
loot_scroller,
|
||||||
map,
|
map,
|
||||||
world_map,
|
world_map,
|
||||||
character_window,
|
character_window,
|
||||||
@ -728,7 +731,7 @@ impl Show {
|
|||||||
|
|
||||||
/// If all of the menus are closed, adjusts coordinates of cursor to center
|
/// If all of the menus are closed, adjusts coordinates of cursor to center
|
||||||
/// of screen
|
/// of screen
|
||||||
fn toggle_cursor_on_menu_close(&self, global_state: &mut GlobalState) {
|
fn toggle_cursor_on_menu_close(&self, global_state: &mut GlobalState, ui: &mut Ui) {
|
||||||
if !self.bag
|
if !self.bag
|
||||||
&& !self.trade
|
&& !self.trade
|
||||||
&& !self.esc_menu
|
&& !self.esc_menu
|
||||||
@ -740,6 +743,9 @@ impl Show {
|
|||||||
&& !self.intro
|
&& !self.intro
|
||||||
&& global_state.window.is_cursor_grabbed()
|
&& global_state.window.is_cursor_grabbed()
|
||||||
{
|
{
|
||||||
|
ui.handle_event(ui::Event(
|
||||||
|
conrod_core::input::Motion::MouseCursor { x: 0.0, y: 0.0 }.into(),
|
||||||
|
));
|
||||||
global_state.window.center_cursor();
|
global_state.window.center_cursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -782,6 +788,9 @@ pub struct Hud {
|
|||||||
item_imgs: ItemImgs,
|
item_imgs: ItemImgs,
|
||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
rot_imgs: ImgsRot,
|
rot_imgs: ImgsRot,
|
||||||
|
failed_block_pickups: HashMap<Vec3<i32>, f32>,
|
||||||
|
failed_entity_pickups: HashMap<EcsEntity, f32>,
|
||||||
|
new_loot_messages: VecDeque<LootMessage>,
|
||||||
new_messages: VecDeque<comp::ChatMsg>,
|
new_messages: VecDeque<comp::ChatMsg>,
|
||||||
new_notifications: VecDeque<Notification>,
|
new_notifications: VecDeque<Notification>,
|
||||||
speech_bubbles: HashMap<Uid, comp::SpeechBubble>,
|
speech_bubbles: HashMap<Uid, comp::SpeechBubble>,
|
||||||
@ -864,6 +873,9 @@ impl Hud {
|
|||||||
item_imgs,
|
item_imgs,
|
||||||
fonts,
|
fonts,
|
||||||
ids,
|
ids,
|
||||||
|
failed_block_pickups: HashMap::default(),
|
||||||
|
failed_entity_pickups: HashMap::default(),
|
||||||
|
new_loot_messages: VecDeque::new(),
|
||||||
new_messages: VecDeque::new(),
|
new_messages: VecDeque::new(),
|
||||||
new_notifications: VecDeque::new(),
|
new_notifications: VecDeque::new(),
|
||||||
speech_bubbles: HashMap::new(),
|
speech_bubbles: HashMap::new(),
|
||||||
@ -1434,8 +1446,9 @@ impl Hud {
|
|||||||
let mut overitem_walker = self.ids.overitems.walk();
|
let mut overitem_walker = self.ids.overitems.walk();
|
||||||
let mut sct_walker = self.ids.scts.walk();
|
let mut sct_walker = self.ids.scts.walk();
|
||||||
let mut sct_bg_walker = self.ids.sct_bgs.walk();
|
let mut sct_bg_walker = self.ids.sct_bgs.walk();
|
||||||
|
let pulse = self.pulse;
|
||||||
|
|
||||||
let make_overitem = |item: &Item, pos, distance, active, fonts| {
|
let make_overitem = |item: &Item, pos, distance, properties, fonts| {
|
||||||
let text = if item.amount() > 1 {
|
let text = if item.amount() > 1 {
|
||||||
format!("{} x {}", item.amount(), item.name())
|
format!("{} x {}", item.amount(), item.name())
|
||||||
} else {
|
} else {
|
||||||
@ -1450,15 +1463,22 @@ impl Hud {
|
|||||||
quality,
|
quality,
|
||||||
distance,
|
distance,
|
||||||
fonts,
|
fonts,
|
||||||
|
&i18n,
|
||||||
&global_state.settings.controls,
|
&global_state.settings.controls,
|
||||||
// If we're currently set to interact with the item...
|
// If we're currently set to interact with the item...
|
||||||
active,
|
properties,
|
||||||
|
pulse,
|
||||||
&global_state.window.key_layout,
|
&global_state.window.key_layout,
|
||||||
)
|
)
|
||||||
.x_y(0.0, 100.0)
|
.x_y(0.0, 100.0)
|
||||||
.position_ingame(pos)
|
.position_ingame(pos)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.failed_block_pickups
|
||||||
|
.retain(|_, t| pulse - *t < overitem::PICKUP_FAILED_FADE_OUT_TIME);
|
||||||
|
self.failed_entity_pickups
|
||||||
|
.retain(|_, t| pulse - *t < overitem::PICKUP_FAILED_FADE_OUT_TIME);
|
||||||
|
|
||||||
// Render overitem: name, etc.
|
// Render overitem: name, etc.
|
||||||
for (entity, pos, item, distance) in (&entities, &pos, &items)
|
for (entity, pos, item, distance) in (&entities, &pos, &items)
|
||||||
.join()
|
.join()
|
||||||
@ -1474,7 +1494,10 @@ impl Hud {
|
|||||||
item,
|
item,
|
||||||
pos.0 + Vec3::unit_z() * 1.2,
|
pos.0 + Vec3::unit_z() * 1.2,
|
||||||
distance,
|
distance,
|
||||||
interactable.as_ref().and_then(|i| i.entity()) == Some(entity),
|
overitem::OveritemProperties {
|
||||||
|
active: interactable.as_ref().and_then(|i| i.entity()) == Some(entity),
|
||||||
|
pickup_failed_pulse: self.failed_entity_pickups.get(&entity).copied(),
|
||||||
|
},
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
)
|
)
|
||||||
.set(overitem_id, ui_widgets);
|
.set(overitem_id, ui_widgets);
|
||||||
@ -1487,6 +1510,10 @@ impl Hud {
|
|||||||
&mut ui_widgets.widget_id_generator(),
|
&mut ui_widgets.widget_id_generator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let overitem_properties = overitem::OveritemProperties {
|
||||||
|
active: true,
|
||||||
|
pickup_failed_pulse: self.failed_block_pickups.get(&pos).copied(),
|
||||||
|
};
|
||||||
let pos = pos.map(|e| e as f32 + 0.5);
|
let pos = pos.map(|e| e as f32 + 0.5);
|
||||||
let over_pos = pos + Vec3::unit_z() * 0.7;
|
let over_pos = pos + Vec3::unit_z() * 0.7;
|
||||||
|
|
||||||
@ -1497,8 +1524,10 @@ impl Hud {
|
|||||||
overitem::TEXT_COLOR,
|
overitem::TEXT_COLOR,
|
||||||
pos.distance_squared(player_pos),
|
pos.distance_squared(player_pos),
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
|
&i18n,
|
||||||
&global_state.settings.controls,
|
&global_state.settings.controls,
|
||||||
true,
|
overitem_properties,
|
||||||
|
self.pulse,
|
||||||
&global_state.window.key_layout,
|
&global_state.window.key_layout,
|
||||||
)
|
)
|
||||||
.x_y(0.0, 100.0)
|
.x_y(0.0, 100.0)
|
||||||
@ -1509,7 +1538,7 @@ impl Hud {
|
|||||||
&item,
|
&item,
|
||||||
over_pos,
|
over_pos,
|
||||||
pos.distance_squared(player_pos),
|
pos.distance_squared(player_pos),
|
||||||
true,
|
overitem_properties,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
)
|
)
|
||||||
.set(overitem_id, ui_widgets);
|
.set(overitem_id, ui_widgets);
|
||||||
@ -1520,8 +1549,10 @@ impl Hud {
|
|||||||
overitem::TEXT_COLOR,
|
overitem::TEXT_COLOR,
|
||||||
pos.distance_squared(player_pos),
|
pos.distance_squared(player_pos),
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
|
&i18n,
|
||||||
&global_state.settings.controls,
|
&global_state.settings.controls,
|
||||||
true,
|
overitem_properties,
|
||||||
|
self.pulse,
|
||||||
&global_state.window.key_layout,
|
&global_state.window.key_layout,
|
||||||
)
|
)
|
||||||
.x_y(0.0, 100.0)
|
.x_y(0.0, 100.0)
|
||||||
@ -2646,6 +2677,24 @@ impl Hud {
|
|||||||
self.new_messages = VecDeque::new();
|
self.new_messages = VecDeque::new();
|
||||||
self.new_notifications = VecDeque::new();
|
self.new_notifications = VecDeque::new();
|
||||||
|
|
||||||
|
//Loot
|
||||||
|
LootScroller::new(
|
||||||
|
&mut self.new_loot_messages,
|
||||||
|
client,
|
||||||
|
&self.show,
|
||||||
|
&self.imgs,
|
||||||
|
&self.item_imgs,
|
||||||
|
&self.rot_imgs,
|
||||||
|
&self.fonts,
|
||||||
|
&*i18n,
|
||||||
|
&msm,
|
||||||
|
item_tooltip_manager,
|
||||||
|
self.pulse,
|
||||||
|
)
|
||||||
|
.set(self.ids.loot_scroller, ui_widgets);
|
||||||
|
|
||||||
|
self.new_loot_messages = VecDeque::new();
|
||||||
|
|
||||||
// Windows
|
// Windows
|
||||||
|
|
||||||
// Char Window will always appear at the left side. Other Windows default to the
|
// Char Window will always appear at the left side. Other Windows default to the
|
||||||
@ -3201,6 +3250,18 @@ impl Hud {
|
|||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_failed_block_pickup(&mut self, pos: Vec3<i32>) {
|
||||||
|
self.failed_block_pickups.insert(pos, self.pulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_failed_entity_pickup(&mut self, entity: EcsEntity) {
|
||||||
|
self.failed_entity_pickups.insert(entity, self.pulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_loot_message(&mut self, item: LootMessage) {
|
||||||
|
self.new_loot_messages.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_message(&mut self, msg: comp::ChatMsg) { self.new_messages.push_back(msg); }
|
pub fn new_message(&mut self, msg: comp::ChatMsg) { self.new_messages.push_back(msg); }
|
||||||
|
|
||||||
pub fn new_notification(&mut self, msg: Notification) { self.new_notifications.push_back(msg); }
|
pub fn new_notification(&mut self, msg: Notification) { self.new_notifications.push_back(msg); }
|
||||||
@ -3443,7 +3504,8 @@ impl Hud {
|
|||||||
|
|
||||||
// When a player closes all menus, resets the cursor
|
// When a player closes all menus, resets the cursor
|
||||||
// to the center of the screen
|
// to the center of the screen
|
||||||
self.show.toggle_cursor_on_menu_close(global_state);
|
self.show
|
||||||
|
.toggle_cursor_on_menu_close(global_state, &mut self.ui);
|
||||||
matching_key
|
matching_key
|
||||||
},
|
},
|
||||||
// Else the player is typing in chat
|
// Else the player is typing in chat
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
i18n::Localization,
|
||||||
settings::ControlSettings,
|
settings::ControlSettings,
|
||||||
ui::{fonts::Fonts, Ingameable},
|
ui::{fonts::Fonts, Ingameable},
|
||||||
window::GameInput,
|
window::GameInput,
|
||||||
};
|
};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
|
color,
|
||||||
widget::{self, RoundedRectangle, Text},
|
widget::{self, RoundedRectangle, Text},
|
||||||
widget_ids, Color, Colorable, Positionable, Widget, WidgetCommon,
|
widget_ids, Color, Colorable, Positionable, Widget, WidgetCommon,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub const TEXT_COLOR: Color = Color::Rgba(0.61, 0.61, 0.89, 1.0);
|
|
||||||
use keyboard_keynames::key_layout::KeyLayout;
|
use keyboard_keynames::key_layout::KeyLayout;
|
||||||
|
|
||||||
|
pub const TEXT_COLOR: Color = Color::Rgba(0.61, 0.61, 0.89, 1.0);
|
||||||
|
pub const PICKUP_FAILED_FADE_OUT_TIME: f32 = 1.5;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
// Name
|
// Name
|
||||||
@ -20,6 +24,9 @@ widget_ids! {
|
|||||||
// Key
|
// Key
|
||||||
btn_bg,
|
btn_bg,
|
||||||
btn,
|
btn,
|
||||||
|
// Inventory full
|
||||||
|
inv_full_bg,
|
||||||
|
inv_full,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,10 +38,12 @@ pub struct Overitem<'a> {
|
|||||||
quality: Color,
|
quality: Color,
|
||||||
distance_from_player_sqr: f32,
|
distance_from_player_sqr: f32,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
|
localized_strings: &'a Localization,
|
||||||
controls: &'a ControlSettings,
|
controls: &'a ControlSettings,
|
||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
active: bool,
|
properties: OveritemProperties,
|
||||||
|
pulse: f32,
|
||||||
key_layout: &'a Option<KeyLayout>,
|
key_layout: &'a Option<KeyLayout>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +53,10 @@ impl<'a> Overitem<'a> {
|
|||||||
quality: Color,
|
quality: Color,
|
||||||
distance_from_player_sqr: f32,
|
distance_from_player_sqr: f32,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
|
localized_strings: &'a Localization,
|
||||||
controls: &'a ControlSettings,
|
controls: &'a ControlSettings,
|
||||||
active: bool,
|
properties: OveritemProperties,
|
||||||
|
pulse: f32,
|
||||||
key_layout: &'a Option<KeyLayout>,
|
key_layout: &'a Option<KeyLayout>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -53,14 +64,21 @@ impl<'a> Overitem<'a> {
|
|||||||
quality,
|
quality,
|
||||||
distance_from_player_sqr,
|
distance_from_player_sqr,
|
||||||
fonts,
|
fonts,
|
||||||
|
localized_strings,
|
||||||
controls,
|
controls,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
active,
|
properties,
|
||||||
|
pulse,
|
||||||
key_layout,
|
key_layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct OveritemProperties {
|
||||||
|
pub active: bool,
|
||||||
|
pub pickup_failed_pulse: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
ids: Ids,
|
ids: Ids,
|
||||||
}
|
}
|
||||||
@ -74,10 +92,14 @@ impl<'a> Ingameable for Overitem<'a> {
|
|||||||
2 + match self
|
2 + match self
|
||||||
.controls
|
.controls
|
||||||
.get_binding(GameInput::Interact)
|
.get_binding(GameInput::Interact)
|
||||||
.filter(|_| self.active)
|
.filter(|_| self.properties.active)
|
||||||
{
|
{
|
||||||
Some(_) => 2,
|
Some(_) => 2,
|
||||||
None => 0,
|
None => 0,
|
||||||
|
} + if self.properties.pickup_failed_pulse.is_some() {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,14 +136,19 @@ impl<'a> Widget for Overitem<'a> {
|
|||||||
// * 20.0)
|
// * 20.0)
|
||||||
// .into();
|
// .into();
|
||||||
let scale = 30.0;
|
let scale = 30.0;
|
||||||
|
|
||||||
let text_font_size = scale * 1.0;
|
let text_font_size = scale * 1.0;
|
||||||
let text_pos_y = scale * 1.2;
|
let text_pos_y = scale * 1.2;
|
||||||
|
|
||||||
let btn_rect_size = scale * 0.8;
|
let btn_rect_size = scale * 0.8;
|
||||||
let btn_font_size = scale * 0.6;
|
let btn_font_size = scale * 0.6;
|
||||||
let btn_rect_pos_y = 0.0;
|
let btn_rect_pos_y = 0.0;
|
||||||
let btn_text_pos_y = btn_rect_pos_y + ((btn_rect_size - btn_font_size) * 0.5);
|
let btn_text_pos_y = btn_rect_pos_y + ((btn_rect_size - btn_font_size) * 0.5);
|
||||||
let btn_radius = btn_rect_size / 5.0;
|
let btn_radius = btn_rect_size / 5.0;
|
||||||
|
|
||||||
|
let inv_full_font_size = scale * 1.0;
|
||||||
|
let inv_full_pos_y = scale * 2.4;
|
||||||
|
|
||||||
// Item Name
|
// Item Name
|
||||||
Text::new(&self.name)
|
Text::new(&self.name)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
@ -144,7 +171,7 @@ impl<'a> Widget for Overitem<'a> {
|
|||||||
if let Some(key_button) = self
|
if let Some(key_button) = self
|
||||||
.controls
|
.controls
|
||||||
.get_binding(GameInput::Interact)
|
.get_binding(GameInput::Interact)
|
||||||
.filter(|_| self.active)
|
.filter(|_| self.properties.active)
|
||||||
{
|
{
|
||||||
RoundedRectangle::fill_with([btn_rect_size, btn_rect_size], btn_radius, btn_color)
|
RoundedRectangle::fill_with([btn_rect_size, btn_rect_size], btn_radius, btn_color)
|
||||||
.x_y(0.0, btn_rect_pos_y)
|
.x_y(0.0, btn_rect_pos_y)
|
||||||
@ -160,5 +187,34 @@ impl<'a> Widget for Overitem<'a> {
|
|||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.btn, ui);
|
.set(state.ids.btn, ui);
|
||||||
}
|
}
|
||||||
|
if let Some(time) = self.properties.pickup_failed_pulse {
|
||||||
|
//should never exceed 1.0, but just in case
|
||||||
|
let age = ((self.pulse - time) / PICKUP_FAILED_FADE_OUT_TIME).clamp(0.0, 1.0);
|
||||||
|
|
||||||
|
let alpha = 1.0 - age.powi(4);
|
||||||
|
let brightness = 1.0 / (age / 0.07 - 1.0).abs().clamp(0.01, 1.0);
|
||||||
|
let shade_color = |color: Color| {
|
||||||
|
let color::Hsla(hue, sat, lum, alp) = color.to_hsl();
|
||||||
|
color::hsla(hue, sat / brightness, lum * brightness.sqrt(), alp * alpha)
|
||||||
|
};
|
||||||
|
|
||||||
|
Text::new(self.localized_strings.get("hud.inventory_full"))
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(inv_full_font_size as u32)
|
||||||
|
.color(shade_color(Color::Rgba(0.0, 0.0, 0.0, 1.0)))
|
||||||
|
.x_y(-1.0, inv_full_pos_y - 2.0)
|
||||||
|
.parent(id)
|
||||||
|
.depth(self.distance_from_player_sqr + 6.0)
|
||||||
|
.set(state.ids.inv_full_bg, ui);
|
||||||
|
|
||||||
|
Text::new(self.localized_strings.get("hud.inventory_full"))
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(inv_full_font_size as u32)
|
||||||
|
.color(shade_color(Color::Rgba(1.0, 0.0, 0.0, 1.0)))
|
||||||
|
.x_y(0.0, inv_full_pos_y)
|
||||||
|
.parent(id)
|
||||||
|
.depth(self.distance_from_player_sqr + 5.0)
|
||||||
|
.set(state.ids.inv_full, ui);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
pub mod settings_change;
|
pub mod settings_change;
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::HashSet, rc::Rc, time::Duration};
|
use std::{cell::RefCell, collections::HashSet, rc::Rc, result::Result, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Join, WorldExt};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{
|
||||||
|
assets::AssetExt,
|
||||||
comp,
|
comp,
|
||||||
comp::{
|
comp::{
|
||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{EquipSlot, Slot},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{tool::ToolKind, ItemDesc},
|
item::{tool::ToolKind, ItemDef, ItemDesc},
|
||||||
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Vel,
|
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Vel,
|
||||||
},
|
},
|
||||||
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||||
@ -34,7 +35,7 @@ use common_net::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
audio::sfx::SfxEvent,
|
audio::sfx::SfxEvent,
|
||||||
hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, PromptDialogSettings},
|
hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, LootMessage, PromptDialogSettings},
|
||||||
key_state::KeyState,
|
key_state::KeyState,
|
||||||
menu::char_selection::CharSelectionState,
|
menu::char_selection::CharSelectionState,
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
@ -179,23 +180,32 @@ impl SessionState {
|
|||||||
let sfx_trigger_item = sfx_triggers.get_key_value(&SfxEvent::from(&inv_event));
|
let sfx_trigger_item = sfx_triggers.get_key_value(&SfxEvent::from(&inv_event));
|
||||||
global_state.audio.emit_sfx_item(sfx_trigger_item);
|
global_state.audio.emit_sfx_item(sfx_trigger_item);
|
||||||
|
|
||||||
let i18n = global_state.i18n.read();
|
|
||||||
|
|
||||||
match inv_event {
|
match inv_event {
|
||||||
InventoryUpdateEvent::CollectFailed => {
|
InventoryUpdateEvent::BlockCollectFailed(pos) => {
|
||||||
self.hud.new_message(ChatMsg {
|
self.hud.add_failed_block_pickup(pos);
|
||||||
message: i18n.get("hud.chat.loot_fail").to_string(),
|
},
|
||||||
chat_type: ChatType::CommandError,
|
InventoryUpdateEvent::EntityCollectFailed(uid) => {
|
||||||
});
|
if let Some(entity) = client.state().ecs().entity_from_uid(uid.into()) {
|
||||||
|
self.hud.add_failed_entity_pickup(entity);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
InventoryUpdateEvent::Collected(item) => {
|
InventoryUpdateEvent::Collected(item) => {
|
||||||
self.hud.new_message(ChatMsg {
|
match Arc::<ItemDef>::load_cloned(item.item_definition_id()) {
|
||||||
message: i18n
|
Result::Ok(item_def) => {
|
||||||
.get("hud.chat.loot_msg")
|
self.hud.new_loot_message(LootMessage {
|
||||||
.replace("{item}", item.name()),
|
item: item_def,
|
||||||
chat_type: ChatType::Loot,
|
amount: item.amount(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
Result::Err(e) => {
|
||||||
|
warn!(
|
||||||
|
?e,
|
||||||
|
"Item not present on client: {}",
|
||||||
|
item.item_definition_id()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -44,7 +44,6 @@ impl ChatFilter {
|
|||||||
ChatType::NpcSay(..) => true,
|
ChatType::NpcSay(..) => true,
|
||||||
ChatType::NpcTell(..) => true,
|
ChatType::NpcTell(..) => true,
|
||||||
ChatType::Meta => true,
|
ChatType::Meta => true,
|
||||||
ChatType::Loot => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user