Add a required quest to gain access to the sewers

This commit is contained in:
Condorra 2024-01-29 00:48:41 +11:00
parent 72c48b44c5
commit d1a5a4d2fa
28 changed files with 1201 additions and 597 deletions

View File

@ -473,6 +473,7 @@ impl DBTrans {
"is_challenge_attack_only", "is_challenge_attack_only",
"aliases", "aliases",
"species", "species",
"static_special_data",
] { ] {
det_ex = format!("jsonb_set({}, '{{{}}}', ${})", det_ex, to_copy, var_id); det_ex = format!("jsonb_set({}, '{{{}}}', ${})", det_ex, to_copy, var_id);
params.push(obj_map.get(to_copy).unwrap_or(&Value::Null)); params.push(obj_map.get(to_copy).unwrap_or(&Value::Null));
@ -1975,6 +1976,20 @@ impl DBTrans {
.await?.get(0)) .await?.get(0))
} }
pub async fn has_key_matching(&self, at_item: &Item, pin_sequence: &str) -> DResult<bool> {
Ok(self
.pg_trans()?
.query_one(
"SELECT EXISTS(SELECT 1 FROM items WHERE \
details->>'location' = $1 AND \
details->'static_special_data'->'KeyData'->>'pin_sequence' = $2) \
AS found",
&[&at_item.refstr(), &pin_sequence],
)
.await?
.get("found"))
}
pub async fn commit(mut self: Self) -> DResult<()> { pub async fn commit(mut self: Self) -> DResult<()> {
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None)); let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
if let Some(trans) = trans_opt { if let Some(trans) = trans_opt {

View File

@ -66,6 +66,7 @@ pub mod rent;
mod report; mod report;
mod reset_spawns; mod reset_spawns;
pub mod say; pub mod say;
mod scan;
pub mod scavenge; pub mod scavenge;
mod score; mod score;
mod share; mod share;
@ -239,6 +240,8 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
"\'" => say::VERB, "\'" => say::VERB,
"say" => say::VERB, "say" => say::VERB,
"scan" => scan::VERB,
"scavenge" => scavenge::VERB, "scavenge" => scavenge::VERB,
"search" => scavenge::VERB, "search" => scavenge::VERB,
@ -263,7 +266,6 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
"sign" => sign::VERB, "sign" => sign::VERB,
"sit" => sit::VERB, "sit" => sit::VERB,
"stand" => stand::VERB, "stand" => stand::VERB,
"st" => status::VERB, "st" => status::VERB,

View File

@ -94,6 +94,7 @@ async fn reset_stats(ctx: &mut VerbContext<'_>) -> UResult<()> {
user_dat.raw_stats = BTreeMap::new(); user_dat.raw_stats = BTreeMap::new();
user_dat.raw_skills = BTreeMap::new(); user_dat.raw_skills = BTreeMap::new();
user_dat.wristpad_hacks = vec![]; user_dat.wristpad_hacks = vec![];
user_dat.scan_codes = vec![];
calculate_total_stats_skills_for_user(&mut player_item, &user_dat); calculate_total_stats_skills_for_user(&mut player_item, &user_dat);
ctx.trans.save_user_model(&user_dat).await?; ctx.trans.save_user_model(&user_dat).await?;
ctx.trans.save_item_model(&player_item).await?; ctx.trans.save_item_model(&player_item).await?;

View File

@ -3,7 +3,7 @@ use super::{
user_error, ItemSearchParams, UResult, UserError, UserVerb, UserVerbRef, VerbContext, user_error, ItemSearchParams, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
}; };
use crate::{ use crate::{
models::item::LocationActionType, models::item::{Item, LocationActionType},
regular_tasks::queued_command::{ regular_tasks::queued_command::{
queue_command, QueueCommand, QueueCommandHandler, QueuedCommandContext, queue_command, QueueCommand, QueueCommandHandler, QueuedCommandContext,
}, },
@ -49,12 +49,15 @@ impl QueueCommandHandler for QueueHandler {
broadcast_to_room(ctx.trans, &ctx.item.location, None, &msg).await?; broadcast_to_room(ctx.trans, &ctx.item.location, None, &msg).await?;
} }
QueueCommand::GetFromContainer { QueueCommand::GetFromContainer {
from_possession_id, from_item_id,
get_possession_id, get_possession_id,
} => { } => {
let (from_item_type, from_item_code) = from_item_id
.split_once("/")
.ok_or_else(|| UserError("Bad from_item_id for get".to_owned()))?;
let container = ctx let container = ctx
.trans .trans
.find_item_by_type_code("possession", &from_possession_id) .find_item_by_type_code(&from_item_type, &from_item_code)
.await? .await?
.ok_or_else(|| UserError("Item to get from not found".to_owned()))?; .ok_or_else(|| UserError("Item to get from not found".to_owned()))?;
if container.location != ctx.item.location if container.location != ctx.item.location
@ -125,12 +128,15 @@ impl QueueCommandHandler for QueueHandler {
(item, None) (item, None)
} }
QueueCommand::GetFromContainer { QueueCommand::GetFromContainer {
from_possession_id, from_item_id,
get_possession_id, get_possession_id,
} => { } => {
let (from_item_type, from_item_code) = from_item_id
.split_once("/")
.ok_or_else(|| UserError("Bad from_item_id for get".to_owned()))?;
let container = ctx let container = ctx
.trans .trans
.find_item_by_type_code("possession", &from_possession_id) .find_item_by_type_code(from_item_type, from_item_code)
.await? .await?
.ok_or_else(|| UserError("Item to get from not found".to_owned()))?; .ok_or_else(|| UserError("Item to get from not found".to_owned()))?;
if container.location != ctx.item.location if container.location != ctx.item.location
@ -200,6 +206,22 @@ impl QueueCommandHandler for QueueHandler {
} }
} }
fn check_get_from_allowed(from_what: &Item) -> UResult<()> {
if from_what.item_type == "player" || from_what.item_type == "npc" {
if from_what.death_data.is_none() {
user_error(format!(
"You don't think {} would let you just take like that!",
&from_what.pronouns.subject
))?;
}
return Ok(());
}
if !vec!["possession", "static_item"].contains(&from_what.item_type.as_str()) {
user_error("You can't get from that!".to_owned())?;
}
Ok(())
}
pub struct Verb; pub struct Verb;
#[async_trait] #[async_trait]
impl UserVerb for Verb { impl UserVerb for Verb {
@ -229,7 +251,6 @@ impl UserVerb for Verb {
&ItemSearchParams { &ItemSearchParams {
include_loc_contents: true, include_loc_contents: true,
include_contents: true, include_contents: true,
item_type_only: Some("possession"),
..ItemSearchParams::base(&player_item, container_str_raw.trim()) ..ItemSearchParams::base(&player_item, container_str_raw.trim())
}, },
) )
@ -255,6 +276,10 @@ impl UserVerb for Verb {
)?; )?;
} }
if include_loc_contents {
check_get_from_allowed(&search_what)?;
}
let mut did_anything: bool = false; let mut did_anything: bool = false;
let mut player_item_mut = (*player_item).clone(); let mut player_item_mut = (*player_item).clone();
for target in targets for target in targets
@ -279,7 +304,7 @@ impl UserVerb for Verb {
ctx, ctx,
&mut player_item_mut, &mut player_item_mut,
&QueueCommand::GetFromContainer { &QueueCommand::GetFromContainer {
from_possession_id: search_what.item_code.clone(), from_item_id: search_what.refstr(),
get_possession_id: target.item_code.clone(), get_possession_id: target.item_code.clone(),
}, },
) )

View File

