Implement chat mode icons in chat window

This commit is contained in:
CapsizeGlimmer 2020-06-05 18:36:31 -04:00 committed by Forest Anderson
parent 22315a6e1d
commit 35ed03aa18
20 changed files with 206 additions and 85 deletions

BIN
assets/voxygen/element/icons/chat/broadcast_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/faction.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/faction_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/group.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/group_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/kill_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/private_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/region.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/region_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/say.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/say_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/tell.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/tell_small.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/world.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/chat/world_small.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -94,25 +94,28 @@ impl ChatMsg {
}
pub fn to_bubble(&self) -> Option<(SpeechBubble, Uid)> {
let tuple = match &self.chat_type {
ChatType::Broadcast => None,
ChatType::Private => None,
ChatType::Kill => None,
ChatType::Tell(u, _) => Some((SpeechBubbleIcon::Tell, u, None)),
ChatType::Say(u) => Some((SpeechBubbleIcon::Say, u, None)),
ChatType::Group(u, _s) => Some((SpeechBubbleIcon::Group, u, None)),
ChatType::Faction(u, _s) => Some((SpeechBubbleIcon::Faction, u, None)),
ChatType::Region(u) => Some((SpeechBubbleIcon::Region, u, None)),
ChatType::World(u) => Some((SpeechBubbleIcon::World, u, None)),
ChatType::Npc(u, r) => Some((SpeechBubbleIcon::None, u, Some(r))),
};
tuple.map(|(icon, from, npc_rand)| {
if let Some(r) = npc_rand {
(SpeechBubble::npc_new(self.message.clone(), *r, icon), *from)
} else {
(SpeechBubble::player_new(self.message.clone(), icon), *from)
}
})
let icon = self.icon();
if let ChatType::Npc(from, r) = self.chat_type {
Some((SpeechBubble::npc_new(self.message.clone(), r, icon), from))
} else {
self.uid()
.map(|from| (SpeechBubble::player_new(self.message.clone(), icon), from))
}
}
pub fn icon(&self) -> SpeechBubbleIcon {
match &self.chat_type {
ChatType::Broadcast => SpeechBubbleIcon::Broadcast,
ChatType::Private => SpeechBubbleIcon::Private,
ChatType::Kill => SpeechBubbleIcon::Kill,
ChatType::Tell(_u, _) => SpeechBubbleIcon::Tell,
ChatType::Say(_u) => SpeechBubbleIcon::Say,
ChatType::Group(_u, _s) => SpeechBubbleIcon::Group,
ChatType::Faction(_u, _s) => SpeechBubbleIcon::Faction,
ChatType::Region(_u) => SpeechBubbleIcon::Region,
ChatType::World(_u) => SpeechBubbleIcon::World,
ChatType::Npc(_u, _r) => SpeechBubbleIcon::None,
}
}
pub fn uid(&self) -> Option<Uid> {
@ -174,6 +177,10 @@ pub enum SpeechBubbleIcon {
Group,
Faction,
World,
// Server chat types
Broadcast,
Private,
Kill,
// For NPCs
Quest, // TODO not implemented
Trade, // TODO not implemented

View File

@ -652,16 +652,18 @@ fn handle_help(
if let Some(cmd) = scan_fmt_some!(&args, &action.arg_fmt(), ChatCommand) {
server.notify_client(client, ServerMsg::private(String::from(cmd.help_string())));
} else {
let mut message = String::new();
for cmd in CHAT_COMMANDS.iter() {
if !cmd.needs_admin() || server.entity_is_admin(client) {
server.notify_client(client, ServerMsg::private(String::from(cmd.help_string())));
message += &cmd.help_string();
message += "\n";
}
}
let shortcuts = CHAT_SHORTCUTS.iter().fold(
"Aditionally, you can use the following shortcuts:".to_string(),
|s, (k, v)| format!("{}\n/{} => /{}", s, k, v.keyword()),
);
server.notify_client(client, ServerMsg::private(shortcuts.to_string()));
message += "Additionally, you can use the following shortcuts:";
for (k, v) in CHAT_SHORTCUTS.iter() {
message += &format!(" /{} => /{}", k, v.keyword());
}
server.notify_client(client, ServerMsg::private(message));
}
}

View File

@ -15,8 +15,8 @@ use conrod_core::{
self,
cursor::{self, Index},
},
widget::{self, Button, Id, List, Rectangle, Text, TextEdit},
widget_ids, Colorable, Positionable, Sizeable, Ui, UiCell, Widget, WidgetCommon,
widget::{self, Button, Id, Image, List, Rectangle, Text, TextEdit},
widget_ids, Color, Colorable, Positionable, Sizeable, Ui, UiCell, Widget, WidgetCommon,
};
use specs::world::WorldExt;
use std::collections::VecDeque;
@ -28,12 +28,15 @@ widget_ids! {
chat_input,
chat_input_bg,
chat_arrow,
completion_box,
chat_icons[],
}
}
const MAX_MESSAGES: usize = 100;
const CHAT_BOX_WIDTH: f64 = 470.0;
const CHAT_BOX_HEIGHT: f64 = 174.0;
#[derive(WidgetCommon)]
pub struct Chat<'a> {
new_messages: &'a mut VecDeque<ChatMsg>,
@ -291,10 +294,10 @@ impl<'a> Widget for Chat<'a> {
Dimension::Absolute(y) => y + 6.0,
_ => 0.0,
};
Rectangle::fill([470.0, y])
Rectangle::fill([CHAT_BOX_WIDTH, y])
.rgba(0.0, 0.0, 0.0, transp + 0.1)
.bottom_left_with_margins_on(ui.window, 10.0, 10.0)
.w(470.0)
.w(CHAT_BOX_WIDTH)
.set(state.ids.chat_input_bg, ui);
if let Some(str) = text_edit
@ -309,27 +312,8 @@ impl<'a> Widget for Chat<'a> {
}
}
let alias_of_uid = |uid| {
self.client
.player_list
.get(uid)
.map_or("<?>".to_string(), |player_info| {
if player_info.is_admin {
format!("ADMIN - {}", player_info.player_alias)
} else {
player_info.player_alias.to_string()
}
})
};
let message_format = |uid, message, group| {
if let Some(group) = group {
format!("{{{}}} [{}]: {}", group, alias_of_uid(uid), message)
} else {
format!("[{}]: {}", alias_of_uid(uid), message)
}
};
// Message box
Rectangle::fill([470.0, 174.0])
Rectangle::fill([CHAT_BOX_WIDTH, CHAT_BOX_HEIGHT])
.rgba(0.0, 0.0, 0.0, transp)
.and(|r| {
if input_focused {
@ -338,49 +322,30 @@ impl<'a> Widget for Chat<'a> {
r.bottom_left_with_margins_on(ui.window, 10.0, 10.0)
}
})
.crop_kids()
.set(state.ids.message_box_bg, ui);
let (mut items, _) = List::flow_down(state.messages.len() + 1)
.top_left_of(state.ids.message_box_bg)
.w_h(470.0, 174.0)
.top_left_with_margins_on(state.ids.message_box_bg, 0.0, 16.0)
.w_h(CHAT_BOX_WIDTH, CHAT_BOX_HEIGHT)
.scroll_kids_vertically()
.set(state.ids.message_box, ui);
if state.ids.chat_icons.len() < state.messages.len() {
state.update(|s| {
s.ids
.chat_icons
.resize(s.messages.len(), &mut ui.widget_id_generator())
});
}
while let Some(item) = items.next(ui) {
// This would be easier if conrod used the v-metrics from rusttype.
if item.i < state.messages.len() {
let ChatMsg { chat_type, message } = &state.messages[item.i];
let (color, msg) = match chat_type {
ChatType::Private => (PRIVATE_COLOR, message.to_string()),
ChatType::Broadcast => (BROADCAST_COLOR, message.to_string()),
ChatType::Kill => (KILL_COLOR, message.to_string()),
ChatType::Tell(from, to) => {
let from_alias = alias_of_uid(&from);
let to_alias = alias_of_uid(&to);
if Some(from)
== self
.client
.state()
.ecs()
.read_storage()
.get(self.client.entity())
{
(TELL_COLOR, format!("To [{}]: {}", to_alias, message))
} else {
(TELL_COLOR, format!("From [{}]: {}", from_alias, message))
}
},
ChatType::Say(uid) => (SAY_COLOR, message_format(uid, message, None)),
ChatType::Group(uid, s) => (GROUP_COLOR, message_format(uid, message, Some(s))),
ChatType::Faction(uid, s) => {
(FACTION_COLOR, message_format(uid, message, Some(s)))
},
ChatType::Region(uid) => (REGION_COLOR, message_format(uid, message, None)),
ChatType::World(uid) => (WORLD_COLOR, message_format(uid, message, None)),
ChatType::Npc(_uid, _r) => continue, // Should be filtered by hud/mod.rs
};
let (color, msg, icon) =
render_chat_line(&state.messages[item.i], &self.imgs, &self.client);
let text = Text::new(&msg)
.font_size(self.fonts.opensans.scale(15))
.font_id(self.fonts.opensans.conrod_id)
.w(470.0)
.w(CHAT_BOX_WIDTH - 16.0)
.color(color)
.line_spacing(2.0);
// Add space between messages.
@ -390,13 +355,19 @@ impl<'a> Widget for Chat<'a> {
};
let widget = text.h(y);
item.set(widget, ui);
let icon_id = state.ids.chat_icons[item.i];
Image::new(icon)
.w_h(16.0, 16.0)
.top_left_with_margins_on(item.widget_id, 2.0, -16.0)
.parent(state.ids.message_box_bg)
.set(icon_id, ui);
} else {
// Spacer at bottom of the last message so that it is not cut off.
// Needs to be larger than the space above.
let widget = Text::new("")
.font_size(self.fonts.opensans.scale(6))
.font_id(self.fonts.opensans.conrod_id)
.w(470.0);
.w(CHAT_BOX_WIDTH);
item.set(widget, ui);
};
}
@ -409,6 +380,7 @@ impl<'a> Widget for Chat<'a> {
.hover_image(self.imgs.chat_arrow_mo)
.press_image(self.imgs.chat_arrow_press)
.bottom_right_with_margins_on(state.ids.message_box_bg, 0.0, -22.0)
.parent(id)
.set(state.ids.chat_arrow, ui)
.was_clicked()
{
@ -511,3 +483,87 @@ fn cursor_offset_to_index(
cursor::index_before_char(infos, offset)
}
fn render_chat_line(
ChatMsg { chat_type, message }: &ChatMsg,
imgs: &Imgs,
client: &Client,
) -> (Color, String, conrod_core::image::Id) {
let alias_of_uid = |uid| {
client
.player_list
.get(uid)
.map_or("<?>".to_string(), |player_info| {
if player_info.is_admin {
format!("ADMIN - {}", player_info.player_alias)
} else {
player_info.player_alias.to_string()
}
})
};
let message_format = |uid, message, group| {
if let Some(group) = group {
format!("{{{}}} [{}]: {}", group, alias_of_uid(uid), message)
} else {
format!("[{}]: {}", alias_of_uid(uid), message)
}
};
match chat_type {
ChatType::Private => (PRIVATE_COLOR, message.to_string(), imgs.chat_private_small),
ChatType::Broadcast => (
BROADCAST_COLOR,
message.to_string(),
imgs.chat_broadcast_small,
),
ChatType::Kill => (KILL_COLOR, message.to_string(), imgs.chat_kill_small),
ChatType::Tell(from, to) => {
let from_alias = alias_of_uid(&from);
let to_alias = alias_of_uid(&to);
if Some(from)
== client
.state()
.ecs()
.read_storage::<common::sync::Uid>()
.get(client.entity())
{
(
TELL_COLOR,
format!("To [{}]: {}", to_alias, message),
imgs.chat_tell_small,
)
} else {
(
TELL_COLOR,
format!("From [{}]: {}", from_alias, message),
imgs.chat_tell_small,
)
}
},
ChatType::Say(uid) => (
SAY_COLOR,
message_format(uid, message, None),
imgs.chat_say_small,
),
ChatType::Group(uid, s) => (
GROUP_COLOR,
message_format(uid, message, Some(s)),
imgs.chat_group_small,
),
ChatType::Faction(uid, s) => (
FACTION_COLOR,
message_format(uid, message, Some(s)),
imgs.chat_faction_small,
),
ChatType::Region(uid) => (
REGION_COLOR,
message_format(uid, message, None),
imgs.chat_region_small,
),
ChatType::World(uid) => (
WORLD_COLOR,
message_format(uid, message, None),
imgs.chat_world_small,
),
ChatType::Npc(_uid, _r) => panic!("NPCs can't talk"), // Should be filtered by hud/mod.rs
}
}

View File

@ -54,7 +54,6 @@ image_ids! {
chat_arrow_press: "voxygen.element.buttons.arrow_down_press",
////////////////////////////////////////////////////////////////////////
<VoxelPixArtGraphic>
@ -298,6 +297,17 @@ image_ids! {
dark_bubble_bottom_right: "voxygen.element.frames.bubble_dark.bottom_right",
dark_bubble_tail: "voxygen.element.frames.bubble_dark.tail",
// Chat icons
chat_broadcast_small: "voxygen.element.icons.chat.broadcast_small",
chat_faction_small: "voxygen.element.icons.chat.faction_small",
chat_group_small: "voxygen.element.icons.chat.group_small",
chat_kill_small: "voxygen.element.icons.chat.kill_small",
chat_private_small: "voxygen.element.icons.chat.private_small",
chat_region_small: "voxygen.element.icons.chat.region_small",
chat_say_small: "voxygen.element.icons.chat.say_small",
chat_tell_small: "voxygen.element.icons.chat.tell_small",
chat_world_small: "voxygen.element.icons.chat.world_small",
<BlankGraphic>
nothing: (),
}

View File

@ -26,6 +26,7 @@ widget_ids! {
speech_bubble_bottom,
speech_bubble_bottom_right,
speech_bubble_tail,
speech_bubble_icon,
// Name
name_bg,