Support player recloning + NPCs can wander and aggro.
This commit is contained in:
parent
2d9fcf9850
commit
a3ea381438
@ -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)
|
||||
|
@ -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()))?
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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()),
|
||||
|
@ -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? {
|
||||
|
@ -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(())
|
||||
|
@ -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()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user