@ -32,6 +32,7 @@ use crate::{
dynzone::{dynzone_by_type, DynzoneType, ExitTarget as DynExitTarget}, dynzone::{dynzone_by_type, DynzoneType, ExitTarget as DynExitTarget},
npc::check_for_instant_aggro, npc::check_for_instant_aggro,
room::{self, check_for_enter_action, Direction, ExitClimb, ExitType, MaterialType}, room::{self, check_for_enter_action, Direction, ExitClimb, ExitType, MaterialType},
species::species_info_map,
}, },
DResult, DResult,
}; };
@ -185,7 +186,7 @@ async fn move_to_where(
match exit.exit_type { match exit.exit_type {
ExitType::Free => {} ExitType::Free => {}
ExitType::Blocked(blocker) => { ExitType::Blocked(ref blocker) => {
if !blocker.attempt_exit(ctx, exit).await? { if !blocker.attempt_exit(ctx, exit).await? {
user_error("Stopping movement".to_owned())?; user_error("Stopping movement".to_owned())?;
} }
@ -410,6 +411,13 @@ async fn attempt_move_immediate(
check_room_access(ctx.trans, ctx.item, &room_with_door).await?; check_room_access(ctx.trans, ctx.item, &room_with_door).await?;
} }
_ => { _ => {
if !species_info_map()
.get(&ctx.item.species)
.map(|inf| inf.can_open_door)
.unwrap_or(false)
{
return Ok(false);
}
attempt_open_immediate(ctx, direction).await?; attempt_open_immediate(ctx, direction).await?;
// Players take an extra step. So tell them to come back. // Players take an extra step. So tell them to come back.
ctx.item.queue.push_front(QueueCommand::Movement { ctx.item.queue.push_front(QueueCommand::Movement {

View File

@ -257,7 +257,7 @@ impl UserVerb for Verb {
ctx, ctx,
&mut player_item_mut, &mut player_item_mut,
&QueueCommand::GetFromContainer { &QueueCommand::GetFromContainer {
from_possession_id: target.item_code.clone(), from_item_id: target.refstr(),
get_possession_id: page.item_code.clone(), get_possession_id: page.item_code.clone(),
}, },
) )

View File

@ -0,0 +1,56 @@
use crate::static_content::room::room_map_by_code;
use super::{
get_player_item_or_fail, user_error, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
};
use async_trait::async_trait;
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
async fn handle(
self: &Self,
ctx: &mut VerbContext,
_verb: &str,
_remaining: &str,
) -> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?;
let (loc_type, loc_code) = player_item
.location
.split_once("/")
.ok_or_else(|| UserError("Your location is invalid".to_owned()))?;
if loc_type != "room" {
user_error("You can't find anything to scan here.".to_owned())?;
}
let room = room_map_by_code()
.get(&loc_code)
.ok_or_else(|| UserError("Your location no longer exists!".to_owned()))?;
let allowed_scan = room
.scan_code
.as_ref()
.ok_or_else(|| UserError("You can't find anything to scan here.".to_owned()))?;
let user = ctx
.user_dat
.as_mut()
.ok_or(UserError("Please log in first".to_owned()))?;
if !user.scan_codes.contains(&allowed_scan) {
user.scan_codes.push(allowed_scan.clone());
ctx.trans.save_user_model(&user).await?;
}
ctx.trans
.queue_for_session(
&ctx.session,
Some("Your wristpad beeps indicating a successful scan.\n"),
)
.await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;

View File

@ -614,6 +614,17 @@ pub enum ItemSpecialData {
}, },
} }
// Note that ItemSpecialData and ItemStaticSpecialData are essentially
// the same except for static items. The difference is that ItemSpecialData
// is things that can change dynamically for static items, and so shouldn't
// be reset on game restart, while ItemStaticSpecialData are reset on game
// server startup to the current latest static version of the data.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub enum ItemStaticSpecialData {
KeyData { pin_sequence: String },
LockData { pin_sequence: String },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub struct DynamicEntrance { pub struct DynamicEntrance {
pub direction: Direction, pub direction: Direction,
@ -714,6 +725,7 @@ pub struct Item {
pub queue: VecDeque<QueueCommand>, pub queue: VecDeque<QueueCommand>,
pub sex: Option<Sex>, pub sex: Option<Sex>,
pub special_data: Option<ItemSpecialData>, pub special_data: Option<ItemSpecialData>,
pub static_special_data: Option<ItemStaticSpecialData>,
pub species: SpeciesType, pub species: SpeciesType,
pub tactic_use: TacticUse, pub tactic_use: TacticUse,
pub temporary_buffs: Vec<Buff>, pub temporary_buffs: Vec<Buff>,
@ -822,6 +834,7 @@ impl Default for Item {
queue: VecDeque::new(), queue: VecDeque::new(),
sex: None, sex: None,
special_data: None, special_data: None,
static_special_data: None,
species: SpeciesType::Human, species: SpeciesType::Human,
tactic_use: Default::default(), tactic_use: Default::default(),
temporary_buffs: Vec::new(), temporary_buffs: Vec::new(),

View File

@ -4,7 +4,7 @@ use super::{
}; };
#[double] #[double]
use crate::db::DBTrans; use crate::db::DBTrans;
use crate::{message_handler::ListenerSession, DResult}; use crate::{message_handler::ListenerSession, static_content::room::ScanCode, DResult};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use mockall_double::double; use mockall_double::double;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -83,6 +83,7 @@ pub struct User {
pub raw_skills: BTreeMap<SkillType, f64>, pub raw_skills: BTreeMap<SkillType, f64>,
pub raw_stats: BTreeMap<StatType, f64>, pub raw_stats: BTreeMap<StatType, f64>,
pub wristpad_hacks: Vec<WristpadHack>, pub wristpad_hacks: Vec<WristpadHack>,
pub scan_codes: Vec<ScanCode>,
pub last_skill_improve: BTreeMap<SkillType, DateTime<Utc>>, pub last_skill_improve: BTreeMap<SkillType, DateTime<Utc>>,
pub last_page_from: Option<String>, pub last_page_from: Option<String>,
pub credits: u64, pub credits: u64,
@ -215,6 +216,7 @@ impl Default for User {
raw_skills: BTreeMap::new(), raw_skills: BTreeMap::new(),
raw_stats: BTreeMap::new(), raw_stats: BTreeMap::new(),
wristpad_hacks: vec![], wristpad_hacks: vec![],
scan_codes: vec![],
last_skill_improve: BTreeMap::new(), last_skill_improve: BTreeMap::new(),
last_page_from: None, last_page_from: None,
credits: 500, credits: 500,

View File

@ -78,7 +78,7 @@ pub enum QueueCommand {
possession_id: String, possession_id: String,
}, },
GetFromContainer { GetFromContainer {
from_possession_id: String, from_item_id: String,
get_possession_id: String, get_possession_id: String,
}, },
Make { Make {

View File

@ -75,6 +75,7 @@ async fn add_per_spawn(
SpawnDistribution::SpawnPossession { what } => { SpawnDistribution::SpawnPossession { what } => {
let mut item: Item = what.clone().into(); let mut item: Item = what.clone().into();
item.location = location.refstr(); item.location = location.refstr();
item.item_code = format!("{}", trans.alloc_item_code().await?);
trans.create_item(&item).await?; trans.create_item(&item).await?;
Ok(false) Ok(false)
} }
@ -110,18 +111,34 @@ async fn add_per_spawn(
fn spawn_list() -> &'static BTreeMap<&'static str, SpawnDistribution> { fn spawn_list() -> &'static BTreeMap<&'static str, SpawnDistribution> {
static SPAWN_LIST: OnceCell<BTreeMap<&'static str, SpawnDistribution>> = OnceCell::new(); static SPAWN_LIST: OnceCell<BTreeMap<&'static str, SpawnDistribution>> = OnceCell::new();
SPAWN_LIST.get_or_init(|| { SPAWN_LIST.get_or_init(|| {
vec![( vec![
"fixed_item/melbs_king_st_spring_fed_fountain", (
SpawnDistribution::SpawnOne { "fixed_item/melbs_king_st_spring_fed_fountain",
pvec: vec![( SpawnDistribution::SpawnOne {
1.0, pvec: vec![(
Box::new(SpawnDistribution::SpawnLiquid { 1.0,
what: LiquidType::Water, Box::new(SpawnDistribution::SpawnLiquid {
how_much: 1000000, what: LiquidType::Water,
}), how_much: 1000000,
)], }),
}, )],
)] },
),
(
"fixed_item/kings_office_basin",
SpawnDistribution::SpawnAll {
subspawns: vec![
Box::new(SpawnDistribution::SpawnLiquid {
what: LiquidType::Water,
how_much: 1000000,
}),
Box::new(SpawnDistribution::SpawnPossession {
what: PossessionType::KingsOfficeKey,
}),
],
},
),
]
.into_iter() .into_iter()
.collect() .collect()
}) })

View File

@ -13,6 +13,7 @@ fn exit_to_simple_exit(exit: &Exit) -> Option<SimpleExit> {
direction: exit.direction.clone(), direction: exit.direction.clone(),
target: exit.target.clone(), target: exit.target.clone(),
exit_climb: exit.exit_climb.clone(), exit_climb: exit.exit_climb.clone(),
needs_scan: None,
}) })
} }
@ -47,6 +48,7 @@ fn room_to_simpleroom(room: &Room) -> Option<SimpleRoom<()>> {
wristpad_hack_allowed: room.wristpad_hack_allowed.clone(), wristpad_hack_allowed: room.wristpad_hack_allowed.clone(),
journal: room.journal.clone(), journal: room.journal.clone(),
scavtable: room.scavtable.clone(), scavtable: room.scavtable.clone(),
scan_code: room.scan_code.clone(),
extra: (), extra: (),
}) })
} }

View File

@ -1,8 +1,8 @@
// For things like signs that don't do much except stay where they are and carry a description. // For things like signs that don't do much except stay where they are and carry a description.
use super::{possession_type::PossessionData, StaticItem}; use super::{possession_type::PossessionData, StaticItem};
use crate::{ use crate::{
models::item::{Item, LiquidType, LocationActionType, Pronouns}, models::item::{Item, ItemStaticSpecialData, LocationActionType, Pronouns},
static_content::{possession_type::LiquidContainerData, room::computer_museum}, static_content::room::{computer_museum, melbs},
}; };
use ansi::ansi; use ansi::ansi;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -17,6 +17,7 @@ pub struct FixedItem {
pub location: String, pub location: String,
pub proper_noun: bool, pub proper_noun: bool,
pub aliases: Vec<String>, pub aliases: Vec<String>,
pub static_special_data: Option<ItemStaticSpecialData>,
pub action_type: LocationActionType, pub action_type: LocationActionType,
} }
@ -29,6 +30,7 @@ impl Default for FixedItem {
location: "unset".to_owned(), location: "unset".to_owned(),
proper_noun: true, proper_noun: true,
aliases: vec![], aliases: vec![],
static_special_data: None,
action_type: LocationActionType::Normal, action_type: LocationActionType::Normal,
} }
} }
@ -58,42 +60,21 @@ fn fixed_item_list() -> &'static Vec<FixedItem> {
aliases: vec!["poster".to_owned()], aliases: vec!["poster".to_owned()],
..Default::default() ..Default::default()
}, },
FixedItem { ].into_iter()
code: "melbs_king_st_spring_fed_fountain".to_owned(), .chain(computer_museum::fixed_items().into_iter())
name: "spring fed fountain".to_owned(), .chain(melbs::fixed_items().into_iter())
description: ansi!("A stainless steel fountain, clearly old, but in surprisingly good \ .collect()
condition. A discoloured bronze plaque attached to it proudly declares \
that it is fed by a natural spring underneath it. It was designed so that \
unused water runs off it into a dog bowl - presumably in a time long past when \
dogs were friendly companions and not the menace they are today. It smells \
faintly of iron. [Try <bold>drink from fountain<reset> or, if you have a suitable \
container, <bold>fill<reset> container <bold>from fountain<reset>].").to_owned(),
location: "room/melbs_kingst_40".to_owned(),
proper_noun: false,
aliases: vec!["fountain".to_owned()],
..Default::default()
},
].into_iter().chain(computer_museum::fixed_items().into_iter()).collect()
}) })
} }
pub fn fixed_item_properties() -> &'static BTreeMap<&'static str, PossessionData> { pub fn fixed_item_properties() -> &'static BTreeMap<&'static str, PossessionData> {
static PROPS: OnceCell<BTreeMap<&'static str, PossessionData>> = OnceCell::new(); static PROPS: OnceCell<BTreeMap<&'static str, PossessionData>> = OnceCell::new();
PROPS.get_or_init(|| { PROPS.get_or_init(|| {
vec![( vec![]
"melbs_king_st_spring_fed_fountain", .into_iter()
PossessionData { .chain(computer_museum::fixed_item_properties().into_iter())
liquid_container_data: Some(LiquidContainerData { .chain(melbs::fixed_item_properties().into_iter())
capacity: 5000000, // mL .collect()
allowed_contents: Some(vec![LiquidType::Water]),
..Default::default()
}),
..Default::default()
},
)]
.into_iter()
.chain(computer_museum::fixed_item_properties().into_iter())
.collect()
}) })
} }
@ -113,6 +94,7 @@ pub fn static_items() -> Box<dyn Iterator<Item = StaticItem>> {
..Pronouns::default_inanimate() ..Pronouns::default_inanimate()
}, },
action_type: r.action_type.clone(), action_type: r.action_type.clone(),
static_special_data: r.static_special_data.clone(),
..Item::default() ..Item::default()
}), }),
extra_items_on_create: Box::new(|_| Box::new(std::iter::empty())), extra_items_on_create: Box::new(|_| Box::new(std::iter::empty())),

