From 5591c7a32888c9950b5d21d1d18a6150907d6ea7 Mon Sep 17 00:00:00 2001 From: ghostly_zsh Date: Tue, 16 Jun 2026 20:01:48 -0500 Subject: [PATCH] feat: part list & dragging ghosts --- .../unified/assets/config/ship_editor.se.toml | 5 + crates/unified/src/shared/config/mod.rs | 1 + .../unified/src/shared/config/ship_editor.rs | 12 ++ crates/unified/src/shared/plugins.rs | 2 + crates/unified/src/ship_editor/components.rs | 13 ++ crates/unified/src/ship_editor/input.rs | 88 +++++++++++++- crates/unified/src/ship_editor/mod.rs | 22 +++- crates/unified/src/ship_editor/plugins.rs | 16 +++ crates/unified/src/ship_editor/ui.rs | 115 ++++++++++++++++-- crates/unified/src/universal_entrypoint.rs | 7 +- 10 files changed, 262 insertions(+), 19 deletions(-) create mode 100644 crates/unified/assets/config/ship_editor.se.toml create mode 100644 crates/unified/src/shared/config/ship_editor.rs create mode 100644 crates/unified/src/ship_editor/components.rs diff --git a/crates/unified/assets/config/ship_editor.se.toml b/crates/unified/assets/config/ship_editor.se.toml new file mode 100644 index 0000000000000000000000000000000000000000..dbd40b35073bfe6e36410cd606968dbb8f681218 --- /dev/null +++ b/crates/unified/assets/config/ship_editor.se.toml @@ -0,0 +1,5 @@ +[part_list] +Hub = { order = 0 } +Basic_Thruster = { order = 1 } +Storage_Hub = { order = 2 } +Thruster = { order = 3 } \ No newline at end of file diff --git a/crates/unified/src/shared/config/mod.rs b/crates/unified/src/shared/config/mod.rs index df8e85bc8a445f5a001c18578dfd6f867939d591..47f40df2781d4d4596296606d7ecaa71f71f37ba 100644 --- a/crates/unified/src/shared/config/mod.rs +++ b/crates/unified/src/shared/config/mod.rs @@ -2,3 +2,4 @@ pub mod part; pub mod planet; pub mod world; pub mod recipe; +pub mod ship_editor; diff --git a/crates/unified/src/shared/config/ship_editor.rs b/crates/unified/src/shared/config/ship_editor.rs new file mode 100644 index 0000000000000000000000000000000000000000..74b4c1d2e2a90d4442c839e903d0f8bc264b743d --- /dev/null +++ b/crates/unified/src/shared/config/ship_editor.rs @@ -0,0 +1,12 @@ +use std::collections::HashMap; +use crate::prelude::*; + +#[derive(Deserialize, Asset, TypePath, Component, Serialize, Clone, Debug)] +pub struct ShipEditorConfig { + pub part_list: HashMap, +} + +#[derive(Deserialize, TypePath, Component, Serialize, Clone, Debug)] +pub struct PartIcon { + pub order: usize, +} \ No newline at end of file diff --git a/crates/unified/src/shared/plugins.rs b/crates/unified/src/shared/plugins.rs index 94951fc24ef37e0ad5c44562e0d4243d4a2d134e..fb77345c7e29f8b5543e25f953cb4282eb48ee40 100644 --- a/crates/unified/src/shared/plugins.rs +++ b/crates/unified/src/shared/plugins.rs @@ -10,6 +10,7 @@ use crate::prelude::*; use crate::shared::config::part::PartConfig; use crate::shared::config::planet::PlanetConfigCollection; use crate::shared::config::recipe::RecipesConfig; +use crate::shared::config::ship_editor::ShipEditorConfig; use crate::shared::config::world::GlobalWorldConfig; //use crate::shared::net::register_replication; use crate::shared::world_config::world_config_plugin; @@ -42,6 +43,7 @@ impl PluginGroup for SharedPluginGroup { .add(TomlAssetPlugin::::new(&["pc.toml"])) .add(TomlAssetPlugin::::new(&["part.toml"])) .add(TomlAssetPlugin::::new(&["rc.toml"])) + .add(TomlAssetPlugin::::new(&["se.toml"])) } } diff --git a/crates/unified/src/ship_editor/components.rs b/crates/unified/src/ship_editor/components.rs new file mode 100644 index 0000000000000000000000000000000000000000..c18fdc0a74553601c24a5c6e8a97c284398ee705 --- /dev/null +++ b/crates/unified/src/ship_editor/components.rs @@ -0,0 +1,13 @@ +use bevy::camera::visibility::RenderLayers; +use crate::prelude::Component; + +pub const MAIN_RENDER_LAYER: RenderLayers = RenderLayers::layer(0); +pub const GHOST_RENDER_LAYER: RenderLayers = RenderLayers::layer(1); + +#[derive(Component)] +pub struct GhostModule; + +#[derive(Component)] +pub struct MainCamera; +#[derive(Component)] +pub struct GhostCamera; \ No newline at end of file diff --git a/crates/unified/src/ship_editor/input.rs b/crates/unified/src/ship_editor/input.rs index a793d307245aa286b1936cc6f856323e68640b0b..655511e5a569df3334072694f3455e4cddac924e 100644 --- a/crates/unified/src/ship_editor/input.rs +++ b/crates/unified/src/ship_editor/input.rs @@ -1,18 +1,24 @@ use bevy::input::mouse::{MouseScrollUnit, MouseWheel}; +use bevy::input_focus::InputFocus; use bevy::prelude::*; use bevy::window::PrimaryWindow; +use crate::ship_editor::components::{GhostCamera, GhostModule, MainCamera, GHOST_RENDER_LAYER}; +use crate::ship_editor::ui::PartEntry; pub fn input_plugin(app: &mut App) { app .insert_resource(ShipEditorDrag::default()) .add_systems(Update, on_scroll) .add_systems(Update, on_click) - .add_systems(Update, drag); + .add_systems(Update, camera_drag) + .add_systems(Update, ghost_drag) + .add_systems(Update, part_button_interaction); } #[derive(Resource, Default)] struct ShipEditorDrag { is_dragging: bool, + is_holding_module: bool, init_cursor_pos: Vec2, init_camera_pos: Vec2, } @@ -52,7 +58,7 @@ fn on_scroll( fn on_click( ev: Res>, mut drag: ResMut, - camera: Single<&Transform, With>, + camera: Single<&Transform, (With, With)>, window: Single<&Window, With>, ) { let Some(cursor_pos) = window.cursor_position() else { return }; @@ -66,12 +72,12 @@ fn on_click( } } -fn drag( +fn camera_drag( drag: ResMut, - mut camera: Single<(&mut Transform, &Projection), With>, + mut camera: Single<(&mut Transform, &Projection), (With, With)>, window: Single<&Window, With>, ) { - if !drag.is_dragging { return } + if !drag.is_dragging || drag.is_holding_module { return } let Some(cursor) = window.cursor_position() else { return }; let projection = camera.1.clone(); let Projection::Orthographic(projection) = projection else { return }; @@ -79,3 +85,75 @@ fn drag( transform.translation = drag.init_camera_pos.extend(0.0) - (cursor.with_y(-cursor.y) - drag.init_cursor_pos).extend(0.0)*projection.scale; } +fn ghost_drag( + drag: Res, + window: Single<&Window, With>, + mut ghost_module: Query<&mut Transform, With>, + mut ghost_camera: Single<(&Camera, &GlobalTransform), (With, With)>, +) { + if !drag.is_dragging || !drag.is_holding_module { return } + let Some(cursor) = window.cursor_position() else { return }; + let (ghost_camera, camera_transform) = *ghost_camera; + let Ok(ghost_position) = ghost_camera.viewport_to_world_2d(camera_transform, cursor) else { + error!("Unable to convert cursor position to world space"); + return // return because this error happens when something is invalid with the camera + }; + + for mut ghost_transform in ghost_module.iter_mut() { + ghost_transform.translation = ghost_position.extend(0.0); + } +} + +fn part_button_interaction( + mouse_ev: Res>, + part_button_query: Query<(Entity, &PartEntry, &Interaction), (With