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:
parent
73a29b339c
commit
c65967ccdb
common/src
server/src
voxygen/src/hud
@ -1,5 +1,5 @@
|
||||
use crate::path::Chaser;
|
||||
use specs::{Component, Entity as EcsEntity};
|
||||
use crate::{path::Chaser, state::Time};
|
||||
use specs::{Component, Entity as EcsEntity, FlaggedStorage, HashMapStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
use vek::*;
|
||||
|
||||
@ -85,3 +85,17 @@ impl Activity {
|
||||
impl Default for Activity {
|
||||
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
|
||||
pub use ability::{CharacterAbility, ItemConfig, Loadout};
|
||||
pub use admin::Admin;
|
||||
pub use agent::{Agent, Alignment};
|
||||
pub use agent::{Agent, Alignment, SpeechBubble, SPEECH_BUBBLE_DURATION};
|
||||
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,
|
||||
|
@ -24,6 +24,7 @@ sum_type! {
|
||||
Sticky(comp::Sticky),
|
||||
Loadout(comp::Loadout),
|
||||
CharacterState(comp::CharacterState),
|
||||
SpeechBubble(comp::SpeechBubble),
|
||||
Pos(comp::Pos),
|
||||
Vel(comp::Vel),
|
||||
Ori(comp::Ori),
|
||||
@ -50,6 +51,7 @@ 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>),
|
||||
@ -76,6 +78,7 @@ 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),
|
||||
@ -100,6 +103,7 @@ 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),
|
||||
@ -128,6 +132,9 @@ 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),
|
||||
|
@ -122,6 +122,7 @@ 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>();
|
||||
|
@ -109,6 +109,7 @@ 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::StatsPersistenceTimer::default());
|
||||
|
@ -4,7 +4,10 @@ use crate::{
|
||||
CLIENT_TIMEOUT,
|
||||
};
|
||||
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},
|
||||
msg::{
|
||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
||||
@ -43,6 +46,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Player>,
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, Controller>,
|
||||
WriteStorage<'a, SpeechBubble>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
@ -67,6 +71,7 @@ impl<'a> System<'a> for Sys {
|
||||
mut players,
|
||||
mut clients,
|
||||
mut controllers,
|
||||
mut speech_bubbles,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
timer.start();
|
||||
@ -394,13 +399,20 @@ impl<'a> System<'a> for Sys {
|
||||
for (entity, msg) in new_chat_msgs {
|
||||
match msg {
|
||||
ServerMsg::ChatMsg { chat_type, message } => {
|
||||
if let Some(entity) = entity {
|
||||
let message = if let Some(entity) = entity {
|
||||
// Handle chat commands.
|
||||
if message.starts_with("/") && message.len() > 1 {
|
||||
let argv = String::from(&message[1..]);
|
||||
server_emitter.emit(ServerEvent::ChatCmd(entity, argv));
|
||||
continue;
|
||||
} 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) => {
|
||||
if admins.get(entity).is_some() {
|
||||
format!("[ADMIN][{}] {}", &player.alias, message)
|
||||
@ -409,17 +421,14 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
},
|
||||
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 {
|
||||
let msg = ServerMsg::ChatMsg { chat_type, message };
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(msg.clone());
|
||||
}
|
||||
message
|
||||
};
|
||||
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 persistence;
|
||||
pub mod sentinel;
|
||||
pub mod speech_bubble;
|
||||
pub mod subscription;
|
||||
pub mod terrain;
|
||||
pub mod terrain_sync;
|
||||
@ -20,6 +21,7 @@ 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 StatsPersistenceTimer = SysTimer<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_SYS: &str = "server_terrain_sys";
|
||||
const WAYPOINT_SYS: &str = "waypoint_sys";
|
||||
const SPEECH_BUBBLE_SYS: &str = "speech_bubble_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::stats::Sys, STATS_PERSISTENCE_SYS, &[]);
|
||||
}
|
||||
|
||||
|
@ -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, Stats, Sticky, Vel,
|
||||
Mass, MountState, Mounting, Ori, Player, Pos, Scale, SpeechBubble, Stats, Sticky, Vel,
|
||||
},
|
||||
msg::EcsCompPacket,
|
||||
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
|
||||
@ -54,6 +54,7 @@ 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(
|
||||
@ -125,6 +126,10 @@ 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()));
|
||||
@ -152,6 +157,7 @@ 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(
|
||||
@ -188,6 +194,12 @@ 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)
|
||||
@ -213,6 +225,7 @@ 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) {
|
||||
@ -236,6 +249,7 @@ 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) {
|
||||
@ -256,6 +270,7 @@ 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
|
||||
|
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 energy = ecs.read_storage::<comp::Energy>();
|
||||
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 players = ecs.read_storage::<comp::Player>();
|
||||
let scales = ecs.read_storage::<comp::Scale>();
|
||||
@ -890,7 +891,7 @@ impl Hud {
|
||||
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) in (
|
||||
for (pos, name, stats, energy, height_offset, hpfl, bubble) in (
|
||||
&entities,
|
||||
&pos,
|
||||
interpolated.maybe(),
|
||||
@ -900,11 +901,12 @@ impl Hud {
|
||||
scales.maybe(),
|
||||
&bodies,
|
||||
&hp_floater_lists,
|
||||
speech_bubbles.maybe(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _, _, _, _, _)| *entity != me && !stats.is_dead)
|
||||
.filter(|(entity, _, _, stats, _, _, _, _, _, _)| *entity != me && !stats.is_dead)
|
||||
// Don't show outside a certain range
|
||||
.filter(|(_, pos, _, _, _, _, _, _, hpfl)| {
|
||||
.filter(|(_, pos, _, _, _, _, _, _, hpfl, _)| {
|
||||
pos.0.distance_squared(player_pos)
|
||||
< (if hpfl
|
||||
.time_since_last_dmg_by_me
|
||||
@ -916,7 +918,7 @@ impl Hud {
|
||||
})
|
||||
.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
|
||||
// If the player used the default character name display their name instead
|
||||
let name = if stats.name == "Character Name" {
|
||||
@ -932,6 +934,7 @@ 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,
|
||||
)
|
||||
})
|
||||
{
|
||||
@ -944,6 +947,7 @@ impl Hud {
|
||||
// Chat bubble, name, level, and hp bars
|
||||
overhead::Overhead::new(
|
||||
&name,
|
||||
bubble,
|
||||
stats,
|
||||
energy,
|
||||
own_level,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{img_ids::Imgs, HP_COLOR, LOW_HP_COLOR, MANA_COLOR};
|
||||
use crate::ui::{fonts::ConrodVoxygenFonts, Ingameable};
|
||||
use common::comp::{Energy, Stats};
|
||||
use common::comp::{Energy, SpeechBubble, Stats};
|
||||
use conrod_core::{
|
||||
position::Align,
|
||||
widget::{self, Image, Rectangle, Text},
|
||||
@ -42,6 +42,7 @@ widget_ids! {
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Overhead<'a> {
|
||||
name: &'a str,
|
||||
bubble: Option<&'a SpeechBubble>,
|
||||
stats: &'a Stats,
|
||||
energy: &'a Energy,
|
||||
own_level: u32,
|
||||
@ -55,6 +56,7 @@ pub struct Overhead<'a> {
|
||||
impl<'a> Overhead<'a> {
|
||||
pub fn new(
|
||||
name: &'a str,
|
||||
bubble: Option<&'a SpeechBubble>,
|
||||
stats: &'a Stats,
|
||||
energy: &'a Energy,
|
||||
own_level: u32,
|
||||
@ -64,6 +66,7 @@ impl<'a> Overhead<'a> {
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
bubble,
|
||||
stats,
|
||||
energy,
|
||||
own_level,
|
||||
@ -84,11 +87,12 @@ impl<'a> Ingameable for Overhead<'a> {
|
||||
// Number of conrod primitives contained in the overhead display. TODO maybe
|
||||
// this could be done automatically?
|
||||
// - 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
|
||||
// - 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)
|
||||
.set(state.ids.name, ui);
|
||||
|
||||
// Speech bubble
|
||||
Text::new("Hello")
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(15)
|
||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||
.up_from(state.ids.name, 10.0)
|
||||
.x_align_to(state.ids.name, Align::Middle)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_text, ui);
|
||||
Image::new(self.imgs.chat_bubble_top_left)
|
||||
.w_h(10.0, 10.0)
|
||||
.top_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_top_left, ui);
|
||||
Image::new(self.imgs.chat_bubble_top)
|
||||
.h(10.0)
|
||||
.w_of(state.ids.chat_bubble_text)
|
||||
.mid_top_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_top, ui);
|
||||
Image::new(self.imgs.chat_bubble_top_right)
|
||||
.w_h(10.0, 10.0)
|
||||
.top_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_top_right, ui);
|
||||
Image::new(self.imgs.chat_bubble_left)
|
||||
.w(10.0)
|
||||
.h_of(state.ids.chat_bubble_text)
|
||||
.mid_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_left, ui);
|
||||
Image::new(self.imgs.chat_bubble_mid)
|
||||
.wh_of(state.ids.chat_bubble_text)
|
||||
.top_left_of(state.ids.chat_bubble_text)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_mid, ui);
|
||||
Image::new(self.imgs.chat_bubble_right)
|
||||
.w(10.0)
|
||||
.h_of(state.ids.chat_bubble_text)
|
||||
.mid_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_right, ui);
|
||||
Image::new(self.imgs.chat_bubble_bottom_left)
|
||||
.w_h(10.0, 10.0)
|
||||
.bottom_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_bottom_left, ui);
|
||||
Image::new(self.imgs.chat_bubble_bottom)
|
||||
.h(10.0)
|
||||
.w_of(state.ids.chat_bubble_text)
|
||||
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_bottom, ui);
|
||||
Image::new(self.imgs.chat_bubble_bottom_right)
|
||||
.w_h(10.0, 10.0)
|
||||
.bottom_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_bottom_right, ui);
|
||||
Image::new(self.imgs.chat_bubble_tail)
|
||||
.w_h(11.0, 16.0)
|
||||
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -16.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_tail, ui);
|
||||
// Why is there a second text widget?: The first is to position the 9-slice
|
||||
// around and the second is to display text. Changing .depth manually
|
||||
// causes strange problems in unrelated parts of the ui (the debug
|
||||
// overlay is offset by a npc's screen position) TODO
|
||||
Text::new("Hello")
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(15)
|
||||
.top_left_of(state.ids.chat_bubble_text)
|
||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_text2, ui);
|
||||
if let Some(bubble) = self.bubble {
|
||||
// Speech bubble
|
||||
let mut text = Text::new(&bubble.message)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(18)
|
||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||
.up_from(state.ids.name, 10.0)
|
||||
.x_align_to(state.ids.name, Align::Middle)
|
||||
.parent(id);
|
||||
if let Some(w) = text.get_w(ui) {
|
||||
if w > 250.0 {
|
||||
text = text.w(250.0);
|
||||
}
|
||||
}
|
||||
Image::new(self.imgs.chat_bubble_top_left)
|
||||
.w_h(10.0, 10.0)
|
||||
.top_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_top_left, ui);
|
||||
Image::new(self.imgs.chat_bubble_top)
|
||||
.h(10.0)
|
||||
.w_of(state.ids.chat_bubble_text)
|
||||
.mid_top_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_top, ui);
|
||||
Image::new(self.imgs.chat_bubble_top_right)
|
||||
.w_h(10.0, 10.0)
|
||||
.top_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_top_right, ui);
|
||||
Image::new(self.imgs.chat_bubble_left)
|
||||
.w(10.0)
|
||||
.h_of(state.ids.chat_bubble_text)
|
||||
.mid_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_left, ui);
|
||||
Image::new(self.imgs.chat_bubble_mid)
|
||||
.wh_of(state.ids.chat_bubble_text)
|
||||
.top_left_of(state.ids.chat_bubble_text)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_mid, ui);
|
||||
Image::new(self.imgs.chat_bubble_right)
|
||||
.w(10.0)
|
||||
.h_of(state.ids.chat_bubble_text)
|
||||
.mid_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_right, ui);
|
||||
Image::new(self.imgs.chat_bubble_bottom_left)
|
||||
.w_h(10.0, 10.0)
|
||||
.bottom_left_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_bottom_left, ui);
|
||||
Image::new(self.imgs.chat_bubble_bottom)
|
||||
.h(10.0)
|
||||
.w_of(state.ids.chat_bubble_text)
|
||||
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_bottom, ui);
|
||||
Image::new(self.imgs.chat_bubble_bottom_right)
|
||||
.w_h(10.0, 10.0)
|
||||
.bottom_right_with_margin_on(state.ids.chat_bubble_text, -10.0)
|
||||
.parent(id)
|
||||
.set(state.ids.chat_bubble_bottom_right, ui);
|
||||
let tail = Image::new(self.imgs.chat_bubble_tail)
|
||||
.w_h(11.0, 16.0)
|
||||
.mid_bottom_with_margin_on(state.ids.chat_bubble_text, -16.0)
|
||||
.parent(id);
|
||||
// Move text to front (conrod depth is lowest first; not a z-index)
|
||||
tail.set(state.ids.chat_bubble_tail, ui);
|
||||
text.depth(tail.get_depth() - 1.0)
|
||||
.set(state.ids.chat_bubble_text, ui);
|
||||
}
|
||||
|
||||
let hp_percentage =
|
||||
self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0;
|
||||
|
Loading…
Reference in New Issue
Block a user