mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Getting merge ready minus device picker
This commit is contained in:
parent
2d088faea4
commit
d47e0bbb73
@ -280,25 +280,25 @@
|
||||
),
|
||||
|
||||
//
|
||||
// Fire Rod / Regeneration Staff
|
||||
// Fire Staff
|
||||
//
|
||||
Wield(Staff): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.weapon.staff_out",
|
||||
"voxygen.audio.sfx.weapon.sword_out",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Unwield(Staff): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.weapon.staff_in",
|
||||
"voxygen.audio.sfx.weapon.sword_in",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Attack(BasicMelee, Staff): (
|
||||
Attack(BasicBeam, Staff): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.swing",
|
||||
"voxygen.audio.sfx.abilities.flame_thrower",
|
||||
],
|
||||
threshold: 0.8,
|
||||
threshold: 0.2,
|
||||
),
|
||||
Attack(BasicRanged, Staff): (
|
||||
files: [
|
||||
|
BIN
assets/voxygen/audio/sfx/abilities/flame_thrower.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/abilities/flame_thrower.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -112,7 +112,7 @@
|
||||
title: "Limits",
|
||||
path: "voxygen.audio.soundtrack.limits",
|
||||
length: 203.0,
|
||||
timing: Some(Night),
|
||||
timing: None,
|
||||
biomes: [],
|
||||
site: Some(Dungeon),
|
||||
artist: "badbbad",
|
||||
|
@ -3,7 +3,6 @@
|
||||
(
|
||||
path: "voxygen.audio.ambient.wind",
|
||||
length: 4.5,
|
||||
timing: Some(Day),
|
||||
),
|
||||
]
|
||||
)
|
||||
|
@ -21,6 +21,7 @@ use crate::audio::{
|
||||
Listener,
|
||||
};
|
||||
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
|
||||
use tracing::warn;
|
||||
use vek::*;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
@ -54,11 +55,23 @@ pub struct MusicChannel {
|
||||
|
||||
impl MusicChannel {
|
||||
pub fn new(stream: &OutputStreamHandle) -> Self {
|
||||
Self {
|
||||
sink: Sink::try_new(stream).unwrap(),
|
||||
tag: MusicChannelTag::TitleMusic,
|
||||
state: ChannelState::Stopped,
|
||||
fader: Fader::default(),
|
||||
let new_sink = Sink::try_new(stream);
|
||||
match new_sink {
|
||||
Ok(sink) => Self {
|
||||
sink,
|
||||
tag: MusicChannelTag::TitleMusic,
|
||||
state: ChannelState::Stopped,
|
||||
fader: Fader::default(),
|
||||
},
|
||||
Err(_) => {
|
||||
warn!("Failed to create a rodio sink. May not play sounds.");
|
||||
Self {
|
||||
sink: Sink::new_idle().0,
|
||||
tag: MusicChannelTag::TitleMusic,
|
||||
state: ChannelState::Stopped,
|
||||
fader: Fader::default(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +153,8 @@ impl MusicChannel {
|
||||
}
|
||||
}
|
||||
|
||||
/// A WindChannel uses a non-positional audio sink designed to play music which
|
||||
/// is always heard at the player's position.
|
||||
pub struct WindChannel {
|
||||
sink: Sink,
|
||||
}
|
||||
@ -147,11 +162,18 @@ pub struct WindChannel {
|
||||
impl WindChannel {
|
||||
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
|
||||
|
||||
pub fn volume(&mut self) -> f32 { self.sink.volume() }
|
||||
pub fn get_volume(&mut self) -> f32 { self.sink.volume() }
|
||||
|
||||
pub fn new(stream: &OutputStreamHandle) -> Self {
|
||||
Self {
|
||||
sink: Sink::try_new(stream).unwrap(),
|
||||
let new_sink = Sink::try_new(stream);
|
||||
match new_sink {
|
||||
Ok(sink) => Self { sink },
|
||||
Err(_) => {
|
||||
warn!("Failed to create rodio sink. May not play wind sounds.");
|
||||
Self {
|
||||
sink: Sink::new_idle().0,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +217,8 @@ impl SfxChannel {
|
||||
self.sink.append(source);
|
||||
}
|
||||
|
||||
/// Same as SfxChannel::play but with the source passed through
|
||||
/// a low pass filter at 300 Hz
|
||||
pub fn play_with_low_pass_filter<S>(&mut self, source: S)
|
||||
where
|
||||
S: Sized + Send + 'static,
|
||||
|
@ -51,31 +51,24 @@ impl AudioFrontend {
|
||||
/// Construct with given device
|
||||
pub fn new(dev: String, max_sfx_channels: usize) -> Self {
|
||||
let audio_device = get_device_raw(&dev);
|
||||
|
||||
let device = match get_default_device() {
|
||||
Some(d) => d,
|
||||
None => "".to_string(),
|
||||
};
|
||||
//if let Some(this_device) = device {
|
||||
//let (stream, audio_stream) = match get_stream(&device.clone().unwrap()) {
|
||||
// Ok(s) => (Some(s.0), Some(s.1)),
|
||||
// Err(_) => (None, None),
|
||||
//};
|
||||
//} else {
|
||||
let (stream, audio_stream) = match get_default_stream() {
|
||||
Ok(s) => (Some(s.0), Some(s.1)),
|
||||
Err(_) => (None, None),
|
||||
|
||||
//let (stream, audio_stream) = match get_default_stream() {
|
||||
let (stream, audio_stream) = if get_device_raw(&dev).is_some() {
|
||||
match get_stream(&get_device_raw(&dev).unwrap()) {
|
||||
Ok(s) => (Some(s.0), Some(s.1)),
|
||||
Err(_) => (None, None),
|
||||
}
|
||||
} else {
|
||||
match get_default_stream() {
|
||||
Ok(s) => (Some(s.0), Some(s.1)),
|
||||
Err(_) => (None, None),
|
||||
}
|
||||
};
|
||||
//}
|
||||
//let (stream, audio_stream) = match &device {
|
||||
// Some(dev) => match get_stream(&dev) {
|
||||
// Ok(s) => (Some(s.0), Some(s.1)),
|
||||
// Err(_) => (None, None),
|
||||
// },
|
||||
// None => match get_default_stream() {
|
||||
// Ok(s) => (Some(s.0), Some(s.1)),
|
||||
// Err(_) => (None, None),
|
||||
// },
|
||||
//};
|
||||
|
||||
let mut sfx_channels = Vec::with_capacity(max_sfx_channels);
|
||||
let mut wind_channels = Vec::new();
|
||||
@ -127,6 +120,7 @@ impl AudioFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrive an empty sfx channel from the list
|
||||
fn get_sfx_channel(&mut self) -> Option<&mut SfxChannel> {
|
||||
if self.audio_stream.is_some() {
|
||||
if let Some(channel) = self.sfx_channels.iter_mut().find(|c| c.is_done()) {
|
||||
@ -246,7 +240,7 @@ impl AudioFrontend {
|
||||
fn get_wind_volume(&mut self) -> f32 {
|
||||
if self.audio_stream.is_some() {
|
||||
if let Some(channel) = self.wind_channels.iter_mut().find(|_c| true) {
|
||||
channel.volume() / self.sfx_volume
|
||||
channel.get_volume() / self.sfx_volume
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
|
@ -50,24 +50,31 @@ use serde::Deserialize;
|
||||
use std::time::Instant;
|
||||
use tracing::warn;
|
||||
|
||||
// TODO These should eventually not be constants if we have seasons
|
||||
const DAY_START_SECONDS: u32 = 28800; // 8:00
|
||||
const DAY_END_SECONDS: u32 = 70200; // 19:30
|
||||
|
||||
/// Collection of all the tracks
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
struct SoundtrackCollection {
|
||||
/// List of tracks
|
||||
tracks: Vec<SoundtrackItem>,
|
||||
}
|
||||
|
||||
/// Configuration for a single music track in the soundtrack
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SoundtrackItem {
|
||||
/// Song title
|
||||
title: String,
|
||||
/// File path to asset
|
||||
path: String,
|
||||
/// Length of the track in seconds
|
||||
length: f32,
|
||||
/// Whether this track should play during day or night
|
||||
timing: Option<DayPeriod>,
|
||||
/// What biomes this track should play in with chance of play
|
||||
biomes: Vec<(BiomeKind, u8)>,
|
||||
/// Whether this track should play in a specific site
|
||||
site: Option<SitesKind>,
|
||||
}
|
||||
|
||||
@ -91,17 +98,19 @@ enum PlayState {
|
||||
|
||||
/// Provides methods to control music playback
|
||||
pub struct MusicMgr {
|
||||
/// Collection of all the tracks
|
||||
soundtrack: SoundtrackCollection,
|
||||
/// Instant at which the current track began playing
|
||||
began_playing: Instant,
|
||||
/// Time until the next track should be played
|
||||
next_track_change: f32,
|
||||
/// The title of the last track played. Used to prevent a track
|
||||
/// being played twice in a row
|
||||
last_track: String,
|
||||
}
|
||||
|
||||
impl MusicMgr {
|
||||
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
||||
pub fn new() -> Self {
|
||||
impl Default for MusicMgr {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
soundtrack: Self::load_soundtrack_items(),
|
||||
began_playing: Instant::now(),
|
||||
@ -109,7 +118,9 @@ impl MusicMgr {
|
||||
last_track: String::from("None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MusicMgr {
|
||||
/// Checks whether the previous track has completed. If so, sends a
|
||||
/// request to play the next (random) track
|
||||
pub fn maintain(&mut self, audio: &mut AudioFrontend, state: &State, client: &Client) {
|
||||
@ -149,15 +160,18 @@ impl MusicMgr {
|
||||
}
|
||||
|
||||
fn play_random_track(&mut self, audio: &mut AudioFrontend, state: &State, client: &Client) {
|
||||
let silence_between_tracks_seconds: f32 = thread_rng().gen_range(30.0, 60.0);
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// Adds a bit of randomness between plays
|
||||
let silence_between_tracks_seconds: f32 = rng.gen_range(30.0, 60.0);
|
||||
|
||||
let game_time = (state.get_time_of_day() as u64 % 86400) as u32;
|
||||
let current_period_of_day = Self::get_current_day_period(game_time);
|
||||
let current_biome = Self::get_current_biome(client);
|
||||
let current_site = Self::get_current_site(client);
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let maybe_track = self
|
||||
// Filters out tracks not matching the timing, site, and biome
|
||||
let maybe_tracks = self
|
||||
.soundtrack
|
||||
.tracks
|
||||
.iter()
|
||||
@ -167,10 +181,10 @@ impl MusicMgr {
|
||||
Some(period_of_day) => period_of_day == ¤t_period_of_day,
|
||||
None => true,
|
||||
}
|
||||
})
|
||||
.filter(|track| match &track.site {
|
||||
Some(site) => site == ¤t_site,
|
||||
None => true,
|
||||
&& match &track.site {
|
||||
Some(site) => site == ¤t_site,
|
||||
None => true,
|
||||
}
|
||||
})
|
||||
.filter(|track| {
|
||||
let mut result = false;
|
||||
@ -187,7 +201,9 @@ impl MusicMgr {
|
||||
})
|
||||
.collect::<Vec<&SoundtrackItem>>();
|
||||
|
||||
let new_maybe_track = maybe_track.choose_weighted(&mut rng, |track| {
|
||||
// Randomly selects a track from the remaining tracks weighted based
|
||||
// on the biome
|
||||
let new_maybe_track = maybe_tracks.choose_weighted(&mut rng, |track| {
|
||||
let mut chance = 0;
|
||||
if track.biomes.len() > 0 {
|
||||
for biome in track.biomes.iter() {
|
||||
@ -196,6 +212,9 @@ impl MusicMgr {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If no biome is listed, the song is still added to the
|
||||
// rotation to allow for site specific songs to play
|
||||
// in any biome
|
||||
chance = 1;
|
||||
}
|
||||
chance
|
||||
@ -228,17 +247,17 @@ impl MusicMgr {
|
||||
fn get_current_site(client: &Client) -> SitesKind {
|
||||
let mut player_alt = 0.0;
|
||||
if let Some(position) = client.current_position() {
|
||||
player_alt = position[2];
|
||||
player_alt = position.z;
|
||||
}
|
||||
let mut cave_alt = 0.0;
|
||||
let mut alt = 0.0;
|
||||
let mut terrain_alt = 0.0;
|
||||
if let Some(chunk) = client.current_chunk() {
|
||||
alt = chunk.meta().alt();
|
||||
terrain_alt = chunk.meta().alt();
|
||||
cave_alt = chunk.meta().cave_alt();
|
||||
}
|
||||
if player_alt < (alt - 20.0) && cave_alt != 0.0 {
|
||||
if player_alt < (terrain_alt - 20.0) && cave_alt != 0.0 {
|
||||
SitesKind::Cave
|
||||
} else if player_alt < (alt - 20.0) {
|
||||
} else if player_alt < (terrain_alt - 20.0) {
|
||||
SitesKind::Dungeon
|
||||
} else {
|
||||
SitesKind::None
|
||||
|
@ -1,5 +1,5 @@
|
||||
/// EventMapper::Block watches the sound emitting blocks in the same
|
||||
/// chunk as the player and emits ambient sfx
|
||||
/// EventMapper::Block watches the sound emitting blocks within
|
||||
/// chunk range of the player and emits ambient sfx
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::{terrain::BlocksOfInterest, Camera, Terrain},
|
||||
@ -54,6 +54,7 @@ impl EventMapper for BlockEventMapper {
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
|
||||
// Get the player position and chunk
|
||||
let player_pos = state
|
||||
.read_component_copied::<Pos>(player_entity)
|
||||
.unwrap_or_default();
|
||||
@ -137,7 +138,6 @@ impl EventMapper for BlockEventMapper {
|
||||
range: 1,
|
||||
sfx: SfxEvent::Bees,
|
||||
volume: 1.0,
|
||||
//cond: |_| true,
|
||||
cond: |st| st.get_day_period().is_light(),
|
||||
},
|
||||
];
|
||||
@ -147,8 +147,8 @@ impl EventMapper for BlockEventMapper {
|
||||
// If the timing condition is false, continue
|
||||
if !(sounds.cond)(state) {
|
||||
continue;
|
||||
// If the player is underground, continue
|
||||
} else if player_pos.0.z < (terrain_alt - 20.0) {
|
||||
// If the player is far enough underground, continue
|
||||
} else if player_pos.0.z < (terrain_alt - 30.0) {
|
||||
continue;
|
||||
// Hack to reduce the number of birdcalls (too many leaf blocks)
|
||||
} else if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
|
||||
@ -171,6 +171,7 @@ impl EventMapper for BlockEventMapper {
|
||||
|
||||
// Iterate through each individual block
|
||||
for block in blocks {
|
||||
// Hack to reduce the number of bird sounds (too many leaf blocks)
|
||||
if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
|
||||
&& thread_rng().gen_bool(0.999)
|
||||
{
|
||||
@ -189,9 +190,9 @@ impl EventMapper for BlockEventMapper {
|
||||
Some(block_pos),
|
||||
Some(sounds.volume),
|
||||
));
|
||||
state.time = Instant::now();
|
||||
state.event = sounds.sfx.clone();
|
||||
}
|
||||
state.time = Instant::now();
|
||||
state.event = sounds.sfx.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -213,7 +214,7 @@ impl BlockEventMapper {
|
||||
) -> bool {
|
||||
if let Some((event, item)) = sfx_trigger_item {
|
||||
if &previous_state.event == event {
|
||||
previous_state.time.elapsed().as_secs_f64() >= item.threshold
|
||||
previous_state.time.elapsed().as_secs_f32() >= item.threshold
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ impl CampfireEventMapper {
|
||||
) -> bool {
|
||||
if let Some((event, item)) = sfx_trigger_item {
|
||||
if &previous_state.event == event {
|
||||
previous_state.time.elapsed().as_secs_f64() >= item.threshold
|
||||
previous_state.time.elapsed().as_secs_f32() >= item.threshold
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ impl CombatEventMapper {
|
||||
) -> bool {
|
||||
if let Some((event, item)) = sfx_trigger_item {
|
||||
if &previous_state.event == event {
|
||||
previous_state.time.elapsed().as_secs_f64() >= item.threshold
|
||||
previous_state.time.elapsed().as_secs_f32() >= item.threshold
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ impl MovementEventMapper {
|
||||
) -> bool {
|
||||
if let Some((event, item)) = sfx_trigger_item {
|
||||
if &previous_state.event == event {
|
||||
previous_state.time.elapsed().as_secs_f64() >= item.threshold
|
||||
previous_state.time.elapsed().as_secs_f32() >= item.threshold
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
@ -116,8 +116,11 @@ use vek::*;
|
||||
const SFX_DIST_LIMIT_SQR: f32 = 20000.0;
|
||||
|
||||
pub struct SfxEventItem {
|
||||
/// The SFX event that triggers this sound
|
||||
pub sfx: SfxEvent,
|
||||
/// The position at which the sound should play
|
||||
pub pos: Option<Vec3<f32>>,
|
||||
/// The volume to play the sound at
|
||||
pub vol: Option<f32>,
|
||||
}
|
||||
|
||||
@ -179,6 +182,7 @@ pub enum SfxInventoryEvent {
|
||||
Swapped,
|
||||
}
|
||||
|
||||
// TODO Move to a separate event mapper?
|
||||
impl From<&InventoryUpdateEvent> for SfxEvent {
|
||||
fn from(value: &InventoryUpdateEvent) -> Self {
|
||||
match value {
|
||||
@ -209,8 +213,10 @@ impl From<&InventoryUpdateEvent> for SfxEvent {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SfxTriggerItem {
|
||||
/// A list of SFX filepaths for this event
|
||||
pub files: Vec<String>,
|
||||
pub threshold: f64,
|
||||
/// The time to wait before repeating this SfxEvent
|
||||
pub threshold: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
@ -247,13 +253,16 @@ impl SfxMgr {
|
||||
terrain: &Terrain<TerrainChunk>,
|
||||
client: &Client,
|
||||
) {
|
||||
// Checks if the SFX volume is set to zero or audio is disabled
|
||||
// This prevents us from running all the following code unnecessarily
|
||||
if !audio.sfx_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ecs = state.ecs();
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
|
||||
// This checks to see if the camera is underwater. If it is,
|
||||
// we pass all sfx through a low pass filter
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let underwater = state
|
||||
.terrain()
|
||||
.get((camera.dependents().cam_pos + focus_off).map(|e| e.floor() as i32))
|
||||
@ -262,6 +271,8 @@ impl SfxMgr {
|
||||
== BlockKind::Water;
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
|
||||
// Sets the listener position to the camera position facing the
|
||||
// same direction as the camera
|
||||
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
||||
|
||||
// TODO: replace; deprecated in favor of outcomes
|
||||
@ -294,6 +305,7 @@ impl SfxMgr {
|
||||
.last()
|
||||
.expect("Failed to determine sound file for this trigger item."),
|
||||
_ => {
|
||||
// If more than one file is listed, choose one at random
|
||||
let rand_step = rand::random::<usize>() % item.files.len();
|
||||
&item.files[rand_step]
|
||||
},
|
||||
|
@ -69,6 +69,8 @@ impl WindMgr {
|
||||
|
||||
let alt_multiplier = (player_alt / 1200.0).abs();
|
||||
|
||||
// Tree density factors into wind volume. The more trees,
|
||||
// the less wind
|
||||
let mut tree_multiplier = self.tree_multiplier;
|
||||
let new_tree_multiplier = if (player_alt - terrain_alt) < 150.0 {
|
||||
1.0 - Self::get_current_tree_density(client)
|
||||
@ -76,6 +78,7 @@ impl WindMgr {
|
||||
1.0
|
||||
};
|
||||
|
||||
// Smooths tree_multiplier transitions between chunks
|
||||
if tree_multiplier < new_tree_multiplier {
|
||||
tree_multiplier += 0.001;
|
||||
} else if tree_multiplier > new_tree_multiplier {
|
||||
@ -83,7 +86,6 @@ impl WindMgr {
|
||||
}
|
||||
self.tree_multiplier = tree_multiplier;
|
||||
|
||||
println!("tree multiplier: {}", tree_multiplier);
|
||||
let mut volume_multiplier = alt_multiplier * self.tree_multiplier;
|
||||
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
@ -108,9 +110,8 @@ impl WindMgr {
|
||||
}
|
||||
|
||||
let target_volume = volume_multiplier;
|
||||
println!("target vol: {}", volume_multiplier);
|
||||
|
||||
println!("current_wind_volume: {}", self.volume);
|
||||
// Transitions the wind smoothly
|
||||
self.volume = audio.get_wind_volume();
|
||||
if self.volume < target_volume {
|
||||
audio.set_wind_volume(self.volume + 0.001);
|
||||
|
@ -308,7 +308,7 @@ impl Scene {
|
||||
particle_mgr: ParticleMgr::new(renderer),
|
||||
figure_mgr: FigureMgr::new(renderer),
|
||||
sfx_mgr: SfxMgr::new(),
|
||||
music_mgr: MusicMgr::new(),
|
||||
music_mgr: MusicMgr::default(),
|
||||
ambient_mgr: WindMgr::new(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user