View File

@ -35,8 +35,7 @@ use std::time;
use uuid::Uuid; use uuid::Uuid;
pub mod computer_museum_npcs; pub mod computer_museum_npcs;
mod melbs_citizen; mod melbs_npcs;
mod melbs_dog;
mod roboporter; mod roboporter;
mod sewer_npcs; mod sewer_npcs;
pub mod statbot; pub mod statbot;
@ -189,8 +188,7 @@ pub fn npc_list() -> &'static Vec<NPC> {
says: vec![], says: vec![],
..Default::default() ..Default::default()
}]; }];
npcs.append(&mut melbs_citizen::npc_list()); npcs.append(&mut melbs_npcs::npc_list());
npcs.append(&mut melbs_dog::npc_list());
npcs.append(&mut roboporter::npc_list()); npcs.append(&mut roboporter::npc_list());
npcs.append(&mut computer_museum_npcs::npc_list()); npcs.append(&mut computer_museum_npcs::npc_list());
npcs.append(&mut sewer_npcs::npc_list()); npcs.append(&mut sewer_npcs::npc_list());

View File

@ -1,63 +0,0 @@
use super::{NPCPronounType, NPCSayInfo, NPCSayType, NPCSpawnPossession, NPC};
use crate::{
models::{consent::ConsentType, item::LocationActionType},
static_content::{npc::npc_pronoun_type_to_pronouns, possession_type::PossessionType},
};
use serde::Deserialize;
use serde_yaml::from_str as from_yaml_str;
#[derive(Deserialize)]
struct Citizen {
code: String,
name: String,
spawn_loc: String,
gender: NPCPronounType,
}
pub fn npc_list() -> Vec<NPC> {
use NPCSayType::FromFixedList;
let melbs_citizen_stdsay = NPCSayInfo {
say_code: "babble",
frequency_secs: 120,
talk_type: FromFixedList(vec![
"I'm so sick of being cloned.",
"I hope I don't die again today.",
"I wish the so-called king would do something about the damned zombies everywhere.",
"I heard in the olden days before the empire babies grew up naturally instead of being taken away to the body factory.",
"I know people hated the empire, but I kind of wish it was still intact - it was a lot better than what we have now.",
"I wish there wasn't so much radiation outside of Melbs CBD.",
"I heard about a guy who went to a special place somewhere around here, and there was a machine that enhanced his wristpad and gave him basically superpowers.",
"The damn vampire movement... they are all so sneaky, and I never know when they are going to come for my blood.",
])
};
from_yaml_str::<Vec<Citizen>>(include_str!("melbs_citizen.yaml"))
.unwrap()
.into_iter()
.map(|c|
NPC {
code: format!("melbs_citizen_{}", &c.code),
name: c.name,
pronouns: npc_pronoun_type_to_pronouns(&c.gender),
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harsh reality of post-apocalyptic life".to_owned(),
spawn_location: format!("room/melbs_{}", &c.spawn_loc),
spawn_possessions: vec![
NPCSpawnPossession {
what: PossessionType::Shirt,
action_type: LocationActionType::Worn,
wear_layer: 0,
},
NPCSpawnPossession {
what: PossessionType::Jeans,
action_type: LocationActionType::Worn,
wear_layer: 0,
},
],
message_handler: None,
wander_zones: vec!("melbs".to_owned()),
says: vec!(melbs_citizen_stdsay.clone()),
player_consents: vec!(ConsentType::Medicine, ConsentType::Share),
..Default::default()
}
).collect()
}

View File

@ -1,240 +0,0 @@
- code: "1"
name: Matthew Thomas
spawn_loc: kingst_latrobest
gender: Male
- code: "2"
name: Matthew Perez
spawn_loc: kingst_20
gender: Male
- code: "3"
name: Kimberly Jackson
spawn_loc: kingst_40
gender: Female
- code: "4"
name: Michael Sanchez
spawn_loc: kingst_50
gender: Male
- code: "5"
name: Jessica Davis
spawn_loc: kingst_bourkest
gender: Female
- code: "6"
name: Robert Davis
spawn_loc: kingst_70
gender: Male
- code: "7"
name: Paul Lewis
spawn_loc: kingst_90
gender: Male
- code: "8"
name: Andrew Moore
spawn_loc: kingst_collinsst
gender: Male
- code: "9"
name: Betty Thomas
spawn_loc: kingst_100
gender: Female
- code: "10"
name: Mary Robinson
spawn_loc: kingst_110
gender: Female
- code: "11"
name: Lisa Lopez
spawn_loc: kingst_flinderst
gender: Female
- code: "12"
name: Kimberly Martinez
spawn_loc: flindersst_200
gender: Female
- code: "13"
name: Anthony Nguyen
spawn_loc: flindersst_190
gender: Male
- code: "14"
name: Joshua Green
spawn_loc: flindersst_180
gender: Male
- code: "15"
name: Emily Wright
spawn_loc: flindersst_170
gender: Female
- code: "16"
name: Ashley Thomas
spawn_loc: lonsdalest_130
gender: Male
- code: "17"
name: Jessica Miller
spawn_loc: kingst_80
gender: Female
- code: "18"
name: Anthony Lopez
spawn_loc: lonsdalest_140
gender: Male
- code: "19"
name: John Lopez
spawn_loc: elizabethst_lonsdalest
gender: Male
- code: "20"
name: Thomas Garcia
spawn_loc: williamsst_120
gender: Male
- code: "21"
name: Donna Thompson
spawn_loc: elizabethst_60
gender: Female
- code: "22"
name: Matthew Davis
spawn_loc: williamsst_100
gender: Male
- code: "23"
name: Steven Jones
spawn_loc: swanstonst_120
gender: Male
- code: "24"
name: Linda Smith
spawn_loc: swanstonst_lonsdalest
gender: Male
- code: "25"
name: Karen Rodriguez
spawn_loc: bourkest_180
gender: Female
- code: "26"
name: Paul Scott
spawn_loc: swanstonst_70
gender: Male
- code: "27"
name: Ashley Thomas
spawn_loc: lonsdalest_130
gender: Male
- code: "28"
name: Sandra Scott
spawn_loc: elizabethst_30
gender: Female
- code: "29"
name: Michael Rodriguez
spawn_loc: swanstonst_70
gender: Male
- code: "30"
name: Donald Miller
spawn_loc: elizabethst_30
gender: Male
- code: "31"
name: Charles Moore
spawn_loc: lonsdalest_160
gender: Male
- code: "32"
name: Ashley Sanchez
spawn_loc: kingst_100
gender: Male
- code: "33"
name: Margaret Lewis
spawn_loc: flindersst_180
gender: Female
- code: "34"
name: Sandra Thompson
spawn_loc: swanstonst_80
gender: Female
- code: "35"
name: Sandra King
spawn_loc: lonsdalest_150
gender: Female
- code: "36"
name: Lisa Anderson
spawn_loc: lonsdalest_210
gender: Female
- code: "37"
name: Kimberly Martin
spawn_loc: kingst_80
gender: Female
- code: "38"
name: Susan Smith
spawn_loc: latrobest_190
gender: Female
- code: "39"
name: Susan Martin
spawn_loc: collinsst_150
gender: Female
- code: "40"
name: Linda Scott
spawn_loc: williamsst_30
gender: Female
- code: "41"
name: Donald Miller
spawn_loc: elizabethst_80
gender: Male
- code: "42"
name: Mark Hill
spawn_loc: collinsst_120
gender: Male
- code: "43"
name: William Perez
spawn_loc: queenst_90
gender: Male
- code: "44"
name: Donald Perez
spawn_loc: queenst_lonsdalest
gender: Male
- code: "45"
name: Lisa Rodriguez
spawn_loc: collinsst_100
gender: Female
- code: "46"
name: James Adams
spawn_loc: latrobest_150
gender: Male
- code: "47"
name: James Moore
spawn_loc: latrobest_130
gender: Male
- code: "48"
name: Joseph Martin
spawn_loc: bourkest_150
gender: Male
- code: "49"
name: Matthew Jones
spawn_loc: kingst_60
gender: Male
- code: "50"
name: Michael Sanchez
spawn_loc: queenst_100
gender: Male
- code: "51"
name: Donna Torres
spawn_loc: flindersst_150
gender: Female
- code: "52"
name: Barbara Garcia
spawn_loc: swanstonst_50
gender: Female
- code: "53"
name: Daniel Miller
spawn_loc: bourkest_110
gender: Male
- code: "54"
name: Robert Young
spawn_loc: kingst_collinsst
gender: Male
- code: "55"
name: Donald Flores
spawn_loc: swanstonst_40
gender: Male
- code: "56"
name: Charles Thomas
spawn_loc: flindersst_110
gender: Male
- code: "57"
name: William Torres
spawn_loc: swanstonst_60
gender: Male
- code: "58"
name: Barbara Gonzalez
spawn_loc: collinsst_190
gender: Female
- code: "59"
name: Mary Smith
spawn_loc: bourkest_180
gender: Female
- code: "60"
name: Michael John
spawn_loc: williamsst_110
gender: Male

