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 { 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;