Compare commits
3 Commits
76deff3f2f
...
4f634306da
| Author | SHA1 | Date |
|---|---|---|
|
|
4f634306da | |
|
|
8ff232f21b | |
|
|
183475c60f |
|
|
@ -2084,6 +2084,7 @@ dependencies = [
|
||||||
"bevy-inspector-egui",
|
"bevy-inspector-egui",
|
||||||
"bevy_prototype_debug_lines",
|
"bevy_prototype_debug_lines",
|
||||||
"bevy_rapier2d",
|
"bevy_rapier2d",
|
||||||
|
"fastrand",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"noise",
|
"noise",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ bevy = { version = "0.9.0", features = ["dynamic"] }
|
||||||
bevy-inspector-egui = "0.14.0"
|
bevy-inspector-egui = "0.14.0"
|
||||||
bevy_prototype_debug_lines = "0.9.0"
|
bevy_prototype_debug_lines = "0.9.0"
|
||||||
bevy_rapier2d = "0.19.0"
|
bevy_rapier2d = "0.19.0"
|
||||||
|
fastrand = "1.8.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
noise = "0.8.2"
|
noise = "0.8.2"
|
||||||
|
|
||||||
|
|
|
||||||
13
src/game.rs
13
src/game.rs
|
|
@ -7,10 +7,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
camera::{GameCameraPlugin, WORLD_WIDTH},
|
camera::GameCameraPlugin, debug::DebugPlugin, kinematic::KinematicPlugin, player::PlayerPlugin,
|
||||||
debug::DebugPlugin,
|
|
||||||
kinematic::KinematicPlugin,
|
|
||||||
player::PlayerPlugin,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
|
|
@ -35,15 +32,15 @@ pub fn init() {
|
||||||
|
|
||||||
fn setup_window(mut windows: ResMut<Windows>) {
|
fn setup_window(mut windows: ResMut<Windows>) {
|
||||||
if let Some(window) = windows.get_primary_mut() {
|
if let Some(window) = windows.get_primary_mut() {
|
||||||
window.set_resolution(900.0, 450.0);
|
window.set_resolution(1280.0, 720.0);
|
||||||
window.set_title("Kuilu".to_string());
|
window.set_title("Kuilu".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_terrain(mut commands: Commands, mut terrain: ResMut<Terrain2D>) {
|
fn setup_terrain(mut commands: Commands, mut terrain: ResMut<Terrain2D>) {
|
||||||
let terrain_gen = TerrainGen2D::new(432678);
|
let terrain_gen = TerrainGen2D::new(432678);
|
||||||
for y in 0..(WORLD_WIDTH / Chunk2D::SIZE_Y as i32) {
|
for y in 0..(Terrain2D::WORLD_HEIGHT / Chunk2D::SIZE_Y as i32) {
|
||||||
for x in 0..(WORLD_WIDTH / Chunk2D::SIZE_X as i32) {
|
for x in 0..(Terrain2D::WORLD_WIDTH / Chunk2D::SIZE_X as i32) {
|
||||||
let position = Vector2I { x, y };
|
let position = Vector2I { x, y };
|
||||||
terrain.add_chunk(position, terrain_gen.gen_chunk(&position));
|
terrain.add_chunk(position, terrain_gen.gen_chunk(&position));
|
||||||
}
|
}
|
||||||
|
|
@ -60,6 +57,6 @@ fn setup_terrain(mut commands: Commands, mut terrain: ResMut<Terrain2D>) {
|
||||||
.spawn(Name::new("Right wall"))
|
.spawn(Name::new("Right wall"))
|
||||||
.insert(Collider::halfspace(Vec2::NEG_X).unwrap())
|
.insert(Collider::halfspace(Vec2::NEG_X).unwrap())
|
||||||
.insert(TransformBundle::from_transform(
|
.insert(TransformBundle::from_transform(
|
||||||
Transform::from_translation(Vec3::new(WORLD_WIDTH as f32, 0.0, 0.0)),
|
Transform::from_translation(Vec3::new(Terrain2D::WORLD_WIDTH as f32, 0.0, 0.0)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@ use bevy::{
|
||||||
};
|
};
|
||||||
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
|
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
|
||||||
|
|
||||||
use crate::util::{move_towards_vec3, vec3_lerp};
|
use crate::{
|
||||||
|
terrain2d::Terrain2D,
|
||||||
pub const WORLD_WIDTH: i32 = 512;
|
util::{move_towards_vec3, vec3_lerp},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct GameCameraPlugin;
|
pub struct GameCameraPlugin;
|
||||||
|
|
||||||
|
|
@ -48,7 +49,7 @@ fn camera_setup(mut commands: Commands) {
|
||||||
Name::new("Camera"),
|
Name::new("Camera"),
|
||||||
Camera2dBundle {
|
Camera2dBundle {
|
||||||
projection: OrthographicProjection {
|
projection: OrthographicProjection {
|
||||||
scaling_mode: ScalingMode::FixedHorizontal(WORLD_WIDTH as f32),
|
scaling_mode: ScalingMode::FixedHorizontal(Terrain2D::WORLD_WIDTH as f32),
|
||||||
window_origin: WindowOrigin::Center,
|
window_origin: WindowOrigin::Center,
|
||||||
scale: 1.0 / 2.0,
|
scale: 1.0 / 2.0,
|
||||||
..default()
|
..default()
|
||||||
|
|
@ -79,7 +80,7 @@ fn camera_system(
|
||||||
|
|
||||||
for (mut camera_transform, projection) in camera_query.iter_mut() {
|
for (mut camera_transform, projection) in camera_query.iter_mut() {
|
||||||
let left_limit = 0.0;
|
let left_limit = 0.0;
|
||||||
let right_limit = WORLD_WIDTH as f32;
|
let right_limit = Terrain2D::WORLD_WIDTH as f32;
|
||||||
let offset = Vec3::new(0.0, 0.0, 999.9);
|
let offset = Vec3::new(0.0, 0.0, 999.9);
|
||||||
match follow.movement {
|
match follow.movement {
|
||||||
FollowMovement::Instant => {
|
FollowMovement::Instant => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
||||||
|
|
||||||
mod terrain;
|
pub mod terrain;
|
||||||
|
|
||||||
use terrain::TerrainDebugPlugin;
|
use terrain::TerrainDebugPlugin;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,10 @@ struct TerrainBrush2D {
|
||||||
|
|
||||||
impl Default for TerrainBrush2D {
|
impl Default for TerrainBrush2D {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TerrainBrush2D { radius: 3, tile: 7 }
|
TerrainBrush2D {
|
||||||
|
radius: 40,
|
||||||
|
tile: 8,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,7 +207,7 @@ fn chunk_debugger(terrain: Res<Terrain2D>, mut debug_draw: ResMut<DebugLines>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_box(debug_draw: &mut DebugLines, min: Vec3, max: Vec3, color: Color, duration: f32) {
|
pub fn draw_box(debug_draw: &mut DebugLines, min: Vec3, max: Vec3, color: Color, duration: f32) {
|
||||||
let points = vec![
|
let points = vec![
|
||||||
Vec3::new(min.x, min.y, min.z),
|
Vec3::new(min.x, min.y, min.z),
|
||||||
Vec3::new(max.x, min.y, min.z),
|
Vec3::new(max.x, min.y, min.z),
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,6 @@ pub fn player_spawn(mut commands: Commands) {
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.insert(TransformBundle::from_transform(Transform::from_xyz(
|
|
||||||
256.0, 128.0, 0.0,
|
|
||||||
)))
|
|
||||||
.insert(Collider::cuboid(3.0, 6.0))
|
.insert(Collider::cuboid(3.0, 6.0))
|
||||||
.insert(PlayerBundle {
|
.insert(PlayerBundle {
|
||||||
kinematic,
|
kinematic,
|
||||||
|
|
|
||||||
168
src/terrain2d.rs
168
src/terrain2d.rs
|
|
@ -17,10 +17,7 @@ pub use terrain_gen2d::*;
|
||||||
pub use texel2d::*;
|
pub use texel2d::*;
|
||||||
pub use texel_behaviour2d::*;
|
pub use texel_behaviour2d::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::util::{frame_counter::FrameCounter, math::*, Vector2I};
|
||||||
game::camera::WORLD_WIDTH,
|
|
||||||
util::{frame_counter::FrameCounter, math::*, Vector2I},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Terrain2DPlugin;
|
pub struct Terrain2DPlugin;
|
||||||
|
|
||||||
|
|
@ -46,10 +43,10 @@ impl Plugin for Terrain2DPlugin {
|
||||||
|
|
||||||
app.register_type::<TerrainChunk2D>()
|
app.register_type::<TerrainChunk2D>()
|
||||||
.insert_resource(Terrain2D::new(
|
.insert_resource(Terrain2D::new(
|
||||||
Some(WORLD_WIDTH * 2),
|
Some(Terrain2D::WORLD_HEIGHT),
|
||||||
Some(0),
|
Some(0),
|
||||||
Some(0),
|
Some(0),
|
||||||
Some(WORLD_WIDTH),
|
Some(Terrain2D::WORLD_WIDTH),
|
||||||
))
|
))
|
||||||
.add_event::<TerrainEvent2D>()
|
.add_event::<TerrainEvent2D>()
|
||||||
.add_system_to_stage(TerrainStages::Simulation, terrain_simulation)
|
.add_system_to_stage(TerrainStages::Simulation, terrain_simulation)
|
||||||
|
|
@ -74,7 +71,11 @@ pub enum TerrainStages {
|
||||||
ChunkSync,
|
ChunkSync,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terrain_simulation(mut terrain: ResMut<Terrain2D>, frame_counter: Res<FrameCounter>) {
|
fn terrain_simulation(
|
||||||
|
mut terrain: ResMut<Terrain2D>,
|
||||||
|
frame_counter: Res<FrameCounter>,
|
||||||
|
mut debug_draw: ResMut<bevy_prototype_debug_lines::DebugLines>,
|
||||||
|
) {
|
||||||
let simulation_frame = (frame_counter.frame % u8::MAX as u64) as u8 + 1;
|
let simulation_frame = (frame_counter.frame % u8::MAX as u64) as u8 + 1;
|
||||||
|
|
||||||
let indices = terrain
|
let indices = terrain
|
||||||
|
|
@ -152,21 +153,29 @@ fn terrain_simulation(mut terrain: ResMut<Terrain2D>, frame_counter: Res<FrameCo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distribute gas
|
// Distribute gas
|
||||||
disperse_gas(global_positions, &mut terrain, &frame_counter)
|
disperse_gas(
|
||||||
|
global_positions,
|
||||||
|
&mut terrain,
|
||||||
|
&frame_counter,
|
||||||
|
&mut debug_draw,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Don't update if the result of dispersion is similar to before
|
||||||
fn disperse_gas(
|
fn disperse_gas(
|
||||||
global_positions: Vec<Vector2I>,
|
global_positions: Vec<Vector2I>,
|
||||||
terrain: &mut Terrain2D,
|
terrain: &mut Terrain2D,
|
||||||
frame_counter: &FrameCounter,
|
frame_counter: &FrameCounter,
|
||||||
|
debug_draw: &mut bevy_prototype_debug_lines::DebugLines,
|
||||||
) {
|
) {
|
||||||
use u32 as Capacity;
|
use u32 as Capacity;
|
||||||
let mut total_densities: HashMap<TexelID, Capacity> = HashMap::new();
|
use u8 as Min;
|
||||||
// let mut total_densities: Vec<(TexelID, Capacity)> = vec![];
|
use u8 as Max;
|
||||||
|
let mut total_densities: HashMap<TexelID, (Capacity, Min, Max)> = HashMap::new();
|
||||||
let mut valid_globals = vec![];
|
let mut valid_globals = vec![];
|
||||||
for global in global_positions.iter() {
|
for global in global_positions.iter() {
|
||||||
let (texel, behaviour) = terrain.get_texel_behaviour(global);
|
let (texel, behaviour) = terrain.get_texel_behaviour(global);
|
||||||
|
|
@ -176,37 +185,75 @@ fn disperse_gas(
|
||||||
match (texel, behaviour) {
|
match (texel, behaviour) {
|
||||||
(Some(texel), Some(behaviour)) => {
|
(Some(texel), Some(behaviour)) => {
|
||||||
if behaviour.form == TexelForm::Gas {
|
if behaviour.form == TexelForm::Gas {
|
||||||
total_densities.insert(
|
if let Some((old_density, old_min, old_max)) = total_densities.get(&texel.id) {
|
||||||
texel.id,
|
total_densities.insert(
|
||||||
texel.density as u32
|
texel.id,
|
||||||
+ total_densities.get(&texel.id).map_or(0, |density| *density),
|
(
|
||||||
);
|
texel.density as u32 + *old_density,
|
||||||
|
texel.density.min(*old_min),
|
||||||
|
texel.density.max(*old_max),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
total_densities.insert(
|
||||||
|
texel.id,
|
||||||
|
(texel.density as u32, texel.density, texel.density),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, _) => (),
|
(_, _) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut total_densities: Vec<(TexelID, Capacity)> =
|
let mut total_densities: Vec<(TexelID, Capacity, Min, Max)> = total_densities
|
||||||
total_densities.iter().map(|(t, d)| (*t, *d)).collect();
|
.iter()
|
||||||
|
.map(|(t, (d, min, max))| (*t, *d, *min, *max))
|
||||||
|
.collect();
|
||||||
|
|
||||||
if total_densities.len() == 0 {
|
if total_densities.len() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
total_densities.sort_unstable_by_key(|(_, density)| *density);
|
total_densities.sort_unstable_by_key(|(_, density, _, _)| *density);
|
||||||
total_densities.reverse();
|
total_densities.reverse();
|
||||||
|
|
||||||
const TILE_CAPACITY: u32 = u8::MAX as u32;
|
const TILE_CAPACITY: u32 = u8::MAX as u32;
|
||||||
let free_slots = valid_globals.len() as u32
|
let free_slots = valid_globals.len() as u32
|
||||||
- total_densities
|
- total_densities
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, v)| (*v / (TILE_CAPACITY + 1)) + 1)
|
.map(|(_, v, _, _)| (*v / (TILE_CAPACITY + 1)) + 1)
|
||||||
.sum::<u32>();
|
.sum::<u32>();
|
||||||
|
|
||||||
|
// Stop if the gas is already close to a stable state
|
||||||
|
const STABLE_TRESHOLD: u8 = 3;
|
||||||
|
if total_densities.iter().all(|(_, _, min, max)| {
|
||||||
|
if u8::abs_diff(*min, *max) > STABLE_TRESHOLD {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free_slots > 0 && *max <= STABLE_TRESHOLD
|
||||||
|
}) {
|
||||||
|
// // DEBUG: draw box for stabilized area
|
||||||
|
// let mut min = valid_globals.first().unwrap().clone();
|
||||||
|
// let mut max = valid_globals.first().unwrap().clone();
|
||||||
|
// for global in valid_globals.iter() {
|
||||||
|
// min = Vector2I::min(&min, global);
|
||||||
|
// max = Vector2I::max(&max, global);
|
||||||
|
// }
|
||||||
|
// max = max + Vector2I::ONE;
|
||||||
|
// crate::game::debug::terrain::draw_box(
|
||||||
|
// debug_draw,
|
||||||
|
// Vec3::from(min),
|
||||||
|
// Vec3::from(max),
|
||||||
|
// Color::CYAN,
|
||||||
|
// 0.0,
|
||||||
|
// );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate slots
|
// Allocate slots
|
||||||
let mut slots: Vec<(TexelID, u32)> = vec![];
|
let mut slots: Vec<(TexelID, u32)> = vec![];
|
||||||
for (id, density) in total_densities.iter() {
|
for (id, density, _, _) in total_densities.iter() {
|
||||||
let min_slots = (density / (TILE_CAPACITY + 1)) + 1;
|
let min_slots = (density / (TILE_CAPACITY + 1)) + 1;
|
||||||
slots.push((*id, min_slots));
|
slots.push((*id, min_slots));
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +264,7 @@ fn disperse_gas(
|
||||||
|
|
||||||
// Disperse into given slots
|
// Disperse into given slots
|
||||||
let mut texels: Vec<Texel2D> = vec![];
|
let mut texels: Vec<Texel2D> = vec![];
|
||||||
for (id, total_density) in total_densities.iter() {
|
for (id, total_density, _, _) in total_densities.iter() {
|
||||||
let slots = slots.iter().find(|s| s.0 == *id).unwrap().1;
|
let slots = slots.iter().find(|s| s.0 == *id).unwrap().1;
|
||||||
let mut density_left = *total_density;
|
let mut density_left = *total_density;
|
||||||
for i in 0..slots {
|
for i in 0..slots {
|
||||||
|
|
@ -242,6 +289,7 @@ fn disperse_gas(
|
||||||
panic!("disperse_gas() - valid_globals is shorter than texels");
|
panic!("disperse_gas() - valid_globals is shorter than texels");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fastrand::shuffle(&mut valid_globals);
|
||||||
for i in 0..valid_globals.len() {
|
for i in 0..valid_globals.len() {
|
||||||
let global = valid_globals[i];
|
let global = valid_globals[i];
|
||||||
if i < texels.len() {
|
if i < texels.len() {
|
||||||
|
|
@ -266,35 +314,37 @@ fn simulate_texel(global: Vector2I, terrain: &mut Terrain2D, frame_counter: &Fra
|
||||||
let grav_offset = Vector2I::from(gravity);
|
let grav_offset = Vector2I::from(gravity);
|
||||||
let grav_pos = global + grav_offset;
|
let grav_pos = global + grav_offset;
|
||||||
|
|
||||||
// Try falling
|
if behaviour.form != TexelForm::Gas || gravity.abs() > fastrand::u8(0..u8::MAX) {
|
||||||
{
|
// Try falling
|
||||||
let (_, other_behaviour) = terrain.get_texel_behaviour(&grav_pos);
|
{
|
||||||
if TexelBehaviour2D::can_displace(&behaviour, &other_behaviour) {
|
let (_, other_behaviour) = terrain.get_texel_behaviour(&grav_pos);
|
||||||
terrain.swap_texels(&global, &grav_pos, Some(simulation_frame));
|
if TexelBehaviour2D::can_displace(&behaviour, &other_behaviour) {
|
||||||
return;
|
terrain.swap_texels(&global, &grav_pos, Some(simulation_frame));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if terrain.can_transfer_density(&global, &grav_pos) {
|
||||||
|
terrain.transfer_density(&global, &grav_pos, gravity, Some(simulation_frame))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if terrain.can_transfer_density(&global, &grav_pos) {
|
|
||||||
terrain.transfer_density(&global, &grav_pos, gravity, Some(simulation_frame))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try "sliding"
|
// Try "sliding"
|
||||||
let mut dirs = vec![Vector2I::RIGHT, Vector2I::LEFT];
|
let mut dirs = vec![Vector2I::RIGHT, Vector2I::LEFT];
|
||||||
if ((frame_counter.frame / 73) % 2) as i32 == global.y % 2 {
|
if ((frame_counter.frame / 73) % 2) as i32 == global.y % 2 {
|
||||||
dirs.reverse();
|
dirs.reverse();
|
||||||
}
|
|
||||||
for dir in dirs.iter() {
|
|
||||||
let slide_pos = match behaviour.form {
|
|
||||||
TexelForm::Solid => grav_pos + *dir,
|
|
||||||
TexelForm::Liquid | TexelForm::Gas => global + *dir,
|
|
||||||
};
|
|
||||||
let (_, other_behaviour) = terrain.get_texel_behaviour(&slide_pos);
|
|
||||||
if TexelBehaviour2D::can_displace(&behaviour, &other_behaviour) {
|
|
||||||
terrain.swap_texels(&global, &slide_pos, Some(simulation_frame));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if terrain.can_transfer_density(&global, &grav_pos) {
|
for dir in dirs.iter() {
|
||||||
terrain.transfer_density(&global, &grav_pos, gravity, Some(simulation_frame))
|
let slide_pos = match behaviour.form {
|
||||||
|
TexelForm::Solid => grav_pos + *dir,
|
||||||
|
TexelForm::Liquid | TexelForm::Gas => global + *dir,
|
||||||
|
};
|
||||||
|
let (_, other_behaviour) = terrain.get_texel_behaviour(&slide_pos);
|
||||||
|
if TexelBehaviour2D::can_displace(&behaviour, &other_behaviour) {
|
||||||
|
terrain.swap_texels(&global, &slide_pos, Some(simulation_frame));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if terrain.can_transfer_density(&global, &grav_pos) {
|
||||||
|
terrain.transfer_density(&global, &grav_pos, gravity, Some(simulation_frame))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -331,13 +381,16 @@ pub struct Terrain2D {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terrain2D {
|
impl Terrain2D {
|
||||||
|
pub const WORLD_WIDTH: i32 = 512;
|
||||||
|
pub const WORLD_HEIGHT: i32 = Self::WORLD_WIDTH * 2;
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
top_boundary: Option<i32>,
|
top_boundary: Option<i32>,
|
||||||
bottom_boundary: Option<i32>,
|
bottom_boundary: Option<i32>,
|
||||||
left_boundary: Option<i32>,
|
left_boundary: Option<i32>,
|
||||||
right_boundary: Option<i32>,
|
right_boundary: Option<i32>,
|
||||||
) -> Terrain2D {
|
) -> Self {
|
||||||
Terrain2D {
|
Self {
|
||||||
chunk_map: HashMap::new(),
|
chunk_map: HashMap::new(),
|
||||||
events: Vec::new(),
|
events: Vec::new(),
|
||||||
top_boundary,
|
top_boundary,
|
||||||
|
|
@ -518,11 +571,18 @@ impl Terrain2D {
|
||||||
) {
|
) {
|
||||||
let from = self.get_texel(from_global).unwrap_or_default();
|
let from = self.get_texel(from_global).unwrap_or_default();
|
||||||
let to = self.get_texel(to_global).unwrap_or_default();
|
let to = self.get_texel(to_global).unwrap_or_default();
|
||||||
let max_transfer = match gravity {
|
let max_transfer = gravity.abs();
|
||||||
TexelGravity::Down(grav) => grav,
|
|
||||||
TexelGravity::Up(grav) => grav,
|
// DEBUG: Test this out, another property?
|
||||||
};
|
const MAX_TARGET_DENSITY: u8 = 25;
|
||||||
let transfer = (u8::MAX - to.density).min(max_transfer).min(from.density);
|
let transfer = (u8::MAX - to.density)
|
||||||
|
.min(max_transfer)
|
||||||
|
.min(from.density)
|
||||||
|
.min(MAX_TARGET_DENSITY.max(to.density) - to.density);
|
||||||
|
if transfer == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if from.density - transfer == 0 {
|
if from.density - transfer == 0 {
|
||||||
self.set_texel(&from_global, Texel2D::default(), simulation_frame);
|
self.set_texel(&from_global, Texel2D::default(), simulation_frame);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use noise::{NoiseFn, PerlinSurflet};
|
use noise::{NoiseFn, PerlinSurflet};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::util::{inverse_lerp, lerp};
|
||||||
|
|
||||||
pub struct TerrainGen2D {
|
pub struct TerrainGen2D {
|
||||||
pub seed: u32,
|
pub seed: u32,
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ lazy_static! {
|
||||||
name: Cow::Borrowed("light gas"),
|
name: Cow::Borrowed("light gas"),
|
||||||
color: Color::rgba(0.0, 1.0, 0.0, 0.5),
|
color: Color::rgba(0.0, 1.0, 0.0, 0.5),
|
||||||
form: TexelForm::Gas,
|
form: TexelForm::Gas,
|
||||||
gravity: Some(TexelGravity::Up(10)),
|
gravity: Some(TexelGravity::Up(160)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -81,7 +81,7 @@ lazy_static! {
|
||||||
name: Cow::Borrowed("heavy gas"),
|
name: Cow::Borrowed("heavy gas"),
|
||||||
color: Color::rgba(1.0, 0.5, 0.5, 0.5),
|
color: Color::rgba(1.0, 0.5, 0.5, 0.5),
|
||||||
form: TexelForm::Gas,
|
form: TexelForm::Gas,
|
||||||
gravity: Some(TexelGravity::Down(10)),
|
gravity: Some(TexelGravity::Down(60)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -128,9 +128,16 @@ lazy_static! {
|
||||||
|
|
||||||
result
|
result
|
||||||
};
|
};
|
||||||
|
static ref FORM_DISPLACEMENT_PRIORITY: HashMap<TexelForm, u8> = {
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
result.insert(TexelForm::Gas, 0);
|
||||||
|
result.insert(TexelForm::Liquid, 1);
|
||||||
|
result.insert(TexelForm::Solid, 2);
|
||||||
|
result
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
pub enum TexelForm {
|
pub enum TexelForm {
|
||||||
#[default]
|
#[default]
|
||||||
// Solid materials, when affected by gravity, create pyramid-like piles
|
// Solid materials, when affected by gravity, create pyramid-like piles
|
||||||
|
|
@ -141,6 +148,15 @@ pub enum TexelForm {
|
||||||
Gas,
|
Gas,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TexelForm {
|
||||||
|
fn priority(&self) -> u8 {
|
||||||
|
FORM_DISPLACEMENT_PRIORITY
|
||||||
|
.get(self)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum TexelGravity {
|
pub enum TexelGravity {
|
||||||
Down(u8),
|
Down(u8),
|
||||||
|
|
@ -156,6 +172,15 @@ impl From<TexelGravity> for Vector2I {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TexelGravity {
|
||||||
|
pub fn abs(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
TexelGravity::Down(grav) => *grav,
|
||||||
|
TexelGravity::Up(grav) => *grav,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TexelBehaviour2D {
|
pub struct TexelBehaviour2D {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
|
|
@ -206,7 +231,10 @@ impl TexelBehaviour2D {
|
||||||
let to = if let Some(to) = to { to } else { return true };
|
let to = if let Some(to) = to { to } else { return true };
|
||||||
|
|
||||||
match (from.form, to.form) {
|
match (from.form, to.form) {
|
||||||
(_, to_form) => {
|
(from_form, to_form) => {
|
||||||
|
if from_form.priority() != to_form.priority() {
|
||||||
|
return from_form.priority() > to_form.priority();
|
||||||
|
}
|
||||||
if let (Some(from_grav), Some(to_grav)) = (from.gravity, to.gravity) {
|
if let (Some(from_grav), Some(to_grav)) = (from.gravity, to.gravity) {
|
||||||
match (from_grav, to_grav) {
|
match (from_grav, to_grav) {
|
||||||
(TexelGravity::Down(from_grav), TexelGravity::Down(to_grav)) => {
|
(TexelGravity::Down(from_grav), TexelGravity::Down(to_grav)) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue