pub(crate) mod determiners;
pub(crate) mod directions;
pub(crate) mod movements;
pub(crate) mod prepositions;
pub(crate) mod rooms;
use crate::parser::errors::{InvalidRoom, InvalidSubject, NoItem};
use crate::NRResult;
use self::determiners::AllowedDeterminers;
use self::directions::AllowedDirections;
use self::movements::AllowedMovements;
use self::prepositions::AllowedPrepositions;
use self::rooms::{Room, RoomBlueprint};
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Narrative {
pub id: u16,
pub text: String,
pub description: String,
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Verb {
pub id: u16,
pub names: Vec<String>,
pub verb_function: VerbFunction,
}
impl std::fmt::Display for Verb {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.verb_function {
VerbFunction::Look => write!(f, "look"),
VerbFunction::Help => write!(f, "help"),
VerbFunction::Take => write!(f, "take"),
VerbFunction::Drop => write!(f, "drop"),
VerbFunction::Inventory => write!(f, "inventory"),
VerbFunction::Quit => write!(f, "quit"),
VerbFunction::Talk => write!(f, "talk"),
VerbFunction::Normal => {
write!(
f,
"{}",
self.names.first().map(String::as_str).unwrap_or("")
)
}
}
}
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum VerbFunction {
#[serde(rename = "quit")]
Quit,
#[serde(rename = "help")]
Help,
#[serde(rename = "look")]
Look,
#[serde(rename = "inventory")]
Inventory,
#[serde(rename = "take")]
Take,
#[serde(rename = "drop")]
Drop,
#[serde(rename = "talk")]
Talk,
#[serde(rename = "normal")]
Normal,
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Subject {
pub id: u16,
pub name: String,
pub description: String,
pub default_text: String,
}
impl std::fmt::Display for Subject {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &self.name[..])
}
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Event {
pub id: u16,
pub location: u16,
pub name: String,
pub description: String,
pub destination: Option<u16>,
pub narrative: Option<u16>,
pub required_verb: Option<u16>,
pub required_subject: Option<u16>,
pub required_item: Option<u16>,
pub completed: bool,
pub add_item: Option<u16>,
#[serde(default)]
pub remove_old_narrative: bool,
pub narrative_after: Option<u16>,
pub remove_item: Option<u16>,
pub required_events: Vec<u16>,
pub add_subject: Option<u16>,
#[serde(default)]
pub remove_subject: bool,
pub move_subject_to_location: Option<u16>,
}
impl Event {
pub fn is_completed(&self) -> bool {
self.completed
}
pub fn complete(&mut self) {
self.completed = true;
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub struct Item {
pub id: u16,
pub name: String,
pub description: String,
pub can_pick: bool,
}
impl std::fmt::Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &self.name[..])
}
}
#[derive(Deserialize, Debug)]
struct ConfigData {
items: Vec<Item>,
narratives: Vec<Narrative>,
room_blueprints: Vec<RoomBlueprint>,
subjects: Vec<Subject>,
events: Vec<Event>,
intro: String,
allowed_verbs: Vec<Verb>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
#[serde(rename_all = "snake_case")]
pub struct Config {
pub allowed_prepositions: AllowedPrepositions,
pub allowed_determiners: AllowedDeterminers,
pub allowed_movements: AllowedMovements,
pub allowed_directions: AllowedDirections,
pub allowed_verbs: Vec<Verb>,
pub items: Vec<Item>,
pub subjects: Vec<Subject>,
pub narratives: Vec<Narrative>,
pub events: Vec<Event>,
pub intro: String,
pub(crate) room_blueprints: Vec<RoomBlueprint>,
}
impl Default for Config {
fn default() -> Config {
Config {
allowed_verbs: Vec::new(),
allowed_prepositions: AllowedPrepositions {
prepositions: Vec::new(),
},
allowed_determiners: AllowedDeterminers {
determiners: Vec::new(),
},
allowed_movements: AllowedMovements {
movements: Vec::new(),
},
allowed_directions: AllowedDirections {
directions: Vec::new(),
},
items: Vec::new(),
subjects: Vec::new(),
narratives: Vec::new(),
room_blueprints: Vec::new(),
events: Vec::new(),
intro: String::new(),
}
}
}
impl Config {
pub fn from_json(data: &str) -> NRResult<Config> {
let config_data: ConfigData = serde_json::from_str(data)?;
let mut items = config_data.items;
let mut verbs = config_data.allowed_verbs;
let mut subjects = config_data.subjects;
let mut narratives = config_data.narratives;
let mut events = config_data.events;
let mut room_blueprints = config_data.room_blueprints;
room_blueprints.sort_by_key(|a| a.id);
events.sort_by_key(|a| a.id);
verbs.sort();
items.sort();
subjects.sort();
narratives.sort();
Ok(Config {
allowed_verbs: verbs,
allowed_prepositions: AllowedPrepositions::init(),
allowed_determiners: AllowedDeterminers::init(),
allowed_movements: AllowedMovements::init(),
allowed_directions: AllowedDirections::init(),
items,
subjects,
narratives,
events,
intro: config_data.intro,
room_blueprints,
})
}
pub fn from_path(path: &str) -> NRResult<Config> {
let read = |file: &str| -> NRResult<String> {
std::fs::read_to_string(format!("{}{}", path, file))
.map_err(|e| format!("Could not read config file {}{}: {}", path, file, e).into())
};
let narratives_config = read("narratives.yml")?;
let items_config = read("items.yml")?;
let rooms_config = read("rooms.yml")?;
let allowed_verbs_config = read("verbs.yml")?;
let subjects_config = read("subjects.yml")?;
let events_config = read("events.yml")?;
let intro_config = read("intro.yml")?;
let mut narratives: Vec<Narrative> = serde_yaml::from_str(&narratives_config)?;
narratives.sort_by_key(|a| a.id);
let mut items: Vec<Item> = serde_yaml::from_str(&items_config)?;
items.sort_by_key(|a| a.id);
let mut room_blueprints: Vec<RoomBlueprint> = serde_yaml::from_str(&rooms_config)?;
room_blueprints.sort_by_key(|a| a.id);
let mut events: Vec<Event> = serde_yaml::from_str(&events_config)?;
events.sort_by_key(|a| a.id);
let mut subjects: Vec<Subject> = serde_yaml::from_str(&subjects_config)?;
subjects.sort_by_key(|a| a.id);
let mut verbs: Vec<Verb> = serde_yaml::from_str(&allowed_verbs_config)?;
verbs.sort_by_key(|a| a.id);
let intro: String = serde_yaml::from_str(&intro_config)?;
Ok(Config {
allowed_determiners: AllowedDeterminers::init(),
allowed_prepositions: AllowedPrepositions::init(),
allowed_movements: AllowedMovements::init(),
allowed_directions: AllowedDirections::init(),
allowed_verbs: verbs,
items,
subjects,
narratives,
events,
intro,
room_blueprints,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct State {
pub input: String,
pub current_room: u16,
pub player: Player,
pub rooms: Vec<Room>,
pub config: Config,
}
impl State {
pub fn init(config: Config) -> Self {
let items = &config.items;
let subjects = &config.subjects;
let events = &config.events;
let room_blueprints = &config.room_blueprints;
let rooms = Room::build_rooms(room_blueprints, events, items, subjects);
Self {
input: String::new(),
current_room: 1,
player: Player {
inventory: Storage::default(),
},
rooms,
config,
}
}
pub fn get_narrative(&self) -> Narrative {
let room = self
.rooms
.iter()
.find(|r| r.id == self.current_room)
.expect("current room should always exist in the state");
let narrative = self
.config
.narratives
.iter()
.find(|n| n.id == room.narrative)
.expect("the current room's narrative should always exist in the config");
narrative.clone()
}
pub fn set_narrative(&mut self, narrative_id: u16) -> NRResult<()> {
let room = self
.rooms
.iter_mut()
.find(|r| r.id == self.current_room)
.ok_or(InvalidRoom)?;
room.narrative = narrative_id;
Ok(())
}
pub fn is_event_completed(&self, event_id: u16) -> bool {
for room in self.rooms.iter() {
if let Some(event) = room.events.iter().find(|e| e.id == event_id) {
return event.completed;
}
}
false
}
pub fn complete_event(&mut self, event_id: u16) {
for room in self.rooms.iter_mut() {
if let Some(event) = room.events.iter_mut().find(|e| e.id == event_id) {
event.completed = true;
}
}
}
pub fn move_subject(&mut self, subject_id: u16, location: u16) -> NRResult<()> {
self.remove_subject(subject_id)?;
let subject = self
.config
.subjects
.iter()
.find(|s| s.id == subject_id)
.ok_or(InvalidSubject)?;
self.rooms
.iter_mut()
.find(|r| r.id == location)
.ok_or(InvalidRoom)?
.add_subject(subject.clone());
Ok(())
}
pub fn remove_subject(&mut self, subject_id: u16) -> NRResult<()> {
let current_room = self
.rooms
.iter_mut()
.find(|r| r.id == self.current_room)
.ok_or(InvalidRoom)?;
current_room.remove_subject(subject_id);
Ok(())
}
pub fn add_subject(&mut self, subject: Subject) -> NRResult<()> {
let current_room = self
.rooms
.iter_mut()
.find(|r| r.id == self.current_room)
.ok_or(InvalidRoom)?;
current_room.add_subject(subject);
Ok(())
}
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize, Default)]
#[serde(rename_all = "snake_case")]
pub struct Storage {
pub items: Vec<Item>,
}
impl Storage {
pub fn add_item(&mut self, item: Item) {
self.items.push(item);
}
pub fn remove_item(&mut self, item: Item) -> NRResult<Item> {
let target_item = self.items.iter().position(|i| i.name == item.name);
match target_item {
Some(item_index) => Ok(self.items.remove(item_index)),
None => Err(NoItem.into()),
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Player {
pub inventory: Storage,
}
#[cfg(test)]
mod tests;