cdungeon/src/game.rs
2025-11-29 14:56:53 +01:00

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(".");
}
}
}
}
}