From ad2517ff63342f5d076ebc7b72f83a4b68b9b19f Mon Sep 17 00:00:00 2001 From: ghostly_zsh Date: Tue, 23 Dec 2025 23:03:05 -0600 Subject: [PATCH] feat: crafting ui start --- .../assets/config/parts/hearty.part.toml | 5 +- crates/unified/src/client/crafting/mod.rs | 1 + crates/unified/src/client/crafting/ui.rs | 70 +++++++++++++++++++ crates/unified/src/client/mod.rs | 3 + crates/unified/src/client/parts.rs | 4 +- crates/unified/src/client/rendering/mod.rs | 4 +- crates/unified/src/client_plugins.rs | 4 +- crates/unified/src/config/part.rs | 5 ++ crates/unified/src/ecs.rs | 5 ++ crates/unified/src/server/part.rs | 13 +++- crates/unified/src/shared_plugins.rs | 3 +- 11 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 crates/unified/src/client/crafting/mod.rs create mode 100644 crates/unified/src/client/crafting/ui.rs diff --git a/crates/unified/assets/config/parts/hearty.part.toml b/crates/unified/assets/config/parts/hearty.part.toml index bc67795ff772eceb635804cbd7744b2923b22771..960b49d2c423e2af339b6fa6f2ba6343128c770e 100644 --- a/crates/unified/assets/config/parts/hearty.part.toml +++ b/crates/unified/assets/config/parts/hearty.part.toml @@ -47,4 +47,7 @@ snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 } [[joint]] id = "Left" target = { translation = [ -55.0, 0.0, 0.0 ], rotation = -270.0 } -snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 } \ No newline at end of file +snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 } + +[crafting] +can_craft = true diff --git a/crates/unified/src/client/crafting/mod.rs b/crates/unified/src/client/crafting/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..6bae95df97a86ba99733b3ac0a40af71a8bdfa8b --- /dev/null +++ b/crates/unified/src/client/crafting/mod.rs @@ -0,0 +1 @@ +pub mod ui; diff --git a/crates/unified/src/client/crafting/ui.rs b/crates/unified/src/client/crafting/ui.rs new file mode 100644 index 0000000000000000000000000000000000000000..38829901c4f5d94c5ad3a0bd83f5a0eeede6d543 --- /dev/null +++ b/crates/unified/src/client/crafting/ui.rs @@ -0,0 +1,70 @@ +use bevy::{input_focus::{AutoFocus, InputFocus}, ui::RelativeCursorPosition}; + +use crate::{attachment::PartInShip, client::colors, ecs::{CanCraft, CraftingUi, MainCamera, Me}, prelude::*}; + +pub fn crafting_ui_plugin(app: &mut App) { + app.add_systems(Update, close_ui); +} + +pub fn open_crafting_ui( + ev: On>, + crafting_parts: Query<(Entity, &Transform), (With, With)>, + hearty: Query<(Entity, &Transform), (With, With)>, + camera: Single<(Entity, &Camera, &GlobalTransform), (With, Without)>, + commands: Commands, +) { + if matches!(ev.button, PointerButton::Secondary) { + let (entity, transform) = if let Ok(part) = crafting_parts.get(ev.entity) { + part + } else if let Ok(part) = hearty.get(ev.entity) { + part + } else { + return + }; + // we have our crafting entity! + // now make the ui + setup_ui(transform, commands, camera); + } +} + +fn setup_ui( + parent_transform: &Transform, + mut commands: Commands, + camera: Single<(Entity, &Camera, &GlobalTransform), (With, Without)>, +) { + let parent_pos = camera.1.world_to_viewport(camera.2, parent_transform.translation).unwrap(); + let entity = commands.spawn(( + UiTargetCamera(camera.0), + Node { + position_type: PositionType::Absolute, + left: Val::Px(parent_pos.x), + top: Val::Px(parent_pos.y), + width: Val::Px(100.0), + height: Val::Px(100.0), + ..default() + }, + AutoFocus, + CraftingUi, + BackgroundColor(colors::MANTLE), + RelativeCursorPosition::default(), + )); +} + +pub fn close_ui( + buttons: Res>, + crafting_ui: Query<(Entity, &RelativeCursorPosition), With>, + mut commands: Commands, +) { + if crafting_ui.is_empty() { return } + if !buttons.any_just_released([MouseButton::Left, MouseButton::Right]) { return } + + for (_, cursor_pos) in &crafting_ui { + if cursor_pos.cursor_over { + return; + } + } + + for (entity, _) in &crafting_ui { + commands.entity(entity).despawn(); + } +} diff --git a/crates/unified/src/client/mod.rs b/crates/unified/src/client/mod.rs index fab47d082649c1923eb761e500118acd5d85b978..5ca2be0d2b7087cc707477b3b5bc6eff3b25f28f 100644 --- a/crates/unified/src/client/mod.rs +++ b/crates/unified/src/client/mod.rs @@ -1,3 +1,4 @@ +use crate::client::crafting::ui::crafting_ui_plugin; use crate::client::key_input::key_input_plugin; use crate::client::parts::parts_plugin; use crate::client::planet::indicators::indicators_plugin; @@ -28,6 +29,7 @@ pub mod ship; pub mod rendering; pub mod input; pub mod starguide; +pub mod crafting; pub struct ClientPlugin { pub server: Option, @@ -61,6 +63,7 @@ impl Plugin for ClientPlugin { .add_plugins(starguide_init_plugin) .add_plugins(starguide_input_plugin) .add_plugins(starguide_orbit_plugin) + .add_plugins(crafting_ui_plugin) .insert_state(GameplayState::Main) .insert_resource(DebugPickingMode::Disabled); diff --git a/crates/unified/src/client/parts.rs b/crates/unified/src/client/parts.rs index db399948f66bfb88e5c8f4ce908d13c5d9684afd..7733291833f6b6583c90a8a8c7c29fd2970f9767 100644 --- a/crates/unified/src/client/parts.rs +++ b/crates/unified/src/client/parts.rs @@ -1,6 +1,7 @@ use std::f32::consts::PI; use crate::attachment::{Joint, JointOf, Joints, PartInShip, Peer, SnapOf, SnapOfJoint}; +use crate::client::crafting::ui::open_crafting_ui; use crate::ecs::Me; use crate::client::colors::GREEN; use crate::ecs::{DragRequestEvent, Part, MAIN_LAYER}; @@ -45,7 +46,8 @@ fn handle_incoming_parts( .insert(MAIN_LAYER) .insert(sprite) .insert(Pickable::default()) - .observe(on_part_click); + .observe(on_part_click) + .observe(open_crafting_ui); } } fn handle_updated_parts( diff --git a/crates/unified/src/client/rendering/mod.rs b/crates/unified/src/client/rendering/mod.rs index ded04c4d6cc4ec4cbe7fd2d2be2c7e6e5ff701bf..24c94b368770b5cda16c5ff9d621aae75dfb883b 100644 --- a/crates/unified/src/client/rendering/mod.rs +++ b/crates/unified/src/client/rendering/mod.rs @@ -2,6 +2,7 @@ use bevy::anti_alias::fxaa::Fxaa; use bevy::app::{App, Startup}; use bevy::core_pipeline::tonemapping::DebandDither; use bevy::post_process::bloom::Bloom; +use crate::client::crafting::ui::close_ui; use crate::ecs::{GameplayState, MAIN_LAYER, MainCamera, Me, STARGUIDE_LAYER, StarguideGizmos}; use crate::prelude::*; @@ -23,7 +24,8 @@ pub fn setup_graphics(mut config_store: ResMut, mut commands: .insert(Bloom::default()) .insert(DebandDither::Enabled) .insert(Fxaa::default()) - .insert(MainCamera); + .insert(MainCamera) + .insert(SpritePickingCamera); let (gizmo_config, _) = config_store.config_mut::(); gizmo_config.render_layers = STARGUIDE_LAYER; gizmo_config.depth_bias = 1.0; diff --git a/crates/unified/src/client_plugins.rs b/crates/unified/src/client_plugins.rs index 17e2cfd8fe132157ed5b841e7b3e1d8221aa636a..74e7cadbb9b07e70e54cf02392603ce83a2e6783 100644 --- a/crates/unified/src/client_plugins.rs +++ b/crates/unified/src/client_plugins.rs @@ -4,6 +4,7 @@ use aeronet_websocket::client::WebSocketClientPlugin; use bevy::app::{PluginGroup, PluginGroupBuilder}; use bevy::dev_tools::picking_debug::DebugPickingPlugin; use bevy::ecs::schedule::ScheduleLabel; +use bevy::input_focus::InputDispatchPlugin; use crate::prelude::*; use bevy::ui::UiPlugin; use leafwing_input_manager::plugin::InputManagerPlugin; @@ -22,9 +23,10 @@ impl PluginGroup for ClientPluginGroup { server: self.server, }) .add(UiPlugin) + .add(InputDispatchPlugin) .add(InputManagerPlugin::::default()) } } #[derive(ScheduleLabel, Clone, Eq, Debug, Hash, PartialEq)] -pub struct DontRunSchedule; \ No newline at end of file +pub struct DontRunSchedule; diff --git a/crates/unified/src/config/part.rs b/crates/unified/src/config/part.rs index 75f03c16904f1ab623b59961a757d19157fbe913..da3181ece5fc8fd43c8f318489fa2e5a5b52ed74 100644 --- a/crates/unified/src/config/part.rs +++ b/crates/unified/src/config/part.rs @@ -13,6 +13,7 @@ pub struct PartConfig { #[serde(default)] #[serde(rename = "joint")] pub joints: Vec, + pub crafting: Option, } #[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)] pub struct PartPartConfig { @@ -53,3 +54,7 @@ impl From for Transform { } } } +#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)] +pub struct CraftingConfig { + pub can_craft: bool, +} diff --git a/crates/unified/src/ecs.rs b/crates/unified/src/ecs.rs index 2de2f3eff7a1b2f561e204786ffa600064a82f59..490d075b42621860d4025cabf08c770d1c854b16 100644 --- a/crates/unified/src/ecs.rs +++ b/crates/unified/src/ecs.rs @@ -108,3 +108,8 @@ pub struct Hi { #[entities] pub you_are: Entity } + +#[derive(Component, Serialize, Deserialize, Debug)] +pub struct CanCraft; +#[derive(Component, Serialize, Deserialize, Debug)] +pub struct CraftingUi; diff --git a/crates/unified/src/server/part.rs b/crates/unified/src/server/part.rs index 79606d214bc3d63e7867c5d2e136aab6e27e9377..6659951b1654ae07e91bf11bf043d1ba58c3dfa6 100644 --- a/crates/unified/src/server/part.rs +++ b/crates/unified/src/server/part.rs @@ -1,6 +1,6 @@ use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoint}; -use crate::config::part::{JointConfig, PartConfig}; -use crate::ecs::{Part, PartHandle}; +use crate::config::part::{CraftingConfig, JointConfig, PartConfig}; +use crate::ecs::{CanCraft, Part, PartHandle}; use crate::prelude::*; use bevy_replicon::prelude::Replicated; use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart}; @@ -32,6 +32,9 @@ fn handle_ready_parts( .entity(entity) .insert(calculate_bundle(strong_config, &loading_part.0)) .remove::(); + if let Some(ref crafting_config) = strong_config.crafting { + crafting_bundle(&mut commands.entity(entity), &crafting_config); + } spawn_joints(strong_config, entity, commands.reborrow()); spawn_thrusters(strong_config, entity, commands.reborrow()); } @@ -129,6 +132,11 @@ fn calculate_bundle(config: &PartConfig, handle: &Handle) -> impl Bu mass, ) } +fn crafting_bundle(entity: &mut EntityCommands, config: &CraftingConfig) { + if config.can_craft { + entity.insert(CanCraft); + } +} fn spawn_joint_bundle(joint: &JointConfig, part: &PartConfig, parent: &Entity) -> impl Bundle { let j_comp = Joint { id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()), @@ -169,3 +177,4 @@ fn spawn_thrusters(config: &PartConfig, part: Entity, mut commands: Commands) { }); } } + diff --git a/crates/unified/src/shared_plugins.rs b/crates/unified/src/shared_plugins.rs index 67fa48e6edb7e02d9d4305db68f274ab4196e067..21b3e80997167f6cf8ee350f6bd3b75f90eb8533 100644 --- a/crates/unified/src/shared_plugins.rs +++ b/crates/unified/src/shared_plugins.rs @@ -1,6 +1,6 @@ use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint}; use crate::config::planet::{Planet, PlanetConfigCollection}; -use crate::ecs::{DragRequestEvent, Hi, Part, Particles, Player, PlayerStorage}; +use crate::ecs::{CanCraft, DragRequestEvent, Hi, Part, Particles, Player, PlayerStorage}; use bevy::app::{App, PluginGroup, PluginGroupBuilder}; use bevy_common_assets::toml::TomlAssetPlugin; use crate::prelude::*; @@ -52,6 +52,7 @@ pub fn register_everything(app: &mut App) { app.replicate::(); app.replicate::(); app.replicate::(); + app.replicate::(); } fn physics_setup_plugin(app: &mut App) {