diff --git a/blastmud_game/src/message_handler/user_commands.rs b/blastmud_game/src/message_handler/user_commands.rs index e1e99c90..b68b8689 100644 --- a/blastmud_game/src/message_handler/user_commands.rs +++ b/blastmud_game/src/message_handler/user_commands.rs @@ -53,6 +53,7 @@ mod page; pub mod parsing; pub mod put; mod quit; +pub mod recline; pub mod register; pub mod remove; pub mod rent; @@ -62,6 +63,7 @@ pub mod say; mod score; mod sign; pub mod sit; +pub mod stand; mod status; mod uninstall; pub mod use_cmd; @@ -209,6 +211,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! { "reply" => page::VERB, "put" => put::VERB, + "recline" => recline::VERB, "remove" => remove::VERB, "rent" => rent::VERB, "report" => report::VERB, @@ -222,6 +225,8 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! { "sign" => sign::VERB, "sit" => sit::VERB, + "stand" => stand::VERB, + "st" => status::VERB, "stat" => status::VERB, "status" => status::VERB, diff --git a/blastmud_game/src/message_handler/user_commands/recline.rs b/blastmud_game/src/message_handler/user_commands/recline.rs new file mode 100644 index 00000000..99c0b6af --- /dev/null +++ b/blastmud_game/src/message_handler/user_commands/recline.rs @@ -0,0 +1,223 @@ +use super::{ + get_player_item_or_fail, search_item_for_user, user_error, ItemSearchParams, UResult, UserVerb, + UserVerbRef, VerbContext, +}; +use crate::{ + models::item::LocationActionType, + regular_tasks::queued_command::{ + queue_command_and_save, QueueCommand, QueueCommandHandler, QueuedCommandContext, + }, + services::comms::broadcast_to_room, +}; +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 { + if ctx.item.death_data.is_some() { + user_error( + "You try to recline, but it turns out the dead can't even do that!".to_owned(), + )?; + } + let item_ref = match ctx.command { + QueueCommand::Recline { item } => item, + _ => user_error("Unexpected command".to_owned())?, + }; + if ctx + .item + .active_combat + .as_ref() + .and_then(|ac| ac.attacking.as_ref()) + .is_some() + { + user_error( + "Recline... while fighting? You can't figure out how to make it work".to_owned(), + )? + } + if ctx.item.active_climb.is_some() { + user_error( + "Recline... while climbing? You can't figure out how to make it work".to_owned(), + )? + } + match ctx.item.action_type { + LocationActionType::Reclining { .. } => { + user_error("You're already reclining.".to_owned())? + } + _ => {} + } + match item_ref { + None => {} + Some(item_ref) => { + let (item_type, item_code) = match item_ref.split_once("/") { + None => user_error("Invalid item ref in Reclining command".to_owned())?, + Some(v) => v, + }; + match ctx + .trans + .find_item_by_type_code(&item_type, &item_code) + .await? + { + None => user_error("Item not found".to_owned())?, + Some(item) => { + if item.location != ctx.item.location { + user_error(format!( + "You try to reclining on {} but realise it's no longer here", + item.display_for_sentence(ctx.explicit().await?, 1, false) + ))? + } + } + } + } + }; + Ok(time::Duration::from_secs(1)) + } + + #[allow(unreachable_patterns)] + async fn finish_command(&self, ctx: &mut QueuedCommandContext<'_>) -> UResult<()> { + if ctx.item.death_data.is_some() { + user_error( + "You try to recline, but it turns out the dead can't even do that!".to_owned(), + )?; + } + let item_ref = match ctx.command { + QueueCommand::Recline { item } => item, + _ => user_error("Unexpected command".to_owned())?, + }; + if ctx + .item + .active_combat + .as_ref() + .and_then(|ac| ac.attacking.as_ref()) + .is_some() + { + user_error( + "Recline... while fighting? You can't figure out how to make it work".to_owned(), + )? + } + if ctx.item.active_climb.is_some() { + user_error( + "Recline... while climbing? You can't figure out how to make it work".to_owned(), + )? + } + match ctx.item.action_type { + LocationActionType::Reclining { .. } => { + user_error("You're already reclining.".to_owned())? + } + _ => {} + } + let (item, desc_exp, desc_nonexp) = match item_ref { + None => (None, "the floor".to_owned(), "the floor".to_owned()), + Some(item_ref) => { + let (item_type, item_code) = match item_ref.split_once("/") { + None => user_error("Invalid item ref in Sit command".to_owned())?, + Some(v) => v, + }; + match ctx + .trans + .find_item_by_type_code(&item_type, &item_code) + .await? + { + None => user_error("Item not found".to_owned())?, + Some(item) => { + if item.location != ctx.item.location { + user_error(format!( + "You try to recline on {} but realise it's no longer here", + item.display_for_sentence(ctx.explicit().await?, 1, false) + ))? + } + ( + Some(item.clone()), + item.display_for_sentence(true, 1, false), + item.display_for_sentence(false, 1, false), + ) + } + } + } + }; + let msg_exp = format!( + "{} reclines on {}.\n", + &ctx.item.display_for_sentence(true, 1, true), + &desc_exp + ); + let msg_nonexp = format!( + "{} reclines on {}.\n", + &ctx.item.display_for_sentence(false, 1, true), + &desc_nonexp + ); + broadcast_to_room( + ctx.trans, + &ctx.item.location, + None, + &msg_exp, + Some(&msg_nonexp), + ) + .await?; + + ctx.item.action_type = LocationActionType::Reclining(item.map(|it| it.refstr())); + Ok(()) + } +} + +pub struct Verb; +#[async_trait] +impl UserVerb for Verb { + async fn handle( + self: &Self, + ctx: &mut VerbContext, + _verb: &str, + mut remaining: &str, + ) -> UResult<()> { + let player_item = get_player_item_or_fail(ctx).await?; + + if remaining.starts_with("on ") { + remaining = remaining[3..].trim(); + } + if remaining == "" { + remaining = "floor"; + } + + let target = if remaining == "here" || remaining == "ground" || remaining == "floor" { + None + } else { + Some( + search_item_for_user( + ctx, + &ItemSearchParams { + include_loc_contents: true, + ..ItemSearchParams::base(&player_item, &remaining) + }, + ) + .await?, + ) + }; + + if player_item.death_data.is_some() { + user_error( + "You try to recline, but it turns out the dead can't even do that!".to_owned(), + )?; + } + + if let Some(target) = target.as_ref() { + if target + .static_data() + .and_then(|pd| pd.sit_data.as_ref()) + .is_none() + { + user_error("You can't recline on that!".to_owned())?; + } + } + queue_command_and_save( + ctx, + &player_item, + &QueueCommand::Recline { + item: target.map(|t| t.refstr()), + }, + ) + .await?; + Ok(()) + } +} +static VERB_INT: Verb = Verb; +pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef; diff --git a/blastmud_game/src/message_handler/user_commands/stand.rs b/blastmud_game/src/message_handler/user_commands/stand.rs new file mode 100644 index 00000000..41d9f650 --- /dev/null +++ b/blastmud_game/src/message_handler/user_commands/stand.rs @@ -0,0 +1,91 @@ +use super::{get_player_item_or_fail, user_error, UResult, UserVerb, UserVerbRef, VerbContext}; +use crate::{ + models::item::LocationActionType, + regular_tasks::queued_command::{ + queue_command_and_save, QueueCommand, QueueCommandHandler, QueuedCommandContext, + }, + services::comms::broadcast_to_room, +}; +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 { + if ctx.item.death_data.is_some() { + user_error( + "You try to stand, but it turns out the dead can't even do that!".to_owned(), + )?; + } + match ctx.command { + QueueCommand::Stand => {} + _ => user_error("Unexpected command".to_owned())?, + }; + match ctx.item.action_type { + LocationActionType::Sitting { .. } | LocationActionType::Reclining { .. } => {} + _ => user_error("You're already standing.".to_owned())?, + } + Ok(time::Duration::from_secs(1)) + } + + #[allow(unreachable_patterns)] + async fn finish_command(&self, ctx: &mut QueuedCommandContext<'_>) -> UResult<()> { + if ctx.item.death_data.is_some() { + user_error( + "You try to stand, but it turns out the dead can't even do that!".to_owned(), + )?; + } + match ctx.command { + QueueCommand::Stand => {} + _ => user_error("Unexpected command".to_owned())?, + }; + match ctx.item.action_type { + LocationActionType::Sitting { .. } | LocationActionType::Reclining { .. } => {} + _ => user_error("You're already standing.".to_owned())?, + } + let msg_exp = format!( + "{} stands up.\n", + &ctx.item.display_for_sentence(true, 1, true), + ); + let msg_nonexp = format!( + "{} stands up.\n", + &ctx.item.display_for_sentence(false, 1, true), + ); + broadcast_to_room( + ctx.trans, + &ctx.item.location, + None, + &msg_exp, + Some(&msg_nonexp), + ) + .await?; + + ctx.item.action_type = LocationActionType::Normal; + Ok(()) + } +} + +pub struct Verb; +#[async_trait] +impl UserVerb for Verb { + async fn handle( + self: &Self, + ctx: &mut VerbContext, + _verb: &str, + _remaining: &str, + ) -> UResult<()> { + let player_item = get_player_item_or_fail(ctx).await?; + + if player_item.death_data.is_some() { + user_error( + "You try to stand, but it turns out the dead can't even do that!".to_owned(), + )?; + } + + queue_command_and_save(ctx, &player_item, &QueueCommand::Stand).await?; + Ok(()) + } +} +static VERB_INT: Verb = Verb; +pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef; diff --git a/blastmud_game/src/regular_tasks/queued_command.rs b/blastmud_game/src/regular_tasks/queued_command.rs index 8bc0270a..6a03e4f3 100644 --- a/blastmud_game/src/regular_tasks/queued_command.rs +++ b/blastmud_game/src/regular_tasks/queued_command.rs @@ -2,8 +2,8 @@ use super::{TaskHandler, TaskRunContext}; #[double] use crate::db::DBTrans; use crate::message_handler::user_commands::{ - close, cut, drink, drop, eat, fill, get, improvise, make, movement, open, put, remove, sit, - use_cmd, user_error, wear, wield, CommandHandlingError, UResult, VerbContext, + close, cut, drink, drop, eat, fill, get, improvise, make, movement, open, put, recline, remove, + sit, stand, use_cmd, user_error, wear, wield, CommandHandlingError, UResult, VerbContext, }; use crate::message_handler::ListenerSession; use crate::models::session::Session; @@ -96,12 +96,16 @@ pub enum QueueCommand { container_possession_id: String, target_possession_id: String, }, + Recline { + item: Option, + }, Remove { possession_id: String, }, Sit { item: Option, }, + Stand, Use { possession_id: String, target_id: String, @@ -137,8 +141,10 @@ impl QueueCommand { Movement { .. } => "Movement", OpenDoor { .. } => "OpenDoor", Put { .. } => "Put", + Recline { .. } => "Recline", Remove { .. } => "Remove", Sit { .. } => "Sit", + Stand { .. } => "Stand", Use { .. } => "Use", Wear { .. } => "Wear", Wield { .. } => "Wield", @@ -235,6 +241,10 @@ fn queue_command_registry( "Put", &put::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), ), + ( + "Recline", + &recline::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), + ), ( "Remove", &remove::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), @@ -243,6 +253,10 @@ fn queue_command_registry( "Sit", &sit::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), ), + ( + "Stand", + &stand::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), + ), ( "Use", &use_cmd::QueueHandler as &(dyn QueueCommandHandler + Sync + Send),