summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToby Vincent <tobyv@tobyvin.dev>2023-08-03 10:38:15 -0500
committerToby Vincent <tobyv@tobyvin.dev>2023-08-03 10:38:25 -0500
commit86be0bf2dda5580ce384ad4a1de6b248ca572bb6 (patch)
treece7d720ab4034c6dd9ec1fb059f916eebd8746f4
parent37c88bfc0b6dcb4a32e33b3dc35d8eba456388c4 (diff)
-rw-r--r--src/entity/player.rs19
-rw-r--r--src/error.rs3
-rw-r--r--src/game.rs43
-rw-r--r--src/lib.rs28
-rw-r--r--src/main.rs2
-rw-r--r--src/physics.rs92
-rw-r--r--src/physics/error.rs20
-rw-r--r--src/physics/point.rs44
-rw-r--r--src/physics/vector.rs25
-rw-r--r--src/structure.rs7
-rw-r--r--src/structure/building.rs21
11 files changed, 222 insertions, 82 deletions
diff --git a/src/entity/player.rs b/src/entity/player.rs
index 45f3e13..e1961df 100644
--- a/src/entity/player.rs
+++ b/src/entity/player.rs
@@ -1,3 +1,4 @@
+use core::fmt;
use std::{
collections::HashMap,
fmt::{Display, Formatter},
@@ -7,8 +8,8 @@ use termion::event::Key;
use crate::{
game::Command,
- physics::{Point, Vector},
- Position, Render, Velocity,
+ physics::{Collidable, Collider, Point, Position, Vector, Velocity},
+ Render,
};
use crate::entity::Controllable;
@@ -31,7 +32,7 @@ impl Player {
}
impl Display for Player {
- fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
return write!(
f,
"{}@",
@@ -64,7 +65,7 @@ impl Position for Player {
self.position
}
- fn set_position<P: Into<crate::physics::Point>>(&mut self, position: P) -> crate::Result<()> {
+ fn set_position<P: Into<Point>>(&mut self, position: P) -> crate::physics::Result<()> {
self.position = position.into();
Ok(())
}
@@ -75,8 +76,16 @@ impl Velocity for Player {
self.velocity
}
- fn set_velocity<V: Into<crate::physics::Vector>>(&mut self, velocity: V) -> crate::Result<()> {
+ fn set_velocity<V: Into<Vector>>(&mut self, velocity: V) -> crate::physics::Result<()> {
self.velocity = velocity.into();
Ok(())
}
}
+
+impl Collider for Player {
+ fn collision_map(&self) -> &Vec<Point> {
+ &vec![self.position]
+ }
+}
+
+impl Collidable for Player {}
diff --git a/src/error.rs b/src/error.rs
index 81dd4fa..fb73c8d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -3,6 +3,9 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Failed to parse key input: {0:?}")]
+ Physics(#[from] crate::physics::Error),
+
+ #[error("Failed to parse key input: {0:?}")]
IO(#[from] std::io::Error),
#[error("Failed to write to screen: {0:?}")]
diff --git a/src/game.rs b/src/game.rs
index 0358638..b93799d 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -7,9 +7,9 @@ use termion::{
use crate::{
entity::{Controllable, Player},
error::Result,
- physics::Vector,
+ physics::{Physics, Vector, Velocity},
structure::Building,
- Render, Velocity,
+ Engine, Render,
};
#[derive(Debug)]
@@ -32,7 +32,7 @@ impl Game {
for key in reader.keys() {
match self.handle_input(key?) {
Some(Command::Quit) => break,
- Some(Command::UpdatePlayer) => &mut self.update()?,
+ Some(Command::UpdatePlayer) => &mut self.tick_time()?,
None => continue,
};
self.write_screen(&mut screen)?;
@@ -60,9 +60,17 @@ impl Game {
}
}
- fn update(&mut self) -> Result<()> {
- self.player.tick_time()?;
- self.player.set_velocity(Vector::default())
+ fn tick_time(&mut self) -> Result<()> {
+ self.player.tick_time(self)?;
+ self.player
+ .set_velocity(Vector::default())
+ .map_err(From::from)
+ }
+}
+
+impl Engine for Game {
+ fn get_colliders<C: crate::physics::Collider>(&self) -> Vec<C> {
+ self.structures
}
}
@@ -70,7 +78,7 @@ impl Game {
mod tests {
use std::io::stdout;
- use crate::{structure::Building, Game, Position};
+ use crate::{physics::Position, structure::Building, Game};
#[test]
fn run_game() {
@@ -95,4 +103,25 @@ mod tests {
eprintln!("{}", err)
}
}
+
+ #[test]
+ fn collisions() {
+ let stdin = "sssdddq".as_bytes();
+ let stdout = stdout();
+
+ let mut game = Game::default();
+
+ let building = Building::builder()
+ .width(50)
+ .height(50)
+ .position((10, 10))
+ .build();
+
+ game.player.set_position((5, 5)).unwrap();
+ game.structures.push(building);
+
+ if let Err(err) = game.run_loop(stdin, stdout) {
+ eprintln!("{}", err)
+ }
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index aed9bcc..f949411 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,11 +2,15 @@ pub use crate::error::{Error, Result};
pub use crate::game::Game;
pub mod entity;
+pub mod physics;
pub mod structure;
mod error;
mod game;
-mod physics;
+
+pub trait Engine {
+ fn get_colliders<C: crate::physics::Collider>(&self) -> Vec<C>;
+}
pub trait Render {
fn render_map(&self) -> &std::collections::HashMap<crate::physics::Point, char>;
@@ -33,25 +37,3 @@ impl<T: Render> Derender for T {
self.render_map()
}
}
-
-pub trait Collision {
- fn collision_map(&self) -> &Vec<crate::physics::Point>;
- fn is_collision<P: Into<crate::physics::Point>>(&self, point: P) -> bool {
- self.collision_map().contains(&point.into())
- }
-}
-
-pub trait Position {
- fn get_position(&self) -> crate::physics::Point;
- fn set_position<P: Into<crate::physics::Point>>(&mut self, position: P) -> crate::Result<()>;
-}
-
-pub trait Velocity: Position {
- fn get_velocity(&self) -> crate::physics::Vector;
-
- fn set_velocity<V: Into<crate::physics::Vector>>(&mut self, velocity: V) -> crate::Result<()>;
-
- fn tick_time(&mut self) -> Result<()> {
- self.set_position(self.get_position() + self.get_velocity())
- }
-}
diff --git a/src/main.rs b/src/main.rs
index 95b6ea3..9b49ea6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
use std::io::{stdin, stdout};
-use rustic_adventure::{structure::Building, Game, Position};
+use rustic_adventure::{physics::Position, structure::Building, Game};
fn main() {
let stdin = stdin();
diff --git a/src/physics.rs b/src/physics.rs
index de09a2f..458f1e7 100644
--- a/src/physics.rs
+++ b/src/physics.rs
@@ -1,64 +1,70 @@
-use std::ops::Add;
+pub(crate) use error::{Error, Result};
+pub use point::Point;
+pub use vector::Vector;
-#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
-pub struct Point {
- pub x: u16,
- pub y: u16,
+mod error;
+mod point;
+mod vector;
+
+pub trait Physics {
+ fn tick_time<E: crate::Engine>(&mut self, engine: E) -> Result<()>;
+}
+
+pub trait Position {
+ fn get_position(&self) -> Point;
+ fn set_position<P: Into<Point>>(&mut self, position: P) -> Result<()>;
}
-impl Point {
- pub fn new(x: u16, y: u16) -> Self {
- Self { x, y }
+pub trait Velocity: Position {
+ fn get_velocity(&self) -> Vector;
+
+ fn set_velocity<V: Into<Vector>>(&mut self, velocity: V) -> Result<()>;
+
+ fn apply_velocity(&mut self) -> Result<()> {
+ self.set_position(self.get_position() + self.get_velocity())
}
- pub fn add_velocity(&mut self, velocity: Vector) {
- self.x += velocity.x;
- self.y += velocity.y;
+ fn reset_velocity(&mut self) -> Result<()> {
+ self.set_velocity(Vector::default())
}
}
-impl From<(u16, u16)> for Point {
- fn from((x, y): (u16, u16)) -> Self {
- Self { x, y }
- }
+pub trait Collider {
+ fn collision_map(&self) -> &Vec<Point>;
}
-impl From<Point> for (u16, u16) {
- fn from(point: Point) -> Self {
- (point.x, point.y)
+pub trait Collidable: Collider + Velocity {
+ fn next_collision_map(&self) -> Vec<Point> {
+ let velocity = self.get_velocity();
+ self.collision_map().iter().map(|p| *p + velocity).collect()
}
-}
-impl Add<Vector> for Point {
- type Output = Self;
+ fn check_collision<C: Collider>(&self, colliders: C) -> Result<()> {
+ let collision = self
+ .next_collision_map()
+ .iter()
+ .zip(colliders.collision_map())
+ .filter_map(|(p1, p2)| p1.eq(p2).then_some(*p1))
+ .collect::<Vec<point::Point>>();
- fn add(self, rhs: Vector) -> Self::Output {
- Self {
- x: self.x + rhs.x,
- y: self.y + rhs.y,
+ if !collision.is_empty() {
+ Ok(())
+ } else {
+ Err(Error::Collision(collision))
}
}
-}
-
-#[derive(Debug, Default, Clone, Copy)]
-pub struct Vector {
- pub x: u16,
- pub y: u16,
-}
-impl Vector {
- pub fn new(x: u16, y: u16) -> Self {
- Self { x, y }
+ fn check_colliders<C: Collider>(&self, colliders: Vec<C>) -> Result<()> {
+ for collidable in colliders {
+ self.check_collision(collidable)?
+ }
+ Ok(())
}
}
-impl Add<Vector> for Vector {
- type Output = Self;
-
- fn add(self, rhs: Vector) -> Self::Output {
- Self {
- x: self.x + rhs.x,
- y: self.y + rhs.y,
- }
+impl<T: Velocity + Collidable> Physics for T {
+ fn tick_time<E: crate::Engine>(&mut self, engine: E) -> Result<()> {
+ self.check_colliders(engine.get_colliders())?;
+ self.apply_velocity()
}
}
diff --git a/src/physics/error.rs b/src/physics/error.rs
new file mode 100644
index 0000000..4ceb325
--- /dev/null
+++ b/src/physics/error.rs
@@ -0,0 +1,20 @@
+use std::fmt::Display;
+
+use super::Point;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+pub enum Error {
+ Collision(Vec<Point>),
+}
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Collision(v) => write!(f, "Collision with collidable at: {:?}", v),
+ }
+ }
+}
diff --git a/src/physics/point.rs b/src/physics/point.rs
new file mode 100644
index 0000000..75bccb4
--- /dev/null
+++ b/src/physics/point.rs
@@ -0,0 +1,44 @@
+use std::ops::Add;
+
+use super::vector::Vector;
+
+#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
+pub struct Point {
+ pub x: u16,
+ pub y: u16,
+}
+
+impl Point {
+ pub fn new(x: u16, y: u16) -> Self {
+ Self { x, y }
+ }
+
+ pub fn add_velocity(&mut self, velocity: Vector) {
+ self.x += velocity.x;
+ self.y += velocity.y;
+ }
+}
+
+impl From<(u16, u16)> for Point {
+ fn from((x, y): (u16, u16)) -> Self {
+ Self { x, y }
+ }
+}
+
+impl From<Point> for (u16, u16) {
+ fn from(point: Point) -> Self {
+ (point.x, point.y)
+ }
+}
+
+impl Add<Vector> for Point {
+ type Output = Self;
+
+ fn add(self, rhs: Vector) -> Self::Output {
+ Self {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+
diff --git a/src/physics/vector.rs b/src/physics/vector.rs
new file mode 100644
index 0000000..48e02e8
--- /dev/null
+++ b/src/physics/vector.rs
@@ -0,0 +1,25 @@
+use std::ops::Add;
+
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Vector {
+ pub x: u16,
+ pub y: u16,
+}
+
+impl Vector {
+ pub fn new(x: u16, y: u16) -> Self {
+ Self { x, y }
+ }
+}
+
+impl Add<Vector> for Vector {
+ type Output = Self;
+
+ fn add(self, rhs: Vector) -> Self::Output {
+ Self {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+
diff --git a/src/structure.rs b/src/structure.rs
index 2137766..f8575b1 100644
--- a/src/structure.rs
+++ b/src/structure.rs
@@ -2,7 +2,10 @@ use std::collections::HashMap;
pub use building::Building;
-use crate::{physics::Point, Collision, Render};
+use crate::{
+ physics::{Collider, Point},
+ Render,
+};
mod building;
@@ -17,7 +20,7 @@ impl Render for Structure {
}
}
-impl Collision for Structure {
+impl Collider for Structure {
fn collision_map(&self) -> &Vec<crate::physics::Point> {
&self.collision_map
}
diff --git a/src/structure/building.rs b/src/structure/building.rs
index b6c6346..55961dd 100644
--- a/src/structure/building.rs
+++ b/src/structure/building.rs
@@ -1,6 +1,9 @@
use std::collections::HashMap;
-use crate::{physics::Point, Render};
+use crate::{
+ physics::{Collider, Point},
+ Render,
+};
#[derive(Debug, PartialEq, Eq)]
pub struct Building {
@@ -78,6 +81,22 @@ impl Render for Building {
&self.render_map
}
}
+
+impl Collider for Building {
+ fn collision_map(&self) -> &Vec<Point> {
+ let collision_map = Vec::new();
+ for x in self.position.x..self.width {
+ collision_map.push((x, self.position.y).into());
+ collision_map.push((x, self.height).into());
+ }
+ for y in self.position.y..self.height {
+ collision_map.push((self.position.x, y).into());
+ collision_map.push((self.width, y).into());
+ }
+ &collision_map
+ }
+}
+
#[cfg(test)]
mod tests {
use std::collections::HashMap;