From 3e4e44840408c9a53fa1068c449405c7a2fb191d Mon Sep 17 00:00:00 2001 From: Condorra Date: Sat, 2 Sep 2023 23:48:43 +1000 Subject: [PATCH] Implement sit command. --- .../src/message_handler/user_commands.rs | 2 + .../src/message_handler/user_commands/look.rs | 26 ++- .../src/message_handler/user_commands/sit.rs | 213 ++++++++++++++++++ blastmud_game/src/models/item.rs | 4 +- .../src/regular_tasks/queued_command.rs | 12 +- blastmud_game/src/services/combat.rs | 2 +- blastmud_game/src/services/urges.rs | 4 +- .../src/static_content/possession_type.rs | 6 + gear.rs | 0 9 files changed, 260 insertions(+), 9 deletions(-) create mode 100644 blastmud_game/src/message_handler/user_commands/sit.rs create mode 100644 gear.rs diff --git a/blastmud_game/src/message_handler/user_commands.rs b/blastmud_game/src/message_handler/user_commands.rs index 9394216e..e1e99c90 100644 --- a/blastmud_game/src/message_handler/user_commands.rs +++ b/blastmud_game/src/message_handler/user_commands.rs @@ -61,6 +61,7 @@ mod reset_spawns; pub mod say; mod score; mod sign; +pub mod sit; mod status; mod uninstall; pub mod use_cmd; @@ -219,6 +220,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! { "score" => score::VERB, "sign" => sign::VERB, + "sit" => sit::VERB, "st" => status::VERB, "stat" => status::VERB, diff --git a/blastmud_game/src/message_handler/user_commands/look.rs b/blastmud_game/src/message_handler/user_commands/look.rs index 0dca68de..d8093d79 100644 --- a/blastmud_game/src/message_handler/user_commands/look.rs +++ b/blastmud_game/src/message_handler/user_commands/look.rs @@ -524,8 +524,30 @@ async fn list_room_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> URe " is " }); match head.action_type { - LocationActionType::Sitting => buf.push_str("sitting "), - LocationActionType::Reclining => buf.push_str("reclining "), + LocationActionType::Sitting(ref on) => { + buf.push_str("sitting "); + if let Some((on_type, on_code)) = + on.as_ref().and_then(|on_ref| on_ref.split_once("/")) + { + if let Some(sit_on) = ctx.trans.find_item_by_type_code(on_type, on_code).await? + { + buf.push_str("on "); + buf.push_str(&sit_on.display_for_session(&ctx.session_dat)); + } + } + } + LocationActionType::Reclining(ref on) => { + buf.push_str("reclining "); + if let Some((on_type, on_code)) = + on.as_ref().and_then(|on_ref| on_ref.split_once("/")) + { + if let Some(sit_on) = ctx.trans.find_item_by_type_code(on_type, on_code).await? + { + buf.push_str("on "); + buf.push_str(&sit_on.display_for_session(&ctx.session_dat)); + } + } + } LocationActionType::Normal | LocationActionType::Attacking(_) if is_creature => { if head.death_data.is_some() { buf.push_str("lying "); diff --git a/blastmud_game/src/message_handler/user_commands/sit.rs b/blastmud_game/src/message_handler/user_commands/sit.rs new file mode 100644 index 00000000..997a676d --- /dev/null +++ b/blastmud_game/src/message_handler/user_commands/sit.rs @@ -0,0 +1,213 @@ +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 sit, but it turns out the dead can't even do that!".to_owned())?; + } + let item_ref = match ctx.command { + QueueCommand::Sit { 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( + "Sit... while fighting? You can't figure out how to make it work".to_owned(), + )? + } + if ctx.item.active_climb.is_some() { + user_error( + "Sit... while climbing? You can't figure out how to make it work".to_owned(), + )? + } + match ctx.item.action_type { + LocationActionType::Sitting { .. } => user_error("You're already sitting.".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 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 sit 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 sit, but it turns out the dead can't even do that!".to_owned())?; + } + let item_ref = match ctx.command { + QueueCommand::Sit { 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( + "Sit... while fighting? You can't figure out how to make it work".to_owned(), + )? + } + if ctx.item.active_climb.is_some() { + user_error( + "Sit... while climbing? You can't figure out how to make it work".to_owned(), + )? + } + match ctx.item.action_type { + LocationActionType::Sitting { .. } => user_error("You're already sitting.".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 sit 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!( + "{} sits on {}.\n", + &ctx.item.display_for_sentence(true, 1, true), + &desc_exp + ); + let msg_nonexp = format!( + "{} sits 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::Sitting(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 sit, 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 sit on that!".to_owned())?; + } + } + queue_command_and_save( + ctx, + &player_item, + &QueueCommand::Sit { + 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/models/item.rs b/blastmud_game/src/models/item.rs index 0af52e26..44546565 100644 --- a/blastmud_game/src/models/item.rs +++ b/blastmud_game/src/models/item.rs @@ -264,8 +264,8 @@ pub enum Subattack { #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum LocationActionType { Normal, - Sitting, - Reclining, + Sitting(Option), + Reclining(Option), Worn, // Clothing etc... Wielded, Attacking(Subattack), diff --git a/blastmud_game/src/regular_tasks/queued_command.rs b/blastmud_game/src/regular_tasks/queued_command.rs index 205b6c87..8bc0270a 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, use_cmd, - user_error, wear, wield, CommandHandlingError, UResult, VerbContext, + close, cut, drink, drop, eat, fill, get, improvise, make, movement, open, put, remove, sit, + use_cmd, user_error, wear, wield, CommandHandlingError, UResult, VerbContext, }; use crate::message_handler::ListenerSession; use crate::models::session::Session; @@ -99,6 +99,9 @@ pub enum QueueCommand { Remove { possession_id: String, }, + Sit { + item: Option, + }, Use { possession_id: String, target_id: String, @@ -135,6 +138,7 @@ impl QueueCommand { OpenDoor { .. } => "OpenDoor", Put { .. } => "Put", Remove { .. } => "Remove", + Sit { .. } => "Sit", Use { .. } => "Use", Wear { .. } => "Wear", Wield { .. } => "Wield", @@ -235,6 +239,10 @@ fn queue_command_registry( "Remove", &remove::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), ), + ( + "Sit", + &sit::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), + ), ( "Use", &use_cmd::QueueHandler as &(dyn QueueCommandHandler + Sync + Send), diff --git a/blastmud_game/src/services/combat.rs b/blastmud_game/src/services/combat.rs index 5a30f92e..a25a7fd1 100644 --- a/blastmud_game/src/services/combat.rs +++ b/blastmud_game/src/services/combat.rs @@ -669,7 +669,7 @@ pub async fn start_attack_mut( let mut msg_nonexp = String::new(); let mut verb: String = "attacks".to_string(); match by_whom.action_type { - LocationActionType::Sitting | LocationActionType::Reclining => { + LocationActionType::Sitting { .. } | LocationActionType::Reclining { .. } => { msg_exp.push_str(&format!(ansi!("{} stands up.\n"), &by_whom.display)); msg_nonexp.push_str(&format!( ansi!("{} stands up.\n"), diff --git a/blastmud_game/src/services/urges.rs b/blastmud_game/src/services/urges.rs index c3e7d4e3..4de3709d 100644 --- a/blastmud_game/src/services/urges.rs +++ b/blastmud_game/src/services/urges.rs @@ -348,8 +348,8 @@ pub async fn set_has_urges_if_needed(trans: &DBTrans, player_item: &mut Item) -> pub async fn recalculate_urge_growth(_trans: &DBTrans, item: &mut Item) -> DResult<()> { let cool = item.total_stats.get(&StatType::Cool).unwrap_or(&8.0); let relax_action_factor = match item.action_type { - LocationActionType::Sitting => 100.0, - LocationActionType::Reclining => 150.0, + LocationActionType::Sitting(_) => 100.0, + LocationActionType::Reclining(_) => 150.0, LocationActionType::Attacking(_) => 0.5, _ => 1.0, }; diff --git a/blastmud_game/src/static_content/possession_type.rs b/blastmud_game/src/static_content/possession_type.rs index cb950fe3..05c56b20 100644 --- a/blastmud_game/src/static_content/possession_type.rs +++ b/blastmud_game/src/static_content/possession_type.rs @@ -305,6 +305,10 @@ pub struct EatData { pub thirst_impact: i16, } +pub struct SitData { + pub stress_impact: i16, +} + pub struct PossessionData { pub weapon_data: Option, pub display: &'static str, @@ -328,6 +332,7 @@ pub struct PossessionData { pub liquid_container_data: Option, pub default_flags: Vec, pub eat_data: Option, + pub sit_data: Option, } impl Default for PossessionData { @@ -355,6 +360,7 @@ impl Default for PossessionData { liquid_container_data: None, default_flags: vec![], eat_data: None, + sit_data: None, } } } diff --git a/gear.rs b/gear.rs new file mode 100644 index 00000000..e69de29b