bracket_terminal::add_wasm_support!();
use bracket_pathfinding::prelude::*;
use bracket_random::prelude::*;
use bracket_terminal::prelude::*;
#[derive(PartialEq, Copy, Clone)]
enum TileType {
Wall,
Floor,
}
#[derive(PartialEq, Copy, Clone)]
enum Mode {
Waiting,
Moving,
}
struct State {
map: Vec<TileType>,
player_position: usize,
visible: Vec<bool>,
mode: Mode,
path: NavigationPath,
}
pub fn xy_idx(x: i32, y: i32) -> usize {
(y as usize * 80) + x as usize
}
pub fn idx_xy(idx: usize) -> (i32, i32) {
(idx as i32 % 80, idx as i32 / 80)
}
impl State {
pub fn new() -> State {
let mut state = State {
map: vec![TileType::Floor; 80 * 50],
player_position: xy_idx(40, 25),
visible: vec![false; 80 * 50],
mode: Mode::Waiting,
path: NavigationPath::new(),
};
for x in 0..80 {
state.map[xy_idx(x, 0)] = TileType::Wall;
state.map[xy_idx(x, 49)] = TileType::Wall;
}
for y in 0..50 {
state.map[xy_idx(0, y)] = TileType::Wall;
state.map[xy_idx(79, y)] = TileType::Wall;
}
let mut rng = RandomNumberGenerator::new();
for _ in 0..1400 {
let x = rng.range(1, 79);
let y = rng.range(1, 49);
let idx = xy_idx(x, y);
if state.player_position != idx {
state.map[idx] = TileType::Wall;
}
}
state
}
pub fn is_exit_valid(&self, x: i32, y: i32) -> bool {
if x < 1 || x > 79 || y < 1 || y > 49 {
return false;
}
let idx = (y * 80) + x;
self.map[idx as usize] == TileType::Floor
}
}
impl GameState for State {
#[allow(non_snake_case)]
fn tick(&mut self, ctx: &mut BTerm) {
let mut draw_batch = DrawBatch::new();
for v in &mut self.visible {
*v = false;
}
let player_position = self.index_to_point2d(self.player_position);
let fov = field_of_view_set(player_position, 8, self);
for idx in &fov {
self.visible[xy_idx(idx.x, idx.y)] = true;
}
draw_batch.cls();
let mut y = 0;
let mut x = 0;
for (i, tile) in self.map.iter().enumerate() {
let mut fg;
let mut glyph = ".";
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();
}
draw_batch.print_color(
Point::new(x, y),
glyph,
ColorPair::new(fg, RGB::from_f32(0., 0., 0.)),
);
x += 1;
if x > 79 {
x = 0;
y += 1;
}
}
if self.mode == Mode::Waiting {
let mouse_pos = INPUT.lock().mouse_tile(0);
let mouse_idx = self.point2d_to_index(mouse_pos);
draw_batch.print_color(
mouse_pos,
"X",
ColorPair::new(RGB::from_f32(0.0, 1.0, 1.0), RGB::from_f32(0.0, 1.0, 1.0)),
);
if self.map[mouse_idx as usize] != TileType::Wall {
let path = a_star_search(self.player_position, mouse_idx, self);
if path.success {
for loc in path.steps.iter().skip(1) {
let x = (loc % 80) as i32;
let y = (loc / 80) as i32;
draw_batch.print_color(
Point::new(x, y),
"*",
ColorPair::new(RGB::from_f32(1., 0., 0.), RGB::from_f32(0., 0., 0.)),
);
}
if INPUT.lock().is_mouse_button_pressed(0) {
self.mode = Mode::Moving;
self.path = path;
}
}
}
} else {
self.player_position = self.path.steps[0] as usize;
self.path.steps.remove(0);
if self.path.steps.is_empty() {
self.mode = Mode::Waiting;
}
}
let ppos = idx_xy(self.player_position);
draw_batch.print_color(
Point::from_tuple(ppos),
"@",
ColorPair::new(RGB::from_f32(1.0, 1.0, 0.0), RGB::from_f32(0., 0., 0.)),
);
draw_batch.submit(0).expect("Batch error");
render_draw_buffer(ctx).expect("Render error");
}
}
impl BaseMap for State {
fn is_opaque(&self, idx: usize) -> bool {
self.map[idx] == TileType::Wall
}
fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> {
let mut exits = SmallVec::new();
let x = (idx % 80) as i32;
let y = (idx / 80) as i32;
if self.is_exit_valid(x - 1, y) {
exits.push((idx - 1, 1.0))
};
if self.is_exit_valid(x + 1, y) {
exits.push((idx + 1, 1.0))
};
if self.is_exit_valid(x, y - 1) {
exits.push((idx - 80, 1.0))
};
if self.is_exit_valid(x, y + 1) {
exits.push((idx + 80, 1.0))
};
if self.is_exit_valid(x - 1, y - 1) {
exits.push(((idx - 80) - 1, 1.4));
}
if self.is_exit_valid(x + 1, y - 1) {
exits.push(((idx - 80) + 1, 1.4));
}
if self.is_exit_valid(x - 1, y + 1) {
exits.push(((idx + 80) - 1, 1.4));
}
if self.is_exit_valid(x + 1, y + 1) {
exits.push(((idx + 80) + 1, 1.4));
}
exits
}
fn get_pathing_distance(&self, idx1: usize, idx2: usize) -> f32 {
let p1 = Point::new(idx1 % 80, idx1 / 80);
let p2 = Point::new(idx2 % 80, idx2 / 80);
DistanceAlg::Pythagoras.distance2d(p1, p2)
}
}
impl Algorithm2D for State {
fn dimensions(&self) -> Point {
Point::new(80, 50)
}
}
fn main() -> BError {
let context = BTermBuilder::simple80x50()
.with_title("Bracket Terminal Example - A* Mouse")
.build()?;
let gs = State::new();
main_loop(context, gs)
}