|
|
|
|
@ -4,10 +4,12 @@
|
|
|
|
|
#![feature(trim_prefix_suffix)]
|
|
|
|
|
|
|
|
|
|
mod mono;
|
|
|
|
|
mod debug;
|
|
|
|
|
|
|
|
|
|
use bevy::{picking::hover::HoverMap, platform::hash::RandomState};
|
|
|
|
|
use games::*;
|
|
|
|
|
use mono::*;
|
|
|
|
|
use debug::*;
|
|
|
|
|
use std::hash::BuildHasher;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
@ -16,7 +18,10 @@ fn main() {
|
|
|
|
|
name: "trees".into(),
|
|
|
|
|
})
|
|
|
|
|
.add_plugins(MonologueAssetsPlugin)
|
|
|
|
|
.add_plugins(TreesDebugPlugin)
|
|
|
|
|
.add_event::<DialogEvent>()
|
|
|
|
|
.add_event::<PlantTree>()
|
|
|
|
|
.add_event::<AssignMonologue>()
|
|
|
|
|
.init_state::<DialogState>()
|
|
|
|
|
.insert_resource(ClearColor(WHITE.into()))
|
|
|
|
|
.add_systems(
|
|
|
|
|
@ -24,11 +29,12 @@ fn main() {
|
|
|
|
|
(
|
|
|
|
|
init_trees,
|
|
|
|
|
init_ui,
|
|
|
|
|
init_debug_ui,
|
|
|
|
|
load_monologues,
|
|
|
|
|
position_camera.after(setup_camera),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
// When we're done loading, plant forest
|
|
|
|
|
.add_systems(OnEnter(LoadingState::Idle), plant_forest.run_if(run_once))
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
(
|
|
|
|
|
@ -42,24 +48,18 @@ fn main() {
|
|
|
|
|
.run_if(in_state(DebuggingState::Off))
|
|
|
|
|
.run_if(in_state(DialogState::Idle))
|
|
|
|
|
.run_if(on_event::<Pointer<Click>>),
|
|
|
|
|
spawn_debug_buttons.run_if(on_event::<AssetEvent<Monologue>>),
|
|
|
|
|
handle_plant_tree.run_if(on_event::<PlantTree>),
|
|
|
|
|
assign_monologue_to_tree
|
|
|
|
|
.run_if(on_event::<AssignMonologue>)
|
|
|
|
|
.after(handle_plant_tree),
|
|
|
|
|
dialog_engine.run_if(on_event::<DialogEvent>),
|
|
|
|
|
auto_scroll.run_if(any_component_added::<DialogOption>),
|
|
|
|
|
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>),
|
|
|
|
|
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(remove_tree_monologue)
|
|
|
|
|
.add_observer(populate_tree)
|
|
|
|
|
.add_observer(show_monologue_list)
|
|
|
|
|
.add_observer(hide_monologue_list)
|
|
|
|
|
.run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -108,78 +108,6 @@ fn init_ui(mut commands: Commands) {
|
|
|
|
|
.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 button = commands
|
|
|
|
|
.spawn((
|
|
|
|
|
Name::new("Monologue Assignment Menu"),
|
|
|
|
|
Text::new("+ Monologue"),
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::Start,
|
|
|
|
|
justify_self: JustifySelf::Start,
|
|
|
|
|
min_width: Val::Px(25.0),
|
|
|
|
|
min_height: Val::Px(25.0),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
BackgroundColor(RED.into()),
|
|
|
|
|
DebuggingState::On,
|
|
|
|
|
MonologuesContainer,
|
|
|
|
|
))
|
|
|
|
|
.id();
|
|
|
|
|
commands
|
|
|
|
|
.spawn((
|
|
|
|
|
NavParent(button),
|
|
|
|
|
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()
|
|
|
|
|
},
|
|
|
|
|
BackgroundColor(BLACK.into()),
|
|
|
|
|
)).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(),
|
|
|
|
|
BackgroundColor(ORANGE.into()),
|
|
|
|
|
MonologuesList,
|
|
|
|
|
)).observe(scroll);
|
|
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
Name::new("Preview"),
|
|
|
|
|
MonologuePreview,
|
|
|
|
|
NavParent(button),
|
|
|
|
|
NavState::default(),
|
|
|
|
|
Node {
|
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
|
padding: UiRect::all(Val::Px(10.0)),
|
|
|
|
|
overflow: Overflow::scroll_y(),
|
|
|
|
|
width: Val::Percent(60.0),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
BackgroundColor(ORANGE_RED.into()),
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn hover_dialog_box_over(
|
|
|
|
|
trigger: Trigger<Pointer<Over>>,
|
|
|
|
|
mut query: Query<&mut BackgroundColor, With<DialogBox>>,
|
|
|
|
|
@ -252,14 +180,14 @@ enum DialogState {
|
|
|
|
|
None,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Start dialog, will expand later with handle to monologue asset
|
|
|
|
|
/// Start dialog
|
|
|
|
|
fn start_dialog(
|
|
|
|
|
mut click_events: EventReader<Pointer<Click>>,
|
|
|
|
|
mut dialog_events: EventWriter<DialogEvent>,
|
|
|
|
|
query: Query<&TreeMonologue, With<Tree>>,
|
|
|
|
|
) {
|
|
|
|
|
click_events.read().for_each(|event| {
|
|
|
|
|
debug!("Click event detected");
|
|
|
|
|
debug!("Click event detected in start dialog systme");
|
|
|
|
|
if let Ok(TreeMonologue(handle)) = query.get(event.target) {
|
|
|
|
|
debug!("Tree Monologue received, sending start dialog event");
|
|
|
|
|
dialog_events.write(DialogEvent::Start(event.target, handle.clone()));
|
|
|
|
|
@ -370,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(
|
|
|
|
|
trigger: Trigger<Pointer<Over>>,
|
|
|
|
|
mut query: Query<(&mut TextColor, &mut BackgroundColor)>,
|
|
|
|
|
@ -417,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(
|
|
|
|
|
state: Res<State<DialogState>>,
|
|
|
|
|
mut dialog_box: Single<&mut Visibility, With<DialogBox>>,
|
|
|
|
|
@ -447,33 +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>,
|
|
|
|
|
scripts: Query<&TreeMonologue>,
|
|
|
|
|
) {
|
|
|
|
|
out_events
|
|
|
|
|
.read()
|
|
|
|
|
.filter_map(|Pointer { target, .. }| scripts.contains(*target).then_some(*target))
|
|
|
|
|
.for_each(|_| {
|
|
|
|
|
tooltip.remove("Script");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
over_events
|
|
|
|
|
.read()
|
|
|
|
|
.filter_map(|Pointer { target, .. }| scripts.contains(*target).then_some(*target))
|
|
|
|
|
.for_each(|e| {
|
|
|
|
|
if let Ok(TreeMonologue(handle)) = scripts.get(e) {
|
|
|
|
|
match handle.path() {
|
|
|
|
|
Some(p) => tooltip.insert("Script", format!("{p}")),
|
|
|
|
|
None => tooltip.insert("Script", "???".into()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_tree_monologue(
|
|
|
|
|
trigger: Trigger<OnAdd, TreeMonologue>,
|
|
|
|
|
query: Query<&MeshMaterial3d<StandardMaterial>>,
|
|
|
|
|
@ -532,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) {
|
|
|
|
|
info!("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
|
|
|
|
|
fn load_monologues(server: ResMut<AssetServer>, mut loaded_assets: Local<Vec<Handle<Monologue>>>) {
|
|
|
|
|
*loaded_assets = include_str!("../../../assets/trees/MONOLOGUES")
|
|
|
|
|
@ -547,148 +395,23 @@ fn load_monologues(server: ResMut<AssetServer>, mut loaded_assets: Local<Vec<Han
|
|
|
|
|
.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,
|
|
|
|
|
Text::new(handle.path().unwrap().to_string().trim_prefix("trees/").trim_suffix(".mono")),
|
|
|
|
|
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
|
|
|
|
|
TreeMonologue(handle.clone()),
|
|
|
|
|
MonologuesList,
|
|
|
|
|
BackgroundColor(PINK.into()),
|
|
|
|
|
))
|
|
|
|
|
.observe(preview_monologue)
|
|
|
|
|
.observe(spawn_monologue_tree)
|
|
|
|
|
.observe(toggle_debug_button_color_over)
|
|
|
|
|
.observe(toggle_debug_button_color_out);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn toggle_debug_button_color_over(
|
|
|
|
|
trigger: Trigger<Pointer<Over>>,
|
|
|
|
|
mut buttons: Query<&mut BackgroundColor, With<Button>>,
|
|
|
|
|
) {
|
|
|
|
|
if let Ok(mut bg) = buttons.get_mut(trigger.target()) {
|
|
|
|
|
bg.0 = RED.into();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn toggle_debug_button_color_out(
|
|
|
|
|
trigger: Trigger<Pointer<Out>>,
|
|
|
|
|
mut buttons: Query<&mut BackgroundColor, With<Button>>,
|
|
|
|
|
) {
|
|
|
|
|
if let Ok(mut bg) = buttons.get_mut(trigger.target()) {
|
|
|
|
|
bg.0 = PINK.into();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 show_monologue_list(
|
|
|
|
|
trigger: Trigger<Pointer<Over>>,
|
|
|
|
|
container: Query<Entity, With<MonologuesContainer>>,
|
|
|
|
|
children: Query<&Children>,
|
|
|
|
|
mut visibility: Query<&mut Visibility>,
|
|
|
|
|
) {
|
|
|
|
|
if let Ok(root) = container.get(trigger.target()) {
|
|
|
|
|
children.iter_descendants(root).for_each(|e| {
|
|
|
|
|
*visibility.get_mut(e).unwrap() = Visibility::Inherited;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn hide_monologue_list(
|
|
|
|
|
trigger: Trigger<Pointer<Out>>,
|
|
|
|
|
container: Query<Entity, With<MonologuesContainer>>,
|
|
|
|
|
children: Query<&Children>,
|
|
|
|
|
mut visibility: Query<&mut Visibility>,
|
|
|
|
|
) {
|
|
|
|
|
if let Ok(root) = container.get(trigger.target()) {
|
|
|
|
|
children.iter_descendants(root).for_each(|e| {
|
|
|
|
|
*visibility.get_mut(e).unwrap() = Visibility::Hidden;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn spawn_monologue_tree(
|
|
|
|
|
trigger: Trigger<Pointer<Click>>,
|
|
|
|
|
tree_monologues: Query<&TreeMonologue, With<Button>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
let tree_monologue = tree_monologues.get(trigger.target()).unwrap();
|
|
|
|
|
info!("Spawning monologuing tree");
|
|
|
|
|
commands.spawn((Tree, tree_monologue.clone()));
|
|
|
|
|
}
|
|
|
|
|
#[derive(Event)]
|
|
|
|
|
struct PlantTree(Option<Handle<Monologue>>);
|
|
|
|
|
|
|
|
|
|
/// When a monologuing tree is added, give it a mesh, a material, and a position in space
|
|
|
|
|
///
|
|
|
|
|
/// TODO: This can be an `on_add` hook intead, just a little more clunky
|
|
|
|
|
fn populate_tree(
|
|
|
|
|
trigger: Trigger<OnAdd, TreeMonologue>,
|
|
|
|
|
/// Plan a tree in the world
|
|
|
|
|
/// Handles random placement, 3d model, materials, and observers
|
|
|
|
|
fn handle_plant_tree(
|
|
|
|
|
mut events: EventReader<PlantTree>,
|
|
|
|
|
mut assignments: EventWriter<AssignMonologue>,
|
|
|
|
|
trees: Query<Entity, With<Tree>>,
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
|
|
|
) {
|
|
|
|
|
if !trees.contains(trigger.target()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
events.read().for_each(|PlantTree(assignment)| {
|
|
|
|
|
debug!("Planing tree");
|
|
|
|
|
let mut tree = commands.spawn(Tree);
|
|
|
|
|
|
|
|
|
|
// Generate "random" X and Y Coordinates for this tree
|
|
|
|
|
// 1. Take the top 2 bytes
|
|
|
|
|
@ -696,7 +419,7 @@ fn populate_tree(
|
|
|
|
|
// 3. Re-interpret as i8s
|
|
|
|
|
// 4. Cast to f32
|
|
|
|
|
let transform = {
|
|
|
|
|
let n = RandomState::default().hash_one(trigger.target());
|
|
|
|
|
let n = RandomState::default().hash_one(tree.id());
|
|
|
|
|
let [a, b, ..] = n.to_be_bytes();
|
|
|
|
|
|
|
|
|
|
let x: f32 = a.cast_signed().wrapping_div(4).into();
|
|
|
|
|
@ -709,117 +432,65 @@ fn populate_tree(
|
|
|
|
|
|
|
|
|
|
let material = MeshMaterial3d(materials.add(StandardMaterial {
|
|
|
|
|
base_color_texture: Some(server.load("trees/placeholder/tree.png")),
|
|
|
|
|
base_color: WHITE.into(),
|
|
|
|
|
base_color: WHITE.with_alpha(0.9).into(),
|
|
|
|
|
alpha_mode: AlphaMode::Blend,
|
|
|
|
|
..default()
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
let mesh = Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0))));
|
|
|
|
|
|
|
|
|
|
debug!("Fleshing out monologuing tree");
|
|
|
|
|
tree.insert((mesh, material, transform));
|
|
|
|
|
|
|
|
|
|
commands
|
|
|
|
|
.entity(trigger.target())
|
|
|
|
|
.insert((mesh, material, transform))
|
|
|
|
|
.observe(delete_tree)
|
|
|
|
|
.observe(drag_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 {
|
|
|
|
|
if 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();
|
|
|
|
|
if let Some(handle) = assignment {
|
|
|
|
|
assignments.write(AssignMonologue(handle.clone()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When you pointer over the '+' make the entire menu visible
|
|
|
|
|
fn hide_menu(
|
|
|
|
|
mut nodes: Query<(Entity, &mut Visibility, &NavState), Changed<NavState>>
|
|
|
|
|
) {
|
|
|
|
|
nodes.iter_mut().for_each(|(e, 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>)>,
|
|
|
|
|
#[derive(Event, Debug)]
|
|
|
|
|
struct AssignMonologue(Handle<Monologue>);
|
|
|
|
|
|
|
|
|
|
/// Assign the given monologue to a tree
|
|
|
|
|
fn assign_monologue_to_tree(
|
|
|
|
|
mut events: EventReader<AssignMonologue>,
|
|
|
|
|
query: Query<Entity, (With<Tree>, Without<TreeMonologue>)>,
|
|
|
|
|
mut notice: ResMut<Notice>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
nodes.iter_mut().for_each(|(e, n)| {
|
|
|
|
|
if matches!(n, NavState::Closed) {
|
|
|
|
|
commands.entity(e).despawn_related::<Children>();
|
|
|
|
|
// Kinda a weird hack because query does not update
|
|
|
|
|
// If we do this inline we assign new monologues to the same first tree
|
|
|
|
|
let mut t = query.iter();
|
|
|
|
|
events.read().for_each(|event| {
|
|
|
|
|
debug!("Assigning monologue {:?}", event);
|
|
|
|
|
|
|
|
|
|
// Get a valid tree to assign an entity to
|
|
|
|
|
if let Some(tree) = t.next() {
|
|
|
|
|
// Create the TreeMonologue component
|
|
|
|
|
let monologue = TreeMonologue(event.0.clone());
|
|
|
|
|
// Insert the component to the entity
|
|
|
|
|
commands.entity(tree).insert(monologue);
|
|
|
|
|
} else if let Some(path) = event.0.path() {
|
|
|
|
|
error!("No trees avaliable for {path:?}");
|
|
|
|
|
notice.0 = format!("No trees avaliable for {path:?}");
|
|
|
|
|
} else {
|
|
|
|
|
error!("Monologue is not yet loaded!");
|
|
|
|
|
notice.0 = "Monologue is not yet loaded!".into();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
/// On startup, plant a forest (add a few trees to the game)
|
|
|
|
|
fn plant_forest(monos: Res<Assets<Monologue>>, mut e_trees: EventWriter<PlantTree>) {
|
|
|
|
|
let mut i = 10;
|
|
|
|
|
for id in monos.ids() {
|
|
|
|
|
debug!("Planting tree with monologue {:?}", id);
|
|
|
|
|
if i > 5 {
|
|
|
|
|
e_trees.write(PlantTree(Some(Handle::Weak(id))));
|
|
|
|
|
} else if i > 0 {
|
|
|
|
|
e_trees.write(PlantTree(None));
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
i -= 1;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|