diff --git a/src/cell.rs b/src/cell.rs index 4b185e8..c6ef732 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -1,18 +1,42 @@ -#[derive(Debug, Clone, Copy, PartialEq)] +use std::ops::BitAnd; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum CellType { Empty = 0, Sand, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum MovementType { None = 0b0000_0000, Down = 0b0000_0001, + // 000 + // 1X1 + // 000 Side = 0b0000_0010, + // 000 + // 0X0 + // 111 DownSide = 0b0000_0011, } +impl BitAnd for MovementType { + type Output = Self; -#[derive(Debug, Clone, Copy)] + // rhs is the "right-hand side" of the expression `a & b` + fn bitand(self, rhs: Self) -> Self::Output { + let a = self as u8 & rhs as u8; + use MovementType::*; + match a { + 0 => None, + 1 => Down, + 2 => Side, + 3 => DownSide, + _ => None, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Cell { pub cell_type: CellType, pub movement_type: MovementType, diff --git a/src/engine.rs b/src/engine.rs index 8a50411..3b4e55e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,23 +1,48 @@ -use std::io::Empty; - use minifb::{Key, Window, WindowOptions}; const WIDTH: usize = 800; const HEIGHT: usize = 600; -use crate::{materials::*, rand::Random, world::World}; +use crate::{ + cell::{Cell, CellType, MovementType}, + materials::*, + rand::Random, + tile::{Tile, PLAYER}, + world::{in_bounds, xy_to_i, World}, +}; + +struct Inventory { + current_selected: usize, + left_hand_side: [Cell; 10], + right_hand_side: [Cell; 10], +} pub struct Engine { pub window: Window, pub front_buffer: Vec, pub brush_size: usize, - pub random: Random, + + inventory: Inventory, pub world: World, + + tiles: Vec, } impl Engine { pub fn new() -> Self { + let inventory = Inventory { + current_selected: 0, + left_hand_side: [ + SAND_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, + EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, + ], + right_hand_side: [ + EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, + EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, + ], + }; + Self { window: Window::new( "Rising Sands :P | Hit ESC to exit", @@ -30,14 +55,13 @@ impl Engine { }), front_buffer: vec![0; WIDTH * HEIGHT], brush_size: 1, - random: Random::new(0), world: World::new(), + inventory, + tiles: vec![PLAYER], } } pub fn run(&mut self) { - self.clear(); - self.window.set_target_fps(60); while self.window.is_open() { @@ -52,33 +76,34 @@ impl Engine { self.brush_size = self.brush_size.saturating_sub(1); } - // TODO: something wrong here? - if self.window.get_mouse_down(minifb::MouseButton::Left) { - // TODO: Swap to mm::Discard and handle the mouse outside of the window - let pos = self.window.get_mouse_pos(minifb::MouseMode::Clamp).unwrap(); - let half = self.brush_size / 2; + self.handle_mouse(); - let mouse_x = pos.0 as usize - half; - let mouse_y = pos.1 as usize - half; - - for x in 0..self.brush_size { - for y in 0..self.brush_size { - self.set_cell(mouse_x + x, mouse_y + y, SAND); - } - } + // TODO: make a self.handle_player_movement() to handle the player movement stuff + // BUG: panics when player moves partially off screen. + if self.window.is_key_down(Key::A) { + self.tiles[0].x -= 1; + } + if self.window.is_key_down(Key::D) { + self.tiles[0].x += 1; + } + if self.window.is_key_down(Key::W) { + self.tiles[0].y -= 1; + } + if self.window.is_key_down(Key::S) { + self.tiles[0].y += 1; } - if self.window.get_mouse_down(minifb::MouseButton::Right) { - // TODO: Swap to mm::Discard and handle the mouse outside of the window - let pos = self.window.get_mouse_pos(minifb::MouseMode::Clamp).unwrap(); - let half = self.brush_size / 2; + for tile in &self.tiles { + for x in 0..8 { + for y in 0..8 { + if in_bounds(x, y) { + // what happens if the cell is already full? + let material = tile.values[y][x]; - let mouse_x = pos.0 as usize - half; - let mouse_y = pos.1 as usize - half; - - for x in 0..self.brush_size { - for y in 0..self.brush_size { - self.set_cell(mouse_x + x, mouse_y + y, EMPTY); + if material != EMPTY_CELL { + self.world.set_xy(x + tile.x, tile.y + y, material); + } + } } } } @@ -86,64 +111,120 @@ impl Engine { for x in 0..WIDTH { for y in 0..HEIGHT { let index = xy_to_i(x, y); - let cell = self.front_buffer[index]; - - if cell == SAND { - let down = self.is_empty(x, y + 1); - let random = self.random.random_bool(); - let left = self.is_empty(x - 1, y + 1) && random; - let right = self.is_empty(x + 1, y + 1) ^ left; - - if down { - self.set_cell(x, y + 1, SAND) - } else if left { - self.set_cell(x - 1, y + 1, SAND) - } else if right { - self.set_cell(x + 1, y + 1, SAND) - } - - if down || left || right { - self.set_cell(x, y, EMPTY); - } + let cell = self.world.cells[index]; + let cell_move = cell.movement_type; + use MovementType::*; + match cell_move { + None => {} + Down => self.world.move_down(x, y), + Side => todo!(), + DownSide => self.world.move_down_side(x, y), } } } + self.world.commit_cells(); + + for x in 0..WIDTH { + for y in 0..HEIGHT { + let index = xy_to_i(x, y); + let cell = self.world.cells[index]; + + self.front_buffer[index] = cell.color; + } + } + + let pos = self.window.get_mouse_pos(minifb::MouseMode::Discard); + if pos.is_some() { + let pos = pos.unwrap(); + let half = self.brush_size / 2; + + let mouse_x = pos.0 as usize - half; + let mouse_y = pos.1 as usize - half; + + for x in 0..self.brush_size { + if in_bounds(mouse_x + x, mouse_y) { + let index = xy_to_i(mouse_x + x, mouse_y); + self.front_buffer[index] = 0x00_ff_ff_ff; + } + } + + for x in 0..self.brush_size { + if in_bounds(mouse_x + x, mouse_y + self.brush_size) { + let index = xy_to_i(mouse_x + x, mouse_y + self.brush_size); + self.front_buffer[index] = 0x00_ff_ff_ff; + } + } + + for y in 0..self.brush_size { + if in_bounds(mouse_x, mouse_y + y) { + let index = xy_to_i(mouse_x, mouse_y + y); + self.front_buffer[index] = 0x00_ff_ff_ff; + } + } + + for y in 0..self.brush_size { + if in_bounds(mouse_x + self.brush_size, mouse_y + y) { + let index = xy_to_i(mouse_x + self.brush_size, mouse_y + y); + self.front_buffer[index] = 0x00_ff_ff_ff; + } + } + + // for x in 0..self.brush_size { + // for y in 0..self.brush_size { + // if in_bounds(mouse_x + x, mouse_y + y) { + // let index = xy_to_i(mouse_x + x, mouse_y + y); + // self.front_buffer[index] = 0x00_ff_ff_ff; + // } + // } + // } + } + // TODO Handle this. self.window .update_with_buffer(&self.front_buffer, WIDTH, HEIGHT) .unwrap(); + + for tile in &self.tiles { + for x in 0..8 { + for y in 0..8 { + // x += tile.x; + // y += tile.y; + + if in_bounds(x, y) { + // what happens if the cell is already full? + self.world.set_xy(x + tile.x, tile.y + y, EMPTY_CELL); + } + } + } + } } } -} -impl Engine { - fn is_empty(&mut self, x: usize, y: usize) -> bool { - let index = xy_to_i(x, y); - if index >= self.front_buffer.len() { - false + pub fn handle_mouse(&mut self) { + let material = if self.window.get_mouse_down(minifb::MouseButton::Left) { + self.inventory.left_hand_side[self.inventory.current_selected] + } else if self.window.get_mouse_down(minifb::MouseButton::Right) { + self.inventory.right_hand_side[self.inventory.current_selected] } else { - self.front_buffer[index] == EMPTY - } - } + return; + }; - fn set_cell(&mut self, x: usize, y: usize, mat: u32) { - let index = xy_to_i(x, y); - if index >= self.front_buffer.len() { - } else { - self.front_buffer[index] = mat; - } - } + // TODO: Swap to MouseMode::Discard and handle the mouse outside of the window + let pos = self.window.get_mouse_pos(minifb::MouseMode::Discard); + if pos.is_some() { + let pos = pos.unwrap(); + let half = self.brush_size / 2; - fn clear(&mut self) { - for x in 0..WIDTH { - for y in 0..HEIGHT { - let index = xy_to_i(x, y); - self.front_buffer[index] = EMPTY; + let mouse_x = pos.0 as usize - half; + let mouse_y = pos.1 as usize - half; + + for x in 0..self.brush_size { + for y in 0..self.brush_size { + if in_bounds(mouse_x + x, mouse_y + y) { + self.world.set_xy(mouse_x + x, mouse_y + y, material); + } + } } } } } - -pub fn xy_to_i(x: usize, y: usize) -> usize { - x + WIDTH * y -} diff --git a/src/main.rs b/src/main.rs index 7455dcf..c806e37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod cell; mod engine; mod materials; mod rand; +mod tile; mod world; fn main() { diff --git a/src/materials.rs b/src/materials.rs index c893a6d..10f3b37 100644 --- a/src/materials.rs +++ b/src/materials.rs @@ -1,2 +1,22 @@ +use crate::cell::Cell; +use crate::cell::CellType::*; +use crate::cell::MovementType::*; + pub const EMPTY: u32 = 0x00_00_00; + pub const SAND: u32 = 0xFF_99_50; + +pub const EMPTY_CELL: Cell = Cell { + cell_type: Empty, + movement_type: None, + color: 0x0, +}; + +pub const SAND_CELL: Cell = Cell { + cell_type: Sand, + movement_type: DownSide, + color: 0xFF_99_50, +}; + +//SAND_START_COLOR_RANGE = 0x00_f6_D7_B0 +//SAND_END_COLOR_RANGE = 0x00_e1_bf_92 diff --git a/src/rand.rs b/src/rand.rs index d3664ca..4b41f08 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -18,4 +18,12 @@ impl Random { pub fn random_bool(&mut self) -> bool { self.rng.gen_bool(0.5) } + + pub fn color_range(&mut self, start: u32, end: u32) -> u32 { + if start == end { + 0 + } else { + self.rng.gen_range(start..=end) + } + } } diff --git a/src/tile.rs b/src/tile.rs new file mode 100644 index 0000000..9751e38 --- /dev/null +++ b/src/tile.rs @@ -0,0 +1,33 @@ +use crate::{cell::Cell, materials::EMPTY_CELL}; + +pub struct Tile { + pub x: usize, + pub y: usize, + pub values: [[Cell; 8]; 8], +} + +const SKIN: Cell = Cell { + cell_type: crate::cell::CellType::Sand, + movement_type: crate::cell::MovementType::None, + color: 0x00_Fe_9f_13, +}; + +const EYE: Cell = Cell { + cell_type: crate::cell::CellType::Sand, + movement_type: crate::cell::MovementType::None, + color: 0x00_00_5e_be, +}; + +const NORMAL_ROW: [Cell; 8] = [ + EMPTY_CELL, EMPTY_CELL, SKIN, SKIN, SKIN, SKIN, EMPTY_CELL, EMPTY_CELL, +]; +const EYE_ROW: [Cell; 8] = [ + EMPTY_CELL, EMPTY_CELL, EYE, SKIN, SKIN, EYE, EMPTY_CELL, EMPTY_CELL, +]; +pub const PLAYER: Tile = Tile { + x: 100, + y: 100, + values: [ + NORMAL_ROW, EYE_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, + ], +}; diff --git a/src/world.rs b/src/world.rs index 56fe894..8f70f08 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,23 +1,26 @@ use crate::cell::{Cell, MovementType}; use crate::cell::CellType::Empty; -use crate::engine::xy_to_i; -use crate::materials::EMPTY; +use crate::materials::{EMPTY, EMPTY_CELL}; +use crate::rand::Random; const WIDTH: usize = 800; const HEIGHT: usize = 600; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Change { x_from: usize, x_to: usize, y_from: usize, y_to: usize, } - +// #[derive(Ord)] pub struct World { pub width: usize, pub height: usize, pub cells: Vec, changes: Vec, + pub random: Random, } impl World { pub fn new() -> Self { @@ -33,6 +36,7 @@ impl World { WIDTH * HEIGHT ], changes: vec![], + random: Random::new(0), } } } @@ -68,7 +72,74 @@ impl World { self.changes.push(change); } // Takes the changes to the world and applies them. - pub fn commit_cells() {} + pub fn commit_cells(&mut self) { + let mut changes_length: usize = self.changes.len(); + if changes_length == 0 { + // println!("WARN: Empty changes."); + return; + } + let mut cell_index = 0; + // while cell_index <= changes_length { + // let cell = &self.changes[cell_index]; + // let first_cell = self.get_xy(cell.x_from, cell.y_from); + // if first_cell.cell_type != Empty { + // self.changes[cell_index] = *self.changes.last().unwrap(); + // let _ = self.changes.pop(); + + // // m_changes.pop_back(); + // changes_length -= 1; + // } + // cell_index += 1; + // } + + // Sort the changes. + self.changes.sort(); + + let changes_length: usize = self.changes.len(); + + // TODO: Apply these changes in random order + for index in 0..changes_length { + let dst_x = self.changes[index].x_to; + let dst_y = self.changes[index].y_to; + + let src_x = self.changes[index].x_from; + let src_y = self.changes[index].y_from; + + let cell = self.get_xy(src_x, src_y); + + self.set_xy(dst_x, dst_y, cell); + + self.set_xy(src_x, src_y, EMPTY_CELL); + } + + self.changes = vec![]; + } +} + +impl World { + pub fn move_down(&mut self, x: usize, y: usize) { + if self.is_empty(x, y + 1) { + self.move_cell(x, y, x, y + 1); + } + } + pub fn move_down_side(&mut self, x: usize, y: usize) { + let mut down_left = self.is_empty(x - 1, y + 1); + let mut down_right = self.is_empty(x + 1, y + 1); + let down = self.is_empty(x, y + 1); + + if down_left && down_right { + down_left = self.random.random_bool(); + down_right = down_right != down_left; + } + + if down_left { + self.move_cell(x, y, x - 1, y + 1); + } else if down_right { + self.move_cell(x, y, x + 1, y + 1); + } else if down { + self.move_cell(x, y, x, y + 1); + } + } } pub fn in_bounds(x: usize, y: usize) -> bool { @@ -77,3 +148,7 @@ pub fn in_bounds(x: usize, y: usize) -> bool { x_in_bounds && y_in_bounds } + +pub fn xy_to_i(x: usize, y: usize) -> usize { + x + WIDTH * y +}