diff options
author | Griffin Smith <root@gws.fyi> | 2019-07-14T18·29-0400 |
---|---|---|
committer | Griffin Smith <root@gws.fyi> | 2019-07-14T18·29-0400 |
commit | e7ad87c7301f266dece36e7558c0f212e370aac6 (patch) | |
tree | 7da150d5648cc0b17d973bf4a30673f36b20be82 /src | |
parent | 081146da30bcf1a17d9533c3dc9c735a3a558165 (diff) |
Add (statically-included) entity raws
Add a system for statically-included entity raws (which necessitated making a deserializable existential Color struct) and test it out by initializing the game (for now) with a single on-screen gormlak.
Diffstat (limited to 'src')
-rw-r--r-- | src/display/color.rs | 149 | ||||
-rw-r--r-- | src/display/mod.rs | 5 | ||||
-rw-r--r-- | src/entities/creature.rs | 43 | ||||
-rw-r--r-- | src/entities/entity_char.rs | 22 | ||||
-rw-r--r-- | src/entities/mod.rs | 10 | ||||
-rw-r--r-- | src/entities/raws.rs | 57 | ||||
-rw-r--r-- | src/entities/raws/gormlak.toml | 10 | ||||
-rw-r--r-- | src/game.rs | 12 | ||||
-rw-r--r-- | src/types/mod.rs | 3 |
9 files changed, 306 insertions, 5 deletions
diff --git a/src/display/color.rs b/src/display/color.rs new file mode 100644 index 000000000000..7de1f124b704 --- /dev/null +++ b/src/display/color.rs @@ -0,0 +1,149 @@ +use serde::de::{self, Unexpected, Visitor}; +use std::fmt; +use std::marker::PhantomData; +use termion::color; + +#[derive(Debug)] +pub struct Color(Box<dyn color::Color>); + +unsafe impl Sync for Color {} +unsafe impl Send for Color {} + +impl Color { + pub fn new<C: color::Color + 'static>(c: C) -> Self { + Color(Box::new(c)) + } +} + +impl color::Color for Color { + fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_fg(f) + } + + fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_bg(f) + } +} + +impl<'a> color::Color for &'a Color { + fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_fg(f) + } + + fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_bg(f) + } +} + +pub struct ColorVisitor { + marker: PhantomData<fn() -> Color>, +} + +impl ColorVisitor { + fn new() -> Self { + ColorVisitor { + marker: PhantomData, + } + } +} + +impl<'de> Visitor<'de> for ColorVisitor { + type Value = Color; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("A color") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + match v.to_lowercase().as_ref() { + "black" => Ok(Color(Box::new(color::Black))), + "blue" => Ok(Color(Box::new(color::Blue))), + "cyan" => Ok(Color(Box::new(color::Cyan))), + "green" => Ok(Color(Box::new(color::Green))), + "light black" | "light_black" => { + Ok(Color(Box::new(color::LightBlack))) + } + "light blue" | "light_blue" => { + Ok(Color(Box::new(color::LightBlue))) + } + "light cyan" | "light_cyan" => { + Ok(Color(Box::new(color::LightCyan))) + } + "light green" | "light_green" => { + Ok(Color(Box::new(color::LightGreen))) + } + "light magenta" | "light_magenta" => { + Ok(Color(Box::new(color::LightMagenta))) + } + "light red" | "light_red" => Ok(Color(Box::new(color::LightRed))), + "light white" | "light_white" => { + Ok(Color(Box::new(color::LightWhite))) + } + "light yellow" | "light_yellow" => { + Ok(Color(Box::new(color::LightYellow))) + } + "magenta" => Ok(Color(Box::new(color::Magenta))), + "magenta" => Ok(Color(Box::new(color::Magenta))), + "red" => Ok(Color(Box::new(color::Red))), + "white" => Ok(Color(Box::new(color::White))), + "yellow" => Ok(Color(Box::new(color::Yellow))), + _ => Err(de::Error::invalid_value( + Unexpected::Str(v), + &"a valid color", + )), + } + } + + fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> + where + A: de::MapAccess<'de>, + { + let mut red = None; + let mut green = None; + let mut blue = None; + while let Some((k, v)) = map.next_entry()? { + match k { + "red" => { + red = Some(v); + } + "green" => { + green = Some(v); + } + "blue" => { + blue = Some(v); + } + _ => { + return Err(de::Error::unknown_field( + k, + &["red", "green", "blue"], + )); + } + } + } + + match (red, green, blue) { + (Some(r), Some(g), Some(b)) => { + Ok(Color(Box::new(color::Rgb(r, g, b)))) + } + (None, _, _) => Err(de::Error::missing_field("red")), + (_, None, _) => Err(de::Error::missing_field("green")), + (_, _, None) => Err(de::Error::missing_field("blue")), + } + } + + fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> { + Ok(Color(Box::new(color::AnsiValue(v)))) + } +} + +impl<'de> serde::Deserialize<'de> for Color { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(ColorVisitor::new()) + } +} diff --git a/src/display/mod.rs b/src/display/mod.rs index 9e15a0d97d62..3e30200ac723 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -1,3 +1,4 @@ +pub mod color; pub mod draw_box; pub mod utils; pub mod viewport; @@ -17,13 +18,13 @@ pub trait Draw: Positioned { fn do_draw(&self, out: &mut Write) -> io::Result<()>; } -impl<T : Draw> Draw for &T { +impl<T: Draw> Draw for &T { fn do_draw(&self, out: &mut Write) -> io::Result<()> { (**self).do_draw(out) } } -impl<T : Draw> Draw for Box<T> { +impl<T: Draw> Draw for Box<T> { fn do_draw(&self, out: &mut Write) -> io::Result<()> { (**self).do_draw(out) } diff --git a/src/entities/creature.rs b/src/entities/creature.rs new file mode 100644 index 000000000000..6ddeade21845 --- /dev/null +++ b/src/entities/creature.rs @@ -0,0 +1,43 @@ +use crate::display; +use crate::entities::raws::CreatureType; +use crate::entities::raws::EntityRaw; +use crate::entities::{raw, Entity}; +use crate::types::Position; +use std::io::{self, Write}; + +pub struct Creature { + pub typ: &'static CreatureType<'static>, + pub position: Position, + pub hitpoints: u16, +} + +impl Creature { + pub fn new_from_raw(name: &'static str, position: Position) -> Self { + match raw(name) { + EntityRaw::Creature(typ) => Self::new_with_type(typ, position), + _ => panic!("Invalid raw type for {:?}, expected Creature", name), + } + } + + pub fn new_with_type( + typ: &'static CreatureType<'static>, + position: Position, + ) -> Self { + Creature { + typ, + position, + hitpoints: typ.max_hitpoints, + } + } +} + +positioned!(Creature); +positioned_mut!(Creature); + +impl Entity for Creature {} + +impl display::Draw for Creature { + fn do_draw(&self, out: &mut Write) -> io::Result<()> { + write!(out, "{}", self.typ.chr) + } +} diff --git a/src/entities/entity_char.rs b/src/entities/entity_char.rs new file mode 100644 index 000000000000..578aaf3da5c3 --- /dev/null +++ b/src/entities/entity_char.rs @@ -0,0 +1,22 @@ +use crate::display::color::Color; +use std::fmt::{self, Display, Formatter}; +use termion::color; + +#[derive(Debug, Deserialize)] +pub struct EntityChar { + color: Color, + #[serde(rename = "char")] + chr: char, +} + +impl Display for EntityChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!( + f, + "{}{}{}", + color::Fg(&self.color), + self.chr, + color::Fg(color::Reset) + ) + } +} diff --git a/src/entities/mod.rs b/src/entities/mod.rs index a23b15eef34c..c4f46bf4a723 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -1,7 +1,15 @@ pub mod character; +pub mod creature; +pub mod entity_char; +pub mod raws; + +pub use character::Character; +pub use creature::Creature; +pub use entity_char::EntityChar; +pub use raws::raw; + use crate::display::Draw; use crate::types::{Positioned, PositionedMut}; -pub use character::Character; use downcast_rs::Downcast; use std::io::{self, Write}; diff --git a/src/entities/raws.rs b/src/entities/raws.rs new file mode 100644 index 000000000000..beeb90a40cea --- /dev/null +++ b/src/entities/raws.rs @@ -0,0 +1,57 @@ +use crate::entities::entity_char::EntityChar; +use crate::types::Speed; +use std::collections::HashMap; + +#[derive(Debug, Deserialize)] +pub struct CreatureType<'a> { + /// The name of the creature. Used in raw lookups. + pub name: &'a str, + + /// A description of the entity, used by the "look" command + pub description: &'a str, + + #[serde(rename = "char")] + pub chr: EntityChar, + pub max_hitpoints: u16, + pub speed: Speed, + pub friendly: bool, +} + +#[derive(Debug, Deserialize)] +pub enum EntityRaw<'a> { + Creature(#[serde(borrow)] CreatureType<'a>), +} + +impl<'a> EntityRaw<'a> { + pub fn name(&self) -> &'a str { + match self { + EntityRaw::Creature(typ) => typ.name, + } + } +} + +static_cfg! { + static ref RAWS: Vec<EntityRaw<'static>> = toml_dir("src/entities/raws"); +} + +lazy_static! { + static ref RAWS_BY_NAME: HashMap<&'static str, &'static EntityRaw<'static>> = { + let mut hm = HashMap::new(); + for er in RAWS.iter() { + if hm.contains_key(er.name()) { + panic!("Duplicate entity: {}", er.name()) + } + + hm.insert(er.name(), er); + } + hm + }; +} + +pub fn raw(name: &'static str) -> &'static EntityRaw<'static> { + debug!("{:?}", RAWS_BY_NAME.keys().collect::<Vec<&&'static str>>()); + RAWS_BY_NAME + .get(name) + .map(|e| *e) + .expect(format!("Raw not found: {}", name).as_str()) +} diff --git a/src/entities/raws/gormlak.toml b/src/entities/raws/gormlak.toml new file mode 100644 index 000000000000..be30362d25bd --- /dev/null +++ b/src/entities/raws/gormlak.toml @@ -0,0 +1,10 @@ +[Creature] +name = "gormlak" +description = """ +A chittering imp-like creature with bright yellow horns. It adores shiny objects +and gathers in swarms. +""" +char = { char = "g", color = "red" } +max_hitpoints = 5 +speed = 120 +friendly = false diff --git a/src/game.rs b/src/game.rs index 1a43628b4318..f86d32d0463c 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,11 +1,12 @@ use crate::display::{self, Viewport}; use crate::entities::Character; -use crate::entities::Entity; +use crate::entities::{Creature, Entity}; use crate::messages::message; use crate::settings::Settings; use crate::types::command::Command; use crate::types::entity_map::EntityID; use crate::types::entity_map::EntityMap; +use crate::types::pos; use crate::types::Ticks; use crate::types::{ BoundingBox, Collision, Dimensions, Position, Positioned, PositionedMut, @@ -74,6 +75,15 @@ impl<'a> Game<'a> { None => SmallRng::from_entropy(), }; let mut entities: EntityMap<AnEntity<'a>> = EntityMap::new(); + + // TODO make this dynamic + { + entities.insert(Box::new(Creature::new_from_raw( + "gormlak", + pos(10, 0), + ))); + } + Game { settings, rng, diff --git a/src/types/mod.rs b/src/types/mod.rs index ac44bcc9c89e..1e86fb369e86 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -348,7 +348,8 @@ pub struct Ticks(pub u16); pub struct Tiles(pub f32); /// The speed of an entity, expressed in ticks per tile -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Deserialize)] +#[serde(transparent)] pub struct Speed(pub u32); impl Speed { |