Allow attacks to be started.

This commit is contained in:
Condorra 2023-01-15 17:30:23 +11:00
parent 8b0a1490db
commit 5d3c8bc0aa
12 changed files with 294 additions and 514 deletions

12
Cargo.lock generated
View File

@ -48,6 +48,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "async-recursion"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.60" version = "0.1.60"
@ -111,6 +122,7 @@ name = "blastmud_game"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ansi", "ansi",
"async-recursion",
"async-trait", "async-trait",
"base64 0.20.0", "base64 0.20.0",
"bcrypt", "bcrypt",

View File

@ -35,3 +35,4 @@ validator = "0.16.0"
itertools = "0.10.5" itertools = "0.10.5"
once_cell = "1.16.0" once_cell = "1.16.0"
rand = "0.8.5" rand = "0.8.5"
async-recursion = "1.0.0"

View File

@ -285,7 +285,8 @@ impl DBTrans {
// Only copy more permanent fields, others are supposed to change over time and shouldn't // Only copy more permanent fields, others are supposed to change over time and shouldn't
// be reset on restart. // be reset on restart.
for to_copy in ["display", "display_less_explicit", "details", "details_less_explicit", for to_copy in ["display", "display_less_explicit", "details", "details_less_explicit",
"total_xp", "total_stats", "total_skills", "pronouns", "flags"] { "total_xp", "total_stats", "total_skills", "pronouns", "flags",
"sex", "is_challenge_attack_only"] {
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));
var_id += 1; var_id += 1;

View File

@ -53,6 +53,14 @@ pub fn pluralise(input: &str) -> String {
input.to_owned() + "s" input.to_owned() + "s"
} }
pub fn caps_first(inp: &str) -> String {
if inp.is_empty() {
"".to_string()
} else {
inp[0..1].to_uppercase() + &inp[1..]
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test] #[test]
@ -76,4 +84,16 @@ mod test {
assert_eq!(super::pluralise(word), plural); assert_eq!(super::pluralise(word), plural);
} }
} }
#[test]
fn caps_first_works() {
for (inp, outp) in vec!(
("", ""),
("cat", "Cat"),
("Cat", "Cat"),
("hello world", "Hello world"),
) {
assert_eq!(super::caps_first(inp), outp);
}
}
} }

View File

@ -10,6 +10,7 @@ use once_cell::sync::OnceCell;
use std::sync::Arc; use std::sync::Arc;
mod agree; mod agree;
pub mod attack;
mod describe; mod describe;
mod help; mod help;
mod ignore; mod ignore;
@ -96,13 +97,20 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
"down" => movement::VERB, "down" => movement::VERB,
// Other commands (alphabetical except aliases grouped): // Other commands (alphabetical except aliases grouped):
"attack" => attack::VERB,
"kill" => attack::VERB,
"k" => attack::VERB,
"describe" => describe::VERB, "describe" => describe::VERB,
"l" => look::VERB, "l" => look::VERB,
"look" => look::VERB, "look" => look::VERB,
"read" => look::VERB, "read" => look::VERB,
"lmap" => map::VERB, "lmap" => map::VERB,
"\'" => say::VERB, "\'" => say::VERB,
"say" => say::VERB, "say" => say::VERB,
"-" => whisper::VERB, "-" => whisper::VERB,
"whisper" => whisper::VERB, "whisper" => whisper::VERB,
"tell" => whisper::VERB, "tell" => whisper::VERB,

View File

