~starkingdoms/starkingdoms

54589315a90d94a48978d94420ff0a656d81a2df — core 24 days ago 827d7ac
feat: thruster metadata
M crates/unified/assets/config/parts/chassis.part.toml => crates/unified/assets/config/parts/chassis.part.toml +4 -4
@@ 8,23 8,23 @@ width = 50
height = 50
mass = 100

[[joints]]
[[joint]]
id = "Top"
target = { translation = [ 0.0, 50.0, 0.0 ], rotation = 0.0 }
snap = { translation = [ 0.0, 25.0, 0.0 ], rotation = 0.0 }


[[joints]]
[[joint]]
id = "Right"
target = { translation = [ 50.0, 0.0, 0.0 ], rotation = -90.0 }
snap = { translation = [ 25.0, 0.0, 0.0 ], rotation = 0.0 }

[[joints]]
[[joint]]
id = "Bottom"
target = { translation = [ 0.0, -50.0, 0.0 ], rotation = -180.0 }
snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 }

[[joints]]
[[joint]]
id = "Left"
target = { translation = [ -50.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

M crates/unified/assets/config/parts/hearty.part.toml => crates/unified/assets/config/parts/hearty.part.toml +8 -7
@@ 8,27 8,28 @@ width = 50
height = 50
mass = 100

[thruster]
flow_rate = 0.1
exhaust_speed = 250
[[thruster]]
id = "bottom left"
apply_force_at_local = [ -50.0, -50.0 ]
thrust_vector = [ 0.0, 25.0 ]

[[joints]]
[[joint]]
id = "Top"
target = { translation = [ 0.0, 55.0, 0.0 ], rotation = 0.0 }
snap = { translation = [ 0.0, 25.0, 0.0 ], rotation = 0.0 }


[[joints]]
[[joint]]
id = "Right"
target = { translation = [ 55.0, 0.0, 0.0 ], rotation = -90.0 }
snap = { translation = [ 25.0, 0.0, 0.0 ], rotation = 0.0 }

[[joints]]
[[joint]]
id = "Bottom"
target = { translation = [ 0.0, -55.0, 0.0 ], rotation = -180.0 }
snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 }

[[joints]]
[[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

M crates/unified/assets/config/parts/housing.part.toml => crates/unified/assets/config/parts/housing.part.toml +4 -4
@@ 8,23 8,23 @@ width = 50
height = 50
mass = 50

[[joints]]
[[joint]]
id = "Top"
target = { translation = [ 0.0, 55.0, 0.0 ], rotation = 0.0 }
snap = { translation = [ 0.0, 25.0, 0.0 ], rotation = 0.0 }


[[joints]]
[[joint]]
id = "Right"
target = { translation = [ 55.0, 0.0, 0.0 ], rotation = -90.0 }
snap = { translation = [ 25.0, 0.0, 0.0 ], rotation = 0.0 }

[[joints]]
[[joint]]
id = "Bottom"
target = { translation = [ 0.0, -55.0, 0.0 ], rotation = -180.0 }
snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 }

[[joints]]
[[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 }

M crates/unified/src/config/part.rs => crates/unified/src/config/part.rs +12 -6
@@ 7,7 7,11 @@ use serde::{Deserialize, Serialize};
pub struct PartConfig {
    pub part: PartPartConfig,
    pub physics: PartPhysicsConfig,
    pub thruster: Option<PartThrusterConfig>,
    #[serde(rename = "thruster")]
    #[serde(default)]
    pub thrusters: Vec<ThrusterConfig>,
    #[serde(default)]
    #[serde(rename = "joint")]
    pub joints: Vec<JointConfig>,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]


@@ 23,16 27,18 @@ pub struct PartPhysicsConfig {
    pub mass: f32,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct PartThrusterConfig {
    pub flow_rate: f32,     // kg/s
    pub exhaust_speed: f32, // m/s
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct JointConfig {
    pub id: String,
    pub target: JointOffset,
    pub snap: JointOffset,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct ThrusterConfig {
    pub id: String,
    #[serde(default)]
    pub apply_force_at_local: Vec2,
    pub thrust_vector: Vec2,
}
#[derive(Deserialize, Serialize, Clone, Debug, TypePath, PartialEq, Copy)]
pub struct JointOffset {
    pub translation: Vec3,

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +2 -0
@@ 1,3 1,5 @@
pub mod thruster;

use crate::config::part::PartConfig;
use bevy::ecs::entity::MapEntities;
use bevy::math::{Quat, Vec2};

A crates/unified/src/ecs/thruster.rs => crates/unified/src/ecs/thruster.rs +44 -0
@@ 0,0 1,44 @@
use std::ops::Deref;
use bevy::ecs::entity::MapEntities;
use bevy::math::Vec2;
use bevy::prelude::Bundle;
use serde::{Deserialize, Serialize};
use crate::prelude::{ChildOf, Component, Entity, Transform};

#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct ThrusterId(pub String);
impl ThrusterId {
    #[must_use]
    pub fn from_part_and_thruster_id(part: impl AsRef<str>, thruster: impl AsRef<str>) -> Self {
        Self(format!("{}:{}", part.as_ref(), thruster.as_ref()))
    }
}

#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship_target(relationship = ThrusterOfPart, linked_spawn)]
pub struct PartThrusters(#[entities] Vec<Entity>);
impl Deref for PartThrusters {
    type Target = Vec<Entity>;

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

#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship(relationship_target = PartThrusters)]
pub struct ThrusterOfPart(#[entities] pub Entity);

#[derive(Component, Serialize, Deserialize)]
pub struct Thruster {
    pub id: ThrusterId,
    pub thrust_vector: Vec2
}

#[derive(Bundle)]
pub struct ThrusterBundle {
    pub thruster: Thruster,
    pub transform: Transform,
    pub child_of: ChildOf,
    pub thruster_of_part: ThrusterOfPart,
}
\ No newline at end of file

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +1 -0
@@ 5,6 5,7 @@ pub mod planets;
pub mod player;
mod system_sets;
mod world_config;
mod thruster;

use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::gravity::newtonian_gravity_plugin;

M crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +25 -1
@@ 3,6 3,7 @@ use crate::config::part::{JointConfig, PartConfig};
use crate::ecs::{Part, PartHandle};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart};

pub fn part_management_plugin(app: &mut App) {
    app.add_systems(PreUpdate, (handle_ready_parts, handle_part_reloading));


@@ 31,12 32,13 @@ fn handle_ready_parts(
                .insert(calculate_bundle(strong_config, &loading_part.0))
                .remove::<SpawnPartRequest>();
            spawn_joints(strong_config, entity, commands.reborrow());
            spawn_thrusters(strong_config, entity, commands.reborrow());
        }
    }
}

fn handle_part_reloading(
    existing_parts: Query<(Entity, &PartHandle, &Joints)>,
    existing_parts: Query<(Entity, &PartHandle, &Joints, &PartThrusters)>,
    joints: Query<(&mut Joint, Option<&Peer>, Entity)>,
    snaps: Query<(Entity, &SnapOfJoint)>,
    assets: Res<Assets<PartConfig>>,


@@ 51,6 53,13 @@ fn handle_part_reloading(
                    commands
                        .entity(existing_part.0)
                        .insert(calculate_bundle(config, &existing_part.1.0));

                    // vaporize all thrusters, then respawn
                    for thruster in &**existing_part.3 {
                        commands.entity(*thruster).despawn();
                    }
                    spawn_thrusters(config, existing_part.0, commands.reborrow());
                    
                    // update all joints
                    let mut used_joints = vec![];
                    for joint_id in &**existing_part.2 {


@@ 144,3 153,18 @@ fn spawn_joints(config: &PartConfig, parent: Entity, mut commands: Commands) {
        commands.spawn(spawn_snap_bundle(joint, &parent, &joint_id));
    }
}

fn spawn_thrusters(config: &PartConfig, part: Entity, mut commands: Commands) {
    for thruster in &config.thrusters {
        commands
            .spawn(ThrusterBundle {
                thruster: Thruster {
                    id: ThrusterId::from_part_and_thruster_id(&config.part.name, &thruster.id),
                    thrust_vector: thruster.thrust_vector,
                },
                transform: Transform::from_translation(Vec3::new(thruster.apply_force_at_local.x, thruster.apply_force_at_local.y, 0.0)),
                child_of: ChildOf(part),
                thruster_of_part: ThrusterOfPart(part),
            });
    }
}

A crates/unified/src/server/thruster/mod.rs => crates/unified/src/server/thruster/mod.rs +3 -0
@@ 0,0 1,3 @@
//! # Thruster solver
//! Given a ship and the desired target vector, solve for the combination of thrusters
//! that will produce as close to the desired target as possible.
\ No newline at end of file