237 lines
8.5 KiB
Rust
237 lines
8.5 KiB
Rust
use super::{
|
|
VerbContext,
|
|
UserVerb,
|
|
UserVerbRef,
|
|
UResult,
|
|
ItemSearchParams,
|
|
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
|
|
},
|
|
TaskHandler,
|
|
TaskRunContext,
|
|
},
|
|
services::{
|
|
broadcast_to_room,
|
|
capacity::{
|
|
check_item_capacity,
|
|
CapacityLevel,
|
|
}
|
|
},
|
|
DResult,
|
|
models::{
|
|
item::{LocationActionType, Item, ItemFlag},
|
|
task::{Task, TaskMeta, TaskDetails},
|
|
},
|
|
};
|
|
use async_trait::async_trait;
|
|
use std::time;
|
|
use chrono::Utc;
|
|
use mockall_double::double;
|
|
#[double] use crate::db::DBTrans;
|
|
|
|
pub struct ExpireItemTaskHandler;
|
|
#[async_trait]
|
|
impl TaskHandler for ExpireItemTaskHandler {
|
|
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
|
|
let item_code = match &mut ctx.task.details {
|
|
TaskDetails::ExpireItem { item_code } => item_code,
|
|
_ => Err("Expected ExpireItem type")?
|
|
};
|
|
let item = match ctx.trans.find_item_by_type_code("possession", item_code).await? {
|
|
None => {
|
|
return Ok(None);
|
|
}
|
|
Some(it) => it
|
|
};
|
|
let (loc_type, loc_code) = match item.location.split_once("/") {
|
|
None => return Ok(None),
|
|
Some(p) => p
|
|
};
|
|
|
|
if loc_type != "room" {
|
|
return Ok(None);
|
|
}
|
|
|
|
let loc_item = match ctx.trans.find_item_by_type_code(loc_type, loc_code).await? {
|
|
None => return Ok(None),
|
|
Some(i) => i
|
|
};
|
|
|
|
if loc_item.flags.contains(&ItemFlag::DroppedItemsDontExpire) {
|
|
return Ok(None);
|
|
}
|
|
|
|
ctx.trans.delete_item("possession", item_code).await?;
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
pub static EXPIRE_ITEM_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &ExpireItemTaskHandler;
|
|
|
|
pub async fn consider_expire_job_for_item(trans: &DBTrans, item: &Item) -> DResult<()> {
|
|
let (loc_type, loc_code) = match item.location.split_once("/") {
|
|
None => return Ok(()),
|
|
Some(p) => p
|
|
};
|
|
|
|
if loc_type != "room" {
|
|
return Ok(())
|
|
}
|
|
|
|
let loc_item = match trans.find_item_by_type_code(loc_type, loc_code).await? {
|
|
None => return Ok(()),
|
|
Some(i) => i
|
|
};
|
|
|
|
if loc_item.flags.contains(&ItemFlag::DroppedItemsDontExpire) {
|
|
return Ok(());
|
|
}
|
|
|
|
trans.upsert_task(
|
|
&Task {
|
|
meta: TaskMeta {
|
|
task_code: format!("{}/{}", item.item_type, item.item_code),
|
|
next_scheduled: Utc::now() + chrono::Duration::hours(1),
|
|
..Default::default()
|
|
},
|
|
details: TaskDetails::ExpireItem {
|
|
item_code: item.item_code.clone()
|
|
}
|
|
}
|
|
).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
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.is_dead {
|
|
user_error("You try to drop it, but your ghostly hands slip through it uselessly".to_owned())?;
|
|
}
|
|
let item_id = match command {
|
|
QueueCommand::Drop { 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 != format!("{}/{}", &player_item.item_type, &player_item.item_code) {
|
|
user_error(
|
|
format!("You try to drop {} but realise you no longer have it",
|
|
item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)
|
|
)
|
|
)?
|
|
}
|
|
let msg_exp = format!("{} prepares to drop {}\n",
|
|
&player_item.display_for_sentence(true, 1, true),
|
|
&item.display_for_sentence(true, 1, false));
|
|
let msg_nonexp = format!("{} prepares to drop {}\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.is_dead {
|
|
user_error("You try to get it, but your ghostly hands slip through it uselessly".to_owned())?;
|
|
}
|
|
let item_id = match command {
|
|
QueueCommand::Drop { 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 != format!("{}/{}", &player_item.item_type, &player_item.item_code) {
|
|
user_error(format!("You try to drop {} but realise you no longer have it!",
|
|
&item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)))?
|
|
}
|
|
|
|
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, &player_item.location, possession_data.weight).await? {
|
|
CapacityLevel::AboveItemLimit => user_error(
|
|
format!("You can't drop {}, because it is so cluttered here there is no where to put it!",
|
|
&item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)
|
|
),
|
|
)?,
|
|
_ => ()
|
|
}
|
|
|
|
let msg_exp = format!("{} drops {}\n",
|
|
&player_item.display_for_sentence(true, 1, true),
|
|
&item.display_for_sentence(true, 1, false));
|
|
let msg_nonexp = format!("{} drops {}\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.location = player_item.location.clone();
|
|
consider_expire_job_for_item(ctx.trans, &item_mut).await?;
|
|
item_mut.action_type = LocationActionType::Normal;
|
|
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),
|
|
..ItemSearchParams::base(&player_item, &remaining)
|
|
}).await?;
|
|
|
|
if player_item.is_dead {
|
|
user_error("You try to drop it, but your ghostly hands slip through it uselessly".to_owned())?;
|
|
}
|
|
|
|
for target in targets {
|
|
if target.item_type != "possession" {
|
|
user_error("You can't drop that!".to_owned())?;
|
|
}
|
|
queue_command(ctx, &QueueCommand::Drop { possession_id: target.item_code.clone() }).await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
static VERB_INT: Verb = Verb;
|
|
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|