blastmud/blastmud_game/src/message_handler/user_commands/close.rs

161 lines
5.7 KiB
Rust

use super::{
get_player_item_or_fail,
open::{is_door_in_direction, DoorSituation},
user_error, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
};
use crate::{
models::item::DoorState,
regular_tasks::queued_command::{
queue_command_and_save, QueueCommand, QueueCommandHandler, QueuedCommandContext,
},
services::comms::broadcast_to_room,
static_content::room::Direction,
};
use async_trait::async_trait;
use std::time;
pub struct QueueHandler;
#[async_trait]
impl QueueCommandHandler for QueueHandler {
async fn start_command(&self, ctx: &mut QueuedCommandContext<'_>) -> UResult<time::Duration> {
let direction = match ctx.command {
QueueCommand::CloseDoor { direction } => direction,
_ => user_error("Unexpected queued command".to_owned())?,
};
let use_location = if ctx.item.death_data.is_some() {
user_error("Your ethereal hands don't seem to be able to move the door.".to_owned())?
} else {
&ctx.item.location
};
match is_door_in_direction(&ctx.trans, &direction, use_location).await? {
DoorSituation::NoDoor => user_error("There is no door to close.".to_owned())?,
DoorSituation::DoorIntoRoom {
state: DoorState { open: false, .. },
..
}
| DoorSituation::DoorOutOfRoom {
state: DoorState { open: false, .. },
..
} => user_error("The door is already closed.".to_owned())?,
_ => {}
}
Ok(time::Duration::from_secs(1))
}
#[allow(unreachable_patterns)]
async fn finish_command(&self, ctx: &mut QueuedCommandContext<'_>) -> UResult<()> {
let direction = match ctx.command {
QueueCommand::CloseDoor { direction } => direction,
_ => user_error("Unexpected queued command".to_owned())?,
};
let use_location = if ctx.item.death_data.is_some() {
user_error("Your ethereal hands don't seem to be able to move the door.".to_owned())?
} else {
&ctx.item.location
};
let (room_1, dir_in_room, room_2) =
match is_door_in_direction(&ctx.trans, &direction, use_location).await? {
DoorSituation::NoDoor => user_error("There is no door to close.".to_owned())?,
DoorSituation::DoorIntoRoom {
state: DoorState { open: false, .. },
..
}
| DoorSituation::DoorOutOfRoom {
state: DoorState { open: false, .. },
..
} => user_error("The door is already closed.".to_owned())?,
DoorSituation::DoorIntoRoom {
room_with_door,
current_room,
..
} => {
if let Some(revdir) = direction.reverse() {
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 = false;
}
};
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 = false;
}
}
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!(
"{} closes the door to the {}.\n",
&ctx.item.display_for_sentence(1, true),
dir
),
)
.await?;
}
ctx.trans
.delete_task(
"SwingShut",
&format!("{}/{}", &room_1.refstr(), &dir_in_room.describe()),
)
.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 dir =
Direction::parse(remaining).ok_or_else(|| UserError("Unknown direction".to_owned()))?;
let player_item = get_player_item_or_fail(ctx).await?;
queue_command_and_save(
ctx,
&player_item,
&QueueCommand::CloseDoor {
direction: dir.clone(),
},
)
.await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;