From 2d5348956e310067b16bade90aabe3aebb426993 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 15 Apr 2024 03:57:21 +0200 Subject: [PATCH] move chatbox to the top left, implement fading of old messages --- src/hud.rs | 150 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 43 deletions(-) diff --git a/src/hud.rs b/src/hud.rs index 6c432ef..1d46a83 100644 --- a/src/hud.rs +++ b/src/hud.rs @@ -9,8 +9,9 @@ use std::time::SystemTime; pub const HUD_REFRESH_TIME: f32 = 0.1; pub const FONT: &str = "fonts/Yupiter-Regular.ttf"; -pub const LOG_MAX: usize = 20; -pub const LOG_MAX_TIME_S: u64 = 20; +pub const LOG_MAX: usize = 4; +pub const LOG_MAX_TIME_S: f64 = 15.0; +pub const LOG_MAX_ROWS: usize = 30; pub const AMBIENT_LIGHT: f32 = 0.0; // Space is DARK pub const AMBIENT_LIGHT_AR: f32 = 15.0; //pub const REPLY_NUMBERS: [char; 10] = ['❶', '❷', '❸', '❹', '❺', '❻', '❼', '❽', '❾', '⓿']; @@ -83,7 +84,15 @@ struct Message { text: String, sender: String, level: LogLevel, - time: u64, + time: f64, +} +impl Message { + pub fn get_freshness(&self) -> f64 { + if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + return (1.0 - (epoch.as_secs_f64() - self.time) / LOG_MAX_TIME_S).clamp(0.0, 1.0); + } + return 1.0; + } } #[derive(Component)] @@ -128,16 +137,17 @@ impl Log { text, sender, level, - time: epoch.as_secs(), + time: epoch.as_secs_f64(), }); self.needs_rerendering = true; } } + #[allow(dead_code)] pub fn remove_old(&mut self) { if let Some(message) = self.logs.front() { if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - if epoch.as_secs() - message.time > LOG_MAX_TIME_S { + if epoch.as_secs_f64() - message.time > LOG_MAX_TIME_S { self.logs.pop_front(); } } @@ -169,12 +179,6 @@ fn setup( color: Color::GRAY, ..default() }; - let style_choices = TextStyle { - font: asset_server.load(FONT), - font_size: settings.font_size_hud, - color: Color::WHITE, - ..default() - }; let mut bundle_fps = TextBundle::from_sections([ TextSection::new("", style.clone()), TextSection::new(" ⚡ ", style.clone()), @@ -206,23 +210,50 @@ fn setup( // Add Chat Box let bundle_chatbox = TextBundle::from_sections([ - TextSection::new("Warning: System Log Uninitialized", style.clone()), - TextSection::new("\n", style.clone()), - TextSection::new("\n\n\n", style_choices), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), + TextSection::new("", style.clone()), ]).with_style(Style { position_type: PositionType::Absolute, - bottom: Val::VMin(0.0), + top: Val::VMin(0.0), left: Val::VMin(0.0), ..default() }).with_text_justify(JustifyText::Left); commands.spawn(( NodeBundle { style: Style { - width: Val::Percent(50.), + width: Val::Percent(45.0), align_items: AlignItems::Start, position_type: PositionType::Absolute, - bottom: Val::Vh(10.0), - left: Val::Vw(25.0), + top: Val::VMin(2.0), + left: Val::VMin(3.0), ..default() }, ..default() @@ -290,6 +321,7 @@ fn update_hud( if timer.0.tick(time.delta()).just_finished() || log.needs_rerendering { let q_camera_result = q_camera.get_single(); let player = player.get_single(); + let mut freshest_line: f64 = 0.0; if player.is_ok() && q_camera_result.is_ok() { let (hp, suit, gforce) = player.unwrap(); let (pos, cam_v) = q_camera_result.unwrap(); @@ -395,6 +427,49 @@ fn update_hud( } if let Ok(mut chat) = query_chat.get_single_mut() { + let mut row = 0; + let bright = Color::rgb(0.8, 0.75, 0.78); + + // Chat Log and System Log + let logfilter = if settings.hud_active { + |_msg: &&Message| { true } + } else { + |msg: &&Message| { match msg.level { + LogLevel::Chat => true, + LogLevel::Warning => true, + LogLevel::Info => true, + _ => false + }} + }; + let mut messages: Vec<&Message> = log.logs.iter() + .filter(logfilter) + .rev() + .take(15) + .collect(); + messages.reverse(); + for msg in &messages { + if msg.sender.is_empty() { + chat.sections[row].value = msg.text.clone() + "\n"; + } + else { + chat.sections[row].value = format!("{}: {}\n", msg.sender, msg.text); + } + let freshness = msg.get_freshness(); + let clr: f32 = (freshness.powf(1.5) as f32).clamp(0.1, 1.0); + freshest_line = freshest_line.max(freshness); + chat.sections[row].style.color = Color::rgba(0.7, 0.7, 0.7, clr); + row += 1; + } + + // Highlight most recent line if in a conversation + if row > 0 && (q_choices.is_empty() || freshest_line > 0.5) { + chat.sections[row-1].style.color = bright; + } + + // Add padding between chat and choices + chat.sections[row].value = "\n".to_string(); + row += 1; + // Choices let mut choices: Vec = Vec::new(); let mut count = 0; @@ -414,34 +489,23 @@ fn update_hud( choices.push(" ".to_string()); } } - chat.sections[2].value = choices.join("\n"); + for choice in choices { + chat.sections[row].value = choice + "\n"; + chat.sections[row].style.color = bright; + row += 1; + } - // Chat Log and System Log - let logfilter = if settings.hud_active { - |_msg: &&Message| { true } - } else { - |msg: &&Message| { match msg.level { - LogLevel::Chat => true, - LogLevel::Warning => true, - LogLevel::Info => true, - _ => false - }} - }; - let mut logs_vec: Vec = log.logs.iter() - .filter(logfilter) - .map(|s| if s.sender.is_empty() { - format!("{}", s.text) - } else { - format!("{}: {}", s.sender, s.text) - }) - .rev() - .take(3) - .collect(); - logs_vec.reverse(); - chat.sections[0].value = logs_vec.join("\n"); + // Blank the remaining rows + while row < LOG_MAX_ROWS { + chat.sections[row].value = "".to_string(); + row += 1; + } } log.needs_rerendering = false; - log.remove_old(); + + if q_choices.is_empty() && freshest_line < 0.2 { + log.remove_old(); + } } }