From d4e49a04fc92ea66d0146c91ca80904049adc402 Mon Sep 17 00:00:00 2001 From: Tyrel Souza <923113+tyrelsouza@users.noreply.github.com> Date: Sat, 28 Aug 2021 00:25:52 -0400 Subject: [PATCH] entity control system --- Cargo.lock | 215 ++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- src/components.rs | 10 ++ src/main.rs | 35 ++++-- src/map.rs | 48 +------- src/player.rs | 36 ------ src/spawners.rs | 12 ++ src/systems/entity_render.rs | 17 +++ src/systems/map_render.rs | 22 ++++ src/systems/mod.rs | 12 ++ src/systems/player_input.rs | 31 +++++ 11 files changed, 346 insertions(+), 95 deletions(-) create mode 100644 src/components.rs delete mode 100644 src/player.rs create mode 100644 src/spawners.rs create mode 100644 src/systems/entity_render.rs create mode 100644 src/systems/map_render.rs create mode 100644 src/systems/mod.rs create mode 100644 src/systems/player_input.rs diff --git a/Cargo.lock b/Cargo.lock index c365c14..49be6f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,6 +60,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -384,6 +399,50 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils 0.8.5", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.5", + "lazy_static", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -395,6 +454,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + [[package]] name = "darling" version = "0.10.2" @@ -486,6 +555,22 @@ name = "dungeoncrawl" version = "0.1.0" dependencies = [ "bracket-lib", + "legion", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "erased-serde" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de9ad4541d99dc22b59134e7ff8dc3d6c988c89ecd7324bf10a8362b07a2afa" +dependencies = [ + "serde", ] [[package]] @@ -649,6 +734,15 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -692,6 +786,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -741,6 +844,40 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "legion" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa40f1a5f64dfdc1830657e0e7dd2c28087c4e32a2a85fcf63a286c429edefc" +dependencies = [ + "bit-set", + "crossbeam-channel 0.4.4", + "derivative", + "downcast-rs", + "erased-serde", + "itertools", + "legion_codegen", + "parking_lot", + "paste", + "rayon", + "serde", + "smallvec", + "thiserror", + "uuid", +] + +[[package]] +name = "legion_codegen" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c24e58060e656eae6b87f83f14f41080656a930fba7ef299122e40eb8ccd307f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + [[package]] name = "libc" version = "0.2.101" @@ -815,6 +952,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.1.2" @@ -1015,6 +1161,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.4.3" @@ -1104,6 +1260,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "paste" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1219,6 +1381,31 @@ dependencies = [ "libc", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel 0.5.1", + "crossbeam-deque", + "crossbeam-utils 0.8.5", + "lazy_static", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -1296,6 +1483,20 @@ name = "serde" version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "shared_library" @@ -1390,7 +1591,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "futures", ] @@ -1400,7 +1601,7 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "futures", "slab", "tokio-executor", @@ -1436,6 +1637,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index d8b622f..fc0b7d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bracket-lib = "~0.8.1" \ No newline at end of file +bracket-lib = "~0.8.1" +legion = "~0.3.1" \ No newline at end of file diff --git a/src/components.rs b/src/components.rs new file mode 100644 index 0000000..50dd738 --- /dev/null +++ b/src/components.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Render { + pub color: ColorPair, + pub glyph: FontCharType, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Player; diff --git a/src/main.rs b/src/main.rs index 8228930..40e9fb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,48 @@ mod camera; +mod components; mod map; mod map_builder; -mod player; +mod spawners; +mod systems; mod prelude { pub use bracket_lib::prelude::*; + pub use legion::systems::CommandBuffer; + pub use legion::world::SubWorld; + pub use legion::*; pub const SCREEN_WIDTH: i32 = 80; pub const SCREEN_HEIGHT: i32 = 50; pub const DISPLAY_WIDTH: i32 = SCREEN_WIDTH / 2; pub const DISPLAY_HEIGHT: i32 = SCREEN_HEIGHT / 2; pub use crate::camera::*; + pub use crate::components::*; pub use crate::map::*; pub use crate::map_builder::*; - pub use crate::player::*; + pub use crate::spawners::*; + pub use crate::systems::*; } use prelude::*; struct State { - map: Map, - player: Player, - camera: Camera, + ecs: World, + resources: Resources, + systems: Schedule, } impl State { fn new() -> Self { + let mut ecs = World::default(); + let mut resources = Resources::default(); let mut rng = RandomNumberGenerator::new(); let map_builder = MapBuilder::new(&mut rng); + spawn_player(&mut ecs, map_builder.player_start); + + resources.insert(map_builder.map); + resources.insert(Camera::new(map_builder.player_start)); State { - map: map_builder.map, - player: Player::new(Point::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2)), - camera: Camera::new(map_builder.player_start), + ecs, + resources, + systems: build_scheduler(), } } } @@ -39,9 +52,9 @@ impl GameState for State { ctx.cls(); ctx.set_active_console(1); ctx.cls(); - self.player.update(ctx, &self.map, &mut self.camera); - self.map.render(ctx, &mut self.camera); - self.player.render(ctx, &mut self.camera); + self.resources.insert(ctx.key); + self.systems.execute(&mut self.ecs, &mut self.resources); + render_draw_buffer(ctx).expect("Render error"); } } diff --git a/src/map.rs b/src/map.rs index 3f9f0fc..de509ba 100644 --- a/src/map.rs +++ b/src/map.rs @@ -21,57 +21,15 @@ impl Map { tiles: vec![TileType::Floor; NUM_TILES], } } - // pub fn render(&self, ctx: &mut BTerm) { - // for y in 0..SCREEN_HEIGHT { - // for x in 0..SCREEN_WIDTH { - // let idx = map_idx(x, y); - // match self.tiles[idx] { - // TileType::Floor => { - // ctx.set(x, y, YELLOW, BLACK, to_cp437('.')); - // } - // TileType::Wall => { - // ctx.set(x, y, GREEN, BLACK, to_cp437('#')); - // } - // } - // } - // } - // } - pub fn render(&self, ctx: &mut BTerm, camera: &Camera) { - ctx.set_active_console(0); - for y in camera.top_y..camera.bottom_y { - for x in camera.left_x..camera.right_x { - if self.in_bounds(Point::new(x, y)) { - let idx = map_idx(x, y); - match self.tiles[idx] { - TileType::Floor => { - ctx.set( - x - camera.left_x, - y - camera.top_y, - WHITE, - BLACK, - to_cp437('.'), - ); - } - TileType::Wall => { - ctx.set( - x - camera.left_x, - y - camera.top_y, - WHITE, - BLACK, - to_cp437('#'), - ); - } - } - } - } - } - } + pub fn in_bounds(&self, point: Point) -> bool { point.x >= 0 && point.x < SCREEN_WIDTH && point.y >= 0 && point.y < SCREEN_HEIGHT } + pub fn can_enter_tile(&self, point: Point) -> bool { self.in_bounds(point) && self.tiles[map_idx(point.x, point.y)] == TileType::Floor } + pub fn try_idx(&self, point: Point) -> Option { if !self.in_bounds(point) { None diff --git a/src/player.rs b/src/player.rs deleted file mode 100644 index 8f837ef..0000000 --- a/src/player.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::prelude::*; - -pub struct Player { - pub position: Point, -} -impl Player { - pub fn new(position: Point) -> Self { - Self { position } - } - pub fn render(&self, ctx: &mut BTerm, camera: &Camera) { - ctx.set_active_console(1); - ctx.set( - self.position.x - camera.left_x, - self.position.y - camera.top_y, - WHITE, - BLACK, - to_cp437('@'), - ); - } - pub fn update(&mut self, ctx: &mut BTerm, map: &Map, camera: &mut Camera) { - if let Some(key) = ctx.key { - let delta = match key { - VirtualKeyCode::Left => Point::new(-1, 0), - VirtualKeyCode::Right => Point::new(1, 0), - VirtualKeyCode::Up => Point::new(0, -1), - VirtualKeyCode::Down => Point::new(0, 1), - _ => Point::zero(), - }; - let new_position = self.position + delta; - if map.can_enter_tile(new_position) { - self.position = new_position; - camera.on_player_move(new_position); - } - } - } -} diff --git a/src/spawners.rs b/src/spawners.rs new file mode 100644 index 0000000..e1f8197 --- /dev/null +++ b/src/spawners.rs @@ -0,0 +1,12 @@ +pub use crate::prelude::*; + +pub fn spawn_player(ecs: &mut World, pos: Point) { + ecs.push(( + Player, + pos, + Render { + color: ColorPair::new(WHITE, BLACK), + glyph: to_cp437('@'), + }, + )); +} diff --git a/src/systems/entity_render.rs b/src/systems/entity_render.rs new file mode 100644 index 0000000..2b81c83 --- /dev/null +++ b/src/systems/entity_render.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +#[system] +#[read_component(Point)] +#[read_component(Render)] +pub fn entity_render(ecs: &mut SubWorld, #[resource] camera: &mut Camera) { + let mut draw_batch = DrawBatch::new(); + draw_batch.target(1); + let offset = Point::new(camera.left_x, camera.top_y); + + <(&Point, &Render)>::query() + .iter(ecs) + .for_each(|(pos, render)| { + draw_batch.set(*pos - offset, render.color, render.glyph); + }); + draw_batch.submit(5000).expect("Batch error"); +} diff --git a/src/systems/map_render.rs b/src/systems/map_render.rs new file mode 100644 index 0000000..9bb0b43 --- /dev/null +++ b/src/systems/map_render.rs @@ -0,0 +1,22 @@ +use crate::prelude::*; + +#[system] +pub fn map_render(#[resource] map: &Map, #[resource] camera: &Camera) { + let mut draw_batch = DrawBatch::new(); + draw_batch.target(0); + for y in camera.top_y..=camera.bottom_y { + for x in camera.left_x..=camera.right_x { + let pt = Point::new(x, y); + let offset = Point::new(camera.left_x, camera.top_y); + if map.in_bounds(pt) { + let idx = map_idx(x, y); + let glyph = match map.tiles[idx] { + TileType::Floor => to_cp437('.'), + TileType::Wall => to_cp437('#'), + }; + draw_batch.set(pt - offset, ColorPair::new(WHITE, BLACK), glyph); + } + } + } + draw_batch.submit(0).expect("Batch error"); +} diff --git a/src/systems/mod.rs b/src/systems/mod.rs new file mode 100644 index 0000000..714d27d --- /dev/null +++ b/src/systems/mod.rs @@ -0,0 +1,12 @@ +use crate::prelude::*; +mod entity_render; +mod map_render; +mod player_input; + +pub fn build_scheduler() -> Schedule { + Schedule::builder() + .add_system(player_input::player_input_system()) + .add_system(map_render::map_render_system()) + .add_system(entity_render::entity_render_system()) + .build() +} diff --git a/src/systems/player_input.rs b/src/systems/player_input.rs new file mode 100644 index 0000000..f49f067 --- /dev/null +++ b/src/systems/player_input.rs @@ -0,0 +1,31 @@ +use crate::prelude::*; + +#[system] +#[write_component(Point)] +#[read_component(Player)] +pub fn player_input( + ecs: &mut SubWorld, + #[resource] map: &Map, + #[resource] key: &Option, + #[resource] camera: &mut Camera, +) { + if let Some(key) = key { + let delta = match key { + VirtualKeyCode::Left => Point::new(-1, 0), + VirtualKeyCode::Right => Point::new(1, 0), + VirtualKeyCode::Up => Point::new(0, -1), + VirtualKeyCode::Down => Point::new(0, 1), + _ => Point::new(0, 0), + }; + if delta.x != 0 || delta.y != 0 { + let mut players = <&mut Point>::query().filter(component::()); + players.iter_mut(ecs).for_each(|pos| { + let destination = *pos + delta; + if map.can_enter_tile(destination) { + *pos = destination; + camera.on_player_move(destination); + } + }); + } + } +}