From 78f4d7911b78089dfe77c7cf9ce17db8304a4158 Mon Sep 17 00:00:00 2001 From: Nicolas Klier Date: Wed, 8 May 2024 00:07:10 +0200 Subject: [PATCH] First attempt to add NPCs --- Cargo.toml | 3 ++- src/cell.rs | 13 +++++++++ src/direction.rs | 11 ++++++++ src/main.rs | 48 +++++++++++++++++++++++++++++++--- src/npc.rs | 26 ++++++++++++++++++ src/stage.rs | 68 +++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 src/direction.rs create mode 100644 src/npc.rs diff --git a/Cargo.toml b/Cargo.toml index 199b80b..7c7788f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ winit_input_helper = "0.14" randomize = "5.0.0" getrandom = "0.2.15" byteorder = "1" -noise = "0.9" \ No newline at end of file +noise = "0.9" +vec2d = "0.4" \ No newline at end of file diff --git a/src/cell.rs b/src/cell.rs index e094c27..6e3243d 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -1,3 +1,6 @@ +use core::fmt; +use std::fmt::write; + #[derive(Clone, Copy, Debug)] pub enum CellVariance { VOID = 0, @@ -5,6 +8,16 @@ pub enum CellVariance { NIGHT = 2 } +impl fmt::Display for CellVariance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CellVariance::VOID => write!(f, "VOID"), + CellVariance::NIGHT => write!(f, "NIGHT"), + CellVariance::DAY => write!(f, "DAY") + } + } +} + impl Default for CellVariance { fn default() -> Self { Self::VOID diff --git a/src/direction.rs b/src/direction.rs new file mode 100644 index 0000000..e46b223 --- /dev/null +++ b/src/direction.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Debug)] +pub struct Direction { + pub x: isize, + pub y: isize +} + +impl Direction { + pub fn new(x: isize, y: isize) -> Self { + Self { x, y } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8200d57..b04a2a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ mod cell; mod stage; +mod npc; +mod direction; use pixels::{Error, Pixels, SurfaceTexture}; use stage::Stage; @@ -11,9 +13,9 @@ use winit::{ }; use winit_input_helper::WinitInputHelper; -const WIDTH: u32 = 256; -const HEIGHT: u32 = 256; -const SCALE_FACTOR: f64 = 4.0; +const WIDTH: u32 = 350; +const HEIGHT: u32 = 350; +const SCALE_FACTOR: f64 = 3.0; fn main() -> Result<(), Error> { env_logger::init(); @@ -65,9 +67,49 @@ fn main() -> Result<(), Error> { return; } + if input.key_pressed(VirtualKeyCode::P) { + paused = !paused; + } + + if input.key_pressed_os(VirtualKeyCode::Space) { + // Space is frame-step, so ensure we're paused + paused = true; + } + if input.key_pressed(VirtualKeyCode::R) { stage.scatter(stage::ScatterTypes::RANDOM); } + + // Handle mouse. This is a bit involved since support some simple + // line drawing (mostly because it makes nice looking patterns). + let (mouse_cell, mouse_prev_cell) = input + .mouse() + .map(|(mx, my)| { + let (dx, dy) = input.mouse_diff(); + let prev_x = mx - dx; + let prev_y = my - dy; + + let (mx_i, my_i) = pixels + .window_pos_to_pixel((mx, my)) + .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); + + let (px_i, py_i) = pixels + .window_pos_to_pixel((prev_x, prev_y)) + .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); + + ( + (mx_i as isize, my_i as isize), + (px_i as isize, py_i as isize), + ) + }) + .unwrap_or_default(); + + if input.mouse_pressed(0) { + println!("Mouse click at {mouse_cell:?}"); + stage.spawn_npc(mouse_cell.0, mouse_cell.1); + + stage.update(); + } // Resize the window if let Some(size) = input.window_resized() { diff --git a/src/npc.rs b/src/npc.rs new file mode 100644 index 0000000..f95e484 --- /dev/null +++ b/src/npc.rs @@ -0,0 +1,26 @@ +use vec2d::{Coord, Vec2D}; + +use crate::{cell::CellVariance, direction::Direction}; + +#[derive(Clone, Debug)] +pub struct NPC { + pub walk_on: CellVariance, + pub obstructed_from: Vec, + pub pos_x: isize, + pub pos_y: isize, + pub direction: Direction +} + +impl NPC { + pub fn new(pos_x: isize, pos_y: isize, direction: Direction, walk_on: CellVariance, obstructed_from: Vec) -> Self { + Self { walk_on, obstructed_from, pos_x, pos_y, direction } + } + + pub fn new_day(pos_x: isize, pos_y: isize, direction: Direction) -> Self { + Self { walk_on: CellVariance::DAY, obstructed_from: vec![CellVariance::NIGHT], pos_x, pos_y, direction } + } + + pub fn new_night(pos_x: isize, pos_y: isize, direction: Direction) -> Self { + Self { walk_on: CellVariance::NIGHT, obstructed_from: vec![CellVariance::DAY], pos_x, pos_y, direction } + } +} \ No newline at end of file diff --git a/src/stage.rs b/src/stage.rs index 3fd9267..1366117 100644 --- a/src/stage.rs +++ b/src/stage.rs @@ -1,22 +1,20 @@ -use noise::core::value; -use noise::core::value::value_2d; -use noise::permutationtable::PermutationTable; use noise::utils::NoiseMapBuilder; use noise::utils::PlaneMapBuilder; use noise::BasicMulti; -use noise::Fbm; +use noise::MultiFractal; use noise::Perlin; -use noise::Seedable; -use randomize::Gen32; use randomize::PCG32; use crate::cell::Cell; use crate::cell::CellVariance; +use crate::direction::Direction; +use crate::npc::NPC; pub struct Stage { cells: Vec, /// Used for temporary work item. Replaces `cells` once done. cells_dup: Vec, + npcs: Vec, width: usize, height: usize, } @@ -33,6 +31,7 @@ impl Stage { Self { cells: vec![Cell::default(); size], cells_dup: vec![Cell::default(); size], + npcs: vec![], width, height, } @@ -59,7 +58,9 @@ impl Stage { let seed = Self::generate_seed(); let mut rng: randomize::PCG32 = PCG32::new(seed.0, seed.1); - let basicmulti = BasicMulti::::new(rng.next_u32()); + let basicmulti = BasicMulti::::new(rng.next_u32()) + .set_octaves(2) + .set_frequency(0.5); let plane = PlaneMapBuilder::new(basicmulti) .set_size(self.width, self.height) @@ -88,6 +89,28 @@ impl Stage { self.update(); } + pub fn spawn_npc(&mut self, pos_x: isize, pos_y: isize) { + let cell = self.get_cell(pos_x, pos_y); + + let npc: NPC; + match cell.variance { + CellVariance::DAY => { + npc = NPC::new_day(pos_x, pos_y, Direction::new(-1, -1)); + }, + CellVariance::NIGHT => { + npc = NPC::new_night(pos_x, pos_y, Direction::new(-1, -1)); + }, + CellVariance::VOID => { + // Ignore this stupid request. + return; + } + } + + println!("Spawn NPC at ({}, {}) as {}", pos_x, pos_y, npc.walk_on); + + self.npcs.push(npc); + } + pub fn draw(&self, screen: &mut [u8]) { debug_assert_eq!(screen.len(), 4 * self.cells.len()); for (c, pix) in self.cells.iter().zip(screen.chunks_exact_mut(4)) { @@ -98,9 +121,40 @@ impl Stage { }; pix.copy_from_slice(&color); } + + for (n, pix) in self.npcs.iter().zip(screen.chunks_exact_mut(4)) { + let color = match n.walk_on { + CellVariance::DAY => [0x10, 0x41, 0x4F, 0xFF], + CellVariance::NIGHT => [0xD3, 0xE3, 0xD3, 0xFF], + _ => { + panic!("This NPC shouldn't even exist!"); + } + }; + + pix.copy_from_slice(&color); + } + } + + pub fn get_cell(&self, pos_x: isize, pos_y: isize) -> Cell { + self.cells[(pos_x + pos_y) as usize * self.width] } pub fn update(&mut self) { + // TODO: Flying balls + + for y in 0..self.height { + for x in 0..self.width { + let idx = x + y * self.width; + // Write into scratch_cells, since we're still reading from `self.cells` + + self.cells_dup[idx] = self.cells[idx]; + } + } + std::mem::swap(&mut self.cells_dup, &mut self.cells); } + + pub fn get_cells(&self) -> &Vec { + &self.cells + } }