@@ 1,10 1,15 @@
+use std::f64::consts::PI;
+use avian2d::physics_transform::{Position, Rotation};
+use avian2d::prelude::{AngularVelocity, LinearVelocity};
+use bevy::camera::visibility::RenderLayers;
use bevy::input::mouse::{MouseScrollUnit, MouseWheel};
use bevy::input_focus::InputFocus;
use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use crate::client::components::Me;
-use crate::shared::attachment::{JointOf, PartInShip, Peer, SnapOf, SnapOfJoint};
-use crate::ship_editor::components::{GhostCamera, GhostModule, MainCamera, Part, GHOST_RENDER_LAYER};
+use crate::shared::attachment::{Joint, JointOf, Joints, PartInShip, Peer, SnapOf, SnapOfJoint};
+use crate::shared::ecs::MAIN_LAYER;
+use crate::ship_editor::components::{GhostCamera, GhostModule, MainCamera, Part, PartConfigHolder, SpawnPartRequest, GHOST_RENDER_LAYER};
use crate::ship_editor::spawn_joints;
use crate::ship_editor::ui::PartEntry;
@@ 24,6 29,8 @@ struct ShipEditorDrag {
is_holding_module: bool,
init_cursor_pos: Vec2,
init_camera_pos: Vec2,
+ target: Option<Entity>,
+ peer: Option<Entity>,
}
fn on_scroll(
@@ 71,6 78,21 @@ fn on_click(
mut drag: ResMut<ShipEditorDrag>,
camera: Single<&Transform, (With<Camera2d>, With<MainCamera>)>,
window: Single<&Window, With<PrimaryWindow>>,
+ mut ghost_module: Query<(Entity, &mut Transform, &mut Sprite, &Joints), (With<GhostModule>, Without<MainCamera>)>,
+ snaps: Query<(&SnapOf, &SnapOfJoint)>,
+ joints: Query<(&Joint, &JointOf, &Transform, Option<&Peer>, Entity), (Without<GhostModule>, Without<MainCamera>)>,
+ mut parts: Query<
+ (
+ &mut Transform,
+ Option<&PartInShip>,
+ Entity,
+ &Joints,
+ &Part,
+ ),
+ (Without<Joint>, Without<GhostModule>, Without<MainCamera>),
+ >,
+ asset_server: Res<AssetServer>,
+ mut commands: Commands,
) {
let Some(cursor_pos) = window.cursor_position() else { return };
if ev.just_pressed(MouseButton::Left) {
@@ 80,6 102,63 @@ fn on_click(
}
if ev.just_released(MouseButton::Left) {
drag.is_dragging = false;
+
+ if let Some(snap_target) = drag.target && let Some(peer_snap) = drag.peer {
+ let Ok((ghost_entity, mut ghost_transform, mut ghost_sprite, ghost_joints)) = ghost_module.single_mut() else { return };
+ let Ok((target_snap_part, target_snap_joint)) = snaps.get(snap_target) else {
+ return;
+ };
+ let Ok((source_snap_part, source_snap_joint)) = snaps.get(peer_snap) else {
+ return;
+ };
+
+ let Ok((target_jt, target_jt_of, target_jt_xform, target_jt_peer, target_jt_id)) = joints.get(target_snap_joint.0) else {
+ return;
+ };
+ let Ok((source_jt, source_jt_of, _source_jt_xform, source_jt_peer, source_jt_id)) = joints.get(source_snap_joint.0) else {
+ return;
+ };
+
+ if target_jt_peer.is_some() {
+ warn!("drag request: cannot attach to a joint that already has a peer, ignoring request");
+ return;
+ }
+ if source_jt_peer.is_some() {
+ warn!("drag request: dragging from a part that is already attached is currently not supported, ignoring request");
+ return;
+ }
+
+ let Ok((target_xform, target_in_ship, target_entity, _, _)) = parts.get(target_jt_of.0) else {
+ return;
+ };
+
+ // attached (housing)
+ /*let Ok((_, _, source_entity, source_joints, _)) = parts.get(source_jt_of.0) else {
+ return;
+ };*/
+
+ commands.entity(source_jt_id).insert(Peer {
+ peer_joint_entity_id: target_jt_id,
+ physics_joint: Entity::PLACEHOLDER, // reusing components and physics joint is not used in the ship editor
+ });
+ commands.entity(target_jt_id).insert(Peer {
+ peer_joint_entity_id: source_jt_id,
+ physics_joint: Entity::PLACEHOLDER,
+ });
+ let target_position = target_xform.mul_transform(*target_jt_xform);
+ ghost_transform.translation = target_position.translation;
+ ghost_transform.rotation = target_position.rotation
+ * source_jt.transform.rotation.inverse()
+ * Quat::from_rotation_z(PI as f32);
+
+ debug!("spawning part");
+ ghost_sprite.color = Color::srgb(1.0, 1.0, 1.0);
+ commands.entity(ghost_entity)
+ .insert(Part)
+ .insert(MAIN_LAYER)
+ .remove::<RenderLayers>()
+ .remove::<GhostModule>();
+ }
}
}
@@ 100,13 179,13 @@ fn camera_drag(
ghost_transform.translation = main_transform.translation;
}
fn ghost_drag(
- drag: Res<ShipEditorDrag>,
+ mut drag: ResMut<ShipEditorDrag>,
window: Single<&Window, With<PrimaryWindow>>,
mut ghost_module: Single<(Entity, &mut Transform), With<GhostModule>>,
mut main_camera: Single<(&Camera, &GlobalTransform), (With<Camera2d>, With<MainCamera>)>,
mut ghost_camera: Single<(&Camera, &GlobalTransform), (With<Camera2d>, With<GhostCamera>)>,
snaps: Query<(&Transform, &SnapOfJoint, &SnapOf, Entity), Without<GhostModule>>,
- joints: Query<(&Transform, &JointOf, Entity), Without<GhostModule>>,
+ joints: Query<(&Transform, &JointOf, Entity), (Without<Peer>, Without<GhostModule>)>,
parts: Query<&GlobalTransform, (Or<(With<Part>, With<GhostModule>)>)>,
) {
const SNAP_CUTOFF: f32 = 25.0;
@@ 124,8 203,10 @@ fn ghost_drag(
return
};
- let mut closest_snap = f32::INFINITY;
+ let mut closest_distance = f32::INFINITY;
let mut closest_snap_transform = None;
+ let mut closest_snap = None;
+ let mut closest_ghost_snap = None;
for (snap_local_transform, snap_joint, snap_part, snap_id) in snaps {
if snap_part.0 == ghost_entity { continue }
@@ 135,7 216,7 @@ fn ghost_drag(
let distance_to_cursor = global_cursor_position.distance(snap_global_translation.translation().xy());
- if distance_to_cursor > closest_snap {
+ if distance_to_cursor > closest_distance {
continue;
}
if distance_to_cursor > SNAP_CUTOFF {
@@ 185,14 266,18 @@ fn ghost_drag(
target_transform.rotation = part_transform.rotation()
* (offset.rotation * closest_joint_transform.unwrap().rotation.inverse())
* Quat::from_rotation_z(180.0f32.to_radians());
- closest_snap = distance_to_cursor;
+ closest_distance = distance_to_cursor;
closest_snap_transform = Some(target_transform);
+ closest_snap = Some(snap_id);
+ closest_ghost_snap = closest_peer_snap;
}
if let Some(transform) = closest_snap_transform {
*ghost_transform = transform;
} else {
ghost_transform.translation = ghost_position.extend(0.0);
}
+ drag.target = closest_snap;
+ drag.peer = closest_ghost_snap;
}
fn part_button_interaction(
@@ 213,16 298,20 @@ fn part_button_interaction(
Interaction::None => {
if !mouse_ev.pressed(MouseButton::Left) {
drag.is_holding_module = false;
- for ghost_entity in ghost_part_query.iter() {
- commands.entity(ghost_entity).despawn();
+ if drag.target.is_none() {
+ for ghost_entity in ghost_part_query.iter() {
+ commands.entity(ghost_entity).despawn();
+ }
}
}
}
Interaction::Hovered => {
if !mouse_ev.pressed(MouseButton::Left) {
drag.is_holding_module = false;
- for ghost_entity in ghost_part_query.iter() {
- commands.entity(ghost_entity).despawn();
+ if drag.target.is_none() {
+ for ghost_entity in ghost_part_query.iter() {
+ commands.entity(ghost_entity).despawn();
+ }
}
}
}
@@ 242,6 331,7 @@ fn part_button_interaction(
sprite,
Transform::from_translation(ghost_position.extend(0.0)),
GhostModule,
+ PartConfigHolder(part_entry.0.clone()),
GHOST_RENDER_LAYER,
));
spawn_joints(&part_entry.0, entity.id(), commands.reborrow());
@@ 9,7 9,7 @@ use crate::prelude::*;
use crate::shared::attachment::{Joint, JointId, JointOf, SnapOf, SnapOfJoint};
use crate::shared::config::part::{JointConfig, PartConfig};
use crate::shared::config::ship_editor::ShipEditorConfig;
-use crate::ship_editor::components::{GhostCamera, MainCamera, Part, PlayerPartRequest, ShipEditorConfigHolder, SpawnPartRequest, GHOST_RENDER_LAYER, MAIN_RENDER_LAYER};
+use crate::ship_editor::components::{GhostCamera, MainCamera, Part, PartConfigHolder, PlayerPartRequest, ShipEditorConfigHolder, SpawnPartRequest, GHOST_RENDER_LAYER, MAIN_RENDER_LAYER};
use crate::ship_editor::input::input_plugin;
use crate::ship_editor::ui::{ui_plugin, PendingPart};
@@ 89,7 89,8 @@ fn spawn_parts(
commands.entity(entity)
.insert(sprite)
- .insert(Part(strong_part_config.clone()))
+ .insert(Part)
+ .insert(PartConfigHolder(strong_part_config.clone()))
.remove::<SpawnPartRequest>();
debug!("spawned part");
spawn_joints(strong_part_config, entity, commands.reborrow());