blastmud/blastmud_game/src/message_handler/user_commands/get.rs
Condorra 590d4640dd Implement craft on benches
Initially just a stove
Also update Rust.
2023-07-24 22:46:50 +10:00

352 lines
14 KiB
Rust

use super::{
get_player_item_or_fail, parsing::parse_count, search_item_for_user, search_items_for_user,
user_error, ItemSearchParams, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
};
use crate::{
models::item::LocationActionType,
regular_tasks::queued_command::{
queue_command, QueueCommand, QueueCommandHandler, QueuedCommandContext,
},
services::{
capacity::{check_item_capacity, recalculate_container_weight, CapacityLevel},
comms::broadcast_to_room,
},
static_content::possession_type::possession_data,
};
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> {
if ctx.item.death_data.is_some() {
user_error(
"You try to get it, but your ghostly hands slip through it uselessly".to_owned(),
)?;
}
match ctx.command {
QueueCommand::Get { possession_id } => {
let item = match ctx
.trans
.find_item_by_type_code("possession", &possession_id)
.await?
{
None => user_error("Item not found".to_owned())?,
Some(it) => it,
};
if item.location != ctx.item.location {
user_error(format!(
"You try to get {} but realise it is no longer there",
item.display_for_sentence(ctx.explicit().await?, 1, false)
))?
}
let msg_exp = format!(
"{} fumbles around trying to pick up {}\n",
&ctx.item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false)
);
let msg_nonexp = format!(
"{} fumbles around trying to pick up {}\n",
&ctx.item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false)
);
broadcast_to_room(
ctx.trans,
&ctx.item.location,
None,
&msg_exp,
Some(&msg_nonexp),
)
.await?;
}
QueueCommand::GetFromContainer {
from_possession_id,
get_possession_id,
} => {
let container = ctx
.trans
.find_item_by_type_code("possession", &from_possession_id)
.await?
.ok_or_else(|| UserError("Item to get from not found".to_owned()))?;
if container.location != ctx.item.location
&& container.location != ctx.item.refstr()
{
user_error(format!(
"You try to get something from {} but realise {} is no longer there",
container.display_for_sentence(ctx.explicit().await?, 1, false),
container.display_for_sentence(ctx.explicit().await?, 1, false),
))?
}
let item = ctx
.trans
.find_item_by_type_code("possession", &get_possession_id)
.await?
.ok_or_else(|| UserError("Item to get not found".to_owned()))?;
if item.location != container.refstr() {
user_error(format!(
"You try to get {} but realise it is no longer in {}",
item.display_for_sentence(ctx.explicit().await?, 1, false),
container.display_for_sentence(ctx.explicit().await?, 1, false),
))?
}
let msg_exp = format!(
"{} fumbles around trying to get {} from {}.\n",
&ctx.item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false),
&container.display_for_sentence(true, 1, false)
);
let msg_nonexp = format!(
"{} fumbles around trying to get {} from {}.\n",
&ctx.item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false),
&container.display_for_sentence(false, 1, false)
);
broadcast_to_room(
ctx.trans,
&ctx.item.location,
None,
&msg_exp,
Some(&msg_nonexp),
)
.await?;
}
_ => user_error("Unexpected command".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 get it, but your ghostly hands slip through it uselessly".to_owned(),
)?;
}
let (item, container_opt) = match ctx.command {
QueueCommand::Get { possession_id } => {
let item = match ctx
.trans
.find_item_by_type_code("possession", &possession_id)
.await?
{
None => user_error("Item not found".to_owned())?,
Some(it) => it,
};
if item.location != ctx.item.location {
user_error(format!(
"You try to get {} but realise it is no longer there",
&item.display_for_sentence(ctx.explicit().await?, 1, false)
))?
}
let msg_exp = format!(
"{} picks up {}\n",
&ctx.item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false)
);
let msg_nonexp = format!(
"{} picks up {}\n",
&ctx.item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false)
);
broadcast_to_room(
ctx.trans,
&ctx.item.location,
None,
&msg_exp,
Some(&msg_nonexp),
)
.await?;
(item, None)
}
QueueCommand::GetFromContainer {
from_possession_id,
get_possession_id,
} => {
let container = ctx
.trans
.find_item_by_type_code("possession", &from_possession_id)
.await?
.ok_or_else(|| UserError("Item to get from not found".to_owned()))?;
if container.location != ctx.item.location
&& container.location != ctx.item.refstr()
{
user_error(format!(
"You try to get something from {} but realise {} is no longer there",
container.display_for_sentence(ctx.explicit().await?, 1, false),
container.display_for_sentence(ctx.explicit().await?, 1, false),
))?
}
let item = ctx
.trans
.find_item_by_type_code("possession", &get_possession_id)
.await?
.ok_or_else(|| UserError("Item to get not found".to_owned()))?;
if item.location != container.refstr() {
user_error(format!(
"You try to get {} but realise it is no longer in {}",
item.display_for_sentence(ctx.explicit().await?, 1, false),
container.display_for_sentence(ctx.explicit().await?, 1, false),
))?
}
let msg_exp = format!(
"{} gets {} from {}.\n",
&ctx.item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false),
&container.display_for_sentence(true, 1, false)
);
let msg_nonexp = format!(
"{} gets {} from {}.\n",
&ctx.item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false),
&container.display_for_sentence(false, 1, false)
);
broadcast_to_room(
ctx.trans,
&ctx.item.location,
None,
&msg_exp,
Some(&msg_nonexp),
)
.await?;
(item, Some(container))
}
_ => user_error("Unexpected command".to_owned())?,
};
let possession_data = match item
.possession_type
.as_ref()
.and_then(|pt| possession_data().get(&pt))
{
None => {
user_error("That item no longer exists in the game so can't be handled".to_owned())?
}
Some(pd) => pd,
};
match check_item_capacity(ctx.trans, &ctx.item, possession_data.weight).await? {
CapacityLevel::AboveItemLimit => {
user_error("You just can't hold that many things!".to_owned())?
}
CapacityLevel::OverBurdened => {
let explicit = ctx.explicit().await?;
user_error(format!(
"{} You can't get {} because it is too heavy!",
if explicit { "Fuck!" } else { "Rats!" },
&item.display_for_sentence(explicit, 1, false)
))?
}
_ => (),
}
let mut item_mut = (*item).clone();
item_mut.location = ctx.item.refstr();
item_mut.action_type = LocationActionType::Normal;
ctx.trans.save_item_model(&item_mut).await?;
if let Some(container) = container_opt {
recalculate_container_weight(&ctx.trans, &container).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 (search_what, for_what, include_contents, include_loc_contents) =
match remaining.split_once(" from ") {
None => (player_item.clone(), remaining, false, true),
Some((item_str_raw, container_str_raw)) => {
let container = search_item_for_user(
ctx,
&ItemSearchParams {
include_loc_contents: true,
include_contents: true,
item_type_only: Some("possession"),
..ItemSearchParams::base(&player_item, container_str_raw.trim())
},
)
.await?;
(container, item_str_raw.trim(), true, false)
}
};
let targets = search_items_for_user(
ctx,
&ItemSearchParams {
include_loc_contents,
include_contents,
item_type_only: Some("possession"),
limit: get_limit.unwrap_or(100),
..ItemSearchParams::base(&search_what, for_what)
},
)
.await?;
if player_item.death_data.is_some() {
user_error(
"You try to get it, but your ghostly hands slip through it uselessly".to_owned(),
)?;
}
let mut did_anything: bool = false;
let mut player_item_mut = (*player_item).clone();
for target in targets
.iter()
.filter(|t| t.action_type.is_visible_in_look())
{
if target.item_type != "possession" {
user_error("You can't get that!".to_owned())?;
}
did_anything = true;
if include_loc_contents {
queue_command(
ctx,
&mut player_item_mut,
&QueueCommand::Get {
possession_id: target.item_code.clone(),
},
)
.await?;
} else {
queue_command(
ctx,
&mut player_item_mut,
&QueueCommand::GetFromContainer {
from_possession_id: search_what.item_code.clone(),
get_possession_id: target.item_code.clone(),
},
)
.await?;
}
}
if !did_anything {
user_error("I didn't find anything matching.".to_owned())?;
} else {
ctx.trans.save_item_model(&player_item_mut).await?;
}
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;