This commit is contained in:
Daniel Flanagan 2022-05-25 10:02:34 -05:00
parent 0befb6d183
commit a15c31d241
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
7 changed files with 2028 additions and 2 deletions

1779
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bracket-lib = "0.8.1"
getrandom = { version = "0.2", features = ["js"] }

11
makefile Normal file
View file

@ -0,0 +1,11 @@
build: ; cargo build
build-release: ; cargo build --release
build-wasm:
cargo build --release --target wasm32-unknown-unknown
wasm-bindgen target/wasm32-unknown-unknown/release/dungeoncrawl.wasm --out-dir target/dungeoncrawl --no-modules --no-typescript
cp src/index.html target/dungeoncrawl
upload target/dungeoncrawl
format: ; cargo fmt

73
src/index.html Normal file
View file

@ -0,0 +1,73 @@
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<title>Dungeon Crawl by lyte.dev</title>
</head>
<body>
<canvas id="canvas" width="640" height="480"></canvas>
<section id="about">
<h1>Dungeon Crawl</h1>
<p>
A tiny roguelike written in rust for funsies and compiled to
webassembly so you can play it in your browser without having to
download <em>anything</em>.
</p>
<h3>Links</h3>
<ul>
<li><a href="//git.lyte.dev/lytedev/rust-dungeoncrawl">Source Code</a></li>
<li><a href="//files.lyte.dev/uploads/dungeoncrawl">Play</a></li>
</ul>
<h3>Controls</h3>
<ul>
<li>
WASD -> Move
</li>
</ul>
</section>
<script src="./dungeoncrawl.js"></script>
<script>
window.addEventListener("load", async () => {
await wasm_bindgen("./dungeoncrawl_bg.wasm");
});
</script>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body, html {
background-color: #111;
display: flex;
min-height: 100vh;
max-width: 100vw;
justify-content: center;
align-items: center;
flex-direction: column;
font-family: sans-serif;
color: #fff;
line-height: 1.5;
}
canvas { max-height: 100vh; max-width: 100vw; }
a { color: #0af; margin-bottom: 1em; }
section#about {
width: 60ch;
text-align: left;
display: flex;
justify-content: center;
align-items: flex-start;
flex-direction: column;
}
section#about > * {
margin-top: 0.5em;
}
section#about > h1,
section#about > h2,
section#about > h3,
section#about > h4,
section#about > h5,
section#about > h6 {
margin-top: 1.5em;
}
ul, ol {
padding-left: 1.2em;
}
</style>
</body>
</html>

View file

@ -1,3 +1,47 @@
fn main() {
println!("Hello, world!");
#![warn(clippy::all, clippy::pedantic)]
mod map;
mod player;
mod prelude {
pub use bracket_lib::prelude::*;
pub const SCREEN_WIDTH: i32 = 80;
pub const SCREEN_HEIGHT: i32 = 50;
pub use crate::map::*;
pub use crate::player::*;
}
use prelude::*;
struct State {
map: Map,
player: Player,
}
impl State {
fn new() -> Self {
Self {
map: Map::new(None, None),
player: Player::new(Point::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2)),
}
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut BTerm) {
ctx.cls();
self.map.render(ctx);
self.player.render(ctx);
self.player.update(ctx, &self.map);
}
}
fn main() -> BError {
let context = BTermBuilder::simple80x50()
.with_title("Dungeon Crawler")
.with_fps_cap(60.0)
.build()?;
println!("Game starting...");
main_loop(context, State::new())
}

82
src/map.rs Normal file
View file

@ -0,0 +1,82 @@
use crate::prelude::*;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TileKind {
Wall,
Floor,
}
#[derive(Debug, Clone)]
pub struct Tile {
kind: TileKind,
}
impl Tile {
pub fn new(kind: TileKind) -> Self {
Self {
kind,
}
}
pub fn render_point(&self, ctx: &mut BTerm, p: Point) {
self.render(ctx, p.x, p.y);
}
pub fn render(&self, ctx: &mut BTerm, x: i32, y: i32) {
match self.kind {
TileKind::Floor => ctx.set(x, y, WHITE, BLACK, to_cp437(' ')),
TileKind::Wall => ctx.set(x, y, WHITE, BLACK, to_cp437('#')),
}
}
pub fn can_enter(&self) -> bool {
match self.kind {
TileKind::Floor => true,
TileKind::Wall => false,
}
}
}
pub struct Map {
pub tiles: Vec<Tile>,
pub width: i32,
pub height: i32,
}
impl Map {
pub fn new(width: Option<i32>, height: Option<i32>) -> Self {
let actual_width = width.unwrap_or(SCREEN_WIDTH);
let actual_height = height.unwrap_or(SCREEN_HEIGHT);
Self {
tiles: vec![Tile::new(TileKind::Floor); (actual_width * actual_height) as usize],
width: actual_width,
height: actual_height,
}
}
pub fn tile_at_point(&self, p: Point) -> &Tile {
self.tile_at(p.x, p.y)
}
pub fn tile_at(&self, x: i32, y: i32) -> &Tile {
&(self.tiles[((y * self.width) + x) as usize])
}
pub fn is_in_bounds_point(&self, p: Point) -> bool {
self.is_in_bounds(p.x, p.y)
}
pub fn is_in_bounds(&self, x: i32, y: i32) -> bool {
x >= 0 && x < self.width && y >= 0 && y < self.height
}
// TODO: this will need a given set of coords to look at?
pub fn render(&self, ctx: &mut BTerm) {
for y in 0..SCREEN_HEIGHT {
for x in 0..SCREEN_WIDTH {
self.tile_at(x, y).render(ctx, x, y);
}
}
}
}

35
src/player.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::prelude::*;
pub struct Player {
pub position: Point,
}
impl Player {
pub fn new(position: Point) -> Self {
Self {
position,
}
}
pub fn render(&self, ctx: &mut BTerm) {
ctx.set(self.position.x, self.position.y, GREEN, BLACK, to_cp437('@'));
}
pub fn update(&mut self, ctx: &mut BTerm, map: &Map) {
if let Some(key) = ctx.key {
let delta = match key {
VirtualKeyCode::A => Point::new(-1, 0),
VirtualKeyCode::S => Point::new(0, 1),
VirtualKeyCode::D => Point::new(1, 0),
VirtualKeyCode::W => Point::new(0, -1),
_ => Point::zero(),
};
let new_position = self.position + delta;
if map.is_in_bounds_point(new_position) && map.tile_at_point(new_position).can_enter() {
self.position = new_position;
}
}
}
}