From 83a18cfd63d1308e0e4c17004215c8f6cd376473 Mon Sep 17 00:00:00 2001 From: Condorra Date: Sat, 14 Oct 2023 22:23:06 +1100 Subject: [PATCH] Improve effect system, including messages when effects wear off. --- .../src/message_handler/user_commands/look.rs | 18 +- .../message_handler/user_commands/use_cmd.rs | 40 +- blastmud_game/src/models.rs | 1 + blastmud_game/src/models/effect.rs | 29 ++ blastmud_game/src/models/item.rs | 4 +- blastmud_game/src/models/task.rs | 14 +- blastmud_game/src/regular_tasks.rs | 2 + blastmud_game/src/services/effect.rs | 171 +++++++- .../src/static_content/possession_type.rs | 34 +- .../possession_type/trauma_kit.rs | 400 ++++++++++-------- .../static_content/room/computer_museum.yaml | 2 +- .../static_content/room/general_hospital.rs | 109 ++--- 12 files changed, 517 insertions(+), 307 deletions(-) create mode 100644 blastmud_game/src/models/effect.rs diff --git a/blastmud_game/src/message_handler/user_commands/look.rs b/blastmud_game/src/message_handler/user_commands/look.rs index 7a82a8e..401dfc0 100644 --- a/blastmud_game/src/message_handler/user_commands/look.rs +++ b/blastmud_game/src/message_handler/user_commands/look.rs @@ -9,9 +9,12 @@ use crate::db::DBTrans; use crate::{ db::ItemSearchParams, language, - models::item::{ - DoorState, Item, ItemFlag, ItemSpecialData, LiquidDetails, LiquidType, LocationActionType, - Subattack, + models::{ + effect::EffectType, + item::{ + DoorState, Item, ItemFlag, ItemSpecialData, LiquidDetails, LiquidType, + LocationActionType, Subattack, + }, }, services::{combat::max_health, skills::calc_level_gap}, static_content::{ @@ -225,14 +228,7 @@ pub async fn describe_normal_item( )); } - if ctx - .trans - .check_task_by_type_code( - "DelayedHealth", - &format!("{}/{}/bandage", &item.item_type, &item.item_code), - ) - .await? - { + if item.active_effects.contains(&EffectType::Bandages) { contents_desc.push_str(&format!( "{} is wrapped up in bandages.\n", &language::caps_first(&item.pronouns.subject) diff --git a/blastmud_game/src/message_handler/user_commands/use_cmd.rs b/blastmud_game/src/message_handler/user_commands/use_cmd.rs index b757453..396134c 100644 --- a/blastmud_game/src/message_handler/user_commands/use_cmd.rs +++ b/blastmud_game/src/message_handler/user_commands/use_cmd.rs @@ -224,24 +224,6 @@ impl QueueCommandHandler for QueueHandler { if let Some(err) = (use_data.errorf)(&item, &target) { user_error(err)?; } - if ctx - .trans - .check_task_by_type_code( - "DelayedHealth", - &format!( - "{}/{}/{}", - &target.item_type, &target.item_code, use_data.task_ref - ), - ) - .await? - { - let explicit = ctx.explicit().await?; - user_error(format!( - "You see no reason to use {} on {}", - item.display_for_sentence(explicit, 1, false), - target.display_for_sentence(explicit, 1, false) - ))?; - } let is_self_use = target_type == &"player" && target_code == &ctx.item.item_code; let skillcheck = skill_check_and_grind( &ctx.trans, @@ -264,16 +246,18 @@ impl QueueCommandHandler for QueueHandler { } else { Some((*target).clone()) }; - run_effects( - ctx.trans, - &effects, - ctx.item, - &item, - &mut target_mut, - skilllvl, - use_data.task_ref, - ) - .await?; + if let Some(actual_effects) = effects { + run_effects( + ctx.trans, + &actual_effects, + ctx.item, + &item, + &mut target_mut, + skilllvl, + use_data.task_ref, + ) + .await?; + } if let Some(target_mut_save) = target_mut { ctx.trans.save_item_model(&target_mut_save).await?; } diff --git a/blastmud_game/src/models.rs b/blastmud_game/src/models.rs index 352f832..02e88e6 100644 --- a/blastmud_game/src/models.rs +++ b/blastmud_game/src/models.rs @@ -1,5 +1,6 @@ pub mod consent; pub mod corp; +pub mod effect; pub mod item; pub mod journal; pub mod session; diff --git a/blastmud_game/src/models/effect.rs b/blastmud_game/src/models/effect.rs new file mode 100644 index 0000000..a843a94 --- /dev/null +++ b/blastmud_game/src/models/effect.rs @@ -0,0 +1,29 @@ +use super::item::Item; +use serde::{Deserialize, Serialize}; + +#[derive(PartialEq, Eq, PartialOrd, Clone, Serialize, Deserialize, Debug, Ord)] +pub enum EffectType { + Ephemeral, // i.e. no enduring impact to show in status. + Bandages, +} + +pub struct EffectSet { + pub effect_type: EffectType, + pub effects: Vec, +} + +pub enum Effect { + // messagef takes player, causative item, target as the 3 parameters. Returns (explicit, non explicit) message. + BroadcastMessage { + delay_secs: u64, + messagef: Box (String, String) + Sync + Send>, + }, + // skill_multiplier is always positive - sign flipped for crit fails. + ChangeTargetHealth { + delay_secs: u64, + base_effect: i64, + skill_multiplier: f64, + max_effect: i64, + message: Box (String, String) + Sync + Send>, + }, +} diff --git a/blastmud_game/src/models/item.rs b/blastmud_game/src/models/item.rs index e42faf3..79f4c85 100644 --- a/blastmud_game/src/models/item.rs +++ b/blastmud_game/src/models/item.rs @@ -1,4 +1,4 @@ -use super::session::Session; +use super::{effect::EffectType, session::Session}; use crate::{ language, regular_tasks::queued_command::QueueCommand, @@ -495,6 +495,7 @@ pub struct Item { pub queue: VecDeque, pub urges: Option, pub liquid_details: Option, + pub active_effects: Vec, } impl Item { @@ -618,6 +619,7 @@ impl Default for Item { queue: VecDeque::new(), urges: None, liquid_details: None, + active_effects: vec![], } } } diff --git a/blastmud_game/src/models/task.rs b/blastmud_game/src/models/task.rs index d495328..d70163a 100644 --- a/blastmud_game/src/models/task.rs +++ b/blastmud_game/src/models/task.rs @@ -1,10 +1,12 @@ -use crate::services::effect::DelayedHealthEffect; +use crate::services::effect::{DelayedHealthEffect, DelayedMessageEffect}; use crate::static_content::room::Direction; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::VecDeque; +use super::effect::EffectType; + #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum TaskRecurrence { FixedDuration { seconds: u32 }, @@ -35,6 +37,10 @@ pub enum TaskDetails { item: String, effect_series: VecDeque, }, + DelayedMessage { + item: String, + effect_series: VecDeque, + }, ExpireItem { item_code: String, }, @@ -58,6 +64,10 @@ pub enum TaskDetails { HospitalERSeePatient { item: String, }, + DispelEffect { + target: String, + effect_type: EffectType, + }, } impl TaskDetails { pub fn name(self: &Self) -> &'static str { @@ -71,6 +81,8 @@ impl TaskDetails { RecloneNPC { .. } => "RecloneNPC", RotCorpse { .. } => "RotCorpse", DelayedHealth { .. } => "DelayedHealth", + DelayedMessage { .. } => "DelayedMessage", + DispelEffect { .. } => "DispelEffect", ExpireItem { .. } => "ExpireItem", ChargeRoom { .. } => "ChargeRoom", SwingShut { .. } => "SwingShut", diff --git a/blastmud_game/src/regular_tasks.rs b/blastmud_game/src/regular_tasks.rs index b4cc218..2e11822 100644 --- a/blastmud_game/src/regular_tasks.rs +++ b/blastmud_game/src/regular_tasks.rs @@ -55,6 +55,8 @@ fn task_handler_registry( ("RecloneNPC", npc::RECLONE_HANDLER), ("RotCorpse", combat::ROT_CORPSE_HANDLER), ("DelayedHealth", effect::DELAYED_HEALTH_HANDLER), + ("DelayedMessage", effect::DELAYED_MESSAGE_HANDLER), + ("DispelEffect", effect::DISPEL_EFFECT_HANDLER), ("ExpireItem", drop::EXPIRE_ITEM_HANDLER), ("ChargeRoom", rent::CHARGE_ROOM_HANDLER), ("SwingShut", open::SWING_SHUT_HANDLER), diff --git a/blastmud_game/src/services/effect.rs b/blastmud_game/src/services/effect.rs index 863883f..ce3ee4f 100644 --- a/blastmud_game/src/services/effect.rs +++ b/blastmud_game/src/services/effect.rs @@ -3,11 +3,11 @@ use super::{combat::change_health, comms::broadcast_to_room}; use crate::db::DBTrans; use crate::{ models::{ + effect::{Effect, EffectSet, EffectType}, item::Item, task::{Task, TaskDetails, TaskMeta}, }, regular_tasks::{TaskHandler, TaskRunContext}, - static_content::possession_type::UseEffect, DResult, }; use async_trait::async_trait; @@ -26,6 +26,13 @@ pub struct DelayedHealthEffect { message_nonexp: String, } +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct DelayedMessageEffect { + delay: u64, + message: String, + message_nonexp: String, +} + pub struct DelayedHealthTaskHandler; #[async_trait] impl TaskHandler for DelayedHealthTaskHandler { @@ -89,9 +96,105 @@ impl TaskHandler for DelayedHealthTaskHandler { pub static DELAYED_HEALTH_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &DelayedHealthTaskHandler; +pub struct DelayedMessageTaskHandler; +#[async_trait] +impl TaskHandler for DelayedMessageTaskHandler { + async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult> { + let ref mut item_effect_series = match &mut ctx.task.details { + TaskDetails::DelayedMessage { + item, + ref mut effect_series, + } => (item, effect_series), + _ => Err("Expected DelayedMessage type")?, + }; + let (item_type, item_code) = match item_effect_series.0.split_once("/") { + None => { + info!( + "Invalid item {} to DelayedMessageTaskHandler", + item_effect_series.0 + ); + return Ok(None); + } + Some((item_type, item_code)) => (item_type, item_code), + }; + let item = match ctx + .trans + .find_item_by_type_code(item_type, item_code) + .await? + { + None => { + return Ok(None); + } + Some(it) => it, + }; + if item.death_data.is_some() { + return Ok(None); + } + match item_effect_series.1.pop_front() { + None => Ok(None), + Some(DelayedMessageEffect { + message, + message_nonexp, + .. + }) => { + broadcast_to_room( + &ctx.trans, + &item.location, + None, + &message, + Some(&message_nonexp), + ) + .await?; + Ok(item_effect_series + .1 + .front() + .map(|it| time::Duration::from_secs(it.delay))) + } + } + } +} +pub static DELAYED_MESSAGE_HANDLER: &'static (dyn TaskHandler + Sync + Send) = + &DelayedMessageTaskHandler; + +pub struct DispelEffectTaskHandler; +#[async_trait] +impl TaskHandler for DispelEffectTaskHandler { + async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult> { + let (target_str, effect_type) = match ctx.task.details { + TaskDetails::DispelEffect { + target: ref t, + effect_type: ref e, + } => (t.as_str(), e), + _ => Err("Expected DispelEffect type")?, + }; + let (target_type, target_code) = match target_str.split_once("/") { + None => Err("Invalid DispelEffect target")?, + Some(v) => v, + }; + let target = match ctx + .trans + .find_item_by_type_code(target_type, target_code) + .await? + { + None => return Ok(None), + Some(t) => t, + }; + let mut target_mut = (*target).clone(); + target_mut + .active_effects + .iter() + .position(|e| e == effect_type) + .map(|p| target_mut.active_effects.remove(p)); + ctx.trans.save_item_model(&target_mut).await?; + Ok(None) + } +} +pub static DISPEL_EFFECT_HANDLER: &'static (dyn TaskHandler + Sync + Send) = + &DispelEffectTaskHandler; + pub async fn run_effects( trans: &DBTrans, - effects: &Vec, + effects: &EffectSet, player: &mut Item, item: &Item, // None if target is player @@ -100,15 +203,34 @@ pub async fn run_effects( task_ref: &str, ) -> DResult<()> { let mut target_health_series = BTreeMap::>::new(); - for effect in effects { + let mut target_message_series = BTreeMap::>::new(); + let mut dispel_time_secs: u64 = 0; + for effect in &effects.effects { match effect { - UseEffect::BroadcastMessage { messagef } => { + Effect::BroadcastMessage { + delay_secs, + messagef, + } => { let (msg_exp, msg_nonexp) = messagef(player, item, target.as_ref().unwrap_or(player)); - broadcast_to_room(trans, &player.location, None, &msg_exp, Some(&msg_nonexp)) - .await?; + if *delay_secs == 0 { + broadcast_to_room(trans, &player.location, None, &msg_exp, Some(&msg_nonexp)) + .await?; + } else { + dispel_time_secs = dispel_time_secs.max(*delay_secs); + let target_it = target.as_ref().unwrap_or(player); + let fx = DelayedMessageEffect { + delay: *delay_secs, + message: msg_exp, + message_nonexp: msg_nonexp, + }; + target_message_series + .entry(format!("{}/{}", target_it.item_type, target_it.item_code)) + .and_modify(|l| l.push_back(fx.clone())) + .or_insert(VecDeque::from([fx])); + } } - UseEffect::ChangeTargetHealth { + Effect::ChangeTargetHealth { delay_secs, base_effect, skill_multiplier, @@ -128,6 +250,7 @@ pub async fn run_effects( ) .await?; } else { + dispel_time_secs = dispel_time_secs.max(*delay_secs); let target_it = target.as_ref().unwrap_or(player); let fx = DelayedHealthEffect { magnitude: health_impact, @@ -144,6 +267,25 @@ pub async fn run_effects( } } + if dispel_time_secs > 0 && effects.effect_type != EffectType::Ephemeral { + let target_item = target.as_mut().unwrap_or(player); + target_item.active_effects.push(effects.effect_type.clone()); + + trans + .upsert_task(&Task { + meta: TaskMeta { + task_code: format!("{}/{}", &target_item.refstr(), task_ref), + next_scheduled: Utc::now() + chrono::Duration::seconds(dispel_time_secs as i64), + ..Default::default() + }, + details: TaskDetails::DispelEffect { + target: target_item.refstr(), + effect_type: effects.effect_type.clone(), + }, + }) + .await?; + } + for (eff_item, l) in target_health_series.into_iter() { trans .upsert_task(&Task { @@ -159,6 +301,21 @@ pub async fn run_effects( }) .await?; } + for (eff_item, l) in target_message_series.into_iter() { + trans + .upsert_task(&Task { + meta: TaskMeta { + task_code: format!("{}/{}", eff_item, task_ref), + next_scheduled: Utc::now() + chrono::Duration::seconds(l[0].delay as i64), + ..Default::default() + }, + details: TaskDetails::DelayedMessage { + effect_series: l, + item: eff_item, + }, + }) + .await?; + } Ok(()) } diff --git a/blastmud_game/src/static_content/possession_type.rs b/blastmud_game/src/static_content/possession_type.rs index 924c96d..4a31645 100644 --- a/blastmud_game/src/static_content/possession_type.rs +++ b/blastmud_game/src/static_content/possession_type.rs @@ -2,8 +2,11 @@ use crate::db::DBTrans; use crate::{ message_handler::user_commands::{UResult, VerbContext}, - models::consent::ConsentType, - models::item::{Item, ItemFlag, LiquidType, Pronouns, SkillType}, + models::{ + consent::ConsentType, + effect::EffectSet, + item::{Item, ItemFlag, LiquidType, Pronouns, SkillType}, + }, regular_tasks::queued_command::QueuedCommandContext, static_content::{room::Direction, species::BodyPart}, }; @@ -160,27 +163,12 @@ impl Default for ChargeData { } } -pub enum UseEffect { - // messagef takes player, item used, target as the 3 parameters. Returns (explicit, non explicit) message. - BroadcastMessage { - messagef: Box (String, String) + Sync + Send>, - }, - // skill_multiplier is always positive - sign flipped for crit fails. - ChangeTargetHealth { - delay_secs: u64, - base_effect: i64, - skill_multiplier: f64, - max_effect: i64, - message: Box (String, String) + Sync + Send>, - }, -} - pub struct UseData { pub uses_skill: SkillType, pub diff_level: f64, - pub crit_fail_effects: Vec, - pub fail_effects: Vec, - pub success_effects: Vec, + pub crit_fail_effects: Option, + pub fail_effects: Option, + pub success_effects: Option, pub errorf: Box Option + Sync + Send>, pub task_ref: &'static str, pub needs_consent_check: Option, @@ -191,9 +179,9 @@ impl Default for UseData { Self { uses_skill: SkillType::Medic, diff_level: 10.0, - crit_fail_effects: vec![], - fail_effects: vec![], - success_effects: vec![], + crit_fail_effects: None, + fail_effects: None, + success_effects: None, errorf: Box::new(|_it, _target| None), task_ref: "set me", needs_consent_check: None, diff --git a/blastmud_game/src/static_content/possession_type/trauma_kit.rs b/blastmud_game/src/static_content/possession_type/trauma_kit.rs index aaeac13..8f8dddc 100644 --- a/blastmud_game/src/static_content/possession_type/trauma_kit.rs +++ b/blastmud_game/src/static_content/possession_type/trauma_kit.rs @@ -1,5 +1,9 @@ -use super::{ChargeData, PossessionData, PossessionType, UseData, UseEffect}; -use crate::models::{consent::ConsentType, item::SkillType}; +use super::{ChargeData, PossessionData, PossessionType, UseData}; +use crate::models::{ + consent::ConsentType, + effect::{Effect, EffectSet, EffectType}, + item::SkillType, +}; use once_cell::sync::OnceCell; use super::PossessionType::*; @@ -21,202 +25,232 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { use_data: Some(UseData { uses_skill: SkillType::Medic, diff_level: 10.0, - crit_fail_effects: vec!( - UseEffect::BroadcastMessage { - messagef: Box::new(|player, _item, target| ( - format!( - "{} attempts to heal {} with a trauma kit, but fucks it up badly\n", - &player.display_for_sentence(true, 1, true), - &if target.item_type == player.item_type && target.item_code == player.item_code { - player.pronouns.intensive.clone() - } else { - target.display_for_sentence(true, 1, false) - } - ), - format!("{} attempts to heal {} with a trauma kit, but messes it up badly\n", - &player.display_for_sentence(false, 1, true), + crit_fail_effects: Some(EffectSet { + effect_type: EffectType::Ephemeral, + effects: vec!( + Effect::BroadcastMessage { + delay_secs: 0, + messagef: Box::new(|player, _item, target| ( + format!( + "{} attempts to heal {} with a trauma kit, but fucks it up badly\n", + &player.display_for_sentence(true, 1, true), &if target.item_type == player.item_type && target.item_code == player.item_code { player.pronouns.intensive.clone() } else { - target.display_for_sentence(false, 1, false) + target.display_for_sentence(true, 1, false) } - ))) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 0, base_effect: -2, skill_multiplier: -3.0, - max_effect: -5, - message: Box::new( - |target| - (format!( - "Fuck! The trauma kit makes {}'s condition worse", - target.display_for_sentence(true, 1, false)), - format!( - "The trauma kit makes {}'s condition worse", - target.display_for_sentence(false, 1, false) - ) - )) - } - ), - fail_effects: vec!( - UseEffect::BroadcastMessage { - messagef: Box::new(|player, _item, target| ( - format!( - "{} attempts unsuccessfully to heal {} with a trauma kit\n", - &player.display_for_sentence(true, 1, true), - &if target.item_type == player.item_type && target.item_code == player.item_code { - player.pronouns.intensive.clone() - } else { - target.display_for_sentence(true, 1, false) - } - ), - format!("{} attempts unsuccessfully to heal {} with a trauma kit\n", - &player.display_for_sentence(false, 1, true), + ), + format!("{} attempts to heal {} with a trauma kit, but messes it up badly\n", + &player.display_for_sentence(false, 1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(false, 1, false) + } + ))) + }, + Effect::ChangeTargetHealth { + delay_secs: 0, base_effect: -2, skill_multiplier: -3.0, + max_effect: -5, + message: Box::new( + |target| + (format!( + "Fuck! The trauma kit makes {}'s condition worse", + target.display_for_sentence(true, 1, false)), + format!( + "The trauma kit makes {}'s condition worse", + target.display_for_sentence(false, 1, false) + ) + )) + } + ) + }), + fail_effects: Some(EffectSet { + effect_type: EffectType::Ephemeral, + effects: vec!( + Effect::BroadcastMessage { + delay_secs: 0, + messagef: Box::new(|player, _item, target| ( + format!( + "{} attempts unsuccessfully to heal {} with a trauma kit\n", + &player.display_for_sentence(true, 1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(true, 1, false) + } + ), + format!("{} attempts unsuccessfully to heal {} with a trauma kit\n", + &player.display_for_sentence(false, 1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(false, 1, false) + } + ))) + }, + ) + }), + success_effects: Some(EffectSet { + effect_type: EffectType::Bandages, + effects: vec!( + Effect::BroadcastMessage { + delay_secs: 0, + messagef: Box::new(|player, _item, target| ( + format!( + "{} expertly heals {} with a trauma kit\n", + &player.display_for_sentence(true, 1, true), &if target.item_type == player.item_type && target.item_code == player.item_code { player.pronouns.intensive.clone() } else { - target.display_for_sentence(false, 1, false) + target.display_for_sentence(true, 1, false) } - ))) - }, - ), - success_effects: vec!( - UseEffect::BroadcastMessage { - messagef: Box::new(|player, _item, target| ( - format!( - "{} expertly heals {} with a trauma kit\n", - &player.display_for_sentence(true, 1, true), - &if target.item_type == player.item_type && target.item_code == player.item_code { - player.pronouns.intensive.clone() - } else { + ), + format!("{} expertly heals {} with a trauma kit\n", + &player.display_for_sentence(false, 1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(false, 1, false) + } + ))) + }, + Effect::ChangeTargetHealth { + delay_secs: 0, base_effect: 2, skill_multiplier: 8.0, + max_effect: 10, + message: Box::new( + |target| + (format!( + "FUUUCK! It hurts {}, but also starts to soothe {}", + target.display_for_sentence(true, 1, false), + &target.pronouns.object + ), + format!( + "It hurts {}, but also starts to soothe {}", + target.display_for_sentence(true, 1, false), + &target.pronouns.object + )) + ) + }, + Effect::ChangeTargetHealth { + delay_secs: 10, base_effect: 2, skill_multiplier: 7.0, + max_effect: 9, + message: Box::new( + |target| + (format!( + "FUUUCK! It hurts {}, but also starts to soothe {}", + target.display_for_sentence(true, 1, false), + &target.pronouns.object + ), + format!( + "It hurts {}, but also starts to soothe {}", + target.display_for_sentence(true, 1, false), + &target.pronouns.object + )) + ) + }, + Effect::ChangeTargetHealth { + delay_secs: 20, base_effect: 1, skill_multiplier: 6.0, + max_effect: 7, + message: Box::new( + |target| + (format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(true, 1, false), + ), + format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(false, 1, false), + )) + ) + }, + Effect::ChangeTargetHealth { + delay_secs: 30, base_effect: 1, skill_multiplier: 5.0, + max_effect: 6, + message: Box::new( + |target| + (format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(true, 1, false), + ), + format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(false, 1, false), + )) + ) + }, + Effect::ChangeTargetHealth { + delay_secs: 40, base_effect: 0, skill_multiplier: 4.0, + max_effect: 4, + message: Box::new( + |target| + (format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(true, 1, false), + ), + format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(false, 1, false), + )) + ) + }, + Effect::ChangeTargetHealth { + delay_secs: 50, base_effect: 0, skill_multiplier: 3.0, + max_effect: 3, + message: Box::new( + |target| + (format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(true, 1, false), + ), + format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(false, 1, false), + )) + ) + }, + Effect::ChangeTargetHealth { + delay_secs: 60, base_effect: 0, skill_multiplier: 2.0, + max_effect: 2, + message: Box::new( + |target| + (format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(true, 1, false), + ), + format!( + "The bandages soothe {}'s wounds", + target.display_for_sentence(false, 1, false), + )) + ) + }, + Effect::BroadcastMessage { + delay_secs: 60, + messagef: Box::new(|_player, _item, target| ( + format!( + "The bandages wrapping {} crumble and fall away, their healing capabilities fully expended.\n", target.display_for_sentence(true, 1, false) - } - ), - format!("{} expertly heals {} with a trauma kit\n", - &player.display_for_sentence(false, 1, true), - &if target.item_type == player.item_type && target.item_code == player.item_code { - player.pronouns.intensive.clone() - } else { - target.display_for_sentence(false, 1, false) - } - ))) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 0, base_effect: 2, skill_multiplier: 8.0, - max_effect: 10, - message: Box::new( - |target| - (format!( - "FUUUCK! It hurts {}, but also starts to soothe {}", - target.display_for_sentence(true, 1, false), - &target.pronouns.object - ), - format!( - "It hurts {}, but also starts to soothe {}", - target.display_for_sentence(true, 1, false), - &target.pronouns.object - )) - ) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 10, base_effect: 2, skill_multiplier: 7.0, - max_effect: 9, - message: Box::new( - |target| - (format!( - "FUUUCK! It hurts {}, but also starts to soothe {}", - target.display_for_sentence(true, 1, false), - &target.pronouns.object - ), - format!( - "It hurts {}, but also starts to soothe {}", - target.display_for_sentence(true, 1, false), - &target.pronouns.object - )) - ) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 20, base_effect: 1, skill_multiplier: 6.0, - max_effect: 7, - message: Box::new( - |target| - (format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(true, 1, false), - ), - format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(false, 1, false), - )) - ) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 30, base_effect: 1, skill_multiplier: 5.0, - max_effect: 6, - message: Box::new( - |target| - (format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(true, 1, false), - ), - format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(false, 1, false), - )) - ) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 40, base_effect: 0, skill_multiplier: 4.0, - max_effect: 4, - message: Box::new( - |target| - (format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(true, 1, false), - ), - format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(false, 1, false), - )) - ) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 50, base_effect: 0, skill_multiplier: 3.0, - max_effect: 3, - message: Box::new( - |target| - (format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(true, 1, false), - ), - format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(false, 1, false), - )) - ) - }, - UseEffect::ChangeTargetHealth { - delay_secs: 60, base_effect: 0, skill_multiplier: 2.0, - max_effect: 2, - message: Box::new( - |target| - (format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(true, 1, false), - ), - format!( - "The bandages soothe {}'s wounds", - target.display_for_sentence(false, 1, false), - )) - ) - }, - ), + ), + format!( + "The bandages wrapping {} crumble and fall away, their healing capabilities fully expended.\n", + target.display_for_sentence(false, 1, false) + ))) + } + ), + }), task_ref: "bandage", errorf: Box::new( - |_item, target| + |item, target| if target.death_data.is_some() { Some(format!("It is too late, {}'s dead", target.pronouns.subject)) } else if target.item_type != "player" && target.item_type != "npc" { Some("It only works on animals.".to_owned()) + } else if target.active_effects.contains(&EffectType::Bandages) { + Some(format!( + "You see no reason to use {} on {}", + item.display_for_sentence(false, 1, false), + target.display_for_sentence(false, 1, false) + )) } else { None }), diff --git a/blastmud_game/src/static_content/room/computer_museum.yaml b/blastmud_game/src/static_content/room/computer_museum.yaml index bedcd9b..213d583 100644 --- a/blastmud_game/src/static_content/room/computer_museum.yaml +++ b/blastmud_game/src/static_content/room/computer_museum.yaml @@ -82,7 +82,7 @@ z: -1 description: | This appears to be a security zone protecting access to a door to the north. A long corridor stretches to the west. The corridor echoes with screams of torment and the sound of metal cutting into flesh. - Mounted on the door is a large screen, labelled as "Doorbot".to_owned(), showing primative ASCII art of some towers, with with some kind of electronic screen on it, showing some kind of towers with coloured discs stacked on them. + Mounted on the door is a large screen, labelled as "Doorbot", showing primative ASCII art of some towers, with with some kind of electronic screen on it, showing some kind of towers with coloured discs stacked on them. [Hint: try -doorbot move from 1 to 2 to ask Doorbot to move the top disc from tower 1 to tower 2] The discs are arranged as follows: exits: diff --git a/blastmud_game/src/static_content/room/general_hospital.rs b/blastmud_game/src/static_content/room/general_hospital.rs index 93e2c95..1dd761e 100644 --- a/blastmud_game/src/static_content/room/general_hospital.rs +++ b/blastmud_game/src/static_content/room/general_hospital.rs @@ -1,9 +1,11 @@ use crate::{ message_handler::user_commands::UResult, - models::task::{Task, TaskDetails, TaskMeta}, + models::{ + effect::{Effect, EffectSet, EffectType}, + task::{Task, TaskDetails, TaskMeta}, + }, regular_tasks::{queued_command::QueuedCommandContext, TaskHandler, TaskRunContext}, services::{combat::max_health, effect::run_effects}, - static_content::possession_type::UseEffect, DResult, }; @@ -111,56 +113,59 @@ impl TaskHandler for SeePatientTaskHandler { let mut who_mut = (*who).clone(); run_effects( &ctx.trans, - &vec![ - UseEffect::ChangeTargetHealth { - delay_secs: 0, - base_effect: 10, - skill_multiplier: 0.0, - max_effect: 10, - message: Box::new(|_item| { - ( - "That feels better".to_owned(), - "That feels better".to_owned(), - ) - }), - }, - UseEffect::ChangeTargetHealth { - delay_secs: 10, - base_effect: 9, - skill_multiplier: 0.0, - max_effect: 10, - message: Box::new(|_item| { - ( - "That feels better".to_owned(), - "That feels better".to_owned(), - ) - }), - }, - UseEffect::ChangeTargetHealth { - delay_secs: 20, - base_effect: 8, - skill_multiplier: 0.0, - max_effect: 10, - message: Box::new(|_item| { - ( - "That feels better".to_owned(), - "That feels better".to_owned(), - ) - }), - }, - UseEffect::ChangeTargetHealth { - delay_secs: 30, - base_effect: 7, - skill_multiplier: 0.0, - max_effect: 10, - message: Box::new(|_item| { - ( - "That feels better".to_owned(), - "That feels better".to_owned(), - ) - }), - }, - ], + &EffectSet { + effect_type: EffectType::Bandages, + effects: vec![ + Effect::ChangeTargetHealth { + delay_secs: 0, + base_effect: 10, + skill_multiplier: 0.0, + max_effect: 10, + message: Box::new(|_item| { + ( + "That feels better".to_owned(), + "That feels better".to_owned(), + ) + }), + }, + Effect::ChangeTargetHealth { + delay_secs: 10, + base_effect: 9, + skill_multiplier: 0.0, + max_effect: 10, + message: Box::new(|_item| { + ( + "That feels better".to_owned(), + "That feels better".to_owned(), + ) + }), + }, + Effect::ChangeTargetHealth { + delay_secs: 20, + base_effect: 8, + skill_multiplier: 0.0, + max_effect: 10, + message: Box::new(|_item| { + ( + "That feels better".to_owned(), + "That feels better".to_owned(), + ) + }), + }, + Effect::ChangeTargetHealth { + delay_secs: 30, + base_effect: 7, + skill_multiplier: 0.0, + max_effect: 10, + message: Box::new(|_item| { + ( + "That feels better".to_owned(), + "That feels better".to_owned(), + ) + }), + }, + ], + }, &mut who_mut, &who, &mut None,