Move trees debug code to separate module

main
Elijah Voigt 3 months ago
parent c72ed47caa
commit dba46a99da

@ -0,0 +1,415 @@
use super::*;
pub(crate) struct TreesDebugPlugin;
impl Plugin for TreesDebugPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Startup,
init_debug_ui,
).add_systems(
Update,
(
(
spawn_debug_buttons.run_if(on_event::<AssetEvent<Monologue>>),
),
(
monologue_asset_tooltip
.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
hide_menu.run_if(any_component_changed::<NavState>),
clear_monologue.run_if(any_component_changed::<NavState>),
control_menu.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
delete_tree.run_if(on_event::<Pointer<Click>>),
drag_tree.run_if(on_event::<Pointer<Drag>>),
).run_if(in_state(DebuggingState::On)),
)
).add_observer(add_dialog_option);
}
}
#[derive(Component)]
struct MonologuesContainer;
#[derive(Component)]
struct MonologuesList;
#[derive(Component)]
struct MonologuePreview;
fn drag_tree(
mut events: EventReader<Pointer<Drag>>,
state: Res<State<DebuggingState>>,
mut query: Query<&mut Transform, With<Tree>>,
camera: Single<(&Camera, &GlobalTransform), With<Camera>>,
window: Single<&Window>,
) {
debug_assert_eq!(*state.get(), DebuggingState::On);
events.read().for_each(|event| {
if let Ok(mut t) = query.get_mut(event.target)
{
let world_position = window
.cursor_position()
.and_then(|cursor| camera.0.viewport_to_world(camera.1, cursor).ok())
.map(|ray| {
// Compute ray's distance to entity
let distance = ray
.intersect_plane(t.translation, InfinitePlane3d::new(t.up()))
.unwrap();
ray.get_point(distance)
});
t.translation = world_position.unwrap();
}
});
}
/// Panel for selecting which monologue tree to spawn
fn init_debug_ui(mut commands: Commands) {
let mut monologue_button = None;
commands
.spawn((
Node {
top: Val::Px(0.0),
left: Val::Px(0.0),
flex_direction: FlexDirection::Row,
..default()
},
DebuggingState::On,
))
.with_children(|parent| {
monologue_button = Some(
parent
.spawn((
Name::new("Monologue Assignment Menu"),
children![Text::new("+Monologue"),],
Button,
Node {
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
..default()
},
DebuggingState::On,
MonologuesContainer,
))
.id(),
);
parent
.spawn((
Name::new("Tree Planter"),
children![Text::new("+Tree"),],
Node {
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
..default()
},
DebuggingState::On,
MonologuesContainer,
Button,
))
.observe(spawn_tree);
});
commands
.spawn((
NavParent(monologue_button.unwrap()),
NavState::default(),
DebuggingState::On,
Name::new("Container"),
Node {
flex_direction: FlexDirection::Row,
top: Val::Px(25.0),
height: Val::Percent(90.0),
width: Val::Percent(80.0),
..default()
},
))
.with_children(|parent| {
parent
.spawn((
Name::new("Buttons"),
Node {
flex_direction: FlexDirection::Column,
height: Val::Percent(100.0),
width: Val::Percent(40.0),
overflow: Overflow::scroll_y(),
..default()
},
ScrollPosition::default(),
MonologuesList,
))
.observe(scroll);
parent.spawn((
Name::new("Preview"),
MonologuePreview,
NavParent(monologue_button.unwrap()),
NavState::default(),
Node {
flex_direction: FlexDirection::Column,
padding: UiRect::all(Val::Px(10.0)),
overflow: Overflow::scroll_y(),
width: Val::Percent(60.0),
..default()
},
));
});
}
// When you pointer goes off of the '+' or any of it's children make the entire menu invisible
fn control_menu(
mut over_events: EventReader<Pointer<Over>>,
mut out_events: EventReader<Pointer<Out>>,
nav_children: Query<&NavParent>,
children: Query<&ChildOf>,
nav_parents: Query<&NavChildren>,
parents: Query<&Children>,
mut nav: Query<&mut NavState>,
hover_map: Res<HoverMap>,
) {
over_events.read().for_each(|over| {
let root = nav_children.root_ancestor(over.target);
nav_parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Open;
}
});
});
// Gives us the enities covered by the HoverMap
let is_hovered: Vec<&Entity> = hover_map
.iter()
.flat_map(|(_, submap)| {
let x: Vec<&Entity> = submap.iter().map(|(node, _)| node).collect();
x
})
.collect();
// For all pointer out events
out_events.read().for_each(|out| {
// If a relative of out.target is hovered, do nothing
// Otherwise set to closed
let root = children.root_ancestor(out.target);
let tree_still_hovered = parents
.iter_descendants(root)
.any(|child| is_hovered.contains(&&child));
if !tree_still_hovered {
if let Ok(mut n) = nav.get_mut(root) {
*n = NavState::Closed;
}
parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Closed;
}
})
}
})
}
/// When a (Text) dialog option is added:
/// 1. Add the Button component
/// 2. Change the color to Orange
/// 3. Add observers for click (select) and hover (change color)
fn add_dialog_option(trigger: Trigger<OnAdd, DialogOption>, mut commands: Commands) {
commands
.entity(trigger.target())
.insert(Button)
.insert(Node {
width: Val::Percent(100.0),
..default()
})
.insert(TextLayout::new_with_justify(JustifyText::Center))
.insert(TextColor(ORANGE.into()))
.observe(choose_dialog_option)
.observe(hover_dialog_option_over)
.observe(hover_dialog_option_out);
}
fn assign_monologue_event(
trigger: Trigger<Pointer<Click>>,
mut events: EventWriter<AssignMonologue>,
monologues: Query<&TreeMonologue>,
) {
let TreeMonologue(handle) = monologues.get(trigger.target()).unwrap();
events.write(AssignMonologue(handle.clone()));
}
/// Observer for the "Plant a new tree" button in the debug UI
fn spawn_tree(_trigger: Trigger<Pointer<Click>>, mut events: EventWriter<PlantTree>) {
events.write(PlantTree(None));
}
fn clear_monologue(
mut nodes: Query<
(Entity, &NavState),
(Changed<NavState>, With<MonologuePreview>, Without<Text>),
>,
mut commands: Commands,
) {
nodes.iter_mut().for_each(|(e, n)| {
if matches!(n, NavState::Closed) {
commands.entity(e).despawn_related::<Children>();
}
});
}
// When you pointer over the '+' make the entire menu visible
fn hide_menu(mut nodes: Query<(&mut Visibility, &NavState), Changed<NavState>>) {
nodes.iter_mut().for_each(|(mut v, n)| {
*v = match n {
NavState::Open => Visibility::Inherited,
NavState::Closed => Visibility::Hidden,
};
});
}
fn delete_tree(mut events: EventReader<Pointer<Click>>, mut commands: Commands, query: Query<Entity, With<Tree>>) {
events.read().for_each(|event| {
if matches!(event.event.button, PointerButton::Middle) && query.contains(event.target) {
debug!("Middle Click -> Despawning {}", event.target);
commands.entity(event.target).despawn();
}
})
}
/// Add the "script: path/to/file.mono" tooltip info
fn monologue_asset_tooltip(
mut over_events: EventReader<Pointer<Over>>,
mut out_events: EventReader<Pointer<Out>>,
mut tooltip: ResMut<ToolTip>,
trees: Query<(&Tree, Option<&TreeMonologue>)>,
) {
out_events
.read()
.filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target))
.for_each(|_| {
tooltip.remove("Script");
});
over_events
.read()
.filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target))
.for_each(|e| match trees.get(e) {
Ok((_tree, Some(TreeMonologue(handle)))) => match handle.path() {
Some(p) => tooltip.insert("Script", format!("{p}")),
None => tooltip.insert("Script", "A".into()),
},
Ok((_tree, None)) => {
tooltip.insert("Script", "N/A".into());
}
_ => (),
});
}
/// When a dialog option is chosen (clicked on) we do the following:
fn choose_dialog_option(
trigger: Trigger<Pointer<Click>>,
mut dialog_events: EventWriter<DialogEvent>,
mut commands: Commands,
texts: Query<&Text>,
options: Query<Entity, With<DialogOption>>,
dialog_box: Single<Entity, With<DialogBox>>,
) {
debug!("Choosing dialog {:?}", trigger.target());
debug!("Despawning dialog options");
options.iter().for_each(|e| {
commands.entity(e).despawn();
});
debug!("Inserting dialog line");
if let Ok(t) = texts.get(trigger.target()) {
commands.entity(*dialog_box).with_children(|parent| {
parent.spawn((t.clone(), DialogLine));
});
}
// trigger the next dialog line
dialog_events.write(DialogEvent::NextBatch);
}
fn preview_monologue(
trigger: Trigger<Pointer<Over>>,
container: Single<Entity, (With<MonologuePreview>, Without<Button>, Without<Text>)>,
tree_monologue: Query<&TreeMonologue, With<Button>>,
monologues: Res<Assets<Monologue>>,
mut commands: Commands,
) {
// Get the handle for this button's monologuie
if let Ok(TreeMonologue(handle)) = tree_monologue.get(trigger.target()) {
// Get the monologue data
if let Some(monologue) = monologues.get(handle) {
commands.entity(*container).despawn_related::<Children>();
// Spawn the monologue
commands.entity(*container).with_children(|parent| {
let mut i = 0;
while let Some(batch) = monologue.get(i) {
parent.spawn((Text::new("---"), MonologuePreview));
debug!("---");
for (n, item) in batch.lines.iter().enumerate() {
parent.spawn((Text::new(format!("{i}.{n}: {item}")), MonologuePreview));
debug!("{i}.{n}: {item}");
}
// TODO: Just implement iter_batches or something
i += 1;
}
parent.spawn((Text::new("---"), MonologuePreview));
debug!("---");
});
}
}
}
fn spawn_debug_buttons(
mut events: EventReader<AssetEvent<Monologue>>,
mut commands: Commands,
container: Single<Entity, (With<MonologuesList>, Without<Button>)>,
server: Res<AssetServer>,
) {
debug_assert!(
!events.is_empty(),
"Should only spawn buttons when monologues are loaded"
);
events.read().for_each(|event| {
// If this is an "Asset was loaded" event
// These are just asset events for monologues so no need to check the asset type
if let AssetEvent::LoadedWithDependencies { id } = event {
// Get the handle from the asset ID
let handle = server.get_id_handle(*id).unwrap();
// Spawn a button in the box containing buttons
commands.entity(*container).with_children(|parent| {
parent
.spawn((
Button,
children![
Text::new(
handle
.path()
.unwrap()
.to_string()
.trim_prefix("trees/")
.trim_suffix(".mono"),
),
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
],
TreeMonologue(handle.clone()),
MonologuesList,
))
.observe(assign_monologue_event)
.observe(preview_monologue);
});
}
});
}

