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, room: Room, } struct Room { size: V2, } 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 { 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 { 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("."); } } } } }