@ -0,0 +1,105 @@
use super::{VerbContext, UserVerb, UserVerbRef, UResult, user_error,
get_player_item_or_fail, search_item_for_user};
use async_trait::async_trait;
use ansi::ansi;
use crate::services::broadcast_to_room;
use crate::db::{DBTrans, ItemSearchParams};
use crate::models::{item::{Item, LocationActionType, Subattack}};
use async_recursion::async_recursion;
#[async_recursion]
async fn start_attack(trans: &DBTrans, by_whom: &Item, to_whom: &Item) -> UResult<()> {
let mut msg_exp = String::new();
let mut msg_nonexp = String::new();
let mut verb: String = "attacks".to_string();
match by_whom.action_type {
LocationActionType::Sitting | LocationActionType::Reclining => {
msg_exp.push_str(&format!(ansi!("{} stands up.\n"), &by_whom.display));
msg_nonexp.push_str(&format!(ansi!("{} stands up.\n"),
by_whom.display_less_explicit.as_ref().unwrap_or(&by_whom.display)));
},
LocationActionType::Attacking(_) => {
match by_whom.active_combat.as_ref().and_then(|ac| ac.attacking.as_ref().and_then(|s| s.split_once("/"))) {
Some((cur_type, cur_code)) if cur_type == to_whom.item_type && cur_code == to_whom.item_code =>
user_error(format!("You're already attacking {}!", to_whom.pronouns.object))?,
Some((cur_type, cur_code)) => {
if let Some(cur_item_arc) = trans.find_item_by_type_code(cur_type, cur_code).await? {
let mut cur_item = (*cur_item_arc).clone();
if let Some(ac) = cur_item.active_combat.as_mut() {
let old_attacker = format!("{}/{}", by_whom.item_type, by_whom.item_code);
ac.attacked_by.retain(|v| v != &old_attacker);
trans.save_item_model(&cur_item).await?;
}
}
}
_ => {}
}
verb = "refocuses ".to_string() + &by_whom.pronouns.possessive + " attacks on";
},
_ => {}
}
msg_exp.push_str(&format!(
ansi!("<red>{} {} {}.<reset>\n"),
&by_whom.display_for_sentence(true, 1, true),
verb,
&to_whom.display_for_sentence(true, 1, false))
);
msg_nonexp.push_str(&format!(
ansi!("<red>{} {} {}.<reset>\n"),
&by_whom.display_for_sentence(false, 1, true),
verb,
&to_whom.display_for_sentence(false, 1, false))
);
broadcast_to_room(trans, &by_whom.location, None, &msg_exp, Some(msg_nonexp.as_str())).await?;
let mut by_whom_for_update = by_whom.clone();
by_whom_for_update.active_combat.get_or_insert_with(|| Default::default()).attacking =
Some(format!("{}/{}",
&to_whom.item_type, &to_whom.item_code));
by_whom_for_update.action_type = LocationActionType::Attacking(Subattack::Normal);
let mut to_whom_for_update = to_whom.clone();
to_whom_for_update.active_combat.get_or_insert_with(|| Default::default()).attacked_by.push(
format!("{}/{}",
&by_whom.item_type, &by_whom.item_code)
);
trans.save_item_model(&by_whom_for_update).await?;
trans.save_item_model(&to_whom_for_update).await?;
// Auto-counterattack if victim isn't busy.
if to_whom_for_update.active_combat.as_ref().and_then(|ac| ac.attacking.as_ref()) == None {
start_attack(trans, &to_whom_for_update, &by_whom_for_update).await?;
}
Ok(())
}
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 attack_whom = search_item_for_user(ctx, &ItemSearchParams {
include_loc_contents: true,
..ItemSearchParams::base(&player_item, remaining)
}).await?;
match attack_whom.item_type.as_str() {
"npc" => {}
"player" => {},
_ => user_error("Only characters (players / NPCs) accept whispers".to_string())?
}
if attack_whom.item_code == player_item.item_code && attack_whom.item_type == player_item.item_type {
user_error("That's you, silly!".to_string())?
}
if attack_whom.is_challenge_attack_only {
// Add challenge check here.
user_error(ansi!("<blue>Your wristpad vibrates and blocks you from doing that.<reset> You get a feeling that while the empire might be gone, the system to stop subjects with working wristpads from fighting each unless they have an accepted challenge very much functional. [Try <bold>help challenge<reset>]").to_string())?
}
start_attack(&ctx.trans, &player_item, &attack_whom).await
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;

View File

