Impl basic loop
This commit is contained in:
parent
c00dd3d37a
commit
7ef25034fd
149
src/game.rs
Normal file
149
src/game.rs
Normal file
@ -0,0 +1,149 @@
|
||||
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(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,64 +1,106 @@
|
||||
use std::ops::{Add, Div, Sub};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
struct P2 {
|
||||
x: i32,
|
||||
y: i32,
|
||||
pub struct P2<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
}
|
||||
|
||||
impl P2 {
|
||||
fn new(x: i32, y: i32) -> Self {
|
||||
impl<T> P2<T> {
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
impl Add<V2> for P2 {
|
||||
type Output = P2;
|
||||
|
||||
fn add(self, rhs: V2) -> Self::Output {
|
||||
impl<T: Copy + Add<T>> Add<V2<T>> for P2<T> {
|
||||
type Output = P2<<T as Add>::Output>;
|
||||
|
||||
fn add(self, rhs: V2<T>) -> Self::Output {
|
||||
P2::new(self.x + rhs.x, self.y + rhs.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<P2> for P2 {
|
||||
type Output = V2;
|
||||
impl<T: Copy + Sub<T>> Sub<P2<T>> for P2<T> {
|
||||
type Output = V2<<T as Sub>::Output>;
|
||||
|
||||
fn sub(self, rhs: P2) -> Self::Output {
|
||||
fn sub(self, rhs: P2<T>) -> Self::Output {
|
||||
V2::new(self.x - rhs.x, self.y - rhs.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<V2> for P2 {
|
||||
type Output = P2;
|
||||
impl<T: Copy + Sub<T>> Sub<V2<T>> for P2<T> {
|
||||
type Output = P2<<T as Sub>::Output>;
|
||||
|
||||
fn sub(self, rhs: V2) -> Self::Output {
|
||||
fn sub(self, rhs: V2<T>) -> Self::Output {
|
||||
P2::new(self.x - rhs.x, self.y - rhs.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<i32> for P2 {
|
||||
type Output = P2;
|
||||
impl<T: Copy + Div<T>> Div<T> for P2<T> {
|
||||
type Output = P2<<T as Div>::Output>;
|
||||
|
||||
fn div(self, rhs: i32) -> Self::Output {
|
||||
Self::new(self.x / rhs, self.y / rhs)
|
||||
fn div(self, rhs: T) -> Self::Output {
|
||||
P2::new(self.x / rhs, self.y / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct V2 {
|
||||
x: i32,
|
||||
y: i32,
|
||||
pub struct V2<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl V2 {
|
||||
fn new(x: i32, y: i32) -> Self {
|
||||
impl<T> V2<T> {
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<i32> for V2 {
|
||||
type Output = V2;
|
||||
impl<T: Copy + Div<T>> Div<T> for V2<T> {
|
||||
type Output = V2<<T as Div>::Output>;
|
||||
|
||||
fn div(self, rhs: i32) -> Self::Output {
|
||||
fn div(self, rhs: T) -> Self::Output {
|
||||
V2::new(self.x / rhs, self.y / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Grid<T> {
|
||||
dims: V2<usize>,
|
||||
elements: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Grid<T> {
|
||||
pub fn from_fn(width: usize, height: usize, mut f: impl FnMut(usize, usize) -> T) -> Self {
|
||||
let mut elements = Vec::new();
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
elements.push(f(x, y));
|
||||
}
|
||||
}
|
||||
Self {
|
||||
dims: V2::new(width, height),
|
||||
elements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, index: P2<isize>) -> Option<&T> {
|
||||
self.valid_index(index).map(|index| &self.elements[index])
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, index: P2<isize>) -> Option<&mut T> {
|
||||
self.valid_index(index)
|
||||
.map(|index| &mut self.elements[index])
|
||||
}
|
||||
|
||||
fn valid_index(&self, index: P2<isize>) -> Option<usize> {
|
||||
if index.x >= 0
|
||||
&& (index.x as usize) < self.dims.x
|
||||
&& index.y >= 0
|
||||
&& (index.y as usize) < self.dims.y
|
||||
{
|
||||
Some((index.y as usize) * self.dims.x + (index.x as usize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
50
src/main.rs
50
src/main.rs
@ -1,29 +1,31 @@
|
||||
use std::cmp::max;
|
||||
|
||||
mod game;
|
||||
mod geometry;
|
||||
mod noise;
|
||||
mod terrain;
|
||||
|
||||
use noise::Noise;
|
||||
use color_eyre::eyre::Result;
|
||||
use crossterm::event;
|
||||
use ratatui::DefaultTerminal;
|
||||
|
||||
fn main() {
|
||||
let width = 50;
|
||||
let height = 20;
|
||||
use game::GameModel;
|
||||
|
||||
let long_side = max(width, height) as f32;
|
||||
|
||||
let mut rand = rand::rng();
|
||||
let height_noise = Noise::from_freqs(&mut rand, 12, 16, 3);
|
||||
let tree_cover_noise = Noise::from_freqs(&mut rand, 12, 16, 4);
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let h = height_noise.sample(x as f32 / long_side, y as f32 / long_side);
|
||||
let t = tree_cover_noise.sample(x as f32 / long_side, y as f32 / long_side);
|
||||
|
||||
let terrain = terrain::terrain(h, t);
|
||||
print!("{}", terrain.printable());
|
||||
}
|
||||
println!();
|
||||
}
|
||||
fn main() -> Result<()> {
|
||||
|
||||
let mut terminal = ratatui::init();
|
||||
let result = main_loop(&mut terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
fn main_loop(terminal: &mut DefaultTerminal) -> Result<()> {
|
||||
let mut model = GameModel::new();
|
||||
loop {
|
||||
terminal.draw(|frame| {
|
||||
model.render(frame)
|
||||
})?;
|
||||
|
||||
let term_event = event::read()?;
|
||||
match model.update(term_event) {
|
||||
Some(new_model) => model = new_model,
|
||||
None => break Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user