View File

@ -1,38 +0,0 @@
use super::{KillBonus, NPC};
use crate::models::{consent::ConsentType, item::Pronouns};
use crate::static_content::{possession_type::PossessionType, species::SpeciesType};
use serde::Deserialize;
use serde_yaml::from_str as from_yaml_str;
#[derive(Deserialize)]
struct MelbsDog {
code: String,
adjectives: String,
spawn_room: String,
}
pub fn npc_list() -> Vec<NPC> {
from_yaml_str::<Vec<MelbsDog>>(include_str!("melbs_dog.yaml"))
.unwrap()
.into_iter()
.map(|d|
NPC {
code: format!("melbs_dog_{}", &d.code),
name: format!("{} dog", &d.adjectives),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
aggression: 12,
wander_zones: vec!("melbs".to_owned()),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.".to_owned(),
aliases: vec!("dog".to_owned()),
spawn_location: format!("room/{}", d.spawn_room),
intrinsic_weapon: Some(PossessionType::Fangs),
species: SpeciesType::Dog,
kill_bonus: Some(KillBonus {
msg: "On your wristpad: Thank you for helping Melbs with animal control! Here's your fee.",
payment: 100,
}),
player_consents: vec!(ConsentType::Fight),
..Default::default()
}
).collect()
}

View File

@ -1,180 +0,0 @@
- code: "1"
adjectives: smelly black
spawn_room: melbs_williamsst_80
- code: "2"
adjectives: howling black
spawn_room: melbs_swanstonst_100
- code: "3"
adjectives: smelly black
spawn_room: melbs_collinsst_160
- code: "4"
adjectives: growling light brown
spawn_room: melbs_kingst_40
- code: "5"
adjectives: ferocious white
spawn_room: melbs_swanstonst_110
- code: "6"
adjectives: mangy grey
spawn_room: melbs_kingst_30
- code: "7"
adjectives: reeking light brown
spawn_room: melbs_flindersst_210
- code: "8"
adjectives: feral brown
spawn_room: melbs_elizabethst_40
- code: "9"
adjectives: reeking grey
spawn_room: melbs_collinsst_190
- code: "10"
adjectives: ferocious grey
spawn_room: melbs_kingst_60
- code: "11"
adjectives: howling brown
spawn_room: melbs_collinsst_140
- code: "12"
adjectives: feral black
spawn_room: melbs_flindersst_160
- code: "13"
adjectives: smelly grey
spawn_room: melbs_queenst_80
- code: "14"
adjectives: howling grey
spawn_room: melbs_kingst_70
- code: "15"
adjectives: smelly grey
spawn_room: melbs_flindersst_110
- code: "16"
adjectives: feral black
spawn_room: melbs_queenst_latrobest
- code: "17"
adjectives: howling grey
spawn_room: melbs_swanstonst_110
- code: "18"
adjectives: mangy grey
spawn_room: melbs_swanstonst_80
- code: "19"
adjectives: reeking light brown
spawn_room: melbs_latrobest_180
- code: "20"
adjectives: smelly white
spawn_room: melbs_flindersst_130
- code: "21"
adjectives: reeking grey
spawn_room: melbs_flindersst_180
- code: "22"
adjectives: growling brown
spawn_room: melbs_williamsst_80
- code: "23"
adjectives: howling black
spawn_room: melbs_lonsdalest_100
- code: "24"
adjectives: growling grey
spawn_room: melbs_latrobest_140
- code: "25"
adjectives: howling light brown
spawn_room: melbs_queenst_30
- code: "26"
adjectives: howling black
spawn_room: melbs_latrobest_160
- code: "27"
adjectives: howling grey
spawn_room: melbs_collinsst_170
- code: "28"
adjectives: growling brown
spawn_room: melbs_elizabethst_latrobest
- code: "29"
adjectives: mangy brown
spawn_room: melbs_kingst_70
- code: "30"
adjectives: growling black
spawn_room: melbs_swanstonst_120
- code: "31"
adjectives: reeking light brown
spawn_room: melbs_latrobest_130
- code: "32"
adjectives: howling white
spawn_room: melbs_bourkest_160
- code: "33"
adjectives: growling black
spawn_room: melbs_elizabethst_50
- code: "34"
adjectives: mangy black
spawn_room: melbs_swanstonst_110
- code: "35"
adjectives: ferocious grey
spawn_room: melbs_collinsst_100
- code: "36"
adjectives: mangy grey
spawn_room: melbs_flindersst_100
- code: "37"
adjectives: growling brown
spawn_room: melbs_swanstonst_flindersst
- code: "38"
adjectives: mangy light brown
spawn_room: melbs_lonsdalest_200
- code: "39"
adjectives: howling light brown
spawn_room: melbs_flindersst_210
- code: "40"
adjectives: mangy light brown
spawn_room: melbs_queenst_flindersst
- code: "41"
adjectives: reeking white
spawn_room: melbs_collinsst_130
- code: "42"
adjectives: growling light brown
spawn_room: melbs_lonsdalest_130
- code: "43"
adjectives: reeking light brown
spawn_room: melbs_elizabethst_70
- code: "44"
adjectives: mangy brown
spawn_room: melbs_swanstonst_30
- code: "45"
adjectives: growling light brown
spawn_room: melbs_swanstonst_lonsdalest
- code: "46"
adjectives: smelly brown
spawn_room: melbs_queenst_lonsdalest
- code: "47"
adjectives: growling white
spawn_room: melbs_elizabethst_bourkest
- code: "48"
adjectives: feral brown
spawn_room: melbs_collinsst_140
- code: "49"
adjectives: ferocious black
spawn_room: melbs_lonsdalest_150
- code: "50"
adjectives: mangy grey
spawn_room: melbs_kingst_collinsst
- code: "51"
adjectives: ferocious brown
spawn_room: melbs_kingst_120
- code: "52"
adjectives: growling white
spawn_room: melbs_elizabethst_10
- code: "53"
adjectives: ferocious white
spawn_room: melbs_lonsdalest_190
- code: "54"
adjectives: smelly grey
spawn_room: melbs_kingst_collinsst
- code: "55"
adjectives: reeking light brown
spawn_room: melbs_elizabethst_90
- code: "56"
adjectives: reeking grey
spawn_room: melbs_swanstonst_20
- code: "57"
adjectives: feral brown
spawn_room: melbs_flindersst_180
- code: "58"
adjectives: reeking brown
spawn_room: melbs_bourkest_130
- code: "59"
adjectives: mangy light brown
spawn_room: melbs_queenst_50
- code: "60"
adjectives: growling white
spawn_room: melbs_kingst_110

View File

