Allow doors to open.
This commit is contained in:
parent
284c49b4a1
commit
131512fbf6
@ -739,11 +739,10 @@ impl DBTrans {
|
||||
self.pg_trans()?.execute("UPDATE items SET details=\
|
||||
JSONB_SET(details, '{action_type}', $1) \
|
||||
WHERE details->>'location' = $2 AND \
|
||||
details->>'action_type' = $3",
|
||||
details->>'action_type' = $3::JSONB::TEXT",
|
||||
&[&serde_json::to_value(other_item_action_type)?,
|
||||
&item.location,
|
||||
&serde_json::to_value(new_action_type)?
|
||||
.as_str().unwrap()
|
||||
]).await?;
|
||||
self.pg_trans()?.execute("UPDATE items SET details=\
|
||||
JSONB_SET(details, '{action_type}', $1) \
|
||||
@ -760,9 +759,9 @@ impl DBTrans {
|
||||
if let Some(item) = self.pg_trans()?.query_opt(
|
||||
"SELECT details FROM items WHERE \
|
||||
details->>'location' = $1 AND \
|
||||
details->>'action_type' = $2",
|
||||
details->>'action_type' = $2::JSONB::TEXT",
|
||||
&[&location,
|
||||
&serde_json::to_value(action_type)?.as_str().unwrap()]).await? {
|
||||
&serde_json::to_value(action_type)?]).await? {
|
||||
return Ok(Some(Arc::new(serde_json::from_value::<Item>(item.get("details"))?)));
|
||||
}
|
||||
Ok(None)
|
||||
|
@ -29,6 +29,7 @@ mod login;
|
||||
mod look;
|
||||
mod map;
|
||||
pub mod movement;
|
||||
pub mod open;
|
||||
mod page;
|
||||
pub mod parsing;
|
||||
mod quit;
|
||||
@ -145,6 +146,8 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"gm" => map::VERB,
|
||||
"gmap" => map::VERB,
|
||||
|
||||
"open" => open::VERB,
|
||||
|
||||
"p" => page::VERB,
|
||||
"page" => page::VERB,
|
||||
"pg" => page::VERB,
|
||||
|
@ -45,8 +45,8 @@ static REGISTERED_HELP_PAGES: phf::Map<&'static str, &'static str> = phf_map! {
|
||||
ansi!("So you've just landed in BlastMud, and want to know how to get started?\n\
|
||||
You control your character, and can tell your character to move around the\n\
|
||||
world, and see things through their eyes.\n\
|
||||
The world (yes, even outside!) is divided up into rooms, and each room has\n
|
||||
exits that you are allowed to take, normally called north, south, east, west,\n
|
||||
The world (yes, even outside!) is divided up into rooms, and each room has\n\
|
||||
exits that you are allowed to take, normally called north, south, east, west,\n\
|
||||
northeast, northwest, southeast, southwest, up and down (sometimes you can also go in).\n\
|
||||
\n\
|
||||
Try <bold>look<reset> (or <bold>l<reset>) to look at the current room. It will\n\
|
||||
@ -56,7 +56,9 @@ static REGISTERED_HELP_PAGES: phf::Map<&'static str, &'static str> = phf_map! {
|
||||
<bold>l<reset> followed by the name of the object (shortening is okay).\n\
|
||||
Once you know what direction to move, you can type the direction, using either\n\
|
||||
the full name of the direction (e.g. <bold>northeast<reset> or <bold>south<reset>,\n\
|
||||
or a short form (<bold>n<reset>, <bold>e<reset>, <bold>s<reset>, <bold>w<reset>, <bold>ne<reset>, <bold>nw<reset>, <bold>se<reset>, <bold>sw<reset>, <bold>up<reset>, <bold>down<reset>)."),
|
||||
or a short form (<bold>n<reset>, <bold>e<reset>, <bold>s<reset>, <bold>w<reset>, <bold>ne<reset>, <bold>nw<reset>, <bold>se<reset>, <bold>sw<reset>, <bold>up<reset>, <bold>down<reset>).\n\n\
|
||||
Also try the map commands - <bold>lmap<reset> for a local map including exits, and <bold>gmap<reset> for a giant \
|
||||
map to let you see the broader context."),
|
||||
"movement" =>
|
||||
ansi!("Once you know what direction to move, you can type the direction, using either\n\
|
||||
the full name of the direction (e.g. <bold>northeast<reset> or <bold>south<reset>,\n\
|
||||
|
@ -241,7 +241,7 @@ async fn list_room_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> URe
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
async fn direction_to_item(
|
||||
pub async fn direction_to_item(
|
||||
trans: &DBTrans,
|
||||
use_location: &str,
|
||||
direction: &Direction,
|
||||
|
255
blastmud_game/src/message_handler/user_commands/open.rs
Normal file
255
blastmud_game/src/message_handler/user_commands/open.rs
Normal file
@ -0,0 +1,255 @@
|
||||
use super::{
|
||||
VerbContext, UserVerb, UserVerbRef, UResult, UserError, user_error,
|
||||
get_player_item_or_fail,
|
||||
look::direction_to_item,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use crate::{
|
||||
DResult,
|
||||
regular_tasks::{
|
||||
TaskRunContext,
|
||||
TaskHandler,
|
||||
queued_command::{
|
||||
QueueCommandHandler,
|
||||
QueueCommand,
|
||||
queue_command
|
||||
},
|
||||
},
|
||||
static_content::{
|
||||
room::Direction,
|
||||
possession_type::possession_data,
|
||||
},
|
||||
models::{
|
||||
item::{Item, LocationActionType},
|
||||
task::{Task, TaskMeta, TaskDetails}
|
||||
},
|
||||
services::comms::broadcast_to_room,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use chrono::{self, Utc};
|
||||
use log::info;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SwingShutHandler;
|
||||
pub static SWING_SHUT_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &SwingShutHandler;
|
||||
|
||||
#[async_trait]
|
||||
impl TaskHandler for SwingShutHandler {
|
||||
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
|
||||
info!("Starting swing shut");
|
||||
let (room_str, direction) = match &ctx.task.details {
|
||||
TaskDetails::SwingShut { room_item, direction } => (room_item, direction),
|
||||
_ => { return Ok(None); }
|
||||
};
|
||||
let (room_item_type, room_item_code) = match room_str.split_once("/") {
|
||||
None => { return Ok(None); },
|
||||
Some(v) => v
|
||||
};
|
||||
let room_item = match ctx.trans.find_item_by_type_code(room_item_type, room_item_code).await? {
|
||||
None => { return Ok(None); },
|
||||
Some(v) => v
|
||||
};
|
||||
|
||||
let mut room_item_mut = (*room_item).clone();
|
||||
let mut door_state = match room_item_mut.door_states.as_mut().and_then(|ds| ds.get_mut(&direction)) {
|
||||
None => { return Ok(None); },
|
||||
Some(v) => v
|
||||
};
|
||||
(*door_state).open = false;
|
||||
ctx.trans.save_item_model(&room_item_mut).await?;
|
||||
|
||||
let msg = format!("The door to the {} swings shut with a click.\n",
|
||||
&direction.describe());
|
||||
broadcast_to_room(&ctx.trans, &room_str, None, &msg, Some(&msg)).await?;
|
||||
|
||||
if let Ok(Some(other_room)) = direction_to_item(&ctx.trans, &room_str, &direction).await {
|
||||
let msg = format!("The door to the {} swings shut with a click.\n",
|
||||
&direction.reverse().map(|d| d.describe()).unwrap_or_else(|| "outside".to_owned()));
|
||||
broadcast_to_room(&ctx.trans, &other_room.refstr(), None, &msg, Some(&msg)).await?;
|
||||
}
|
||||
|
||||
info!("Finishing swing shut");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueueHandler;
|
||||
#[async_trait]
|
||||
impl QueueCommandHandler for QueueHandler {
|
||||
async fn start_command(&self, ctx: &mut VerbContext<'_>, command: &QueueCommand)
|
||||
-> UResult<time::Duration> {
|
||||
info!("Starting open start_command");
|
||||
let direction = match command {
|
||||
QueueCommand::OpenDoor { direction } => direction,
|
||||
_ => user_error("Unexpected command".to_owned())?
|
||||
};
|
||||
info!("Direction good");
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
info!("Got player");
|
||||
match is_door_in_direction(ctx, &direction, &player_item).await? {
|
||||
DoorSituation::NoDoor => user_error("There is no door to open.".to_owned())?,
|
||||
DoorSituation::DoorIntoRoom { is_open: true, .. } | DoorSituation::DoorOutOfRoom { is_open: true, .. } =>
|
||||
user_error("The door is already open.".to_owned())?,
|
||||
DoorSituation::DoorIntoRoom { room_with_door: entering_room, .. } => {
|
||||
let entering_room_loc = entering_room.refstr();
|
||||
if let Some(revdir) = direction.reverse() {
|
||||
if let Some(lock) = ctx.trans.find_by_action_and_location(
|
||||
&entering_room_loc,
|
||||
&LocationActionType::InstalledOnDoorAsLock(revdir)).await?
|
||||
{
|
||||
if let Some(lockcheck) = lock.possession_type.as_ref()
|
||||
.and_then(|pt| possession_data().get(pt))
|
||||
.and_then(|pd| pd.lockcheck_handler) {
|
||||
lockcheck.cmd(ctx, &player_item, &lock).await?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
info!("Clean exit open start_command");
|
||||
Ok(time::Duration::from_secs(1))
|
||||
}
|
||||
|
||||
#[allow(unreachable_patterns)]
|
||||
async fn finish_command(&self, ctx: &mut VerbContext<'_>, command: &QueueCommand)
|
||||
-> UResult<()> {
|
||||
info!("Starting open finish_command");
|
||||
let direction = match command {
|
||||
QueueCommand::OpenDoor { direction } => direction,
|
||||
_ => user_error("Unexpected command".to_owned())?
|
||||
};
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
let (room_1, dir_in_room, room_2) = match is_door_in_direction(ctx, &direction, &player_item).await? {
|
||||
DoorSituation::NoDoor => user_error("There is no door to open.".to_owned())?,
|
||||
DoorSituation::DoorIntoRoom { is_open: true, .. } |
|
||||
DoorSituation::DoorOutOfRoom { is_open: true, .. } =>
|
||||
user_error("The door is already open.".to_owned())?,
|
||||
DoorSituation::DoorIntoRoom { room_with_door, current_room, .. } => {
|
||||
let entering_room_loc = room_with_door.refstr();
|
||||
if let Some(revdir) = direction.reverse() {
|
||||
if let Some(lock) = ctx.trans.find_by_action_and_location(
|
||||
&entering_room_loc,
|
||||
&LocationActionType::InstalledOnDoorAsLock(revdir.clone())).await?
|
||||
{
|
||||
if let Some(lockcheck) = lock.possession_type.as_ref()
|
||||
.and_then(|pt| possession_data().get(pt))
|
||||
.and_then(|pd| pd.lockcheck_handler) {
|
||||
lockcheck.cmd(ctx, &player_item, &lock).await?
|
||||
}
|
||||
}
|
||||
let mut entering_room_mut = (*room_with_door).clone();
|
||||
if let Some(door_map) = entering_room_mut.door_states.as_mut() {
|
||||
if let Some(door) = door_map.get_mut(&direction) {
|
||||
(*door).open = true;
|
||||
}
|
||||
}
|
||||
ctx.trans.save_item_model(&entering_room_mut).await?;
|
||||
(room_with_door, revdir, current_room)
|
||||
|
||||
} else {
|
||||
user_error("There's no door possible there.".to_owned())?
|
||||
}
|
||||
},
|
||||
DoorSituation::DoorOutOfRoom { room_with_door, new_room, .. } => {
|
||||
let mut entering_room_mut = (*room_with_door).clone();
|
||||
if let Some(door_map) = entering_room_mut.door_states.as_mut() {
|
||||
if let Some(door) = door_map.get_mut(&direction) {
|
||||
(*door).open = true;
|
||||
}
|
||||
}
|
||||
ctx.trans.save_item_model(&entering_room_mut).await?;
|
||||
(room_with_door, direction.clone(), new_room)
|
||||
}
|
||||
};
|
||||
|
||||
for (loc, dir) in [(&room_1.refstr(), &dir_in_room.describe()),
|
||||
(&room_2.refstr(), &dir_in_room.reverse().map(|d| d.describe())
|
||||
.unwrap_or_else(|| "outside".to_owned()))] {
|
||||
broadcast_to_room(
|
||||
&ctx.trans,
|
||||
loc,
|
||||
None,
|
||||
&format!("{} opens the door to the {}.\n",
|
||||
&player_item.display_for_sentence(true, 1, true),
|
||||
dir
|
||||
),
|
||||
Some(
|
||||
&format!("{} opens the door to the {}.\n",
|
||||
&player_item.display_for_sentence(false, 1, true),
|
||||
dir
|
||||
)
|
||||
)
|
||||
).await?;
|
||||
}
|
||||
|
||||
ctx.trans.upsert_task(&Task {
|
||||
meta: TaskMeta {
|
||||
task_code: format!("{}/{}", &room_1.refstr(), &direction.describe()),
|
||||
next_scheduled: Utc::now() + chrono::Duration::seconds(120),
|
||||
..Default::default()
|
||||
},
|
||||
details: TaskDetails::SwingShut {
|
||||
room_item: room_1.refstr(),
|
||||
direction: dir_in_room.clone()
|
||||
}
|
||||
}).await?;
|
||||
|
||||
info!("Clean exit open finish_command");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DoorSituation {
|
||||
NoDoor,
|
||||
DoorIntoRoom { is_open: bool, room_with_door: Arc<Item>, current_room: Arc<Item> }, // Can be locked etc...
|
||||
DoorOutOfRoom { is_open: bool, room_with_door: Arc<Item>, new_room: Arc<Item> } // No lockable.
|
||||
}
|
||||
|
||||
pub async fn is_door_in_direction(ctx: &mut VerbContext<'_>, direction: &Direction, player_item: &Item) ->
|
||||
UResult<DoorSituation> {
|
||||
let (loc_type_t, loc_type_c) = player_item.location.split_once("/")
|
||||
.ok_or_else(|| UserError("Invalid location".to_owned()))?;
|
||||
let cur_loc_item = ctx.trans.find_item_by_type_code(loc_type_t, loc_type_c).await?
|
||||
.ok_or_else(|| UserError("Can't find your current location anymore.".to_owned()))?;
|
||||
let new_loc_item = direction_to_item(ctx.trans, &player_item.location, direction).await?
|
||||
.ok_or_else(|| UserError("That exit doesn't really seem to go anywhere!".to_owned()))?;
|
||||
if let Some(door_state) =
|
||||
cur_loc_item.door_states.as_ref()
|
||||
.and_then(|v| v.get(direction)) {
|
||||
return Ok(DoorSituation::DoorOutOfRoom {
|
||||
is_open: door_state.open,
|
||||
room_with_door: cur_loc_item,
|
||||
new_room: new_loc_item
|
||||
});
|
||||
}
|
||||
if let Some(door_state) =
|
||||
new_loc_item.door_states.as_ref()
|
||||
.and_then(|v| direction.reverse().as_ref()
|
||||
.and_then(|rev| v.get(rev).map(|door| door.clone()))) {
|
||||
return Ok(DoorSituation::DoorIntoRoom {
|
||||
is_open: door_state.open,
|
||||
room_with_door: new_loc_item,
|
||||
current_room: cur_loc_item
|
||||
});
|
||||
}
|
||||
Ok(DoorSituation::NoDoor)
|
||||
}
|
||||
|
||||
pub struct Verb;
|
||||
|
||||
#[async_trait]
|
||||
impl UserVerb for Verb {
|
||||
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> {
|
||||
let dir = Direction::parse(remaining)
|
||||
.ok_or_else(|| UserError("Unknown direction".to_owned()))?;
|
||||
info!("Queueing open");
|
||||
queue_command(ctx, &QueueCommand::OpenDoor { direction: dir.clone() }).await?;
|
||||
info!("Returning from open handler");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
static VERB_INT: Verb = Verb;
|
||||
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -256,7 +256,8 @@ pub enum LocationActionType {
|
||||
Reclining,
|
||||
Worn, // Clothing etc...
|
||||
Wielded,
|
||||
Attacking(Subattack)
|
||||
Attacking(Subattack),
|
||||
InstalledOnDoorAsLock(Direction),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
@ -269,7 +270,8 @@ pub enum Sex {
|
||||
pub enum ItemFlag {
|
||||
NoSay,
|
||||
NoSeeContents,
|
||||
DroppedItemsDontExpire
|
||||
DroppedItemsDontExpire,
|
||||
PrivatePlace,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
@ -307,6 +309,11 @@ pub struct DynamicEntrance {
|
||||
pub source_item: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub struct DoorState {
|
||||
pub open: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
||||
#[serde(default)]
|
||||
pub struct Item {
|
||||
@ -338,6 +345,7 @@ pub struct Item {
|
||||
pub special_data: Option<ItemSpecialData>,
|
||||
pub dynamic_entrance: Option<DynamicEntrance>,
|
||||
pub owner: Option<String>,
|
||||
pub door_states: Option<BTreeMap<Direction, DoorState>>
|
||||
}
|
||||
|
||||
impl Item {
|
||||
@ -384,6 +392,10 @@ impl Item {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn refstr(&self) -> String {
|
||||
format!("{}/{}", &self.item_type, &self.item_code)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Item {
|
||||
@ -417,6 +429,7 @@ impl Default for Item {
|
||||
special_data: None,
|
||||
dynamic_entrance: None,
|
||||
owner: None,
|
||||
door_states: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use serde_json::Value;
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::services::effect::DelayedHealthEffect;
|
||||
use std::collections::VecDeque;
|
||||
use crate::static_content::room::Direction;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum TaskRecurrence {
|
||||
@ -40,6 +41,10 @@ pub enum TaskDetails {
|
||||
ChargeRoom {
|
||||
zone_item: String,
|
||||
daily_price: u64
|
||||
},
|
||||
SwingShut {
|
||||
room_item: String,
|
||||
direction: Direction
|
||||
}
|
||||
}
|
||||
impl TaskDetails {
|
||||
@ -56,6 +61,7 @@ impl TaskDetails {
|
||||
DelayedHealth { .. } => "DelayedHealth",
|
||||
ExpireItem { .. } => "ExpireItem",
|
||||
ChargeRoom { .. } => "ChargeRoom",
|
||||
SwingShut { .. } => "SwingShut",
|
||||
// Don't forget to add to TASK_HANDLER_REGISTRY in regular_tasks.rs too.
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
listener::{ListenerMap, ListenerSend},
|
||||
static_content::npc,
|
||||
services::{combat, effect},
|
||||
message_handler::user_commands::{drop, rent},
|
||||
message_handler::user_commands::{drop, rent, open},
|
||||
};
|
||||
#[cfg(not(test))] use crate::models::task::{TaskParse, TaskRecurrence};
|
||||
use mockall_double::double;
|
||||
@ -45,7 +45,8 @@ fn task_handler_registry() -> &'static BTreeMap<&'static str, &'static (dyn Task
|
||||
("RotCorpse", combat::ROT_CORPSE_HANDLER.clone()),
|
||||
("DelayedHealth", effect::DELAYED_HEALTH_HANDLER.clone()),
|
||||
("ExpireItem", drop::EXPIRE_ITEM_HANDLER.clone()),
|
||||
("ChargeRoom", rent::CHARGE_ROOM_HANDLER.clone())
|
||||
("ChargeRoom", rent::CHARGE_ROOM_HANDLER.clone()),
|
||||
("SwingShut", open::SWING_SHUT_HANDLER.clone()),
|
||||
).into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ use crate::message_handler::user_commands::{
|
||||
use_cmd,
|
||||
wield,
|
||||
user_error,
|
||||
get_user_or_fail
|
||||
get_user_or_fail,
|
||||
open
|
||||
};
|
||||
use crate::static_content::room::Direction;
|
||||
use once_cell::sync::OnceCell;
|
||||
@ -32,6 +33,7 @@ pub enum QueueCommand {
|
||||
Use { possession_id: String, target_id: String },
|
||||
Get { possession_id: String },
|
||||
Drop { possession_id: String },
|
||||
OpenDoor { direction: Direction },
|
||||
}
|
||||
impl QueueCommand {
|
||||
pub fn name(&self) -> &'static str {
|
||||
@ -42,6 +44,7 @@ impl QueueCommand {
|
||||
Use {..} => "Use",
|
||||
Get {..} => "Get",
|
||||
Drop {..} => "Drop",
|
||||
OpenDoor {..} => "OpenDoor",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,6 +64,7 @@ fn queue_command_registry() -> &'static BTreeMap<&'static str, &'static (dyn Que
|
||||
("Movement", &movement::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
|
||||
("Use", &use_cmd::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
|
||||
("Wield", &wield::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
|
||||
("OpenDoor", &open::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
|
||||
).into_iter().collect())
|
||||
}
|
||||
|
||||
@ -79,7 +83,7 @@ pub async fn queue_command(ctx: &mut VerbContext<'_>, command: &QueueCommand) ->
|
||||
Err(CommandHandlingError::UserError(err_msg)) => {
|
||||
ctx.session_dat.queue.truncate(0);
|
||||
ctx.trans.save_session_model(ctx.session, ctx.session_dat).await?;
|
||||
user_error(err_msg)?;
|
||||
ctx.trans.queue_for_session(&ctx.session, Some(&(err_msg + "\r\n"))).await?;
|
||||
}
|
||||
Err(CommandHandlingError::SystemError(e)) => Err(e)?,
|
||||
Ok(dur) => {
|
||||
|
@ -5,7 +5,7 @@
|
||||
use super::room::{Direction, GridCoords};
|
||||
use crate::{
|
||||
message_handler::user_commands::{user_error, UResult},
|
||||
models::item::{Item, ItemFlag, ItemSpecialData, DynamicEntrance}
|
||||
models::item::{Item, ItemFlag, ItemSpecialData, DynamicEntrance, DoorState}
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::BTreeMap;
|
||||
@ -98,6 +98,15 @@ impl Dynzone {
|
||||
} else { None },
|
||||
flags: room.item_flags.clone(),
|
||||
owner: Some(owner.clone()),
|
||||
door_states: Some(room.exits.iter()
|
||||
.filter_map(|ex|
|
||||
if ex.exit_type == ExitType::Doored {
|
||||
Some((ex.direction.clone(), DoorState {
|
||||
open: false
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}).collect()),
|
||||
..Default::default()
|
||||
}
|
||||
).await?;
|
||||
@ -118,6 +127,9 @@ impl Default for Dynzone {
|
||||
}
|
||||
}
|
||||
|
||||
// Note that either the room being entered or the room being left should be
|
||||
// doored, not both. And doors should be on the inner-most room (furthest from
|
||||
// public) - locks only protect entry into the room, not exit from it.
|
||||
#[derive(Eq, Clone, PartialEq, Ord, PartialOrd)]
|
||||
pub enum ExitType {
|
||||
Doorless,
|
||||
|
@ -8,6 +8,7 @@ use super::{
|
||||
super::room::GridCoords
|
||||
};
|
||||
use crate::static_content::room::Direction;
|
||||
use crate::models::item::ItemFlag;
|
||||
|
||||
pub fn zone() -> Dynzone {
|
||||
Dynzone {
|
||||
@ -30,7 +31,7 @@ pub fn zone() -> Dynzone {
|
||||
Exit {
|
||||
direction: Direction::EAST,
|
||||
target: ExitTarget::Intrazone { subcode: "studio" },
|
||||
exit_type: ExitType::Doored
|
||||
exit_type: ExitType::Doorless
|
||||
}
|
||||
),
|
||||
grid_coords: GridCoords { x: 0, y: 0, z: 0 },
|
||||
@ -53,7 +54,8 @@ pub fn zone() -> Dynzone {
|
||||
),
|
||||
grid_coords: GridCoords { x: 1, y: 0, z: 0 },
|
||||
should_caption: true,
|
||||
item_flags: vec!(),
|
||||
item_flags: vec!(ItemFlag::DroppedItemsDontExpire,
|
||||
ItemFlag::PrivatePlace),
|
||||
..Default::default()
|
||||
})
|
||||
).into_iter().collect(),
|
||||
|
@ -14,6 +14,7 @@ mod fangs;
|
||||
mod antenna_whip;
|
||||
mod trauma_kit;
|
||||
mod corp_licence;
|
||||
mod lock;
|
||||
|
||||
pub type AttackMessageChoice = Vec<Box<dyn Fn(&Item, &Item, bool) -> String + 'static + Sync + Send>>;
|
||||
pub type AttackMessageChoicePart = Vec<Box<dyn Fn(&Item, &Item, &BodyPart, bool) -> String + 'static + Sync + Send>>;
|
||||
@ -135,6 +136,7 @@ pub struct PossessionData {
|
||||
pub weight: u64,
|
||||
pub write_handler: Option<&'static (dyn WriteHandler + Sync + Send)>,
|
||||
pub sign_handler: Option<&'static (dyn ArglessHandler + Sync + Send)>,
|
||||
pub lockcheck_handler: Option<&'static (dyn ArglessHandler + Sync + Send)>,
|
||||
}
|
||||
|
||||
impl Default for PossessionData {
|
||||
@ -153,6 +155,7 @@ impl Default for PossessionData {
|
||||
use_data: None,
|
||||
write_handler: None,
|
||||
sign_handler: None,
|
||||
lockcheck_handler: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,6 +193,7 @@ pub enum PossessionType {
|
||||
EmptyMedicalBox,
|
||||
NewCorpLicence,
|
||||
CertificateOfIncorporation,
|
||||
Scanlock,
|
||||
}
|
||||
|
||||
impl Into<Item> for PossessionType {
|
||||
@ -257,6 +261,7 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, PossessionData> {
|
||||
(EmptyMedicalBox, trauma_kit::empty_data()),
|
||||
(NewCorpLicence, corp_licence::data()),
|
||||
(CertificateOfIncorporation, corp_licence::cert_data()),
|
||||
(Scanlock, lock::scan()),
|
||||
).into_iter().collect()
|
||||
})
|
||||
}
|
||||
|
11
blastmud_game/src/static_content/possession_type/lock.rs
Normal file
11
blastmud_game/src/static_content/possession_type/lock.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use super::PossessionData;
|
||||
|
||||
pub fn scan() -> PossessionData {
|
||||
PossessionData {
|
||||
display: "scanlock",
|
||||
details: "A relatively basic lock with a fingerprint scanner built into it, made to ensure only the owner of something can enter or use it.",
|
||||
aliases: vec!("lock"),
|
||||
weight: 500,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
@ -76,9 +76,8 @@ pub trait ExitBlocker {
|
||||
}
|
||||
|
||||
pub enum ExitType {
|
||||
Free, // Anyone can just walk it.
|
||||
Free, // Anyone can just walk it (subject to any door logic).
|
||||
Blocked(&'static (dyn ExitBlocker + Sync + Send)), // Custom code about who can pass.
|
||||
// Future ideas: Doors with locks, etc...
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -147,6 +146,22 @@ impl Direction {
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reverse(&self) -> Option<Direction> {
|
||||
match self {
|
||||
Direction::NORTH => Some(Direction::SOUTH),
|
||||
Direction::SOUTH => Some(Direction::NORTH),
|
||||
Direction::EAST => Some(Direction::WEST),
|
||||
Direction::WEST => Some(Direction::EAST),
|
||||
Direction::NORTHEAST => Some(Direction::SOUTHWEST),
|
||||
Direction::SOUTHEAST => Some(Direction::NORTHWEST),
|
||||
Direction::NORTHWEST => Some(Direction::SOUTHEAST),
|
||||
Direction::SOUTHWEST => Some(Direction::NORTHEAST),
|
||||
Direction::UP => Some(Direction::DOWN),
|
||||
Direction::DOWN => Some(Direction::UP),
|
||||
Direction::IN { .. } => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq,Ord,Debug,PartialEq,PartialOrd,Clone)]
|
||||
|
@ -3283,10 +3283,41 @@ pub fn room_list() -> Vec<Room> {
|
||||
target: ExitTarget::UseGPS,
|
||||
exit_type: ExitType::Free
|
||||
},
|
||||
Exit {
|
||||
direction: Direction::SOUTHEAST,
|
||||
target: ExitTarget::UseGPS,
|
||||
exit_type: ExitType::Free
|
||||
}
|
||||
),
|
||||
should_caption: false,
|
||||
..Default::default()
|
||||
},
|
||||
Room {
|
||||
zone: "melbs",
|
||||
secondary_zones: vec!(),
|
||||
code: "melbs_lockedandloaded",
|
||||
name: "Locked & Loaded",
|
||||
short: ansi!("<yellow>LL<reset>"),
|
||||
description: "This seems to be some kind of security shop, selling locks from super high-tech to primitive. Behind a counter sits a grizzled old man, who appears eager to sell you something",
|
||||
description_less_explicit: None,
|
||||
grid_coords: GridCoords { x: 7, y: 8, z: 0 },
|
||||
exits: vec!(
|
||||
Exit {
|
||||
direction: Direction::NORTHWEST,
|
||||
target: ExitTarget::UseGPS,
|
||||
exit_type: ExitType::Free
|
||||
},
|
||||
),
|
||||
stock_list: vec!(
|
||||
RoomStock {
|
||||
possession_type: PossessionType::Scanlock,
|
||||
list_price: 200,
|
||||
..Default::default()
|
||||
}
|
||||
),
|
||||
should_caption: true,
|
||||
..Default::default()
|
||||
},
|
||||
Room {
|
||||
zone: "melbs",
|
||||
secondary_zones: vec!(),
|
||||
|
Loading…
Reference in New Issue
Block a user