Compare commits
	
		
			7 Commits 
		
	
	
		
			27522e2209
			...
			3b75fed684
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 3b75fed684 | 2 years ago | 
|  | 0a56361168 | 2 years ago | 
|  | 4495be1cd9 | 2 years ago | 
|  | 2b9f96d4b4 | 2 years ago | 
|  | 2433f6386d | 2 years ago | 
|  | 1963c290a1 | 2 years ago | 
|  | 36605781e8 | 2 years ago | 
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,155 @@ | |||||||
|  | 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(); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -0,0 +1,55 @@ | |||||||
|  | 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>(); | ||||||
|  |                 }); | ||||||
|  |         }); | ||||||
|  | } | ||||||
| @ -0,0 +1,120 @@ | |||||||
|  | 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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,138 @@ | |||||||
|  | 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())), | ||||||
|  |         }); | ||||||
|  | } | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | 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; | ||||||
|  |         } | ||||||
|  |     }) | ||||||
|  | } | ||||||
| @ -0,0 +1,109 @@ | |||||||
|  | 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>(); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,68 @@ | |||||||
|  | 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(); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -0,0 +1,167 @@ | |||||||
|  | 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()); | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | 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; | ||||||
|  |     }) | ||||||
|  | } | ||||||
| @ -0,0 +1,59 @@ | |||||||
|  | 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(()) | ||||||
|  |             }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -0,0 +1,229 @@ | |||||||
|  | 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(), | ||||||
|  |                 }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | 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::*}; | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | 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); | ||||||
|  | } | ||||||
| @ -0,0 +1,69 @@ | |||||||
|  | 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(); | ||||||
|  | } | ||||||
| @ -0,0 +1,93 @@ | |||||||
|  | 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() | ||||||
|  |                         }); | ||||||
|  |                     }); | ||||||
|  |             }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -0,0 +1,325 @@ | |||||||
|  | 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