feat: Split tool type and tool struct, added tool element selection

This commit is contained in:
Rendo 2026-08-05 18:54:33 +05:00
commit 2ae8f20e85
2 changed files with 117 additions and 48 deletions

View file

@ -1,3 +1,5 @@
use std::fmt::Display;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::picking::backend::ray::RayMap; use bevy::picking::backend::ray::RayMap;
@ -7,17 +9,41 @@ pub struct EditorPlugin;
impl Plugin for EditorPlugin { impl Plugin for EditorPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<EditorTools>() app.init_resource::<EditorTool>()
.add_observer(apply_tool) .add_observer(apply_tool)
.add_systems(Update, calculate_tools); .add_systems(Update, calculate_tools);
} }
} }
#[derive(Default, Clone, PartialEq, Eq,Resource)] #[derive(Default, Clone, PartialEq, Eq)]
pub enum EditorTools { pub enum Confirmation<T> {
#[default] #[default]
None, None,
Paint{ position: UVec3, block: GridElement }, Suggestion(T),
Confirmed(T)
}
#[derive(Default, Clone, PartialEq, Eq,Resource)]
pub struct EditorTool {
pub block: GridElement,
pub kind: ToolType
}
#[derive(Default, Clone, PartialEq, Eq)]
pub enum ToolType {
#[default]
None,
Paint{ position: Option<UVec3> },
//Box { first_corner: Confirmation<UVec3>, second_corner: Option<UVec3> }
}
impl Display for ToolType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}",match self {
ToolType::None => "None",
ToolType::Paint { position: _ } => "Paint"
})
}
} }
@ -25,54 +51,56 @@ pub enum EditorTools {
pub struct ViewCube; pub struct ViewCube;
#[derive(Event)] #[derive(Event)]
pub struct ApplyTool(EditorTools); pub struct ApplyTool{
pub tool: EditorTool
}
impl ApplyTool {
pub fn new(tool: EditorTool) -> Self {
Self { tool }
}
}
pub fn calculate_tools( pub fn calculate_tools(
mut commands: Commands, mut commands: Commands,
mouse_button: Res<ButtonInput<MouseButton>>, mouse_button: Res<ButtonInput<MouseButton>>,
ray_map: Res<RayMap>, ray_map: Res<RayMap>,
mut raycast: MeshRayCast, mut raycast: MeshRayCast,
mut tool: ResMut<EditorTools>, mut tool: ResMut<EditorTool>,
mut gizmos: Gizmos,
grid: Res<Grid> grid: Res<Grid>
) { ) {
let new_tool: Option<EditorTools> = match tool.clone() { let tool_change: ToolType = match tool.kind {
EditorTools::None => None, ToolType::None => ToolType::None,
EditorTools::Paint{position: _, block} => { ToolType::Paint { position: _ } => {
let mut new_position: Option<UVec3> = None; let mut new_position: Option<UVec3> = None;
for (_, ray) in ray_map.iter() { for (_, ray) in ray_map.iter() {
let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;}; let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;};
let converted_position = (hit.point + 0.51 * hit.normal.normalize()).round();
gizmos.sphere(converted_position, 0.5, Color::BLACK); let placement_position = if let GridElement::Air = tool.block { hit.point - 0.51 * hit.normal.normalize() } else { hit.point + 0.51 * hit.normal.normalize() }.round();
new_position = Some(converted_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE));
new_position = Some(placement_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE));
} }
new_position.map(|pos|{ ToolType::Paint { position: new_position }
EditorTools::Paint { position: pos , block }}
)
}, },
}; };
if let Some(new_tool) = new_tool {
*tool = new_tool; tool.kind = tool_change;
}
if mouse_button.just_pressed(MouseButton::Left) { if mouse_button.just_pressed(MouseButton::Left) {
match tool.clone() { commands.trigger(ApplyTool::new(tool.clone()));
EditorTools::None => {},
EditorTools::Paint { position, block } => {
commands.trigger(ApplyTool(EditorTools::Paint { position, block }));
},
}
} }
} }
pub fn apply_tool(params: On<ApplyTool>, mut grid: ResMut<Grid>) { pub fn apply_tool(params: On<ApplyTool>, mut grid: ResMut<Grid>) {
match params.0 { match params.tool.kind {
EditorTools::None => {}, ToolType::None => {},
EditorTools::Paint { position, block } => { ToolType::Paint { position } => {
let Some(index) = grid.indexify(position) else { let Some(position) = position else { return; };
error!("Grid position {position} is incorrect"); let index = grid.indexify(position).unwrap();
return;}; grid.data[index] = params.tool.block;
grid.data[index] = block;
}, },
} }
} }