@ -7,7 +7,6 @@ use crate::models::{item::{Item, LocationActionType, Subattack, ItemFlag}};
use crate::static_content::room::{self, Direction}; use crate::static_content::room::{self, Direction};
use itertools::Itertools; use itertools::Itertools;
use std::sync::Arc; use std::sync::Arc;
use crate::language::pluralise;
pub fn render_map(room: &room::Room, width: usize, height: usize) -> String { pub fn render_map(room: &room::Room, width: usize, height: usize) -> String {
let mut buf = String::new(); let mut buf = String::new();
@ -43,7 +42,7 @@ pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult
ctx.trans.queue_for_session( ctx.trans.queue_for_session(
ctx.session, ctx.session,
Some(&format!("{}\n{}\n", Some(&format!("{}\n{}\n",
item.display_for_session(&ctx.session_dat), &item.display_for_session(&ctx.session_dat),
item.details_for_session(&ctx.session_dat).unwrap_or("") item.details_for_session(&ctx.session_dat).unwrap_or("")
)) ))
).await?; ).await?;
@ -85,7 +84,7 @@ async fn list_item_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> URe
let all_groups: Vec<Vec<&Arc<Item>>> = items let all_groups: Vec<Vec<&Arc<Item>>> = items
.iter() .iter()
.group_by(|i| &i.display) .group_by(|i| i.display_for_sentence(true, 1, false))
.into_iter() .into_iter()
.map(|(_, g)|g.collect::<Vec<&Arc<Item>>>()) .map(|(_, g)|g.collect::<Vec<&Arc<Item>>>())
.collect::<Vec<Vec<&Arc<Item>>>>(); .collect::<Vec<Vec<&Arc<Item>>>>();
@ -94,16 +93,8 @@ async fn list_item_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> URe
let head = &group_items[0]; let head = &group_items[0];
let is_creature = head.item_type == "player" || head.item_type.starts_with("npc"); let is_creature = head.item_type == "player" || head.item_type.starts_with("npc");
buf.push(' '); buf.push(' ');
if group_items.len() > 1 { buf.push_str(&head.display_for_sentence(!ctx.session_dat.less_explicit_mode,
buf.push_str(&format!("{} ", group_items.len())) group_items.len(), true));
} else if !head.pronouns.is_proper {
buf.push_str("A ");
}
let mut disp = head.display_for_session(&ctx.session_dat).to_owned();
if group_items.len() > 1 {
disp = pluralise(&disp);
}
buf.push_str(&disp);
buf.push_str(if group_items.len() > 1 { " are " } else { " is "}); buf.push_str(if group_items.len() > 1 { " are " } else { " is "});
match head.action_type { match head.action_type {
LocationActionType::Sitting => buf.push_str("sitting "), LocationActionType::Sitting => buf.push_str("sitting "),
@ -129,7 +120,7 @@ async fn list_item_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> URe
match ctx.trans.find_item_by_type_code(ttype, tcode).await? { match ctx.trans.find_item_by_type_code(ttype, tcode).await? {
None => buf.push_str("someone"), None => buf.push_str("someone"),
Some(it) => buf.push_str( Some(it) => buf.push_str(
it.display_for_session(&ctx.session_dat) &it.display_for_session(&ctx.session_dat)
) )
} }
} }

View File

@ -26,12 +26,12 @@ pub async fn say_to_room<'l>(
} }
let msg_exp = format!( let msg_exp = format!(
ansi!("<yellow>{} says: <reset><bold>\"{}\"<reset>\n"), ansi!("<yellow>{} says: <reset><bold>\"{}\"<reset>\n"),
from_item.display, from_item.display_for_sentence(true, 1, true),
say_what say_what
); );
let msg_lessexp = format!( let msg_lessexp = format!(
ansi!("<yellow>{} says: <reset><bold>\"{}\"<reset>\n"), ansi!("<yellow>{} says: <reset><bold>\"{}\"<reset>\n"),
from_item.display_less_explicit.as_ref().unwrap_or(&from_item.display), from_item.display_for_sentence(false, 1, true),
say_what say_what
); );

View File

