Browse Source

Add rltk

master
Garrit Franke 3 years ago
parent
commit
7062e9d9fd
  1. 1895
      Cargo.lock
  2. 2
      Cargo.toml
  3. 284
      src/main.rs

1895
Cargo.lock generated

File diff suppressed because it is too large Load Diff

2
Cargo.toml

@ -7,6 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
termion = "1.5.5"
rltk = "0.8.1"
rand = "0.8.2"
noise = "0.7"

284
src/main.rs

@ -1,162 +1,182 @@
extern crate noise;
extern crate rand;
extern crate termion;
use crate::noise::utils::NoiseMapBuilder;
use crate::termion::cursor::DetectCursorPos;
use noise::utils::PlaneMapBuilder;
use noise::Fbm;
use std::fmt::Debug;
use std::fmt::Display;
use std::io::{stdin, stdout, Write};
use std::path::Path;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{clear, cursor, screen};
const MAP_SIZE_X: usize = 100;
const MAP_SIZE_Y: usize = 100;
struct GOL {
screen: Box<dyn Write>,
player: Player,
map: Box<[[char; MAP_SIZE_X]; MAP_SIZE_Y]>,
// This is based on example 3, but adds in highlighting visible tiles.
//
// Comments that duplicate previous examples have been removed for brevity.
//////////////////////////////////////////////////////////////
rltk::add_wasm_support!();
use rltk::prelude::*;
#[derive(PartialEq, Copy, Clone)]
enum TileType {
Wall,
Floor,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub struct Player {
pub pos: Pos,
// Just like example 3, but we're adding an additional vector: visible
struct State {
map: Vec<TileType>,
player_position: usize,
visible: Vec<bool>,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub struct Pos {
pub x: usize,
pub y: usize,
}
impl GOL {
pub fn new() -> Self {
let screen = screen::AlternateScreen::from(stdout().into_raw_mode().unwrap());
let fbm = Fbm::new();
impl State {
pub fn new() -> State {
// Same as example 3, but we've added the visible tiles
let mut state = State {
map: vec![TileType::Floor; 80 * 50],
player_position: (25 * 80) + 40, // Equivalent to point2d_to_index(40, 25) but we haven't initialized it yet
visible: vec![false; 80 * 50],
};
let noise_map = PlaneMapBuilder::new(&fbm)
.set_size(MAP_SIZE_X, MAP_SIZE_Y)
.set_x_bounds(-5.0, 5.0)
.set_y_bounds(-5.0, 5.0)
.build();
for x in 0..80 {
let wall1_pos = state.point2d_to_index(Point::new(x, 0));
let wall2_pos = state.point2d_to_index(Point::new(x, 49));
state.map[wall1_pos] = TileType::Wall;
state.map[wall2_pos] = TileType::Wall;
}
for y in 0..50 {
let wall1_pos = state.point2d_to_index(Point::new(0, y));
let wall2_pos = state.point2d_to_index(Point::new(79, y));
state.map[wall1_pos] = TileType::Wall;
state.map[wall2_pos] = TileType::Wall;
}
let mut map: Box<[[char; MAP_SIZE_X]; MAP_SIZE_Y]> =
Box::new([[' '; MAP_SIZE_X]; MAP_SIZE_Y]);
let mut rng = RandomNumberGenerator::new();
for (i, row) in map.clone().iter().enumerate() {
for (j, _) in row.iter().enumerate() {
map[i][j] = match noise_map.get_value(i, j) {
v if v > 0.0 => '#',
_ => ' ',
}
for _ in 0..400 {
let x = rng.range(1, 79);
let y = rng.range(1, 49);
let idx = state.point2d_to_index(Point::new(x, y));
if state.player_position != idx {
state.map[idx] = TileType::Wall;
}
}
let mut s = Self {
screen: Box::new(screen),
player: Player {
pos: Pos {
x: MAP_SIZE_X / 2,
y: MAP_SIZE_Y / 2,
},
},
map: map,
};
s.write(&termion::clear::All);
s.write(&termion::cursor::Goto(1, 1));
log(format!("{:?}", s.player));
s.flush();
s
state
}
pub fn write(&mut self, w: &dyn Display) {
log(w);
write!(self.screen, "{}", w);
pub fn move_player(&mut self, delta: Point) {
let current_position = self.index_to_point2d(self.player_position);
let new_position = current_position + delta;
let new_idx = self.point2d_to_index(new_position);
if self.map[new_idx] == TileType::Floor {
self.player_position = new_idx;
}
}
}
pub fn pos(&mut self) -> (u16, u16) {
return self.screen.cursor_pos().unwrap();
}
// Implement the game loop
impl GameState for State {
#[allow(non_snake_case)]
fn tick(&mut self, ctx: &mut Rltk) {
match ctx.key {
None => {} // Nothing happened
Some(key) => {
// A key is pressed or held
match key {
// Numpad
VirtualKeyCode::Numpad8 => self.move_player(Point::new(0, -1)),
VirtualKeyCode::Numpad4 => self.move_player(Point::new(-1, 0)),
VirtualKeyCode::Numpad6 => self.move_player(Point::new(1, 0)),
VirtualKeyCode::Numpad2 => self.move_player(Point::new(0, 1)),
// Numpad diagonals
VirtualKeyCode::Numpad7 => self.move_player(Point::new(-1, -1)),
VirtualKeyCode::Numpad9 => self.move_player(Point::new(1, -1)),
VirtualKeyCode::Numpad1 => self.move_player(Point::new(-1, 1)),
VirtualKeyCode::Numpad3 => self.move_player(Point::new(1, 1)),
// Cursors
VirtualKeyCode::Up => self.move_player(Point::new(0, -1)),
VirtualKeyCode::Down => self.move_player(Point::new(0, 1)),
VirtualKeyCode::Left => self.move_player(Point::new(-1, 0)),
VirtualKeyCode::Right => self.move_player(Point::new(1, 0)),
_ => {} // Ignore all the other possibilities
}
}
}
pub fn flush(&mut self) {
self.screen.flush().unwrap();
}
}
// Set all tiles to not visible
for v in self.visible.iter_mut() {
*v = false;
}
fn log(x: impl Display) {
std::fs::File::create(Path::new("log")).unwrap();
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(true)
.open("./log")
.unwrap();
// Obtain the player's visible tile set, and apply it
let player_position = self.index_to_point2d(self.player_position);
let fov = rltk::field_of_view_set(player_position, 8, self);
if let Err(e) = writeln!(file, "{}", x) {
eprintln!("Couldn't write to file: {}", e);
}
}
// Note that the steps above would generally not be run every frame!
for idx in fov.iter() {
let point = self.point2d_to_index(*idx);
self.visible[point] = true;
}
fn main() {
let stdin = stdin();
let mut gol = GOL::new();
gol.write(&termion::cursor::Hide);
// Clear the screen
ctx.cls();
gol.flush();
// Iterate the map array, incrementing coordinates as we go.
let mut y = 0;
let mut x = 0;
for (i, tile) in self.map.iter().enumerate() {
// Render a tile depending upon the tile type; now we check visibility as well!
let mut fg;
let mut glyph = ".";
draw(&mut gol);
match tile {
TileType::Floor => {
fg = RGB::from_f32(0.5, 0.5, 0.0);
}
TileType::Wall => {
fg = RGB::from_f32(0.0, 1.0, 0.0);
glyph = "#";
}
}
if !self.visible[i] {
fg = fg.to_greyscale();
}
ctx.print_color(x, y, fg, RGB::from_f32(0., 0., 0.), glyph);
for c in stdin.keys() {
let key = c.unwrap();
if let Err(_) = update(&mut gol, &key) {
break;
// Move the coordinates
x += 1;
if x > 79 {
x = 0;
y += 1;
}
}
draw(&mut gol);
gol.flush();
// Render the player @ symbol
let ppos = self.index_to_point2d(self.player_position);
ctx.print_color(
ppos.x,
ppos.y,
RGB::from_f32(1.0, 1.0, 0.0),
RGB::from_f32(0., 0., 0.),
"@",
);
}
gol.write(&termion::cursor::Show);
}
fn update(game: &mut GOL, key: &Key) -> Result<(), ()> {
match key {
Key::Left => game.player.pos.x -= 1,
Key::Right => game.player.pos.x += 1,
Key::Up => game.player.pos.y -= 1,
Key::Down => game.player.pos.y += 1,
Key::Char('q') => return Err(()),
_ => {}
};
Ok(())
// To work with RLTK's algorithm features, we need to implement some the Algorithm2D trait for our map.
// First, default implementations of some we aren't using yet (more on these later!)
impl BaseMap for State {
// We'll use this one - if its a wall, we can't see through it
fn is_opaque(&self, idx: usize) -> bool {
self.map[idx as usize] == TileType::Wall
}
}
fn draw(game: &mut GOL) {
game.write(&clear::All);
let size = termion::terminal_size().unwrap();
let center_x = size.0 / 2;
let center_y = size.1 / 2;
game.write(&cursor::Goto(center_x, center_y));
game.write(&'X');
for col in 1..size.0 {
for row in 1..size.1 {
let cell_x = game.player.pos.x + row as usize;
let cell_y = game.player.pos.y + col as usize;
if let Some(r) = game.map.clone().get_mut(cell_x) {
if let Some(cell) = r.get_mut(cell_y) {
game.write(&cursor::Goto(row, col));
game.write(&cell);
}
}
}
impl Algorithm2D for State {
fn dimensions(&self) -> Point {
Point::new(80, 50)
}
game.flush();
}
fn main() -> RltkError {
let context = RltkBuilder::simple80x50()
.with_title("RLTK Example 4 - Field-Of-View")
.build()?;
let gs = State::new();
rltk::main_loop(context, gs)
}

Loading…
Cancel
Save