View file

@ -2,9 +2,10 @@ use bevy::feathers::{FeathersPlugins, containers::*, controls::*, dark_theme::cr
use bevy::ui_widgets::{Activate}; use bevy::ui_widgets::{Activate};
use bevy::prelude::*; use bevy::prelude::*;
use crate::MAX_FLUID_AMOUNT;
use crate::grid::{Grid, GridElement}; use crate::grid::{Grid, GridElement};
use crate::simulation::SimulationStatus; use crate::simulation::SimulationStatus;
use crate::view::editor::EditorTools; use crate::view::editor::{EditorTool,ToolType};
pub struct UIPlugin; pub struct UIPlugin;
@ -56,7 +57,8 @@ fn ui() -> impl Scene {
} }
Children [ Children [
debug_column(), debug_column(),
editor_column() editor_tools_column(),
editor_elements_column(),
] ]
} }
} }
@ -84,7 +86,7 @@ fn debug_column() -> impl Scene {
} }
} }
fn editor_column() -> impl Scene { fn editor_tools_column() -> impl Scene {
bsn! { bsn! {
pane() pane()
pane_header() pane_header()
@ -102,24 +104,63 @@ fn editor_column() -> impl Scene {
Children [ Children [
label("Tools") label("Tools")
ToolLabel, ToolLabel,
@FeathersButton { tool_button(ToolType::None),
@caption: bsn!{label("None")} tool_button(ToolType::Paint{position: None})
}
on(|_activate: On<Activate>, mut tool: ResMut<EditorTools>, mut label: Single<&mut Text, With<ToolLabel>>| {
*tool = EditorTools::None;
label.0 = "Current tool: None".to_string();
}),
@FeathersButton {
@caption: bsn!{label("Paint")}
}
on(|_activate: On<Activate>, mut tool: ResMut<EditorTools>, mut label: Single<&mut Text, With<ToolLabel>>| {
*tool = EditorTools::Paint { position: UVec3::default(), block: GridElement::Water { amount: 1000 } };
label.0 = "Current tool: Paint".to_string();
}),
] ]
} }
} }
fn editor_elements_column() -> impl Scene {
bsn! {
pane()
pane_header()
pane_body()
Node {
display: Display::Flex,
flex_direction: FlexDirection::Column,
align_items: AlignItems::Stretch,
justify_content: JustifyContent::Start,
padding: px(8),
row_gap: px(8),
width: px(256),
min_width: px(256),
}
Children [
label("Elements"),
element_button(GridElement::Air),
element_button(GridElement::Solid),
element_button(GridElement::Drain),
element_button(GridElement::Water { amount: MAX_FLUID_AMOUNT }),
element_button(GridElement::Source { source_amount: MAX_FLUID_AMOUNT })
]
}
}
fn tool_button(kind: ToolType) -> impl Scene {
bsn! {
@FeathersButton {
@caption: bsn!{
label(kind.to_string())
}
}
on(move |_activate: On<Activate>, mut tool: ResMut<EditorTool>, mut label: Single<&mut Text, With<ToolLabel>>| {
tool.kind = kind.clone();
label.0 = format!("Current tool: {}",kind.to_string());
})
}
}
fn element_button(element: GridElement) -> impl Scene {
bsn! {
@FeathersButton {
@caption: bsn!{label(element.to_string())}
}
on(move |_activate: On<Activate>, mut tool: ResMut<EditorTool>| {
tool.block = element.clone()
})
}
}
fn water_amount_label() -> impl Scene { fn water_amount_label() -> impl Scene {
bsn! { bsn! {
Node { Node {