From 863ba692f4a547288429ed35afe5c7d021a4ff93 Mon Sep 17 00:00:00 2001 From: Condorra Date: Sun, 26 Feb 2023 02:01:48 +1100 Subject: [PATCH] Possessions eventually expire if dropped in a public place. --- .../src/message_handler/user_commands/drop.rs | 96 ++++++++++++++++++- blastmud_game/src/models/item.rs | 3 +- blastmud_game/src/models/task.rs | 6 +- blastmud_game/src/regular_tasks.rs | 2 + blastmud_game/src/services/combat.rs | 11 ++- 5 files changed, 110 insertions(+), 8 deletions(-) diff --git a/blastmud_game/src/message_handler/user_commands/drop.rs b/blastmud_game/src/message_handler/user_commands/drop.rs index 4c58fe5..dd0e5a9 100644 --- a/blastmud_game/src/message_handler/user_commands/drop.rs +++ b/blastmud_game/src/message_handler/user_commands/drop.rs @@ -11,10 +11,14 @@ use super::{ }; use crate::{ static_content::possession_type::possession_data, - regular_tasks::queued_command::{ - QueueCommandHandler, - QueueCommand, - queue_command + regular_tasks::{ + queued_command::{ + QueueCommandHandler, + QueueCommand, + queue_command + }, + TaskHandler, + TaskRunContext, }, services::{ broadcast_to_room, @@ -23,10 +27,91 @@ use crate::{ CapacityLevel, } }, - models::item::LocationActionType, + 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> { + 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] @@ -106,6 +191,7 @@ impl QueueCommandHandler for QueueHandler { 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(()) diff --git a/blastmud_game/src/models/item.rs b/blastmud_game/src/models/item.rs index 8a174e5..9339d66 100644 --- a/blastmud_game/src/models/item.rs +++ b/blastmud_game/src/models/item.rs @@ -255,7 +255,8 @@ pub enum Sex { #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum ItemFlag { NoSay, - NoSeeContents + NoSeeContents, + DroppedItemsDontExpire } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] diff --git a/blastmud_game/src/models/task.rs b/blastmud_game/src/models/task.rs index 1a2b3f3..1806703 100644 --- a/blastmud_game/src/models/task.rs +++ b/blastmud_game/src/models/task.rs @@ -33,7 +33,10 @@ pub enum TaskDetails { DelayedHealth { item: String, effect_series: VecDeque - } + }, + ExpireItem { + item_code: String + }, } impl TaskDetails { pub fn name(self: &Self) -> &'static str { @@ -47,6 +50,7 @@ impl TaskDetails { RecloneNPC { .. } => "RecloneNPC", RotCorpse { .. } => "RotCorpse", DelayedHealth { .. } => "DelayedHealth", + ExpireItem { .. } => "ExpireItem", } } } diff --git a/blastmud_game/src/regular_tasks.rs b/blastmud_game/src/regular_tasks.rs index 894325a..8dc9558 100644 --- a/blastmud_game/src/regular_tasks.rs +++ b/blastmud_game/src/regular_tasks.rs @@ -7,6 +7,7 @@ use crate::{ listener::{ListenerMap, ListenerSend}, static_content::npc, services::{combat, effect}, + message_handler::user_commands::drop, }; #[cfg(not(test))] use crate::models::task::{TaskParse, TaskRecurrence}; use mockall_double::double; @@ -43,6 +44,7 @@ fn task_handler_registry() -> &'static BTreeMap<&'static str, &'static (dyn Task ("RecloneNPC", npc::RECLONE_HANDLER.clone()), ("RotCorpse", combat::ROT_CORPSE_HANDLER.clone()), ("DelayedHealth", effect::DELAYED_HEALTH_HANDLER.clone()), + ("ExpireItem", drop::EXPIRE_ITEM_HANDLER.clone()), ).into_iter().collect() ) } diff --git a/blastmud_game/src/services/combat.rs b/blastmud_game/src/services/combat.rs index 821597b..ed31d1b 100644 --- a/blastmud_game/src/services/combat.rs +++ b/blastmud_game/src/services/combat.rs @@ -12,7 +12,7 @@ use crate::{ possession_type::{WeaponData, possession_data, fist}, npc::npc_by_code, }, - message_handler::user_commands::{user_error, UResult}, + message_handler::user_commands::{user_error, UResult, drop::consider_expire_job_for_item}, regular_tasks::{TaskRunContext, TaskHandler}, DResult, }; @@ -504,6 +504,15 @@ impl TaskHandler for RotCorpseTaskHandler { corpse.display_for_sentence(true, 1, true)); let msg_nonexp = format!("{} rots away to nothing.\n", corpse.display_for_sentence(false, 1, true)); + + for item in ctx.trans.find_items_by_location( + &format!("{}/{}", &corpse.item_type, &corpse.item_code)).await?.into_iter() { + let mut item_mut = (*item).clone(); + // We only update this to support consider_expire_job - it gets updated in bulk + // by transfer_all_possession below. + item_mut.location = corpse.location.clone(); + consider_expire_job_for_item(ctx.trans, &item_mut).await?; + } ctx.trans.transfer_all_possessions_code( &format!("{}/{}", &corpse.item_type, &corpse.item_code), &corpse.location).await?;