veloren/voxygen/src/hud/subtitles.rs

228 lines
7.1 KiB
Rust
Raw Normal View History

2023-04-26 14:20:56 +00:00
use std::collections::VecDeque;
2023-04-26 18:38:35 +00:00
use crate::{settings::Settings, ui::fonts::Fonts};
2023-04-26 14:20:56 +00:00
use client::Client;
use common::comp;
use conrod_core::{
2023-04-26 18:38:35 +00:00
widget::{self, Id, Rectangle, Text},
widget_ids, Colorable, Positionable, UiCell, Widget, WidgetCommon,
2023-04-26 14:20:56 +00:00
};
use i18n::Localization;
use vek::{Vec2, Vec3};
widget_ids! {
struct Ids {
subtitle_box_bg,
subtitle_message[],
2023-04-26 18:38:35 +00:00
subtitle_dir[],
2023-04-26 14:20:56 +00:00
}
}
#[derive(WidgetCommon)]
pub struct Subtitles<'a> {
client: &'a Client,
2023-04-26 18:38:35 +00:00
settings: &'a Settings,
2023-04-26 14:20:56 +00:00
fonts: &'a Fonts,
new_subtitles: &'a mut VecDeque<Subtitle>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
localized_strings: &'a Localization,
}
impl<'a> Subtitles<'a> {
pub fn new(
client: &'a Client,
2023-04-26 18:38:35 +00:00
settings: &'a Settings,
2023-04-26 14:20:56 +00:00
new_subtitles: &'a mut VecDeque<Subtitle>,
fonts: &'a Fonts,
localized_strings: &'a Localization,
) -> Self {
Self {
client,
2023-04-26 18:38:35 +00:00
settings,
2023-04-26 14:20:56 +00:00
fonts,
new_subtitles,
common: widget::CommonBuilder::default(),
localized_strings,
}
}
}
const MAX_SUBTITLE_DIST: f32 = 80.0;
#[derive(Debug)]
pub struct Subtitle {
pub localization: String,
pub position: Option<Vec3<f32>>,
pub show_until: f64,
}
pub struct State {
subtitles: Vec<Subtitle>,
ids: Ids,
}
impl<'a> Widget for Subtitles<'a> {
type Event = ();
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
subtitles: Vec::new(),
ids: Ids::new(id_gen),
}
}
fn style(&self) -> Self::Style {}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
common_base::prof_span!("Chat::update");
let widget::UpdateArgs { state, ui, .. } = args;
let time = self.client.state().get_time();
let player_pos = self.client.position().unwrap_or_default();
let player_dir = self
.client
.state()
.read_storage::<comp::Ori>()
.get(self.client.entity())
.map_or(Vec3::unit_y(), |ori| ori.look_vec());
// Empty old subtitles and add new.
state.update(|s| {
s.subtitles.retain(|subtitle| {
time <= subtitle.show_until
&& subtitle
.position
.map_or(true, |pos| pos.distance(player_pos) <= MAX_SUBTITLE_DIST)
});
for mut subtitle in self.new_subtitles.drain(..) {
if subtitle
.position
.map_or(false, |pos| pos.distance(player_pos) > MAX_SUBTITLE_DIST)
{
continue;
}
let t = time + subtitle.show_until;
if let Some(s) = s
.subtitles
.iter_mut()
.find(|s| s.localization == subtitle.localization)
{
if t > s.show_until {
s.show_until = t;
s.position = subtitle.position;
}
} else {
subtitle.show_until = t;
s.subtitles.push(subtitle);
}
}
s.ids
.subtitle_message
.resize(s.subtitles.len(), &mut ui.widget_id_generator());
2023-04-26 18:38:35 +00:00
s.ids
.subtitle_dir
.resize(s.subtitles.len(), &mut ui.widget_id_generator());
2023-04-26 14:20:56 +00:00
});
2023-04-26 18:38:35 +00:00
let color = |t: &Subtitle| {
conrod_core::Color::Rgba(
0.9,
1.0,
1.0,
((t.show_until - time) * 1.5).clamp(0.0, 1.0) as f32,
)
};
2023-04-26 14:20:56 +00:00
let player_dir = player_dir.xy().try_normalized().unwrap_or(Vec2::unit_y());
let player_right = Vec2::new(player_dir.y, -player_dir.x);
2023-04-26 18:38:35 +00:00
let message = |subtitle: &Subtitle| self.localized_strings.get_msg(&subtitle.localization);
let dir = |subtitle: &Subtitle, id: &Id, dir_id: &Id, ui: &mut UiCell| {
2023-04-26 14:20:56 +00:00
let is_right = subtitle.position.and_then(|pos| {
let dist = pos.distance(player_pos);
let dir = (pos - player_pos) / dist;
let dot = dir.xy().dot(player_dir);
if dist < 2.0 || dot > 0.85 {
None
} else {
Some(dir.xy().dot(player_right) >= 0.0)
}
});
match is_right {
2023-04-26 18:38:35 +00:00
Some(true) => Text::new("> ")
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.parent(state.ids.subtitle_box_bg)
.align_right_of(state.ids.subtitle_box_bg)
.align_middle_y_of(*id)
.color(color(subtitle))
.set(*dir_id, ui),
Some(false) => Text::new(" <")
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.parent(state.ids.subtitle_box_bg)
.align_left_of(state.ids.subtitle_box_bg)
.align_middle_y_of(*id)
.color(color(subtitle))
.set(*dir_id, ui),
None => Text::new("")
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.parent(state.ids.subtitle_box_bg)
.color(color(subtitle))
.set(*dir_id, ui),
2023-04-26 14:20:56 +00:00
}
};
2023-04-26 18:38:35 +00:00
Rectangle::fill([200.0, 2.0 + 22.0 * state.subtitles.len() as f64])
.rgba(0.0, 0.0, 0.0, self.settings.chat.chat_opacity)
.bottom_right_with_margins_on(ui.window, 40.0, 50.0)
.set(state.ids.subtitle_box_bg, ui);
let mut subtitles = state
.ids
.subtitle_message
.iter()
.zip(state.ids.subtitle_dir.iter())
.zip(state.subtitles.iter());
if let Some(((id, dir_id), subtitle)) = subtitles.next() {
2023-04-26 14:20:56 +00:00
Text::new(&message(subtitle))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
2023-04-26 18:38:35 +00:00
.parent(state.ids.subtitle_box_bg)
2023-04-26 14:20:56 +00:00
.center_justify()
2023-04-26 18:38:35 +00:00
.mid_bottom_with_margin_on(state.ids.subtitle_box_bg, 6.0)
.color(color(subtitle))
2023-04-26 14:20:56 +00:00
.set(*id, ui);
2023-04-26 18:38:35 +00:00
dir(subtitle, id, dir_id, ui);
2023-04-26 14:20:56 +00:00
let mut last_id = *id;
2023-04-26 18:38:35 +00:00
for ((id, dir_id), subtitle) in subtitles {
2023-04-26 14:20:56 +00:00
Text::new(&message(subtitle))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
2023-04-26 18:38:35 +00:00
.parent(state.ids.subtitle_box_bg)
.up_from(last_id, 8.0)
.align_middle_x_of(last_id)
.color(color(subtitle))
2023-04-26 14:20:56 +00:00
.set(*id, ui);
2023-04-26 18:38:35 +00:00
dir(subtitle, id, dir_id, ui);
2023-04-26 14:20:56 +00:00
last_id = *id;
}
}
}
}