Possessions eventually expire if dropped in a public place.

This commit is contained in:
Condorra 2023-02-26 02:01:48 +11:00
parent 8085689490
commit 863ba692f4
5 changed files with 110 additions and 8 deletions

View File

@ -11,10 +11,14 @@ use super::{
}; };
use crate::{ use crate::{
static_content::possession_type::possession_data, static_content::possession_type::possession_data,
regular_tasks::queued_command::{ regular_tasks::{
QueueCommandHandler, queued_command::{
QueueCommand, QueueCommandHandler,
queue_command QueueCommand,
queue_command
},
TaskHandler,
TaskRunContext,
}, },
services::{ services::{
broadcast_to_room, broadcast_to_room,
@ -23,10 +27,91 @@ use crate::{
CapacityLevel, CapacityLevel,
} }
}, },
models::item::LocationActionType, DResult,
models::{
item::{LocationActionType, Item, ItemFlag},
task::{Task, TaskMeta, TaskDetails},
},
}; };
use async_trait::async_trait; use async_trait::async_trait;
use std::time; 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; pub struct QueueHandler;
#[async_trait] #[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?; broadcast_to_room(ctx.trans, &player_item.location, None, &msg_exp, Some(&msg_nonexp)).await?;
let mut item_mut = (*item).clone(); let mut item_mut = (*item).clone();
item_mut.location = player_item.location.clone(); item_mut.location = player_item.location.clone();
consider_expire_job_for_item(ctx.trans, &item_mut).await?;
item_mut.action_type = LocationActionType::Normal; item_mut.action_type = LocationActionType::Normal;
ctx.trans.save_item_model(&item_mut).await?; ctx.trans.save_item_model(&item_mut).await?;
Ok(()) Ok(())

View File

@ -255,7 +255,8 @@ pub enum Sex {
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ItemFlag { pub enum ItemFlag {
NoSay, NoSay,
NoSeeContents NoSeeContents,
DroppedItemsDontExpire
} }
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]

View File

@ -33,7 +33,10 @@ pub enum TaskDetails {
DelayedHealth { DelayedHealth {
item: String, item: String,
effect_series: VecDeque<DelayedHealthEffect> effect_series: VecDeque<DelayedHealthEffect>
} },
ExpireItem {
item_code: String
},
} }
impl TaskDetails { impl TaskDetails {
pub fn name(self: &Self) -> &'static str { pub fn name(self: &Self) -> &'static str {
@ -47,6 +50,7 @@ impl TaskDetails {
RecloneNPC { .. } => "RecloneNPC", RecloneNPC { .. } => "RecloneNPC",
RotCorpse { .. } => "RotCorpse", RotCorpse { .. } => "RotCorpse",
DelayedHealth { .. } => "DelayedHealth", DelayedHealth { .. } => "DelayedHealth",
ExpireItem { .. } => "ExpireItem",
} }
} }
} }

View File

@ -7,6 +7,7 @@ use crate::{
listener::{ListenerMap, ListenerSend}, listener::{ListenerMap, ListenerSend},
static_content::npc, static_content::npc,
services::{combat, effect}, services::{combat, effect},
message_handler::user_commands::drop,
}; };
#[cfg(not(test))] use crate::models::task::{TaskParse, TaskRecurrence}; #[cfg(not(test))] use crate::models::task::{TaskParse, TaskRecurrence};
use mockall_double::double; use mockall_double::double;
@ -43,6 +44,7 @@ fn task_handler_registry() -> &'static BTreeMap<&'static str, &'static (dyn Task
("RecloneNPC", npc::RECLONE_HANDLER.clone()), ("RecloneNPC", npc::RECLONE_HANDLER.clone()),
("RotCorpse", combat::ROT_CORPSE_HANDLER.clone()), ("RotCorpse", combat::ROT_CORPSE_HANDLER.clone()),
("DelayedHealth", effect::DELAYED_HEALTH_HANDLER.clone()), ("DelayedHealth", effect::DELAYED_HEALTH_HANDLER.clone()),
("ExpireItem", drop::EXPIRE_ITEM_HANDLER.clone()),
).into_iter().collect() ).into_iter().collect()
) )
} }

View File

@ -12,7 +12,7 @@ use crate::{
possession_type::{WeaponData, possession_data, fist}, possession_type::{WeaponData, possession_data, fist},
npc::npc_by_code, 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}, regular_tasks::{TaskRunContext, TaskHandler},
DResult, DResult,
}; };
@ -504,6 +504,15 @@ impl TaskHandler for RotCorpseTaskHandler {
corpse.display_for_sentence(true, 1, true)); corpse.display_for_sentence(true, 1, true));
let msg_nonexp = format!("{} rots away to nothing.\n", let msg_nonexp = format!("{} rots away to nothing.\n",
corpse.display_for_sentence(false, 1, true)); 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( ctx.trans.transfer_all_possessions_code(
&format!("{}/{}", &corpse.item_type, &corpse.item_code), &format!("{}/{}", &corpse.item_type, &corpse.item_code),
&corpse.location).await?; &corpse.location).await?;