@ -0,0 +1,138 @@
use super::{NPCPronounType, NPCSayInfo, NPCSayType, NPCSpawnPossession, NPC};
use crate::{
models::{
consent::ConsentType,
item::{LocationActionType, Pronouns, SkillType},
},
static_content::{
npc::{npc_pronoun_type_to_pronouns, KillBonus},
possession_type::PossessionType,
species::SpeciesType,
},
};
use serde::Deserialize;
use serde_yaml::from_str as from_yaml_str;
#[derive(Deserialize)]
enum MelbsNPC {
Citizen {
code: String,
name: String,
spawn_loc: String,
gender: NPCPronounType,
},
Dog {
code: String,
adjectives: String,
spawn_room: String,
},
MutantRat {
code: String,
adjectives: String,
spawn_room: String,
},
}
pub fn npc_list() -> Vec<NPC> {
use NPCSayType::FromFixedList;
let melbs_citizen_stdsay = NPCSayInfo {
say_code: "babble",
frequency_secs: 120,
talk_type: FromFixedList(vec![
"I'm so sick of being cloned.",
"I hope I don't die again today.",
"I wish the so-called king would do something about the damned zombies everywhere.",
"I heard in the olden days before the empire babies grew up naturally instead of being taken away to the body factory.",
"I know people hated the empire, but I kind of wish it was still intact - it was a lot better than what we have now.",
"I wish there wasn't so much radiation outside of Melbs CBD.",
"I heard about a guy who went to a special place somewhere around here, and there was a machine that enhanced his wristpad and gave him basically superpowers.",
"The damn vampire movement... they are all so sneaky, and I never know when they are going to come for my blood.",
])
};
from_yaml_str::<Vec<MelbsNPC>>(include_str!("melbs_npcs.yaml"))
.unwrap()
.into_iter()
.map(|c|
match c {
MelbsNPC::Citizen {
code, name, gender, spawn_loc
} =>
NPC {
code: format!("melbs_citizen_{}", &code),
name,
pronouns: npc_pronoun_type_to_pronouns(&gender),
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harsh reality of post-apocalyptic life".to_owned(),
spawn_location: format!("room/melbs_{}", &spawn_loc),
spawn_possessions: vec![
NPCSpawnPossession {
what: PossessionType::Shirt,
action_type: LocationActionType::Worn,
wear_layer: 0,
},
NPCSpawnPossession {
what: PossessionType::Jeans,
action_type: LocationActionType::Worn,
wear_layer: 0,
},
],
message_handler: None,
wander_zones: vec!("melbs".to_owned()),
says: vec!(melbs_citizen_stdsay.clone()),
player_consents: vec!(ConsentType::Medicine, ConsentType::Share),
..Default::default()
},
MelbsNPC::Dog { code, adjectives, spawn_room } =>
NPC {
code: format!("melbs_dog_{}", &code),
name: format!("{} dog", &adjectives),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
aggression: 12,
wander_zones: vec!("melbs".to_owned()),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.".to_owned(),
aliases: vec!("dog".to_owned()),
spawn_location: format!("room/{}", spawn_room),
intrinsic_weapon: Some(PossessionType::Fangs),
species: SpeciesType::Dog,
kill_bonus: Some(KillBonus {
msg: "On your wristpad: Thank you for helping Melbs with animal control! Here's your fee.",
payment: 100,
}),
player_consents: vec!(ConsentType::Fight),
..Default::default()
},
MelbsNPC::MutantRat { code, adjectives, spawn_room } =>
NPC {
code: format!("melbs_rat_{}", &code),
name: format!("{} mutant rat", &adjectives),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
aggression: 21,
wander_zones: vec!("kings_office".to_owned()),
description: "Something that resembles a brown rat - except it is about 2 metres tall! Huge teeth poke through its lips, and its feet betray brutal looking claws. Gigantic whiskers poke from its face".to_owned(),
aliases: vec!("rat".to_owned()),
spawn_location: format!("room/{}", spawn_room),
intrinsic_weapon: Some(PossessionType::Fangs),
species: SpeciesType::Rat,
aggro_pc_only: true,
kill_bonus: Some(KillBonus {
msg: "On your wristpad: Thank you for helping Melbs with animal control! Here's your fee.",
payment: 150,
}),
total_skills: SkillType::values()
.into_iter()
.map(|sk| {
(
sk.clone(),
match sk {
SkillType::Dodge => 15.0,
SkillType::Fists => 15.0,
_ => 8.0
}
)
}).collect(),
player_consents: vec!(ConsentType::Fight),
..Default::default()
},
}
).collect()
}

View File

@ -0,0 +1,548 @@
- !Citizen
code: "1"
name: Matthew Thomas
spawn_loc: kingst_latrobest
gender: Male
- !Citizen
code: "2"
name: Matthew Perez
spawn_loc: kingst_20
gender: Male
- !Citizen
code: "3"
name: Kimberly Jackson
spawn_loc: kingst_40
gender: Female
- !Citizen
code: "4"
name: Michael Sanchez
spawn_loc: kingst_50
gender: Male
- !Citizen
code: "5"
name: Jessica Davis
spawn_loc: kingst_bourkest
gender: Female
- !Citizen
code: "6"
name: Robert Davis
spawn_loc: kingst_70
gender: Male
- !Citizen
code: "7"
name: Paul Lewis
spawn_loc: kingst_90
gender: Male
- !Citizen
code: "8"
name: Andrew Moore
spawn_loc: kingst_collinsst
gender: Male
- !Citizen
code: "9"
name: Betty Thomas
spawn_loc: kingst_100
gender: Female
- !Citizen
code: "10"
name: Mary Robinson
spawn_loc: kingst_110
gender: Female
- !Citizen
code: "11"
name: Lisa Lopez
spawn_loc: kingst_flinderst
gender: Female
- !Citizen
code: "12"
name: Kimberly Martinez
spawn_loc: flindersst_200
gender: Female
- !Citizen
code: "13"
name: Anthony Nguyen
spawn_loc: flindersst_190
gender: Male
- !Citizen
code: "14"
name: Joshua Green
spawn_loc: flindersst_180
gender: Male
- !Citizen
code: "15"
name: Emily Wright
spawn_loc: flindersst_170
gender: Female
- !Citizen
code: "16"
name: Ashley Thomas
spawn_loc: lonsdalest_130
gender: Male
- !Citizen
code: "17"
name: Jessica Miller
spawn_loc: kingst_80
gender: Female
- !Citizen
code: "18"
name: Anthony Lopez
spawn_loc: lonsdalest_140
gender: Male
- !Citizen
code: "19"
name: John Lopez
spawn_loc: elizabethst_lonsdalest
gender: Male
- !Citizen
code: "20"
name: Thomas Garcia
spawn_loc: williamsst_120
gender: Male
- !Citizen
code: "21"
name: Donna Thompson
spawn_loc: elizabethst_60
gender: Female
- !Citizen
code: "22"
name: Matthew Davis
spawn_loc: williamsst_100
gender: Male
- !Citizen
code: "23"
name: Steven Jones
spawn_loc: swanstonst_120
gender: Male
- !Citizen
code: "24"
name: Linda Smith
spawn_loc: swanstonst_lonsdalest
gender: Male
- !Citizen
code: "25"
name: Karen Rodriguez
spawn_loc: bourkest_180
gender: Female
- !Citizen
code: "26"
name: Paul Scott
spawn_loc: swanstonst_70
gender: Male
- !Citizen
code: "27"
name: Ashley Thomas
spawn_loc: lonsdalest_130
gender: Male
- !Citizen
code: "28"
name: Sandra Scott
spawn_loc: elizabethst_30
gender: Female
- !Citizen
code: "29"
name: Michael Rodriguez
spawn_loc: swanstonst_70
gender: Male
- !Citizen
code: "30"
name: Donald Miller
spawn_loc: elizabethst_30
gender: Male
- !Citizen
code: "31"
name: Charles Moore
spawn_loc: lonsdalest_160
gender: Male
- !Citizen
code: "32"
name: Ashley Sanchez
spawn_loc: kingst_100
gender: Male
- !Citizen
code: "33"
name: Margaret Lewis
spawn_loc: flindersst_180
gender: Female
- !Citizen
code: "34"
name: Sandra Thompson
spawn_loc: swanstonst_80
gender: Female
- !Citizen
code: "35"
name: Sandra King
spawn_loc: lonsdalest_150
gender: Female
- !Citizen
code: "36"
name: Lisa Anderson
spawn_loc: lonsdalest_210
gender: Female
- !Citizen
code: "37"
name: Kimberly Martin
spawn_loc: kingst_80
gender: Female
- !Citizen
code: "38"
name: Susan Smith
spawn_loc: latrobest_190
gender: Female
- !Citizen
code: "39"
name: Susan Martin
spawn_loc: collinsst_150
gender: Female
- !Citizen
code: "40"
name: Linda Scott
spawn_loc: williamsst_30
gender: Female
- !Citizen
code: "41"
name: Donald Miller
spawn_loc: elizabethst_80
gender: Male
- !Citizen
code: "42"
name: Mark Hill
spawn_loc: collinsst_120
gender: Male
- !Citizen
code: "43"
name: William Perez
spawn_loc: queenst_90
gender: Male
- !Citizen
code: "44"
name: Donald Perez
spawn_loc: queenst_lonsdalest
gender: Male
- !Citizen
code: "45"
name: Lisa Rodriguez
spawn_loc: collinsst_100
gender: Female
- !Citizen
code: "46"
name: James Adams
spawn_loc: latrobest_150
gender: Male
- !Citizen
code: "47"
name: James Moore
spawn_loc: latrobest_130
gender: Male
- !Citizen
code: "48"
name: Joseph Martin
spawn_loc: bourkest_150
gender: Male
- !Citizen
code: "49"
name: Matthew Jones
spawn_loc: kingst_60
gender: Male
- !Citizen
code: "50"
name: Michael Sanchez
spawn_loc: queenst_100
gender: Male
- !Citizen
code: "51"
name: Donna Torres
spawn_loc: flindersst_150
gender: Female
- !Citizen
code: "52"
name: Barbara Garcia
spawn_loc: swanstonst_50
gender: Female
- !Citizen
code: "53"
name: Daniel Miller
spawn_loc: bourkest_110
gender: Male
- !Citizen
code: "54"
name: Robert Young
spawn_loc: kingst_collinsst
gender: Male
- !Citizen
code: "55"
name: Donald Flores
spawn_loc: swanstonst_40
gender: Male
- !Citizen
code: "56"
name: Charles Thomas
spawn_loc: flindersst_110
gender: Male
- !Citizen
code: "57"
name: William Torres
spawn_loc: swanstonst_60
gender: Male
- !Citizen
code: "58"
name: Barbara Gonzalez
spawn_loc: collinsst_190
gender: Female
- !Citizen
code: "59"
name: Mary Smith
spawn_loc: bourkest_180
gender: Female
- !Citizen
code: "60"
name: Michael John
spawn_loc: williamsst_110
gender: Male
- !Dog
code: "1"
adjectives: smelly black
spawn_room: melbs_williamsst_80
- !Dog
code: "2"
adjectives: howling black
spawn_room: melbs_swanstonst_100
- !Dog
code: "3"
adjectives: smelly black
spawn_room: melbs_collinsst_160
- !Dog
code: "4"
adjectives: growling light brown
spawn_room: melbs_kingst_40
- !Dog
code: "5"
adjectives: ferocious white
spawn_room: melbs_swanstonst_110
- !Dog
code: "6"
adjectives: mangy grey
spawn_room: melbs_kingst_30
- !Dog
code: "7"
adjectives: reeking light brown
spawn_room: melbs_flindersst_210
- !Dog
code: "8"
adjectives: feral brown
spawn_room: melbs_elizabethst_40
- !Dog
code: "9"
adjectives: reeking grey
spawn_room: melbs_collinsst_190
- !Dog
code: "10"
adjectives: ferocious grey
spawn_room: melbs_kingst_60
- !Dog
code: "11"
adjectives: howling brown
spawn_room: melbs_collinsst_140
- !Dog
code: "12"
adjectives: feral black
spawn_room: melbs_flindersst_160
- !Dog
code: "13"
adjectives: smelly grey
spawn_room: melbs_queenst_80
- !Dog
code: "14"
adjectives: howling grey
spawn_room: melbs_kingst_70
- !Dog
code: "15"
adjectives: smelly grey
spawn_room: melbs_flindersst_110
- !Dog
code: "16"
adjectives: feral black
spawn_room: melbs_queenst_latrobest
- !Dog
code: "17"
adjectives: howling grey
spawn_room: melbs_swanstonst_110
- !Dog
code: "18"
adjectives: mangy grey
spawn_room: melbs_swanstonst_80
- !Dog
code: "19"
adjectives: reeking light brown
spawn_room: melbs_latrobest_180
- !Dog
code: "20"
adjectives: smelly white
spawn_room: melbs_flindersst_130
- !Dog
code: "21"
adjectives: reeking grey
spawn_room: melbs_flindersst_180
- !Dog
code: "22"
adjectives: growling brown
spawn_room: melbs_williamsst_80
- !Dog
code: "23"
adjectives: howling black
spawn_room: melbs_lonsdalest_100
- !Dog
code: "24"
adjectives: growling grey
spawn_room: melbs_latrobest_140
- !Dog
code: "25"
adjectives: howling light brown
spawn_room: melbs_queenst_30
- !Dog
code: "26"
adjectives: howling black
spawn_room: melbs_latrobest_160
- !Dog
code: "27"
adjectives: howling grey
spawn_room: melbs_collinsst_170
- !Dog
code: "28"
adjectives: growling brown
spawn_room: melbs_elizabethst_latrobest
- !Dog
code: "29"
adjectives: mangy brown
spawn_room: melbs_kingst_70
- !Dog
code: "30"
adjectives: growling black
spawn_room: melbs_swanstonst_120
- !Dog
code: "31"
adjectives: reeking light brown
spawn_room: melbs_latrobest_130
- !Dog
code: "32"
adjectives: howling white
spawn_room: melbs_bourkest_160
- !Dog
code: "33"
adjectives: growling black
spawn_room: melbs_elizabethst_50
- !Dog
code: "34"
adjectives: mangy black
spawn_room: melbs_swanstonst_110
- !Dog
code: "35"
adjectives: ferocious grey
spawn_room: melbs_collinsst_100
- !Dog
code: "36"
adjectives: mangy grey
spawn_room: melbs_flindersst_100
- !Dog
code: "37"
adjectives: growling brown
spawn_room: melbs_swanstonst_flindersst
- !Dog
code: "38"
adjectives: mangy light brown
spawn_room: melbs_lonsdalest_200
- !Dog
code: "39"
adjectives: howling light brown
spawn_room: melbs_flindersst_210
- !Dog
code: "40"
adjectives: mangy light brown
spawn_room: melbs_queenst_flindersst
- !Dog
code: "41"
adjectives: reeking white
spawn_room: melbs_collinsst_130
- !Dog
code: "42"
adjectives: growling light brown
spawn_room: melbs_lonsdalest_130
- !Dog
code: "43"
adjectives: reeking light brown
spawn_room: melbs_elizabethst_70
- !Dog
code: "44"
adjectives: mangy brown
spawn_room: melbs_swanstonst_30
- !Dog
code: "45"
adjectives: growling light brown
spawn_room: melbs_swanstonst_lonsdalest
- !Dog
code: "46"
adjectives: smelly brown
spawn_room: melbs_queenst_lonsdalest
- !Dog
code: "47"
adjectives: growling white
spawn_room: melbs_elizabethst_bourkest
- !Dog
code: "48"
adjectives: feral brown
spawn_room: melbs_collinsst_140
- !Dog
code: "49"
adjectives: ferocious black
spawn_room: melbs_lonsdalest_150
- !Dog
code: "50"
adjectives: mangy grey
spawn_room: melbs_kingst_collinsst
- !Dog
code: "51"
adjectives: ferocious brown
spawn_room: melbs_kingst_120
- !Dog
code: "52"
adjectives: growling white
spawn_room: melbs_elizabethst_10
- !Dog
code: "53"
adjectives: ferocious white
spawn_room: melbs_lonsdalest_190
- !Dog
code: "54"
adjectives: smelly grey
spawn_room: melbs_kingst_collinsst
- !Dog
code: "55"
adjectives: reeking light brown
spawn_room: melbs_elizabethst_90
- !Dog
code: "56"
adjectives: reeking grey
spawn_room: melbs_swanstonst_20
- !Dog
code: "57"
adjectives: feral brown
spawn_room: melbs_flindersst_180
- !Dog
code: "58"
adjectives: reeking brown
spawn_room: melbs_bourkest_130
- !Dog
code: "59"
adjectives: mangy light brown
spawn_room: melbs_queenst_50
- !Dog
code: "60"
adjectives: growling white
spawn_room: melbs_kingst_110
- !MutantRat
code: "1"
adjectives: giant scary
spawn_room: kings_office_hallway
- !MutantRat
code: "2"
adjectives: huge frightening
spawn_room: kings_office_hallway

