Open doors automatically on movement.
This commit is contained in:
parent
131512fbf6
commit
62f5457d3a
@ -1,11 +1,16 @@
|
||||
use super::{VerbContext, UserVerb, UserVerbRef, UResult, UserError, user_error,
|
||||
get_player_item_or_fail, search_item_for_user};
|
||||
use super::{
|
||||
VerbContext, UserVerb, UserVerbRef, UResult, UserError, user_error,
|
||||
get_player_item_or_fail, search_item_for_user,
|
||||
map::{render_map, render_map_dyn},
|
||||
open::{is_door_in_direction, DoorSituation},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use ansi::{ansi, flow_around, word_wrap};
|
||||
use crate::{
|
||||
db::ItemSearchParams,
|
||||
models::{item::{
|
||||
Item, LocationActionType, Subattack, ItemFlag, ItemSpecialData
|
||||
Item, LocationActionType, Subattack, ItemFlag, ItemSpecialData,
|
||||
DoorState
|
||||
}},
|
||||
static_content::{
|
||||
room::{self, Direction},
|
||||
@ -15,7 +20,6 @@ use crate::{
|
||||
language,
|
||||
services::combat::max_health,
|
||||
};
|
||||
use super::map::{render_map, render_map_dyn};
|
||||
use itertools::Itertools;
|
||||
use std::sync::Arc;
|
||||
use mockall_double::double;
|
||||
@ -176,6 +180,32 @@ pub async fn describe_dynroom(ctx: &VerbContext<'_>,
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn describe_door(
|
||||
ctx: &VerbContext<'_>,
|
||||
room_item: &Item,
|
||||
state: &DoorState,
|
||||
direction: &Direction,
|
||||
) -> UResult<()> {
|
||||
let mut msg = format!("That exit is blocked by {}.",
|
||||
&state.description);
|
||||
if let Some(lock) = ctx.trans.find_by_action_and_location(
|
||||
&room_item.refstr(),
|
||||
&LocationActionType::InstalledOnDoorAsLock((*direction).clone())).await?
|
||||
{
|
||||
let lock_desc = lock.display_for_session(&ctx.session_dat);
|
||||
msg.push_str(&format!(" The door is locked with {} {}",
|
||||
&language::indefinite_article(&lock_desc),
|
||||
&lock_desc
|
||||
));
|
||||
}
|
||||
msg.push('\n');
|
||||
ctx.trans.queue_for_session(
|
||||
ctx.session,
|
||||
Some(&msg)).await?;
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
async fn list_room_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> UResult<String> {
|
||||
if item.flags.contains(&ItemFlag::NoSeeContents) {
|
||||
return Ok(" It is too foggy to see who or what else is here.".to_owned());
|
||||
@ -321,6 +351,19 @@ impl UserVerb for Verb {
|
||||
ctx.trans.find_item_by_type_code(heretype, herecode).await?
|
||||
.ok_or_else(|| UserError("Sorry, that no longer exists".to_owned()))?
|
||||
} else if let Some(dir) = Direction::parse(&rem_trim) {
|
||||
match is_door_in_direction(&ctx.trans, &dir, &player_item).await? {
|
||||
DoorSituation::NoDoor |
|
||||
DoorSituation::DoorOutOfRoom { state: DoorState { open: true, .. }, .. } |
|
||||
DoorSituation::DoorIntoRoom { state: DoorState { open: true, .. }, .. } => {},
|
||||
DoorSituation::DoorIntoRoom { state, room_with_door, .. } => {
|
||||
if let Some(rev_dir) = dir.reverse() {
|
||||
return describe_door(ctx, &room_with_door, &state, &rev_dir).await;
|
||||
}
|
||||
},
|
||||
DoorSituation::DoorOutOfRoom { state, room_with_door, .. } => {
|
||||
return describe_door(ctx, &room_with_door, &state, &dir).await;
|
||||
}
|
||||
}
|
||||
direction_to_item(&ctx.trans, use_location, &dir).await?
|
||||
.ok_or_else(|| UserError("There's nothing in that direction".to_owned()))?
|
||||
} else if rem_trim == "me" || rem_trim == "self" {
|
||||
|
@ -2,6 +2,7 @@ use super::{
|
||||
VerbContext, UserVerb, UserVerbRef, UResult, UserError, user_error,
|
||||
get_player_item_or_fail,
|
||||
look,
|
||||
open::{DoorSituation, is_door_in_direction, attempt_open_immediate},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use crate::{
|
||||
@ -20,7 +21,8 @@ use crate::{
|
||||
Item,
|
||||
ItemSpecialData,
|
||||
SkillType,
|
||||
LocationActionType
|
||||
LocationActionType,
|
||||
DoorState,
|
||||
},
|
||||
services::{
|
||||
comms::broadcast_to_room,
|
||||
@ -139,7 +141,9 @@ pub async fn attempt_move_immediate(
|
||||
trans: &DBTrans,
|
||||
orig_mover: &Item,
|
||||
direction: &Direction,
|
||||
mut player_ctx: Option<&mut VerbContext<'_>>
|
||||
// player_ctx should only be Some if called from queue_handler finish_command
|
||||
// for the orig_mover's queue, because might re-queue a move command.
|
||||
mut player_ctx: &mut Option<&mut VerbContext<'_>>
|
||||
) -> UResult<()> {
|
||||
let use_location = if orig_mover.is_dead {
|
||||
if orig_mover.item_type != "player" {
|
||||
@ -150,6 +154,27 @@ pub async fn attempt_move_immediate(
|
||||
&orig_mover.location
|
||||
};
|
||||
|
||||
match is_door_in_direction(trans, direction, orig_mover).await? {
|
||||
DoorSituation::NoDoor |
|
||||
DoorSituation::DoorIntoRoom { state: DoorState { open: true, .. }, .. } |
|
||||
DoorSituation::DoorOutOfRoom { state: DoorState { open: true, .. }, .. } => {},
|
||||
_ => {
|
||||
attempt_open_immediate(trans, player_ctx, orig_mover, direction).await?;
|
||||
match player_ctx.as_mut() {
|
||||
None => {
|
||||
// NPCs etc... open and move in one step, but can't unlock.
|
||||
},
|
||||
Some(actual_player_ctx) => {
|
||||
// Players take an extra step. So tell them to come back.
|
||||
actual_player_ctx.session_dat.queue.push_front(
|
||||
QueueCommand::Movement { direction: direction.clone() }
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut mover = (*orig_mover).clone();
|
||||
let (new_loc, new_loc_item) = move_to_where(trans, use_location, direction, &mut mover, &mut player_ctx).await?;
|
||||
|
||||
@ -253,7 +278,7 @@ impl QueueCommandHandler for QueueHandler {
|
||||
_ => user_error("Unexpected command".to_owned())?
|
||||
};
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
attempt_move_immediate(ctx.trans, &player_item, direction, Some(ctx)).await?;
|
||||
attempt_move_immediate(ctx.trans, &player_item, direction, &mut Some(ctx)).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||
possession_type::possession_data,
|
||||
},
|
||||
models::{
|
||||
item::{Item, LocationActionType},
|
||||
item::{Item, LocationActionType, DoorState},
|
||||
task::{Task, TaskMeta, TaskDetails}
|
||||
},
|
||||
services::comms::broadcast_to_room,
|
||||
@ -28,6 +28,8 @@ use crate::{
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use chrono::{self, Utc};
|
||||
use mockall_double::double;
|
||||
#[double] use crate::db::DBTrans;
|
||||
use log::info;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -37,7 +39,6 @@ pub static SWING_SHUT_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &Swing
|
||||
#[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); }
|
||||
@ -69,27 +70,118 @@ impl TaskHandler for SwingShutHandler {
|
||||
broadcast_to_room(&ctx.trans, &other_room.refstr(), None, &msg, Some(&msg)).await?;
|
||||
}
|
||||
|
||||
info!("Finishing swing shut");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn attempt_open_immediate(trans: &DBTrans, ctx_opt: &mut Option<&mut VerbContext<'_>>,
|
||||
who: &Item, direction: &Direction) -> UResult<()> {
|
||||
if who.is_dead {
|
||||
user_error("Your ethereal hands don't seem to be able to move the door.".to_owned())?;
|
||||
}
|
||||
let (room_1, dir_in_room, room_2) = match is_door_in_direction(trans, &direction, &who).await? {
|
||||
DoorSituation::NoDoor => user_error("There is no door to open.".to_owned())?,
|
||||
DoorSituation::DoorIntoRoom { state: DoorState { open: true, .. }, .. } |
|
||||
DoorSituation::DoorOutOfRoom { state: DoorState { 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) = trans.find_by_action_and_location(
|
||||
&entering_room_loc,
|
||||
&LocationActionType::InstalledOnDoorAsLock(revdir.clone())).await?
|
||||
{
|
||||
if let Some(ctx) = ctx_opt {
|
||||
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, &who, &lock).await?
|
||||
}
|
||||
} else {
|
||||
// NPCs don't unlock doors.
|
||||
user_error("Can't get through locked doors".to_owned())?;
|
||||
}
|
||||
}
|
||||
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(&revdir) {
|
||||
(*door).open = true;
|
||||
info!("Set door_map");
|
||||
} else {
|
||||
info!("door_map missing direction");
|
||||
}
|
||||
} else {
|
||||
info!("door_map None");
|
||||
}
|
||||
info!("Saving door map");
|
||||
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;
|
||||
}
|
||||
}
|
||||
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(
|
||||
&trans,
|
||||
loc,
|
||||
None,
|
||||
&format!("{} opens the door to the {}.\n",
|
||||
&who.display_for_sentence(true, 1, true),
|
||||
dir
|
||||
),
|
||||
Some(
|
||||
&format!("{} opens the door to the {}.\n",
|
||||
&who.display_for_sentence(false, 1, true),
|
||||
dir
|
||||
)
|
||||
)
|
||||
).await?;
|
||||
}
|
||||
|
||||
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?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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? {
|
||||
match is_door_in_direction(&ctx.trans, &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, .. } =>
|
||||
DoorSituation::DoorIntoRoom { state: DoorState { open: true, .. }, .. } |
|
||||
DoorSituation::DoorOutOfRoom { state: DoorState { 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();
|
||||
@ -109,118 +201,42 @@ impl QueueCommandHandler for QueueHandler {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
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)
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
attempt_open_immediate(&ctx.trans, &mut Some(ctx), &player_item, &direction).await?;
|
||||
|
||||
} 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.
|
||||
DoorIntoRoom { state: DoorState, room_with_door: Arc<Item>, current_room: Arc<Item> }, // Can be locked etc...
|
||||
DoorOutOfRoom { state: DoorState, 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) ->
|
||||
pub async fn is_door_in_direction(trans: &DBTrans, 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?
|
||||
let cur_loc_item = 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?
|
||||
let new_loc_item = direction_to_item(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,
|
||||
state: door_state.clone(),
|
||||
room_with_door: cur_loc_item,
|
||||
new_room: new_loc_item
|
||||
});
|
||||
@ -230,7 +246,7 @@ pub async fn is_door_in_direction(ctx: &mut VerbContext<'_>, direction: &Directi
|
||||
.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,
|
||||
state: door_state.clone(),
|
||||
room_with_door: new_loc_item,
|
||||
current_room: cur_loc_item
|
||||
});
|
||||
@ -245,9 +261,7 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
@ -310,8 +310,19 @@ pub struct DynamicEntrance {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
||||
#[serde(default)]
|
||||
pub struct DoorState {
|
||||
pub open: bool,
|
||||
pub description: String
|
||||
}
|
||||
|
||||
impl Default for DoorState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
open: false,
|
||||
description: "a solid looking wooden door".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
||||
|
@ -100,9 +100,10 @@ impl Dynzone {
|
||||
owner: Some(owner.clone()),
|
||||
door_states: Some(room.exits.iter()
|
||||
.filter_map(|ex|
|
||||
if ex.exit_type == ExitType::Doored {
|
||||
if let ExitType::Doored { description } = ex.exit_type {
|
||||
Some((ex.direction.clone(), DoorState {
|
||||
open: false
|
||||
open: false,
|
||||
description: description.to_owned()
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
@ -133,7 +134,7 @@ impl Default for Dynzone {
|
||||
#[derive(Eq, Clone, PartialEq, Ord, PartialOrd)]
|
||||
pub enum ExitType {
|
||||
Doorless,
|
||||
Doored,
|
||||
Doored { description: &'static str },
|
||||
}
|
||||
|
||||
#[derive(Eq, Ord, Debug, PartialEq, PartialOrd, Clone)]
|
||||
|
@ -49,7 +49,7 @@ pub fn zone() -> Dynzone {
|
||||
Exit {
|
||||
direction: Direction::WEST,
|
||||
target: ExitTarget::Intrazone { subcode: "doorstep" },
|
||||
exit_type: ExitType::Doored
|
||||
exit_type: ExitType::Doored { description: "a reasonably sturdy looking fire-rated solid core beige painted door" }
|
||||
}
|
||||
),
|
||||
grid_coords: GridCoords { x: 1, y: 0, z: 0 },
|
||||
|
@ -354,7 +354,7 @@ impl TaskHandler for NPCWanderTaskHandler {
|
||||
);
|
||||
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 {
|
||||
match attempt_move_immediate(ctx.trans, &item, &dir, &mut None).await {
|
||||
Ok(()) | Err(CommandHandlingError::UserError(_)) => {},
|
||||
Err(CommandHandlingError::SystemError(e)) => Err(e)?
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user