mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Move message processing and chat bubbles to the client
This commit is contained in:
parent
0b2a3ebe8b
commit
289ef5d6b2
@ -802,7 +802,11 @@ impl Client {
|
||||
};
|
||||
}
|
||||
},
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(uid)) => {
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(_uid)) => {
|
||||
// Don't remove players because we need to remember the
|
||||
// names of disconnected players in chat.
|
||||
|
||||
/*
|
||||
if self.player_list.remove(&uid).is_none() {
|
||||
warn!(
|
||||
"Received msg to remove uid {} from the player list by they \
|
||||
@ -810,6 +814,7 @@ impl Client {
|
||||
uid
|
||||
);
|
||||
}
|
||||
*/
|
||||
},
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias(uid, new_name)) => {
|
||||
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{path::Chaser, state::Time};
|
||||
use specs::{Component, Entity as EcsEntity, FlaggedStorage, HashMapStorage};
|
||||
use crate::path::Chaser;
|
||||
use specs::{Component, Entity as EcsEntity};
|
||||
use specs_idvs::IDVStorage;
|
||||
use vek::*;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::sync::Uid;
|
||||
use specs::Component;
|
||||
use specs_idvs::IDVStorage;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// A player's current chat mode.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -18,10 +19,26 @@ pub enum ChatMode {
|
||||
/// Talk to every player on the server
|
||||
World,
|
||||
}
|
||||
|
||||
impl Component for ChatMode {
|
||||
type Storage = IDVStorage<Self>;
|
||||
}
|
||||
|
||||
impl ChatMode {
|
||||
/// Create a message from your current chat mode and uuid.
|
||||
pub fn msg_from(&self, from: Uid, message: String) -> ChatMsg {
|
||||
let chat_type = match self {
|
||||
ChatMode::Tell(to) => ChatType::Tell(from, *to),
|
||||
ChatMode::Say => ChatType::Say(from),
|
||||
ChatMode::Region => ChatType::Region(from),
|
||||
ChatMode::Group => ChatType::Group(from),
|
||||
ChatMode::Faction => ChatType::Faction(from),
|
||||
ChatMode::World => ChatType::World(from),
|
||||
};
|
||||
ChatMsg { chat_type, message }
|
||||
}
|
||||
}
|
||||
|
||||
/// List of chat types. Note that this is a superset of `ChatMode`; this is
|
||||
/// because `ChatType::Kill`, `ChatType::Broadcast`, and `ChatType::Private`
|
||||
/// cannot be sent by players.
|
||||
@ -45,6 +62,10 @@ pub enum ChatType {
|
||||
Region(Uid),
|
||||
/// World chat
|
||||
World(Uid),
|
||||
/// Messages sent from NPCs (Not shown in chat but as speech bubbles)
|
||||
///
|
||||
/// The u16 field is a random number for selecting localization variants.
|
||||
Npc(Uid, u16),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -53,18 +74,32 @@ pub struct ChatMsg {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl ChatMode {
|
||||
/// Create a message from your current chat mode and uuid.
|
||||
pub fn msg_from(&self, from: Uid, message: String) -> ChatMsg {
|
||||
let chat_type = match self {
|
||||
ChatMode::Tell(to) => ChatType::Tell(from, *to),
|
||||
ChatMode::Say => ChatType::Say(from),
|
||||
ChatMode::Region => ChatType::Region(from),
|
||||
ChatMode::Group => ChatType::Group(from),
|
||||
ChatMode::Faction => ChatType::Faction(from),
|
||||
ChatMode::World => ChatType::World(from),
|
||||
impl ChatMsg {
|
||||
pub fn npc(uid: Uid, message: String) -> Self {
|
||||
let chat_type = ChatType::Npc(uid, rand::random());
|
||||
Self { chat_type, message }
|
||||
}
|
||||
|
||||
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) => Some((SpeechBubbleIcon::Group, u, None)),
|
||||
ChatType::Faction(u) => 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))),
|
||||
};
|
||||
ChatMsg { chat_type, message }
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,3 +120,68 @@ pub struct Faction(String);
|
||||
impl Component for Faction {
|
||||
type Storage = IDVStorage<Self>;
|
||||
}
|
||||
|
||||
/// The contents of a speech bubble
|
||||
pub enum SpeechBubbleMessage {
|
||||
/// This message was said by a player and needs no translation
|
||||
Plain(String),
|
||||
/// This message was said by an NPC. The fields are a i18n key and a random
|
||||
/// u16 index
|
||||
Localized(String, u16),
|
||||
}
|
||||
|
||||
pub enum SpeechBubbleIcon {
|
||||
// One for each chat mode
|
||||
Tell,
|
||||
Say,
|
||||
Region,
|
||||
Group,
|
||||
Faction,
|
||||
World,
|
||||
// For NPCs
|
||||
Quest, // TODO not implemented
|
||||
Trade, // TODO not implemented
|
||||
None, // No icon (default for npcs)
|
||||
}
|
||||
|
||||
/// Adds a speech bubble above the character
|
||||
pub struct SpeechBubble {
|
||||
pub message: SpeechBubbleMessage,
|
||||
pub icon: SpeechBubbleIcon,
|
||||
pub timeout: Instant,
|
||||
}
|
||||
|
||||
impl SpeechBubble {
|
||||
/// Default duration in seconds of speech bubbles
|
||||
pub const DEFAULT_DURATION: f64 = 5.0;
|
||||
|
||||
pub fn npc_new(i18n_key: String, r: u16, icon: SpeechBubbleIcon) -> Self {
|
||||
let message = SpeechBubbleMessage::Localized(i18n_key, r);
|
||||
let timeout = Instant::now() + Duration::from_secs_f64(SpeechBubble::DEFAULT_DURATION);
|
||||
Self {
|
||||
message,
|
||||
timeout,
|
||||
icon,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player_new(message: String, icon: SpeechBubbleIcon) -> Self {
|
||||
let message = SpeechBubbleMessage::Plain(message);
|
||||
let timeout = Instant::now() + Duration::from_secs_f64(SpeechBubble::DEFAULT_DURATION);
|
||||
Self {
|
||||
message,
|
||||
timeout,
|
||||
icon,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message<F>(&self, i18n_variation: F) -> String
|
||||
where
|
||||
F: Fn(String, u16) -> String,
|
||||
{
|
||||
match &self.message {
|
||||
SpeechBubbleMessage::Plain(m) => m.to_string(),
|
||||
SpeechBubbleMessage::Localized(k, i) => i18n_variation(k.to_string(), *i).to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,13 @@ mod visual;
|
||||
// Reexports
|
||||
pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout};
|
||||
pub use admin::Admin;
|
||||
pub use agent::{Agent, Alignment, SpeechBubble, SPEECH_BUBBLE_DURATION};
|
||||
pub use agent::{Agent, Alignment};
|
||||
pub use body::{
|
||||
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, golem,
|
||||
humanoid, object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData,
|
||||
};
|
||||
pub use character_state::{Attacking, CharacterState, StateUpdate};
|
||||
pub use chat::{ChatMode, ChatMsg, ChatType, Faction, Group};
|
||||
pub use chat::{ChatMode, ChatMsg, ChatType, Faction, Group, SpeechBubble};
|
||||
pub use controller::{
|
||||
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, Input, InventoryManip,
|
||||
MountState, Mounting,
|
||||
|
@ -119,6 +119,8 @@ pub enum ServerEvent {
|
||||
ClientDisconnect(EcsEntity),
|
||||
ChunkRequest(EcsEntity, Vec2<i32>),
|
||||
ChatCmd(EcsEntity, String),
|
||||
/// Send a chat message from an npc to the player
|
||||
Chat(comp::ChatMsg),
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -25,7 +25,6 @@ sum_type! {
|
||||
Sticky(comp::Sticky),
|
||||
Loadout(comp::Loadout),
|
||||
CharacterState(comp::CharacterState),
|
||||
SpeechBubble(comp::SpeechBubble),
|
||||
Pos(comp::Pos),
|
||||
Vel(comp::Vel),
|
||||
Ori(comp::Ori),
|
||||
@ -52,7 +51,6 @@ sum_type! {
|
||||
Sticky(PhantomData<comp::Sticky>),
|
||||
Loadout(PhantomData<comp::Loadout>),
|
||||
CharacterState(PhantomData<comp::CharacterState>),
|
||||
SpeechBubble(PhantomData<comp::SpeechBubble>),
|
||||
Pos(PhantomData<comp::Pos>),
|
||||
Vel(PhantomData<comp::Vel>),
|
||||
Ori(PhantomData<comp::Ori>),
|
||||
@ -79,7 +77,6 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Loadout(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::SpeechBubble(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Pos(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world),
|
||||
@ -104,7 +101,6 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Loadout(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::SpeechBubble(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Pos(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world),
|
||||
@ -133,9 +129,6 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPhantom::CharacterState(_) => {
|
||||
sync::handle_remove::<comp::CharacterState>(entity, world)
|
||||
},
|
||||
EcsCompPhantom::SpeechBubble(_) => {
|
||||
sync::handle_remove::<comp::SpeechBubble>(entity, world)
|
||||
},
|
||||
EcsCompPhantom::Pos(_) => sync::handle_remove::<comp::Pos>(entity, world),
|
||||
EcsCompPhantom::Vel(_) => sync::handle_remove::<comp::Vel>(entity, world),
|
||||
EcsCompPhantom::Ori(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
||||
|
@ -71,7 +71,7 @@ pub enum ServerMsg {
|
||||
Ping,
|
||||
Pong,
|
||||
/// A message to go into the client chat box. The client is responsible for
|
||||
/// formatting the message.
|
||||
/// formatting the message and turning it into a speech bubble.
|
||||
ChatMsg(comp::ChatMsg),
|
||||
SetPlayerEntity(Uid),
|
||||
TimeOfDay(state::TimeOfDay),
|
||||
|
@ -122,7 +122,6 @@ impl State {
|
||||
ecs.register::<comp::Sticky>();
|
||||
ecs.register::<comp::Gravity>();
|
||||
ecs.register::<comp::CharacterState>();
|
||||
ecs.register::<comp::SpeechBubble>();
|
||||
|
||||
// Register components send from clients -> server
|
||||
ecs.register::<comp::Controller>();
|
||||
|
@ -3,12 +3,13 @@ use crate::{
|
||||
self,
|
||||
agent::Activity,
|
||||
item::{tool::ToolKind, ItemKind},
|
||||
Agent, Alignment, CharacterState, ControlAction, Controller, Loadout, MountState, Ori, Pos,
|
||||
Scale, SpeechBubble, Stats,
|
||||
Agent, Alignment, CharacterState, ChatMsg, ControlAction, Controller, Loadout, MountState,
|
||||
Ori, Pos, Scale, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
path::Chaser,
|
||||
state::{DeltaTime, Time},
|
||||
sync::UidAllocator,
|
||||
sync::{Uid, UidAllocator},
|
||||
terrain::TerrainGrid,
|
||||
util::Dir,
|
||||
vol::ReadVol,
|
||||
@ -16,7 +17,7 @@ use crate::{
|
||||
use rand::{thread_rng, Rng};
|
||||
use specs::{
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage,
|
||||
Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
@ -28,6 +29,7 @@ impl<'a> System<'a> for Sys {
|
||||
Read<'a, UidAllocator>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, DeltaTime>,
|
||||
Write<'a, EventBus<ServerEvent>>,
|
||||
Entities<'a>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Ori>,
|
||||
@ -35,11 +37,11 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, Loadout>,
|
||||
ReadStorage<'a, CharacterState>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadStorage<'a, Alignment>,
|
||||
WriteStorage<'a, Agent>,
|
||||
WriteStorage<'a, Controller>,
|
||||
WriteStorage<'a, SpeechBubble>,
|
||||
ReadStorage<'a, MountState>,
|
||||
);
|
||||
|
||||
@ -50,6 +52,7 @@ impl<'a> System<'a> for Sys {
|
||||
uid_allocator,
|
||||
time,
|
||||
dt,
|
||||
event_bus,
|
||||
entities,
|
||||
positions,
|
||||
orientations,
|
||||
@ -57,11 +60,11 @@ impl<'a> System<'a> for Sys {
|
||||
stats,
|
||||
loadouts,
|
||||
character_states,
|
||||
uids,
|
||||
terrain,
|
||||
alignments,
|
||||
mut agents,
|
||||
mut controllers,
|
||||
mut speech_bubbles,
|
||||
mount_states,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
@ -72,6 +75,7 @@ impl<'a> System<'a> for Sys {
|
||||
alignment,
|
||||
loadout,
|
||||
character_state,
|
||||
uid,
|
||||
agent,
|
||||
controller,
|
||||
mount_state,
|
||||
@ -82,6 +86,7 @@ impl<'a> System<'a> for Sys {
|
||||
alignments.maybe(),
|
||||
&loadouts,
|
||||
&character_states,
|
||||
&uids,
|
||||
&mut agents,
|
||||
&mut controllers,
|
||||
mount_states.maybe(),
|
||||
@ -386,10 +391,9 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
if stats.get(attacker).map_or(false, |a| !a.is_dead) {
|
||||
if agent.can_speak {
|
||||
let message =
|
||||
"npc.speech.villager_under_attack".to_string();
|
||||
let bubble = SpeechBubble::npc_new(message, *time);
|
||||
let _ = speech_bubbles.insert(entity, bubble);
|
||||
let msg = "npc.speech.villager_under_attack".to_string();
|
||||
event_bus
|
||||
.emit_now(ServerEvent::Chat(ChatMsg::npc(*uid, msg)));
|
||||
}
|
||||
|
||||
agent.activity = Activity::Attack {
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::Server;
|
||||
use common::event::{EventBus, ServerEvent};
|
||||
use crate::{state_ext::StateExt, Server};
|
||||
use common::{
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::ServerMsg,
|
||||
};
|
||||
use entity_creation::{
|
||||
handle_create_npc, handle_create_waypoint, handle_initialize_character,
|
||||
handle_loaded_character_data, handle_shoot,
|
||||
@ -103,6 +106,10 @@ impl Server {
|
||||
ServerEvent::ChatCmd(entity, cmd) => {
|
||||
chat_commands.push((entity, cmd));
|
||||
},
|
||||
ServerEvent::Chat(msg) => {
|
||||
self.state
|
||||
.notify_registered_clients(ServerMsg::ChatMsg(msg));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,8 +114,8 @@ impl Server {
|
||||
state.ecs_mut().insert(sys::TerrainSyncTimer::default());
|
||||
state.ecs_mut().insert(sys::TerrainTimer::default());
|
||||
state.ecs_mut().insert(sys::WaypointTimer::default());
|
||||
state.ecs_mut().insert(sys::SpeechBubbleTimer::default());
|
||||
state.ecs_mut().insert(sys::PersistenceTimer::default());
|
||||
//state.ecs_mut().insert(sys::StatsPersistenceTimer::default());
|
||||
|
||||
// System schedulers to control execution of systems
|
||||
state
|
||||
|
@ -464,7 +464,8 @@ impl<'a> System<'a> for Sys {
|
||||
server_emitter.emit(ServerEvent::ChatCmd(entity, argv));
|
||||
}
|
||||
} else {
|
||||
// TODO FIXME speech bubbles and prefixes are handled by the client now
|
||||
// Send speech bubble and chat message
|
||||
// TODO filter group, faction, say, and bubble distance.
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ pub mod entity_sync;
|
||||
pub mod message;
|
||||
pub mod persistence;
|
||||
pub mod sentinel;
|
||||
pub mod speech_bubble;
|
||||
pub mod subscription;
|
||||
pub mod terrain;
|
||||
pub mod terrain_sync;
|
||||
@ -21,9 +20,10 @@ pub type SubscriptionTimer = SysTimer<subscription::Sys>;
|
||||
pub type TerrainTimer = SysTimer<terrain::Sys>;
|
||||
pub type TerrainSyncTimer = SysTimer<terrain_sync::Sys>;
|
||||
pub type WaypointTimer = SysTimer<waypoint::Sys>;
|
||||
pub type SpeechBubbleTimer = SysTimer<speech_bubble::Sys>;
|
||||
pub type PersistenceTimer = SysTimer<persistence::Sys>;
|
||||
pub type PersistenceScheduler = SysScheduler<persistence::Sys>;
|
||||
//pub type StatsPersistenceTimer = SysTimer<persistence::stats::Sys>;
|
||||
//pub type StatsPersistenceScheduler = SysScheduler<persistence::stats::Sys>;
|
||||
|
||||
// System names
|
||||
// Note: commented names may be useful in the future
|
||||
@ -33,14 +33,14 @@ pub type PersistenceScheduler = SysScheduler<persistence::Sys>;
|
||||
//const TERRAIN_SYNC_SYS: &str = "server_terrain_sync_sys";
|
||||
const TERRAIN_SYS: &str = "server_terrain_sys";
|
||||
const WAYPOINT_SYS: &str = "waypoint_sys";
|
||||
const SPEECH_BUBBLE_SYS: &str = "speech_bubble_sys";
|
||||
const PERSISTENCE_SYS: &str = "persistence_sys";
|
||||
//const STATS_PERSISTENCE_SYS: &str = "stats_persistence_sys";
|
||||
|
||||
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch_builder.add(terrain::Sys, TERRAIN_SYS, &[]);
|
||||
dispatch_builder.add(waypoint::Sys, WAYPOINT_SYS, &[]);
|
||||
dispatch_builder.add(speech_bubble::Sys, SPEECH_BUBBLE_SYS, &[]);
|
||||
dispatch_builder.add(persistence::Sys, PERSISTENCE_SYS, &[]);
|
||||
//dispatch_builder.add(persistence::stats::Sys, STATS_PERSISTENCE_SYS, &[]);
|
||||
}
|
||||
|
||||
pub fn run_sync_systems(ecs: &mut specs::World) {
|
||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
||||
use common::{
|
||||
comp::{
|
||||
Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, Loadout,
|
||||
Mass, MountState, Mounting, Ori, Player, Pos, Scale, SpeechBubble, Stats, Sticky, Vel,
|
||||
Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel,
|
||||
},
|
||||
msg::EcsCompPacket,
|
||||
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
|
||||
@ -54,7 +54,6 @@ pub struct TrackedComps<'a> {
|
||||
pub gravity: ReadStorage<'a, Gravity>,
|
||||
pub loadout: ReadStorage<'a, Loadout>,
|
||||
pub character_state: ReadStorage<'a, CharacterState>,
|
||||
pub speech_bubble: ReadStorage<'a, SpeechBubble>,
|
||||
}
|
||||
impl<'a> TrackedComps<'a> {
|
||||
pub fn create_entity_package(
|
||||
@ -126,10 +125,6 @@ impl<'a> TrackedComps<'a> {
|
||||
.get(entity)
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
self.speech_bubble
|
||||
.get(entity)
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
// Add untracked comps
|
||||
pos.map(|c| comps.push(c.into()));
|
||||
vel.map(|c| comps.push(c.into()));
|
||||
@ -157,7 +152,6 @@ pub struct ReadTrackers<'a> {
|
||||
pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
||||
pub loadout: ReadExpect<'a, UpdateTracker<Loadout>>,
|
||||
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
|
||||
pub speech_bubble: ReadExpect<'a, UpdateTracker<SpeechBubble>>,
|
||||
}
|
||||
impl<'a> ReadTrackers<'a> {
|
||||
pub fn create_sync_packages(
|
||||
@ -194,12 +188,6 @@ impl<'a> ReadTrackers<'a> {
|
||||
&*self.character_state,
|
||||
&comps.character_state,
|
||||
filter,
|
||||
)
|
||||
.with_component(
|
||||
&comps.uid,
|
||||
&*self.speech_bubble,
|
||||
&comps.speech_bubble,
|
||||
filter,
|
||||
);
|
||||
|
||||
(entity_sync_package, comp_sync_package)
|
||||
@ -225,7 +213,6 @@ pub struct WriteTrackers<'a> {
|
||||
gravity: WriteExpect<'a, UpdateTracker<Gravity>>,
|
||||
loadout: WriteExpect<'a, UpdateTracker<Loadout>>,
|
||||
character_state: WriteExpect<'a, UpdateTracker<CharacterState>>,
|
||||
speech_bubble: WriteExpect<'a, UpdateTracker<SpeechBubble>>,
|
||||
}
|
||||
|
||||
fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
||||
@ -249,7 +236,6 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
||||
trackers
|
||||
.character_state
|
||||
.record_changes(&comps.character_state);
|
||||
trackers.speech_bubble.record_changes(&comps.speech_bubble);
|
||||
}
|
||||
|
||||
pub fn register_trackers(world: &mut World) {
|
||||
@ -270,7 +256,6 @@ pub fn register_trackers(world: &mut World) {
|
||||
world.register_tracker::<Gravity>();
|
||||
world.register_tracker::<Loadout>();
|
||||
world.register_tracker::<CharacterState>();
|
||||
world.register_tracker::<SpeechBubble>();
|
||||
}
|
||||
|
||||
/// Deleted entities grouped by region
|
||||
|
@ -1,29 +0,0 @@
|
||||
use super::SysTimer;
|
||||
use common::{comp::SpeechBubble, state::Time};
|
||||
use specs::{Entities, Join, Read, System, Write, WriteStorage};
|
||||
|
||||
/// This system removes timed-out speech bubbles
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, Time>,
|
||||
WriteStorage<'a, SpeechBubble>,
|
||||
Write<'a, SysTimer<Self>>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (entities, time, mut speech_bubbles, mut timer): Self::SystemData) {
|
||||
timer.start();
|
||||
|
||||
let expired_ents: Vec<_> = (&entities, &mut speech_bubbles)
|
||||
.join()
|
||||
.filter(|(_, speech_bubble)| speech_bubble.timeout.map_or(true, |t| t.0 < time.0))
|
||||
.map(|(ent, _)| ent)
|
||||
.collect();
|
||||
for ent in expired_ents {
|
||||
speech_bubbles.remove(ent);
|
||||
}
|
||||
|
||||
timer.end();
|
||||
}
|
||||
}
|
@ -367,6 +367,7 @@ impl<'a> Widget for Chat<'a> {
|
||||
ChatType::Faction(uid) => (FACTION_COLOR, message_format(uid, message)),
|
||||
ChatType::Region(uid) => (REGION_COLOR, message_format(uid, message)),
|
||||
ChatType::World(uid) => (WORLD_COLOR, message_format(uid, message)),
|
||||
ChatType::Npc(_uid, _r) => continue, // Should be filtered by hud/mod.rs
|
||||
};
|
||||
let text = Text::new(&msg)
|
||||
.font_size(self.fonts.opensans.scale(15))
|
||||
|
@ -47,14 +47,17 @@ use crate::{
|
||||
GlobalState,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{assets::load_expect, comp, terrain::TerrainChunk, vol::RectRasterableVol};
|
||||
use common::{assets::load_expect, comp, sync::Uid, terrain::TerrainChunk, vol::RectRasterableVol};
|
||||
use conrod_core::{
|
||||
text::cursor::Index,
|
||||
widget::{self, Button, Image, Text},
|
||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
use specs::{Join, WorldExt};
|
||||
use std::collections::VecDeque;
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
time::Instant,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
||||
@ -465,6 +468,7 @@ pub struct Hud {
|
||||
rot_imgs: ImgsRot,
|
||||
new_messages: VecDeque<comp::ChatMsg>,
|
||||
new_notifications: VecDeque<common::msg::Notification>,
|
||||
speech_bubbles: HashMap<Uid, comp::SpeechBubble>,
|
||||
show: Show,
|
||||
//never_show: bool,
|
||||
//intro: bool,
|
||||
@ -530,9 +534,10 @@ impl Hud {
|
||||
fonts,
|
||||
ids,
|
||||
new_messages: VecDeque::new(),
|
||||
new_notifications: VecDeque::new(),
|
||||
speech_bubbles: HashMap::new(),
|
||||
//intro: false,
|
||||
//intro_2: false,
|
||||
new_notifications: VecDeque::new(),
|
||||
show: Show {
|
||||
help: false,
|
||||
intro: true,
|
||||
@ -606,7 +611,7 @@ impl Hud {
|
||||
let stats = ecs.read_storage::<comp::Stats>();
|
||||
let energy = ecs.read_storage::<comp::Energy>();
|
||||
let hp_floater_lists = ecs.read_storage::<vcomp::HpFloaterList>();
|
||||
let speech_bubbles = ecs.read_storage::<comp::SpeechBubble>();
|
||||
let uids = ecs.read_storage::<common::sync::Uid>();
|
||||
let interpolated = ecs.read_storage::<vcomp::Interpolated>();
|
||||
let players = ecs.read_storage::<comp::Player>();
|
||||
let scales = ecs.read_storage::<comp::Scale>();
|
||||
@ -934,12 +939,23 @@ impl Hud {
|
||||
}
|
||||
}
|
||||
|
||||
// Pop speech bubbles
|
||||
self.speech_bubbles
|
||||
.retain(|_uid, bubble| bubble.timeout > Instant::now());
|
||||
|
||||
// Push speech bubbles
|
||||
for msg in self.new_messages.iter() {
|
||||
if let Some((bubble, uid)) = msg.to_bubble() {
|
||||
self.speech_bubbles.insert(uid, bubble);
|
||||
}
|
||||
}
|
||||
|
||||
let mut overhead_walker = self.ids.overheads.walk();
|
||||
let mut sct_walker = self.ids.scts.walk();
|
||||
let mut sct_bg_walker = self.ids.sct_bgs.walk();
|
||||
|
||||
// Render overhead name tags and health bars
|
||||
for (pos, name, stats, energy, height_offset, hpfl, bubble) in (
|
||||
for (pos, name, stats, energy, height_offset, hpfl, uid) in (
|
||||
&entities,
|
||||
&pos,
|
||||
interpolated.maybe(),
|
||||
@ -949,7 +965,7 @@ impl Hud {
|
||||
scales.maybe(),
|
||||
&bodies,
|
||||
&hp_floater_lists,
|
||||
speech_bubbles.maybe(),
|
||||
&uids,
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _, _, _, _, _, _)| *entity != me && !stats.is_dead)
|
||||
@ -966,7 +982,7 @@ impl Hud {
|
||||
})
|
||||
.powi(2)
|
||||
})
|
||||
.map(|(_, pos, interpolated, stats, energy, player, scale, body, hpfl, bubble)| {
|
||||
.map(|(_, pos, interpolated, stats, energy, player, scale, body, hpfl, uid)| {
|
||||
// TODO: This is temporary
|
||||
// If the player used the default character name display their name instead
|
||||
let name = if stats.name == "Character Name" {
|
||||
@ -982,10 +998,12 @@ impl Hud {
|
||||
// TODO: when body.height() is more accurate remove the 2.0
|
||||
body.height() * 2.0 * scale.map_or(1.0, |s| s.0),
|
||||
hpfl,
|
||||
bubble,
|
||||
uid,
|
||||
)
|
||||
})
|
||||
{
|
||||
let bubble = self.speech_bubbles.get(uid);
|
||||
|
||||
let overhead_id = overhead_walker.next(
|
||||
&mut self.ids.overheads,
|
||||
&mut ui_widgets.widget_id_generator(),
|
||||
@ -1551,6 +1569,15 @@ impl Hud {
|
||||
.set(self.ids.skillbar, ui_widgets);
|
||||
}
|
||||
|
||||
// Don't put NPC messages in chat box.
|
||||
self.new_messages.retain(|m| {
|
||||
if let comp::ChatType::Npc(_, _) = m.chat_type {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
// Chat box
|
||||
match Chat::new(
|
||||
&mut self.new_messages,
|
||||
|
@ -77,11 +77,8 @@ impl SessionState {
|
||||
fn tick(&mut self, dt: Duration, global_state: &mut GlobalState) -> Result<TickAction, Error> {
|
||||
self.inputs.tick(dt);
|
||||
|
||||
for event in self.client.borrow_mut().tick(
|
||||
self.inputs.clone(),
|
||||
dt,
|
||||
crate::ecs::sys::add_local_systems,
|
||||
)? {
|
||||
let mut client = self.client.borrow_mut();
|
||||
for event in client.tick(self.inputs.clone(), dt, crate::ecs::sys::add_local_systems)? {
|
||||
match event {
|
||||
client::Event::Chat(m) => {
|
||||
self.hud.new_message(m);
|
||||
|
Loading…
Reference in New Issue
Block a user