~starkingdoms/starkingdoms

50606a2dd2def4d49876eb5c3abf16b85ba06280 — core 5 months ago c8da57e
feat: part rotation during drag ghosting with Q/E
M crates/unified/src/attachment.rs => crates/unified/src/attachment.rs +8 -0
@@ 1,3 1,4 @@
use std::ops::Deref;
use bevy::ecs::entity::MapEntities;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};


@@ 35,6 36,13 @@ pub struct SnapOf(#[entities] pub Entity);
#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship_target(relationship = SnapOf)]
pub struct Snaps(#[entities] Vec<Entity>);
impl Deref for Snaps {
    type Target = Vec<Entity>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Serialize, Deserialize)]
pub struct JointId(pub String);

M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +46 -10
@@ 1,4 1,5 @@
use bevy::color::palettes::css::{ORANGE, RED};
use bevy::color::palettes::tailwind::{CYAN_400, CYAN_800};
use crate::client::Me;
use crate::ecs::{CursorWorldCoordinates, DragRequestEvent, Part};
use bevy::prelude::*;


@@ 10,6 11,7 @@ use crate::client::key_input::AttachmentDebugRes;

pub fn parts_plugin(app: &mut App) {
    app.insert_resource(DragResource(None));
    app.insert_resource(SnapResource(None));
    app.add_systems(Update, (handle_incoming_parts, handle_updated_parts, update_drag_ghosts));
    app.add_observer(on_part_release);
}


@@ 59,6 61,9 @@ fn handle_updated_parts(

#[derive(Resource)]
struct DragResource(Option<Entity>);
#[derive(Resource)]
struct SnapResource(Option<Entity>);

#[derive(Component)]
struct DragGhost;



@@ 91,35 96,50 @@ fn on_part_release(
    mut events: EventWriter<DragRequestEvent>,
    cursor: Res<CursorWorldCoordinates>,
    mut commands: Commands,
    ghosts: Query<Entity, With<DragGhost>>,
    ghost: Single<(Entity, &Transform), With<DragGhost>>,
    snap: Res<SnapResource>
) {
    if ev.button != PointerButton::Primary {
        return;
    }

    if let Some(e) = drag.0 {
        for ghost in &ghosts {
            commands.entity(ghost).despawn();
        }
        let mut rotation = ghost.1.rotation;
        commands.entity(ghost.0).despawn();

        if let Some(c) = cursor.0 {
            debug!(?e, ?c, "sending drag request");
            events.write(DragRequestEvent(e, c));
            events.write(DragRequestEvent {
                drag_target: e,
                drag_to: c,
                set_rotation: rotation,
                snap_target: snap.0,
            });
        }
    }

    drag.0 = None;
}


/// !IMPORTANT!
/// This function forms the bulk of the attachment system.
/// PLEASE DO NOT MODIFY OR MOVE
///
/// This code is super cursed, and it will break at the lightest breeze
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), Without<Peer>>,
    snaps: Query<(&Transform, &SnapOfJoint, &SnapOf, Entity)>,
    joints: Query<(&Transform, &JointOf, Entity), Without<Peer>>,
    parts: Query<(&GlobalTransform, Option<&Me>, Option<&PartInShip>), With<Part>>,
    me: Single<Entity, With<Me>>,
    debug: Res<AttachmentDebugRes>,
    mut rsnap: ResMut<SnapResource>,
    drag: Res<DragResource>,
    mut gizmos: Gizmos,
    keys: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
) {
    let Some(cursor) = cursor.0 else { return };



@@ 127,8 147,11 @@ fn update_drag_ghosts(

    let mut best_distance = f32::INFINITY;
    let mut best_target = Transform::from_xyz(cursor.x, cursor.y, 0.0);
    best_target.rotation = ghost.rotation;
    let mut snap = None;
    let mut target_gpos = None;

    for (snap_local_transform, snap_joint, snap_part) in &snaps {
    for (snap_local_transform, snap_joint, snap_part, snap_id) in &snaps {
        if Some(snap_part.0) == drag.0 { continue; }

        // only snap to ourselves


@@ 153,7 176,7 @@ fn update_drag_ghosts(

        if debug.0 { gizmos.circle_2d(snap_global_translation, 3.0, GREEN); }

        let Ok((offset, _)) = joints.get(snap_joint.0) else { continue; };
        let Ok((offset, _, _)) = joints.get(snap_joint.0) else { continue; };

        let joint_target = parent_position.transform_point(offset.translation);



@@ 161,12 184,25 @@ fn update_drag_ghosts(

        let target_transform = Transform {
            translation: joint_target,
            rotation: parent_position.rotation().mul_quat(offset.rotation),
            rotation: ghost.rotation,
            scale: offset.scale,
        };
        best_distance = distance_to_cursor;
        best_target = target_transform;

        snap = Some(snap_id);
        target_gpos = Some(joint_target);
    }

    **ghost = best_target;

    if keys.pressed(KeyCode::KeyQ) {
        ghost.rotation = ghost.rotation.mul_quat(Quat::from_rotation_z(180.0_f32.to_radians() * time.delta_secs()));
    }
    if keys.pressed(KeyCode::KeyE) {
        ghost.rotation = ghost.rotation.mul_quat(Quat::from_rotation_z(-180.0_f32.to_radians() * time.delta_secs()));
    }

    rsnap.0 = snap;

}
\ No newline at end of file

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +10 -2
@@ 1,5 1,5 @@
use bevy::ecs::entity::MapEntities;
use bevy::math::Vec2;
use bevy::math::{Quat, Vec2};
use bevy::prelude::{Component, Entity, Event, Handle, Resource};
use bevy_rapier2d::dynamics::RigidBody;
use bevy_rapier2d::prelude::*;


@@ 64,4 64,12 @@ pub struct Particles {
}

#[derive(Serialize, Deserialize, Event, Debug, MapEntities, Clone)]
pub struct DragRequestEvent(#[entities] pub Entity, pub Vec2);
pub struct DragRequestEvent {
    #[entities]
    pub drag_target: Entity,
    pub drag_to: Vec2,
    pub set_rotation: Quat,
    #[entities]
    pub snap_target: Option<Entity>
}


M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +16 -6
@@ 1,5 1,5 @@
use std::f32::consts::PI;

use std::ops::Deref;
use crate::config::planet::Planet;
use crate::ecs::{
    DragRequestEvent, Part, Player, PlayerThrust, ThrustEvent,


@@ 10,9 10,11 @@ use crate::server::{ConnectedGameEntity, ConnectedNetworkEntity};
use bevy::prelude::*;
use bevy_rapier2d::prelude::ExternalForce;
use bevy_replicon::prelude::FromClient;
use crate::attachment::PartInShip;
use crate::attachment::{JointOf, PartInShip, Peer, SnapOf, SnapOfJoint, Snaps};
use crate::client::Me;
use crate::server::system_sets::PlayerInputSet;


pub fn player_management_plugin(app: &mut App) {
    app.add_systems(Update, (handle_new_players, player_thrust, dragging).in_set(PlayerInputSet));
}


@@ 20,11 22,19 @@ pub fn player_management_plugin(app: &mut App) {
fn dragging(
    mut events: EventReader<FromClient<DragRequestEvent>>,
    mut parts: Query<&mut Transform, With<Part>>,
    clients: Query<&ConnectedNetworkEntity>,
    snap_parts: Query<(&GlobalTransform, Option<&Me>, Option<&PartInShip>, &Snaps), With<Part>>,
) {
    for event in events.read() {
        let mut part = parts.get_mut(event.event.0).unwrap();
        part.translation.x = event.event.1.x;
        part.translation.y = event.event.1.y;
    for FromClient {
        client_entity,
        event,
    } in events.read() {
        let ConnectedNetworkEntity { game_entity } = clients.get(*client_entity).unwrap();

        let mut part = parts.get_mut(event.drag_target).unwrap();
        part.translation.x = event.drag_to.x;
        part.translation.y = event.drag_to.y;
        part.rotation = event.set_rotation;
    }
}