From fc8c21569b3cea6fe9e8e87f31a5b2cb6c4794d7 Mon Sep 17 00:00:00 2001 From: Isaac Turci <78173025+Zac8668@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:05:27 -0300 Subject: [PATCH] Added "atoms.ron" file --- Cargo.lock | 134 +++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +- assets/atoms.ron | 20 +++++++ src/actors.rs | 92 +++++++++++++++++++++++------ src/atom.rs | 65 +++++++-------------- src/chunk.rs | 2 +- src/chunk_manager.rs | 31 +++++----- src/debug.rs | 6 +- src/main.rs | 7 ++- src/manager_api.rs | 46 ++++++++------- src/materials.rs | 132 ++++++++++++++++++++++++++++++++++++++++++ src/particles.rs | 17 +++--- src/player.rs | 29 +++++----- 13 files changed, 453 insertions(+), 131 deletions(-) create mode 100644 assets/atoms.ron create mode 100644 src/materials.rs diff --git a/Cargo.lock b/Cargo.lock index 3af522b..b1d129d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,6 +249,7 @@ dependencies = [ "fastrand 2.0.1", "itertools", "rand", + "ron", "serde", "serde-big-array", "serde_derive", @@ -467,6 +468,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + [[package]] name = "bevy" version = "0.12.0" @@ -599,6 +606,7 @@ dependencies = [ "futures-io", "futures-lite 1.13.0", "js-sys", + "notify-debouncer-full", "parking_lot", "ron", "serde", @@ -808,7 +816,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85adc6b1fc86687bf67149e0bafaa4d6da432232fa956472d1b37f19121d3ace" dependencies = [ - "base64", + "base64 0.13.1", "bevy_animation", "bevy_app", "bevy_asset", @@ -1336,6 +1344,9 @@ name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] [[package]] name = "blake3" @@ -1918,6 +1929,27 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "file-id" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1991,6 +2023,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -2367,6 +2408,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -2500,6 +2561,26 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "ktx2" version = "0.3.0" @@ -2815,6 +2896,39 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99756f5493e135528f0cd660ac67b4c3a542bb65a3565efe92bb2c2317eb3669" +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.1", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +dependencies = [ + "crossbeam-channel", + "file-id", + "log", + "notify", + "parking_lot", + "walkdir", +] + [[package]] name = "ntapi" version = "0.4.0" @@ -3290,6 +3404,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -3363,13 +3486,14 @@ dependencies = [ [[package]] name = "ron" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64", - "bitflags 1.3.2", + "base64 0.21.5", + "bitflags 2.4.1", "serde", + "serde_derive", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5d381ee..598e28c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "PolyForm NonCommercial v1.0" default-run = "astratomic" [dependencies] -bevy = "0.12" +bevy = {version = "0.12", features =["file_watcher"]} bevy-inspector-egui = "0.22" rand = { version = "0.8.5", features = ["small_rng"] } fastrand = "2.0.1" @@ -19,6 +19,7 @@ serde = "1.0" serde_derive = "1.0" bincode = "1.3.3" serde-big-array = "0.5.1" +ron = "0.8.1" bevy-async-task = "1.3.1" # Enable a small amount of optimization in debug mode diff --git a/assets/atoms.ron b/assets/atoms.ron new file mode 100644 index 0000000..1afbb90 --- /dev/null +++ b/assets/atoms.ron @@ -0,0 +1,20 @@ +([ + //0 Void + Void, + + //1 Dummy atom + Object, + + //2 Sand + Powder, + //3 Water + Liquid ( flow: 5 ), + //4 Gravel + Powder, + //5 Lava + Liquid ( flow: 1 ), + //6 Dirt + Solid, + //7 Rock + Solid +]) \ No newline at end of file diff --git a/src/actors.rs b/src/actors.rs index da8302c..556dc74 100644 --- a/src/actors.rs +++ b/src/actors.rs @@ -13,13 +13,16 @@ pub fn add_actors( mut chunk_manager: ResMut, actors: Query<&Actor>, mut dirty_rects: ResMut, + materials: (Res>, Res), ) { + let materials = materials.0.get(materials.1 .0.clone()).unwrap(); + for actor in actors.iter() { for x_off in 0..actor.width as i32 { for y_off in 0..actor.height as i32 { let pos = global_to_chunk(actor.pos + ivec2(x_off, y_off)); if let Some(atom) = chunk_manager.get_mut_atom(pos) { - if atom.state == State::Void { + if materials[atom.id].is_void() { *atom = Atom::object(); } } @@ -30,13 +33,19 @@ pub fn add_actors( } //Called after simulation, before actor update -pub fn remove_actors(mut chunk_manager: ResMut, actors: Query<&Actor>) { +pub fn remove_actors( + mut chunk_manager: ResMut, + actors: Query<&Actor>, + materials: (Res>, Res), +) { + let materials = materials.0.get(materials.1 .0.clone()).unwrap(); + for actor in actors.iter() { for x_off in 0..actor.width as i32 { for y_off in 0..actor.height as i32 { let pos = global_to_chunk(actor.pos + ivec2(x_off, y_off)); if let Some(atom) = chunk_manager.get_mut_atom(pos) { - if atom.state == State::Object { + if materials[atom.id].is_object() { *atom = Atom::default(); } } @@ -45,12 +54,12 @@ pub fn remove_actors(mut chunk_manager: ResMut, actors: Query<&Act } } -pub fn on_ground(chunk_manager: &ChunkManager, actor: &Actor) -> bool { +pub fn on_ground(chunk_manager: &ChunkManager, actor: &Actor, materials: &Materials) -> bool { for x_off in 0..actor.width { let chunk_pos = global_to_chunk(actor.pos + ivec2(x_off as i32, actor.height as i32)); if let Some(atom) = chunk_manager.get_atom(&chunk_pos) { - if atom.state != State::Void { + if !materials[atom].is_void() { return true; } } else { @@ -61,7 +70,13 @@ pub fn on_ground(chunk_manager: &ChunkManager, actor: &Actor) -> bool { false } -pub fn update_actors(mut chunk_manager: ResMut, mut actors: Query<&mut Actor>) { +pub fn update_actors( + mut chunk_manager: ResMut, + mut actors: Query<&mut Actor>, + materials: (Res>, Res), +) { + let materials = materials.0.get(materials.1 .0.clone()).unwrap(); + for mut actor in actors.iter_mut() { let mut prev = actor.pos; for v in Line::new(actor.pos, actor.vel.as_ivec2()) { @@ -73,15 +88,20 @@ pub fn update_actors(mut chunk_manager: ResMut, mut actors: Query< }; if move_hor { - let moved_x = move_x(&mut chunk_manager, &mut actor, (v.x - prev.x).signum()); - if on_ground(&chunk_manager, &actor) { + let moved_x = move_x( + &mut chunk_manager, + &mut actor, + (v.x - prev.x).signum(), + materials, + ); + if on_ground(&chunk_manager, &actor, materials) { let starting_y = actor.pos.y; match moved_x { //If we can't move to the left or right //Check if we can get up a stair-like structure false => { for i in 1..=UP_WALK_HEIGHT { - let moved_y = move_y(&mut chunk_manager, &mut actor, -1); + let moved_y = move_y(&mut chunk_manager, &mut actor, -1, materials); //Abort if we couldn't move up, or if we moved up but couldn't move sideways on the last step if !moved_y || i == UP_WALK_HEIGHT @@ -89,9 +109,16 @@ pub fn update_actors(mut chunk_manager: ResMut, mut actors: Query< &mut chunk_manager, &mut actor, (v.x - prev.x).signum(), + materials, ) { - abort_stair(&mut chunk_manager, &mut actor, starting_y, 1); + abort_stair( + &mut chunk_manager, + &mut actor, + starting_y, + 1, + materials, + ); break; } } @@ -100,17 +127,28 @@ pub fn update_actors(mut chunk_manager: ResMut, mut actors: Query< //Check if we can snap back to the ground true => { for i in 1..=DOWN_WALK_HEIGHT { - if !move_y(&mut chunk_manager, &mut actor, 1) { + if !move_y(&mut chunk_manager, &mut actor, 1, materials) { break; } else if i == DOWN_WALK_HEIGHT { - abort_stair(&mut chunk_manager, &mut actor, starting_y, -1); + abort_stair( + &mut chunk_manager, + &mut actor, + starting_y, + -1, + materials, + ); } } } } } } else { - move_y(&mut chunk_manager, &mut actor, (v.y - prev.y).signum()); + move_y( + &mut chunk_manager, + &mut actor, + (v.y - prev.y).signum(), + materials, + ); } prev = v; @@ -118,13 +156,24 @@ pub fn update_actors(mut chunk_manager: ResMut, mut actors: Query< } } -pub fn abort_stair(chunk_manager: &mut ChunkManager, actor: &mut Actor, starting_y: i32, dir: i32) { +pub fn abort_stair( + chunk_manager: &mut ChunkManager, + actor: &mut Actor, + starting_y: i32, + dir: i32, + materials: &Materials, +) { for _ in 0..(starting_y - actor.pos.y) { - move_y(chunk_manager, actor, dir); + move_y(chunk_manager, actor, dir, materials); } } -pub fn move_x(chunk_manager: &mut ChunkManager, actor: &mut Actor, dir: i32) -> bool { +pub fn move_x( + chunk_manager: &mut ChunkManager, + actor: &mut Actor, + dir: i32, + materials: &Materials, +) -> bool { //Check if we can move for y_off in 0..actor.height as i32 { let pos = actor.pos @@ -139,7 +188,7 @@ pub fn move_x(chunk_manager: &mut ChunkManager, actor: &mut Actor, dir: i32) -> // Check bounds and if it's free to move if let Some(atom) = chunk_manager.get_mut_atom(chunk_pos) { - if atom.state == State::Powder || atom.state == State::Solid { + if materials[atom.id].is_powder() || materials[atom.id].is_solid() { actor.vel = Vec2::ZERO; return false; } @@ -154,7 +203,12 @@ pub fn move_x(chunk_manager: &mut ChunkManager, actor: &mut Actor, dir: i32) -> true } -pub fn move_y(chunk_manager: &mut ChunkManager, actor: &mut Actor, dir: i32) -> bool { +pub fn move_y( + chunk_manager: &mut ChunkManager, + actor: &mut Actor, + dir: i32, + materials: &Materials, +) -> bool { //Check if we can move for x_off in 0..actor.width as i32 { let pos = actor.pos @@ -169,7 +223,7 @@ pub fn move_y(chunk_manager: &mut ChunkManager, actor: &mut Actor, dir: i32) -> // Check bounds and if it's free to move if let Some(atom) = chunk_manager.get_mut_atom(chunk_pos) { - if atom.state == State::Powder || atom.state == State::Solid { + if materials[atom.id].is_powder() || materials[atom.id].is_solid() { actor.vel = Vec2::ZERO; return false; } diff --git a/src/atom.rs b/src/atom.rs index 3a4b916..dc22a37 100644 --- a/src/atom.rs +++ b/src/atom.rs @@ -2,14 +2,14 @@ use rand::Rng; use std::collections::HashSet; use std::f32::consts::PI; +use serde::Deserialize; + use crate::prelude::*; -// TODO Make smaller #[derive(Clone, Copy, Default, PartialEq, Debug, Serialize, Deserialize)] pub struct Atom { pub color: [u8; 4], - pub state: State, - pub properties: Option, + pub id: u8, #[serde(skip)] pub speed: (i8, i8), @@ -23,21 +23,22 @@ pub struct Atom { impl Atom { pub fn object() -> Self { Atom { - state: State::Object, + id: 1, //color: [255, 255, 255, 255], ..Default::default() } } - //TODO Change this to a yaml file pub fn new(id: u8) -> Atom { - let mut atom = Atom::default(); + let mut atom = Atom { + id, + ..Default::default() + }; - //Change color and state, etc + //Change color and material, etc match id { - 0 => { + 2 => { //Sand - atom.state = State::Powder; atom.color = [ (230 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, (197 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, @@ -45,20 +46,17 @@ impl Atom { 255, ]; } - 1 => { + 3 => { //Water - atom.state = State::Liquid; atom.color = [ (20 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, (125 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, (204 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, 150, ]; - atom.properties = Some(Properties::Liquid { flow: 5 }); } - 2 => { + 4 => { //Gravel - atom.state = State::Powder; atom.color = [ (110 + rand::thread_rng().gen_range(-12_i16..12_i16)) as u8, (110 + rand::thread_rng().gen_range(-12_i16..12_i16)) as u8, @@ -66,20 +64,17 @@ impl Atom { 255, ]; } - 3 => { + 5 => { //Lava - atom.state = State::Liquid; atom.color = [ (245 + rand::thread_rng().gen_range(-10_i16..10_i16)) as u8, (140 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, (10 + rand::thread_rng().gen_range(-10_i16..10_i16)) as u8, 255, ]; - atom.properties = Some(Properties::Liquid { flow: 1 }); } - 4 => { + 6 => { //Dirt - atom.state = State::Powder; atom.color = [ (120 + rand::thread_rng().gen_range(-10_i16..10_i16)) as u8, (70 + rand::thread_rng().gen_range(-10_i16..10_i16)) as u8, @@ -94,23 +89,6 @@ impl Atom { } } -// TODO Change this to a Material type -#[derive(Default, Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] -pub enum State { - Solid, - Powder, - Liquid, - Gas, - Object, - #[default] - Void, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] -pub enum Properties { - Liquid { flow: u8 }, -} - // Update different types of atoms /// Updates powder and returns atoms awakened @@ -127,7 +105,7 @@ pub fn update_powder(chunks: &mut UpdateChunksType, pos: IVec2, dt: u8) -> HashS } for _ in 0..speed { - let neigh = down_neigh(chunks, cur_pos, &[(State::Liquid, 0.2)], dt); + let neigh = down_neigh(chunks, cur_pos, &[(3, 0.2)], dt); let mut swapped = false; for neigh in neigh { if neigh.0 { @@ -205,8 +183,8 @@ pub fn update_liquid( if let Some(side) = side { for _ in 0..flow { - let state = get_state(chunks, cur_pos); - if !swapable(chunks, cur_pos + IVec2::new(side, 0), &[], state, dt) { + let material = get_material(chunks, cur_pos); + if !swapable(chunks, cur_pos + IVec2::new(side, 0), &[], material, dt) { break; } @@ -236,13 +214,12 @@ pub fn update_atom(chunks: &mut UpdateChunksType, pos: IVec2, dt: u8) -> HashSet // Move for pos in Line::new(cur_pos, vel) { awakened.insert(cur_pos); - let state = get_state(chunks, cur_pos); - if swapable(chunks, pos, &[], state, dt) { + let material = get_material(chunks, cur_pos); + if swapable(chunks, pos, &[], material, dt) { swap(chunks, cur_pos, pos, dt); cur_pos = pos; awakened.insert(cur_pos); - } else if get_state(chunks, pos) == State::Liquid - && get_state(chunks, cur_pos) == State::Liquid + } else if get_material(chunks, pos).is_liquid() && get_material(chunks, cur_pos).is_liquid() { awakened.insert(pos); set_vel(chunks, pos, vel * 4 / 5); @@ -255,7 +232,7 @@ pub fn update_atom(chunks: &mut UpdateChunksType, pos: IVec2, dt: u8) -> HashSet cur_pos, (Vec2::from_angle(PI).rotate(vel.as_vec2()) * 0.5).as_ivec2(), ); - } else if !swapable(chunks, cur_pos + IVec2::Y, &[], state, dt) { + } else if !swapable(chunks, cur_pos + IVec2::Y, &[], material, dt) { set_vel(chunks, cur_pos, IVec2::ZERO); } break; diff --git a/src/chunk.rs b/src/chunk.rs index 4690e46..e42e6c7 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -31,7 +31,7 @@ impl Chunk { Ordering::Less => {} _ => { for atom in &mut atoms { - atom.state = State::Powder; + atom.id = 2; atom.color = [ (230 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, (197 + rand::thread_rng().gen_range(-20_i16..20_i16)) as u8, diff --git a/src/chunk_manager.rs b/src/chunk_manager.rs index 8382b78..3c039ab 100644 --- a/src/chunk_manager.rs +++ b/src/chunk_manager.rs @@ -212,6 +212,7 @@ pub fn manager_setup( pub fn chunk_manager_update( mut chunk_manager: ResMut, mut dirty_rects_resource: ResMut, + materials: (Res>, Res), ) { chunk_manager.dt = chunk_manager.dt.wrapping_add(1); let dt = chunk_manager.dt; @@ -223,6 +224,9 @@ pub fn chunk_manager_update( render: render_dirty_rects, } = &mut *dirty_rects_resource; + //Get materials + let materials = &materials.0.get(materials.1 .0.clone()).unwrap(); + let first_x = chunk_manager.pos.x; let first_y = chunk_manager.pos.y; let last_x = chunk_manager.pos.x + LOAD_WIDTH; @@ -370,6 +374,7 @@ pub fn chunk_manager_update( group: chunk_group, dirty_update_rect_send, dirty_render_rect_send, + materials, }, dt, rect, @@ -389,6 +394,8 @@ pub fn chunk_manager_update( } pub fn update_chunks(chunks: &mut UpdateChunksType, dt: u8, dirty_rect: &URect) { + let materials = chunks.materials; + let x_iter = rand_range(dirty_rect.min.x as i32..dirty_rect.max.x as i32 + 1).into_iter(); let y_iter = rand_range(dirty_rect.min.y as i32..dirty_rect.max.y as i32 + 1).into_iter(); for (y, x) in y_iter.cartesian_product(x_iter) { @@ -400,16 +407,17 @@ pub fn update_chunks(chunks: &mut UpdateChunksType, dt: u8, dirty_rect: &URect) } let mut awake_self = false; - let state; + let id; let speed; - let prop; { let atom = &mut chunks.group[local_pos]; - state = atom.state; + id = atom.id; speed = atom.speed; - prop = atom.properties; - if atom.f_idle < FRAMES_SLEEP && state != State::Void && state != State::Solid { + if atom.f_idle < FRAMES_SLEEP + && materials[id] != Material::Void + && materials[id] != Material::Solid + { atom.f_idle += 1; awake_self = true; } @@ -418,16 +426,9 @@ pub fn update_chunks(chunks: &mut UpdateChunksType, dt: u8, dirty_rect: &URect) let (vector, mut awakened) = if speed.0 == 0 && speed.1 >= 0 { ( false, - match state { - State::Powder => update_powder(chunks, pos, dt), - State::Liquid => { - let flow = if let Some(Properties::Liquid { flow }) = prop { - flow - } else { - 3 - }; - update_liquid(chunks, pos, flow, dt) - } + match materials[id] { + Material::Powder => update_powder(chunks, pos, dt), + Material::Liquid { flow } => update_liquid(chunks, pos, flow, dt), _ => HashSet::new(), }, ) diff --git a/src/debug.rs b/src/debug.rs index eb80722..bd82395 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -16,11 +16,11 @@ fn brush( ) { let atom; if input.0.pressed(MouseButton::Middle) { - atom = Atom::new(0); + atom = Atom::new(2); } else if input.1.pressed(KeyCode::ControlLeft) { - atom = Atom::new(1); + atom = Atom::new(3); } else if input.1.pressed(KeyCode::ShiftLeft) { - atom = Atom::new(2); + atom = Atom::new(4); } else { return; } diff --git a/src/main.rs b/src/main.rs index 147fe11..3b73ace 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,13 +10,13 @@ mod consts; mod debug; mod geom_tools; mod manager_api; +mod materials; mod particles; mod player; mod prelude { - pub use crate::atom::State; pub use crate::{ actors::*, animation::*, atom::*, chunk::*, chunk_group::*, chunk_manager::*, consts::*, - debug::*, geom_tools::*, manager_api::*, particles::*, player::*, + debug::*, geom_tools::*, manager_api::*, materials::*, particles::*, player::*, }; pub use bevy::input::mouse::MouseScrollUnit; pub use bevy::input::mouse::MouseWheel; @@ -33,6 +33,8 @@ mod prelude { pub use std::fs; pub use std::fs::File; pub use std::io::Write; + + pub use crate::materials::Material; } use prelude::*; @@ -50,6 +52,7 @@ fn main() { PlayerPlugin, animation::AnimationPlugin, ParticlesPlugin, + MaterialsPlugin, )) .add_systems(Startup, setup_camera); diff --git a/src/manager_api.rs b/src/manager_api.rs index 15fbb3e..5c51017 100644 --- a/src/manager_api.rs +++ b/src/manager_api.rs @@ -11,20 +11,21 @@ pub struct UpdateChunksType<'a> { pub group: ChunkGroup<'a>, pub dirty_update_rect_send: &'a Sender, pub dirty_render_rect_send: &'a Sender, + pub materials: &'a Materials, } /// Swap two atoms from global 3x3 chunks positions pub fn swap(chunks: &mut UpdateChunksType, pos1: IVec2, pos2: IVec2, dt: u8) { - let states = [get_state(chunks, pos1), get_state(chunks, pos2)]; + let materials = [get_material(chunks, pos1), get_material(chunks, pos2)]; let chunk_group = &mut chunks.group; { let temp = chunk_group[pos1]; - chunk_group[pos1] = if states[1] == State::Object { + chunk_group[pos1] = if materials[1].is_object() { Atom::default() } else { chunk_group[pos2] }; - chunk_group[pos2] = if states[0] == State::Object { + chunk_group[pos2] = if materials[0].is_object() { Atom::default() } else { temp @@ -93,20 +94,23 @@ pub fn global_to_chunk(mut pos: IVec2) -> ChunkPos { } /// See if position is swapable, that means it sees if the position is a void -/// or if it's a swapable state and has been not updated +/// or if it's a swapable material and has been not updated pub fn swapable( chunks: &UpdateChunksType, pos: IVec2, - states: &[(State, f32)], - state: State, + ids: &[(u8, f32)], + material: Material, dt: u8, ) -> bool { if let Some(atom) = chunks.group.get_global(pos) { - atom.state == State::Void - || (states.iter().any(|&(state, prob)| { - state == atom.state && rand::thread_rng().gen_range(0.0..1.0) < prob - }) && atom.updated_at != dt) - || (atom.state == State::Object && state == State::Liquid) + let atom_material = chunks.materials.0[atom.id as usize]; + + atom_material == Material::Void + || (ids + .iter() + .any(|&(id, prob)| id == atom.id && rand::thread_rng().gen_range(0.0..1.0) < prob) + && atom.updated_at != dt) + || (atom_material.is_object() && material.is_liquid()) } else { false } @@ -116,14 +120,14 @@ pub fn swapable( pub fn down_neigh( chunks: &UpdateChunksType, pos: IVec2, - states: &[(State, f32)], + ids: &[(u8, f32)], dt: u8, ) -> [(bool, IVec2); 3] { let mut neigh = [(false, IVec2::ZERO); 3]; - let state = get_state(chunks, pos); + let material = get_material(chunks, pos); for (neigh, x) in neigh.iter_mut().zip([0, -1, 1]) { - neigh.0 = swapable(chunks, pos + IVec2::new(x, 1), states, state, dt); + neigh.0 = swapable(chunks, pos + IVec2::new(x, 1), ids, material, dt); neigh.1 = IVec2::new(x, 1); } @@ -138,14 +142,14 @@ pub fn down_neigh( pub fn side_neigh( chunks: &UpdateChunksType, pos: IVec2, - states: &[(State, f32)], + ids: &[(u8, f32)], dt: u8, ) -> [(bool, IVec2); 2] { let mut neigh = [(false, IVec2::ZERO); 2]; - let state = get_state(chunks, pos); + let material = get_material(chunks, pos); for (neigh, x) in neigh.iter_mut().zip([-1, 1]) { - neigh.0 = swapable(chunks, pos + IVec2::new(x, 0), states, state, dt); + neigh.0 = swapable(chunks, pos + IVec2::new(x, 0), ids, material, dt); neigh.1 = IVec2::new(x, 0); } @@ -180,15 +184,15 @@ pub fn set_vel(chunks: &mut UpdateChunksType, pos: IVec2, vel: IVec2) { chunks.group[pos].speed.1 = vel.y as i8; } -/// Gets state from a global pos -pub fn get_state(chunks: &UpdateChunksType, pos: IVec2) -> State { - chunks.group[pos].state +/// Gets material from a global pos +pub fn get_material(chunks: &UpdateChunksType, pos: IVec2) -> Material { + chunks.materials.0[chunks.group[pos].id as usize] } /// Checks if atom is able to update this frame from a global pos pub fn dt_updatable(chunks: &UpdateChunksType, pos: IVec2, dt: u8) -> bool { if let Some(atom) = chunks.group.get_global(pos) { - atom.updated_at != dt || atom.state == State::Void + atom.updated_at != dt || (chunks.materials.0[atom.id as usize]).is_void() } else { false } diff --git a/src/materials.rs b/src/materials.rs new file mode 100644 index 0000000..553ca67 --- /dev/null +++ b/src/materials.rs @@ -0,0 +1,132 @@ +use crate::prelude::*; + +use bevy::utils::thiserror; +use bevy::{ + asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}, + prelude::*, + reflect::TypePath, + utils::BoxedFuture, +}; +use serde::Deserialize; +use thiserror::Error; + +#[derive(Default, Debug, Deserialize, PartialEq, Clone, Copy)] +pub enum Material { + Solid, + Powder, + Liquid { + flow: u8, + }, + Gas, + Object, + #[default] + Void, +} + +impl Material { + pub fn is_liquid(&self) -> bool { + matches!(self, Material::Liquid { .. }) + } + + pub fn is_void(&self) -> bool { + matches!(self, Material::Void) + } + + pub fn is_object(&self) -> bool { + matches!(self, Material::Object) + } + + pub fn is_powder(&self) -> bool { + matches!(self, Material::Powder) + } + + pub fn is_solid(&self) -> bool { + matches!(self, Material::Solid) + } +} + +#[derive(Asset, TypePath, Debug, Deserialize)] +pub struct Materials(pub Vec); + +impl Materials { + pub fn get_from_atom(&self, atom: &Atom) -> &Material { + &self.0[atom.id as usize] + } + + pub fn get_from_id(&self, id: u8) -> &Material { + &self.0[id as usize] + } +} + +impl std::ops::Index<&Atom> for Materials { + type Output = Material; + #[track_caller] + fn index(&self, atom: &Atom) -> &Self::Output { + self.get_from_atom(atom) + } +} + +impl std::ops::Index for Materials { + type Output = Material; + #[track_caller] + fn index(&self, id: u8) -> &Self::Output { + self.get_from_id(id) + } +} + +//Asset stuff + +#[derive(Resource, Default)] +pub struct MaterialsHandle(pub Handle); + +#[derive(Default)] +pub struct MaterialsLoader; + +#[non_exhaustive] +#[derive(Debug, Error)] +pub enum MaterialsLoaderError { + /// An [IO](std::io) Error + #[error("Could not load asset: {0}")] + Io(#[from] std::io::Error), + /// A [RON](ron) Error + #[error("Could not parse RON: {0}")] + RonSpannedError(#[from] ron::error::SpannedError), +} + +impl AssetLoader for MaterialsLoader { + type Asset = Materials; + type Settings = (); + type Error = MaterialsLoaderError; + fn load<'a>( + &'a self, + reader: &'a mut Reader, + _settings: &'a (), + _load_context: &'a mut LoadContext, + ) -> BoxedFuture<'a, Result> { + Box::pin(async move { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let custom_asset = ron::de::from_bytes::(&bytes)?; + Ok(custom_asset) + }) + } + + fn extensions(&self) -> &[&str] { + &["ron"] + } +} + +pub fn setup(mut materials_handle: ResMut, asset_server: Res) { + materials_handle.0 = asset_server.load("atoms.ron"); + println!("{:#?}", materials_handle.0); +} + +pub struct MaterialsPlugin; +impl Plugin for MaterialsPlugin { + fn build(&self, app: &mut App) { + app.init_asset::() + .init_resource::() + .init_asset_loader::() + .add_systems(Startup, setup); + } +} diff --git a/src/particles.rs b/src/particles.rs index 7cdc7b7..b08bef7 100644 --- a/src/particles.rs +++ b/src/particles.rs @@ -66,7 +66,10 @@ pub fn update_particles( entities: Query<&GlobalTransform, Without>, mut chunk_manager: ResMut, mut dirty_rects: ResMut, + materials: (Res>, Res), ) { + let materials = materials.0.get(materials.1 .0.clone()).unwrap(); + let compute_pool = ComputeTaskPool::get(); compute_pool.scope(|deferred_scope| { @@ -84,7 +87,7 @@ pub fn update_particles( } if let Some(atom) = chunk_manager.get_mut_atom(update.chunk_pos) { - if atom.state == State::Void { + if materials[atom.id].is_void() { *atom = update.atom; commands.entity(update.ent).despawn(); @@ -143,13 +146,13 @@ pub fn update_particles( [prev_chunk_pos.atom.d1()]; if particle.state == PartState::Normal - && atom.state != State::Void - && atom.state != State::Object + && !materials[atom.id].is_void() + && !materials[atom.id].is_object() { //Hit something! //If our previous pos is free - if prev_atom.state == State::Void - || prev_atom.state == State::Object + if materials[prev_atom.id].is_void() + || materials[prev_atom.id].is_object() { particle_send .try_send(DeferredParticleUpdate { @@ -168,8 +171,8 @@ pub fn update_particles( break; } else if particle.state == PartState::Looking - && atom.state == State::Void - || atom.state == State::Object + && materials[prev_atom.id].is_void() + || materials[prev_atom.id].is_object() { particle_send .try_send(DeferredParticleUpdate { diff --git a/src/player.rs b/src/player.rs index d2f24cf..2aa3955 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,5 @@ use bevy::sprite::Anchor; -use crate::atom::State; use crate::prelude::*; #[derive(Component)] @@ -15,7 +14,7 @@ impl Default for Player { Self { fuel: FUEL_MAX, jetpack: false, - atom_id: 0, + atom_id: 2, } } } @@ -89,10 +88,12 @@ pub fn update_player( mut player: Query<(&mut Actor, &mut Player, &mut AnimationIndices)>, chunk_manager: ResMut, mut camera: Query<&mut Transform, (Without, With)>, + materials: (Res>, Res), ) { let (mut actor, mut player, mut anim_idxs) = player.single_mut(); let (keys, mut scroll_evr) = input; let mut camera_transform = camera.single_mut(); + let materials = materials.0.get(materials.1 .0.clone()).unwrap(); // Gravity if actor.vel.y < TERM_VEL as f32 { @@ -104,7 +105,7 @@ pub fn update_player( actor.vel.x = x * RUN_SPEED; // Refuel - let on_ground = on_ground(&chunk_manager, &actor); + let on_ground = on_ground(&chunk_manager, &actor, materials); if on_ground { player.fuel = (player.fuel + FUEL_REGEN).clamp(0., Player::default().fuel); } @@ -157,36 +158,38 @@ pub fn update_player( //Change shooting atoms if keys.just_pressed(KeyCode::Key1) { - player.atom_id = 0; + player.atom_id = 2; } else if keys.just_pressed(KeyCode::Key2) { - player.atom_id = 1; + player.atom_id = 3; } else if keys.just_pressed(KeyCode::Key3) { - player.atom_id = 2; + player.atom_id = 4; } else if keys.just_pressed(KeyCode::Key4) { - player.atom_id = 3; + player.atom_id = 5; } else if keys.just_pressed(KeyCode::Key5) { - player.atom_id = 4; + player.atom_id = 6; } } pub fn tool_system( mut commands: Commands, mut tool: Query<(&mut Transform, &GlobalTransform, &mut Sprite), With>, - window: Query<&Window>, mut camera: Query<(&Camera, &GlobalTransform), Without>, tool_front_ent: Query>, - mut player: Query<(&mut TextureAtlasSprite, &Player)>, + querys: (Query<&Window>, Query<(&mut TextureAtlasSprite, &Player)>), resources: ( ResMut, ResMut, Res>, ), + materials: (Res>, Res), ) { let (mut tool_transform, tool_gtransform, mut tool_sprite) = tool.single_mut(); let (camera, camera_gtransform) = camera.single_mut(); - let window = window.single(); + let (window, mut player) = querys; let (mut textatlas_sprite, player) = player.single_mut(); let (mut chunk_manager, mut dirty_rects, mouse) = resources; + let window = window.single(); + let materials = materials.0.get(materials.1 .0.clone()).unwrap(); if let Some(world_position) = window .cursor_position() @@ -229,7 +232,7 @@ pub fn tool_system( chunk_manager.get_mut_atom(chunk_pos), Atom::new(player.atom_id), ) { - if atom.state == State::Void || atom.state == State::Object { + if materials[atom.id].is_void() || materials[atom.id].is_object() { let angle = fastrand::f32() * 0.5 - 0.25; let vel = (tool_slope * 10. * (fastrand::f32() * 0.2 + 0.8)) .rotate(vec2(angle.cos(), angle.sin())); @@ -252,7 +255,7 @@ pub fn tool_system( for vec in Line::new(tool_front.as_ivec2(), bound_vec - tool_front.as_ivec2()) { let chunk_pos = global_to_chunk(vec); if let Some(atom) = chunk_manager.get_mut_atom(chunk_pos) { - if atom.state != State::Void && atom.state != State::Object { + if !materials[atom.id].is_void() && !materials[atom.id].is_object() { commands.spawn(Particle { atom: *atom, pos: chunk_pos.to_global().as_vec2(),