View File

@ -5,7 +5,7 @@ use crate::{
models::{ models::{
consent::ConsentType, consent::ConsentType,
effect::{EffectSet, EffectType}, effect::{EffectSet, EffectType},
item::{Item, ItemFlag, LiquidType, Pronouns, SkillType}, item::{Item, ItemFlag, ItemStaticSpecialData, LiquidType, Pronouns, SkillType},
}, },
regular_tasks::queued_command::QueuedCommandContext, regular_tasks::queued_command::QueuedCommandContext,
static_content::{room::Direction, species::BodyPart}, static_content::{room::Direction, species::BodyPart},
@ -28,6 +28,7 @@ mod fangs;
mod food; mod food;
pub mod head_armour; pub mod head_armour;
mod junk; mod junk;
mod keys;
pub mod lights; pub mod lights;
pub mod lock; pub mod lock;
pub mod lower_armour; pub mod lower_armour;
@ -344,6 +345,7 @@ pub struct PossessionData {
pub default_flags: Vec<ItemFlag>, pub default_flags: Vec<ItemFlag>,
pub eat_data: Option<EatData>, pub eat_data: Option<EatData>,
pub sit_data: Option<SitData>, pub sit_data: Option<SitData>,
pub static_special_data: Option<ItemStaticSpecialData>,
} }
impl Default for PossessionData { impl Default for PossessionData {
@ -371,6 +373,7 @@ impl Default for PossessionData {
default_flags: vec![], default_flags: vec![],
eat_data: None, eat_data: None,
sit_data: None, sit_data: None,
static_special_data: None,
} }
} }
} }
@ -431,6 +434,8 @@ pub enum PossessionType {
// Security // Security
Basiclock, Basiclock,
Scanlock, Scanlock,
// Keys
KingsOfficeKey,
// Food // Food
GrilledSteak, GrilledSteak,
GreasyBurger, GreasyBurger,
@ -473,6 +478,7 @@ impl Into<Item> for PossessionType {
.as_ref() .as_ref()
.map(|cd| cd.max_charges) .map(|cd| cd.max_charges)
.unwrap_or(0), .unwrap_or(0),
static_special_data: possession_dat.static_special_data.clone(),
..Default::default() ..Default::default()
} }
} }
@ -541,6 +547,7 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, &'static Possessio
.chain(bottles::data().iter().map(|v| ((*v).0.clone(), &(*v).1))) .chain(bottles::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(trauma_kit::data().iter().map(|v| ((*v).0.clone(), &(*v).1))) .chain(trauma_kit::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(club::data().iter().map(|v| ((*v).0.clone(), &(*v).1))) .chain(club::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(keys::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain( .chain(
corp_licence::data() corp_licence::data()
.iter() .iter()

View File

@ -0,0 +1,21 @@
use once_cell::sync::OnceCell;
use crate::models::item::ItemStaticSpecialData;
use super::{PossessionData, PossessionType};
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
static D: OnceCell<Vec<(PossessionType, PossessionData)>> = OnceCell::new();
&D.get_or_init(|| vec![(PossessionType::KingsOfficeKey, PossessionData {
display: "King's Office key",
details: "A sturdy silver metal key attached by a metal ring to a pink plastic key tag reading: Kings Office Key",
aliases: vec!["key", "kings office key"],
weight: 50,
static_special_data: Some(
ItemStaticSpecialData::KeyData {
pin_sequence: "KINGSOFF".to_owned()
}
),
..Default::default()
})])
}

View File

@ -1,7 +1,7 @@
use super::{LockcheckHandler, PossessionData, PossessionType}; use super::{LockcheckHandler, PossessionData, PossessionType};
use crate::{ use crate::{
message_handler::user_commands::{user_error, UResult, VerbContext}, message_handler::user_commands::{user_error, UResult, VerbContext},
models::item::{Item, LocationActionType}, models::item::{Item, ItemStaticSpecialData, LocationActionType},
regular_tasks::queued_command::QueuedCommandContext, regular_tasks::queued_command::QueuedCommandContext,
services::{ services::{
capacity::{check_item_capacity, CapacityLevel}, capacity::{check_item_capacity, CapacityLevel},
@ -15,8 +15,15 @@ use once_cell::sync::OnceCell;
struct BasicLockLockcheck; struct BasicLockLockcheck;
#[async_trait] #[async_trait]
impl LockcheckHandler for BasicLockLockcheck { impl LockcheckHandler for BasicLockLockcheck {
async fn cmd(&self, _ctx: &mut QueuedCommandContext, _what: &Item) -> UResult<()> { async fn cmd(&self, ctx: &mut QueuedCommandContext, what: &Item) -> UResult<()> {
// TODO implement actual check for key once we have them. match what.static_special_data.as_ref() {
Some(ItemStaticSpecialData::LockData { pin_sequence }) => {
if ctx.trans.has_key_matching(&ctx.item, &pin_sequence).await? {
return Ok(());
}
}
_ => {}
}
user_error( user_error(
"You search your possessions for a key that fits the door, but end up just rattling the handle uselessly.".to_owned())?; "You search your possessions for a key that fits the door, but end up just rattling the handle uselessly.".to_owned())?;
Ok(()) Ok(())

View File

@ -5,7 +5,7 @@ use super::{
#[double] #[double]
use crate::db::DBTrans; use crate::db::DBTrans;
use crate::{ use crate::{
message_handler::user_commands::UResult, message_handler::user_commands::{CommandHandlingError, UResult},
models::{ models::{
item::{DoorState, Item, ItemFlag}, item::{DoorState, Item, ItemFlag},
journal::JournalType, journal::JournalType,
@ -80,6 +80,11 @@ pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> {
display: "Melbs Sewers", display: "Melbs Sewers",
outdoors: false, outdoors: false,
}, },
Zone {
code: "kings_office",
display: "King's Office",
outdoors: false,
},
] ]
.into_iter() .into_iter()
.map(|x| (x.code, x)) .map(|x| (x.code, x))
@ -158,7 +163,7 @@ pub trait ExitBlocker {
pub enum ExitType { pub enum ExitType {
Free, // Anyone can just walk it (subject to any door logic). Free, // Anyone can just walk it (subject to any door logic).
Blocked(&'static (dyn ExitBlocker + Sync + Send)), // Custom code about who can pass. Blocked(Box<dyn ExitBlocker + Sync + Send>), // Custom code about who can pass.
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -297,12 +302,56 @@ impl Default for Exit {
} }
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[serde(default)]
pub struct ScanBlockerInfo {
need_scancode: ScanCode,
block_message: String,
}
impl Default for ScanBlockerInfo {
fn default() -> Self {
Self {
need_scancode: ScanCode::SewerAccess,
block_message: "You aren't allowed in there".to_owned(),
}
}
}
#[async_trait]
impl ExitBlocker for ScanBlockerInfo {
async fn attempt_exit(&self, ctx: &mut QueuedCommandContext, _exit: &Exit) -> UResult<bool> {
if ctx.item.item_type != "player" {
return Ok(false);
}
let user = ctx
.trans
.find_by_username(&ctx.item.item_code)
.await?
.ok_or_else(|| CommandHandlingError::UserError("No user exists".to_owned()))?;
if !user.scan_codes.contains(&self.need_scancode) {
if let Some((sess, _sess_dat)) = ctx
.trans
.find_session_for_player(&ctx.item.item_code)
.await?
{
ctx.trans
.queue_for_session(&sess, Some(&format!("{}\n", &self.block_message)))
.await?;
}
return Ok(false);
}
Ok(true)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[serde(default)] #[serde(default)]
pub struct SimpleExit { pub struct SimpleExit {
pub direction: Direction, pub direction: Direction,
pub target: ExitTarget, pub target: ExitTarget,
pub exit_climb: Option<ExitClimb>, pub exit_climb: Option<ExitClimb>,
pub needs_scan: Option<ScanBlockerInfo>,
} }
impl Default for SimpleExit { impl Default for SimpleExit {
@ -311,6 +360,7 @@ impl Default for SimpleExit {
direction: Direction::NORTH, direction: Direction::NORTH,
target: ExitTarget::UseGPS, target: ExitTarget::UseGPS,
exit_climb: None, exit_climb: None,
needs_scan: None,
} }
} }
} }
@ -320,7 +370,13 @@ impl Into<Exit> for SimpleExit {
Exit { Exit {
direction: self.direction, direction: self.direction,
target: self.target, target: self.target,
exit_type: ExitType::Free, exit_type: match self.needs_scan {
None => ExitType::Free,
Some(s) => ExitType::Blocked(Box::new(ScanBlockerInfo {
block_message: parse_ansi_markup(&s.block_message).unwrap(),
..s
})),
},
exit_climb: self.exit_climb, exit_climb: self.exit_climb,
} }
} }
@ -341,6 +397,11 @@ pub struct RoomStock {
pub poverty_discount: bool, pub poverty_discount: bool,
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ScanCode {
SewerAccess,
}
impl Default for RoomStock { impl Default for RoomStock {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -401,6 +462,7 @@ pub struct Room {
pub has_power: bool, pub has_power: bool,
pub door_states: Option<BTreeMap<Direction, DoorState>>, pub door_states: Option<BTreeMap<Direction, DoorState>>,
pub wristpad_hack_allowed: Option<WristpadHack>, pub wristpad_hack_allowed: Option<WristpadHack>,
pub scan_code: Option<ScanCode>,
pub journal: Option<JournalType>, pub journal: Option<JournalType>,
pub enter_trigger: Option<&'static (dyn RoomEnterTrigger + Sync + Send)>, pub enter_trigger: Option<&'static (dyn RoomEnterTrigger + Sync + Send)>,
pub scavtable: ScavtableType, pub scavtable: ScavtableType,
@ -426,6 +488,7 @@ impl Default for Room {
has_power: false, has_power: false,
door_states: None, door_states: None,
wristpad_hack_allowed: None, wristpad_hack_allowed: None,
scan_code: None,
journal: None, journal: None,
enter_trigger: None, enter_trigger: None,
scavtable: ScavtableType::Nothing, scavtable: ScavtableType::Nothing,
@ -456,6 +519,7 @@ pub struct SimpleRoom<T> {
pub has_power: bool, pub has_power: bool,
pub door_states: Option<BTreeMap<Direction, DoorState>>, pub door_states: Option<BTreeMap<Direction, DoorState>>,
pub wristpad_hack_allowed: Option<WristpadHack>, pub wristpad_hack_allowed: Option<WristpadHack>,
pub scan_code: Option<ScanCode>,
pub journal: Option<JournalType>, pub journal: Option<JournalType>,
pub scavtable: ScavtableType, pub scavtable: ScavtableType,
pub extra: T, pub extra: T,
@ -490,6 +554,7 @@ impl<T> Into<Room> for SimpleRoom<T> {
has_power: self.has_power, has_power: self.has_power,
door_states: self.door_states, door_states: self.door_states,
wristpad_hack_allowed: self.wristpad_hack_allowed, wristpad_hack_allowed: self.wristpad_hack_allowed,
scan_code: self.scan_code,
journal: self.journal, journal: self.journal,
enter_trigger: None, enter_trigger: None,
scavtable: self.scavtable, scavtable: self.scavtable,
@ -517,6 +582,7 @@ impl<'a, T: Default> Default for SimpleRoom<T> {
has_power: false, has_power: false,
door_states: None, door_states: None,
wristpad_hack_allowed: None, wristpad_hack_allowed: None,
scan_code: None,
journal: None, journal: None,
scavtable: ScavtableType::Nothing, scavtable: ScavtableType::Nothing,
extra: Default::default(), extra: Default::default(),

View File

@ -1,8 +1,14 @@
use super::{Room, SimpleRoom}; use super::{Room, SimpleRoom};
use crate::{ use crate::{
models::item::Scavtype, models::item::{ItemStaticSpecialData, LiquidType, LocationActionType, Scavtype},
static_content::{possession_type::PossessionType, scavtable::Scavinfo}, static_content::{
fixed_item::FixedItem,
possession_type::{possession_data, LiquidContainerData, PossessionData, PossessionType},
room::Direction,
scavtable::Scavinfo,
},
}; };
use ansi::ansi;
use serde_yaml::from_str as from_yaml_str; use serde_yaml::from_str as from_yaml_str;
pub fn street_scavtable() -> Vec<Scavinfo> { pub fn street_scavtable() -> Vec<Scavinfo> {
@ -15,6 +21,86 @@ pub fn street_scavtable() -> Vec<Scavinfo> {
}] }]
} }
pub fn fixed_items() -> Vec<FixedItem> {
vec![
FixedItem {
code: "melbs_king_st_spring_fed_fountain".to_owned(),
name: "spring fed fountain".to_owned(),
description: ansi!("A stainless steel fountain, clearly old, but in surprisingly good \
condition. A discoloured bronze plaque attached to it proudly declares \
that it is fed by a natural spring underneath it. It was designed so that \
unused water runs off it into a dog bowl - presumably in a time long past when \
dogs were friendly companions and not the menace they are today. It smells \
faintly of iron. [Try <bold>drink from fountain<reset> or, if you have a suitable \
container, <bold>fill<reset> container <bold>from fountain<reset>].").to_owned(),
location: "room/melbs_kingst_40".to_owned(),
proper_noun: false,
aliases: vec!["fountain".to_owned()],
..Default::default()
},
FixedItem {
code: "kings_office_basin".to_owned(),
name: "basin".to_owned(),
description: ansi!("A white porcelain basin, with chrome mixer taps in the centre, that you \
can imagine was once pristine and very fancy, but is now stained from \
years of grime. [Try <bold>drink from basin<reset> or, if you have a suitable \
container, <bold>fill<reset> container <bold>from basin<reset>].").to_owned(),
location: "room/kings_office_bathroom".to_owned(),
proper_noun: false,
aliases: vec!["sink".to_owned()],
..Default::default()
},
FixedItem {
code: "kings_office_hallway_door_lock".to_owned(),
name: "basic keyed lock".to_owned(),
description: "A basic lock that looks like it needs a key to open".to_owned(),
location: "room/kings_office_hallway".to_owned(),
proper_noun: false,
aliases: vec!["lock".to_owned()],
action_type: LocationActionType::InstalledOnDoorAsLock(Direction::SOUTH),
static_special_data: Some(ItemStaticSpecialData::LockData {
pin_sequence: "KINGSOFF".to_owned()
}),
..Default::default()
}
]
}
pub fn fixed_item_properties() -> Vec<(&'static str, PossessionData)> {
let lock_tmpl = possession_data().get(&PossessionType::Basiclock).unwrap();
vec![
(
"melbs_king_st_spring_fed_fountain",
PossessionData {
liquid_container_data: Some(LiquidContainerData {
capacity: 5000000, // mL
allowed_contents: Some(vec![LiquidType::Water]),
..Default::default()
}),
..Default::default()
},
),
(
"kings_office_basin",
PossessionData {
liquid_container_data: Some(LiquidContainerData {
capacity: 1100000, // mL
allowed_contents: Some(vec![LiquidType::Water]),
..Default::default()
}),
..Default::default()
},
),
(
"kings_office_hallway_door_lock",
PossessionData {
lockcheck_handler: lock_tmpl.lockcheck_handler,
..Default::default()
},
),
]
}
pub fn room_list() -> Vec<Room> { pub fn room_list() -> Vec<Room> {
from_yaml_str::<Vec<SimpleRoom<()>>>(include_str!("melbs.yaml")) from_yaml_str::<Vec<SimpleRoom<()>>>(include_str!("melbs.yaml"))
.unwrap() .unwrap()

View File

@ -12,6 +12,9 @@
- direction: east - direction: east
- direction: down - direction: down
target: !Custom room/melbs_sewers_1a target: !Custom room/melbs_sewers_1a
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -182,6 +185,9 @@
- direction: east - direction: east
- direction: down - direction: down
target: !Custom room/melbs_sewers_1i target: !Custom room/melbs_sewers_1i
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -306,6 +312,9 @@
- direction: east - direction: east
- direction: down - direction: down
target: !Custom room/melbs_sewers_2o target: !Custom room/melbs_sewers_2o
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -478,6 +487,9 @@
- direction: east - direction: east
- direction: down - direction: down
target: !Custom room/melbs_sewers_9q target: !Custom room/melbs_sewers_9q
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -593,6 +605,9 @@
- direction: north - direction: north
- direction: down - direction: down
target: !Custom room/melbs_sewers_17q target: !Custom room/melbs_sewers_17q
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -609,6 +624,9 @@
- direction: west - direction: west
- direction: down - direction: down
target: !Custom room/melbs_sewers_17a target: !Custom room/melbs_sewers_17a
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -725,6 +743,9 @@
- direction: west - direction: west
- direction: down - direction: down
target: !Custom room/melbs_sewers_17i target: !Custom room/melbs_sewers_17i
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -939,6 +960,9 @@
- direction: east - direction: east
- direction: down - direction: down
target: !Custom room/melbs_sewers_9a target: !Custom room/melbs_sewers_9a
needs_scan:
need_scancode: !SewerAccess
block_message: "You try to open the sewer grate, but a mechanical voice built into the lock on it intones: <blue>For your safety, only authorised people are allowed into the sewers. Officially authorised cleaners should refer to the corkboard in the cleaners room in the Kings Office for further instructions.<reset>"
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs - zone: melbs
@ -1458,23 +1482,105 @@
- direction: north - direction: north
- direction: south - direction: south
- direction: east - direction: east
target: !Custom room/kings_office_reception
should_caption: false should_caption: false
scavtable: CityStreet scavtable: CityStreet
- zone: melbs secondary_zones:
code: kings_office - zone: kings_office
name: Kings Office short: <yellow>EX<reset>
short: <bgyellow><white>KO<reset> grid_coords:
x: -1
y: 0
z: 0
caption: Exit
- zone: kings_office
code: kings_office_reception
name: King's Office - Reception
short: <bgyellow><white>RC<reset>
secondary_zones:
- zone: melbs
short: <bgyellow><white>KO<reset>
grid_coords:
x: 6
y: 5
z: 0
caption: King's Office
grid_coords: grid_coords:
x: 6 x: 0
y: 5 y: 0
z: 0 z: 0
description: A dilapidated office that clearly was once a grand governor's office under the empire. It is now barely maintained. The self-styled king slouches behind a desk in the corner, shuffling paperwork around his desk. [Use <bold>list<reset> to see contracts for sale here] description: A dilapidated office that clearly was once a grand governor's office under the empire. It is now barely maintained. The self-styled king slouches behind a desk in the corner, shuffling paperwork around his desk. [Use <bold>list<reset> to see contracts for sale here]
exits: exits:
- direction: west - direction: west
target: !Custom room/melbs_williamsst_80
- direction: north
stock_list: stock_list:
- possession_type: NewCorpLicence - possession_type: NewCorpLicence
list_price: 5000 list_price: 5000
poverty_discount: false poverty_discount: false
- zone: kings_office
code: kings_office_bathroom
name: Public Bathroom
short: <bggreen><red>BR<reset>
grid_coords:
x: 0
y: -1
z: 0
description: A once luxurious unisex bathroom, now greatly degraded through the action of time. Despite the dusty floor and crumbling tiles, the room still retains some of the wonder it once held. Bathroom stalls line one side of the bathroom, while a series of large white porcelain basins, now stained, provide for hand washing. Near the corner of the room, a door labelled "Danger! Staff Only" leads north
exits:
- direction: south
- direction: north
- zone: kings_office
code: kings_office_hallway
name: Rat Infested Hallway
should_caption: false
short: <yellow>==<reset>
grid_coords:
x: 0
y: -2
z: 0
description: A hallway showing signs of damage by rodents... except the bite marks in everything are huge
exits:
- direction: south
- direction: east
door_states:
south:
open: false
description: a solid door with a keyhole in it
- zone: kings_office
code: kings_office_hallway_2
name: Rat Infested Hallway
should_caption: false
short: <yellow>==<reset>
grid_coords:
x: 1
y: -2
z: 0
description: A hallway showing signs of damage by rodents... except the bite marks in everything are huge
exits:
- direction: east
- direction: west
door_states:
south:
open: false
description: a solid door with a keyhole in it
- zone: kings_office
code: kings_office_cleaners_office
name: Cleaner's Office
should_caption: true
short: <white><bgblack>CO<reset>
grid_coords:
x: 2
y: -2
z: 0
description: "A relatively small room full of metal sinks, buckets, and mops. You notice a small corkboard with a message pinned on the wall, reading: \"Cleaners - to gain access to the sewers, scan the QR code with your wristpad\". Below that is a QR code, and red text reading: <red>Beware - sewers are infested with dangerous mutants. Take a source of light.<reset> [To gain sewer access, try <bold>scan<reset>]"
exits:
- direction: west
scan_code: !SewerAccess
door_states:
west:
open: false
description: a solid wooden door with a handle
- zone: melbs - zone: melbs
code: melbs_williamsst_90 code: melbs_williamsst_90
name: Williams St - 90 block name: Williams St - 90 block

View File

@ -25,7 +25,7 @@ pub fn room_list() -> Vec<Room> {
grid_coords: GridCoords { x: 0, y: 0, z: -1 }, grid_coords: GridCoords { x: 0, y: 0, z: -1 },
exits: vec!(Exit { exits: vec!(Exit {
direction: Direction::EAST, direction: Direction::EAST,
exit_type: ExitType::Blocked(&npc::statbot::ChoiceRoomBlocker), exit_type: ExitType::Blocked(Box::new(npc::statbot::ChoiceRoomBlocker)),
..Default::default() ..Default::default()
}), }),
item_flags: vec![ItemFlag::NoUrgesHere], item_flags: vec![ItemFlag::NoUrgesHere],

View File

@ -9,6 +9,7 @@ pub enum SpeciesType {
Human, Human,
Dog, Dog,
Robot, Robot,
Rat,
} }
impl SpeciesType { impl SpeciesType {
@ -73,6 +74,7 @@ impl BodyPart {
pub struct SpeciesInfo { pub struct SpeciesInfo {
pub body_parts: Vec<BodyPart>, pub body_parts: Vec<BodyPart>,
pub corpse_butchers_into: Vec<PossessionType>, pub corpse_butchers_into: Vec<PossessionType>,
pub can_open_door: bool,
} }
pub fn species_info_map() -> &'static BTreeMap<SpeciesType, SpeciesInfo> { pub fn species_info_map() -> &'static BTreeMap<SpeciesType, SpeciesInfo> {
@ -99,6 +101,7 @@ pub fn species_info_map() -> &'static BTreeMap<SpeciesType, SpeciesInfo> {
PossessionType::AnimalSkin, PossessionType::AnimalSkin,
PossessionType::SeveredHead, PossessionType::SeveredHead,
], ],
can_open_door: true,
}, },
), ),
( (
@ -118,6 +121,27 @@ pub fn species_info_map() -> &'static BTreeMap<SpeciesType, SpeciesInfo> {
PossessionType::AnimalSkin, PossessionType::AnimalSkin,
PossessionType::SeveredHead, PossessionType::SeveredHead,
], ],
can_open_door: false,
},
),
(
SpeciesType::Rat,
SpeciesInfo {
body_parts: vec![
BodyPart::Head,
BodyPart::Face,
BodyPart::Chest,
BodyPart::Back,
BodyPart::Groin,
BodyPart::Legs,
BodyPart::Feet,
],
corpse_butchers_into: vec![
PossessionType::Steak,
PossessionType::AnimalSkin,
PossessionType::SeveredHead,
],
can_open_door: false,
}, },
), ),
( (
@ -132,6 +156,7 @@ pub fn species_info_map() -> &'static BTreeMap<SpeciesType, SpeciesInfo> {
BodyPart::Feet, BodyPart::Feet,
], ],
corpse_butchers_into: vec![], corpse_butchers_into: vec![],
can_open_door: false,
}, },
), ),
] ]