new rendering system
This commit is contained in:
parent
357ef6c8e2
commit
bf70ab9154
30
src/cell.rs
30
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,
|
||||
|
|
229
src/engine.rs
229
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<u32>,
|
||||
pub brush_size: usize,
|
||||
pub random: Random,
|
||||
|
||||
inventory: Inventory,
|
||||
|
||||
pub world: World,
|
||||
|
||||
tiles: Vec<Tile>,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ mod cell;
|
|||
mod engine;
|
||||
mod materials;
|
||||
mod rand;
|
||||
mod tile;
|
||||
mod world;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
33
src/tile.rs
Normal file
33
src/tile.rs
Normal file
|
@ -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,
|
||||
],
|
||||
};
|
83
src/world.rs
83
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<Cell>,
|
||||
changes: Vec<Change>,
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue