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.
This commit is contained in:
		
							parent
							
								
									081146da30
								
							
						
					
					
						commit
						e7ad87c730
					
				
					 9 changed files with 306 additions and 5 deletions
				
			
		
							
								
								
									
										149
									
								
								src/display/color.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/display/color.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										43
									
								
								src/entities/creature.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/entities/creature.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/entities/entity_char.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/entities/entity_char.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								src/entities/raws.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/entities/raws.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/entities/raws/gormlak.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/entities/raws/gormlak.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
							
								
								
									
										12
									
								
								src/game.rs
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue