use crate::interactive_fiction::data::ids::{DialogueId, RoomId};
use crate::interactive_fiction::data::text::Text;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum EntityLocation {
Room(RoomId),
Nowhere,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum EntityKind {
Character {
initial_disposition: i64,
},
Object,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
pub name: String,
pub synonyms: Vec<String>,
pub description: Text,
pub dialogue: Option<DialogueId>,
pub initial_location: Option<EntityLocation>,
pub tags: BTreeSet<String>,
pub kind: EntityKind,
}
impl Entity {
pub fn character(name: impl Into<String>, description: Text) -> CharacterBuilder {
CharacterBuilder(Self::new(
name,
description,
EntityKind::Character {
initial_disposition: 0,
},
))
}
pub fn object(name: impl Into<String>, description: Text) -> ObjectBuilder {
ObjectBuilder(Self::new(name, description, EntityKind::Object))
}
fn new(name: impl Into<String>, description: Text, kind: EntityKind) -> Self {
Self {
name: name.into(),
synonyms: Vec::new(),
description,
dialogue: None,
initial_location: None,
tags: BTreeSet::new(),
kind,
}
}
pub const fn is_character(&self) -> bool {
matches!(self.kind, EntityKind::Character { .. })
}
pub const fn is_object(&self) -> bool {
matches!(self.kind, EntityKind::Object)
}
}
macro_rules! common_builder_methods {
($builder:ident) => {
impl $builder {
pub fn with_synonyms(
mut self,
synonyms: impl IntoIterator<Item = &'static str>,
) -> Self {
self.0
.synonyms
.extend(synonyms.into_iter().map(String::from));
self
}
pub fn with_dialogue(mut self, dialogue: DialogueId) -> Self {
self.0.dialogue = Some(dialogue);
self
}
pub fn starting_in(mut self, room: RoomId) -> Self {
self.0.initial_location = Some(EntityLocation::Room(room));
self
}
pub fn starting_nowhere(mut self) -> Self {
self.0.initial_location = Some(EntityLocation::Nowhere);
self
}
pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
self.0.tags.insert(tag.into());
self
}
pub fn build(self) -> Entity {
self.0
}
}
impl From<$builder> for Entity {
fn from(builder: $builder) -> Entity {
builder.0
}
}
};
}
pub struct CharacterBuilder(Entity);
common_builder_methods!(CharacterBuilder);
impl CharacterBuilder {
pub fn with_disposition(mut self, disposition: i64) -> Self {
if let EntityKind::Character {
initial_disposition,
} = &mut self.0.kind
{
*initial_disposition = disposition;
}
self
}
}
pub struct ObjectBuilder(Entity);
common_builder_methods!(ObjectBuilder);