mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Chatting now creates speech bubbles
This commit is contained in:

committed by
Pfauenauge90
parent
73a29b339c
commit
c65967ccdb
@ -1,5 +1,5 @@
|
|||||||
use crate::path::Chaser;
|
use crate::{path::Chaser, state::Time};
|
||||||
use specs::{Component, Entity as EcsEntity};
|
use specs::{Component, Entity as EcsEntity, FlaggedStorage, HashMapStorage};
|
||||||
use specs_idvs::IDVStorage;
|
use specs_idvs::IDVStorage;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -85,3 +85,17 @@ impl Activity {
|
|||||||
impl Default for Activity {
|
impl Default for Activity {
|
||||||
fn default() -> Self { Activity::Idle(Vec2::zero()) }
|
fn default() -> Self { Activity::Idle(Vec2::zero()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default duration in seconds of chat bubbles
|
||||||
|
pub const SPEECH_BUBBLE_DURATION: f64 = 5.0;
|
||||||
|
|
||||||
|
/// Adds a speech bubble to the entity
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct SpeechBubble {
|
||||||
|
pub message: String,
|
||||||
|
pub timeout: Option<Time>,
|
||||||
|
// TODO add icon enum for player chat type / npc quest+trade
|
||||||
|
}
|
||||||
|
impl Component for SpeechBubble {
|
||||||
|
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
|
||||||
|
}
|
||||||
|
@ -18,7 +18,7 @@ mod visual;
|
|||||||
// Reexports
|
// Reexports
|
||||||
pub use ability::{CharacterAbility, ItemConfig, Loadout};
|
pub use ability::{CharacterAbility, ItemConfig, Loadout};
|
||||||
pub use admin::Admin;
|
pub use admin::Admin;
|
||||||
pub use agent::{Agent, Alignment};
|
pub use agent::{Agent, Alignment, SpeechBubble, SPEECH_BUBBLE_DURATION};
|
||||||
pub use body::{
|
pub use body::{
|
||||||
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, golem,
|
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, golem,
|
||||||
humanoid, object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData,
|
humanoid, object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData,
|
||||||
|
@ -24,6 +24,7 @@ sum_type! {
|
|||||||
Sticky(comp::Sticky),
|
Sticky(comp::Sticky),
|
||||||
Loadout(comp::Loadout),
|
Loadout(comp::Loadout),
|
||||||
CharacterState(comp::CharacterState),
|
CharacterState(comp::CharacterState),
|
||||||
|
SpeechBubble(comp::SpeechBubble),
|
||||||
Pos(comp::Pos),
|
Pos(comp::Pos),
|
||||||
Vel(comp::Vel),
|
Vel(comp::Vel),
|
||||||
Ori(comp::Ori),
|
Ori(comp::Ori),
|
||||||
@ -50,6 +51,7 @@ sum_type! {
|
|||||||
Sticky(PhantomData<comp::Sticky>),
|
Sticky(PhantomData<comp::Sticky>),
|
||||||
Loadout(PhantomData<comp::Loadout>),
|
Loadout(PhantomData<comp::Loadout>),
|
||||||
CharacterState(PhantomData<comp::CharacterState>),
|
CharacterState(PhantomData<comp::CharacterState>),
|
||||||
|
SpeechBubble(PhantomData<comp::SpeechBubble>),
|
||||||
Pos(PhantomData<comp::Pos>),
|
Pos(PhantomData<comp::Pos>),
|
||||||
Vel(PhantomData<comp::Vel>),
|
Vel(PhantomData<comp::Vel>),
|
||||||
Ori(PhantomData<comp::Ori>),
|
Ori(PhantomData<comp::Ori>),
|
||||||
@ -76,6 +78,7 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Loadout(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::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::Pos(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Vel(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),
|
EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world),
|
||||||
@ -100,6 +103,7 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Loadout(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::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::Pos(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Vel(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),
|
EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world),
|
||||||
@ -128,6 +132,9 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPhantom::CharacterState(_) => {
|
EcsCompPhantom::CharacterState(_) => {
|
||||||
sync::handle_remove::<comp::CharacterState>(entity, world)
|
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::Pos(_) => sync::handle_remove::<comp::Pos>(entity, world),
|
||||||
EcsCompPhantom::Vel(_) => sync::handle_remove::<comp::Vel>(entity, world),
|
EcsCompPhantom::Vel(_) => sync::handle_remove::<comp::Vel>(entity, world),
|
||||||
EcsCompPhantom::Ori(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
EcsCompPhantom::Ori(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
||||||
|
@ -122,6 +122,7 @@ impl State {
|
|||||||
ecs.register::<comp::Sticky>();
|
ecs.register::<comp::Sticky>();
|
||||||
ecs.register::<comp::Gravity>();
|
ecs.register::<comp::Gravity>();
|
||||||
ecs.register::<comp::CharacterState>();
|
ecs.register::<comp::CharacterState>();
|
||||||
|
ecs.register::<comp::SpeechBubble>();
|
||||||
|
|
||||||
// Register components send from clients -> server
|
// Register components send from clients -> server
|
||||||
ecs.register::<comp::Controller>();
|
ecs.register::<comp::Controller>();
|
||||||
|
@ -109,6 +109,7 @@ impl Server {
|
|||||||
state.ecs_mut().insert(sys::TerrainSyncTimer::default());
|
state.ecs_mut().insert(sys::TerrainSyncTimer::default());
|
||||||
state.ecs_mut().insert(sys::TerrainTimer::default());
|
state.ecs_mut().insert(sys::TerrainTimer::default());
|
||||||
state.ecs_mut().insert(sys::WaypointTimer::default());
|
state.ecs_mut().insert(sys::WaypointTimer::default());
|
||||||
|
state.ecs_mut().insert(sys::SpeechBubbleTimer::default());
|
||||||
state
|
state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.insert(sys::StatsPersistenceTimer::default());
|
.insert(sys::StatsPersistenceTimer::default());
|
||||||
|
@ -4,7 +4,10 @@ use crate::{
|
|||||||
CLIENT_TIMEOUT,
|
CLIENT_TIMEOUT,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel},
|
comp::{
|
||||||
|
Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, SpeechBubble,
|
||||||
|
Stats, Vel, SPEECH_BUBBLE_DURATION,
|
||||||
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
msg::{
|
msg::{
|
||||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
||||||
@ -43,6 +46,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteStorage<'a, Player>,
|
WriteStorage<'a, Player>,
|
||||||
WriteStorage<'a, Client>,
|
WriteStorage<'a, Client>,
|
||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
|
WriteStorage<'a, SpeechBubble>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -67,6 +71,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut players,
|
mut players,
|
||||||
mut clients,
|
mut clients,
|
||||||
mut controllers,
|
mut controllers,
|
||||||
|
mut speech_bubbles,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
timer.start();
|
timer.start();
|
||||||
@ -394,13 +399,20 @@ impl<'a> System<'a> for Sys {
|
|||||||
for (entity, msg) in new_chat_msgs {
|
for (entity, msg) in new_chat_msgs {
|
||||||
match msg {
|
match msg {
|
||||||
ServerMsg::ChatMsg { chat_type, message } => {
|
ServerMsg::ChatMsg { chat_type, message } => {
|
||||||
if let Some(entity) = entity {
|
let message = if let Some(entity) = entity {
|
||||||
// Handle chat commands.
|
// Handle chat commands.
|
||||||
if message.starts_with("/") && message.len() > 1 {
|
if message.starts_with("/") && message.len() > 1 {
|
||||||
let argv = String::from(&message[1..]);
|
let argv = String::from(&message[1..]);
|
||||||
server_emitter.emit(ServerEvent::ChatCmd(entity, argv));
|
server_emitter.emit(ServerEvent::ChatCmd(entity, argv));
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let message = match players.get(entity) {
|
let timeout = Some(Time(time + SPEECH_BUBBLE_DURATION));
|
||||||
|
let bubble = SpeechBubble {
|
||||||
|
message: message.clone(),
|
||||||
|
timeout,
|
||||||
|
};
|
||||||
|
let _ = speech_bubbles.insert(entity, bubble);
|
||||||
|
match players.get(entity) {
|
||||||
Some(player) => {
|
Some(player) => {
|
||||||
if admins.get(entity).is_some() {
|
if admins.get(entity).is_some() {
|
||||||
format!("[ADMIN][{}] {}", &player.alias, message)
|
format!("[ADMIN][{}] {}", &player.alias, message)
|
||||||
@ -409,17 +421,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => format!("[<Unknown>] {}", message),
|
None => format!("[<Unknown>] {}", message),
|
||||||
};
|
|
||||||
let msg = ServerMsg::ChatMsg { chat_type, message };
|
|
||||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
|
||||||
client.notify(msg.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let msg = ServerMsg::ChatMsg { chat_type, message };
|
message
|
||||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
};
|
||||||
client.notify(msg.clone());
|
let msg = ServerMsg::ChatMsg { chat_type, message };
|
||||||
}
|
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||||
|
client.notify(msg.clone());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -2,6 +2,7 @@ pub mod entity_sync;
|
|||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod persistence;
|
pub mod persistence;
|
||||||
pub mod sentinel;
|
pub mod sentinel;
|
||||||
|
pub mod speech_bubble;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
pub mod terrain_sync;
|
pub mod terrain_sync;
|
||||||
@ -20,6 +21,7 @@ pub type SubscriptionTimer = SysTimer<subscription::Sys>;
|
|||||||
pub type TerrainTimer = SysTimer<terrain::Sys>;
|
pub type TerrainTimer = SysTimer<terrain::Sys>;
|
||||||
pub type TerrainSyncTimer = SysTimer<terrain_sync::Sys>;
|
pub type TerrainSyncTimer = SysTimer<terrain_sync::Sys>;
|
||||||
pub type WaypointTimer = SysTimer<waypoint::Sys>;
|
pub type WaypointTimer = SysTimer<waypoint::Sys>;
|
||||||
|
pub type SpeechBubbleTimer = SysTimer<speech_bubble::Sys>;
|
||||||
pub type StatsPersistenceTimer = SysTimer<persistence::stats::Sys>;
|
pub type StatsPersistenceTimer = SysTimer<persistence::stats::Sys>;
|
||||||
pub type StatsPersistenceScheduler = SysScheduler<persistence::stats::Sys>;
|
pub type StatsPersistenceScheduler = SysScheduler<persistence::stats::Sys>;
|
||||||
|
|
||||||
@ -31,11 +33,13 @@ pub type StatsPersistenceScheduler = SysScheduler<persistence::stats::Sys>;
|
|||||||
//const TERRAIN_SYNC_SYS: &str = "server_terrain_sync_sys";
|
//const TERRAIN_SYNC_SYS: &str = "server_terrain_sync_sys";
|
||||||
const TERRAIN_SYS: &str = "server_terrain_sys";
|
const TERRAIN_SYS: &str = "server_terrain_sys";
|
||||||
const WAYPOINT_SYS: &str = "waypoint_sys";
|
const WAYPOINT_SYS: &str = "waypoint_sys";
|
||||||
|
const SPEECH_BUBBLE_SYS: &str = "speech_bubble_sys";
|
||||||
const STATS_PERSISTENCE_SYS: &str = "stats_persistence_sys";
|
const STATS_PERSISTENCE_SYS: &str = "stats_persistence_sys";
|
||||||
|
|
||||||
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
dispatch_builder.add(terrain::Sys, TERRAIN_SYS, &[]);
|
dispatch_builder.add(terrain::Sys, TERRAIN_SYS, &[]);
|
||||||
dispatch_builder.add(waypoint::Sys, WAYPOINT_SYS, &[]);
|
dispatch_builder.add(waypoint::Sys, WAYPOINT_SYS, &[]);
|
||||||
|
dispatch_builder.add(speech_bubble::Sys, SPEECH_BUBBLE_SYS, &[]);
|
||||||
dispatch_builder.add(persistence::stats::Sys, STATS_PERSISTENCE_SYS, &[]);
|
dispatch_builder.add(persistence::stats::Sys, STATS_PERSISTENCE_SYS, &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, Loadout,
|
Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, Loadout,
|
||||||
Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel,
|
Mass, MountState, Mounting, Ori, Player, Pos, Scale, SpeechBubble, Stats, Sticky, Vel,
|
||||||
},
|
},
|
||||||
msg::EcsCompPacket,
|
msg::EcsCompPacket,
|
||||||
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
|
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
|
||||||
@ -54,6 +54,7 @@ pub struct TrackedComps<'a> {
|
|||||||
pub gravity: ReadStorage<'a, Gravity>,
|
pub gravity: ReadStorage<'a, Gravity>,
|
||||||
pub loadout: ReadStorage<'a, Loadout>,
|
pub loadout: ReadStorage<'a, Loadout>,
|
||||||
pub character_state: ReadStorage<'a, CharacterState>,
|
pub character_state: ReadStorage<'a, CharacterState>,
|
||||||
|
pub speech_bubble: ReadStorage<'a, SpeechBubble>,
|
||||||
}
|
}
|
||||||
impl<'a> TrackedComps<'a> {
|
impl<'a> TrackedComps<'a> {
|
||||||
pub fn create_entity_package(
|
pub fn create_entity_package(
|
||||||
@ -125,6 +126,10 @@ impl<'a> TrackedComps<'a> {
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|c| comps.push(c.into()));
|
.map(|c| comps.push(c.into()));
|
||||||
|
self.speech_bubble
|
||||||
|
.get(entity)
|
||||||
|
.cloned()
|
||||||
|
.map(|c| comps.push(c.into()));
|
||||||
// Add untracked comps
|
// Add untracked comps
|
||||||
pos.map(|c| comps.push(c.into()));
|
pos.map(|c| comps.push(c.into()));
|
||||||
vel.map(|c| comps.push(c.into()));
|
vel.map(|c| comps.push(c.into()));
|
||||||
@ -152,6 +157,7 @@ pub struct ReadTrackers<'a> {
|
|||||||
pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
||||||
pub loadout: ReadExpect<'a, UpdateTracker<Loadout>>,
|
pub loadout: ReadExpect<'a, UpdateTracker<Loadout>>,
|
||||||
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
|
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
|
||||||
|
pub speech_bubble: ReadExpect<'a, UpdateTracker<SpeechBubble>>,
|
||||||
}
|
}
|
||||||
impl<'a> ReadTrackers<'a> {
|
impl<'a> ReadTrackers<'a> {
|
||||||
pub fn create_sync_packages(
|
pub fn create_sync_packages(
|
||||||
@ -188,6 +194,12 @@ impl<'a> ReadTrackers<'a> {
|
|||||||
&*self.character_state,
|
&*self.character_state,
|
||||||
&comps.character_state,
|
&comps.character_state,
|
||||||
filter,
|
filter,
|
||||||
|
)
|
||||||
|
.with_component(
|
||||||
|
&comps.uid,
|
||||||
|
&*self.speech_bubble,
|
||||||
|
&comps.speech_bubble,
|
||||||
|
filter,
|
||||||
);
|
);
|
||||||
|
|
||||||
(entity_sync_package, comp_sync_package)
|
(entity_sync_package, comp_sync_package)
|
||||||
@ -213,6 +225,7 @@ pub struct WriteTrackers<'a> {
|
|||||||
gravity: WriteExpect<'a, UpdateTracker<Gravity>>,
|
gravity: WriteExpect<'a, UpdateTracker<Gravity>>,
|
||||||
loadout: WriteExpect<'a, UpdateTracker<Loadout>>,
|
loadout: WriteExpect<'a, UpdateTracker<Loadout>>,
|
||||||
character_state: WriteExpect<'a, UpdateTracker<CharacterState>>,
|
character_state: WriteExpect<'a, UpdateTracker<CharacterState>>,
|
||||||
|
speech_bubble: WriteExpect<'a, UpdateTracker<SpeechBubble>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
||||||
@ -236,6 +249,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
|||||||
trackers
|
trackers
|
||||||
.character_state
|
.character_state
|
||||||
.record_changes(&comps.character_state);
|
.record_changes(&comps.character_state);
|
||||||
|
trackers.speech_bubble.record_changes(&comps.speech_bubble);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_trackers(world: &mut World) {
|
pub fn register_trackers(world: &mut World) {
|
||||||
@ -256,6 +270,7 @@ pub fn register_trackers(world: &mut World) {
|
|||||||
world.register_tracker::<Gravity>();
|
world.register_tracker::<Gravity>();
|
||||||
world.register_tracker::<Loadout>();
|
world.register_tracker::<Loadout>();
|
||||||
world.register_tracker::<CharacterState>();
|
world.register_tracker::<CharacterState>();
|
||||||
|
world.register_tracker::<SpeechBubble>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deleted entities grouped by region
|
/// Deleted entities grouped by region
|
||||||
|
30
server/src/sys/speech_bubble.rs
Normal file
30
server/src/sys/speech_bubble.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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 {
|
||||||
|
println!("Remoaving bobble");
|
||||||
|
speech_bubbles.remove(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.end();
|
||||||
|
}
|
||||||
|
}
|
@ -566,6 +566,7 @@ impl Hud {
|
|||||||
let stats = ecs.read_storage::<comp::Stats>();
|
let stats = ecs.read_storage::<comp::Stats>();
|
||||||
let energy = ecs.read_storage::<comp::Energy>();
|
let energy = ecs.read_storage::<comp::Energy>();
|
||||||
let hp_floater_lists = ecs.read_storage::<vcomp::HpFloaterList>();
|
let hp_floater_lists = ecs.read_storage::<vcomp::HpFloaterList>();
|
||||||
|
let speech_bubbles = ecs.read_storage::<comp::SpeechBubble>();
|
||||||
let interpolated = ecs.read_storage::<vcomp::Interpolated>();
|
let interpolated = ecs.read_storage::<vcomp::Interpolated>();
|
||||||
let players = ecs.read_storage::<comp::Player>();
|
let players = ecs.read_storage::<comp::Player>();
|
||||||
let scales = ecs.read_storage::<comp::Scale>();
|
let scales = ecs.read_storage::<comp::Scale>();
|
||||||
@ -890,7 +891,7 @@ impl Hud {
|
|||||||
let mut sct_bg_walker = self.ids.sct_bgs.walk();
|
let mut sct_bg_walker = self.ids.sct_bgs.walk();
|
||||||
|
|
||||||
// Render overhead name tags and health bars
|
// Render overhead name tags and health bars
|
||||||
for (pos, name, stats, energy, height_offset, hpfl) in (
|
for (pos, name, stats, energy, height_offset, hpfl, bubble) in (
|
||||||
&entities,
|
&entities,
|
||||||
&pos,
|
&pos,
|
||||||
interpolated.maybe(),
|
interpolated.maybe(),
|
||||||
@ -900,11 +901,12 @@ impl Hud {
|
|||||||
scales.maybe(),
|
scales.maybe(),
|
||||||
&bodies,
|
&bodies,
|
||||||
&hp_floater_lists,
|
&hp_floater_lists,
|
||||||
|
speech_bubbles.maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(entity, _, _, stats, _, _, _, _, _)| *entity != me && !stats.is_dead)
|
.filter(|(entity, _, _, stats, _, _, _, _, _, _)| *entity != me && !stats.is_dead)
|
||||||
// Don't show outside a certain range
|
// Don't show outside a certain range
|
||||||
.filter(|(_, pos, _, _, _, _, _, _, hpfl)| {
|
.filter(|(_, pos, _, _, _, _, _, _, hpfl, _)| {
|
||||||
pos.0.distance_squared(player_pos)
|
pos.0.distance_squared(player_pos)
|
||||||
< (if hpfl
|
< (if hpfl
|
||||||
.time_since_last_dmg_by_me
|
.time_since_last_dmg_by_me
|
||||||
@ -916,7 +918,7 @@ impl Hud {
|
|||||||
})
|
})
|
||||||
.powi(2)
|
.powi(2)
|
||||||
})
|
})
|
||||||
.map(|(_, pos, interpolated, stats, energy, player, scale, body, hpfl)| {
|
.map(|(_, pos, interpolated, stats, energy, player, scale, body, hpfl, bubble)| {
|
||||||
// TODO: This is temporary
|
// TODO: This is temporary
|
||||||
// If the player used the default character name display their name instead
|
// If the player used the default character name display their name instead
|
||||||
let name = if stats.name == "Character Name" {
|
let name = if stats.name == "Character Name" {
|
||||||
@ -932,6 +934,7 @@ impl Hud {
|
|||||||
// TODO: when body.height() is more accurate remove the 2.0
|
// TODO: when body.height() is more accurate remove the 2.0
|
||||||
body.height() * 2.0 * scale.map_or(1.0, |s| s.0),
|
body.height() * 2.0 * scale.map_or(1.0, |s| s.0),
|
||||||
hpfl,
|
hpfl,
|
||||||
|
bubble,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -944,6 +947,7 @@ impl Hud {
|
|||||||
// Chat bubble, name, level, and hp bars
|
// Chat bubble, name, level, and hp bars
|
||||||
overhead::Overhead::new(
|
overhead::Overhead::new(
|
||||||
&name,
|
&name,
|
||||||
|
bubble,
|
||||||
stats,
|
stats,
|
||||||
energy,
|
energy,
|
||||||
own_level,
|
own_level,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{img_ids::Imgs, HP_COLOR, LOW_HP_COLOR, MANA_COLOR};
|
use super::{img_ids::Imgs, HP_COLOR, LOW_HP_COLOR, MANA_COLOR};
|
||||||
use crate::ui::{fonts::ConrodVoxygenFonts, Ingameable};
|
use crate::ui::{fonts::ConrodVoxygenFonts, Ingameable};
|
||||||
use common::comp::{Energy, Stats};
|
use common::comp::{Energy, SpeechBubble, Stats};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
position::Align,
|
position::Align,
|
||||||
widget::{self, Image, Rectangle, Text},
|
widget::{self, Image, Rectangle, Text},
|
||||||
@ -42,6 +42,7 @@ widget_ids! {
|
|||||||
#[derive(WidgetCommon)]
|
#[derive(WidgetCommon)]
|
||||||
pub struct Overhead<'a> {
|
pub struct Overhead<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
bubble: Option<&'a SpeechBubble>,
|
||||||
stats: &'a Stats,
|
stats: &'a Stats,
|
||||||
energy: &'a Energy,
|
energy: &'a Energy,
|
||||||
own_level: u32,
|
own_level: u32,
|
||||||
@ -55,6 +56,7 @@ pub struct Overhead<'a> {
|
|||||||
impl<'a> Overhead<'a> {
|
impl<'a> Overhead<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
bubble: Option<&'a SpeechBubble>,
|
||||||
stats: &'a Stats,
|
stats: &'a Stats,
|
||||||
energy: &'a Energy,
|
energy: &'a Energy,
|
||||||
own_level: u32,
|
own_level: u32,
|
||||||
@ -64,6 +66,7 @@ impl<'a> Overhead<'a> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
bubble,
|
||||||
stats,
|
stats,
|
||||||
energy,
|
energy,
|
||||||
own_level,
|
own_level,
|
||||||
@ -84,11 +87,12 @@ impl<'a> Ingameable for Overhead<'a> {
|
|||||||
// Number of conrod primitives contained in the overhead display. TODO maybe
|
// Number of conrod primitives contained in the overhead display. TODO maybe
|
||||||
// this could be done automatically?
|
// this could be done automatically?
|
||||||
// - 2 Text::new for name
|
// - 2 Text::new for name
|
||||||
// - 2 Text::new for speech bubble
|
|
||||||
// - 10 Image::new for speech bubble (9-slice + tail)
|
|
||||||
// - 1 for level: either Text or Image
|
// - 1 for level: either Text or Image
|
||||||
// - 4 for HP + mana + fg + bg
|
// - 4 for HP + mana + fg + bg
|
||||||
19
|
// If there's a speech bubble
|
||||||
|
// - 1 Text::new for speech bubble
|
||||||
|
// - 10 Image::new for speech bubble (9-slice + tail)
|
||||||
|
7 + if self.bubble.is_some() { 11 } else { 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,80 +130,78 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
.x_y(0.0, MANA_BAR_Y + 50.0)
|
.x_y(0.0, MANA_BAR_Y + 50.0)
|
||||||
.set(state.ids.name, ui);
|
.set(state.ids.name, ui);
|
||||||
|
|
||||||
// Speech bubble
|
if let Some(bubble) = self.bubble {
|
||||||
Text::new("Hello")
|
// Speech bubble
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
let mut text = Text::new(&bubble.message)
|
||||||
.font_size(15)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
.font_size(18)
|
||||||
.up_from(state.ids.name, 10.0)
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
.x_align_to(state.ids.name, Align::Middle)
|
.up_from(state.ids.name, 10.0)
|
||||||
.parent(id)
|
.x_align_to(state.ids.name, Align::Middle)
|
||||||
.set(state.ids.chat_bubble_text, ui);
|
.parent(id);
|
||||||
Image::new(self.imgs.chat_bubble_top_left)
|
if let Some(w) = text.get_w(ui) {
|
||||||
.w_h(10.0, 10.0)
|
if w > 250.0 {
|
||||||
.top_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
text = text.w(250.0);
|
||||||
.parent(id)
|
}
|
||||||
.set(state.ids.chat_bubble_top_left, ui);
|
}
|
||||||
Image::new(self.imgs.chat_bubble_top)
|
Image::new(self.imgs.chat_bubble_top_left)
|
||||||
.h(10.0)
|
.w_h(10.0, 10.0)
|
||||||
.w_of(state.ids.chat_bubble_text)
|
.top_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.mid_top_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.parent(id)
|
||||||
.parent(id)
|
.set(state.ids.chat_bubble_top_left, ui);
|
||||||
.set(state.ids.chat_bubble_top, ui);
|
Image::new(self.imgs.chat_bubble_top)
|
||||||
Image::new(self.imgs.chat_bubble_top_right)
|
.h(10.0)
|
||||||
.w_h(10.0, 10.0)
|
.w_of(state.ids.chat_bubble_text)
|
||||||
.top_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.mid_top_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.chat_bubble_top_right, ui);
|
.set(state.ids.chat_bubble_top, ui);
|
||||||
Image::new(self.imgs.chat_bubble_left)
|
Image::new(self.imgs.chat_bubble_top_right)
|
||||||
.w(10.0)
|
.w_h(10.0, 10.0)
|
||||||
.h_of(state.ids.chat_bubble_text)
|
.top_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.mid_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.parent(id)
|
||||||
.parent(id)
|
.set(state.ids.chat_bubble_top_right, ui);
|
||||||
.set(state.ids.chat_bubble_left, ui);
|
Image::new(self.imgs.chat_bubble_left)
|
||||||
Image::new(self.imgs.chat_bubble_mid)
|
.w(10.0)
|
||||||
.wh_of(state.ids.chat_bubble_text)
|
.h_of(state.ids.chat_bubble_text)
|
||||||
.top_left_of(state.ids.chat_bubble_text)
|
.mid_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.chat_bubble_mid, ui);
|
.set(state.ids.chat_bubble_left, ui);
|
||||||
Image::new(self.imgs.chat_bubble_right)
|
Image::new(self.imgs.chat_bubble_mid)
|
||||||
.w(10.0)
|
.wh_of(state.ids.chat_bubble_text)
|
||||||
.h_of(state.ids.chat_bubble_text)
|
.top_left_of(state.ids.chat_bubble_text)
|
||||||
.mid_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.parent(id)
|
||||||
.parent(id)
|
.set(state.ids.chat_bubble_mid, ui);
|
||||||
.set(state.ids.chat_bubble_right, ui);
|
Image::new(self.imgs.chat_bubble_right)
|
||||||
Image::new(self.imgs.chat_bubble_bottom_left)
|
.w(10.0)
|
||||||
.w_h(10.0, 10.0)
|
.h_of(state.ids.chat_bubble_text)
|
||||||
.bottom_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.mid_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.chat_bubble_bottom_left, ui);
|
.set(state.ids.chat_bubble_right, ui);
|
||||||
Image::new(self.imgs.chat_bubble_bottom)
|
Image::new(self.imgs.chat_bubble_bottom_left)
|
||||||
.h(10.0)
|
.w_h(10.0, 10.0)
|
||||||
.w_of(state.ids.chat_bubble_text)
|
.bottom_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.parent(id)
|
||||||
.parent(id)
|
.set(state.ids.chat_bubble_bottom_left, ui);
|
||||||
.set(state.ids.chat_bubble_bottom, ui);
|
Image::new(self.imgs.chat_bubble_bottom)
|
||||||
Image::new(self.imgs.chat_bubble_bottom_right)
|
.h(10.0)
|
||||||
.w_h(10.0, 10.0)
|
.w_of(state.ids.chat_bubble_text)
|
||||||
.bottom_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.chat_bubble_bottom_right, ui);
|
.set(state.ids.chat_bubble_bottom, ui);
|
||||||
Image::new(self.imgs.chat_bubble_tail)
|
Image::new(self.imgs.chat_bubble_bottom_right)
|
||||||
.w_h(11.0, 16.0)
|
.w_h(10.0, 10.0)
|
||||||
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -16.0)
|
.bottom_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.chat_bubble_tail, ui);
|
.set(state.ids.chat_bubble_bottom_right, ui);
|
||||||
// Why is there a second text widget?: The first is to position the 9-slice
|
let tail = Image::new(self.imgs.chat_bubble_tail)
|
||||||
// around and the second is to display text. Changing .depth manually
|
.w_h(11.0, 16.0)
|
||||||
// causes strange problems in unrelated parts of the ui (the debug
|
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -16.0)
|
||||||
// overlay is offset by a npc's screen position) TODO
|
.parent(id);
|
||||||
Text::new("Hello")
|
// Move text to front (conrod depth is lowest first; not a z-index)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
tail.set(state.ids.chat_bubble_tail, ui);
|
||||||
.font_size(15)
|
text.depth(tail.get_depth() - 1.0)
|
||||||
.top_left_of(state.ids.chat_bubble_text)
|
.set(state.ids.chat_bubble_text, ui);
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
}
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.chat_bubble_text2, ui);
|
|
||||||
|
|
||||||
let hp_percentage =
|
let hp_percentage =
|
||||||
self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0;
|
self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0;
|
||||||
|
Reference in New Issue
Block a user