Compare commits
6 Commits
0a74409dcc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 97435fbd4e | |||
| c75c598a53 | |||
| 33a865d059 | |||
| ddb8e0423d | |||
| da0d6e204a | |||
| 54fafe8742 |
@@ -1,5 +1,4 @@
|
||||
use core::fmt;
|
||||
use std::fmt::write;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum CellVariance {
|
||||
@@ -33,4 +32,12 @@ impl Cell {
|
||||
pub fn new(variance: CellVariance) -> Self {
|
||||
Self { variance }
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> Self {
|
||||
Cell::new(match self.variance {
|
||||
CellVariance::DAY => CellVariance::NIGHT,
|
||||
CellVariance::NIGHT => CellVariance::DAY,
|
||||
CellVariance::VOID => CellVariance::VOID
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Direction {
|
||||
pub struct Coord {
|
||||
pub x: isize,
|
||||
pub y: isize
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
impl Coord {
|
||||
pub fn new(x: isize, y: isize) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn invert(&mut self) {
|
||||
self.x *= -1;
|
||||
self.y *= -1;
|
||||
}
|
||||
}
|
||||
20
src/main.rs
20
src/main.rs
@@ -9,12 +9,12 @@ use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{Event, VirtualKeyCode},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{self, WindowBuilder},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
use winit_input_helper::WinitInputHelper;
|
||||
|
||||
const WIDTH: u32 = 64;
|
||||
const HEIGHT: u32 = 64;
|
||||
const WIDTH: usize = 64;
|
||||
const HEIGHT: usize = 64;
|
||||
const SCALE_FACTOR: f64 = 10.0;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
@@ -39,7 +39,7 @@ fn main() -> Result<(), Error> {
|
||||
let mut pixels = {
|
||||
let window_size = window.inner_size();
|
||||
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
|
||||
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
||||
Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
|
||||
};
|
||||
|
||||
let mut stage = Stage::new(WIDTH as usize, HEIGHT as usize);
|
||||
@@ -47,13 +47,11 @@ fn main() -> Result<(), Error> {
|
||||
|
||||
let mut paused = false;
|
||||
|
||||
let mut draw_state: Option<bool> = None;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
if let Event::RedrawRequested(_) = event {
|
||||
stage.draw(pixels.frame_mut());
|
||||
|
||||
if let Err(err) = pixels.render() {
|
||||
if let Err(_) = pixels.render() {
|
||||
debug_assert!(true, "Oops, I did it again.");
|
||||
|
||||
*control_flow = ControlFlow::Exit;
|
||||
@@ -80,9 +78,13 @@ fn main() -> Result<(), Error> {
|
||||
stage.scatter(stage::ScatterTypes::Random);
|
||||
}
|
||||
|
||||
if input.key_pressed(VirtualKeyCode::H) {
|
||||
stage.scatter(stage::ScatterTypes::HalfVertical);
|
||||
}
|
||||
|
||||
// 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
|
||||
let (mouse_cell, _) = input
|
||||
.mouse()
|
||||
.map(|(mx, my)| {
|
||||
let (dx, dy) = input.mouse_diff();
|
||||
@@ -116,7 +118,7 @@ fn main() -> Result<(), Error> {
|
||||
|
||||
// Resize the window
|
||||
if let Some(size) = input.window_resized() {
|
||||
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||
if let Err(_) = pixels.resize_surface(size.width, size.height) {
|
||||
debug_assert!(true, "Resize failed!");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
|
||||
179
src/npc.rs
179
src/npc.rs
@@ -1,4 +1,27 @@
|
||||
use crate::{cell::CellVariance, direction::Direction};
|
||||
use std::cell;
|
||||
|
||||
use crate::{
|
||||
cell::{Cell, CellVariance},
|
||||
direction::Coord,
|
||||
stage::{self, Stage},
|
||||
HEIGHT, WIDTH,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Direction {
|
||||
TOP, /* 💪 */
|
||||
BOTTOM, /* 👉👈🥺 */
|
||||
LEFT,
|
||||
RIGHT,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Neighbour {
|
||||
pub cell: Cell,
|
||||
pub direction: Direction,
|
||||
pub pos_x: usize,
|
||||
pub pos_y: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NPC {
|
||||
@@ -6,19 +29,159 @@ pub struct NPC {
|
||||
pub obstructed_from: Vec<CellVariance>,
|
||||
pub pos_x: usize,
|
||||
pub pos_y: usize,
|
||||
pub direction: Direction
|
||||
pub direction: Coord,
|
||||
}
|
||||
|
||||
impl NPC {
|
||||
pub fn new(pos_x: usize, pos_y: usize, direction: Direction, walk_on: CellVariance, obstructed_from: Vec<CellVariance>) -> Self {
|
||||
Self { walk_on, obstructed_from, pos_x, pos_y, direction }
|
||||
pub fn new_day(pos_x: usize, pos_y: usize, direction: Coord) -> Self {
|
||||
Self {
|
||||
walk_on: CellVariance::DAY,
|
||||
obstructed_from: vec![CellVariance::NIGHT, CellVariance::VOID],
|
||||
pos_x,
|
||||
pos_y,
|
||||
direction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_day(pos_x: usize, pos_y: usize, direction: Direction) -> Self {
|
||||
Self { walk_on: CellVariance::DAY, obstructed_from: vec![CellVariance::NIGHT], pos_x, pos_y, direction }
|
||||
pub fn new_night(pos_x: usize, pos_y: usize, direction: Coord) -> Self {
|
||||
Self {
|
||||
walk_on: CellVariance::NIGHT,
|
||||
obstructed_from: vec![CellVariance::DAY, CellVariance::VOID],
|
||||
pos_x,
|
||||
pos_y,
|
||||
direction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_night(pos_x: usize, pos_y: usize, direction: Direction) -> Self {
|
||||
Self { walk_on: CellVariance::NIGHT, obstructed_from: vec![CellVariance::DAY], pos_x, pos_y, direction }
|
||||
pub fn into_cell(&self) -> Cell {
|
||||
// TODO: Bad idea.
|
||||
Cell::new(self.obstructed_from[0])
|
||||
}
|
||||
|
||||
pub fn process_bounce(&mut self, map: &Vec<Cell>) -> Option<Neighbour> {
|
||||
// Get the current and next positions
|
||||
|
||||
let mut next_direction = self.direction.clone();
|
||||
|
||||
// If we are here, we know that we hit something.
|
||||
match self.check_neighborhood(map) {
|
||||
None => {
|
||||
self.move_next();
|
||||
return None;
|
||||
}
|
||||
Some(neighbour) => {
|
||||
if neighbour.direction == Direction::LEFT || neighbour.direction == Direction::RIGHT
|
||||
{
|
||||
next_direction.x *= -1;
|
||||
} else {
|
||||
next_direction.y *= -1;
|
||||
}
|
||||
|
||||
// Return the new bounce direction
|
||||
self.direction = next_direction;
|
||||
|
||||
self.move_next();
|
||||
|
||||
// Don't tell update function that we hit a void block, to avoid overriding it.
|
||||
// We changed direction anyway.
|
||||
if neighbour.cell.variance != CellVariance::VOID {
|
||||
return Some(neighbour);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_neighborhood(&self, map: &Vec<Cell>) -> Option<Neighbour> {
|
||||
if self.pos_y == 0 && self.direction.y == -1 {
|
||||
return Some(Neighbour {
|
||||
cell: map[0],
|
||||
direction: Direction::TOP,
|
||||
pos_x: 0,
|
||||
pos_y: 0,
|
||||
});
|
||||
}
|
||||
|
||||
if self.pos_x == 0 && self.direction.x == -1 {
|
||||
return Some(Neighbour {
|
||||
cell: map[0],
|
||||
direction: Direction::LEFT,
|
||||
pos_x: 0,
|
||||
pos_y: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let left = (self.pos_x.checked_sub(1).unwrap_or(0), self.pos_y);
|
||||
let right = (self.pos_x.checked_add(1).unwrap_or(0), self.pos_y);
|
||||
let top = (self.pos_x, self.pos_y.checked_sub(1).unwrap_or(0));
|
||||
let bottom = (self.pos_x, (self.pos_y.checked_add(1).unwrap_or(0)));
|
||||
|
||||
let cell_left = map.get(left.0 + left.1 * WIDTH).unwrap_or(&Cell {
|
||||
variance: CellVariance::VOID,
|
||||
});
|
||||
let cell_right = map.get(right.0 + right.1 * WIDTH).unwrap_or(&Cell {
|
||||
variance: CellVariance::VOID,
|
||||
});
|
||||
let cell_top = map.get(top.0 + top.1 * WIDTH).unwrap_or(&Cell {
|
||||
variance: CellVariance::VOID,
|
||||
});
|
||||
let cell_bottom = map.get(bottom.0 + bottom.1 * WIDTH).unwrap_or(&Cell {
|
||||
variance: CellVariance::VOID,
|
||||
});
|
||||
|
||||
if self.obstructed_from.contains(&cell_left.variance) {
|
||||
return Some(Neighbour {
|
||||
cell: cell_left.clone(),
|
||||
direction: Direction::LEFT,
|
||||
pos_x: left.0,
|
||||
pos_y: left.1,
|
||||
});
|
||||
}
|
||||
if self.obstructed_from.contains(&cell_right.variance) {
|
||||
return Some(Neighbour {
|
||||
cell: cell_right.clone(),
|
||||
direction: Direction::RIGHT,
|
||||
pos_x: right.0,
|
||||
pos_y: right.1,
|
||||
});
|
||||
}
|
||||
if self.obstructed_from.contains(&cell_top.variance) {
|
||||
return Some(Neighbour {
|
||||
cell: cell_top.clone(),
|
||||
direction: Direction::TOP,
|
||||
pos_x: top.0,
|
||||
pos_y: top.1,
|
||||
});
|
||||
}
|
||||
if self.obstructed_from.contains(&cell_bottom.variance) {
|
||||
return Some(Neighbour {
|
||||
cell: cell_bottom.clone(),
|
||||
direction: Direction::BOTTOM,
|
||||
pos_x: bottom.0,
|
||||
pos_y: bottom.1,
|
||||
});
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn move_next(&mut self) {
|
||||
let mov_x = self.pos_x.checked_add_signed(self.direction.x);
|
||||
let mov_y = self.pos_y.checked_add_signed(self.direction.y);
|
||||
|
||||
if mov_x.is_none() || mov_y.is_none() {
|
||||
println!("Illegal move prevented");
|
||||
} else {
|
||||
self.pos_x = mov_x.unwrap();
|
||||
self.pos_y = mov_y.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_position(&self) -> (Option<usize>, Option<usize>) {
|
||||
(
|
||||
(self.pos_x.checked_add_signed(self.direction.x)),
|
||||
(self.pos_y.checked_add_signed(self.direction.y)),
|
||||
)
|
||||
}
|
||||
}
|
||||
82
src/stage.rs
82
src/stage.rs
@@ -1,3 +1,6 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::cell;
|
||||
|
||||
use noise::utils::NoiseMapBuilder;
|
||||
use noise::utils::PlaneMapBuilder;
|
||||
use noise::BasicMulti;
|
||||
@@ -7,15 +10,16 @@ use randomize::PCG32;
|
||||
|
||||
use crate::cell::Cell;
|
||||
use crate::cell::CellVariance;
|
||||
use crate::direction::Direction;
|
||||
use crate::direction::Coord;
|
||||
use crate::npc::NPC;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Stage {
|
||||
cells: Vec<Cell>,
|
||||
|
||||
/// Used for temporary work item. Replaces `cells` once done.
|
||||
cells_dup: Vec<Cell>,
|
||||
npcs: Vec<NPC>,
|
||||
npc_map: Vec<Option<Cell>>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
@@ -34,7 +38,6 @@ impl Stage {
|
||||
cells: vec![Cell::default(); size],
|
||||
cells_dup: vec![Cell::default(); size],
|
||||
npcs: vec![],
|
||||
npc_map: vec![None; size],
|
||||
width,
|
||||
height,
|
||||
}
|
||||
@@ -113,10 +116,10 @@ impl Stage {
|
||||
let npc: NPC;
|
||||
match cell.variance {
|
||||
CellVariance::DAY => {
|
||||
npc = NPC::new_day(pos_x, pos_y, Direction::new(-1, -1));
|
||||
npc = NPC::new_day(pos_x, pos_y, Coord::new(-1, -1));
|
||||
}
|
||||
CellVariance::NIGHT => {
|
||||
npc = NPC::new_night(pos_x, pos_y, Direction::new(-1, -1));
|
||||
npc = NPC::new_night(pos_x, pos_y, Coord::new(-1, -1));
|
||||
}
|
||||
CellVariance::VOID => {
|
||||
// Ignore this stupid request.
|
||||
@@ -132,29 +135,28 @@ impl Stage {
|
||||
pub fn draw(&mut self, screen: &mut [u8]) {
|
||||
debug_assert_eq!(screen.len(), 4 * self.cells.len());
|
||||
|
||||
// Draw NPCs
|
||||
self.npc_map.fill(None);
|
||||
for (i, npc) in self.npcs.iter().enumerate() {
|
||||
let map_pos = npc.pos_x + npc.pos_y * self.width;
|
||||
// let color = match npc.walk_on {
|
||||
// CellVariance::DAY => [0x10, 0x41, 0x4F, 0xFF],
|
||||
// CellVariance::NIGHT => [0xD3, 0xE3, 0xD3, 0xFF],
|
||||
// _ => {
|
||||
// panic!("This NPC shouldn't even exist!");
|
||||
// }
|
||||
// };
|
||||
|
||||
self.npc_map[map_pos] = Some(Cell::new(npc.obstructed_from[0]));
|
||||
}
|
||||
|
||||
// Draw background
|
||||
for (i, (mut c, pix)) in self.cells.iter().zip(screen.chunks_exact_mut(4)).enumerate() {
|
||||
let npc_cell = self.npc_map.get(i).unwrap();
|
||||
if let Some(npc_cell) = npc_cell {
|
||||
c = &npc_cell;
|
||||
for (i, (c, pix)) in self
|
||||
.cells
|
||||
.iter()
|
||||
.zip(screen.chunks_exact_mut(4))
|
||||
.enumerate()
|
||||
{
|
||||
let mut cell: Cell = c.clone();
|
||||
|
||||
let mut npc_cell: Option<NPC> = None;
|
||||
for npc in self.npcs.iter() {
|
||||
if npc.pos_x + npc.pos_y * self.width == i {
|
||||
npc_cell = Some(npc.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let color = match c.variance {
|
||||
// Draw NPCs
|
||||
if let Some(npc_cell) = npc_cell {
|
||||
cell = npc_cell.into_cell();
|
||||
}
|
||||
|
||||
let color = match cell.variance {
|
||||
CellVariance::VOID => [0x0, 0x0, 0x0, 0xFF],
|
||||
CellVariance::DAY => [0xD3, 0xE3, 0xD3, 0xFF],
|
||||
CellVariance::NIGHT => [0x10, 0x41, 0x4F, 0xFF],
|
||||
@@ -169,35 +171,23 @@ impl Stage {
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
// TODO: Flying balls
|
||||
// Process bounce logic
|
||||
for npc in self.npcs.iter_mut() {
|
||||
let next_pos = ((npc.pos_x as isize + npc.direction.x), (npc.pos_y as isize + npc.direction.y));
|
||||
if next_pos.0 < 0 || next_pos.1 < 0 {
|
||||
// TODO: Bounce
|
||||
continue;
|
||||
match npc.process_bounce(&self.cells_dup) {
|
||||
Some(n) => {
|
||||
let c = self.cells[n.pos_x + n.pos_y * self.width];
|
||||
self.cells[n.pos_x + n.pos_y * self.width] = c.invert();
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
let index = next_pos.0 as usize + next_pos.1 as usize * self.width;
|
||||
let next_cell = self.cells_dup[index];
|
||||
|
||||
// If our next cell would be one that we collide with.
|
||||
if npc.obstructed_from.contains(&next_cell.variance) {
|
||||
// TODO: Bounce
|
||||
continue;
|
||||
}
|
||||
|
||||
npc.pos_x = next_pos.0 as usize;
|
||||
npc.pos_y = next_pos.1 as usize;
|
||||
|
||||
// We can move freely for now.
|
||||
self.npc_map[index] = Some(Cell::new(npc.obstructed_from[0]));
|
||||
}
|
||||
|
||||
// Write current background
|
||||
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`
|
||||
|
||||
// Write into scratch_cells, since we're still reading from `self.cells`
|
||||
self.cells_dup[idx] = self.cells[idx];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user