Document all shops and registered user commands

Include tests to keep it that way.
This commit is contained in:
Condorra 2024-06-20 22:58:08 +10:00
parent 5c2462a396
commit 851e3d8d1e
8 changed files with 404 additions and 219 deletions

25
Cargo.lock generated
View File

@ -227,7 +227,6 @@ dependencies = [
"nom", "nom",
"once_cell", "once_cell",
"ouroboros", "ouroboros",
"phf",
"rand", "rand",
"rand_distr", "rand_distr",
"ring", "ring",
@ -1246,33 +1245,9 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_macros",
"phf_shared", "phf_shared",
] ]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.11.2" version = "0.11.2"

View File

@ -26,7 +26,6 @@ tokio-stream = "0.1.14"
tokio-util = { version = "0.7.10", features = ["codec"] } tokio-util = { version = "0.7.10", features = ["codec"] }
uuid = { version = "1.7.0", features = ["v4", "serde", "rng"] } uuid = { version = "1.7.0", features = ["v4", "serde", "rng"] }
serde_json = "1.0.114" serde_json = "1.0.114"
phf = { version = "0.11.2", features = ["macros"] }
async-trait = "0.1.77" async-trait = "0.1.77"
nom = "7.1.3" nom = "7.1.3"
ouroboros = "0.18.3" ouroboros = "0.18.3"

View File

