Compare commits
	
		
			No commits in common. '3b75fed684c70880285632d7ad48365a708b249f' and '27522e2209efb2619b69f8b158d3b7d6e020ef58' have entirely different histories. 
		
	
	
		
			3b75fed684
			...
			27522e2209
		
	
		
	
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -1,155 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct AnimationWidget; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct AnimationPlayAll; |  | ||||||
| 
 |  | ||||||
| pub fn init_animations_ui(events: Query<Entity, Added<AnimationWidget>>, mut commands: Commands) { |  | ||||||
|     events.iter().for_each(|entity| { |  | ||||||
|         commands.entity(entity).with_children(|parent| { |  | ||||||
|             parent.spawn(( |  | ||||||
|                 AnimationPlayAll, |  | ||||||
|                 ButtonBundle { |  | ||||||
|                     style: Style { |  | ||||||
|                         border: UiRect::all(Val::Px(1.0)), |  | ||||||
|                         margin: UiRect::all(Val::Px(1.0)), |  | ||||||
|                         padding: UiRect::all(Val::Px(1.0)), |  | ||||||
|                         ..default() |  | ||||||
|                     }, |  | ||||||
|                     border_color: Color::BLACK.into(), |  | ||||||
|                     ..default() |  | ||||||
|                 }, |  | ||||||
|                 ui::Title { |  | ||||||
|                     text: "Play All".into(), |  | ||||||
|                     ..default() |  | ||||||
|                 }, |  | ||||||
|             )); |  | ||||||
|         }); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// When a new scene is loaded, add any newly compatible animations
 |  | ||||||
| /// TODO: Add target entity(s) too
 |  | ||||||
| pub fn add_animations_ui( |  | ||||||
|     player_spawned: Query<&Name, Added<AnimationPlayer>>, |  | ||||||
|     widget: Query<Entity, With<AnimationWidget>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     gltfs: Res<Assets<Gltf>>, |  | ||||||
|     clips: Res<Assets<AnimationClip>>, |  | ||||||
| ) { |  | ||||||
|     player_spawned.iter().for_each(|player_name| { |  | ||||||
|         gltfs |  | ||||||
|             .iter() |  | ||||||
|             .flat_map(|(_, gltf)| gltf.named_animations.iter()) |  | ||||||
|             .filter_map(|(clip_name, handle)| { |  | ||||||
|                 clips.get(&handle).map(|clip| (clip_name, handle, clip)) |  | ||||||
|             }) |  | ||||||
|             .filter(|(_, _, clip)| clip.compatible_with(player_name)) |  | ||||||
|             .for_each(|(clip_name, handle, _)| { |  | ||||||
|                 create_asset_button( |  | ||||||
|                     &widget, |  | ||||||
|                     &mut commands, |  | ||||||
|                     ui::TargetAsset { |  | ||||||
|                         handle: handle.clone(), |  | ||||||
|                     }, |  | ||||||
|                     clip_name.clone(), |  | ||||||
|                     None, |  | ||||||
|                 ); |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // When a scene is de-selected, remove any outdated animation options
 |  | ||||||
| pub fn remove_animations_ui( |  | ||||||
|     mut removed_players: RemovedComponents<Handle<Scene>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<AnimationClip>)>, |  | ||||||
|     clips: Res<Assets<AnimationClip>>, |  | ||||||
|     targets: Query<(&AnimationPlayer, &Name)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // For each removed scene
 |  | ||||||
|     removed_players.iter().for_each(|_| { |  | ||||||
|         // Iterate over the current animation buttons
 |  | ||||||
|         current |  | ||||||
|             .iter() |  | ||||||
|             .filter(|(_, ui::TargetAsset { handle })| { |  | ||||||
|                 // Check if this clip is compatible with any remaining entities
 |  | ||||||
|                 // NOTE: We are checking this is *not* compatible with any entities
 |  | ||||||
|                 clips |  | ||||||
|                     .get(handle) |  | ||||||
|                     .map(|clip| !(targets.iter().any(|(_, name)| clip.compatible_with(name)))) |  | ||||||
|                     .unwrap_or(true) |  | ||||||
|             }) |  | ||||||
|             .for_each(|(_, ui::TargetAsset { handle })| { |  | ||||||
|                 // Destroy the buton if it is so
 |  | ||||||
|                 destroy_asset_button( |  | ||||||
|                     ¤t, |  | ||||||
|                     &mut commands, |  | ||||||
|                     &ui::TargetAsset { |  | ||||||
|                         handle: handle.clone(), |  | ||||||
|                     }, |  | ||||||
|                 ); |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn play_all_animations( |  | ||||||
|     start: Query<Entity, (With<Button>, Added<ui::Active>)>, |  | ||||||
|     mut stop: RemovedComponents<ui::Active>, |  | ||||||
|     play_all_btn: Query<Entity, With<AnimationPlayAll>>, |  | ||||||
|     clip_btns: Query<Entity, With<ui::TargetAsset<AnimationClip>>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     stop.iter() |  | ||||||
|         .filter(|&entity| play_all_btn.contains(entity)) |  | ||||||
|         .for_each(|_| { |  | ||||||
|             clip_btns.iter().for_each(|entity| { |  | ||||||
|                 commands.entity(entity).remove::<ui::Active>(); |  | ||||||
|             }) |  | ||||||
|         }); |  | ||||||
|     start |  | ||||||
|         .iter() |  | ||||||
|         .filter(|&entity| play_all_btn.contains(entity)) |  | ||||||
|         .for_each(|_| { |  | ||||||
|             clip_btns.iter().for_each(|entity| { |  | ||||||
|                 commands.entity(entity).insert(ui::Active); |  | ||||||
|             }) |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn play_animation( |  | ||||||
|     start: Query<Entity, (With<Button>, Added<ui::Active>)>, |  | ||||||
|     mut stop: RemovedComponents<ui::Active>, |  | ||||||
|     clip_refs: Query<&ui::TargetAsset<AnimationClip>>, |  | ||||||
|     mut targets: Query<(&mut AnimationPlayer, &Name), With<Transform>>, |  | ||||||
|     clips: Res<Assets<AnimationClip>>, |  | ||||||
| ) { |  | ||||||
|     stop.iter().for_each(|entity| { |  | ||||||
|         if let Ok(ui::TargetAsset { handle }) = clip_refs.get(entity) { |  | ||||||
|             let clip = clips.get(&handle).expect("Load animation clip"); |  | ||||||
|             targets |  | ||||||
|                 .iter_mut() |  | ||||||
|                 .filter(|(_, name)| clip.compatible_with(name)) |  | ||||||
|                 .for_each(|(mut player, _)| { |  | ||||||
|                     player.pause(); |  | ||||||
|                 }) |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|     start.iter().for_each(|entity| { |  | ||||||
|         if let Ok(ui::TargetAsset { handle }) = clip_refs.get(entity) { |  | ||||||
|             let clip = clips.get(&handle).expect("Load animation clip"); |  | ||||||
|             targets |  | ||||||
|                 .iter_mut() |  | ||||||
|                 .filter(|(_, name)| clip.compatible_with(name)) |  | ||||||
|                 .for_each(|(mut player, _)| { |  | ||||||
|                     if player.is_paused() { |  | ||||||
|                         player.resume(); |  | ||||||
|                     } else { |  | ||||||
|                         player.play(handle.clone()).repeat(); |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @ -1,55 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| // This sets buttons to active when their associated handle is spawned
 |  | ||||||
| pub fn sync_asset_buttons<T: Asset>( |  | ||||||
|     events: Query<&Handle<T>, Added<Handle<T>>>, |  | ||||||
|     buttons: Query<(Entity, &ui::TargetAsset<T>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|this_handle| { |  | ||||||
|         info!("Syncing {:?}", this_handle); |  | ||||||
|         buttons |  | ||||||
|             .iter() |  | ||||||
|             .find_map(|(entity, ui::TargetAsset { handle })| { |  | ||||||
|                 if handle == this_handle { |  | ||||||
|                     Some(entity) |  | ||||||
|                 } else { |  | ||||||
|                     None |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
|             .iter() |  | ||||||
|             .for_each(|&entity| { |  | ||||||
|                 commands.entity(entity).insert(ui::Active); |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Remove active when handle is despawned?
 |  | ||||||
| // ONLY IF there are no instances of that handle [!any(*)]
 |  | ||||||
| pub fn sync_remove_asset_buttons<T: Asset>( |  | ||||||
|     mut events: RemovedComponents<Handle<T>>, |  | ||||||
|     asset_entities: Query<&Handle<T>>, |  | ||||||
|     buttons: Query<(Entity, &ui::TargetAsset<T>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events |  | ||||||
|         .iter() |  | ||||||
|         .find_map(|this_asset_entity| asset_entities.get(this_asset_entity).ok()) |  | ||||||
|         .iter() |  | ||||||
|         .for_each(|this_handle| { |  | ||||||
|             info!("Syncing removal of {:?}", this_handle); |  | ||||||
|             buttons |  | ||||||
|                 .iter() |  | ||||||
|                 .find_map(|(entity, ui::TargetAsset { handle })| { |  | ||||||
|                     if handle == *this_handle { |  | ||||||
|                         Some(entity) |  | ||||||
|                     } else { |  | ||||||
|                         None |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|                 .iter() |  | ||||||
|                 .for_each(|&entity| { |  | ||||||
|                     commands.entity(entity).remove::<ui::Active>(); |  | ||||||
|                 }); |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| @ -1,120 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Resource, Default)] |  | ||||||
| pub struct AssetRegistry(pub Vec<HandleUntyped>); |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct ReloadAssets; |  | ||||||
| 
 |  | ||||||
| pub fn reload_assets( |  | ||||||
|     server: Res<AssetServer>, |  | ||||||
|     mut registry: ResMut<AssetRegistry>, |  | ||||||
|     mut writer: EventWriter<ui::Alert>, |  | ||||||
| ) { |  | ||||||
|     match server.load_folder(".") { |  | ||||||
|         Ok(handles) => registry.0 = handles, |  | ||||||
|         Err(e) => writer.send(ui::Alert::Warn(format!( |  | ||||||
|             "Could not find `assets` folder!\n{:?}", |  | ||||||
|             e |  | ||||||
|         ))), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn get_asset_name<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String { |  | ||||||
|     if let Some(asset_path) = server.get_handle_path(handle.clone()) { |  | ||||||
|         if let Some(stem) = asset_path.path().file_stem() { |  | ||||||
|             if let Some(val) = stem.to_str() { |  | ||||||
|                 String::from(val) |  | ||||||
|             } else { |  | ||||||
|                 String::from("???") |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             String::from("???") |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         String::from("???") |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn create_asset_button<A: Asset, C: Component>( |  | ||||||
|     root: &Query<Entity, With<C>>, |  | ||||||
|     commands: &mut Commands, |  | ||||||
|     target: ui::TargetAsset<A>, |  | ||||||
|     name: String, |  | ||||||
|     font: Option<Handle<Font>>, |  | ||||||
| ) -> Entity { |  | ||||||
|     commands |  | ||||||
|         .spawn(( |  | ||||||
|             target, |  | ||||||
|             ButtonBundle { |  | ||||||
|                 style: Style { |  | ||||||
|                     border: UiRect::all(Val::Px(1.0)), |  | ||||||
|                     margin: UiRect::all(Val::Px(1.0)), |  | ||||||
|                     padding: UiRect::all(Val::Px(1.0)), |  | ||||||
|                     ..default() |  | ||||||
|                 }, |  | ||||||
|                 border_color: Color::BLACK.into(), |  | ||||||
|                 ..default() |  | ||||||
|             }, |  | ||||||
|             ui::Title { |  | ||||||
|                 text: name, |  | ||||||
|                 font: font.clone(), |  | ||||||
|             }, |  | ||||||
|         )) |  | ||||||
|         .set_parent(root.single()) |  | ||||||
|         .id() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn create_entity_button<C: Component>( |  | ||||||
|     root: &Query<Entity, With<C>>, |  | ||||||
|     commands: &mut Commands, |  | ||||||
|     target: ui::TargetEntity, |  | ||||||
|     name: String, |  | ||||||
| ) -> Entity { |  | ||||||
|     commands |  | ||||||
|         .spawn(( |  | ||||||
|             target, |  | ||||||
|             ButtonBundle { |  | ||||||
|                 style: Style { |  | ||||||
|                     border: UiRect::all(Val::Px(1.0)), |  | ||||||
|                     margin: UiRect::all(Val::Px(1.0)), |  | ||||||
|                     padding: UiRect::all(Val::Px(1.0)), |  | ||||||
|                     ..default() |  | ||||||
|                 }, |  | ||||||
|                 border_color: Color::BLACK.into(), |  | ||||||
|                 ..default() |  | ||||||
|             }, |  | ||||||
|             ui::Title { |  | ||||||
|                 text: name, |  | ||||||
|                 ..default() |  | ||||||
|             }, |  | ||||||
|         )) |  | ||||||
|         .set_parent(root.single()) |  | ||||||
|         .id() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn destroy_asset_button<A: Asset>( |  | ||||||
|     current: &Query<(Entity, &ui::TargetAsset<A>)>, |  | ||||||
|     commands: &mut Commands, |  | ||||||
|     target: &ui::TargetAsset<A>, |  | ||||||
| ) { |  | ||||||
|     if let Some(entity) = current |  | ||||||
|         .iter() |  | ||||||
|         .find_map(|(entity, this)| (this.handle == target.handle).then_some(entity)) |  | ||||||
|     { |  | ||||||
|         commands.entity(entity).despawn_recursive(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn destroy_entity_button( |  | ||||||
|     current: &Query<(Entity, &ui::TargetEntity)>, |  | ||||||
|     commands: &mut Commands, |  | ||||||
|     target: &ui::TargetEntity, |  | ||||||
| ) { |  | ||||||
|     if let Some(entity) = current |  | ||||||
|         .iter() |  | ||||||
|         .find_map(|(entity, this)| (this.entity == target.entity).then_some(entity)) |  | ||||||
|     { |  | ||||||
|         commands.entity(entity).despawn_recursive(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,138 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| use bevy::audio::PlaybackMode; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Reflect, Default)] |  | ||||||
| #[reflect(Component)] |  | ||||||
| pub struct AudioRoot; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct AudioWidget; |  | ||||||
| 
 |  | ||||||
| pub fn audio_ui( |  | ||||||
|     mut events: EventReader<AssetEvent<AudioSource>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     widget: Query<Entity, With<AudioWidget>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<AudioSource>)>, |  | ||||||
|     server: Res<AssetServer>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         AssetEvent::Created { handle } => { |  | ||||||
|             info!("Asset created! {:?}", event); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Removed { handle } => { |  | ||||||
|             info!("Asset removed! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Modified { handle } => { |  | ||||||
|             info!("Asset modified! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Event)] |  | ||||||
| pub enum ControlAudio { |  | ||||||
|     Loop(Handle<AudioSource>), |  | ||||||
|     Stop(Handle<AudioSource>), |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn control_audio( |  | ||||||
|     mut events: EventReader<ControlAudio>, |  | ||||||
|     root: Query<Entity, With<AudioRoot>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     sources: Query<(Entity, &Handle<AudioSource>), With<AudioSink>>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         ControlAudio::Loop(handle) => { |  | ||||||
|             info!("Looping audio {:?}", handle); |  | ||||||
|             let root = if let Ok(entity) = root.get_single() { |  | ||||||
|                 entity |  | ||||||
|             } else { |  | ||||||
|                 commands.spawn(AudioRoot).id() |  | ||||||
|             }; |  | ||||||
|             commands.entity(root).with_children(|parent| { |  | ||||||
|                 parent.spawn(AudioSourceBundle { |  | ||||||
|                     source: handle.clone(), |  | ||||||
|                     settings: PlaybackSettings { |  | ||||||
|                         mode: PlaybackMode::Loop, |  | ||||||
|                         paused: false, |  | ||||||
|                         ..default() |  | ||||||
|                     }, |  | ||||||
|                 }); |  | ||||||
|                 info!("Done spawning"); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|         ControlAudio::Stop(handle) => { |  | ||||||
|             info!("Stopping audio {:?}", handle); |  | ||||||
|             sources |  | ||||||
|                 .iter() |  | ||||||
|                 .find_map(|(entity, source_handle)| { |  | ||||||
|                     if source_handle == handle { |  | ||||||
|                         Some(entity) |  | ||||||
|                     } else { |  | ||||||
|                         None |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|                 .iter() |  | ||||||
|                 .for_each(|&entity| { |  | ||||||
|                     commands.entity(entity).despawn_recursive(); |  | ||||||
|                     info!("Done despawning"); |  | ||||||
|                 }); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn ui_control_audio( |  | ||||||
|     events: Query< |  | ||||||
|         ( |  | ||||||
|             &Interaction, |  | ||||||
|             &ui::TargetAsset<AudioSource>, |  | ||||||
|             Option<&ui::Active>, |  | ||||||
|         ), |  | ||||||
|         (With<Button>, Changed<Interaction>), |  | ||||||
|     >, |  | ||||||
|     mut writer: EventWriter<ControlAudio>, |  | ||||||
| ) { |  | ||||||
|     events |  | ||||||
|         .iter() |  | ||||||
|         .filter_map( |  | ||||||
|             |(interaction, ui::TargetAsset { handle }, active)| match interaction { |  | ||||||
|                 Interaction::Pressed => Some((handle, active)), |  | ||||||
|                 _ => None, |  | ||||||
|             }, |  | ||||||
|         ) |  | ||||||
|         .for_each(|(handle, active)| match active { |  | ||||||
|             Some(_) => writer.send(ControlAudio::Stop(handle.clone())), |  | ||||||
|             None => writer.send(ControlAudio::Loop(handle.clone())), |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| @ -1,66 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct EditorCamera; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct CameraWidget; |  | ||||||
| 
 |  | ||||||
| pub fn cameras_ui( |  | ||||||
|     mut added: Query<(Entity, &mut Camera, &Name), Added<Camera>>, |  | ||||||
|     mut removed: RemovedComponents<Camera>, |  | ||||||
|     editor_camera: Query<Entity, With<EditorCamera>>, |  | ||||||
|     widget: Query<Entity, With<CameraWidget>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetEntity)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     removed.iter().for_each(|entity| { |  | ||||||
|         info!("Destroy button for {:?}", entity); |  | ||||||
|         destroy_entity_button(¤t, &mut commands, &ui::TargetEntity { entity }); |  | ||||||
|     }); |  | ||||||
|     added.iter_mut().for_each(|(entity, mut camera, name)| { |  | ||||||
|         info!("Camera added {:?} {:?}", entity, name); |  | ||||||
|         create_entity_button( |  | ||||||
|             &widget, |  | ||||||
|             &mut commands, |  | ||||||
|             ui::TargetEntity { entity }, |  | ||||||
|             name.as_str().into(), |  | ||||||
|         ); |  | ||||||
|         camera.is_active = entity == editor_camera.single(); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Set the camera active component based on button clicks
 |  | ||||||
| pub fn manage_active_camera( |  | ||||||
|     events: Query<&ui::TargetEntity, Added<ui::Active>>, |  | ||||||
|     mut cameras: Query<(Entity, &mut Camera)>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|ui::TargetEntity { entity }| { |  | ||||||
|         cameras.iter_mut().for_each(|(this_entity, mut camera)| { |  | ||||||
|             if this_entity == *entity { |  | ||||||
|                 info!("Marking {:?} as active camera", entity); |  | ||||||
|                 camera.is_active = true; |  | ||||||
|             } else { |  | ||||||
|                 info!("Marking {:?} as inactive camera", entity); |  | ||||||
|                 camera.is_active = false; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // In the event that an active camera is despawned, fall back to the editor camera
 |  | ||||||
| pub fn fallback_camera( |  | ||||||
|     modified: Query<Entity, (Changed<Camera>, Without<EditorCamera>)>, |  | ||||||
|     mut removed: RemovedComponents<Camera>, |  | ||||||
|     other_cameras: Query<&Camera, Without<EditorCamera>>, |  | ||||||
|     mut editor_camera: Query<&mut Camera, With<EditorCamera>>, |  | ||||||
| ) { |  | ||||||
|     // Any time a camera is modified
 |  | ||||||
|     modified.iter().chain(removed.iter()).for_each(|_| { |  | ||||||
|         // If no other cameras are active
 |  | ||||||
|         if !other_cameras.iter().any(|camera| camera.is_active) { |  | ||||||
|             // Make the editor camera active
 |  | ||||||
|             editor_camera.single_mut().is_active = true; |  | ||||||
|         } |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| @ -1,109 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct FontWidget; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Resource, Default)] |  | ||||||
| pub struct FontInfo { |  | ||||||
|     pub default: Option<Handle<Font>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn fonts_ui( |  | ||||||
|     mut events: EventReader<AssetEvent<Font>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     widget: Query<Entity, With<FontWidget>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<Font>)>, |  | ||||||
|     server: Res<AssetServer>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         AssetEvent::Created { handle } => { |  | ||||||
|             info!("Asset created! {:?}", event); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 Some(handle.clone()), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Removed { handle } => { |  | ||||||
|             info!("Asset removed! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Modified { handle } => { |  | ||||||
|             info!("Asset modified! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 Some(handle.clone()), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn ui_control_font( |  | ||||||
|     events: Query< |  | ||||||
|         (&Interaction, &ui::TargetAsset<Font>, Option<&ui::Active>), |  | ||||||
|         (With<Button>, Changed<Interaction>), |  | ||||||
|     >, |  | ||||||
|     mut font: ResMut<FontInfo>, |  | ||||||
| ) { |  | ||||||
|     events |  | ||||||
|         .iter() |  | ||||||
|         .filter_map( |  | ||||||
|             |(interaction, ui::TargetAsset { handle }, active)| match interaction { |  | ||||||
|                 Interaction::Pressed => Some((handle, active)), |  | ||||||
|                 _ => None, |  | ||||||
|             }, |  | ||||||
|         ) |  | ||||||
|         .for_each(|(handle, active)| match active { |  | ||||||
|             Some(_) => font.default = None, |  | ||||||
|             None => font.default = Some(handle.clone()), |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn sync_font( |  | ||||||
|     query: Query<(Entity, &ui::TargetAsset<Font>)>, |  | ||||||
|     font: Res<FontInfo>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     if font.is_changed() || font.is_added() { |  | ||||||
|         match &font.default { |  | ||||||
|             Some(current_handle) => { |  | ||||||
|                 query |  | ||||||
|                     .iter() |  | ||||||
|                     .for_each(|(entity, ui::TargetAsset { handle })| { |  | ||||||
|                         if *handle == *current_handle { |  | ||||||
|                             commands.entity(entity).insert(ui::Active); |  | ||||||
|                         } else { |  | ||||||
|                             commands.entity(entity).remove::<ui::Active>(); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|             } |  | ||||||
|             None => { |  | ||||||
|                 query.iter().for_each(|(entity, _)| { |  | ||||||
|                     commands.entity(entity).remove::<ui::Active>(); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,68 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct GltfWidget; |  | ||||||
| 
 |  | ||||||
| pub fn gltf_ui( |  | ||||||
|     mut events: EventReader<AssetEvent<Gltf>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     widget: Query<Entity, With<GltfWidget>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<Gltf>)>, |  | ||||||
|     server: Res<AssetServer>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         AssetEvent::Created { handle } => { |  | ||||||
|             info!("Asset created! {:?}", event); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Removed { handle } => { |  | ||||||
|             info!("Asset removed! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Modified { handle } => { |  | ||||||
|             info!("Asset modified! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn control_active_gltf( |  | ||||||
|     events: Query<Entity, (With<ui::TargetAsset<Gltf>>, Added<ui::Active>)>, |  | ||||||
|     root: Query<Entity, With<LevelRoot>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|_| { |  | ||||||
|         root.iter().for_each(|entity| { |  | ||||||
|             commands.entity(entity).despawn_descendants(); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @ -1,167 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| use bevy::tasks::IoTaskPool; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Reflect, Default)] |  | ||||||
| #[reflect(Component)] |  | ||||||
| pub struct LevelRoot; |  | ||||||
| 
 |  | ||||||
| pub type Level = DynamicScene; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct LevelWidget; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct ExportLevel; |  | ||||||
| 
 |  | ||||||
| pub fn level_ui( |  | ||||||
|     mut events: EventReader<AssetEvent<Level>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     widget: Query<Entity, With<LevelWidget>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<Level>)>, |  | ||||||
|     server: Res<AssetServer>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         AssetEvent::Created { handle } => { |  | ||||||
|             info!("Asset created! {:?}", event); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Removed { handle } => { |  | ||||||
|             info!("Asset removed! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Modified { handle } => { |  | ||||||
|             info!("Asset modified! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_level( |  | ||||||
|     events: Query< |  | ||||||
|         &ui::TargetAsset<DynamicScene>, |  | ||||||
|         (Added<ui::Active>, With<ui::TargetAsset<DynamicScene>>), |  | ||||||
|     >, |  | ||||||
|     root: Query<Entity, With<LevelRoot>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         root.iter().for_each(|entity| { |  | ||||||
|             commands.entity(entity).despawn_recursive(); |  | ||||||
|         }); |  | ||||||
|         commands.spawn(DynamicSceneBundle { |  | ||||||
|             scene: handle.clone(), |  | ||||||
|             ..default() |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn export_level( |  | ||||||
|     level_root: Query<Entity, With<LevelRoot>>, |  | ||||||
|     audio_root: Query<Entity, With<AudioRoot>>, |  | ||||||
|     children: Query<&Children>, |  | ||||||
|     world: &World, |  | ||||||
| ) { |  | ||||||
|     let app_type_registry = world.resource::<AppTypeRegistry>().clone(); |  | ||||||
|     let mut builder = DynamicSceneBuilder::from_world(world.clone()); |  | ||||||
| 
 |  | ||||||
|     builder.deny_all_resources(); |  | ||||||
| 
 |  | ||||||
|     // Exclude computed visibility
 |  | ||||||
|     builder.deny_all(); |  | ||||||
| 
 |  | ||||||
|     // Level administrivia
 |  | ||||||
|     builder.allow::<LevelRoot>(); |  | ||||||
|     builder.allow::<AudioRoot>(); |  | ||||||
| 
 |  | ||||||
|     // TODO: Serialize Timeline
 |  | ||||||
| 
 |  | ||||||
|     // Scene components
 |  | ||||||
|     builder.allow::<Handle<Scene>>(); |  | ||||||
| 
 |  | ||||||
|     // Spatial components
 |  | ||||||
|     builder.allow::<Transform>(); |  | ||||||
|     builder.allow::<GlobalTransform>(); |  | ||||||
|     builder.allow::<Visibility>(); |  | ||||||
| 
 |  | ||||||
|     // Audio components
 |  | ||||||
|     builder.allow::<Handle<AudioSource>>(); |  | ||||||
|     builder.allow::<PlaybackSettings>(); |  | ||||||
| 
 |  | ||||||
|     // Text components
 |  | ||||||
|     builder.allow::<Handle<Font>>(); |  | ||||||
|     builder.allow::<Handle<Monologue>>(); |  | ||||||
| 
 |  | ||||||
|     level_root.iter().for_each(|level| { |  | ||||||
|         // Extract the level root
 |  | ||||||
|         builder.extract_entity(level); |  | ||||||
| 
 |  | ||||||
|         if let Ok(kids) = children.get(level) { |  | ||||||
|             builder.extract_entities(kids.into_iter().map(|&e| e)); |  | ||||||
|         } else { |  | ||||||
|             warn!("Level is empty!"); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     audio_root.iter().for_each(|audio| { |  | ||||||
|         // Extract the level root
 |  | ||||||
|         builder.extract_entity(audio); |  | ||||||
| 
 |  | ||||||
|         if let Ok(kids) = children.get(audio) { |  | ||||||
|             builder.extract_entities(kids.into_iter().map(|&e| e)); |  | ||||||
|         } else { |  | ||||||
|             warn!("Audio is empty!"); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     let scene = builder.build(); |  | ||||||
| 
 |  | ||||||
|     let serialized = scene |  | ||||||
|         .serialize_ron(&app_type_registry) |  | ||||||
|         .expect("Serialize scene"); |  | ||||||
| 
 |  | ||||||
|     IoTaskPool::get() |  | ||||||
|         .spawn(async move { |  | ||||||
|             // Write the scene RON data to file
 |  | ||||||
|             std::fs::write(format!("assets/output.scn.ron"), serialized.as_bytes()) |  | ||||||
|                 .expect("Error while writing scene to file"); |  | ||||||
|         }) |  | ||||||
|         .detach(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn rehydrate_level<W: Component, WO: Component + Default>( |  | ||||||
|     events: Query<Entity, (Added<W>, Without<WO>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|entity| { |  | ||||||
|         commands.entity(entity).insert(WO::default()); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| pub fn spot_light_force_shadows(mut spot_lights: Query<&mut SpotLight, Added<SpotLight>>) { |  | ||||||
|     spot_lights.iter_mut().for_each(|mut light| { |  | ||||||
|         light.shadows_enabled = true; |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn directional_light_force_shadows( |  | ||||||
|     mut directional_lights: Query<&mut DirectionalLight, Added<DirectionalLight>>, |  | ||||||
| ) { |  | ||||||
|     directional_lights.iter_mut().for_each(|mut light| { |  | ||||||
|         light.shadows_enabled = true; |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn point_light_force_shadows(mut point_lights: Query<&mut PointLight, Added<PointLight>>) { |  | ||||||
|     point_lights.iter_mut().for_each(|mut light| { |  | ||||||
|         light.shadows_enabled = true; |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| @ -1,59 +0,0 @@ | |||||||
| pub mod animation; |  | ||||||
| pub mod asset_sync; |  | ||||||
| pub mod assets; |  | ||||||
| pub mod audio; |  | ||||||
| pub mod camera; |  | ||||||
| pub mod font; |  | ||||||
| pub mod gltf; |  | ||||||
| pub mod level; |  | ||||||
| pub mod lighting; |  | ||||||
| pub mod monologue; |  | ||||||
| mod prelude; |  | ||||||
| pub mod quit; |  | ||||||
| pub mod reset; |  | ||||||
| pub mod scene; |  | ||||||
| pub mod timeline; |  | ||||||
| 
 |  | ||||||
| use crate::ui; |  | ||||||
| use bevy::{asset::Asset, prelude::*}; |  | ||||||
| 
 |  | ||||||
| pub fn ui_active<T: Asset>( |  | ||||||
|     events: Query<&Handle<T>, Added<Handle<T>>>, |  | ||||||
|     buttons: Query<(Entity, &ui::TargetAsset<T>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|this_handle| { |  | ||||||
|         buttons |  | ||||||
|             .iter() |  | ||||||
|             .find_map(|(entity, ui::TargetAsset { handle })| { |  | ||||||
|                 if handle == this_handle { |  | ||||||
|                     Some(entity) |  | ||||||
|                 } else { |  | ||||||
|                     None |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
|             .and_then(|entity| { |  | ||||||
|                 commands.entity(entity).insert(ui::Active); |  | ||||||
|                 Some(()) |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn ui_inactive<T: Asset>( |  | ||||||
|     mut events: RemovedComponents<Handle<T>>, |  | ||||||
|     targets: Query<(Entity, &ui::TargetAsset<T>), With<ui::Active>>, |  | ||||||
|     sources: Query<&Handle<T>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|_| { |  | ||||||
|         targets |  | ||||||
|             .iter() |  | ||||||
|             .find_map(|(entity, ui::TargetAsset { handle })| { |  | ||||||
|                 (!sources.iter().any(|this_handle| this_handle == handle)).then_some(entity) |  | ||||||
|             }) |  | ||||||
|             .and_then(|entity| { |  | ||||||
|                 commands.entity(entity).remove::<ui::Active>(); |  | ||||||
|                 Some(()) |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @ -1,229 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| use bevy::{ |  | ||||||
|     asset::{AssetLoader, LoadContext, LoadedAsset}, |  | ||||||
|     reflect::{TypePath, TypeUuid}, |  | ||||||
|     ui::FocusPolicy, |  | ||||||
|     utils::BoxedFuture, |  | ||||||
| }; |  | ||||||
| use serde::Deserialize; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct MonologueWidget; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Deserialize, TypeUuid, TypePath, PartialEq)] |  | ||||||
| #[uuid = "216a570b-d142-4026-baed-d7feb0250458"] |  | ||||||
| pub struct Monologue { |  | ||||||
|     text: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Event)] |  | ||||||
| pub enum ControlMonologue { |  | ||||||
|     Show(Handle<Monologue>), |  | ||||||
|     Hide, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct MonologueModal; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct MonologueContainer; |  | ||||||
| 
 |  | ||||||
| #[derive(Default)] |  | ||||||
| pub struct MonologueLoader; |  | ||||||
| 
 |  | ||||||
| impl AssetLoader for MonologueLoader { |  | ||||||
|     fn load<'a>( |  | ||||||
|         &'a self, |  | ||||||
|         bytes: &'a [u8], |  | ||||||
|         load_context: &'a mut LoadContext, |  | ||||||
|     ) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> { |  | ||||||
|         Box::pin(async move { |  | ||||||
|             let asset = Monologue { |  | ||||||
|                 text: String::from_utf8(bytes.to_vec())?, |  | ||||||
|             }; |  | ||||||
|             load_context.set_default_asset(LoadedAsset::new(asset)); |  | ||||||
|             Ok(()) |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn extensions(&self) -> &[&str] { |  | ||||||
|         &["monologue.txt"] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn init_texts_ui(mut commands: Commands) { |  | ||||||
|     commands.spawn(( |  | ||||||
|         NodeBundle { |  | ||||||
|             style: Style { |  | ||||||
|                 width: Val::Percent(100.0), |  | ||||||
|                 align_items: AlignItems::Center, |  | ||||||
|                 justify_content: JustifyContent::Center, |  | ||||||
|                 ..default() |  | ||||||
|             }, |  | ||||||
|             focus_policy: FocusPolicy::Pass, |  | ||||||
|             ..default() |  | ||||||
|         }, |  | ||||||
|         MonologueContainer, |  | ||||||
|     )); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn texts_ui( |  | ||||||
|     mut events: EventReader<AssetEvent<Monologue>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     widget: Query<Entity, With<MonologueWidget>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<Monologue>)>, |  | ||||||
|     server: Res<AssetServer>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         AssetEvent::Created { handle } => { |  | ||||||
|             info!("Monologue created! {:?}", event); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Removed { handle } => { |  | ||||||
|             info!("Monologue removed! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         AssetEvent::Modified { handle } => { |  | ||||||
|             info!("Monologue modified! {:?}", event); |  | ||||||
|             destroy_asset_button( |  | ||||||
|                 ¤t, |  | ||||||
|                 &mut commands, |  | ||||||
|                 &ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             create_asset_button( |  | ||||||
|                 &widget, |  | ||||||
|                 &mut commands, |  | ||||||
|                 ui::TargetAsset { |  | ||||||
|                     handle: handle.clone(), |  | ||||||
|                 }, |  | ||||||
|                 get_asset_name(&server, handle.clone()), |  | ||||||
|                 None, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn control_monologue( |  | ||||||
|     mut events: EventReader<ControlMonologue>, |  | ||||||
|     monologues: Res<Assets<Monologue>>, |  | ||||||
|     container: Query<Entity, With<MonologueContainer>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     font: Res<FontInfo>, // Not convinced we need this...
 |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|event| match event { |  | ||||||
|         ControlMonologue::Hide => { |  | ||||||
|             commands.entity(container.single()).despawn_descendants(); |  | ||||||
|         } |  | ||||||
|         ControlMonologue::Show(handle) => { |  | ||||||
|             monologues.get(handle).iter().for_each(|&monologue| { |  | ||||||
|                 commands |  | ||||||
|                     .entity(container.single()) |  | ||||||
|                     .despawn_descendants() |  | ||||||
|                     .with_children(|parent| { |  | ||||||
|                         parent |  | ||||||
|                             .spawn(NodeBundle { |  | ||||||
|                                 style: Style { |  | ||||||
|                                     max_width: Val::Percent(50.0), |  | ||||||
|                                     padding: UiRect::all(Val::Px(1.0)), |  | ||||||
|                                     margin: UiRect::all(Val::Px(1.0)), |  | ||||||
|                                     border: UiRect::all(Val::Px(1.0)), |  | ||||||
|                                     flex_direction: FlexDirection::Column, |  | ||||||
|                                     ..default() |  | ||||||
|                                 }, |  | ||||||
|                                 background_color: Color::WHITE.into(), |  | ||||||
|                                 border_color: Color::BLACK.into(), |  | ||||||
|                                 ..default() |  | ||||||
|                             }) |  | ||||||
|                             .with_children(|parent| { |  | ||||||
|                                 parent.spawn(( |  | ||||||
|                                     ui::TitleBarBase::new(Color::VIOLET).bundle(), |  | ||||||
|                                     ui::Title { |  | ||||||
|                                         text: "Monologue".into(), |  | ||||||
|                                         ..default() |  | ||||||
|                                     }, |  | ||||||
|                                     ui::Close { |  | ||||||
|                                         target: parent.parent_entity(), |  | ||||||
|                                     }, |  | ||||||
|                                     ui::Sorting(0), |  | ||||||
|                                 )); |  | ||||||
|                                 let style = match &font.default { |  | ||||||
|                                     Some(handle) => TextStyle { |  | ||||||
|                                         color: Color::BLACK.into(), |  | ||||||
|                                         font_size: 16.0, |  | ||||||
|                                         font: handle.clone(), |  | ||||||
|                                         ..default() |  | ||||||
|                                     }, |  | ||||||
|                                     None => TextStyle { |  | ||||||
|                                         color: Color::BLACK.into(), |  | ||||||
|                                         font_size: 16.0, |  | ||||||
|                                         ..default() |  | ||||||
|                                     }, |  | ||||||
|                                 }; |  | ||||||
|                                 parent.spawn(( |  | ||||||
|                                     TextBundle::from_section(monologue.text.clone(), style), |  | ||||||
|                                     handle.clone(), |  | ||||||
|                                 )); |  | ||||||
|                             }); |  | ||||||
|                     }); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn ui_control_monologue( |  | ||||||
|     events: Query< |  | ||||||
|         ( |  | ||||||
|             &Interaction, |  | ||||||
|             &ui::TargetAsset<Monologue>, |  | ||||||
|             Option<&ui::Active>, |  | ||||||
|         ), |  | ||||||
|         (With<Button>, Changed<Interaction>), |  | ||||||
|     >, |  | ||||||
|     mut writer: EventWriter<ControlMonologue>, |  | ||||||
| ) { |  | ||||||
|     events |  | ||||||
|         .iter() |  | ||||||
|         .filter_map( |  | ||||||
|             |(interaction, ui::TargetAsset { handle }, active)| match interaction { |  | ||||||
|                 Interaction::Pressed => Some((handle, active)), |  | ||||||
|                 _ => None, |  | ||||||
|             }, |  | ||||||
|         ) |  | ||||||
|         .for_each(|(handle, active)| match active { |  | ||||||
|             Some(_) => writer.send(ControlMonologue::Hide), |  | ||||||
|             None => writer.send(ControlMonologue::Show(handle.clone())), |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO: Sync Handle<Monologue> and TextStyle components to automagically generate and sync text
 |  | ||||||
| pub fn sync_monologue_font( |  | ||||||
|     mut texts: Query<&mut Text, With<Handle<Monologue>>>, |  | ||||||
|     font: Res<FontInfo>, |  | ||||||
| ) { |  | ||||||
|     if font.is_changed() || font.is_added() { |  | ||||||
|         texts.iter_mut().for_each(|mut text| { |  | ||||||
|             text.sections |  | ||||||
|                 .iter_mut() |  | ||||||
|                 .for_each(|section| match &font.default { |  | ||||||
|                     Some(handle) => section.style.font = handle.clone(), |  | ||||||
|                     None => section.style.font = Handle::default(), |  | ||||||
|                 }); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,8 +0,0 @@ | |||||||
| pub use crate::{ |  | ||||||
|     editor::{ |  | ||||||
|         animation::*, asset_sync::*, assets::*, audio::*, camera::*, font::*, gltf::*, level::*, |  | ||||||
|         lighting::*, monologue::*, quit::*, reset::*, scene::*, timeline::*, *, |  | ||||||
|     }, |  | ||||||
|     ui, |  | ||||||
| }; |  | ||||||
| pub use bevy::{gltf::Gltf, prelude::*}; |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| use bevy::app::AppExit; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct QuitAction; |  | ||||||
| 
 |  | ||||||
| pub fn quit(mut exit: EventWriter<AppExit>) { |  | ||||||
|     exit.send(AppExit); |  | ||||||
| } |  | ||||||
| @ -1,69 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct ClearLevel; |  | ||||||
| 
 |  | ||||||
| pub fn clear_level( |  | ||||||
|     events: Query<Entity, (With<ClearLevel>, Added<ui::Active>)>, |  | ||||||
|     actives: Query< |  | ||||||
|         Entity, |  | ||||||
|         ( |  | ||||||
|             With<ui::Active>, |  | ||||||
|             Or<( |  | ||||||
|                 With<ui::TargetEntity>, |  | ||||||
|                 With<ui::TargetAsset<Gltf>>, |  | ||||||
|                 With<ui::TargetAsset<Scene>>, |  | ||||||
|                 With<ui::TargetAsset<AnimationClip>>, |  | ||||||
|                 With<ui::TargetAsset<AudioSource>>, |  | ||||||
|                 With<ui::TargetAsset<Monologue>>, |  | ||||||
|                 With<ui::TargetAsset<Font>>, |  | ||||||
|             )>, |  | ||||||
|         ), |  | ||||||
|     >, |  | ||||||
|     root: Query<Entity, With<LevelRoot>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|_| { |  | ||||||
|         actives.iter().for_each(|entity| { |  | ||||||
|             commands.entity(entity).remove::<ui::Active>(); |  | ||||||
|         }); |  | ||||||
|         root.iter().for_each(|entity| { |  | ||||||
|             commands.entity(entity).despawn_descendants(); |  | ||||||
|         }); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct ClearAssets; |  | ||||||
| 
 |  | ||||||
| pub fn clear_assets( |  | ||||||
|     asset_holders: Query< |  | ||||||
|         Entity, |  | ||||||
|         Or<( |  | ||||||
|             With<ui::TargetAsset<Gltf>>, |  | ||||||
|             With<Handle<Gltf>>, |  | ||||||
|             With<ui::TargetAsset<Scene>>, |  | ||||||
|             With<Handle<Scene>>, |  | ||||||
|             With<ui::TargetAsset<AnimationClip>>, |  | ||||||
|             With<Handle<AnimationClip>>, |  | ||||||
|             With<ui::TargetAsset<AudioSource>>, |  | ||||||
|             With<Handle<AudioSource>>, |  | ||||||
|             With<ui::TargetAsset<Monologue>>, |  | ||||||
|             With<Handle<Monologue>>, |  | ||||||
|             With<ui::TargetAsset<Font>>, |  | ||||||
|             With<Handle<Font>>, |  | ||||||
|         )>, |  | ||||||
|     >, |  | ||||||
|     mut registry: ResMut<AssetRegistry>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     info!("Clearing assets"); |  | ||||||
| 
 |  | ||||||
|     // Clear buttons holding asset references
 |  | ||||||
|     asset_holders |  | ||||||
|         .iter() |  | ||||||
|         .for_each(|entity| commands.entity(entity).despawn_recursive()); |  | ||||||
| 
 |  | ||||||
|     // Empty asset registry
 |  | ||||||
|     registry.0.clear(); |  | ||||||
| } |  | ||||||
| @ -1,93 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Component, Default)] |  | ||||||
| pub struct SceneWidget; |  | ||||||
| 
 |  | ||||||
| pub fn add_scenes_ui( |  | ||||||
|     gltf_selected: Query<&ui::TargetAsset<Gltf>, Added<ui::Active>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
|     gltfs: Res<Assets<Gltf>>, |  | ||||||
|     widget: Query<Entity, With<SceneWidget>>, |  | ||||||
| ) { |  | ||||||
|     gltf_selected.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         if let Some(gltf) = gltfs.get(&handle.clone()) { |  | ||||||
|             gltf.named_scenes.iter().for_each(|(name, handle)| { |  | ||||||
|                 create_asset_button( |  | ||||||
|                     &widget, |  | ||||||
|                     &mut commands, |  | ||||||
|                     ui::TargetAsset { |  | ||||||
|                         handle: handle.clone(), |  | ||||||
|                     }, |  | ||||||
|                     name.clone(), |  | ||||||
|                     None, |  | ||||||
|                 ); |  | ||||||
|             }) |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn remove_scenes_ui( |  | ||||||
|     mut gltf_unselected: RemovedComponents<ui::Active>, |  | ||||||
|     target_assets: Query<&ui::TargetAsset<Gltf>>, |  | ||||||
|     current: Query<(Entity, &ui::TargetAsset<Scene>)>, |  | ||||||
|     gltfs: Res<Assets<Gltf>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     gltf_unselected |  | ||||||
|         .iter() |  | ||||||
|         .filter_map(|entity| target_assets.get(entity).ok()) |  | ||||||
|         .filter_map(|ui::TargetAsset { handle }| gltfs.get(handle)) |  | ||||||
|         .for_each(|gltf| { |  | ||||||
|             gltf.scenes.iter().for_each(|handle| { |  | ||||||
|                 destroy_asset_button( |  | ||||||
|                     ¤t, |  | ||||||
|                     &mut commands, |  | ||||||
|                     &ui::TargetAsset { |  | ||||||
|                         handle: handle.clone(), |  | ||||||
|                     }, |  | ||||||
|                 ); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn control_active_scenes( |  | ||||||
|     added: Query<Entity, (With<Button>, Added<ui::Active>)>, |  | ||||||
|     mut removed: RemovedComponents<ui::Active>, |  | ||||||
|     scene_refs: Query<&ui::TargetAsset<Scene>>, |  | ||||||
|     scenes: Query<(Entity, &Handle<Scene>)>, |  | ||||||
|     level_root: Query<Entity, With<LevelRoot>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // A scene button was marked inactive
 |  | ||||||
|     removed.iter().for_each(|entity| { |  | ||||||
|         // Get the handle associated with that button
 |  | ||||||
|         scene_refs |  | ||||||
|             .get(entity) |  | ||||||
|             .iter() |  | ||||||
|             .for_each(|ui::TargetAsset { handle }| { |  | ||||||
|                 scenes |  | ||||||
|                     .iter() |  | ||||||
|                     .find_map(|(entity, this_handle)| (this_handle == handle).then_some(entity)) |  | ||||||
|                     .iter() |  | ||||||
|                     .for_each(|&entity| { |  | ||||||
|                         commands.entity(entity).despawn_recursive(); |  | ||||||
|                     }); |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
|     added.iter().for_each(|entity| { |  | ||||||
|         scene_refs |  | ||||||
|             .get(entity) |  | ||||||
|             .iter() |  | ||||||
|             .for_each(|ui::TargetAsset { handle }| { |  | ||||||
|                 info!("Spawning Scene {:?}", handle); |  | ||||||
|                 commands |  | ||||||
|                     .entity(level_root.single()) |  | ||||||
|                     .with_children(|parent| { |  | ||||||
|                         parent.spawn(SceneBundle { |  | ||||||
|                             scene: handle.clone(), |  | ||||||
|                             ..default() |  | ||||||
|                         }); |  | ||||||
|                     }); |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @ -1,325 +0,0 @@ | |||||||
| use crate::editor::prelude::*; |  | ||||||
| 
 |  | ||||||
| /// Timeline widget marker
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct TimelineWidget; |  | ||||||
| 
 |  | ||||||
| /// Add Epoch component, used on a button to trigger a new epoch addition
 |  | ||||||
| #[derive(Debug, Component)] |  | ||||||
| pub struct AddEpoch; |  | ||||||
| 
 |  | ||||||
| /// Epoch ID Component
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochId { |  | ||||||
|     id: usize, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch GLTF Component
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochGltf { |  | ||||||
|     gltf: Handle<Gltf>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch Scene Component
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochScene { |  | ||||||
|     scene: Handle<Scene>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch Camera Component, marking the current camera
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochCamera { |  | ||||||
|     camera: Entity, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch music component, marking the opening track for this epoch
 |  | ||||||
| #[derive(Debug, Reflect, Default, Component, Clone)] |  | ||||||
| pub struct EpochMusic { |  | ||||||
|     music: Handle<AudioSource>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch monologue, marking the dialog spoken this epoch
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochMonologue { |  | ||||||
|     monologue: Handle<Monologue>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch font, marking the font used for this epoch's monologue
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochFont { |  | ||||||
|     font: Handle<Font>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// A vector of audios looping this epoch as background tracks
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochSfx { |  | ||||||
|     sfx: Vec<Handle<AudioSource>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Epoch animations, looping this epoch
 |  | ||||||
| #[derive(Debug, Reflect, Component, Clone)] |  | ||||||
| pub struct EpochAnimations { |  | ||||||
|     animations: Vec<Handle<AnimationClip>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// System for adding an epoch to the level's timeline
 |  | ||||||
| /// Triggered when a button with the AddEpoch marker is Active
 |  | ||||||
| pub fn add_timeline_epoch( |  | ||||||
|     root: Query<(Entity, &Children), With<TimelineWidget>>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     info!("Adding timeline epoch"); |  | ||||||
| 
 |  | ||||||
|     root.iter().for_each(|(entity, children)| { |  | ||||||
|         let id = children.iter().len(); |  | ||||||
|         let name = format!("{}", id); |  | ||||||
|         commands.entity(entity).with_children(|parent| { |  | ||||||
|             parent.spawn(( |  | ||||||
|                 ButtonBundle { |  | ||||||
|                     style: Style { |  | ||||||
|                         border: UiRect::all(Val::Px(1.0)), |  | ||||||
|                         margin: UiRect::all(Val::Px(1.0)), |  | ||||||
|                         padding: UiRect::all(Val::Px(1.0)), |  | ||||||
|                         ..default() |  | ||||||
|                     }, |  | ||||||
|                     border_color: Color::BLACK.into(), |  | ||||||
|                     ..default() |  | ||||||
|                 }, |  | ||||||
|                 ui::Title { |  | ||||||
|                     text: name, |  | ||||||
|                     ..default() |  | ||||||
|                 }, |  | ||||||
|                 EpochId { id }, |  | ||||||
|             )); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Set the GLTF for the current epoch
 |  | ||||||
| pub fn set_epoch_gltf( |  | ||||||
|     events: Query<&ui::TargetAsset<Gltf>, Added<ui::Active>>, |  | ||||||
|     active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a GLTF is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch.iter().for_each(|entity| { |  | ||||||
|             // Set the GLTF (overwrite existing GLTF selections)
 |  | ||||||
|             commands.entity(entity).insert(EpochGltf { |  | ||||||
|                 gltf: handle.clone(), |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             // TODO: Unset Scene, Camera, Animations
 |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_gltf(events: Query<Option<&EpochGltf>, (Added<ui::Active>, With<EpochId>)>) { |  | ||||||
|     events.iter().for_each(|epoch_gltf| { |  | ||||||
|         warn!("TODO: Load epoch GLTF!"); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_scene( |  | ||||||
|     events: Query<&ui::TargetAsset<Scene>, Added<ui::Active>>, |  | ||||||
|     active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch.iter().for_each(|entity| { |  | ||||||
|             // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|             commands.entity(entity).insert(EpochScene { |  | ||||||
|                 scene: handle.clone(), |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_scene(events: Query<Option<&EpochScene>, (Added<ui::Active>, With<EpochId>)>) { |  | ||||||
|     events.iter().for_each(|epoch_scene| { |  | ||||||
|         warn!("TODO: Load epoch Scene!"); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_camera( |  | ||||||
|     events: Query<&ui::TargetEntity, Added<ui::Active>>, |  | ||||||
|     active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetEntity { entity }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch.iter().for_each(|this_entity| { |  | ||||||
|             // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|             commands |  | ||||||
|                 .entity(this_entity) |  | ||||||
|                 .insert(EpochCamera { camera: *entity }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_camera(events: Query<Option<&EpochCamera>, (Added<ui::Active>, With<EpochId>)>) { |  | ||||||
|     events.iter().for_each(|epoch_camera| { |  | ||||||
|         warn!("TODO: Load epoch Camera"); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_music( |  | ||||||
|     events: Query<&ui::TargetAsset<AudioSource>, Added<ui::Active>>, |  | ||||||
|     active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         info!("TODO: Select scene music"); |  | ||||||
| 
 |  | ||||||
|         // // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         // active_epoch.iter().for_each(|entity| {
 |  | ||||||
|         //     // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|         //     commands.entity(entity).insert(EpochMusic { music: handle.clone() });
 |  | ||||||
|         // });
 |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_music(events: Query<Option<&EpochMusic>, (Added<ui::Active>, With<EpochId>)>) { |  | ||||||
|     events.iter().for_each(|epoch_music| { |  | ||||||
|         warn!("TODO: Load epoch music!"); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_monologue( |  | ||||||
|     events: Query<&ui::TargetAsset<Monologue>, Added<ui::Active>>, |  | ||||||
|     active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch.iter().for_each(|entity| { |  | ||||||
|             // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|             commands.entity(entity).insert(EpochMonologue { |  | ||||||
|                 monologue: handle.clone(), |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_monologue( |  | ||||||
|     events: Query<Option<&EpochMonologue>, (Added<ui::Active>, With<EpochId>)>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|epoch_monologue| { |  | ||||||
|         warn!("TODO: unset epoch Monologue!"); |  | ||||||
|         epoch_monologue |  | ||||||
|             .iter() |  | ||||||
|             .for_each(|EpochMonologue { monologue }| { |  | ||||||
|                 warn!("TODO: Set level epoch"); |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_font( |  | ||||||
|     events: Query<&ui::TargetAsset<Font>, Added<ui::Active>>, |  | ||||||
|     active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch.iter().for_each(|entity| { |  | ||||||
|             // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|             commands.entity(entity).insert(EpochFont { |  | ||||||
|                 font: handle.clone(), |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_font( |  | ||||||
|     events: Query<Option<&EpochFont>, (Added<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut font_info: ResMut<FontInfo>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|epoch_font| { |  | ||||||
|         font_info.default = epoch_font.map(|EpochFont { font }| font.clone()); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_sfx( |  | ||||||
|     events: Query<&ui::TargetAsset<AudioSource>, Added<ui::Active>>, |  | ||||||
|     mut active_epoch: Query<(Entity, Option<&mut EpochSfx>), (With<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch.iter_mut().for_each(|(entity, maybe_sfx)| { |  | ||||||
|             info!("Adding sfx {:?} to epoch {:?}", maybe_sfx, entity); |  | ||||||
|             if let Some(mut epoch_sfx) = maybe_sfx { |  | ||||||
|                 epoch_sfx.sfx.push(handle.clone()); |  | ||||||
|             } else { |  | ||||||
|                 // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|                 commands.entity(entity).insert(EpochSfx { |  | ||||||
|                     sfx: vec![handle.clone()], |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_sfx( |  | ||||||
|     added: Query<Entity, (Added<ui::Active>, With<EpochId>)>, |  | ||||||
|     mut removed: RemovedComponents<ui::Active>, |  | ||||||
|     epoch_sfx: Query<&EpochSfx>, |  | ||||||
|     mut writer: EventWriter<ControlAudio>, |  | ||||||
| ) { |  | ||||||
|     removed.iter().for_each(|entity| { |  | ||||||
|         epoch_sfx.get(entity).iter().for_each(|EpochSfx { sfx }| { |  | ||||||
|             sfx.iter().for_each(|handle| { |  | ||||||
|                 writer.send(ControlAudio::Stop(handle.clone())); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|     added.iter().for_each(|entity| { |  | ||||||
|         epoch_sfx.get(entity).iter().for_each(|EpochSfx { sfx }| { |  | ||||||
|             sfx.iter().for_each(|handle| { |  | ||||||
|                 writer.send(ControlAudio::Loop(handle.clone())); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn set_epoch_animations( |  | ||||||
|     events: Query<&ui::TargetAsset<AnimationClip>, Added<ui::Active>>, |  | ||||||
|     mut active_epoch: Query< |  | ||||||
|         (Entity, Option<&mut EpochAnimations>), |  | ||||||
|         (With<ui::Active>, With<EpochId>), |  | ||||||
|     >, |  | ||||||
|     mut commands: Commands, |  | ||||||
| ) { |  | ||||||
|     // Each time a Scene is selected in the editor
 |  | ||||||
|     events.iter().for_each(|ui::TargetAsset { handle }| { |  | ||||||
|         // Iterate over all (0 or 1) active epochs
 |  | ||||||
|         active_epoch |  | ||||||
|             .iter_mut() |  | ||||||
|             .for_each(|(entity, maybe_animations)| { |  | ||||||
|                 if let Some(mut epoch_animations) = maybe_animations { |  | ||||||
|                     epoch_animations.animations.push(handle.clone()); |  | ||||||
|                 } else { |  | ||||||
|                     // Set the Scene (overwrite existing Scene selections)
 |  | ||||||
|                     commands.entity(entity).insert(EpochAnimations { |  | ||||||
|                         animations: vec![handle.clone()], |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn load_epoch_animations( |  | ||||||
|     events: Query<Option<&EpochAnimations>, (Added<ui::Active>, With<EpochId>)>, |  | ||||||
| ) { |  | ||||||
|     events.iter().for_each(|epoch_animations| { |  | ||||||
|         warn!("TODO: Load epoch Animations!"); |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
					Loading…
					
					
				
		Reference in New Issue