Document all shops and registered user commands
Include tests to keep it that way.
This commit is contained in:
parent
5c2462a396
commit
851e3d8d1e
25
Cargo.lock
generated
25
Cargo.lock
generated
@ -227,7 +227,6 @@ dependencies = [
|
||||
"nom",
|
||||
"once_cell",
|
||||
"ouroboros",
|
||||
"phf",
|
||||
"rand",
|
||||
"rand_distr",
|
||||
"ring",
|
||||
@ -1246,33 +1245,9 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"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]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
|
@ -26,7 +26,6 @@ tokio-stream = "0.1.14"
|
||||
tokio-util = { version = "0.7.10", features = ["codec"] }
|
||||
uuid = { version = "1.7.0", features = ["v4", "serde", "rng"] }
|
||||
serde_json = "1.0.114"
|
||||
phf = { version = "0.11.2", features = ["macros"] }
|
||||
async-trait = "0.1.77"
|
||||
nom = "7.1.3"
|
||||
ouroboros = "0.18.3"
|
||||
|
@ -14,7 +14,7 @@ use async_trait::async_trait;
|
||||
use log::warn;
|
||||
use mockall_double::double;
|
||||
use once_cell::sync::OnceCell;
|
||||
use phf::phf_map;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
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. */
|
||||
pub type UserVerbRef = &'static (dyn UserVerb + Sync + Send);
|
||||
type UserVerbRegistry = phf::Map<&'static str, UserVerbRef>;
|
||||
|
||||
static ALWAYS_AVAILABLE_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"" => ignore::VERB,
|
||||
"help" => help::VERB,
|
||||
"quit" => quit::VERB,
|
||||
};
|
||||
pub fn always_available_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
|
||||
static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
|
||||
V.get_or_init(|| {
|
||||
vec![
|
||||
("", ignore::VERB),
|
||||
("help", help::VERB),
|
||||
("quit", quit::VERB),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
static UNREGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"agree" => agree::VERB,
|
||||
"connect" => login::VERB,
|
||||
"login" => login::VERB,
|
||||
"register" => register::VERB,
|
||||
};
|
||||
pub fn unregistered_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
|
||||
static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
|
||||
V.get_or_init(|| {
|
||||
vec![
|
||||
("agree", agree::VERB),
|
||||
("connect", login::VERB),
|
||||
("login", login::VERB),
|
||||
("register", register::VERB),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
// Movement comments first:
|
||||
"north" => movement::VERB,
|
||||
"n" => movement::VERB,
|
||||
"northeast" => movement::VERB,
|
||||
"ne" => movement::VERB,
|
||||
"east" => movement::VERB,
|
||||
"e" => movement::VERB,
|
||||
"southeast" => movement::VERB,
|
||||
"se" => movement::VERB,
|
||||
"south" => movement::VERB,
|
||||
"s" => movement::VERB,
|
||||
"southwest" => movement::VERB,
|
||||
"sw" => movement::VERB,
|
||||
"west" => movement::VERB,
|
||||
"w" => movement::VERB,
|
||||
"northwest" => movement::VERB,
|
||||
"nw" => movement::VERB,
|
||||
"up" => movement::VERB,
|
||||
"down" => movement::VERB,
|
||||
"in" => movement::VERB,
|
||||
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:
|
||||
("north", movement::VERB),
|
||||
("n", movement::VERB),
|
||||
("northeast", movement::VERB),
|
||||
("ne", movement::VERB),
|
||||
("east", movement::VERB),
|
||||
("e", movement::VERB),
|
||||
("southeast", movement::VERB),
|
||||
("se", movement::VERB),
|
||||
("south", movement::VERB),
|
||||
("s", movement::VERB),
|
||||
("southwest", movement::VERB),
|
||||
("sw", movement::VERB),
|
||||
("west", movement::VERB),
|
||||
("w", movement::VERB),
|
||||
("northwest", movement::VERB),
|
||||
("nw", movement::VERB),
|
||||
("up", movement::VERB),
|
||||
("down", movement::VERB),
|
||||
("in", movement::VERB),
|
||||
// Other commands (alphabetical except aliases grouped):
|
||||
("allow", 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()
|
||||
})
|
||||
}
|
||||
|
||||
// Other commands (alphabetical except aliases grouped):
|
||||
"allow" => 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,
|
||||
};
|
||||
|
||||
static STAFF_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"staff_invincible" => invincible::VERB,
|
||||
"staff_reset_spawns" => reset_spawns::VERB,
|
||||
"staff_show" => staff_show::VERB,
|
||||
};
|
||||
pub fn staff_commands() -> &'static BTreeMap<&'static str, UserVerbRef> {
|
||||
static V: OnceCell<BTreeMap<&'static str, UserVerbRef>> = OnceCell::new();
|
||||
V.get_or_init(|| {
|
||||
vec![
|
||||
("staff_invincible", invincible::VERB),
|
||||
("staff_reset_spawns", reset_spawns::VERB),
|
||||
("staff_show", staff_show::VERB),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
None => {
|
||||
result = result.or_else(|| UNREGISTERED_COMMANDS.get(cmd));
|
||||
result = result.or_else(|| unregistered_commands().get(cmd));
|
||||
}
|
||||
Some(user_dat) => {
|
||||
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) {
|
||||
result = result.or_else(|| STAFF_COMMANDS.get(cmd));
|
||||
result = result.or_else(|| staff_commands().get(cmd));
|
||||
}
|
||||
} else if cmd == "agree" {
|
||||
result = Some(&agree::VERB);
|
||||
|
@ -65,6 +65,10 @@ pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::message_handler::user_commands::registered_commands;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@ -81,4 +85,18 @@ mod tests {
|
||||
fn always_help_ok() {
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
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
|
||||
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>).
|
||||
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.
|
||||
combat: >-
|
||||
n: *movement
|
||||
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.
|
||||
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>).
|
||||
@ -46,6 +71,20 @@ combat: >-
|
||||
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
|
||||
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: >-
|
||||
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
|
||||
@ -59,40 +98,55 @@ skills: >-
|
||||
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
|
||||
your stats, and any temporary buffs or debuffs caused by your character's state.
|
||||
information: |-
|
||||
information: &info |-
|
||||
Commands to examine the world:
|
||||
<bold>look<reset> (or <bold>l<reset>) to look around - follow it with an exit or
|
||||
a character / item name for detail on that.
|
||||
<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 |-
|
||||
Use:
|
||||
<bold>'<reset>message to send message to the room.
|
||||
<bold>-<reset>user message to whisper to someone.
|
||||
<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
|
||||
whisper: *talk
|
||||
page: *talk
|
||||
reply: *talk
|
||||
"'": *talk
|
||||
"-": *talk
|
||||
rep: *talk
|
||||
repl: *talk
|
||||
tell: *talk
|
||||
p: *talk
|
||||
pg: *talk
|
||||
possessions: &possessions |-
|
||||
Use:
|
||||
<bold>get<reset> item to pick something up from the ground.
|
||||
<bold>drop<reset> item to drop something from your inventory.
|
||||
<bold>inv<reset> to see what you are holding.
|
||||
<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>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.
|
||||
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
|
||||
drop: *possessions
|
||||
i: *possessions
|
||||
inv: *possessions
|
||||
inventory: *possessions
|
||||
buy: *possessions
|
||||
sell: *possessions
|
||||
list: *possessions
|
||||
wield: *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).
|
||||
|
||||
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
|
||||
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.
|
||||
consent: *consent
|
||||
disallow: *consent
|
||||
corp: |-
|
||||
<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
|
||||
@ -193,6 +249,10 @@ corp: |-
|
||||
death - Deaths of players in the corp.
|
||||
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."
|
||||
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."
|
||||
"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."
|
||||
@ -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.
|
||||
get: |-
|
||||
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].
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
@ -890,6 +890,7 @@ pub async fn check_for_enter_action(ctx: &mut QueuedCommandContext<'_>) -> UResu
|
||||
mod test {
|
||||
use super::super::scavtable::scavtable_map;
|
||||
use super::*;
|
||||
use ansi::ansi;
|
||||
use itertools::Itertools;
|
||||
|
||||
#[test]
|
||||
@ -1002,4 +1003,16 @@ mod test {
|
||||
.collect::<Vec<String>>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@
|
||||
x: 2
|
||||
y: -2
|
||||
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:
|
||||
- direction: west
|
||||
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 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:
|
||||
- direction: west
|
||||
stock_list:
|
||||
@ -1511,7 +1511,7 @@
|
||||
|
||||
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:
|
||||
- direction: east
|
||||
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 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:
|
||||
- direction: west
|
||||
stock_list:
|
||||
@ -1737,7 +1737,7 @@
|
||||
code: oorans_testing
|
||||
name: OORANS Testing Centre
|
||||
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>
|
||||
grid_coords:
|
||||
x: 1
|
||||
@ -1752,7 +1752,7 @@
|
||||
code: oorans_gift
|
||||
name: OORANS Gift Shop
|
||||
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>
|
||||
grid_coords:
|
||||
x: 1
|
||||
@ -1894,7 +1894,7 @@
|
||||
x: 3
|
||||
y: 2
|
||||
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:
|
||||
- direction: south
|
||||
stock_list:
|
||||
@ -1909,7 +1909,7 @@
|
||||
x: 3
|
||||
y: 4
|
||||
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:
|
||||
- direction: north
|
||||
stock_list:
|
||||
@ -2382,7 +2382,7 @@
|
||||
x: 7
|
||||
y: 8
|
||||
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:
|
||||
- direction: northwest
|
||||
stock_list:
|
||||
|
@ -1024,7 +1024,7 @@
|
||||
x: 9
|
||||
y: 1
|
||||
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
|
||||
exits:
|
||||
- direction: south
|
||||
|
Loading…
Reference in New Issue
Block a user