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

215 lines
8.5 KiB
Rust

use super::{
VerbContext,
UserVerb,
UserVerbRef,
UResult,
ItemSearchParams,
UserError,
user_error,
get_player_item_or_fail,
search_items_for_user,
parsing::parse_count
};
use crate::{
static_content::possession_type::possession_data,
regular_tasks::queued_command::{
QueueCommandHandler,
QueueCommand,
queue_command
},
services::{
comms::broadcast_to_room,
skills::calculate_total_stats_skills_for_user,
},
models::item::{
LocationActionType,
Buff,
BuffCause,
BuffImpact,
SkillType,
},
};
use async_trait::async_trait;
use chrono::Utc;
use std::time;
pub struct QueueHandler;
#[async_trait]
impl QueueCommandHandler for QueueHandler {
async fn start_command(&self, ctx: &mut VerbContext<'_>, command: &QueueCommand)
-> UResult<time::Duration> {
let player_item = get_player_item_or_fail(ctx).await?;
if player_item.death_data.is_some() {
user_error("You try to wear it, but your ghostly hands slip through it uselessly".to_owned())?;
}
let item_id = match command {
QueueCommand::Wear { possession_id } => possession_id,
_ => user_error("Unexpected command".to_owned())?
};
let item = match ctx.trans.find_item_by_type_code("possession", &item_id).await? {
None => user_error("Item not found".to_owned())?,
Some(it) => it
};
if item.location != player_item.refstr() {
user_error(
format!("You try to wear {} but realise you no longer have it",
item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)
)
)?
}
if item.action_type == LocationActionType::Worn {
user_error("You realise you're already wearing it!".to_owned())?;
}
let poss_data = item.possession_type.as_ref()
.and_then(|pt| possession_data().get(&pt))
.ok_or_else(|| UserError(
"That item no longer exists in the game so can't be handled".to_owned()))?;
poss_data.wear_data.as_ref().ok_or_else(
|| UserError("You can't wear that!".to_owned()))?;
let msg_exp = format!("{} fumbles around trying to put on {}\n",
&player_item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false));
let msg_nonexp = format!("{} fumbles around trying to put on {}\n",
&player_item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false));
broadcast_to_room(ctx.trans, &player_item.location, None, &msg_exp, Some(&msg_nonexp)).await?;
Ok(time::Duration::from_secs(1))
}
#[allow(unreachable_patterns)]
async fn finish_command(&self, ctx: &mut VerbContext<'_>, command: &QueueCommand)
-> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?;
if player_item.death_data.is_some() {
user_error("You try to wear it, but your ghostly hands slip through it uselessly".to_owned())?;
}
let item_id = match command {
QueueCommand::Wear { possession_id } => possession_id,
_ => user_error("Unexpected command".to_owned())?
};
let item = match ctx.trans.find_item_by_type_code("possession", &item_id).await? {
None => user_error("Item not found".to_owned())?,
Some(it) => it
};
if item.location != player_item.refstr() {
user_error(format!("You try to wear {} but realise it is no longer there.",
&item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)))?
}
if item.action_type == LocationActionType::Worn {
user_error("You realise you're already wearing it!".to_owned())?;
}
let poss_data = item.possession_type.as_ref()
.and_then(|pt| possession_data().get(&pt))
.ok_or_else(|| UserError(
"That item no longer exists in the game so can't be handled".to_owned()))?;
let wear_data = poss_data.wear_data.as_ref().ok_or_else(
|| UserError("You can't wear that!".to_owned()))?;
let other_clothes =
ctx.trans.find_by_action_and_location(
&player_item.refstr(), &LocationActionType::Worn).await?;
for part in &wear_data.covers_parts {
let thickness: f64 =
other_clothes.iter().fold(
wear_data.thickness,
|tot, other_item|
match other_item.possession_type.as_ref()
.and_then(|pt| possession_data().get(&pt))
.and_then(|pd| pd.wear_data.as_ref())
{
Some(wd) if wd.covers_parts.contains(&part) =>
tot + wd.thickness,
_ => tot,
}
);
if thickness > 12.0 {
user_error(format!(
"You're wearing too much on your {} already.",
part.display(player_item.sex.clone())
))?;
}
}
let msg_exp = format!("{} wears {}\n",
&player_item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false));
let msg_nonexp = format!("{} wears {}\n",
&player_item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false));
broadcast_to_room(ctx.trans, &player_item.location, None, &msg_exp, Some(&msg_nonexp)).await?;
let mut item_mut = (*item).clone();
item_mut.action_type = LocationActionType::Worn;
item_mut.action_type_started = Some(Utc::now());
if wear_data.dodge_penalty != 0.0 {
let mut player_item_mut = (*player_item).clone();
player_item_mut.temporary_buffs.push(Buff {
description: "Dodge penalty".to_owned(),
cause: BuffCause::ByItem {
item_type: item_mut.item_type.clone(),
item_code: item_mut.item_code.clone(),
},
impacts: vec!(BuffImpact::ChangeSkill {
skill: SkillType::Dodge,
magnitude: -wear_data.dodge_penalty
})
});
if let Some(ref usr) = ctx.user_dat {
calculate_total_stats_skills_for_user(&mut player_item_mut, usr);
}
ctx.trans.save_item_model(&player_item_mut).await?;
}
ctx.trans.save_item_model(&item_mut).await?;
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?;
let mut get_limit = Some(1);
if remaining == "all" || remaining.starts_with("all ") {
remaining = remaining[3..].trim();
get_limit = None;
} else if let (Some(n), remaining2) = parse_count(remaining) {
get_limit = Some(n);
remaining = remaining2;
}
let targets = search_items_for_user(ctx, &ItemSearchParams {
include_contents: true,
item_type_only: Some("possession"),
limit: get_limit.unwrap_or(100),
item_action_type_only: Some(&LocationActionType::Normal),
..ItemSearchParams::base(&player_item, &remaining)
}).await?;
if player_item.death_data.is_some() {
user_error("The dead don't dress themselves".to_owned())?;
}
let mut did_anything: bool = false;
for target in targets.iter().filter(|t| t.action_type.is_visible_in_look()) {
if target.item_type != "possession" {
user_error("You can't wear that!".to_owned())?;
}
did_anything = true;
queue_command(ctx, &QueueCommand::Wear { possession_id: target.item_code.clone() }).await?;
}
if !did_anything {
user_error("I didn't find anything matching.".to_owned())?;
}
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;