use crate::{settings, actor, audio}; use bevy::prelude::*; use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}; use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings}; use std::collections::VecDeque; use std::time::SystemTime; const HUD_REFRESH_TIME: f32 = 0.5; const FONT: &str = "tmp/fonts/NotoSansSC-Thin.ttf"; const LOG_MAX: usize = 5; const LOG_MAX_TIME_S: u64 = 20; pub struct HudPlugin; impl Plugin for HudPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup); app.add_systems(Update, (update, handle_input)); app.insert_resource(Log { logs: VecDeque::with_capacity(LOG_MAX), needs_rerendering: true, }); app.insert_resource(FPSUpdateTimer( Timer::from_seconds(HUD_REFRESH_TIME, TimerMode::Repeating))); } } #[derive(Component)] struct GaugesText; #[derive(Component)] struct ChatText; #[derive(Resource)] struct FPSUpdateTimer(Timer); pub enum LogLevel { Warning, //Error, Info, //Debug, Chat, //Ping, } struct Message { text: String, sender: String, level: LogLevel, time: u64, } #[derive(Resource)] pub struct Log { logs: VecDeque, needs_rerendering: bool, } impl Log { pub fn info(&mut self, message: String) { self.add(message, "System".to_string(), LogLevel::Info); } pub fn chat(&mut self, message: String, sender: String) { self.add(message, sender, LogLevel::Chat); } pub fn warning(&mut self, message: String) { self.add(message, "WARNING".to_string(), LogLevel::Warning); } pub fn add(&mut self, message: String, sender: String, level: LogLevel) { if self.logs.len() == LOG_MAX { self.logs.pop_front(); } if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { self.logs.push_back(Message { text: message, sender: sender, level: level, time: epoch.as_secs(), }); self.needs_rerendering = true; } } 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 { self.logs.pop_front(); } } } } } fn setup( mut commands: Commands, settings: Res, asset_server: Res, mut log: ResMut, ) { log.info("Customer wake-up registered.".to_string()); log.info("Systems reactivated.".to_string()); log.warning("Oxygen Low".to_string()); let visibility = if settings.hud_active { Visibility::Inherited } else { Visibility::Hidden }; let mut bundle_fps = TextBundle::from_sections([ TextSection::new( "帧率 ", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() }, ), TextSection::new( "", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), TextSection::new( "\n电量 ", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() }, ), TextSection::new( "", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), TextSection::new( "\n氧 OXYGEN ", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() }, ), TextSection::new( "", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::MAROON, ..default() } ), TextSection::new( "\nAdren水平 ", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() }, ), TextSection::new( "", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), TextSection::new( "\nProximity 警告 ", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() }, ), TextSection::new( "", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), TextSection::new( "\n\n", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), TextSection::from_style( TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), ]).with_style(Style { top: Val::VMin(2.0), left: Val::VMin(3.0), ..default() }); bundle_fps.visibility = visibility; commands.spawn(( bundle_fps, GaugesText, )); // Add Chat Box let bundle_chatbox = TextBundle::from_sections([ TextSection::new( "Hello World!\nThis Is Cool, right?", TextStyle { font: asset_server.load(FONT), font_size: settings.font_size_hud, color: Color::GRAY, ..default() } ), ]).with_style(Style { position_type: PositionType::Absolute, top: Val::VMin(2.0), right: Val::VMin(6.0), //bottom: Val::VMin(40.0), //left: Val::VMin(30.0), ..default() }).with_text_justify(JustifyText::Right); commands.spawn(( bundle_chatbox, ChatText, )); } fn update( diagnostics: Res, time: Res