150 lines
3.8 KiB
Rust
150 lines
3.8 KiB
Rust
use crossterm::event::{Event, KeyCode};
|
|
use ratatui::{
|
|
Frame, buffer::{Buffer, Cell}, layout::Rect, style::Color, widgets::{Block, Widget}
|
|
};
|
|
|
|
use crate::geometry::{Grid, P2, V2};
|
|
|
|
pub struct GameModel {
|
|
player_pos: P2<isize>,
|
|
room: Room,
|
|
}
|
|
|
|
struct Room {
|
|
size: V2<usize>,
|
|
}
|
|
|
|
impl GameModel {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
player_pos: P2::new(0, 0),
|
|
room: Room {
|
|
size: V2::new(20, 10),
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn update(self, event: Event) -> Option<Self> {
|
|
match event {
|
|
Event::Key(key_event) => match key_event.code {
|
|
KeyCode::Char('q') => return None,
|
|
KeyCode::Char('j') => {
|
|
return Some(Self {
|
|
player_pos: self.player_pos + V2::new(0, 1),
|
|
..self
|
|
})
|
|
},
|
|
KeyCode::Char('k') => {
|
|
return Some(Self {
|
|
player_pos: self.player_pos + V2::new(0, -1),
|
|
..self
|
|
})
|
|
},
|
|
KeyCode::Char('h') => {
|
|
return Some(Self {
|
|
player_pos: self.player_pos + V2::new(-1, 0),
|
|
..self
|
|
})
|
|
},
|
|
KeyCode::Char('l') => {
|
|
return Some(Self {
|
|
player_pos: self.player_pos + V2::new(1, 0),
|
|
..self
|
|
})
|
|
},
|
|
_ => (),
|
|
},
|
|
_ => (),
|
|
}
|
|
|
|
Some(self)
|
|
}
|
|
|
|
pub fn render(&self, frame: &mut Frame) {
|
|
let camera_block = Block::bordered().title("camera");
|
|
let camera_area = camera_block.inner(frame.area());
|
|
let camera = CameraWidget::new(self);
|
|
|
|
frame.render_widget(camera_block, frame.area());
|
|
frame.render_widget(camera, camera_area);
|
|
}
|
|
|
|
fn render_tiles(&self) -> Grid<Tile> {
|
|
let mut tiles = Grid::from_fn(self.room.size.x, self.room.size.y, |_x, _y| Tile::Floor);
|
|
|
|
tiles
|
|
.get_mut(P2::new(1, 1))
|
|
.map(|tile| *tile = Tile::Ladder);
|
|
|
|
tiles
|
|
.get_mut(P2::new(5, 2))
|
|
.map(|tile| *tile = Tile::Amphora);
|
|
|
|
tiles
|
|
.get_mut(P2::new(8, 5))
|
|
.map(|tile| *tile = Tile::Frog);
|
|
|
|
tiles
|
|
.get_mut(self.player_pos)
|
|
.map(|tile| *tile = Tile::Player);
|
|
|
|
|
|
tiles
|
|
}
|
|
}
|
|
|
|
enum Tile {
|
|
Floor,
|
|
Ladder,
|
|
Player,
|
|
Amphora,
|
|
Frog,
|
|
}
|
|
|
|
impl Tile {
|
|
fn render(&self, cell: &mut Cell) {
|
|
match self {
|
|
Tile::Floor => {
|
|
cell.set_symbol(" ");
|
|
}
|
|
Tile::Ladder => {
|
|
cell.set_symbol("Ħ");
|
|
}
|
|
Tile::Player => {
|
|
cell.set_symbol("▲̶͈̊");
|
|
}
|
|
Tile::Amphora => {
|
|
cell.set_symbol("⚱").set_fg(Color::LightRed);
|
|
}
|
|
Tile::Frog => {
|
|
cell.set_symbol("ä̃").set_fg(Color::Green);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct CameraWidget<'a> {
|
|
model: &'a GameModel,
|
|
}
|
|
impl<'a> CameraWidget<'a> {
|
|
fn new(model: &'a GameModel) -> Self {
|
|
Self { model }
|
|
}
|
|
}
|
|
|
|
impl<'a> Widget for CameraWidget<'a> {
|
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
let tiles = self.model.render_tiles();
|
|
for y in 0..area.height {
|
|
for x in 0..area.width {
|
|
let cell = &mut buf[(area.left() + x, area.top() + y)];
|
|
if let Some(tile) = tiles.get(P2::new(x as isize, y as isize)) {
|
|
tile.render(cell);
|
|
} else {
|
|
cell.set_symbol(".");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|