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) {