@ -1,5 +1,6 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::language;
use super::{user::{SkillType, StatType}, session::Session}; use super::{user::{SkillType, StatType}, session::Session};
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
@ -97,7 +98,7 @@ pub enum LocationActionType {
Reclining, Reclining,
Worn, // Clothing etc... Worn, // Clothing etc...
Wielded, Wielded,
Attacking(Subattack), Attacking(Subattack)
} }
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
@ -112,6 +113,22 @@ pub enum ItemFlag {
NoSeeContents NoSeeContents
} }
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[serde(default)]
pub struct ActiveCombat {
pub attacking: Option<String>,
pub attacked_by: Vec<String>
}
impl Default for ActiveCombat {
fn default() -> Self {
Self {
attacking: None,
attacked_by: vec!()
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[serde(default)] #[serde(default)]
pub struct Item { pub struct Item {
@ -125,6 +142,8 @@ pub struct Item {
pub action_type: LocationActionType, pub action_type: LocationActionType,
pub presence_target: Option<String>, // e.g. what are they sitting on. pub presence_target: Option<String>, // e.g. what are they sitting on.
pub is_static: bool, pub is_static: bool,
pub is_dead: bool,
pub is_challenge_attack_only: bool,
pub total_xp: u64, pub total_xp: u64,
pub total_stats: BTreeMap<StatType, u16>, pub total_stats: BTreeMap<StatType, u16>,
@ -133,12 +152,41 @@ pub struct Item {
pub pronouns: Pronouns, pub pronouns: Pronouns,
pub flags: Vec<ItemFlag>, pub flags: Vec<ItemFlag>,
pub sex: Option<Sex>, pub sex: Option<Sex>,
pub active_combat: Option<ActiveCombat>,
} }
impl Item { impl Item {
pub fn display_for_session<'l>(self: &'l Self, session: &Session) -> &'l str { pub fn display_for_sentence(&self, explicit_ok: bool, pluralise: usize, caps: bool) -> String {
session.explicit_if_allowed(&self.display, let mut buf = String::new();
self.display_less_explicit.as_ref().map(String::as_str)) if self.is_dead {
if pluralise > 1 {
buf.push_str("the bodies of ");
} else {
buf.push_str("the body of ");
}
}
if !self.pronouns.is_proper && pluralise == 1 {
buf.push_str("a ");
}
if pluralise > 1 {
buf.push_str(&format!("{} ", pluralise));
}
let singular = if explicit_ok { &self.display } else {
self.display_less_explicit.as_ref().unwrap_or(&self.display) };
if pluralise > 1 {
buf.push_str(&language::pluralise(singular));
} else {
buf.push_str(singular);
}
if caps {
language::caps_first(&buf)
} else {
buf
}
}
pub fn display_for_session<'l>(self: &'l Self, session: &Session) -> String {
self.display_for_sentence(!session.less_explicit_mode, 1, false)
} }
pub fn details_for_session<'l>(self: &'l Self, session: &Session) -> Option<&'l str>{ pub fn details_for_session<'l>(self: &'l Self, session: &Session) -> Option<&'l str>{
@ -154,7 +202,7 @@ impl Item {
impl Default for Item { impl Default for Item {
fn default() -> Self { fn default() -> Self {
Item { Self {
item_code: "unset".to_owned(), item_code: "unset".to_owned(),
item_type: "unset".to_owned(), item_type: "unset".to_owned(),
display: "Item".to_owned(), display: "Item".to_owned(),
@ -165,13 +213,16 @@ impl Default for Item {
action_type: LocationActionType::Normal, action_type: LocationActionType::Normal,
presence_target: None, presence_target: None,
is_static: false, is_static: false,
is_dead: false,
is_challenge_attack_only: true,
total_xp: 0, total_xp: 0,
total_stats: BTreeMap::new(), total_stats: BTreeMap::new(),
total_skills: BTreeMap::new(), total_skills: BTreeMap::new(),
temporary_buffs: Vec::new(), temporary_buffs: Vec::new(),
pronouns: Pronouns::default_inanimate(), pronouns: Pronouns::default_inanimate(),
flags: vec!(), flags: vec!(),
sex: None sex: None,
active_combat: Some(Default::default()),
} }
} }
} }

View File

@ -52,7 +52,8 @@ pub struct NPC {
pub description: &'static str, pub description: &'static str,
pub spawn_location: &'static str, pub spawn_location: &'static str,
pub message_handler: Option<&'static (dyn NPCMessageHandler + Sync + Send)>, pub message_handler: Option<&'static (dyn NPCMessageHandler + Sync + Send)>,
pub says: Vec<NPCSayInfo> pub says: Vec<NPCSayInfo>,
pub attackable: bool
} }
impl Default for NPC { impl Default for NPC {
@ -64,7 +65,8 @@ impl Default for NPC {
description: "default", description: "default",
spawn_location: "default", spawn_location: "default",
message_handler: None, message_handler: None,
says: vec!() says: vec!(),
attackable: false
} }
} }
} }
@ -121,6 +123,7 @@ pub fn npc_static_items() -> Box<dyn Iterator<Item = StaticItem>> {
location: c.spawn_location.to_owned(), location: c.spawn_location.to_owned(),
is_static: true, is_static: true,
pronouns: c.pronouns.clone(), pronouns: c.pronouns.clone(),
is_challenge_attack_only: !c.attackable,
..Item::default() ..Item::default()
}) })
})) }))

