Start modelling and supporting rooms / zones.
This commit is contained in:
parent
c4f9577645
commit
d362c2f371
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -116,6 +116,7 @@ dependencies = [
|
||||
"log",
|
||||
"nix",
|
||||
"nom",
|
||||
"once_cell",
|
||||
"ouroboros",
|
||||
"phf",
|
||||
"ring",
|
||||
|
@ -33,3 +33,4 @@ chrono = { version = "0.4.23", features = ["serde"] }
|
||||
bcrypt = "0.13.0"
|
||||
validator = "0.16.0"
|
||||
itertools = "0.10.5"
|
||||
once_cell = "1.16.0"
|
||||
|
@ -34,7 +34,7 @@ impl UserVerb for Verb {
|
||||
item_type: "player".to_owned(),
|
||||
item_code: username.to_lowercase(),
|
||||
display: username.to_owned(),
|
||||
location: "room/chargen_room".to_owned(),
|
||||
location: "room/repro_xv_chargen".to_owned(),
|
||||
..Item::default()
|
||||
}).await?;
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
use crate::DResult;
|
||||
use crate::db::DBPool;
|
||||
use crate::models::item::Item;
|
||||
use log::error;
|
||||
use itertools::Itertools;
|
||||
use std::collections::{BTreeSet, BTreeMap};
|
||||
use log::info;
|
||||
|
||||
mod room;
|
||||
|
||||
pub struct StaticItem {
|
||||
pub item_code: &'static str,
|
||||
pub initial_item: fn () -> Item
|
||||
pub initial_item: Box<dyn Fn() -> Item>
|
||||
}
|
||||
|
||||
struct StaticItemTypeGroup {
|
||||
@ -26,25 +26,15 @@ fn static_item_registry() -> Vec<StaticItemTypeGroup> {
|
||||
},
|
||||
StaticItemTypeGroup {
|
||||
item_type: "room",
|
||||
items: || room::static_items()
|
||||
items: || room::room_static_items()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
async fn refresh_static_items(pool: &DBPool) -> DResult<()> {
|
||||
let mut registry = static_item_registry();
|
||||
registry.sort_unstable_by(|x, y| x.item_type.cmp(y.item_type));
|
||||
let registry = static_item_registry();
|
||||
|
||||
let duplicates: Vec<&'static str> =
|
||||
registry.iter()
|
||||
.group_by(|x| x.item_type).into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
error!("static_item_registry has duplicate item_types: {:}", duplicates.join(", "));
|
||||
Err("Duplicate item_types in static_item_registry")?;
|
||||
}
|
||||
let expected_type: BTreeSet<String> =
|
||||
registry.iter().map(|x| x.item_type.to_owned()).collect();
|
||||
let cur_types: Box<BTreeSet<String>> = pool.find_static_item_types().await?;
|
||||
@ -53,18 +43,7 @@ async fn refresh_static_items(pool: &DBPool) -> DResult<()> {
|
||||
}
|
||||
|
||||
for type_group in registry.iter() {
|
||||
let iterator : Box<dyn Iterator<Item = StaticItem>> = (type_group.items)();
|
||||
let duplicates: Vec<&'static str> = iterator
|
||||
.group_by(|x| x.item_code)
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
error!("static_item_registry has duplicate item_codes for {}: {:}",
|
||||
type_group.item_type,
|
||||
duplicates.join(", "));
|
||||
Err("Duplicate item_types in static_item_registry")?;
|
||||
}
|
||||
info!("Checking static_content of item_type {}", type_group.item_type);
|
||||
let tx = pool.start_transaction().await?;
|
||||
let existing_items = tx.find_static_items_by_type(type_group.item_type).await?;
|
||||
let expected_items: BTreeMap<String, StaticItem> =
|
||||
@ -77,6 +56,8 @@ async fn refresh_static_items(pool: &DBPool) -> DResult<()> {
|
||||
tx.create_item(&(expected_items.get(new_item_code)
|
||||
.unwrap().initial_item)()).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
info!("Committed any changes for static_content of item_type {}", type_group.item_type);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -85,3 +66,37 @@ pub async fn refresh_static_content(pool: &DBPool) -> DResult<()> {
|
||||
refresh_static_items(pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_duplicate_static_content() {
|
||||
let mut registry = static_item_registry();
|
||||
registry.sort_unstable_by(|x, y| x.item_type.cmp(y.item_type));
|
||||
|
||||
let duplicates: Vec<&'static str> =
|
||||
registry.iter()
|
||||
.group_by(|x| x.item_type).into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
panic!("static_item_registry has duplicate item_types: {:}", duplicates.join(", "));
|
||||
}
|
||||
|
||||
for type_group in registry.iter() {
|
||||
let iterator : Box<dyn Iterator<Item = StaticItem>> = (type_group.items)();
|
||||
let duplicates: Vec<&'static str> = iterator
|
||||
.group_by(|x| x.item_code)
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
panic!("static_item_registry has duplicate item_codes for {}: {:}",
|
||||
type_group.item_type,
|
||||
duplicates.join(", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,134 @@
|
||||
use super::StaticItem;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::BTreeMap;
|
||||
use ansi_macro::ansi;
|
||||
use crate::models::item::Item;
|
||||
|
||||
pub fn static_items() -> Box<dyn Iterator<Item = StaticItem>> {
|
||||
Box::new(vec!().into_iter())
|
||||
pub struct Zone {
|
||||
pub code: &'static str,
|
||||
pub display: &'static str,
|
||||
pub outdoors: bool,
|
||||
}
|
||||
|
||||
static STATIC_ZONE_DETAILS: OnceCell<BTreeMap<&'static str, Zone>> = OnceCell::new();
|
||||
pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> {
|
||||
STATIC_ZONE_DETAILS.get_or_init(
|
||||
|| vec!(
|
||||
Zone { code: "melbs",
|
||||
display: "Melbs",
|
||||
outdoors: true },
|
||||
Zone { code: "repro_xv",
|
||||
display: "Reprolabs XV",
|
||||
outdoors: true },
|
||||
).into_iter().map(|x|(x.code, x)).collect())
|
||||
}
|
||||
|
||||
pub struct GridCoords {
|
||||
x: i64,
|
||||
y: i64,
|
||||
z: i64
|
||||
}
|
||||
|
||||
pub enum ExitType {
|
||||
Free, // Anyone can just walk it.
|
||||
// Future ideas: Doors with locks, etc...
|
||||
}
|
||||
|
||||
pub enum Direction {
|
||||
NORTH,
|
||||
SOUTH,
|
||||
EAST,
|
||||
WEST,
|
||||
NORTHEAST,
|
||||
SOUTEAST,
|
||||
NORTHWEST,
|
||||
SOUTHWEST,
|
||||
UP,
|
||||
DOWN,
|
||||
IN(&'static str)
|
||||
}
|
||||
|
||||
pub enum ExitTarget {
|
||||
UseGPS,
|
||||
Custom(&'static str)
|
||||
}
|
||||
|
||||
pub struct Exit {
|
||||
direction: Direction,
|
||||
target: ExitTarget,
|
||||
exit_type: ExitType
|
||||
}
|
||||
|
||||
pub struct Room {
|
||||
pub zone: &'static str,
|
||||
pub code: &'static str,
|
||||
pub name: &'static str,
|
||||
pub short: &'static str,
|
||||
pub grid_coords: GridCoords,
|
||||
pub description: &'static str,
|
||||
pub exits: Vec<Exit>
|
||||
}
|
||||
|
||||
static STATIC_ROOM_LIST: OnceCell<Vec<Room>> = OnceCell::new();
|
||||
pub fn room_list() -> &'static Vec<Room> {
|
||||
STATIC_ROOM_LIST.get_or_init(
|
||||
|| vec!(
|
||||
Room {
|
||||
zone: "repro_xv",
|
||||
code: "repro_xv_chargen",
|
||||
name: "Choice Room",
|
||||
short: ansi!("<green>CR<reset>"),
|
||||
description: "A room brightly lit in unnaturally white light, covered in sparkling \
|
||||
white tiles from floor to \
|
||||
ceiling. A loudspeaker plays a message on loop:\r\n\
|
||||
\t\"Citizen, you are here because your memory has been wiped and you \
|
||||
are ready to start a fresh life. As a being enhanced by Gazos-Murlison \
|
||||
Co technology, the emperor has granted you the power to choose 14 points \
|
||||
of upgrades to yourself. Choose wisely, as it will impact who you end up \
|
||||
being, and you would need to completely wipe your brain again to change \
|
||||
them. Talk to Statbot to spend your 14 points and create your body.\"\r\n\
|
||||
[Try <bold>\"statbot hi<reset>, to send hi to statbot - the \" means to \
|
||||
whisper to a particular person in the room]",
|
||||
grid_coords: GridCoords { x: 0, y: 0, z: 2 },
|
||||
exits: vec!()
|
||||
},
|
||||
).into_iter().collect())
|
||||
}
|
||||
|
||||
static STATIC_ROOM_MAP_BY_CODE: OnceCell<BTreeMap<&'static str, &'static Room>> = OnceCell::new();
|
||||
pub fn room_map_by_code() -> &'static BTreeMap<&'static str, &'static Room> {
|
||||
STATIC_ROOM_MAP_BY_CODE.get_or_init(
|
||||
|| room_list().iter().map(|r| (r.code, r)).collect())
|
||||
}
|
||||
|
||||
pub fn room_static_items() -> Box<dyn Iterator<Item = StaticItem>> {
|
||||
Box::new(room_list().iter().map(|r| StaticItem {
|
||||
item_code: r.code,
|
||||
initial_item: Box::new(|| Item {
|
||||
item_code: r.code.to_owned(),
|
||||
item_type: "room".to_owned(),
|
||||
display: r.description.to_owned(),
|
||||
location: r.code.to_owned(),
|
||||
is_static: true,
|
||||
..Item::default()
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn room_zones_should_exist() {
|
||||
for room in room_list() {
|
||||
zone_details().get(room.zone).expect(
|
||||
&format!("zone {} for room {} should exist", room.zone, room.code));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn room_map_by_code_should_have_repro_xv_chargen() {
|
||||
assert_eq!(room_map_by_code().get("repro_xv_chargen").expect("repro_xv_chargen to exist").code,
|
||||
"repro_xv_chargen");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user