use bevy::color::palettes::css::{ORANGE, RED};
use crate::client::Me;
use crate::ecs::{CursorWorldCoordinates, DragRequestEvent, Part};
use bevy::prelude::*;
use bevy_rapier2d::dynamics::MassProperties;
use bevy_rapier2d::prelude::AdditionalMassProperties;
use crate::attachment::{JointOf, PartInShip, SnapOf, SnapOfJoint};
use crate::client::colors::GREEN;
use crate::client::key_input::AttachmentDebugRes;
pub fn parts_plugin(app: &mut App) {
app.insert_resource(DragResource(None));
app.add_systems(Update, (handle_incoming_parts, handle_updated_parts, update_drag_ghosts));
app.add_observer(on_part_release);
}
fn handle_incoming_parts(
mut commands: Commands,
new_parts: Query<(Entity, &Part), Added<Part>>,
asset_server: Res<AssetServer>,
) {
for (new_entity, new_part) in new_parts.iter() {
let mut sprite = Sprite::from_image(asset_server.load(&new_part.strong_config.part.sprite_disconnected));
sprite.custom_size = Some(Vec2::new(new_part.strong_config.physics.width, new_part.strong_config.physics.height));
commands
.entity(new_entity)
.insert(sprite)
.insert(AdditionalMassProperties::MassProperties(MassProperties {
local_center_of_mass: Vec2::ZERO,
mass: new_part.strong_config.physics.mass,
principal_inertia: 7.5,
}))
.insert(Pickable::default())
.observe(on_part_click);
}
}
fn handle_updated_parts(
mut commands: Commands,
updated_parts: Query<(Entity, &Part), Changed<Part>>,
asset_server: Res<AssetServer>,
) {
for (updated_entity, updated_part) in updated_parts.iter() {
let mut sprite = Sprite::from_image(asset_server.load(&updated_part.strong_config.part.sprite_disconnected));
sprite.custom_size = Some(Vec2::new(updated_part.strong_config.physics.width, updated_part.strong_config.physics.height));
commands
.entity(updated_entity)
.remove::<Sprite>()
.remove::<AdditionalMassProperties>()
.insert(sprite)
.insert(AdditionalMassProperties::MassProperties(MassProperties {
local_center_of_mass: Vec2::ZERO,
mass: updated_part.strong_config.physics.mass,
principal_inertia: 7.5,
}));
}
}
#[derive(Resource)]
struct DragResource(Option<Entity>);
#[derive(Component)]
struct DragGhost;
fn on_part_click(
ev: Trigger<Pointer<Pressed>>,
sprites: Query<(&Sprite, &Transform), Without<Me>>,
mut drag: ResMut<DragResource>,
mut commands: Commands
) {
if ev.button != PointerButton::Primary {
return;
}
let Ok(sprite) = sprites.get(ev.target()) else {
return;
};
let mut s = sprite.0.clone();
s.color = Color::srgba(0.7, 0.7, 0.7, 1.0);
commands.spawn((
DragGhost,
sprite.1.clone(),
s
));
drag.0 = Some(ev.target());
}
fn on_part_release(
ev: Trigger<Pointer<Released>>,
mut drag: ResMut<DragResource>,
mut events: EventWriter<DragRequestEvent>,
cursor: Res<CursorWorldCoordinates>,
mut commands: Commands,
ghosts: Query<Entity, With<DragGhost>>,
) {
if ev.button != PointerButton::Primary {
return;
}
if let Some(e) = drag.0 {
for ghost in &ghosts {
commands.entity(ghost).despawn();
}
if let Some(c) = cursor.0 {
debug!(?e, ?c, "sending drag request");
events.write(DragRequestEvent(e, c));
}
}
drag.0 = None;
}
fn update_drag_ghosts(
mut ghost: Single<&mut Transform, (With<DragGhost>, Without<SnapOf>, Without<JointOf>, Without<Part>)>,
cursor: Res<CursorWorldCoordinates>,
snaps: Query<(&Transform, &SnapOfJoint, &SnapOf)>,
joints: Query<(&Transform, &JointOf)>,
parts: Query<&GlobalTransform, With<Part>>,
debug: Res<AttachmentDebugRes>,
mut gizmos: Gizmos,
) {
let Some(cursor) = cursor.0 else { return };
const CUTOFF: f32 = 25.0; // px
let mut best_distance = f32::INFINITY;
let mut best_target = Transform::from_xyz(cursor.x, cursor.y, 0.0);
for (snap_local_transform, snap_joint, snap_part) in &snaps {
let Ok(parent_position) = parts.get(snap_part.0) else { continue; };
let snap_global_translation = parent_position.transform_point(snap_local_transform.translation).xy();
let distance_to_cursor = cursor.distance(snap_global_translation);
if distance_to_cursor > best_distance {
if debug.0 {gizmos.circle_2d(snap_global_translation, 3.0, RED); }
continue;
}
if distance_to_cursor > CUTOFF {
if debug.0 {gizmos.circle_2d(snap_global_translation, 3.0, ORANGE); }
continue;
}
if debug.0 { gizmos.circle_2d(snap_global_translation, 3.0, GREEN); }
let Ok((offset, parent)) = joints.get(snap_joint.0) else { continue; };
let Ok(parent_pos) = parts.get(parent.0) else { continue; };
let joint_target = parent_pos.transform_point(offset.translation);
if debug.0 { gizmos.circle_2d(joint_target.xy(), 3.0, GREEN); }
let target_transform = Transform {
translation: joint_target,
rotation: parent_position.rotation().mul_quat(offset.rotation),
scale: offset.scale,
};
best_distance = distance_to_cursor;
best_target = target_transform;
}
**ghost = best_target;
}