blackout/src/sound/icon.rs

181 lines
5.1 KiB
Rust

use bevy::prelude::*;
use bevy_synthizer::{Audio, Sound};
use rand::random;
use crate::{
core::Player,
exploration::ExplorationFocused,
visibility::{Visible, VisibleEntities},
};
#[derive(Component, Clone, Debug)]
pub struct SoundIcon {
pub audio: Audio,
pub gain: f64,
pub pitch: f64,
pub interval: Option<Timer>,
}
impl Default for SoundIcon {
fn default() -> Self {
let seconds = random::<f32>() + 4.5;
Self {
audio: default(),
gain: 1.,
pitch: 1.,
interval: Some(Timer::from_seconds(seconds, TimerMode::Once)),
}
}
}
fn added(mut commands: Commands, icons: Query<(Entity, &SoundIcon), Added<SoundIcon>>) {
for (entity, icon) in &icons {
let buffer = icon.audio.clone();
let gain = icon.gain;
let pitch = icon.pitch;
let looping = icon.interval.is_none();
commands.entity(entity).insert(Sound {
audio: buffer,
gain,
pitch,
looping,
paused: true,
..default()
});
}
}
fn update<S>(
config: Res<SoundIconPlugin<S>>,
state: Res<State<S>>,
time: Res<Time>,
viewers: Query<&VisibleEntities, With<Player>>,
mut icons: Query<(
Entity,
&mut SoundIcon,
Option<&Visible>,
Option<&Parent>,
&mut Sound,
)>,
) where
S: States,
{
if !config.states.is_empty() && !config.states.contains(state.get()) {
return;
}
for visible in &viewers {
for (icon_entity, mut icon, visibility, parent, mut sound) in &mut icons {
let entity = if visibility.is_some() {
Some(icon_entity)
} else if parent.is_some() {
Some(**parent.unwrap())
} else {
None
};
if let Some(entity) = entity {
if visible.contains(&entity) {
if let Some(interval) = icon.interval.as_mut() {
if interval.finished() {
interval.reset();
continue;
} else if interval.percent() == 0. {
sound.generator = None;
}
interval.tick(time.delta());
}
if sound.audio != icon.audio {
sound.audio = icon.audio.clone();
}
if sound.gain != icon.gain {
sound.gain = icon.gain;
}
if sound.pitch != icon.pitch {
sound.pitch = icon.pitch;
}
let looping = icon.interval.is_none();
if sound.looping != looping {
sound.looping = looping;
}
if sound.paused {
sound.paused = false;
sound.generator = None;
}
} else if !sound.paused {
sound.paused = true;
if let Some(interval) = icon.interval.as_mut() {
interval.reset();
}
}
} else {
panic!("Should not happen");
}
}
}
}
fn exploration_focus_changed(
mut focused: Query<(Entity, Option<&Children>), Changed<ExplorationFocused>>,
mut icons: Query<&mut SoundIcon>,
) {
const ICON_GAIN: f64 = 3.;
for (entity, children) in &mut focused {
if let Ok(mut icon) = icons.get_mut(entity) {
icon.gain *= ICON_GAIN;
}
if let Some(children) = children {
for child in children.iter() {
if let Ok(mut icon) = icons.get_mut(*child) {
icon.gain *= ICON_GAIN;
}
}
}
}
}
fn exploration_focus_removed(
mut removed: RemovedComponents<ExplorationFocused>,
mut query: Query<&mut SoundIcon>,
children: Query<&Children>,
) {
const ICON_GAIN: f64 = 3.;
for entity in &mut removed {
if let Ok(mut icon) = query.get_mut(entity) {
icon.gain /= ICON_GAIN;
}
if let Ok(children) = children.get(entity) {
for child in children.iter() {
if let Ok(mut icon) = query.get_mut(*child) {
icon.gain /= ICON_GAIN;
}
}
}
}
}
#[derive(Resource, Clone)]
pub struct SoundIconPlugin<S> {
pub states: Vec<S>,
}
impl<S> Default for SoundIconPlugin<S> {
fn default() -> Self {
Self { states: default() }
}
}
impl<S> Plugin for SoundIconPlugin<S>
where
S: States,
{
fn build(&self, app: &mut App) {
app.insert_resource(self.clone())
.add_systems(PreUpdate, added)
.add_systems(PreUpdate, (exploration_focus_changed, update::<S>).chain())
.add_systems(
PostUpdate,
exploration_focus_removed.after(exploration_focus_changed),
);
}
}