Support player recloning + NPCs can wander and aggro.

This commit is contained in:
Condorra 2023-01-27 00:36:49 +11:00
parent 2d9fcf9850
commit a3ea381438
15 changed files with 397 additions and 572 deletions

View File

@ -15,6 +15,11 @@ pub struct Verb;
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?;
if player_item.is_dead {
user_error("It doesn't really seem fair, but you realise you won't be able to attack anyone while you're dead!".to_string())?;
}
let attack_whom = search_item_for_user(ctx, &ItemSearchParams {
include_loc_contents: true,
..ItemSearchParams::base(&player_item, remaining)

View File

@ -143,7 +143,10 @@ impl UserVerb for Verb {
let player_item = get_player_item_or_fail(ctx).await?;
let rem_trim = remaining.trim().to_lowercase();
let (heretype, herecode) = player_item.location.split_once("/").unwrap_or(("room", "repro_xv_chargen"));
let use_location = if player_item.is_dead { "room/repro_xv_respawn" } else {
&player_item.location
};
let (heretype, herecode) = use_location.split_once("/").unwrap_or(("room", "repro_xv_chargen"));
let item: Arc<Item> = if rem_trim == "" {
ctx.trans.find_item_by_type_code(heretype, herecode).await?
.ok_or_else(|| UserError("Sorry, that no longer exists".to_owned()))?

View File

@ -23,27 +23,27 @@ use crate::{
broadcast_to_room,
skills::skill_check_and_grind,
combat::stop_attacking,
combat::handle_resurrect,
}
};
use std::time;
pub async fn announce_move(trans: &DBTrans, character: &Item, leaving: &Item, arriving: &Item) -> DResult<()> {
let msg_leaving_exp = format!("{} departs towards {}\n", &character.display, &leaving.display);
let msg_leaving_exp = format!("{} departs towards {}\n",
&character.display_for_sentence(true, 1, true),
&arriving.display);
let msg_leaving_nonexp = format!("{} departs towards {}\n",
character.display_less_explicit
.as_ref()
.unwrap_or(&character.display),
character.display_for_sentence(true, 1, false),
arriving.display_less_explicit
.as_ref()
.unwrap_or(&arriving.display));
broadcast_to_room(trans, &format!("{}/{}", &leaving.item_type, &leaving.item_code),
None, &msg_leaving_exp, Some(&msg_leaving_nonexp)).await?;
let msg_arriving_exp = format!("{} arrives from {}\n", &character.display, &leaving.display);
let msg_arriving_exp = format!("{} arrives from {}\n", &character.display_for_sentence(true, 1, true),
&leaving.display);
let msg_arriving_nonexp = format!("{} arrives from {}\n",
character.display_less_explicit
.as_ref()
.unwrap_or(&character.display),
character.display_for_sentence(true, 1, false),
leaving.display_less_explicit
.as_ref()
.unwrap_or(&leaving.display));
@ -58,7 +58,15 @@ pub async fn attempt_move_immediate(
direction: &Direction,
mut player_ctx: Option<&mut VerbContext<'_>>
) -> UResult<()> {
let (heretype, herecode) = orig_mover.location.split_once("/").unwrap_or(("room", "repro_xv_chargen"));
let use_location = if orig_mover.is_dead {
if orig_mover.item_type != "player" {
user_error("Dead players don't move".to_owned())?;
}
"room/repro_xv_respawn"
} else {
&orig_mover.location
};
let (heretype, herecode) = use_location.split_once("/").unwrap_or(("room", "repro_xv_chargen"));
if heretype != "room" {
// Fix this when we have planes / boats / roomkits.
user_error("Navigating outside rooms not yet supported.".to_owned())?
@ -115,9 +123,9 @@ pub async fn attempt_move_immediate(
trans.queue_for_session(ctx.session,
Some(&format!("You successfully get away from {}\n",
&attacker_names_str))).await?;
for item in &attacker_items[..] {
stop_attacking(trans, &item, &mover).await?;
}
}
for item in &attacker_items[..] {
stop_attacking(trans, &item, &mover).await?;
}
} else {
if let Some(ctx) = player_ctx.as_ref() {
@ -131,9 +139,16 @@ pub async fn attempt_move_immediate(
}
}
if mover.is_dead {
if !handle_resurrect(trans, &mut mover).await? {
user_error("You couldn't be resurrected.".to_string())?;
}
}
mover.location = format!("{}/{}", "room", new_room.code);
mover.action_type = LocationActionType::Normal;
mover.active_combat = None;
trans.save_item_model(&mover).await?;
if let Some(ctx) = player_ctx {

View File

@ -54,6 +54,9 @@ impl UserVerb for Verb {
user_error("You need to provide a message to send.".to_owned())?;
}
let player_item = get_player_item_or_fail(ctx).await?;
if player_item.is_dead {
user_error("Shush, the dead can't talk!".to_string())?;
}
say_to_room(ctx.trans, &player_item, &player_item.location,
&say_what, is_likely_explicit(&say_what)).await
}

View File

@ -17,6 +17,9 @@ impl UserVerb for Verb {
user_error("You need to provide a message to send.".to_owned())?;
}
let player_item = get_player_item_or_fail(ctx).await?;
if player_item.is_dead {
user_error("Shush, the dead can't talk!".to_string())?;
}
let to_whom = search_item_for_user(ctx, &ItemSearchParams {
include_loc_contents: true,
..ItemSearchParams::base(&player_item, &to_whom_name)

View File

@ -15,6 +15,12 @@ pub enum TaskDetails {
npc_code: String,
say_code: String
},
NPCWander {
npc_code: String,
},
NPCAggro {
npc_code: String,
},
AttackTick,
RecloneNPC {
npc_code: String
@ -29,6 +35,8 @@ impl TaskDetails {
match self {
RunQueuedCommand => "RunQueuedCommand",
NPCSay { .. } => "NPCSay",
NPCWander { .. } => "NPCWander",
NPCAggro { .. } => "NPCAggro",
AttackTick => "AttackTick",
RecloneNPC { .. } => "RecloneNPC",
RotCorpse { .. } => "RotCorpse",

View File

@ -34,6 +34,8 @@ fn task_handler_registry() -> &'static BTreeMap<&'static str, &'static (dyn Task
|| vec!(
("RunQueuedCommand", queued_command::HANDLER.clone()),
("NPCSay", npc::SAY_HANDLER.clone()),
("NPCWander", npc::WANDER_HANDLER.clone()),
("NPCAggro", npc::AGGRO_HANDLER.clone()),
("AttackTick", combat::TASK_HANDLER.clone()),
("RecloneNPC", npc::RECLONE_HANDLER.clone()),
("RotCorpse", combat::ROT_CORPSE_HANDLER.clone()),

View File

@ -10,7 +10,7 @@ pub mod combat;
pub async fn broadcast_to_room(trans: &DBTrans, location: &str, from_item: Option<&Item>,
message_explicit_ok: &str, message_nonexplicit: Option<&str>) -> DResult<()> {
for item in trans.find_items_by_location(location).await? {
if item.item_type != "player" {
if item.item_type != "player" || item.is_dead {
continue;
}
if let Some((session, session_dat)) = trans.find_session_for_player(&item.item_code).await? {

View File

@ -22,6 +22,7 @@ use chrono::Utc;
use async_recursion::async_recursion;
use std::time;
use ansi::ansi;
use rand::prelude::IteratorRandom;
use rand_distr::{Normal, Distribution};
#[derive(Clone)]
@ -129,8 +130,41 @@ impl TaskHandler for AttackTaskHandler {
}
}
pub async fn consider_xp_gain_for(trans: &DBTrans, by_item: &mut Item, for_item: &Item) -> DResult<()> {
if by_item.item_type != "player" {
return Ok(());
}
let (session, _) = match trans.find_session_for_player(&by_item.item_code).await? {
None => return Ok(()),
Some(r) => r
};
if by_item.total_xp >= for_item.total_xp {
trans.queue_for_session(&session, Some("[You didn't gain any experience for that]\n")).await?;
return Ok(());
}
let xp_gain =
(((for_item.total_xp - by_item.total_xp) as f64 * 10.0 / (by_item.total_xp + 1) as f64) as u64)
.min(100);
if xp_gain == 0 {
trans.queue_for_session(&session, Some("[You didn't gain any experience for that]\n")).await?;
return Ok(());
}
by_item.total_xp += xp_gain;
let mut user = match trans.find_by_username(&by_item.item_code).await? {
None => return Ok(()),
Some(r) => r
};
user.experience.xp_change_for_this_reroll += xp_gain as i64;
trans.save_user_model(&user).await?;
trans.queue_for_session(&session, Some(&format!("You gained {} experience points!\n", xp_gain))).await?;
Ok(())
}
pub async fn handle_death(trans: &DBTrans, whom: &mut Item) -> DResult<()> {
whom.is_dead = true;
let msg_exp = format!(
ansi!("<red>{} stiffens and collapses dead.<reset>\n"),
&whom.display_for_sentence(true, 1, true)
@ -139,14 +173,17 @@ pub async fn handle_death(trans: &DBTrans, whom: &mut Item) -> DResult<()> {
ansi!("<red>{} stiffens and collapses dead.<reset>\n"),
&whom.display_for_sentence(false, 1, true)
);
broadcast_to_room(trans, &whom.location, None, &msg_exp, Some(&msg_nonexp)).await?;
whom.is_dead = true;
if let Some(ac) = &whom.active_combat {
let at_str = ac.attacking.clone();
for attacker in ac.attacked_by.clone().iter() {
if let Some((atype, acode)) = attacker.split_once("/") {
if let Some(aitem) = trans.find_item_by_type_code(atype, acode).await? {
let mut new_aitem = (*aitem).clone();
stop_attacking_mut(trans, &mut new_aitem, whom).await?;
consider_xp_gain_for(trans, &mut new_aitem, &whom).await?;
stop_attacking_mut(trans, &mut new_aitem, whom, true).await?;
trans.save_item_model(&new_aitem).await?;
}
}
@ -154,7 +191,7 @@ pub async fn handle_death(trans: &DBTrans, whom: &mut Item) -> DResult<()> {
if let Some((vtype, vcode)) = at_str.as_ref().and_then(|a| a.split_once("/")) {
if let Some(vitem) = trans.find_item_by_type_code(vtype, vcode).await? {
let mut new_vitem = (*vitem).clone();
stop_attacking_mut(trans, whom, &mut new_vitem).await?;
stop_attacking_mut(trans, whom, &mut new_vitem, false).await?;
trans.save_item_model(&new_vitem).await?;
}
}
@ -171,8 +208,29 @@ pub async fn handle_death(trans: &DBTrans, whom: &mut Item) -> DResult<()> {
}
}).await?;
}
Ok(())
}
pub async fn handle_resurrect(trans: &DBTrans, player: &mut Item) -> DResult<bool> {
corpsify_item(trans, &player).await?;
player.is_dead = false;
let lost_xp = (player.total_xp / 200).max(10).min(player.total_xp);
let (session, _) = match trans.find_session_for_player(&player.item_code).await? {
None => return Ok(false),
Some(r) => r
};
let mut user = match trans.find_by_username(&player.item_code).await? {
None => return Ok(false),
Some(r) => r
};
trans.queue_for_session(&session,
Some(&format!("You lost {} experience points by dying.\n", lost_xp))).await?;
player.total_xp -= lost_xp;
user.experience.xp_change_for_this_reroll -= lost_xp as i64;
player.health = max_health(&player);
broadcast_to_room(trans, &whom.location, None, &msg_exp, Some(&msg_nonexp)).await
trans.save_user_model(&user).await?;
Ok(true)
}
pub fn max_health(_whom: &Item) -> u64 {
@ -181,7 +239,8 @@ pub fn max_health(_whom: &Item) -> u64 {
pub static TASK_HANDLER: &(dyn TaskHandler + Sync + Send) = &AttackTaskHandler;
pub async fn stop_attacking_mut(trans: &DBTrans, new_by_whom: &mut Item, new_to_whom: &mut Item) ->
pub async fn stop_attacking_mut(trans: &DBTrans, new_by_whom: &mut Item, new_to_whom: &mut Item,
auto_refocus: bool) ->
DResult<()>
{
trans.delete_task("AttackTick",
@ -194,6 +253,19 @@ pub async fn stop_attacking_mut(trans: &DBTrans, new_by_whom: &mut Item, new_to_
}
if let Some(ac) = new_by_whom.active_combat.as_mut() {
ac.attacking = None;
if auto_refocus {
let old_vic = format!("{}/{}", new_to_whom.item_type, new_to_whom.item_code);
let new_vic_opt = ac.attacked_by.iter().filter(|i| **i != old_vic).choose(&mut rand::thread_rng());
if let Some(new_vic) = new_vic_opt {
if let Some((vtype, vcode)) = new_vic.split_once("/") {
if let Some(vic_item) = trans.find_item_by_type_code(vtype, vcode).await? {
let mut new_vic_item = (*vic_item).clone();
start_attack_mut(trans, new_by_whom, &mut new_vic_item);
trans.save_item_model(&new_vic_item).await?;
}
}
}
}
}
new_by_whom.action_type = LocationActionType::Normal;
Ok(())
@ -203,7 +275,7 @@ pub async fn stop_attacking_mut(trans: &DBTrans, new_by_whom: &mut Item, new_to_
pub async fn stop_attacking(trans: &DBTrans, by_whom: &Item, to_whom: &Item) -> DResult<()> {
let mut new_by_whom = (*by_whom).clone();
let mut new_to_whom = (*to_whom).clone();
stop_attacking_mut(trans, &mut new_by_whom, &mut new_to_whom).await?;
stop_attacking_mut(trans, &mut new_by_whom, &mut new_to_whom, false).await?;
trans.save_item_model(&new_by_whom).await?;
trans.save_item_model(&new_to_whom).await?;
Ok(())
@ -228,6 +300,16 @@ fn attack_speed(_who: &Item) -> time::Duration {
#[async_recursion]
pub async fn start_attack(trans: &DBTrans, by_whom: &Item, to_whom: &Item) -> UResult<()> {
let mut by_whom_for_update = by_whom.clone();
let mut to_whom_for_update = to_whom.clone();
start_attack_mut(trans, &mut by_whom_for_update, &mut to_whom_for_update).await?;
trans.save_item_model(&by_whom_for_update).await?;
trans.save_item_model(&to_whom_for_update).await?;
Ok(())
}
#[async_recursion]
pub async fn start_attack_mut(trans: &DBTrans, by_whom: &mut Item, to_whom: &mut Item) -> UResult<()> {
let mut msg_exp = String::new();
let mut msg_nonexp = String::new();
let mut verb: String = "attacks".to_string();
@ -272,13 +354,11 @@ pub async fn start_attack(trans: &DBTrans, by_whom: &Item, to_whom: &Item) -> UR
broadcast_to_room(trans, &by_whom.location, None::<&Item>, &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 =
by_whom.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(
by_whom.action_type = LocationActionType::Attacking(Subattack::Normal);
to_whom.active_combat.get_or_insert_with(|| Default::default()).attacked_by.push(
format!("{}/{}",
&by_whom.item_type, &by_whom.item_code)
);
@ -292,11 +372,9 @@ pub async fn start_attack(trans: &DBTrans, by_whom: &Item, to_whom: &Item) -> UR
},
details: TaskDetails::AttackTick
}).await?;
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?;
if to_whom.active_combat.as_ref().and_then(|ac| ac.attacking.as_ref()) == None {
start_attack(trans, &to_whom, &by_whom).await?;
}
Ok(())

View File

@ -50,6 +50,14 @@ fn static_task_registry() -> Vec<StaticThingTypeGroup<StaticTask>> {
thing_type: "NPCSay",
things: || npc::npc_say_tasks()
},
StaticThingTypeGroup::<StaticTask> {
thing_type: "NPCWander",
things: || npc::npc_wander_tasks()
},
StaticThingTypeGroup::<StaticTask> {
thing_type: "NPCAggro",
things: || npc::npc_aggro_tasks()
},
)
}

View File

@ -2,7 +2,11 @@ use super::{
StaticItem,
StaticTask,
possession_type::PossessionType,
species::SpeciesType
species::SpeciesType,
room::{
room_map_by_code,
resolve_exit
}
};
use crate::models::{
item::{Item, Pronouns, SkillType},
@ -12,13 +16,15 @@ use crate::services::{
combat::{
max_health,
corpsify_item,
start_attack,
}
};
use once_cell::sync::OnceCell;
use std::collections::BTreeMap;
use crate::message_handler::user_commands::{
VerbContext, UResult, CommandHandlingError,
say::say_to_room
say::say_to_room,
movement::attempt_move_immediate,
};
use crate::DResult;
use async_trait::async_trait;
@ -66,9 +72,12 @@ pub struct NPC {
pub aliases: Vec<&'static str>,
pub says: Vec<NPCSayInfo>,
pub attackable: bool,
pub aggression: u64,
pub intrinsic_weapon: Option<PossessionType>,
pub total_xp: u64,
pub total_skills: BTreeMap<SkillType, f64>,
pub species: SpeciesType,
pub wander_zones: Vec<&'static str>
}
impl Default for NPC {
@ -82,10 +91,13 @@ impl Default for NPC {
message_handler: None,
aliases: vec!(),
says: vec!(),
total_xp: 1000,
total_skills: SkillType::values().into_iter().map(|sk| (sk, 10.0)).collect(),
attackable: false,
aggression: 0,
intrinsic_weapon: None,
species: SpeciesType::Human,
wander_zones: vec!()
}
}
}
@ -174,6 +186,54 @@ pub fn npc_say_tasks() -> Box<dyn Iterator<Item = StaticTask>> {
})))
}
pub fn npc_wander_tasks() -> Box<dyn Iterator<Item = StaticTask>> {
Box::new(npc_list().iter().filter(|c| !c.wander_zones.is_empty())
.map(|c| StaticTask {
task_code: c.code.to_owned(),
initial_task: Box::new(
|| {
let mut rng = thread_rng();
Task {
meta: TaskMeta {
task_code: c.code.to_owned(),
is_static: true,
recurrence: Some(TaskRecurrence::FixedDuration { seconds: rng.gen_range(100..150) as u32 }),
next_scheduled: Utc::now() + chrono::Duration::seconds(rng.gen_range(0..30) as i64),
..TaskMeta::default()
},
details: TaskDetails::NPCWander {
npc_code: c.code.to_owned(),
},
}
})
}))
}
pub fn npc_aggro_tasks() -> Box<dyn Iterator<Item = StaticTask>> {
Box::new(npc_list().iter().filter(|c| c.aggression != 0)
.map(|c| StaticTask {
task_code: c.code.to_owned(),
initial_task: Box::new(
|| {
let mut rng = thread_rng();
let aggro_time = (rng.gen_range(100..150) as u64) / c.aggression;
Task {
meta: TaskMeta {
task_code: c.code.to_owned(),
is_static: true,
recurrence: Some(TaskRecurrence::FixedDuration { seconds: aggro_time as u32 }),
next_scheduled: Utc::now() + chrono::Duration::seconds(rng.gen_range(0..aggro_time) as i64),
..TaskMeta::default()
},
details: TaskDetails::NPCAggro {
npc_code: c.code.to_owned(),
},
}
})
}))
}
pub struct NPCSayTaskHandler;
#[async_trait]
impl TaskHandler for NPCSayTaskHandler {
@ -226,6 +286,105 @@ impl TaskHandler for NPCSayTaskHandler {
}
pub static SAY_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &NPCSayTaskHandler;
pub struct NPCWanderTaskHandler;
#[async_trait]
impl TaskHandler for NPCWanderTaskHandler {
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
let npc_code = match &ctx.task.details {
TaskDetails::NPCWander { npc_code } => npc_code.clone(),
_ => Err("Expected NPCWander type")?
};
let npc = match npc_by_code().get(npc_code.as_str()) {
None => {
info!("NPC {} is gone / not yet in static items, ignoring in wander handler", &npc_code);
return Ok(None)
},
Some(r) => r
};
let item = match ctx.trans.find_item_by_type_code("npc", &npc_code).await? {
None => {
info!("NPC {} is gone / not yet in DB, ignoring in wander handler", &npc_code);
return Ok(None)
},
Some(r) => r
};
if item.is_dead {
return Ok(None)
}
let (ltype, lcode) = match item.location.split_once("/") {
None => return Ok(None),
Some(r) => r
};
if ltype != "room" {
let mut new_item = (*item).clone();
new_item.location = npc.spawn_location.to_owned();
ctx.trans.save_item_model(&new_item).await?;
return Ok(None);
}
let room = match room_map_by_code().get(lcode) {
None => {
let mut new_item = (*item).clone();
new_item.location = npc.spawn_location.to_owned();
ctx.trans.save_item_model(&new_item).await?;
return Ok(None);
},
Some(r) => r
};
let ex_iter = room.exits
.iter()
.filter(
|ex| resolve_exit(room, ex).map(
|new_room| npc.wander_zones.contains(&new_room.zone) &&
!new_room.repel_npc).unwrap_or(false)
);
let dir_opt = ex_iter.choose(&mut thread_rng()).map(|ex| ex.direction.clone()).clone();
if let Some(dir) = dir_opt {
match attempt_move_immediate(ctx.trans, &item, &dir, None).await {
Ok(()) | Err(CommandHandlingError::UserError(_)) => {},
Err(CommandHandlingError::SystemError(e)) => Err(e)?
}
}
Ok(None)
}
}
pub static WANDER_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &NPCWanderTaskHandler;
pub struct NPCAggroTaskHandler;
#[async_trait]
impl TaskHandler for NPCAggroTaskHandler {
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
let npc_code = match &ctx.task.details {
TaskDetails::NPCAggro { npc_code } => npc_code.clone(),
_ => Err("Expected NPCAggro type")?
};
let item = match ctx.trans.find_item_by_type_code("npc", &npc_code).await? {
None => {
info!("NPC {} is gone / not yet in DB, ignoring in aggro handler", &npc_code);
return Ok(None)
},
Some(r) => r
};
if item.is_dead || item.active_combat.as_ref().map(|ac| ac.attacking.is_some()).unwrap_or(false) {
return Ok(None);
}
let items_loc = ctx.trans.find_items_by_location(&item.location).await?;
let vic_opt = items_loc
.iter()
.filter(|it| (it.item_type == "player" || it.item_type == "npc") &&
!it.is_dead && (it.item_type != item.item_type || it.item_code != item.item_code))
.choose(&mut thread_rng());
if let Some(victim) = vic_opt {
match start_attack(ctx.trans, &item, victim).await {
Ok(()) | Err(CommandHandlingError::UserError(_)) => {}
Err(CommandHandlingError::SystemError(e)) => Err(e)?
}
}
Ok(None)
}
}
pub static AGGRO_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &NPCAggroTaskHandler;
pub struct NPCRecloneTaskHandler;
#[async_trait]
impl TaskHandler for NPCRecloneTaskHandler {

View File

@ -1,4 +1,5 @@
use super::{NPC, NPCSayInfo, NPCSayType};
use crate::models::item::Pronouns;
pub fn npc_list() -> Vec<NPC> {
use NPCSayType::FromFixedList;
@ -17,547 +18,82 @@ pub fn npc_list() -> Vec<NPC> {
))
};
vec!(
NPC {
code: "melbs_citizen_1",
name: "Matthew Thomas",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_latrobest",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_2",
name: "Matthew Perez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_20",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
macro_rules! citizen {
($code: expr, $name: expr, $spawn: expr, $pronouns: expr) => {
NPC {
code: concat!("melbs_citizen_", $code),
name: $name,
pronouns: $pronouns,
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: concat!("room/melbs_", $spawn),
message_handler: None,
wander_zones: vec!("melbs"),
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
}
}
}
NPC {
code: "melbs_citizen_3",
name: "Kimberly Jackson",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_40",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_4",
name: "Michael Sanchez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_50",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_5",
name: "Jessica Davis",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_bourkest",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_6",
name: "Robert Davis",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_70",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_7",
name: "Paul Lewis",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_90",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_8",
name: "Andrew Moore",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_collinsst",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_9",
name: "Betty Thomas",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_100",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_10",
name: "Mary Robinson",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_110",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_11",
name: "Lisa Lopez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_flinderst",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_12",
name: "Kimberly Martinez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_200",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_13",
name: "Anthony Nguyen",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_190",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_14",
name: "Joshua Green",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_180",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_15",
name: "Emily Wright",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_170",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_16",
name: "Ashley Thomas",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_lonsdalest_130",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_17",
name: "Jessica Miller",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_80",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_18",
name: "Anthony Lopez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_lonsdalest_140",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_19",
name: "John Lopez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_elizabethst_lonsdalest",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_20",
name: "Thomas Garcia",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_williamsst_120",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_21",
name: "Donna Thompson",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_elizabethst_60",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_22",
name: "Matthew Davis",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_williamsst_100",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_23",
name: "Steven Jones",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_120",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_24",
name: "Linda Smith",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_lonsdalest",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_25",
name: "Karen Rodriguez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_bourkest_180",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_26",
name: "Paul Scott",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_70",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_27",
name: "Ashley Thomas",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_lonsdalest_130",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_28",
name: "Sandra Scott",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_elizabethst_30",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_29",
name: "Michael Rodriguez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_70",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_30",
name: "Donald Miller",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_elizabethst_30",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_31",
name: "Charles Moore",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_lonsdalest_160",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_32",
name: "Ashley Sanchez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_100",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_33",
name: "Margaret Lewis",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_180",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_34",
name: "Sandra Thompson",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_80",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_35",
name: "Sandra King",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_lonsdalest_150",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_36",
name: "Lisa Anderson",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_lonsdalest_210",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_37",
name: "Kimberly Martin",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_80",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_38",
name: "Susan Smith",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_latrobest_190",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_39",
name: "Susan Martin",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_collinsst_150",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_40",
name: "Linda Scott",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_williamsst_30",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_41",
name: "Donald Miller",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_elizabethst_80",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_42",
name: "Mark Hill",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_collinsst_120",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_43",
name: "William Perez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_queenst_90",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_44",
name: "Donald Perez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_queenst_lonsdalest",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_45",
name: "Lisa Rodriguez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_collinsst_100",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_46",
name: "James Adams",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_latrobest_150",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_47",
name: "James Moore",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_latrobest_130",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_48",
name: "Joseph Martin",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_bourkest_150",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_49",
name: "Matthew Jones",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_60",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_50",
name: "Michael Sanchez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_queenst_100",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_51",
name: "Donna Torres",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_150",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_52",
name: "Barbara Garcia",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_50",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_53",
name: "Daniel Miller",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_bourkest_110",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_54",
name: "Robert Young",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_kingst_collinsst",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_55",
name: "Donald Flores",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_40",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_56",
name: "Charles Thomas",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_flindersst_110",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_57",
name: "William Torres",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_swanstonst_60",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_58",
name: "Barbara Gonzalez",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_collinsst_190",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_59",
name: "Mary Smith",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_bourkest_180",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
},
NPC {
code: "melbs_citizen_60",
name: "Michael Jackson",
description: "A fairly ordinary looking citizen of Melbs, clearly weary from the harst reality of post-apocalyptic life",
spawn_location: "room/melbs_williamsst_110",
message_handler: None,
says: vec!(melbs_citizen_stdsay.clone()),
..Default::default()
}
vec!(
citizen!("1", "Matthew Thomas", "kingst_latrobest", Pronouns::default_male()),
citizen!("2", "Matthew Perez", "kingst_20", Pronouns::default_male()),
citizen!("3", "Kimberly Jackson", "kingst_40", Pronouns::default_female()),
citizen!("4", "Michael Sanchez", "kingst_50", Pronouns::default_male()),
citizen!("5", "Jessica Davis", "kingst_bourkest", Pronouns::default_female()),
citizen!("6", "Robert Davis", "kingst_70", Pronouns::default_male()),
citizen!("7", "Paul Lewis", "kingst_90", Pronouns::default_male()),
citizen!("8", "Andrew Moore", "kingst_collinsst", Pronouns::default_male()),
citizen!("9", "Betty Thomas", "kingst_100", Pronouns::default_female()),
citizen!("10", "Mary Robinson", "kingst_110", Pronouns::default_female()),
citizen!("11", "Lisa Lopez", "kingst_flinderst", Pronouns::default_female()),
citizen!("12", "Kimberly Martinez", "flindersst_200", Pronouns::default_female()),
citizen!("13", "Anthony Nguyen", "flindersst_190", Pronouns::default_male()),
citizen!("14", "Joshua Green", "flindersst_180", Pronouns::default_male()),
citizen!("15", "Emily Wright", "flindersst_170", Pronouns::default_female()),
citizen!("16", "Ashley Thomas", "lonsdalest_130", Pronouns::default_male()),
citizen!("17", "Jessica Miller", "kingst_80", Pronouns::default_female()),
citizen!("18", "Anthony Lopez", "lonsdalest_140", Pronouns::default_male()),
citizen!("19", "John Lopez", "elizabethst_lonsdalest", Pronouns::default_male()),
citizen!("20", "Thomas Garcia", "williamsst_120", Pronouns::default_male()),
citizen!("21", "Donna Thompson", "elizabethst_60", Pronouns::default_female()),
citizen!("22", "Matthew Davis", "williamsst_100", Pronouns::default_male()),
citizen!("23", "Steven Jones", "swanstonst_120", Pronouns::default_male()),
citizen!("24", "Linda Smith", "swanstonst_lonsdalest", Pronouns::default_male()),
citizen!("25", "Karen Rodriguez", "bourkest_180", Pronouns::default_female()),
citizen!("26", "Paul Scott", "swanstonst_70", Pronouns::default_male()),
citizen!("27", "Ashley Thomas", "lonsdalest_130", Pronouns::default_male()),
citizen!("28", "Sandra Scott", "elizabethst_30", Pronouns::default_female()),
citizen!("29", "Michael Rodriguez", "swanstonst_70", Pronouns::default_male()),
citizen!("30", "Donald Miller", "elizabethst_30", Pronouns::default_male()),
citizen!("31", "Charles Moore", "lonsdalest_160", Pronouns::default_male()),
citizen!("32", "Ashley Sanchez", "kingst_100", Pronouns::default_male()),
citizen!("33", "Margaret Lewis", "flindersst_180", Pronouns::default_female()),
citizen!("34", "Sandra Thompson", "swanstonst_80", Pronouns::default_female()),
citizen!("35", "Sandra King", "lonsdalest_150", Pronouns::default_female()),
citizen!("36", "Lisa Anderson", "lonsdalest_210", Pronouns::default_female()),
citizen!("37", "Kimberly Martin", "kingst_80", Pronouns::default_female()),
citizen!("38", "Susan Smith", "latrobest_190", Pronouns::default_female()),
citizen!("39", "Susan Martin", "collinsst_150", Pronouns::default_female()),
citizen!("40", "Linda Scott", "williamsst_30", Pronouns::default_female()),
citizen!("41", "Donald Miller", "elizabethst_80", Pronouns::default_male()),
citizen!("42", "Mark Hill", "collinsst_120", Pronouns::default_male()),
citizen!("43", "William Perez", "queenst_90", Pronouns::default_male()),
citizen!("44", "Donald Perez", "queenst_lonsdalest", Pronouns::default_male()),
citizen!("45", "Lisa Rodriguez", "collinsst_100", Pronouns::default_female()),
citizen!("46", "James Adams", "latrobest_150", Pronouns::default_male()),
citizen!("47", "James Moore", "latrobest_130", Pronouns::default_male()),
citizen!("48", "Joseph Martin", "bourkest_150", Pronouns::default_male()),
citizen!("49", "Matthew Jones", "kingst_60", Pronouns::default_male()),
citizen!("50", "Michael Sanchez", "queenst_100", Pronouns::default_male()),
citizen!("51", "Donna Torres", "flindersst_150", Pronouns::default_female()),
citizen!("52", "Barbara Garcia", "swanstonst_50", Pronouns::default_female()),
citizen!("53", "Daniel Miller", "bourkest_110", Pronouns::default_male()),
citizen!("54", "Robert Young", "kingst_collinsst", Pronouns::default_male()),
citizen!("55", "Donald Flores", "swanstonst_40", Pronouns::default_male()),
citizen!("56", "Charles Thomas", "flindersst_110", Pronouns::default_male()),
citizen!("57", "William Torres", "swanstonst_60", Pronouns::default_male()),
citizen!("58", "Barbara Gonzalez", "collinsst_190", Pronouns::default_female()),
citizen!("59", "Mary Smith", "bourkest_180", Pronouns::default_female()),
citizen!("60", "Michael John", "williamsst_110", Pronouns::default_male()),
)
}

View File

@ -12,6 +12,8 @@ macro_rules! dog {
name: concat!($adj, " dog"),
pronouns: Pronouns { is_proper: false, ..Pronouns::default_inanimate() },
attackable: true,
aggression: 12,
wander_zones: vec!("melbs"),
description: "A malnourished looking dog. Its skeleton is visible through its thin and patchy fur. It smells terrible, and certainly doesn't look tame.",
aliases: vec!("dog"),
spawn_location: concat!("room/", $spawn),

View File

@ -157,6 +157,7 @@ pub struct Room {
pub description_less_explicit: Option<&'static str>,
pub exits: Vec<Exit>,
pub should_caption: bool,
pub repel_npc: bool,
pub item_flags: Vec<ItemFlag>
}
@ -173,6 +174,7 @@ impl Default for Room {
description_less_explicit: None,
exits: vec!(),
should_caption: true,
repel_npc: false,
item_flags: vec!(),
}
}

View File

@ -181,6 +181,7 @@ pub fn room_list() -> Vec<Room> {
),
should_caption: true,
item_flags: vec!(ItemFlag::NoSay, ItemFlag::NoSeeContents),
repel_npc: true,
..Default::default()
},
Room {