@ -4,10 +4,12 @@
#![feature(trim_prefix_suffix)] #![feature(trim_prefix_suffix)]
mod mono; mod mono;
mod debug;
use bevy::{picking::hover::HoverMap, platform::hash::RandomState}; use bevy::{picking::hover::HoverMap, platform::hash::RandomState};
use games::*; use games::*;
use mono::*; use mono::*;
use debug::*;
use std::hash::BuildHasher; use std::hash::BuildHasher;
fn main() { fn main() {
@ -16,6 +18,7 @@ fn main() {
name: "trees".into(), name: "trees".into(),
}) })
.add_plugins(MonologueAssetsPlugin) .add_plugins(MonologueAssetsPlugin)
.add_plugins(TreesDebugPlugin)
.add_event::<DialogEvent>() .add_event::<DialogEvent>()
.add_event::<PlantTree>() .add_event::<PlantTree>()
.add_event::<AssignMonologue>() .add_event::<AssignMonologue>()
@ -26,7 +29,6 @@ fn main() {
( (
init_trees, init_trees,
init_ui, init_ui,
init_debug_ui,
load_monologues, load_monologues,
position_camera.after(setup_camera), position_camera.after(setup_camera),
), ),
@ -46,7 +48,6 @@ fn main() {
.run_if(in_state(DebuggingState::Off)) .run_if(in_state(DebuggingState::Off))
.run_if(in_state(DialogState::Idle)) .run_if(in_state(DialogState::Idle))
.run_if(on_event::<Pointer<Click>>), .run_if(on_event::<Pointer<Click>>),
spawn_debug_buttons.run_if(on_event::<AssetEvent<Monologue>>),
handle_plant_tree.run_if(on_event::<PlantTree>), handle_plant_tree.run_if(on_event::<PlantTree>),
assign_monologue_to_tree assign_monologue_to_tree
.run_if(on_event::<AssignMonologue>) .run_if(on_event::<AssignMonologue>)
@ -54,15 +55,9 @@ fn main() {
dialog_engine.run_if(on_event::<DialogEvent>), dialog_engine.run_if(on_event::<DialogEvent>),
auto_scroll.run_if(any_component_added::<DialogOption>), auto_scroll.run_if(any_component_added::<DialogOption>),
dialog_box_visibility.run_if(state_changed::<DialogState>), dialog_box_visibility.run_if(state_changed::<DialogState>),
monologue_asset_tooltip
.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
scale_window.run_if(on_event::<WindowResized>), scale_window.run_if(on_event::<WindowResized>),
hide_menu.run_if(any_component_changed::<NavState>),
clear_monologue.run_if(any_component_changed::<NavState>),
control_menu.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
), ),
) )
.add_observer(add_dialog_option)
.add_observer(add_tree_monologue) .add_observer(add_tree_monologue)
.add_observer(remove_tree_monologue) .add_observer(remove_tree_monologue)
.run(); .run();
@ -113,106 +108,6 @@ fn init_ui(mut commands: Commands) {
.observe(hover_dialog_box_out); .observe(hover_dialog_box_out);
} }
#[derive(Component)]
struct MonologuesContainer;
#[derive(Component)]
struct MonologuesList;
#[derive(Component)]
struct MonologuePreview;
/// Panel for selecting which monologue tree to spawn
fn init_debug_ui(mut commands: Commands) {
let mut monologue_button = None;
commands
.spawn((
Node {
top: Val::Px(0.0),
left: Val::Px(0.0),
flex_direction: FlexDirection::Row,
..default()
},
DebuggingState::On,
))
.with_children(|parent| {
monologue_button = Some(
parent
.spawn((
Name::new("Monologue Assignment Menu"),
children![Text::new("+Monologue"),],
Button,
Node {
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
..default()
},
DebuggingState::On,
MonologuesContainer,
))
.id(),
);
parent
.spawn((
Name::new("Tree Planter"),
children![Text::new("+Tree"),],
Node {
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
..default()
},
DebuggingState::On,
MonologuesContainer,
Button,
))
.observe(spawn_tree);
});
commands
.spawn((
NavParent(monologue_button.unwrap()),
NavState::default(),
Name::new("Container"),
Node {
flex_direction: FlexDirection::Row,
top: Val::Px(25.0),
height: Val::Percent(90.0),
width: Val::Percent(80.0),
..default()
},
))
.with_children(|parent| {
parent
.spawn((
Name::new("Buttons"),
Node {
flex_direction: FlexDirection::Column,
height: Val::Percent(100.0),
width: Val::Percent(40.0),
overflow: Overflow::scroll_y(),
..default()
},
ScrollPosition::default(),
MonologuesList,
))
.observe(scroll);
parent.spawn((
Name::new("Preview"),
MonologuePreview,
NavParent(monologue_button.unwrap()),
NavState::default(),
Node {
flex_direction: FlexDirection::Column,
padding: UiRect::all(Val::Px(10.0)),
overflow: Overflow::scroll_y(),
width: Val::Percent(60.0),
..default()
},
));
});
}
fn hover_dialog_box_over( fn hover_dialog_box_over(
trigger: Trigger<Pointer<Over>>, trigger: Trigger<Pointer<Over>>,
mut query: Query<&mut BackgroundColor, With<DialogBox>>, mut query: Query<&mut BackgroundColor, With<DialogBox>>,
@ -403,33 +298,6 @@ fn dialog_engine(
}); });
} }
/// When a dialog option is chosen (clicked on) we do the following:
fn choose_dialog_option(
trigger: Trigger<Pointer<Click>>,
mut dialog_events: EventWriter<DialogEvent>,
mut commands: Commands,
texts: Query<&Text>,
options: Query<Entity, With<DialogOption>>,
dialog_box: Single<Entity, With<DialogBox>>,
) {
debug!("Choosing dialog {:?}", trigger.target());
debug!("Despawning dialog options");
options.iter().for_each(|e| {
commands.entity(e).despawn();
});
debug!("Inserting dialog line");
if let Ok(t) = texts.get(trigger.target()) {
commands.entity(*dialog_box).with_children(|parent| {
parent.spawn((t.clone(), DialogLine));
});
}
// trigger the next dialog line
dialog_events.write(DialogEvent::NextBatch);
}
fn hover_dialog_option_over( fn hover_dialog_option_over(
trigger: Trigger<Pointer<Over>>, trigger: Trigger<Pointer<Over>>,
mut query: Query<(&mut TextColor, &mut BackgroundColor)>, mut query: Query<(&mut TextColor, &mut BackgroundColor)>,
@ -450,25 +318,6 @@ fn hover_dialog_option_out(
} }
} }
/// When a (Text) dialog option is added:
/// 1. Add the Button component
/// 2. Change the color to Orange
/// 3. Add observers for click (select) and hover (change color)
fn add_dialog_option(trigger: Trigger<OnAdd, DialogOption>, mut commands: Commands) {
commands
.entity(trigger.target())
.insert(Button)
.insert(Node {
width: Val::Percent(100.0),
..default()
})
.insert(TextLayout::new_with_justify(JustifyText::Center))
.insert(TextColor(ORANGE.into()))
.observe(choose_dialog_option)
.observe(hover_dialog_option_over)
.observe(hover_dialog_option_out);
}
fn dialog_box_visibility( fn dialog_box_visibility(
state: Res<State<DialogState>>, state: Res<State<DialogState>>,
mut dialog_box: Single<&mut Visibility, With<DialogBox>>, mut dialog_box: Single<&mut Visibility, With<DialogBox>>,
@ -480,35 +329,6 @@ fn dialog_box_visibility(
}; };
} }
/// Add the "script: path/to/file.mono" tooltip info
fn monologue_asset_tooltip(
mut over_events: EventReader<Pointer<Over>>,
mut out_events: EventReader<Pointer<Out>>,
mut tooltip: ResMut<ToolTip>,
trees: Query<(&Tree, Option<&TreeMonologue>)>,
) {
out_events
.read()
.filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target))
.for_each(|_| {
tooltip.remove("Script");
});
over_events
.read()
.filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target))
.for_each(|e| match trees.get(e) {
Ok((_tree, Some(TreeMonologue(handle)))) => match handle.path() {
Some(p) => tooltip.insert("Script", format!("{p}")),
None => tooltip.insert("Script", "A".into()),
},
Ok((_tree, None)) => {
tooltip.insert("Script", "N/A".into());
}
_ => (),
});
}
fn add_tree_monologue( fn add_tree_monologue(
trigger: Trigger<OnAdd, TreeMonologue>, trigger: Trigger<OnAdd, TreeMonologue>,
query: Query<&MeshMaterial3d<StandardMaterial>>, query: Query<&MeshMaterial3d<StandardMaterial>>,
@ -567,13 +387,6 @@ fn scale_window(events: EventReader<WindowResized>, mut window: Single<&mut Wind
); );
} }
fn delete_tree(trigger: Trigger<Pointer<Click>>, mut commands: Commands) {
if matches!(trigger.event.button, PointerButton::Middle) {
debug!("Middle Click -> Despawning {}", trigger.target());
commands.entity(trigger.target()).despawn();
}
}
/// Load all monologues so they are in the asset store and trigger on-load events /// Load all monologues so they are in the asset store and trigger on-load events
fn load_monologues(server: ResMut<AssetServer>, mut loaded_assets: Local<Vec<Handle<Monologue>>>) { fn load_monologues(server: ResMut<AssetServer>, mut loaded_assets: Local<Vec<Handle<Monologue>>>) {
*loaded_assets = include_str!("../../../assets/trees/MONOLOGUES") *loaded_assets = include_str!("../../../assets/trees/MONOLOGUES")
@ -582,85 +395,6 @@ fn load_monologues(server: ResMut<AssetServer>, mut loaded_assets: Local<Vec<Han
.collect(); .collect();
} }
fn spawn_debug_buttons(
mut events: EventReader<AssetEvent<Monologue>>,
mut commands: Commands,
container: Single<Entity, (With<MonologuesList>, Without<Button>)>,
server: Res<AssetServer>,
) {
debug_assert!(
!events.is_empty(),
"Should only spawn buttons when monologues are loaded"
);
events.read().for_each(|event| {
// If this is an "Asset was loaded" event
// These are just asset events for monologues so no need to check the asset type
if let AssetEvent::LoadedWithDependencies { id } = event {
// Get the handle from the asset ID
let handle = server.get_id_handle(*id).unwrap();
// Spawn a button in the box containing buttons
commands.entity(*container).with_children(|parent| {
parent
.spawn((
Button,
children![
Text::new(
handle
.path()
.unwrap()
.to_string()
.trim_prefix("trees/")
.trim_suffix(".mono"),
),
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
],
TreeMonologue(handle.clone()),
MonologuesList,
))
.observe(assign_monologue_event)
.observe(preview_monologue);
});
}
});
}
fn preview_monologue(
trigger: Trigger<Pointer<Over>>,
container: Single<Entity, (With<MonologuePreview>, Without<Button>, Without<Text>)>,
tree_monologue: Query<&TreeMonologue, With<Button>>,
monologues: Res<Assets<Monologue>>,
mut commands: Commands,
) {
// Get the handle for this button's monologuie
if let Ok(TreeMonologue(handle)) = tree_monologue.get(trigger.target()) {
// Get the monologue data
if let Some(monologue) = monologues.get(handle) {
commands.entity(*container).despawn_related::<Children>();
// Spawn the monologue
commands.entity(*container).with_children(|parent| {
let mut i = 0;
while let Some(batch) = monologue.get(i) {
parent.spawn((Text::new("---"), MonologuePreview));
debug!("---");
for (n, item) in batch.lines.iter().enumerate() {
parent.spawn((Text::new(format!("{i}.{n}: {item}")), MonologuePreview));
debug!("{i}.{n}: {item}");
}
// TODO: Just implement iter_batches or something
i += 1;
}
parent.spawn((Text::new("---"), MonologuePreview));
debug!("---");
});
}
}
}
#[derive(Event)] #[derive(Event)]
struct PlantTree(Option<Handle<Monologue>>); struct PlantTree(Option<Handle<Monologue>>);
@ -705,9 +439,7 @@ fn handle_plant_tree(
let mesh = Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))); let mesh = Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0))));
tree.insert((mesh, material, transform)) tree.insert((mesh, material, transform));
.observe(delete_tree)
.observe(drag_tree);
if let Some(handle) = assignment { if let Some(handle) = assignment {
assignments.write(AssignMonologue(handle.clone())); assignments.write(AssignMonologue(handle.clone()));
@ -747,119 +479,6 @@ fn assign_monologue_to_tree(
}); });
} }
fn drag_tree(
trigger: Trigger<Pointer<Drag>>,
state: Res<State<DebuggingState>>,
mut query: Query<&mut Transform, With<Tree>>,
camera: Single<(&Camera, &GlobalTransform), With<Camera>>,
window: Single<&Window>,
) {
if *state.get() == DebuggingState::On
&& let Ok(mut t) = query.get_mut(trigger.target())
{
let world_position = window
.cursor_position()
.and_then(|cursor| camera.0.viewport_to_world(camera.1, cursor).ok())
.map(|ray| {
// Compute ray's distance to entity
let distance = ray
.intersect_plane(t.translation, InfinitePlane3d::new(t.up()))
.unwrap();
ray.get_point(distance)
});
t.translation = world_position.unwrap();
}
}
// When you pointer over the '+' make the entire menu visible
fn hide_menu(mut nodes: Query<(&mut Visibility, &NavState), Changed<NavState>>) {
nodes.iter_mut().for_each(|(mut v, n)| {
*v = match n {
NavState::Open => Visibility::Inherited,
NavState::Closed => Visibility::Hidden,
};
});
}
fn clear_monologue(
mut nodes: Query<
(Entity, &NavState),
(Changed<NavState>, With<MonologuePreview>, Without<Text>),
>,
mut commands: Commands,
) {
nodes.iter_mut().for_each(|(e, n)| {
if matches!(n, NavState::Closed) {
commands.entity(e).despawn_related::<Children>();
}
});
}
// When you pointer goes off of the '+' or any of it's children make the entire menu invisible
fn control_menu(
mut over_events: EventReader<Pointer<Over>>,
mut out_events: EventReader<Pointer<Out>>,
nav_children: Query<&NavParent>,
children: Query<&ChildOf>,
nav_parents: Query<&NavChildren>,
parents: Query<&Children>,
mut nav: Query<&mut NavState>,
hover_map: Res<HoverMap>,
) {
over_events.read().for_each(|over| {
let root = nav_children.root_ancestor(over.target);
nav_parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Open;
}
});
});
// Gives us the enities covered by the HoverMap
let is_hovered: Vec<&Entity> = hover_map
.iter()
.flat_map(|(_, submap)| {
let x: Vec<&Entity> = submap.iter().map(|(node, _)| node).collect();
x
})
.collect();
// For all pointer out events
out_events.read().for_each(|out| {
// If a relative of out.target is hovered, do nothing
// Otherwise set to closed
let root = children.root_ancestor(out.target);
let tree_still_hovered = parents
.iter_descendants(root)
.any(|child| is_hovered.contains(&&child));
if !tree_still_hovered {
if let Ok(mut n) = nav.get_mut(root) {
*n = NavState::Closed;
}
parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Closed;
}
})
}
})
}
/// Observer for the "Plant a new tree" button in the debug UI
fn spawn_tree(_trigger: Trigger<Pointer<Click>>, mut events: EventWriter<PlantTree>) {
events.write(PlantTree(None));
}
fn assign_monologue_event(
trigger: Trigger<Pointer<Click>>,
mut events: EventWriter<AssignMonologue>,
monologues: Query<&TreeMonologue>,
) {
let TreeMonologue(handle) = monologues.get(trigger.target()).unwrap();
events.write(AssignMonologue(handle.clone()));
}
/// On startup, plant a forest (add a few trees to the game) /// On startup, plant a forest (add a few trees to the game)
fn plant_forest(monos: Res<Assets<Monologue>>, mut e_trees: EventWriter<PlantTree>) { fn plant_forest(monos: Res<Assets<Monologue>>, mut e_trees: EventWriter<PlantTree>) {
let mut i = 10; let mut i = 10;

Loading…
Cancel
Save