|
|
|
@ -56,7 +56,6 @@ fn main() {
|
|
|
|
load_audio_tones,
|
|
|
|
load_audio_tones,
|
|
|
|
load_materials,
|
|
|
|
load_materials,
|
|
|
|
load_meshes,
|
|
|
|
load_meshes,
|
|
|
|
spawn_audio_players.after(load_audio_tones),
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.configure_sets(
|
|
|
|
.configure_sets(
|
|
|
|
@ -111,7 +110,14 @@ fn main() {
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
control_audio_players.run_if(any_component_changed::<Cell>.or_else(state_changed::<SimulationPlayback>).or_else(state_changed::<AudioPlayback>)).in_set(GameSet::React),
|
|
|
|
control_audio_players
|
|
|
|
|
|
|
|
.run_if(
|
|
|
|
|
|
|
|
on_message::<Lifecycle>
|
|
|
|
|
|
|
|
.or_else(state_changed::<SimulationPlayback>)
|
|
|
|
|
|
|
|
.or_else(state_changed::<AudioPlayback>),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.in_set(GameSet::React),
|
|
|
|
|
|
|
|
manage_audio_players.run_if(resource_changed::<PitchMap>),
|
|
|
|
update_pitch_map_resource_ui.run_if(resource_changed::<PitchMap>),
|
|
|
|
update_pitch_map_resource_ui.run_if(resource_changed::<PitchMap>),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
@ -122,6 +128,7 @@ fn main() {
|
|
|
|
grid_gizmo,
|
|
|
|
grid_gizmo,
|
|
|
|
update_debug_info_coordinates,
|
|
|
|
update_debug_info_coordinates,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
assert_cell_uniqueness
|
|
|
|
assert_cell_uniqueness
|
|
|
|
.run_if(any_with_component::<Cell>)
|
|
|
|
.run_if(any_with_component::<Cell>)
|
|
|
|
.after(update_material),
|
|
|
|
.after(update_material),
|
|
|
|
@ -784,6 +791,7 @@ fn is_ui_hovered(hovered: Query<&Hovered>) -> bool {
|
|
|
|
hovered.iter().any(|Hovered(h)| *h)
|
|
|
|
hovered.iter().any(|Hovered(h)| *h)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
fn assert_cell_uniqueness(q: Query<(Entity, &Coordinates), With<Cell>>) {
|
|
|
|
fn assert_cell_uniqueness(q: Query<(Entity, &Coordinates), With<Cell>>) {
|
|
|
|
q.iter().for_each(|(e1, c1)| {
|
|
|
|
q.iter().for_each(|(e1, c1)| {
|
|
|
|
q.iter().for_each(|(e2, c2)| {
|
|
|
|
q.iter().for_each(|(e2, c2)| {
|
|
|
|
@ -1231,6 +1239,18 @@ impl PitchMap {
|
|
|
|
// always ensure the notes list is sorted
|
|
|
|
// always ensure the notes list is sorted
|
|
|
|
self.notes.sort();
|
|
|
|
self.notes.sort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Return an iterator over all enabled CellPitch values
|
|
|
|
|
|
|
|
fn iter_enabled_cell_pitches(&self) -> impl Iterator<Item = CellPitch> {
|
|
|
|
|
|
|
|
self.notes.iter().flat_map(|n| {
|
|
|
|
|
|
|
|
(0..=self.octaves)
|
|
|
|
|
|
|
|
.map(|o| o as isize)
|
|
|
|
|
|
|
|
.map(|o| o - (o / 2))
|
|
|
|
|
|
|
|
.map(|o| {
|
|
|
|
|
|
|
|
CellPitch { note: n.clone(), octave: o }
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
@ -1383,15 +1403,35 @@ fn update_camera_position(mut query: Query<(&Coordinates, &mut Transform), With<
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Ordering: first, once, after(load_audio_tones)
|
|
|
|
// Run when PitchMap changes to ensure we have just the audio sinks we need
|
|
|
|
fn spawn_audio_players(mut commands: Commands, cell_assets: Res<CellAssets>) {
|
|
|
|
fn manage_audio_players(
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
cell_assets: Res<CellAssets>,
|
|
|
|
|
|
|
|
pitch_map: Res<PitchMap>,
|
|
|
|
|
|
|
|
current: Query<(Entity, &CellPitch), With<AudioSink>>,
|
|
|
|
|
|
|
|
mut active: Local<HashSet<CellPitch>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
*active = pitch_map.iter_enabled_cell_pitches().collect();
|
|
|
|
CellPitch::iter_all().for_each(|cp| {
|
|
|
|
CellPitch::iter_all().for_each(|cp| {
|
|
|
|
let h = cell_assets.pitches.get(&cp).unwrap().clone();
|
|
|
|
// The cell is active, ensure it has an audio entity
|
|
|
|
commands.spawn((AudioPlayer(h), cp, PlaybackSettings::default().muted()));
|
|
|
|
if active.contains(&cp) {
|
|
|
|
});
|
|
|
|
if !current.iter().any(|(_this_e, this_cp)| *this_cp == cp) {
|
|
|
|
|
|
|
|
let h = cell_assets.pitches.get(&cp).unwrap().clone();
|
|
|
|
|
|
|
|
commands.spawn((AudioPlayer(h), cp, PlaybackSettings::default().muted()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// The cell is inactive, ensure it does not have an audio entity
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
if let Some(e) = current
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.find_map(|(this_e, this_cp)| (*this_cp == cp).then_some(this_e))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
commands.entity(e).despawn();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Ordering: React
|
|
|
|
|
|
|
|
fn control_audio_players(
|
|
|
|
fn control_audio_players(
|
|
|
|
audio_state: Res<State<AudioPlayback>>,
|
|
|
|
audio_state: Res<State<AudioPlayback>>,
|
|
|
|
simulation_state: Res<State<SimulationPlayback>>,
|
|
|
|
simulation_state: Res<State<SimulationPlayback>>,
|
|
|
|
|