~starkingdoms/starkingdoms

30a38bc4ffe5d95dfa97578d8e368897da9e488b — ghostly_zsh 21 hours ago ad2517f
feat: constant cooling and heating from thrusters
M crates/unified/Cargo.toml => crates/unified/Cargo.toml +1 -1
@@ 104,4 104,4 @@ server = ["aeronet_websocket/server", "aeronet_replicon/server"]
client = [
    "dep:leafwing-input-manager",
    "dep:good_lp"
]
\ No newline at end of file
]

M crates/unified/assets/config/parts/hearty.part.toml => crates/unified/assets/config/parts/hearty.part.toml +12 -0
@@ 12,21 12,29 @@ mass = 100
id = "bottom left"
apply_force_at_local = [ -25.0, -25.0 ]
thrust_vector = [ 0.0, 2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1

[[thruster]]
id = "bottom right"
apply_force_at_local = [ 25.0, -25.0 ]
thrust_vector = [ 0.0, 2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1

[[thruster]]
id = "top left"
apply_force_at_local = [ -25.0, 25.0 ]
thrust_vector = [ 0.0, -2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1

[[thruster]]
id = "top right"
apply_force_at_local = [ 25.0, 25.0 ]
thrust_vector = [ 0.0, -2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1

[[joint]]
id = "Top"


@@ 49,5 57,9 @@ id = "Left"
target = { translation = [ -55.0, 0.0, 0.0 ], rotation = -270.0 }
snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 }

[cooling]
cool_temperature = 273.0
heat_cooling_constant = 1.0

[crafting]
can_craft = true

M crates/unified/assets/config/parts/housing.part.toml => crates/unified/assets/config/parts/housing.part.toml +2 -0
@@ 12,6 12,8 @@ mass = 50
id = "main"
apply_force_at_local = [ 0, 0 ]
thrust_vector = [ 0.0, -20000.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1

[[joint]]
id = "Top"

M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +20 -7
@@ 2,11 2,12 @@ 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::ecs::{Me, Temperature};
use crate::client::colors::GREEN;
use crate::ecs::{DragRequestEvent, Part, MAIN_LAYER};
use crate::client::input::CursorWorldCoordinates;
use bevy::color::palettes::css::{ORANGE, PURPLE, RED, YELLOW};
use bevy::ui::update;
use crate::client::ship::attachment::AttachmentDebugRes;
use crate::prelude::*;



@@ 20,6 21,7 @@ pub fn parts_plugin(app: &mut App) {
            handle_incoming_parts,
            handle_updated_parts,
            update_part_sprites,
            handle_updated_temperature,
        ),
    );
    app.add_observer(on_part_release);


@@ 27,15 29,16 @@ pub fn parts_plugin(app: &mut App) {

fn handle_incoming_parts(
    mut commands: Commands,
    new_parts: Query<(Entity, &Part, Option<&PartInShip>), Added<Part>>,
    new_parts: Query<(Entity, &Part, &Temperature, Option<&PartInShip>), Added<Part>>,
    asset_server: Res<AssetServer>,
) {
    for (new_entity, new_part, is_connected) in new_parts.iter() {
    for (new_entity, new_part, temperature, is_connected) in new_parts.iter() {
        let mut sprite = Sprite::from_image(asset_server.load(if is_connected.is_some() {
            &new_part.strong_config.part.sprite_connected
        } else {
            &new_part.strong_config.part.sprite_disconnected
        }));
        sprite.color = Color::srgb(1.0 + ((temperature.0 - 300.0) / 700.0).max(0.0), 1.0, 1.0);
        sprite.custom_size = Some(Vec2::new(
            new_part.strong_config.physics.width,
            new_part.strong_config.physics.height,


@@ 50,12 53,19 @@ fn handle_incoming_parts(
            .observe(open_crafting_ui);
    }
}
fn handle_updated_temperature(
    mut updated_parts: Query<(&mut Sprite, &Temperature), (With<Part>, Changed<Temperature>)>
) {
    for (mut sprite, temperature) in updated_parts.iter_mut() {
        sprite.color = Color::srgb(1.0 + ((temperature.0 - 300.0) / 700.0).max(0.0), 1.0, 1.0);
    }
}
fn handle_updated_parts(
    mut commands: Commands,
    updated_parts: Query<(Entity, &Part, Option<&PartInShip>), Changed<Part>>,
    updated_parts: Query<(Entity, &Part, &Temperature, Option<&PartInShip>), Changed<Part>>,
    asset_server: Res<AssetServer>,
) {
    for (updated_entity, updated_part, is_connected) in updated_parts.iter() {
    for (updated_entity, updated_part, temperature, is_connected) in updated_parts.iter() {
        let mut sprite = Sprite::from_image(asset_server.load(if is_connected.is_some() {
            &updated_part.strong_config.part.sprite_connected
        } else {


@@ 65,6 75,7 @@ fn handle_updated_parts(
            updated_part.strong_config.physics.width,
            updated_part.strong_config.physics.height,
        ));
        sprite.color = Color::srgb(1.0 + ((temperature.0 - 300.0) / 700.0).max(0.0), 1.0, 1.0);

        commands
            .entity(updated_entity)


@@ 76,12 87,12 @@ fn handle_updated_parts(
fn update_part_sprites(
    added: Query<Entity, Added<PartInShip>>,
    mut removed: RemovedComponents<PartInShip>,
    parts: Query<(&Part, Option<&PartInShip>)>,
    parts: Query<(&Part, &Temperature, Option<&PartInShip>)>,
    asset_server: Res<AssetServer>,
    mut commands: Commands,
) {
    for e in added.into_iter().chain(removed.read()) {
        let Ok((part, connected_to)) = parts.get(e) else {
        let Ok((part, temperature, connected_to)) = parts.get(e) else {
            continue;
        };



@@ 96,6 107,8 @@ fn update_part_sprites(
            part.strong_config.physics.width,
            part.strong_config.physics.height,
        ));
        sprite.color = Color::srgb(1.0 + ((temperature.0 - 300.0) / 700.0).max(0.0), 1.0, 1.0);
        //sprite.color = Color::srgb(1.0, (700.0 - temperature.0) / 700.0, (700.0 - temperature.0) / 700.0);

        commands.entity(e).insert(sprite);
    }

M crates/unified/src/config/part.rs => crates/unified/src/config/part.rs +9 -0
@@ 13,6 13,7 @@ pub struct PartConfig {
    #[serde(default)]
    #[serde(rename = "joint")]
    pub joints: Vec<JointConfig>,
    pub cooling: Option<CoolingConfig>,
    pub crafting: Option<CraftingConfig>,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]


@@ 39,6 40,9 @@ pub struct ThrusterConfig {
    #[serde(default)]
    pub apply_force_at_local: Vec2,
    pub thrust_vector: Vec2,

    pub exhaust_temperature: f32,
    pub heat_constant: f32,
}
#[derive(Deserialize, Serialize, Clone, Debug, TypePath, PartialEq, Copy)]
pub struct JointOffset {


@@ 55,6 59,11 @@ impl From<JointOffset> for Transform {
    }
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct CoolingConfig {
    pub cool_temperature: f32,
    pub heat_cooling_constant: f32,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct CraftingConfig {
    pub can_craft: bool,
}

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +11 -0
@@ 113,3 113,14 @@ pub struct Hi {
pub struct CanCraft;
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct CraftingUi;

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Temperature(pub f32);
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct TemperatureSprite;
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct Cooler {
    pub cool_temperature: f32,
    pub heat_cooling_constant: f32,
}

M crates/unified/src/ecs/thruster.rs => crates/unified/src/ecs/thruster.rs +4 -2
@@ 34,7 34,9 @@ pub struct ThrusterOfPart(#[entities] pub Entity);
#[require(Replicated)]
pub struct Thruster {
    pub id: ThrusterId,
    pub thrust_vector: Vec2
    pub thrust_vector: Vec2,
    pub exhaust_temperature: f32,
    pub heat_constant: f32,
}

#[derive(Bundle)]


@@ 43,4 45,4 @@ pub struct ThrusterBundle {
    pub transform: Transform,
    pub child_of: ChildOf,
    pub thruster_of_part: ThrusterOfPart,
}
\ No newline at end of file
}

A crates/unified/src/server/heat/cooling.rs => crates/unified/src/server/heat/cooling.rs +16 -0
@@ 0,0 1,16 @@
use crate::prelude::*;
use crate::ecs::{Cooler, Temperature};

pub fn heat_cooling_plugin(app: &mut App) {
    app.add_systems(Update, cool_part);
}

fn cool_part(
    time: Res<Time>,
    mut parts: Query<(&mut Temperature, &Cooler)>
) {
    for (mut temperature, cooler) in parts.iter_mut() {
        temperature.0 += cooler.heat_cooling_constant * (cooler.cool_temperature - temperature.0)
            * time.delta_secs();
    }
}

A crates/unified/src/server/heat/mod.rs => crates/unified/src/server/heat/mod.rs +1 -0
@@ 0,0 1,1 @@
pub mod cooling;

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +3 -0
@@ 1,12 1,14 @@
mod earth_parts;
mod gravity;
mod part;
mod heat;
pub mod planets;
pub mod player;
mod system_sets;

use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::gravity::newtonian_gravity_plugin;
use crate::server::heat::cooling::heat_cooling_plugin;
use crate::server::part::part_management_plugin;
use crate::server::planets::planets_plugin;
use crate::server::player::player_management_plugin;


@@ 48,6 50,7 @@ impl Plugin for ServerPlugin {
        .add_plugins(spawn_parts_plugin)
        .add_plugins(part_management_plugin)
        .add_plugins(server_thrust_plugin)
        .add_plugins(heat_cooling_plugin)
        .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet));
        //.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend));
    }

M crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +15 -2
@@ 1,6 1,6 @@
use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoint};
use crate::config::part::{CraftingConfig, JointConfig, PartConfig};
use crate::ecs::{CanCraft, Part, PartHandle};
use crate::config::part::{CoolingConfig, CraftingConfig, JointConfig, PartConfig};
use crate::ecs::{CanCraft, Cooler, Part, PartHandle, Temperature};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart};


@@ 35,6 35,9 @@ fn handle_ready_parts(
            if let Some(ref crafting_config) = strong_config.crafting {
                crafting_bundle(&mut commands.entity(entity), &crafting_config);
            }
            if let Some(ref cooling_config) = strong_config.cooling {
                cooling_bundle(&mut commands.entity(entity), &cooling_config);
            }
            spawn_joints(strong_config, entity, commands.reborrow());
            spawn_thrusters(strong_config, entity, commands.reborrow());
        }


@@ 124,12 127,14 @@ fn calculate_bundle(config: &PartConfig, handle: &Handle<PartConfig>) -> impl Bu
    let part_handle = PartHandle(handle.clone());
    let collider = Collider::rectangle(config.physics.width, config.physics.height);
    let mass = Mass(config.physics.mass);
    let temperature = Temperature(298.0); // note that this is 25 degrees C

    (
        part,
        part_handle,
        collider,
        mass,
        temperature,
    )
}
fn crafting_bundle(entity: &mut EntityCommands, config: &CraftingConfig) {


@@ 137,6 142,12 @@ fn crafting_bundle(entity: &mut EntityCommands, config: &CraftingConfig) {
        entity.insert(CanCraft);
    }
}
fn cooling_bundle(entity: &mut EntityCommands, config: &CoolingConfig) {
    entity.insert(Cooler {
        cool_temperature: config.cool_temperature,
        heat_cooling_constant: config.heat_cooling_constant,
    });
}
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()),


@@ 170,6 181,8 @@ fn spawn_thrusters(config: &PartConfig, part: Entity, mut commands: Commands) {
                thruster: Thruster {
                    id: ThrusterId::from_part_and_thruster_id(&config.part.name, &thruster.id),
                    thrust_vector: thruster.thrust_vector,
                    exhaust_temperature: thruster.exhaust_temperature,
                    heat_constant: thruster.heat_constant,
                },
                transform: Transform::from_translation(Vec3::new(thruster.apply_force_at_local.x, thruster.apply_force_at_local.y, 0.0)),
                child_of: ChildOf(part),

M crates/unified/src/server/player/thrust.rs => crates/unified/src/server/player/thrust.rs +8 -5
@@ 4,7 4,7 @@
//! and apply it to the physics simulation.

use crate::attachment::Parts;
use crate::ecs::Part;
use crate::ecs::{Part, Temperature};
use crate::ecs::thruster::{Thruster, ThrusterOfPart};
use crate::prelude::*;
use crate::server::ConnectedNetworkEntity;


@@ 51,7 51,8 @@ fn process_thrust_events(
fn apply_thrust_solutions(
    players: Query<(Entity, &ThrustSolution, Option<&Parts>)>,
    thrusters: Query<(&Thruster, &ThrusterOfPart, &GlobalTransform)>,
    mut parts: Query<Forces, With<Part>>,
    mut parts: Query<(Forces, &mut Temperature), With<Part>>,
    time: Res<Time>,
) {
    //gizmos.arrow_2d(
    //             thruster.2.translation().xy(),


@@ 92,12 93,14 @@ fn apply_thrust_solutions(
                continue
            } // not permitted

            // great, it's valid; apply the force
            let mut part_forces = parts.get_mut(parent_part.0).unwrap();
            // great, it's valid; apply the force and increase temperature
            let (mut part_forces, mut temperature) = parts.get_mut(parent_part.0).unwrap();
            temperature.0 += thruster_info.heat_constant * (thruster_info.exhaust_temperature - temperature.0) * time.delta_secs();

            part_forces.apply_force_at_point(
                (thruster_transform.rotation() * thruster_info.thrust_vector.extend(0.0)).xy(),
                thruster_transform.translation().xy()
            );
        }
    }
}
\ No newline at end of file
}

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +2 -1
@@ 1,6 1,6 @@
use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint};
use crate::config::planet::{Planet, PlanetConfigCollection};
use crate::ecs::{CanCraft, DragRequestEvent, Hi, Part, Particles, Player, PlayerStorage};
use crate::ecs::{CanCraft, DragRequestEvent, Hi, Part, Particles, Player, PlayerStorage, Temperature};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy_common_assets::toml::TomlAssetPlugin;
use crate::prelude::*;


@@ 53,6 53,7 @@ pub fn register_everything(app: &mut App) {
    app.replicate::<Thruster>();
    app.replicate::<ThrusterOfPart>();
    app.replicate::<CanCraft>();
    app.replicate::<Temperature>();
}

fn physics_setup_plugin(app: &mut App) {