@ -14,7 +14,7 @@ use async_trait::async_trait;
use log::warn; use log::warn;
use mockall_double::double; use mockall_double::double;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use phf::phf_map; use std::collections::BTreeMap;
use std::sync::Arc; use std::sync::Arc;
mod agree; mod agree;
@ -124,195 +124,195 @@ pub fn user_error<A>(msg: String) -> UResult<A> {
/* Verb registries list types of commands available in different circumstances. */ /* Verb registries list types of commands available in different circumstances. */
pub type UserVerbRef = &'static (dyn UserVerb + Sync + Send); pub type UserVerbRef = &'static (dyn UserVerb + Sync + Send);
type UserVerbRegistry = phf::Map<&'static str, UserVerbRef>;
static ALWAYS_AVAILABLE_COMMANDS: UserVerbRegistry = phf_map! { pub fn always_available_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
"" => ignore::VERB, static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
"help" => help::VERB, V.get_or_init(|| {
"quit" => quit::VERB, vec![
}; ("", ignore::VERB),
("help", help::VERB),
("quit", quit::VERB),
]
.into_iter()
.collect()
})
}
static UNREGISTERED_COMMANDS: UserVerbRegistry = phf_map! { pub fn unregistered_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
"agree" => agree::VERB, static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
"connect" => login::VERB, V.get_or_init(|| {
"login" => login::VERB, vec![
"register" => register::VERB, ("agree", agree::VERB),
}; ("connect", login::VERB),
("login", login::VERB),
("register", register::VERB),
]
.into_iter()
.collect()
})
}
static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! { pub fn registered_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
V.get_or_init(|| {
vec![
// Movement comments first: // Movement comments first:
"north" => movement::VERB, ("north", movement::VERB),
"n" => movement::VERB, ("n", movement::VERB),
"northeast" => movement::VERB, ("northeast", movement::VERB),
"ne" => movement::VERB, ("ne", movement::VERB),
"east" => movement::VERB, ("east", movement::VERB),
"e" => movement::VERB, ("e", movement::VERB),
"southeast" => movement::VERB, ("southeast", movement::VERB),
"se" => movement::VERB, ("se", movement::VERB),
"south" => movement::VERB, ("south", movement::VERB),
"s" => movement::VERB, ("s", movement::VERB),
"southwest" => movement::VERB, ("southwest", movement::VERB),
"sw" => movement::VERB, ("sw", movement::VERB),
"west" => movement::VERB, ("west", movement::VERB),
"w" => movement::VERB, ("w", movement::VERB),
"northwest" => movement::VERB, ("northwest", movement::VERB),
"nw" => movement::VERB, ("nw", movement::VERB),
"up" => movement::VERB, ("up", movement::VERB),
"down" => movement::VERB, ("down", movement::VERB),
"in" => movement::VERB, ("in", movement::VERB),
// Other commands (alphabetical except aliases grouped): // Other commands (alphabetical except aliases grouped):
"allow" => allow::VERB, ("allow", allow::VERB),
"disallow" => allow::VERB, ("disallow", allow::VERB),
("attack", attack::VERB),
("butcher", butcher::VERB),
("buy", buy::VERB),
("c", c::VERB),
("close", close::VERB),
("corp", corp::VERB),
("cut", cut::VERB),
("delete", delete::VERB),
("drink", drink::VERB),
("drop", drop::VERB),
("eat", eat::VERB),
("fill", fill::VERB),
("fire", fire::VERB),
("follow", follow::VERB),
("unfollow", follow::VERB),
("gear", gear::VERB),
("get", get::VERB),
("hack", hack::VERB),
("hire", hire::VERB),
("improv", improvise::VERB),
("improvise", improvise::VERB),
("improvize", improvise::VERB),
("install", install::VERB),
("inventory", inventory::VERB),
("inv", inventory::VERB),
("i", inventory::VERB),
("kill", attack::VERB),
("k", attack::VERB),
("describe", describe::VERB),
("l", look::VERB),
("look", look::VERB),
("read", look::VERB),
("examine", look::VERB),
("ex", look::VERB),
("feint", feint::VERB),
("list", list::VERB),
("load", load::VERB),
("unload", load::VERB),
("lm", map::VERB),
("lmap", map::VERB),
("gm", map::VERB),
("gmap", map::VERB),
("make", make::VERB),
("open", open::VERB),
("p", page::VERB),
("page", page::VERB),
("pg", page::VERB),
("rep", page::VERB),
("repl", page::VERB),
("reply", page::VERB),
("pay", pay::VERB),
("plug", plug::VERB),
("pow", pow::VERB),
("power", pow::VERB),
("powerattack", pow::VERB),
("put", put::VERB),
("recline", recline::VERB),
("remove", remove::VERB),
("rent", rent::VERB),
("report", report::VERB),
(("\'", say::VERB)),
("say", say::VERB),
("scan", scan::VERB),
("scavenge", scavenge::VERB),
("search", scavenge::VERB),
("sc", score::VERB),
("score", score::VERB),
("sell", sell::VERB),
("share", share::VERB),
("serious", share::VERB),
("amicable", share::VERB),
("joking", share::VERB),
("parody", share::VERB),
("play", share::VERB),
("thoughts", share::VERB),
("exploring", share::VERB),
("roaming", share::VERB),
("fishing", share::VERB),
("good", share::VERB),
("surviving", share::VERB),
("slow", share::VERB),
("normal", share::VERB),
("intense", share::VERB),
("sign", sign::VERB),
("sit", sit::VERB),
("stand", stand::VERB),
("st", status::VERB),
("stat", status::VERB),
("stats", status::VERB),
("status", status::VERB),
("stop", stop::VERB),
("turn", turn::VERB),
("uninstall", uninstall::VERB),
("use", use_cmd::VERB),
("vacate", vacate::VERB),
("-", whisper::VERB),
("whisper", whisper::VERB),
("tell", whisper::VERB),
("wear", wear::VERB),
("wield", wield::VERB),
("who", who::VERB),
("write", write::VERB),
]
.into_iter()
.collect()
})
}
"attack" => attack::VERB, pub fn staff_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
"butcher" => butcher::VERB, static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
"buy" => buy::VERB, V.get_or_init(|| {
"c" => c::VERB, vec![
"close" => close::VERB, ("staff_invincible", invincible::VERB),
"corp" => corp::VERB, ("staff_reset_spawns", reset_spawns::VERB),
"cut" => cut::VERB, ("staff_show", staff_show::VERB),
"delete" => delete::VERB, ]
"drink" => drink::VERB, .into_iter()
"drop" => drop::VERB, .collect()
"eat" => eat::VERB, })
"fill" => fill::VERB, }
"fire" => fire::VERB,
"follow" => follow::VERB,
"unfollow" => follow::VERB,
"gear" => gear::VERB,
"get" => get::VERB,
"hack" => hack::VERB,
"hire" => hire::VERB,
"improv" => improvise::VERB,
"improvise" => improvise::VERB,
"improvize" => improvise::VERB,
"install" => install::VERB,
"inventory" => inventory::VERB,
"inv" => inventory::VERB,
"i" => inventory::VERB,
"kill" => attack::VERB,
"k" => attack::VERB,
"describe" => describe::VERB,
"l" => look::VERB,
"look" => look::VERB,
"read" => look::VERB,
"examine" => look::VERB,
"ex" => look::VERB,
"feint" => feint::VERB,
"list" => list::VERB,
"load" => load::VERB,
"unload" => load::VERB,
"lm" => map::VERB,
"lmap" => map::VERB,
"gm" => map::VERB,
"gmap" => map::VERB,
"make" => make::VERB,
"open" => open::VERB,
"p" => page::VERB,
"page" => page::VERB,
"pg" => page::VERB,
"rep" => page::VERB,
"repl" => page::VERB,
"reply" => page::VERB,
"pay" => pay::VERB,
"plug" => plug::VERB,
"pow" => pow::VERB,
"power" => pow::VERB,
"powerattack" => pow::VERB,
"put" => put::VERB,
"recline" => recline::VERB,
"remove" => remove::VERB,
"rent" => rent::VERB,
"report" => report::VERB,
"\'" => say::VERB,
"say" => say::VERB,
"scan" => scan::VERB,
"scavenge" => scavenge::VERB,
"search" => scavenge::VERB,
"sc" => score::VERB,
"score" => score::VERB,
"sell" => sell::VERB,
"share" => share::VERB,
"serious" => share::VERB,
"amicable" => share::VERB,
"joking" => share::VERB,
"parody" => share::VERB,
"play" => share::VERB,
"thoughts" => share::VERB,
"exploring" => share::VERB,
"roaming" => share::VERB,
"fishing" => share::VERB,
"good" => share::VERB,
"surviving" => share::VERB,
"slow" => share::VERB,
"normal" => share::VERB,
"intense" => share::VERB,
"sign" => sign::VERB,
"sit" => sit::VERB,
"stand" => stand::VERB,
"st" => status::VERB,
"stat" => status::VERB,
"stats" => status::VERB,
"status" => status::VERB,
"stop" => stop::VERB,
"turn" => turn::VERB,
"uninstall" => uninstall::VERB,
"use" => use_cmd::VERB,
"vacate" => vacate::VERB,
"-" => whisper::VERB,
"whisper" => whisper::VERB,
"tell" => whisper::VERB,
"wear" => wear::VERB,
"wield" => wield::VERB,
"who" => who::VERB,
"write" => write::VERB,
};
static STAFF_COMMANDS: UserVerbRegistry = phf_map! {
"staff_invincible" => invincible::VERB,
"staff_reset_spawns" => reset_spawns::VERB,
"staff_show" => staff_show::VERB,
};
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> { fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
let mut result = ALWAYS_AVAILABLE_COMMANDS.get(cmd); let mut result = always_available_commands().get(cmd);
match &ctx.user_dat { match &ctx.user_dat {
None => { None => {
result = result.or_else(|| UNREGISTERED_COMMANDS.get(cmd)); result = result.or_else(|| unregistered_commands().get(cmd));
} }
Some(user_dat) => { Some(user_dat) => {
if user_dat.terms.terms_complete { if user_dat.terms.terms_complete {
result = result.or_else(|| REGISTERED_COMMANDS.get(cmd)); result = result.or_else(|| registered_commands().get(cmd));
if user_dat.user_flags.contains(&UserFlag::Staff) { if user_dat.user_flags.contains(&UserFlag::Staff) {
result = result.or_else(|| STAFF_COMMANDS.get(cmd)); result = result.or_else(|| staff_commands().get(cmd));
} }
} else if cmd == "agree" { } else if cmd == "agree" {
result = Some(&agree::VERB); result = Some(&agree::VERB);

View File

@ -65,6 +65,10 @@ pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeSet;
use crate::message_handler::user_commands::registered_commands;
use super::*; use super::*;
#[test] #[test]
@ -81,4 +85,18 @@ mod tests {
fn always_help_ok() { fn always_help_ok() {
always_help_pages(); always_help_pages();
} }
#[test]
fn all_registered_user_commands_documented() {
let help_keys: BTreeSet<String> =
registered_help_pages().keys().map(|k| k.clone()).collect();
let cmds: BTreeSet<String> = registered_commands()
.keys()
.map(|k| k.to_string())
.collect();
assert_eq!(
cmds.difference(&help_keys).collect::<BTreeSet<&String>>(),
BTreeSet::<&String>::new()
);
}
} }

View File

@ -26,12 +26,37 @@ newbie: |-
Also try the map commands - <bold>lmap<reset> for a local map including exits, and <bold>gmap<reset> for a giant Also try the map commands - <bold>lmap<reset> for a local map including exits, and <bold>gmap<reset> for a giant
map to let you see the broader context. map to let you see the broader context.
movement: |- movement: &movement |-
Once you know what direction to move, you can type the direction, using either Once you know what direction to move, you can type the direction, using either
the full name of the direction (e.g. <bold>northeast<reset> or <bold>south<reset>, the full name of the direction (e.g. <bold>northeast<reset> or <bold>south<reset>,
or a short form (<bold>n<reset>, <bold>e<reset>, <bold>s<reset>, <bold>w<reset>, <bold>ne<reset>, <bold>nw<reset>, <bold>se<reset>, <bold>sw<reset>, <bold>up<reset>, <bold>down<reset>). or a short form (<bold>n<reset>, <bold>e<reset>, <bold>s<reset>, <bold>w<reset>, <bold>ne<reset>, <bold>nw<reset>, <bold>se<reset>, <bold>sw<reset>, <bold>up<reset>, <bold>down<reset>).
look: Try <bold>look<reset> or <bold>l<reset> to look at the current room. Follow it with the name of an exit to look at an adjacent room. Or name an object or player in the room to look at that. n: *movement
combat: >- north: *movement
ne: *movement
northeast: *movement
e: *movement
east: *movement
se: *movement
southeast: *movement
s: *movement
south: *movement
sw: *movement
southwest: *movement
w: *movement
west: *movement
nw: *movement
northwest: *movement
up: *movement
down: *movement
in: *movement
look: &look Try <bold>look<reset>, <bold>l<reset>, <bold>examine<reset> or <bold>ex<reset> to look at the current room. Follow it with the name of an exit to look at an adjacent room. Or name an object or player in the room to look at that.
l: *look
ex: *look
read: *look
examine: *look
describe: Use <bold>describe me as<reset> description to set how you appear when people look at you.
who: Use <bold>who<reset> to see what players are online.
combat: &combat >-
Type <bold>kill<reset> character or <bold>k<reset> character to attack a character. Type <bold>kill<reset> character or <bold>k<reset> character to attack a character.
If their health points hits zero, they die. If your health points hit zero, you die. If their health points hits zero, they die. If your health points hit zero, you die.
Note that characters can reclone so death isn't fully permanent (see <bold>help death<reset>). Note that characters can reclone so death isn't fully permanent (see <bold>help death<reset>).
@ -46,6 +71,20 @@ combat: >-
Remember, you can always (at least while you are not dead) try to run away from combat by just Remember, you can always (at least while you are not dead) try to run away from combat by just
by using movement commands (see <bold>help movement<reset> - but if your dodge skill isn't high by using movement commands (see <bold>help movement<reset> - but if your dodge skill isn't high
enough, it might fail. enough, it might fail.
k: *combat
kill: *combat
attack: *combat
pow: &pow >-
Type <bold>powerattack<reset> or <bold>pow<reset> while you are in combat to make your
next move a powerattack. For weapons where it is possible, a powerattack takes longer
to do, but hits harder, and has a higher chance of inflicting critical damage on the
enemy.
power: *pow
powerattack: *pow
feint: >-
Use <bold>feint<reset> while you are in combat to make your next move a feint. This
will pit your intelligence against your opponent, and confuse them if you win. But
beware, if they win, you might end up confused!
death: >- death: >-
In Blastmud, you can die if your health points reach zero. While you are dead, you can't In Blastmud, you can die if your health points reach zero. While you are dead, you can't
move or talk! Unless someone quickly comes and uses a defibrilator on you, your current move or talk! Unless someone quickly comes and uses a defibrilator on you, your current
@ -59,40 +98,55 @@ skills: >-
and some from your raws. Raw skill is how much you have improved by learning the skill as you and some from your raws. Raw skill is how much you have improved by learning the skill as you
play the game. Your raw skill caps out at 15 per skill, and adds on to the contribution from play the game. Your raw skill caps out at 15 per skill, and adds on to the contribution from
your stats, and any temporary buffs or debuffs caused by your character's state. your stats, and any temporary buffs or debuffs caused by your character's state.
information: |- information: &info |-
Commands to examine the world: Commands to examine the world:
<bold>look<reset> (or <bold>l<reset>) to look around - follow it with an exit or <bold>look<reset> (or <bold>l<reset>) to look around - follow it with an exit or
a character / item name for detail on that. a character / item name for detail on that.
<bold>lmap<reset> - get a map showing exits and places. <bold>lmap<reset> - get a map showing exits and places.
<bold>gmap<reset> - see a larger (giant) map.
lmap: *info
lm: *info
gmap: *info
gm: *info
talk: &talk |- talk: &talk |-
Use: Use:
<bold>'<reset>message to send message to the room. <bold>'<reset>message to send message to the room.
<bold>-<reset>user message to whisper to someone. <bold>-<reset>user message to whisper to someone.
<bold>p<reset> user message to page someone on your wristpad. <bold>p<reset> user message to page someone on your wristpad.
<bold>reply<reset> message to page back the last person to page you. <bold>reply<reset> message (or <bold>rep<reset> message) to page back the last person to page you.
say: *talk say: *talk
whisper: *talk whisper: *talk
page: *talk page: *talk
reply: *talk reply: *talk
"'": *talk
"-": *talk
rep: *talk
repl: *talk
tell: *talk
p: *talk
pg: *talk
possessions: &possessions |- possessions: &possessions |-
Use: Use:
<bold>get<reset> item to pick something up from the ground. <bold>get<reset> item to pick something up from the ground.
<bold>drop<reset> item to drop something from your inventory. <bold>drop<reset> item to drop something from your inventory.
<bold>inv<reset> to see what you are holding. <bold>inv<reset> to see what you are holding.
<bold>buy<reset> item to buy something from the shop you are in. <bold>buy<reset> item to buy something from the shop you are in.
<bold>sell<reset> item to sell something to the shop you are in.
<bold>list<reset> to see the price list for the stop. <bold>list<reset> to see the price list for the stop.
<bold>wield<reset> to hold a weapon in your inventory for use when you attack. <bold>wield<reset> to hold a weapon in your inventory for use when you attack.
<bold>gear<reset> to see what you are wearing, and how much protection it offers. <bold>gear<reset> to see what you are wearing, and how much protection it offers.
Hint: get and drop support an item name, but you can also prefix it with a number - e.g. <bold>get 2 whip<reset>. Instead of a number, you can use <bold>all<reset>. You can also omit the item name to match any possession, e.g. <bold>drop all<reset> will drop everything you have. Hint: get and drop support an item name, but you can also prefix it with a number - e.g. <bold>get 2 whip<reset>. Instead of a number, you can use <bold>all<reset>. You can also omit the item name to match any possession, e.g. <bold>drop all<reset> will drop everything you have.
get: *possessions get: *possessions
drop: *possessions drop: *possessions
i: *possessions
inv: *possessions inv: *possessions
inventory: *possessions inventory: *possessions
buy: *possessions buy: *possessions
sell: *possessions
list: *possessions list: *possessions
wield: *possessions wield: *possessions
gear: *possessions gear: *possessions
allow: |- allow: &consent |-
<bold>allow<reset> is the corner-stone of Blastmud's consent system. Consents in Blastmud let you choose how you want to play with other players (it only affects players, not NPCs). There are 5 types of consent: <bold>fight<reset> (for hostile actions like attack or pick), <bold>medicine<reset> (for medical actions, including those that might crit fail and do harm), <bold>gifts<reset> (lets them give you things), <bold>visit<reset> (lets them on to a tile owned by you legally), and <bold>share<reset> (lets them local share knowledge with you, making both parties stronger). <bold>allow<reset> is the corner-stone of Blastmud's consent system. Consents in Blastmud let you choose how you want to play with other players (it only affects players, not NPCs). There are 5 types of consent: <bold>fight<reset> (for hostile actions like attack or pick), <bold>medicine<reset> (for medical actions, including those that might crit fail and do harm), <bold>gifts<reset> (lets them give you things), <bold>visit<reset> (lets them on to a tile owned by you legally), and <bold>share<reset> (lets them local share knowledge with you, making both parties stronger).
To allow, as an individual, use the syntax <bold>allow <reset>type <bold>from <reset>player options To allow, as an individual, use the syntax <bold>allow <reset>type <bold>from <reset>player options
@ -113,6 +167,8 @@ allow: |-
<bold>disallow<reset> action <bold>from<reset> player <bold>disallow<reset> action <bold>from<reset> player
Consent for fight can be revoked similarly if the consent used the <bold>allow revoke<reset> option. Consent for fight can be revoked similarly if the consent used the <bold>allow revoke<reset> option.
Otherwise, attempting to revoke informs the other player, and it is revoked when they also issue a disallow command. Otherwise, attempting to revoke informs the other player, and it is revoked when they also issue a disallow command.
consent: *consent
disallow: *consent
corp: |- corp: |-
<bold>corp<reset> is the entry point to commands related to corps. It supports the following subcommands: <bold>corp<reset> is the entry point to commands related to corps. It supports the following subcommands:
<bold>corp hire<reset> username <bold>into<reset> corpname <bold>corp hire<reset> username <bold>into<reset> corpname
@ -193,6 +249,10 @@ corp: |-
death - Deaths of players in the corp. death - Deaths of players in the corp.
consent - Changes to corp consents (war declarations). consent - Changes to corp consents (war declarations).
"corp order": "<bold>corp order<reset> corpname <bold>as<reset> number Set the position of the corp in your list." "corp order": "<bold>corp order<reset> corpname <bold>as<reset> number Set the position of the corp in your list."
c: >-
<bold>c<reset> sends a message to one of your corporations. Use it as <bold>c<reset> message to send message to your
first corp (use <bold>corp order<reset> to choose which corp is first). Or explicitly name a corp like this:
<bold>corp @<reset>corpname message
"install": "<bold>install<reset> item <bold>on door to<reset> direction Installs hardware such as a lock on a door." "install": "<bold>install<reset> item <bold>on door to<reset> direction Installs hardware such as a lock on a door."
"uninstall": "<bold>uninstall<reset> item <bold>from door to<reset> direction Removes installed hardware such as a lock on a door." "uninstall": "<bold>uninstall<reset> item <bold>from door to<reset> direction Removes installed hardware such as a lock on a door."
"gear": "<bold>gear<reset> See equipment you have on, and what protection it offers you." "gear": "<bold>gear<reset> See equipment you have on, and what protection it offers you."
@ -210,7 +270,7 @@ put: |-
You can put something in a container using a command like <bold>put<reset> thing <bold>in<reset> container. See also <bold>help get<reset> to learn how to get them back out. You can put something in a container using a command like <bold>put<reset> thing <bold>in<reset> container. See also <bold>help get<reset> to learn how to get them back out.
get: |- get: |-
You can get something out of a container using a command like <bold>get<reset> thing <bold>from<reset> container. You can get something out of a container using a command like <bold>get<reset> thing <bold>from<reset> container.
sharing: |- sharing: &sharing |-
Sharing lets you share knowledge with another player. Because knowledge is power, sharing will increase the stats of both players for a little while (10 minutes); the better a job you do at sharing, the greater the impact on your stats (up to a maximum of two points of increase to every stat). If you share twice, you will forget what you from the first sharing, and the more recent one will affect your stats. Sharing is therefore a great way to collaborate with another player to temporarily become more capable. However, sharing effectively takes great skill [both player skill, and share skill in the game]. Sharing lets you share knowledge with another player. Because knowledge is power, sharing will increase the stats of both players for a little while (10 minutes); the better a job you do at sharing, the greater the impact on your stats (up to a maximum of two points of increase to every stat). If you share twice, you will forget what you from the first sharing, and the more recent one will affect your stats. Sharing is therefore a great way to collaborate with another player to temporarily become more capable. However, sharing effectively takes great skill [both player skill, and share skill in the game].
Type <bold>share knowledge with<reset> player to start a sharing session. You will immediately begin sharing, and if you type <bold>share status<reset>, you'll start to see growth in certain interest types. Sharing stops when the pair sharing get to 100% on any interest level and get bored - so you'll want to change topics to avoid that. To get the best score, try to get all interest levels balanced to just below the maximum before you go over on any. However, there are a couple of catches: firstly, changing anything about the conversation is going to be awkward (might fail) if you try too soon after the last change - the higher your Share skill, the faster you can change with a high chance of success. Secondly, to discuss some topics, you need to switch to an appropriate conversation style (amicable, playful, or serious) for the topic first - which could take even longer if your skill level is low. It is a lot easier if yyou work with your conversational partner - they might be able to change earlier. Type <bold>share knowledge with<reset> player to start a sharing session. You will immediately begin sharing, and if you type <bold>share status<reset>, you'll start to see growth in certain interest types. Sharing stops when the pair sharing get to 100% on any interest level and get bored - so you'll want to change topics to avoid that. To get the best score, try to get all interest levels balanced to just below the maximum before you go over on any. However, there are a couple of catches: firstly, changing anything about the conversation is going to be awkward (might fail) if you try too soon after the last change - the higher your Share skill, the faster you can change with a high chance of success. Secondly, to discuss some topics, you need to switch to an appropriate conversation style (amicable, playful, or serious) for the topic first - which could take even longer if your skill level is low. It is a lot easier if yyou work with your conversational partner - they might be able to change earlier.
@ -218,3 +278,123 @@ sharing: |-
Throughout the conversation, the game will tell you what styles you can change to, and what topics you can change to under the current style. Just type the name of the topic or style to switch. Each topic has a different rate at which it increases or decreases different interest types. Throughout the conversation, the game will tell you what styles you can change to, and what topics you can change to under the current style. Just type the name of the topic or style to switch. Each topic has a different rate at which it increases or decreases different interest types.
If you find the pace of the conversation too fast or slow, you can also change the pace with <bold>share slowly<reset>, <bold>share normally<reset> or <bold>share quickly<reset>. Keep in mind changing the pace counts as a change, like changing topics - but once you have set the pace, it scales how fast conversational interest levels grow. If you find the pace of the conversation too fast or slow, you can also change the pace with <bold>share slowly<reset>, <bold>share normally<reset> or <bold>share quickly<reset>. Keep in mind changing the pace counts as a change, like changing topics - but once you have set the pace, it scales how fast conversational interest levels grow.
amicable: *sharing
exploring: *sharing
fishing: *sharing
good: *sharing
joking: *sharing
parody: *sharing
play: *sharing
serious: *sharing
share: *sharing
slow: *sharing
normal: *sharing
intense: *sharing
surviving: *sharing
thoughts: *sharing
roaming: *sharing
butcher: &butcher >-
Use the <bold>butcher<reset> command to cut everything possible out of a corpse.
Alternatively, you can cut one part from a corpse by doing something like:
<bold>cut<reset> steak <bold>from<reset> dead dog
This typically requires craft skill.
cut: *butcher
hire: &hiring >-
Use <bold>hire<reset> npc to hire an NPC (e.g. a Roboporter) into your personal employment.
Use <bold>fire<reset> npc to fire them and terminate their employment.
Please note that these commands hire them personally. See also <bold>corp hire<reset>
and <bold>corp fire<reset> to learn about hiring into corps.
fire: *hiring
follow: &follow >-
Use <bold>follow<reset> player to start following a player around.
Use <bold>unfollow<reset> to stop following.
unfollow: *follow
open: &doors >-
Use <bold>open<reset> direction to open a door (and attempting to unlock).
Doors will auto-open just by moving through them, but opening lets you look first,
or let someone else in.
Use <bold>close<reset> direction to close the door in that direction (and lock if applicable).
doors: *doors
close: *doors
hack: >-
If you've found a room where you can install a wristpad hack, use
<bold>hack<reset> hackname to install that hack on your wristpad.
The room description will generally tell you what hackname to use.
improvise: &improvise >-
If you don't have a crafting bench or instructions, you might still be able to make something.
Try <bold>improv with<reset> item to explore what you can make with item.
Once you know what to make, try <bold>improv<reset> output <bold>from<reset> item1, item2, ...
This will let you use as many items as you need.
improv: *improvise
improvize: *improvise
make: &craft >-
To craft, you generally need some form of instructions, a craft bench of some form (which might be a stove, specialised workbench, etc...), and some ingredients.
To start crafting, firstly <bold>put<reset> the instructions (or the entire book) into the craft bench. See <bold>help put<reset> for help on putting. Likewise put the ingredients in the bench
Then use <bold>make<reset> thing <bold>on<reset> bench, where thing is the thing the instructions describe how to
make. The thing will end up in the craft bench. Pay attention to make sure you get it out, and not the recipe.
craft: *craft
crafting: *craft
load: &loading >-
To load an item onto an NPC such as a roboporter (so it will carry your stuff around for you), try:
<bold>load<reset> item <bold>onto<reset> npc
You can unload with <bold>unload<reset> item <bold>from<reset> npc
unload: *loading
pay: >-
Use <bold>pay<reset> $amount <bold> [<bold>from<reset> corp] to recipient
Leave off the <bold>from<reset> corp part if it is from you.
Recipient can be a corp or a user.
You must have finance privileges in the corp you are paying from.
plug: >-
Use <bold>plug in<reset> equipment to plug something electrical in to charge.
It needs to be on the ground (not in inventory), and you need to do it where there is power.
recline: &posture >-
Use <bold>sit<reset> to sit down.
Use <bold>recline<reset> to recline (lie down).
Use <bold>stand<reset> to stand up.
sit: *posture
stand: *posture
stand up: *posture
rent: &rent >-
Renting property gives you somewhere to stay, or gives your corp an HQ.
To rent, go to the place where property is on offer (usually the lobby), and use
<bold>rent<reset> roomtype
To rent for a corp, use:
<bold>rent<reset> roomtype <bold>for<reset> corpname
To stop renting, use:
<bold>vacate<reset> roomtype [<bold>for<reset> corpname]
vacate: *rent
renting: *rent
property: *rent
report abuse: &report
If someone breaks the rules (for example, by posting content that isn't allowed, or by harassing another user),
immediately type <bold>report abuse<reset> to record the last 80 things the MUD sent to you, and give you a report
ID. Once you have a report ID, send it to staff@blastmud.org with a description of the problem. We'll be able to
look up the report ID to see what you are referring to.
report: *report
abuse: *report
harassment: *report
scan: >-
Use the <bold>scan<reset> command to scan a QR code found in the game with your wristpad.
score: &score >-
Use the <bold>score<reset> command to see your stats, skills, experience, and hacks.
sc: *score
scavenge: &scavenge >-
Use the <bold>scavenge<reset> or <bold>search<reset> command to look for hidden treasure (or junk) at the current location.
search: *scavenge
sign: Use <bold>sign<reset> thing to sign a contract.
status: &stats >-
Use <bold>stats<reset> to see your character's basic stats and credits.
st: *stats
stat: *stats
stats: *stats
stop: Use <bold>stop<reset> to stop any queued actions, and reverse the current one if possible.
turn: &turn >-
Use <bold>turn on<reset> thing to turn something on.
Use <bold>turn off<reset> thing to turn something off.
use: Use <bold>use<reset> item [<bold>on<reset> character] to use something on someone (optional).
wear: &clothes >-
Use <bold>wear<reset> clothing to wear something.
Use <bold>remove<reset> clothing to remove some clothing.
Clothing must be removed in the opposite order it is layered on.
remove: *clothes
write: Use <bold>write<reset> message <bold>on<reset> item to write on something.

View File

@ -890,6 +890,7 @@ pub async fn check_for_enter_action(ctx: &mut QueuedCommandContext<'_>) -> UResu
mod test { mod test {
use super::super::scavtable::scavtable_map; use super::super::scavtable::scavtable_map;
use super::*; use super::*;
use ansi::ansi;
use itertools::Itertools; use itertools::Itertools;
#[test] #[test]
@ -1002,4 +1003,16 @@ mod test {
.collect::<Vec<String>>(); .collect::<Vec<String>>();
assert_eq!(unresolved_exits, Vec::<String>::new()); assert_eq!(unresolved_exits, Vec::<String>::new());
} }
#[test]
fn rooms_with_stock_list_have_help() {
let stock_rooms_without_help: Vec<&'static str> = room_list()
.iter()
.filter(|r| {
!r.stock_list.is_empty() && !r.description.contains(ansi!("<bold>list<reset>"))
})
.map(|r| r.code.as_str())
.collect();
assert_eq!(stock_rooms_without_help, Vec::<&'static str>::new());
}
} }

View File

@ -78,7 +78,7 @@
x: 2 x: 2
y: -2 y: -2
z: 0 z: 0
description: A dated looking eatery that looks like it has existed long before the collapse. Inside the diner is speckled white linoleum floors that run up to the walls. The smell of bacon grease pervades the air, reminding you of the purpose of this venue with every breath you take. Tables of various sizes are set out for diners to sit at, and at the back is a kitchen. The kitchen whirs with stainless steel exhaust pans, as cooks appear to do nothing but frying. Separating the kitchen from the dining area, and running the width of the restaurant, runs a counter. Hanging signs convey a menu in hand-painted black on white. A middle-aged woman sits behind the counter, looking eager to take your order. description: A dated looking eatery that looks like it has existed long before the collapse. Inside the diner is speckled white linoleum floors that run up to the walls. The smell of bacon grease pervades the air, reminding you of the purpose of this venue with every breath you take. Tables of various sizes are set out for diners to sit at, and at the back is a kitchen. The kitchen whirs with stainless steel exhaust pans, as cooks appear to do nothing but frying. Separating the kitchen from the dining area, and running the width of the restaurant, runs a counter. Hanging signs convey a menu in hand-painted black on white. A middle-aged woman sits behind the counter, looking eager to take your order. [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: west - direction: west
should_caption: false should_caption: false
@ -330,7 +330,7 @@
A warm and inviting atmosphere surrounds you as you explore the meticulously arranged space. Rich, reclaimed hardwood floors stretch out beneath your feet, bearing the echoes of countless stories from the past. Worn yet sturdy, the wooden furniture exudes an enduring charm that captivates the eye, while the kitchen and bathroom sections of the store harbour more modern furniture. A warm and inviting atmosphere surrounds you as you explore the meticulously arranged space. Rich, reclaimed hardwood floors stretch out beneath your feet, bearing the echoes of countless stories from the past. Worn yet sturdy, the wooden furniture exudes an enduring charm that captivates the eye, while the kitchen and bathroom sections of the store harbour more modern furniture.
A seasoned storekeeper stands ready to assist shoppers A seasoned storekeeper stands ready to assist shoppers [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: west - direction: west
stock_list: stock_list:
@ -1511,7 +1511,7 @@
It smells slightly acrid here. It smells slightly acrid here.
Behind a counter, a serious looking tall man clad in a labcoat stands behind a counter, waiting patiently for you to select your equipment and pay him Behind a counter, a serious looking tall man clad in a labcoat stands behind a counter, waiting patiently for you to select your equipment and pay him [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: east - direction: east
stock_list: stock_list:
@ -1533,7 +1533,7 @@
The inhabitants of this forsaken land gather here, seeking solace and hope within the forgotten stories and practical guides that line the shelves. The inhabitants of this forsaken land gather here, seeking solace and hope within the forgotten stories and practical guides that line the shelves.
The Dusty Pages stands as a beacon of intellectual survival, a sanctuary where survivors can momentarily escape the harsh realities of their existence The Dusty Pages stands as a beacon of intellectual survival, a sanctuary where survivors can momentarily escape the harsh realities of their existence [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: west - direction: west
stock_list: stock_list:
@ -1737,7 +1737,7 @@
code: oorans_testing code: oorans_testing
name: OORANS Testing Centre name: OORANS Testing Centre
description: |- description: |-
A room, painted completely white. The room has rows of desks, each with a pen attached by a string. Stern looking invigilators pace up and down the room ensuring that no one cheats on the test. At the front, it looks like you can <bold>buy<reset> test papers from the head invigilator to sit the test for a permit to access fallout contaminated areas A room, painted completely white. The room has rows of desks, each with a pen attached by a string. Stern looking invigilators pace up and down the room ensuring that no one cheats on the test. At the front, it looks like you can <bold>buy<reset> test papers from the head invigilator to sit the test for a permit to access fallout contaminated areas. [Try <bold>list<reset> to see what is for sale here]
short: <bgblack><yellow>TE<reset> short: <bgblack><yellow>TE<reset>
grid_coords: grid_coords:
x: 1 x: 1
@ -1752,7 +1752,7 @@
code: oorans_gift code: oorans_gift
name: OORANS Gift Shop name: OORANS Gift Shop
description: |- description: |-
A brightly coloured room, each wall a different primary colour. Rows of merchandise hang neatly on racks, while a shop keeper stands near the front, his hands clasped in anticipation of a sale A brightly coloured room, each wall a different primary colour. Rows of merchandise hang neatly on racks, while a shop keeper stands near the front, his hands clasped in anticipation of a sale. [Try <bold>list<reset> to see what is for sale here]
short: <bgred><green>G$<reset> short: <bgred><green>G$<reset>
grid_coords: grid_coords:
x: 1 x: 1
@ -1894,7 +1894,7 @@
x: 3 x: 3
y: 2 y: 2
z: 0 z: 0
description: A store full of beeping gadgets locked in glass display cabinets, all of which seem to be focused around health and medicine. A tall male technician in a white lab coat stands behind a counter, his eyes following you hoping that you are going to buy something description: A store full of beeping gadgets locked in glass display cabinets, all of which seem to be focused around health and medicine. A tall male technician in a white lab coat stands behind a counter, his eyes following you hoping that you are going to buy something. [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: south - direction: south
stock_list: stock_list:
@ -1909,7 +1909,7 @@
x: 3 x: 3
y: 4 y: 4
z: 0 z: 0
description: A shop that looks like it once helped people camp for fun, its clientele now days are probably more focused on just surviving! A weary looking woman with calloused, grease covered hands paces the shop floor, prepared to spring on you and hype up whatever merchandise you look at as the best thing ever description: A shop that looks like it once helped people camp for fun, its clientele now days are probably more focused on just surviving! A weary looking woman with calloused, grease covered hands paces the shop floor, prepared to spring on you and hype up whatever merchandise you look at as the best thing ever. [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: north - direction: north
stock_list: stock_list:
@ -2382,7 +2382,7 @@
x: 7 x: 7
y: 8 y: 8
z: 0 z: 0
description: This seems to be some kind of security shop, selling locks from super high-tech to primitive. Behind a counter sits a grizzled old man, who appears eager to sell you something description: This seems to be some kind of security shop, selling locks from super high-tech to primitive. Behind a counter sits a grizzled old man, who appears eager to sell you something. [Try <bold>list<reset> to see what is for sale here]
exits: exits:
- direction: northwest - direction: northwest
stock_list: stock_list:

View File

@ -1024,7 +1024,7 @@
x: 9 x: 9
y: 1 y: 1
z: -3 z: -3
description: A large room that has been carved into the bedrock, its walls, floor formed of grey stone. A gentle fresh breeze blows in through cracks in the rock. The room is dimly lit by yellow glowing light bulbs, suspended by cables, and apparently powered by cables that snake across the stone ceiling and up through a hole in the rock. Some of the cables snake down to power points on the wall. The room is stacked with unlabelled crates. In the northeast corner of the room, a woman sits in an office chair behind a basic wooden desk. Above her head, afixed to the wall, is a banner that says "Welcome the Josephine's Shop!" description: A large room that has been carved into the bedrock, its walls, floor formed of grey stone. A gentle fresh breeze blows in through cracks in the rock. The room is dimly lit by yellow glowing light bulbs, suspended by cables, and apparently powered by cables that snake across the stone ceiling and up through a hole in the rock. Some of the cables snake down to power points on the wall. The room is stacked with unlabelled crates. In the northeast corner of the room, a woman sits in an office chair behind a basic wooden desk. Above her head, afixed to the wall, is a banner that says "Welcome the Josephine's Shop!". [Try <bold>list<reset> to see what is for sale here]
has_power: true has_power: true
exits: exits:
- direction: south - direction: south