Create 60 NPCs and let them spontaneously talk.
This commit is contained in:
parent
d93a1fc3ca
commit
4207ab8f4d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -126,6 +126,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"ouroboros",
|
||||
"phf",
|
||||
"rand",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -34,3 +34,4 @@ bcrypt = "0.13.0"
|
||||
validator = "0.16.0"
|
||||
itertools = "0.10.5"
|
||||
once_cell = "1.16.0"
|
||||
rand = "0.8.5"
|
||||
|
@ -151,6 +151,17 @@ impl DBPool {
|
||||
.collect()))
|
||||
}
|
||||
|
||||
pub async fn find_static_task_types(self: &Self) -> DResult<Box<BTreeSet<String>>> {
|
||||
Ok(Box::new(
|
||||
self
|
||||
.get_conn().await?
|
||||
.query("SELECT DISTINCT details->>'task_type' AS task_type \
|
||||
FROM tasks WHERE details->>'is_static' = 'true'", &[]).await?
|
||||
.iter()
|
||||
.map(|r| r.get("task_type"))
|
||||
.collect()))
|
||||
}
|
||||
|
||||
pub async fn delete_static_items_by_type(self: &Self, item_type: &str) -> DResult<()> {
|
||||
self.get_conn().await?.query(
|
||||
"DELETE FROM items WHERE details->>'is_static' = 'true' AND details->>'item_type' = {}",
|
||||
@ -158,6 +169,13 @@ impl DBPool {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_static_tasks_by_type(self: &Self, task_type: &str) -> DResult<()> {
|
||||
self.get_conn().await?.query(
|
||||
"DELETE FROM tasks WHERE details->>'is_static' = 'true' AND details->>'task_type' = {}",
|
||||
&[&task_type]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_conn(self: &DBPool) ->
|
||||
DResult<Object> {
|
||||
let conn = self.pool.get().await?;
|
||||
@ -279,6 +297,29 @@ impl DBTrans {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn limited_update_static_task(self: &Self, task: &Task) -> DResult<()> {
|
||||
let value = serde_json::to_value(task)?;
|
||||
let obj_map = value.as_object()
|
||||
.expect("Static task to be object in JSON");
|
||||
let task_name: &(dyn ToSql + Sync) = &task.details.name();
|
||||
let mut params: Vec<&(dyn ToSql + Sync)> = vec!(task_name, &task.meta.task_code);
|
||||
let mut det_ex: String = "details".to_owned();
|
||||
let mut var_id = 3;
|
||||
// Only copy more permanent fields, others are supposed to change over time and shouldn't
|
||||
// be reset on restart. We do reset failure count since the problem may be fixed.
|
||||
for to_copy in ["recurrence", "consecutive_failure_count", "task_details"] {
|
||||
det_ex = format!("jsonb_set({}, '{{{}}}', ${})", det_ex, to_copy, var_id);
|
||||
params.push(obj_map.get(to_copy).unwrap_or(&Value::Null));
|
||||
var_id += 1;
|
||||
}
|
||||
self.pg_trans()?.execute(
|
||||
&("UPDATE tasks SET details = ".to_owned() + &det_ex +
|
||||
" WHERE details->>'task_type' = $1 AND details->>'task_code' = $2"),
|
||||
¶ms).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub async fn create_user(self: &Self, session: &ListenerSession, user_dat: &User) -> DResult<()> {
|
||||
self.pg_trans()?.execute("INSERT INTO users (\
|
||||
username, current_session, current_listener, details\
|
||||
@ -332,6 +373,19 @@ impl DBTrans {
|
||||
.collect()))
|
||||
}
|
||||
|
||||
pub async fn find_static_tasks_by_type(self: &Self, task_type: &str) ->
|
||||
DResult<Box<BTreeSet<String>>> {
|
||||
Ok(Box::new(
|
||||
self.pg_trans()?
|
||||
.query("SELECT DISTINCT details->>'task_code' AS task_code FROM tasks WHERE \
|
||||
details->>'is_static' = 'true' AND \
|
||||
details->>'task_type' = $1", &[&task_type])
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|v| v.get("task_code"))
|
||||
.collect()))
|
||||
}
|
||||
|
||||
pub async fn delete_static_items_by_code(self: &Self, item_type: &str,
|
||||
item_code: &str) -> DResult<()> {
|
||||
self.pg_trans()?.query(
|
||||
@ -342,6 +396,16 @@ impl DBTrans {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_static_tasks_by_code(self: &Self, task_type: &str,
|
||||
task_code: &str) -> DResult<()> {
|
||||
self.pg_trans()?.query(
|
||||
"DELETE FROM task WHERE details->>'is_static' = 'true' AND \
|
||||
details->>'task_type' = $1 AND \
|
||||
details->>'task_code' = $2",
|
||||
&[&task_type, &task_code]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn find_item_by_type_code(self: &Self, item_type: &str, item_code: &str) ->
|
||||
DResult<Option<Arc<Item>>> {
|
||||
if let Some(item) = self.pg_trans()?.query_opt(
|
||||
@ -464,7 +528,7 @@ impl DBTrans {
|
||||
match self.pg_trans()?.query_opt(
|
||||
"SELECT details FROM tasks WHERE \
|
||||
CAST(details->>'next_scheduled' AS TIMESTAMPTZ) <= now() \
|
||||
ORDER BY details->>'next_scheduled'", &[]
|
||||
ORDER BY details->>'next_scheduled' ASC LIMIT 1", &[]
|
||||
).await? {
|
||||
None => Ok(None),
|
||||
Some(row) => Ok(serde_json::from_value(row.get("details"))?)
|
||||
|
@ -21,6 +21,7 @@ pub mod movement;
|
||||
pub mod parsing;
|
||||
mod quit;
|
||||
mod register;
|
||||
pub mod say;
|
||||
mod whisper;
|
||||
|
||||
pub struct VerbContext<'l> {
|
||||
@ -100,8 +101,11 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"look" => look::VERB,
|
||||
"read" => look::VERB,
|
||||
"lmap" => map::VERB,
|
||||
"\'" => say::VERB,
|
||||
"say" => say::VERB,
|
||||
"-" => whisper::VERB,
|
||||
"whisper" => whisper::VERB,
|
||||
"tell" => whisper::VERB,
|
||||
};
|
||||
|
||||
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
|
||||
|
56
blastmud_game/src/message_handler/user_commands/say.rs
Normal file
56
blastmud_game/src/message_handler/user_commands/say.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use super::{VerbContext, UserVerb, UserVerbRef, UResult, UserError,
|
||||
user_error,
|
||||
get_player_item_or_fail, is_likely_explicit};
|
||||
use crate::models::item::{Item, ItemFlag};
|
||||
use crate::db::DBTrans;
|
||||
use async_trait::async_trait;
|
||||
use ansi::{ignore_special_characters, ansi};
|
||||
|
||||
pub async fn say_to_room<'l>(
|
||||
trans: &DBTrans,
|
||||
from_item: &Item,
|
||||
location: &str,
|
||||
say_what: &str,
|
||||
is_explicit: bool
|
||||
) -> UResult<()> {
|
||||
let (loc_type, loc_code) = location.split_once("/")
|
||||
.ok_or_else(|| UserError("Invalid location".to_owned()))?;
|
||||
let room_item = trans.find_item_by_type_code(loc_type, loc_code).await?
|
||||
.ok_or_else(|| UserError("Room missing".to_owned()))?;
|
||||
if room_item.flags.contains(&ItemFlag::NoSay) {
|
||||
user_error("Your wristpad vibrates and flashes up an error - apparently it has \
|
||||
been programmed to block your voice from working here.".to_owned())?
|
||||
}
|
||||
for item in trans.find_items_by_location(location).await? {
|
||||
if item.item_type != "player" {
|
||||
continue;
|
||||
}
|
||||
if let Some((session, session_dat)) = trans.find_session_for_player(&item.item_code).await? {
|
||||
if session_dat.less_explicit_mode && is_explicit && from_item.item_code != item.item_code {
|
||||
continue;
|
||||
}
|
||||
trans.queue_for_session(&session, Some(&format!(
|
||||
ansi!("<yellow>{} says: <reset><bold>\"{}\"<reset>\n"),
|
||||
from_item.display_for_session(&session_dat),
|
||||
say_what
|
||||
))).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct Verb;
|
||||
#[async_trait]
|
||||
impl UserVerb for Verb {
|
||||
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> {
|
||||
let say_what = ignore_special_characters(remaining);
|
||||
if say_what == "" {
|
||||
user_error("You need to provide a message to send.".to_owned())?;
|
||||
}
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
say_to_room(ctx.trans, &player_item, &player_item.location,
|
||||
&say_what, is_likely_explicit(&say_what)).await
|
||||
}
|
||||
}
|
||||
static VERB_INT: Verb = Verb;
|
||||
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -107,6 +107,12 @@ pub enum Sex {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum ItemFlag {
|
||||
NoSay
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[serde(default)]
|
||||
pub struct Item {
|
||||
pub item_code: String,
|
||||
pub item_type: String,
|
||||
@ -124,6 +130,7 @@ pub struct Item {
|
||||
pub total_skills: BTreeMap<SkillType, u16>,
|
||||
pub temporary_buffs: Vec<Buff>,
|
||||
pub pronouns: Pronouns,
|
||||
pub flags: Vec<ItemFlag>,
|
||||
pub sex: Option<Sex>,
|
||||
}
|
||||
|
||||
@ -162,6 +169,7 @@ impl Default for Item {
|
||||
total_skills: BTreeMap::new(),
|
||||
temporary_buffs: Vec::new(),
|
||||
pronouns: Pronouns::default_inanimate(),
|
||||
flags: vec!(),
|
||||
sex: None
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use serde::{Serialize, Deserialize};
|
||||
use serde_json::Value;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum TaskRecurrence {
|
||||
FixedDuration { seconds: u32 }
|
||||
@ -11,13 +10,18 @@ pub enum TaskRecurrence {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[serde(tag="task_type", content="task_details")]
|
||||
pub enum TaskDetails {
|
||||
RunQueuedCommand
|
||||
RunQueuedCommand,
|
||||
NPCSay {
|
||||
npc_code: String,
|
||||
say_code: String
|
||||
}
|
||||
}
|
||||
impl TaskDetails {
|
||||
pub fn name(self: &Self) -> &'static str {
|
||||
use TaskDetails::*;
|
||||
match self {
|
||||
RunQueuedCommand => "RunQueuedCommand"
|
||||
RunQueuedCommand => "RunQueuedCommand",
|
||||
NPCSay { .. } => "NPCSay",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use once_cell::sync::OnceCell;
|
||||
use std::ops::AddAssign;
|
||||
use std::collections::BTreeMap;
|
||||
use chrono::Utc;
|
||||
use crate::static_content::npc;
|
||||
|
||||
pub mod queued_command;
|
||||
|
||||
@ -26,7 +27,8 @@ fn task_handler_registry() -> &'static BTreeMap<&'static str, &'static (dyn Task
|
||||
OnceCell::new();
|
||||
TASK_HANDLER_REGISTRY.get_or_init(
|
||||
|| vec!(
|
||||
("RunQueuedCommand", queued_command::HANDLER.clone())
|
||||
("RunQueuedCommand", queued_command::HANDLER.clone()),
|
||||
("NPCSay", npc::SAY_HANDLER.clone()),
|
||||
).into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::DResult;
|
||||
use crate::db::DBPool;
|
||||
use crate::models::item::Item;
|
||||
use crate::models::{item::Item, task::Task};
|
||||
use std::collections::{BTreeSet, BTreeMap};
|
||||
use log::info;
|
||||
|
||||
@ -13,50 +13,64 @@ pub struct StaticItem {
|
||||
pub initial_item: Box<dyn Fn() -> Item>
|
||||
}
|
||||
|
||||
struct StaticItemTypeGroup {
|
||||
item_type: &'static str,
|
||||
items: fn () -> Box<dyn Iterator<Item = StaticItem>>
|
||||
pub struct StaticTask {
|
||||
pub task_code: String,
|
||||
pub initial_task: Box<dyn Fn() -> Task>
|
||||
}
|
||||
|
||||
fn static_item_registry() -> Vec<StaticItemTypeGroup> {
|
||||
struct StaticThingTypeGroup<Thing> {
|
||||
thing_type: &'static str,
|
||||
things: fn () -> Box<dyn Iterator<Item = Thing>>
|
||||
}
|
||||
|
||||
fn static_item_registry() -> Vec<StaticThingTypeGroup<StaticItem>> {
|
||||
vec!(
|
||||
// Must have no duplicates.
|
||||
StaticItemTypeGroup {
|
||||
item_type: "npc",
|
||||
items: || npc::npc_static_items()
|
||||
StaticThingTypeGroup::<StaticItem> {
|
||||
thing_type: "npc",
|
||||
things: || npc::npc_static_items()
|
||||
},
|
||||
StaticItemTypeGroup {
|
||||
item_type: "room",
|
||||
items: || room::room_static_items()
|
||||
StaticThingTypeGroup::<StaticItem> {
|
||||
thing_type: "room",
|
||||
things: || room::room_static_items()
|
||||
},
|
||||
StaticItemTypeGroup {
|
||||
item_type: "fixed_item",
|
||||
items: || fixed_item::static_items()
|
||||
StaticThingTypeGroup::<StaticItem> {
|
||||
thing_type: "fixed_item",
|
||||
things: || fixed_item::static_items()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn static_task_registry() -> Vec<StaticThingTypeGroup<StaticTask>> {
|
||||
vec!(
|
||||
// Must have no duplicates.
|
||||
StaticThingTypeGroup::<StaticTask> {
|
||||
thing_type: "NPCSay",
|
||||
things: || npc::npc_say_tasks()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
async fn refresh_static_items(pool: &DBPool) -> DResult<()> {
|
||||
let registry = static_item_registry();
|
||||
|
||||
let expected_type: BTreeSet<String> =
|
||||
registry.iter().map(|x| x.item_type.to_owned()).collect();
|
||||
registry.iter().map(|x| x.thing_type.to_owned()).collect();
|
||||
let cur_types: Box<BTreeSet<String>> = pool.find_static_item_types().await?;
|
||||
for item_type in cur_types.difference(&expected_type) {
|
||||
pool.delete_static_items_by_type(item_type).await?;
|
||||
}
|
||||
|
||||
for type_group in registry.iter() {
|
||||
info!("Checking static_content of item_type {}", type_group.item_type);
|
||||
info!("Checking static_content of item_type {}", type_group.thing_type);
|
||||
let tx = pool.start_transaction().await?;
|
||||
let existing_items = tx.find_static_items_by_type(type_group.item_type).await?;
|
||||
let existing_items = tx.find_static_items_by_type(type_group.thing_type).await?;
|
||||
let expected_items: BTreeMap<String, StaticItem> =
|
||||
(type_group.items)().map(|x| (x.item_code.to_owned(), x)).collect();
|
||||
(type_group.things)().map(|x| (x.item_code.to_owned(), x)).collect();
|
||||
let expected_set: BTreeSet<String> = expected_items.keys().map(|x|x.to_owned()).collect();
|
||||
for unwanted_item in existing_items.difference(&expected_set) {
|
||||
info!("Deleting item {:?}", unwanted_item);
|
||||
tx.delete_static_items_by_code(type_group.item_type, unwanted_item).await?;
|
||||
tx.delete_static_items_by_code(type_group.thing_type, unwanted_item).await?;
|
||||
}
|
||||
for new_item_code in expected_set.difference(&existing_items) {
|
||||
info!("Creating item {:?}", new_item_code);
|
||||
@ -69,13 +83,51 @@ async fn refresh_static_items(pool: &DBPool) -> DResult<()> {
|
||||
.unwrap().initial_item)()).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
info!("Committed any changes for static_content of item_type {}", type_group.item_type);
|
||||
info!("Committed any changes for static_content of item_type {}", type_group.thing_type);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn refresh_static_tasks(pool: &DBPool) -> DResult<()> {
|
||||
let registry = static_task_registry();
|
||||
|
||||
let expected_type: BTreeSet<String> =
|
||||
registry.iter().map(|x| x.thing_type.to_owned()).collect();
|
||||
let cur_types: Box<BTreeSet<String>> = pool.find_static_task_types().await?;
|
||||
for task_type in cur_types.difference(&expected_type) {
|
||||
pool.delete_static_tasks_by_type(task_type).await?;
|
||||
}
|
||||
|
||||
for type_group in registry.iter() {
|
||||
info!("Checking static_content of task_type {}", type_group.thing_type);
|
||||
let tx = pool.start_transaction().await?;
|
||||
let existing_tasks = tx.find_static_tasks_by_type(type_group.thing_type).await?;
|
||||
let expected_tasks: BTreeMap<String, StaticTask> =
|
||||
(type_group.things)().map(|x| (x.task_code.to_owned(), x)).collect();
|
||||
let expected_set: BTreeSet<String> = expected_tasks.keys().map(|x|x.to_owned()).collect();
|
||||
for unwanted_task in existing_tasks.difference(&expected_set) {
|
||||
info!("Deleting task {:?}", unwanted_task);
|
||||
tx.delete_static_tasks_by_code(type_group.thing_type, unwanted_task).await?;
|
||||
}
|
||||
for new_task_code in expected_set.difference(&existing_tasks) {
|
||||
info!("Creating task {:?}", new_task_code);
|
||||
tx.upsert_task(&(expected_tasks.get(new_task_code)
|
||||
.unwrap().initial_task)()).await?;
|
||||
}
|
||||
for existing_task_code in expected_set.intersection(&existing_tasks) {
|
||||
tx.limited_update_static_task(
|
||||
&(expected_tasks.get(existing_task_code)
|
||||
.unwrap().initial_task)()).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
info!("Committed any changes for static_content of task_type {}", type_group.thing_type);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn refresh_static_content(pool: &DBPool) -> DResult<()> {
|
||||
refresh_static_items(pool).await?;
|
||||
refresh_static_tasks(pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -85,13 +137,13 @@ mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_duplicate_static_content() {
|
||||
fn no_duplicate_static_items() {
|
||||
let mut registry = static_item_registry();
|
||||
registry.sort_unstable_by(|x, y| x.item_type.cmp(y.item_type));
|
||||
registry.sort_unstable_by(|x, y| x.thing_type.cmp(y.thing_type));
|
||||
|
||||
let duplicates: Vec<&'static str> =
|
||||
registry.iter()
|
||||
.group_by(|x| x.item_type).into_iter()
|
||||
.group_by(|x| x.thing_type).into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
@ -99,7 +151,7 @@ mod test {
|
||||
}
|
||||
|
||||
for type_group in registry.iter() {
|
||||
let iterator : Box<dyn Iterator<Item = StaticItem>> = (type_group.items)();
|
||||
let iterator : Box<dyn Iterator<Item = StaticItem>> = (type_group.things)();
|
||||
let duplicates: Vec<&'static str> = iterator
|
||||
.group_by(|x| x.item_code)
|
||||
.into_iter()
|
||||
@ -107,7 +159,36 @@ mod test {
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
panic!("static_item_registry has duplicate item_codes for {}: {:}",
|
||||
type_group.item_type,
|
||||
type_group.thing_type,
|
||||
duplicates.join(", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_duplicate_static_tasks() {
|
||||
let mut registry = static_task_registry();
|
||||
registry.sort_unstable_by(|x, y| x.thing_type.cmp(y.thing_type));
|
||||
|
||||
let duplicates: Vec<&'static str> =
|
||||
registry.iter()
|
||||
.group_by(|x| x.thing_type).into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
panic!("static_task_registry has duplicate task_types: {:}", duplicates.join(", "));
|
||||
}
|
||||
|
||||
for type_group in registry.iter() {
|
||||
let iterator : Box<dyn Iterator<Item = StaticTask>> = (type_group.things)();
|
||||
let duplicates: Vec<&'static str> = iterator
|
||||
.group_by(|x| x.task_code)
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
|
||||
.collect();
|
||||
if duplicates.len() > 0 {
|
||||
panic!("static_task_registry has duplicate task_codes for {}: {:}",
|
||||
type_group.thing_type,
|
||||
duplicates.join(", "));
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,21 @@
|
||||
use super::StaticItem;
|
||||
use crate::models::item::Item;
|
||||
use super::{StaticItem, StaticTask};
|
||||
use crate::models::{
|
||||
item::Item,
|
||||
task::{Task, TaskMeta, TaskRecurrence, TaskDetails}
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::BTreeMap;
|
||||
use crate::message_handler::user_commands::{VerbContext, UResult};
|
||||
use crate::message_handler::user_commands::{
|
||||
VerbContext, UResult, CommandHandlingError,
|
||||
say::say_to_room
|
||||
};
|
||||
use crate::DResult;
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use rand::{thread_rng, Rng, prelude::*};
|
||||
use crate::regular_tasks::{TaskHandler, TaskRunContext};
|
||||
use log::info;
|
||||
use std::time;
|
||||
|
||||
pub mod statbot;
|
||||
|
||||
@ -18,26 +30,584 @@ pub trait NPCMessageHandler {
|
||||
) -> UResult<()>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NPCSayType {
|
||||
// Bool is true if it should be filtered for less-explicit.
|
||||
FromFixedList(Vec<(bool, &'static str)>)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NPCSayInfo {
|
||||
pub say_code: &'static str,
|
||||
pub frequency_secs: u64,
|
||||
pub talk_type: NPCSayType
|
||||
}
|
||||
|
||||
pub struct NPC {
|
||||
pub code: &'static str,
|
||||
pub name: &'static str,
|
||||
pub description: &'static str,
|
||||
pub spawn_location: &'static str,
|
||||
pub message_handler: Option<&'static (dyn NPCMessageHandler + Sync + Send)>
|
||||
pub message_handler: Option<&'static (dyn NPCMessageHandler + Sync + Send)>,
|
||||
pub says: Vec<NPCSayInfo>
|
||||
}
|
||||
|
||||
pub fn npc_list() -> &'static Vec<NPC> {
|
||||
use NPCSayType::FromFixedList;
|
||||
static NPC_LIST: OnceCell<Vec<NPC>> = OnceCell::new();
|
||||
NPC_LIST.get_or_init(|| vec!(
|
||||
NPC {
|
||||
code: "repro_xv_chargen_statbot",
|
||||
name: "Statbot",
|
||||
description: "A silvery shiny metal mechanical being. It lets out a whirring sound as it moves.",
|
||||
spawn_location: "room/repro_xv_chargen",
|
||||
message_handler: Some(&statbot::StatbotMessageHandler)
|
||||
}
|
||||
))
|
||||
NPC_LIST.get_or_init(
|
||||
|| {
|
||||
let melbs_citizen_stdsay = NPCSayInfo { say_code: "babble", frequency_secs: 60, talk_type: FromFixedList(vec!((false, "I'm so sick of being cloned")))};
|
||||
vec!(
|
||||
NPC {
|
||||
code: "repro_xv_chargen_statbot",
|
||||
name: "Statbot",
|
||||
description: "A silvery shiny metal mechanical being. It lets out a whirring sound as it moves.",
|
||||
spawn_location: "room/repro_xv_chargen",
|
||||
message_handler: Some(&statbot::StatbotMessageHandler),
|
||||
says: 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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
|
||||
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())
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npc_by_code() -> &'static BTreeMap<&'static str, &'static NPC> {
|
||||
@ -48,6 +618,18 @@ pub fn npc_by_code() -> &'static BTreeMap<&'static str, &'static NPC> {
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn npc_say_info_by_npc_code_say_code() -> &'static BTreeMap<(&'static str, &'static str),
|
||||
&'static NPCSayInfo> {
|
||||
static NPC_SAYINFO_MAP: OnceCell<BTreeMap<(&'static str, &'static str),
|
||||
&'static NPCSayInfo>> = OnceCell::new();
|
||||
NPC_SAYINFO_MAP.get_or_init(
|
||||
|| npc_list().iter().flat_map(
|
||||
|npc| npc.says.iter().map(
|
||||
|says| ((npc.code, says.say_code), says)
|
||||
)
|
||||
).collect())
|
||||
}
|
||||
|
||||
pub fn npc_static_items() -> Box<dyn Iterator<Item = StaticItem>> {
|
||||
Box::new(npc_list().iter().map(|c| StaticItem {
|
||||
item_code: c.code,
|
||||
@ -62,3 +644,78 @@ pub fn npc_static_items() -> Box<dyn Iterator<Item = StaticItem>> {
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn npc_say_tasks() -> Box<dyn Iterator<Item = StaticTask>> {
|
||||
Box::new(npc_list().iter().flat_map(|c| c.says.iter().map(|say| StaticTask {
|
||||
task_code: c.code.to_owned() + "_" + say.say_code,
|
||||
initial_task: Box::new(
|
||||
|| {
|
||||
let mut rng = thread_rng();
|
||||
Task {
|
||||
meta: TaskMeta {
|
||||
task_code: c.code.to_owned() + "_" + say.say_code,
|
||||
is_static: true,
|
||||
recurrence: Some(TaskRecurrence::FixedDuration { seconds: say.frequency_secs as u32 }),
|
||||
next_scheduled: Utc::now() + chrono::Duration::seconds(rng.gen_range(0..say.frequency_secs) as i64),
|
||||
..TaskMeta::default()
|
||||
},
|
||||
details: TaskDetails::NPCSay {
|
||||
npc_code: c.code.to_owned(),
|
||||
say_code: say.say_code.to_owned()
|
||||
},
|
||||
}
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
pub struct NPCSayTaskHandler;
|
||||
#[async_trait]
|
||||
impl TaskHandler for NPCSayTaskHandler {
|
||||
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
|
||||
let (npc_code, say_code) = match &ctx.task.details {
|
||||
TaskDetails::NPCSay { npc_code, say_code } => (npc_code.clone(), say_code.clone()),
|
||||
_ => Err("Expected NPC say task to be NPCSay type")?
|
||||
};
|
||||
|
||||
let say_info = match npc_say_info_by_npc_code_say_code().get(&(&npc_code, &say_code)) {
|
||||
None => {
|
||||
info!("NPCSayTaskHandler can't find NPCSayInfo for npc {} say_code {}",
|
||||
npc_code, say_code);
|
||||
return Ok(None);
|
||||
}
|
||||
Some(r) => r
|
||||
};
|
||||
let npc_item = match ctx.trans.find_item_by_type_code("npc", &npc_code).await? {
|
||||
None => {
|
||||
info!("NPCSayTaskHandler can't find NPC {}", npc_code);
|
||||
return Ok(None);
|
||||
}
|
||||
Some(r) => r
|
||||
};
|
||||
|
||||
let (is_explicit, say_what) = match &say_info.talk_type {
|
||||
NPCSayType::FromFixedList(l) => {
|
||||
let mut rng = thread_rng();
|
||||
match l[..].choose(&mut rng) {
|
||||
None => {
|
||||
info!("NPCSayTaskHandler NPCSayInfo for npc {} say_code {} has no choices",
|
||||
npc_code, say_code);
|
||||
return Ok(None);
|
||||
}
|
||||
Some(r) => r.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match say_to_room(ctx.trans, &npc_item, &npc_item.location, say_what, is_explicit).await {
|
||||
Ok(()) => {}
|
||||
Err(CommandHandlingError::UserError(e)) => {
|
||||
info!("NPCSayHandler couldn't send for npc {} say_code {}: {}",
|
||||
npc_code, say_code, e);
|
||||
}
|
||||
Err(CommandHandlingError::SystemError(e)) => Err(e)?
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
pub static SAY_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &NPCSayTaskHandler;
|
||||
|
Loading…
Reference in New Issue
Block a user