View File

@ -1,487 +1,81 @@
use super::NPC; use super::NPC;
use crate::models::item::Pronouns; use crate::models::item::Pronouns;
macro_rules! dog {
($code:expr, $adj:expr, $spawn: expr) => {
NPC {
code: concat!("melbs_dog_", $code),
name: concat!($adj, " dog"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
attackable: true,
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: concat!("room/", $spawn),
..Default::default()
}
}
}
pub fn npc_list() -> Vec<NPC> { pub fn npc_list() -> Vec<NPC> {
vec!( vec!(
NPC { dog!("1", "smelly black", "melbs_williamsst_80"),
code: "melbs_dog_1", dog!("2", "howling black", "melbs_swanstonst_100"),
name: "feral black dog", dog!("3", "smelly black", "melbs_collinsst_160"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("4", "growling light brown", "melbs_kingst_40"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("5", "ferocious white", "melbs_swanstonst_110"),
spawn_location: "room/melbs_elizabethst_collinsst", dog!("6", "mangy grey", "melbs_kingst_30"),
..Default::default() dog!("7", "reeking light brown", "melbs_flindersst_210"),
}, dog!("8", "feral brown", "melbs_elizabethst_40"),
NPC { dog!("9", "reeking grey", "melbs_collinsst_190"),
code: "melbs_dog_2", dog!("10", "ferocious grey", "melbs_kingst_60"),
name: "howling black dog", dog!("11", "howling brown", "melbs_collinsst_140"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("12", "feral black", "melbs_flindersst_160"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("13", "smelly grey", "melbs_queenst_80"),
spawn_location: "room/melbs_collinsst_100", dog!("14", "howling grey", "melbs_kingst_70"),
..Default::default() dog!("15", "smelly grey", "melbs_flindersst_110"),
}, dog!("16", "feral black", "melbs_queenst_latrobest"),
NPC { dog!("17", "howling grey", "melbs_swanstonst_110"),
code: "melbs_dog_3", dog!("18", "mangy grey", "melbs_swanstonst_80"),
name: "ferocious white dog", dog!("19", "reeking light brown", "melbs_latrobest_180"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("20", "smelly white", "melbs_flindersst_130"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("21", "reeking grey", "melbs_flindersst_180"),
spawn_location: "room/melbs_elizabethst_lonsdalest", dog!("22", "growling brown", "melbs_williamsst_80"),
..Default::default() dog!("23", "howling black", "melbs_lonsdalest_100"),
}, dog!("24", "growling grey", "melbs_latrobest_140"),
NPC { dog!("25", "howling light brown", "melbs_queenst_30"),
code: "melbs_dog_4", dog!("26", "howling black", "melbs_latrobest_160"),
name: "mangy grey dog", dog!("27", "howling grey", "melbs_collinsst_170"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("28", "growling brown", "melbs_elizabethst_latrobest"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("29", "mangy brown", "melbs_kingst_70"),
spawn_location: "room/melbs_collinsst_160", dog!("30", "growling black", "melbs_swanstonst_120"),
..Default::default() dog!("31", "reeking light brown", "melbs_latrobest_130"),
}, dog!("32", "howling white", "melbs_bourkest_160"),
NPC { dog!("33", "growling black", "melbs_elizabethst_50"),
code: "melbs_dog_5", dog!("34", "mangy black", "melbs_swanstonst_110"),
name: "howling grey dog", dog!("35", "ferocious grey", "melbs_collinsst_100"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("36", "mangy grey", "melbs_flindersst_100"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("37", "growling brown", "melbs_swanstonst_flindersst"),
spawn_location: "room/melbs_lonsdalest_100", dog!("38", "mangy light brown", "melbs_lonsdalest_200"),
..Default::default() dog!("39", "howling light brown", "melbs_flindersst_210"),
}, dog!("40", "mangy light brown", "melbs_queenst_flindersst"),
NPC { dog!("41", "reeking white", "melbs_collinsst_130"),
code: "melbs_dog_6", dog!("42", "growling light brown", "melbs_lonsdalest_130"),
name: "reeking black dog", dog!("43", "reeking light brown", "melbs_elizabethst_70"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("44", "mangy brown", "melbs_swanstonst_30"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("45", "growling light brown", "melbs_swanstonst_lonsdalest"),
spawn_location: "room/melbs_lonsdalest_120", dog!("46", "smelly brown", "melbs_queenst_lonsdalest"),
..Default::default() dog!("47", "growling white", "melbs_elizabethst_bourkest"),
}, dog!("48", "feral brown", "melbs_collinsst_140"),
NPC { dog!("49", "ferocious black", "melbs_lonsdalest_150"),
code: "melbs_dog_7", dog!("50", "mangy grey", "melbs_kingst_collinsst"),
name: "growling grey dog", dog!("51", "ferocious brown", "melbs_kingst_120"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("52", "growling white", "melbs_elizabethst_10"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.", dog!("53", "ferocious white", "melbs_lonsdalest_190"),
spawn_location: "room/melbs_williamsst_20", dog!("54", "smelly grey", "melbs_kingst_collinsst"),
..Default::default() dog!("55", "reeking light brown", "melbs_elizabethst_90"),
}, dog!("56", "reeking grey", "melbs_swanstonst_20"),
NPC { dog!("57", "feral brown", "melbs_flindersst_180"),
code: "melbs_dog_8", dog!("58", "reeking brown", "melbs_bourkest_130"),
name: "mangy black dog", dog!("59", "mangy light brown", "melbs_queenst_50"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() }, dog!("60", "growling white", "melbs_kingst_110"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_bourkest_120",
..Default::default()
},
NPC {
code: "melbs_dog_9",
name: "howling black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_collinsst_100",
..Default::default()
},
NPC {
code: "melbs_dog_10",
name: "howling brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_flindersst_130",
..Default::default()
},
NPC {
code: "melbs_dog_11",
name: "ferocious black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_flindersst_100",
..Default::default()
},
NPC {
code: "melbs_dog_12",
name: "mangy grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_90",
..Default::default()
},
NPC {
code: "melbs_dog_13",
name: "reeking light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_lonsdalest_140",
..Default::default()
},
NPC {
code: "melbs_dog_14",
name: "smelly black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_20",
..Default::default()
},
NPC {
code: "melbs_dog_15",
name: "growling light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_10",
..Default::default()
},
NPC {
code: "melbs_dog_16",
name: "howling brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_swanstonst_flindersst",
..Default::default()
},
NPC {
code: "melbs_dog_17",
name: "feral brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_kingst_latrobest",
..Default::default()
},
NPC {
code: "melbs_dog_18",
name: "smelly grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_kingst_40",
..Default::default()
},
NPC {
code: "melbs_dog_19",
name: "feral black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_60",
..Default::default()
},
NPC {
code: "melbs_dog_20",
name: "smelly white dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_swanstonst_120",
..Default::default()
},
NPC {
code: "melbs_dog_21",
name: "growling white dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_90",
..Default::default()
},
NPC {
code: "melbs_dog_22",
name: "feral light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_lonsdalest_150",
..Default::default()
},
NPC {
code: "melbs_dog_23",
name: "mangy grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_lonsdalest_150",
..Default::default()
},
NPC {
code: "melbs_dog_24",
name: "reeking grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_100",
..Default::default()
},
NPC {
code: "melbs_dog_25",
name: "smelly grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_bourkest_180",
..Default::default()
},
NPC {
code: "melbs_dog_26",
name: "growling light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_100",
..Default::default()
},
NPC {
code: "melbs_dog_27",
name: "feral light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_10",
..Default::default()
},
NPC {
code: "melbs_dog_28",
name: "feral grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_lonsdalest_160",
..Default::default()
},
NPC {
code: "melbs_dog_29",
name: "reeking brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_bourkest_110",
..Default::default()
},
NPC {
code: "melbs_dog_30",
name: "howling grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_80",
..Default::default()
},
NPC {
code: "melbs_dog_31",
name: "howling brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_bourkest_160",
..Default::default()
},
NPC {
code: "melbs_dog_32",
name: "feral black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_swanstonst_collinsst",
..Default::default()
},
NPC {
code: "melbs_dog_33",
name: "reeking brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_kingst_flinderst",
..Default::default()
},
NPC {
code: "melbs_dog_34",
name: "reeking white dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_100",
..Default::default()
},
NPC {
code: "melbs_dog_35",
name: "growling light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_110",
..Default::default()
},
NPC {
code: "melbs_dog_36",
name: "reeking black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_williamsst_90",
..Default::default()
},
NPC {
code: "melbs_dog_37",
name: "growling black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_latrobesst_200",
..Default::default()
},
NPC {
code: "melbs_dog_38",
name: "feral black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_90",
..Default::default()
},
NPC {
code: "melbs_dog_39",
name: "mangy black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_40",
..Default::default()
},
NPC {
code: "melbs_dog_40",
name: "growling white dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_williamsst_40",
..Default::default()
},
NPC {
code: "melbs_dog_41",
name: "reeking grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_latrobest",
..Default::default()
},
NPC {
code: "melbs_dog_42",
name: "mangy grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_flindersst_210",
..Default::default()
},
NPC {
code: "melbs_dog_43",
name: "ferocious brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_kingst_latrobest",
..Default::default()
},
NPC {
code: "melbs_dog_44",
name: "ferocious light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_collinsst_120",
..Default::default()
},
NPC {
code: "melbs_dog_45",
name: "ferocious light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_swanstonst_lonsdalest",
..Default::default()
},
NPC {
code: "melbs_dog_46",
name: "smelly grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_williamsst_30",
..Default::default()
},
NPC {
code: "melbs_dog_47",
name: "growling grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_lonsdalest_100",
..Default::default()
},
NPC {
code: "melbs_dog_48",
name: "ferocious brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_latrobest_210",
..Default::default()
},
NPC {
code: "melbs_dog_49",
name: "reeking brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_latrobest_140",
..Default::default()
},
NPC {
code: "melbs_dog_50",
name: "howling grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_swanstonst_110",
..Default::default()
},
NPC {
code: "melbs_dog_51",
name: "howling black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_elizabethst_flindersst",
..Default::default()
},
NPC {
code: "melbs_dog_52",
name: "growling light brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_flindersst_120",
..Default::default()
},
NPC {
code: "melbs_dog_53",
name: "ferocious black dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_110",
..Default::default()
},
NPC {
code: "melbs_dog_54",
name: "growling grey dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_flindersst_210",
..Default::default()
},
NPC {
code: "melbs_dog_55",
name: "reeking brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_williamsst_60",
..Default::default()
},
NPC {
code: "melbs_dog_56",
name: "ferocious white dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_queenst_lonsdalest",
..Default::default()
},
NPC {
code: "melbs_dog_57",
name: "smelly brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_swanstonst_lonsdalest",
..Default::default()
},
NPC {
code: "melbs_dog_58",
name: "mangy white dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_williamsst_bourkest",
..Default::default()
},
NPC {
code: "melbs_dog_59",
name: "mangy brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_latrobest_170",
..Default::default()
},
NPC {
code: "melbs_dog_60",
name: "growling brown dog",
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
spawn_location: "room/melbs_williamsst_110",
..Default::default()
},
) )
} }

View File

@ -156,14 +156,8 @@ rooms = [
character :: Int -> T.Text -> T.Text -> T.Text character :: Int -> T.Text -> T.Text -> T.Text
character id adjective room = character id adjective room =
" NPC {\n\ " dog!(\"" <> (T.pack $ show id) <> "\", \"" <> adjective <>
\ code: \"melbs_dog_" <> (T.pack $ show id) <> "\",\n\ "\", \"" <> room <> "\"),\n"
\ name: \"" <> adjective <> " dog\",\n\
\ proper_noun: false,\n\
\ description: \"A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.\",\n\
\ spawn_location: \"room/" <> room <> "\",\n\
\ ..Default::default()\n\
\ },\n"
chooseFromList :: [a] -> IO a chooseFromList :: [a] -> IO a
chooseFromList l = (l!!) <$> randomRIO (0, length l - 1) chooseFromList l = (l!!) <$> randomRIO (0, length l - 1)