From 5d008bb52a130145a2f021aff9723e39775cb915 Mon Sep 17 00:00:00 2001 From: Condorra Date: Sun, 7 Apr 2024 00:37:39 +1100 Subject: [PATCH] Add rad detox + assorted fixups Now ChangeTargetHealth effect is generalised to ChangeTargetParameter that can also change raddamage (and in the future could cover other things). Also fixes a long-standing lmap bug - only show exits where both ends are in the zone of the current room, rather than just requiring one of the rooms to be in the zone. --- .../src/message_handler/user_commands/map.rs | 47 ++-- blastmud_game/src/models/effect.rs | 19 +- blastmud_game/src/models/task.rs | 8 +- blastmud_game/src/regular_tasks.rs | 2 +- blastmud_game/src/services/effect.rs | 109 ++++++--- .../src/static_content/possession_type.rs | 1 + .../possession_type/trauma_kit.rs | 224 +++++++++++++++++- .../static_content/room/general_hospital.rs | 14 +- .../src/static_content/room/melbs.yaml | 2 + 9 files changed, 344 insertions(+), 82 deletions(-) diff --git a/blastmud_game/src/message_handler/user_commands/map.rs b/blastmud_game/src/message_handler/user_commands/map.rs index 50af288..e9db7b1 100644 --- a/blastmud_game/src/message_handler/user_commands/map.rs +++ b/blastmud_game/src/message_handler/user_commands/map.rs @@ -108,6 +108,20 @@ pub fn render_map_dyn( buf } +fn has_exit_to_dir_in_zone(room: &Option<&&room::Room>, zone: &str, direction: &Direction) -> bool { + match room.as_ref().and_then(|r| { + r.exits + .iter() + .find(|ex| ex.direction == *direction) + .and_then(|ex| room::resolve_exit(r, ex)) + }) { + None => false, + Some(other_room) => { + other_room.zone == zone || other_room.secondary_zones.iter().any(|z| z.zone == zone) + } + } +} + pub fn render_lmap( room: &room::Room, width: usize, @@ -174,38 +188,25 @@ pub fn render_lmap( } } } - match coord_room.and_then(|r| r.exits.iter().find(|ex| ex.direction == Direction::EAST)) - { - None => buf.push(' '), - Some(_) => buf.push('-'), + if has_exit_to_dir_in_zone(&coord_room, &room.zone, &Direction::EAST) { + buf.push('-') + } else { + buf.push(' ') } } buf.push('\n'); for x in min_x..max_x { let mut coord = room::GridCoords { x, y, z: my_loc.z }; let coord_room = room::room_map_by_zloc().get(&(&room.zone, &coord)); - match coord_room - .and_then(|r| r.exits.iter().find(|ex| ex.direction == Direction::SOUTH)) - { - None => buf.push_str(" "), - Some(_) => buf.push_str(" | "), + if has_exit_to_dir_in_zone(&coord_room, &room.zone, &Direction::SOUTH) { + buf.push_str(" | "); + } else { + buf.push_str(" "); } - let has_se = coord_room - .and_then(|r| { - r.exits - .iter() - .find(|ex| ex.direction == Direction::SOUTHEAST) - }) - .is_some(); + let has_se = has_exit_to_dir_in_zone(&coord_room, &room.zone, &Direction::SOUTHEAST); coord.x += 1; let coord_room_s = room::room_map_by_zloc().get(&(&room.zone, &coord)); - let has_sw = coord_room_s - .and_then(|r| { - r.exits - .iter() - .find(|ex| ex.direction == Direction::SOUTHWEST) - }) - .is_some(); + let has_sw = has_exit_to_dir_in_zone(&coord_room_s, &room.zone, &Direction::SOUTHWEST); if has_se && has_sw { buf.push('X'); } else if has_se { diff --git a/blastmud_game/src/models/effect.rs b/blastmud_game/src/models/effect.rs index 8b0fa74..a5df7ca 100644 --- a/blastmud_game/src/models/effect.rs +++ b/blastmud_game/src/models/effect.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; pub enum EffectType { Ephemeral, // i.e. no enduring impact to show in status. Bandages, + RadDetox, Bleed, Stunned, CurrentRoom, @@ -16,6 +17,12 @@ pub struct EffectSet { pub effects: Vec, } +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub enum EffectParameter { + Health, + Raddamage, +} + pub enum Effect { // messagef takes player, causative item, target as the 3 parameters. Returns message. BroadcastMessage { @@ -27,11 +34,12 @@ pub enum Effect { messagef: Box String + Sync + Send>, }, // skill_multiplier is always positive - sign flipped for crit fails. - ChangeTargetHealth { + ChangeTargetParameter { delay_secs: u64, base_effect: i64, skill_multiplier: f64, max_effect: i64, + parameter: EffectParameter, message: Box String + Sync + Send>, }, } @@ -47,11 +55,12 @@ pub enum SimpleEffect { message: String, }, // skill_multiplier is always positive - sign flipped for crit fails. - ChangeTargetHealth { + ChangeTargetParameter { delay_secs: u64, base_effect: i64, skill_multiplier: f64, max_effect: i64, + parameter: EffectParameter, message: String, }, } @@ -79,19 +88,21 @@ impl From<&SimpleEffect> for Effect { messagef: Box::new(move |_, _, _| messagem.clone()), } } - SimpleEffect::ChangeTargetHealth { + SimpleEffect::ChangeTargetParameter { delay_secs, base_effect, skill_multiplier, max_effect, + parameter, message, } => { let messagem = parse_ansi_markup(message).unwrap() + "\n"; - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: *delay_secs, base_effect: *base_effect, skill_multiplier: *skill_multiplier, max_effect: *max_effect, + parameter: parameter.clone(), message: Box::new(move |_| messagem.clone()), } } diff --git a/blastmud_game/src/models/task.rs b/blastmud_game/src/models/task.rs index 5308a46..f34565c 100644 --- a/blastmud_game/src/models/task.rs +++ b/blastmud_game/src/models/task.rs @@ -1,4 +1,4 @@ -use crate::services::effect::{DelayedHealthEffect, DelayedMessageEffect}; +use crate::services::effect::{DelayedMessageEffect, DelayedParameterEffect}; use crate::static_content::room::Direction; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -34,9 +34,9 @@ pub enum TaskDetails { RotCorpse { corpse_code: String, }, - DelayedHealth { + DelayedParameter { item: String, - effect_series: VecDeque, + effect_series: VecDeque, }, DelayedMessage { item: String, @@ -103,7 +103,7 @@ impl TaskDetails { ShareTick => "ShareTick", RecloneNPC { .. } => "RecloneNPC", RotCorpse { .. } => "RotCorpse", - DelayedHealth { .. } => "DelayedHealth", + DelayedParameter { .. } => "DelayedParameter", DelayedMessage { .. } => "DelayedMessage", DispelEffect { .. } => "DispelEffect", ExpireItem { .. } => "ExpireItem", diff --git a/blastmud_game/src/regular_tasks.rs b/blastmud_game/src/regular_tasks.rs index 27b6455..76f886f 100644 --- a/blastmud_game/src/regular_tasks.rs +++ b/blastmud_game/src/regular_tasks.rs @@ -56,7 +56,7 @@ fn task_handler_registry( ("ShareTick", sharing::TASK_HANDLER), ("RecloneNPC", npc::RECLONE_HANDLER), ("RotCorpse", combat::ROT_CORPSE_HANDLER), - ("DelayedHealth", effect::DELAYED_HEALTH_HANDLER), + ("DelayedParameter", effect::DELAYED_PARAMETER_HANDLER), ("DelayedMessage", effect::DELAYED_MESSAGE_HANDLER), ("DispelEffect", effect::DISPEL_EFFECT_HANDLER), ("ExpireItem", drop::EXPIRE_ITEM_HANDLER), diff --git a/blastmud_game/src/services/effect.rs b/blastmud_game/src/services/effect.rs index 3e2064b..9ee7503 100644 --- a/blastmud_game/src/services/effect.rs +++ b/blastmud_game/src/services/effect.rs @@ -3,7 +3,7 @@ use super::{combat::change_health, comms::broadcast_to_room}; use crate::db::DBTrans; use crate::{ models::{ - effect::{Effect, EffectSet, EffectType}, + effect::{Effect, EffectParameter, EffectSet, EffectType}, item::Item, task::{Task, TaskDetails, TaskMeta}, }, @@ -22,9 +22,10 @@ use std::collections::{BTreeMap, VecDeque}; use std::time; #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub struct DelayedHealthEffect { +pub struct DelayedParameterEffect { magnitude: i64, delay: u64, + parameter: EffectParameter, message: String, } @@ -35,21 +36,21 @@ pub struct DelayedMessageEffect { is_direct: bool, } -pub struct DelayedHealthTaskHandler; +pub struct DelayedParameterTaskHandler; #[async_trait] -impl TaskHandler for DelayedHealthTaskHandler { +impl TaskHandler for DelayedParameterTaskHandler { async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult> { let ref mut item_effect_series = match &mut ctx.task.details { - TaskDetails::DelayedHealth { + TaskDetails::DelayedParameter { item, ref mut effect_series, } => (item, effect_series), - _ => Err("Expected DelayedHealth type")?, + _ => Err("Expected DelayedParameter type")?, }; let (item_type, item_code) = match item_effect_series.0.split_once("/") { None => { info!( - "Invalid item {} to DelayedHealthTaskHandler", + "Invalid item {} to DelayedParameterTaskHandler", item_effect_series.0 ); return Ok(None); @@ -71,11 +72,22 @@ impl TaskHandler for DelayedHealthTaskHandler { } match item_effect_series.1.pop_front() { None => Ok(None), - Some(DelayedHealthEffect { - magnitude, message, .. + Some(DelayedParameterEffect { + magnitude, + message, + parameter, + .. }) => { let mut item_mut = (*item).clone(); - change_health(ctx.trans, magnitude, &mut item_mut, &message).await?; + match parameter { + EffectParameter::Health => { + change_health(ctx.trans, magnitude, &mut item_mut, &message).await?; + () + } + EffectParameter::Raddamage => { + item_mut.raddamage = (item_mut.raddamage as i64 + magnitude).max(0) as u64 + } + } ctx.trans.save_item_model(&item_mut).await?; Ok(item_effect_series .1 @@ -85,8 +97,8 @@ impl TaskHandler for DelayedHealthTaskHandler { } } } -pub static DELAYED_HEALTH_HANDLER: &'static (dyn TaskHandler + Sync + Send) = - &DelayedHealthTaskHandler; +pub static DELAYED_PARAMETER_HANDLER: &'static (dyn TaskHandler + Sync + Send) = + &DelayedParameterTaskHandler; pub struct DelayedMessageTaskHandler; #[async_trait] @@ -196,7 +208,7 @@ pub async fn run_effects( mut target: Option<&mut Item>, level: f64, ) -> DResult<()> { - let mut target_health_series = BTreeMap::>::new(); + let mut target_health_series = BTreeMap::>::new(); let mut target_message_series = BTreeMap::>::new(); let mut dispel_time_secs: u64 = 0; @@ -267,30 +279,46 @@ pub async fn run_effects( .or_insert(VecDeque::from([fx])); } } - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs, base_effect, skill_multiplier, max_effect, + parameter, message, } => { - let health_impact = - (*base_effect + ((skill_multiplier * level) as i64)).min(*max_effect) as i64; + let param_impact = *base_effect + ((skill_multiplier * level) as i64); + let param_impact = if *max_effect >= 0 { + param_impact.min(*max_effect) + } else { + param_impact.max(*max_effect) + }; let msg = message(target.as_ref().map(|t| &**t).unwrap_or(player)); if *delay_secs == 0 { - change_health( - trans, - health_impact, - *target.as_mut().unwrap_or(&mut player), - &msg, - ) - .await?; + match parameter { + EffectParameter::Health => { + change_health( + trans, + param_impact, + *target.as_mut().unwrap_or(&mut player), + &msg, + ) + .await?; + () + } + EffectParameter::Raddamage => { + let eff_target = target.as_mut().unwrap_or(&mut player); + eff_target.raddamage = + (eff_target.raddamage as i64 + param_impact).max(0) as u64 + } + } } else { dispel_time_secs = dispel_time_secs.max(*delay_secs); let target_it = target.as_ref().map(|t| &**t).unwrap_or(player); - let fx = DelayedHealthEffect { - magnitude: health_impact, + let fx = DelayedParameterEffect { + magnitude: param_impact, delay: *delay_secs, + parameter: (*parameter).clone(), message: msg, }; target_health_series @@ -313,7 +341,8 @@ pub async fn run_effects( .upsert_task(&Task { meta: TaskMeta { task_code: format!("{}/{}", &actual_target.refstr(), task_ref), - next_scheduled: Utc::now() + chrono::TimeDelta::try_seconds(dispel_time_secs as i64).unwrap(), + next_scheduled: Utc::now() + + chrono::TimeDelta::try_seconds(dispel_time_secs as i64).unwrap(), ..Default::default() }, details: TaskDetails::DispelEffect { @@ -329,10 +358,11 @@ pub async fn run_effects( .upsert_task(&Task { meta: TaskMeta { task_code: format!("{}/{}", eff_item, trans.alloc_task_code().await?), - next_scheduled: Utc::now() + chrono::TimeDelta::try_seconds(l[0].delay as i64).unwrap(), + next_scheduled: Utc::now() + + chrono::TimeDelta::try_seconds(l[0].delay as i64).unwrap(), ..Default::default() }, - details: TaskDetails::DelayedHealth { + details: TaskDetails::DelayedParameter { effect_series: l, item: eff_item, }, @@ -344,7 +374,8 @@ pub async fn run_effects( .upsert_task(&Task { meta: TaskMeta { task_code: format!("{}/{}", eff_item, task_ref), - next_scheduled: Utc::now() + chrono::TimeDelta::try_seconds(l[0].delay as i64).unwrap(), + next_scheduled: Utc::now() + + chrono::TimeDelta::try_seconds(l[0].delay as i64).unwrap(), ..Default::default() }, details: TaskDetails::DelayedMessage { @@ -374,7 +405,7 @@ pub async fn cancel_effect( .await?; trans .delete_task( - "DelayedHealth", + "DelayedParameter", &format!( "{}/{}/{}", &character.item_type, &character.item_code, effect.1 @@ -409,11 +440,12 @@ pub fn default_effects_for_type() -> &'static BTreeMap { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 10, base_effect: -12, skill_multiplier: 0.0, max_effect: -12, + parameter: EffectParameter::Health, message: Box::new(|target| format!("{} pulses from {}'s wound", if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" }, @@ -421,11 +453,12 @@ pub fn default_effects_for_type() -> &'static BTreeMap { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 20, base_effect: -10, skill_multiplier: 0.0, max_effect: -10, + parameter: EffectParameter::Health, message: Box::new(|target| format!("{} pulses from {}'s wound", if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" }, @@ -433,11 +466,12 @@ pub fn default_effects_for_type() -> &'static BTreeMap { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 30, base_effect: -8, skill_multiplier: 0.0, max_effect: -8, + parameter: EffectParameter::Health, message: Box::new(|target| format!("{} pulses from {}'s wound", if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" }, @@ -445,11 +479,12 @@ pub fn default_effects_for_type() -> &'static BTreeMap { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 40, base_effect: -6, skill_multiplier: 0.0, max_effect: -6, + parameter: EffectParameter::Health, message: Box::new(|target| format!("{} pulses from {}'s wound", if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" }, @@ -457,11 +492,12 @@ pub fn default_effects_for_type() -> &'static BTreeMap { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 50, base_effect: -4, skill_multiplier: 0.0, max_effect: -4, + parameter: EffectParameter::Health, message: Box::new(|target| format!("{} pulses from {}'s wound", if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" }, @@ -469,11 +505,12 @@ pub fn default_effects_for_type() -> &'static BTreeMap { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 60, base_effect: -2, skill_multiplier: 0.0, max_effect: -2, + parameter: EffectParameter::Health, message: Box::new(|target| format!("A final tiny drop of {} oozes from {}'s wound as it heals", if target.species == SpeciesType::Robot { "coolant" } else { "blood" }, diff --git a/blastmud_game/src/static_content/possession_type.rs b/blastmud_game/src/static_content/possession_type.rs index 166d74e..f200589 100644 --- a/blastmud_game/src/static_content/possession_type.rs +++ b/blastmud_game/src/static_content/possession_type.rs @@ -437,6 +437,7 @@ pub enum PossessionType { // Medical MediumTraumaKit, EmptyMedicalBox, + RadDetox, // Corporate NewCorpLicence, CertificateOfIncorporation, 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 e51b58f..d2ff4ff 100644 --- a/blastmud_game/src/static_content/possession_type/trauma_kit.rs +++ b/blastmud_game/src/static_content/possession_type/trauma_kit.rs @@ -1,7 +1,7 @@ use super::{ChargeData, PossessionData, PossessionType, UseData}; use crate::models::{ consent::ConsentType, - effect::{Effect, EffectSet, EffectType}, + effect::{Effect, EffectParameter, EffectSet, EffectType}, item::SkillType, }; use once_cell::sync::OnceCell; @@ -42,9 +42,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ), ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 0, base_effect: -2, skill_multiplier: -3.0, max_effect: -5, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -90,9 +91,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 0, base_effect: 2, skill_multiplier: 8.0, max_effect: 10, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -102,9 +104,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 10, base_effect: 2, skill_multiplier: 7.0, max_effect: 9, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -114,9 +117,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 20, base_effect: 1, skill_multiplier: 6.0, max_effect: 7, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -125,8 +129,9 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 30, base_effect: 1, skill_multiplier: 5.0, + parameter: EffectParameter::Health, max_effect: 6, message: Box::new( |target| @@ -136,9 +141,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 40, base_effect: 0, skill_multiplier: 4.0, max_effect: 4, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -147,9 +153,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 50, base_effect: 0, skill_multiplier: 3.0, max_effect: 3, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -158,9 +165,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { ) ) }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 60, base_effect: 0, skill_multiplier: 2.0, max_effect: 2, + parameter: EffectParameter::Health, message: Box::new( |target| format!( @@ -210,6 +218,204 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> { aliases: vec!("box"), ..Default::default() } + ), + ( + RadDetox, + PossessionData { + display: "rad detox kit", + details: "A medical box labelled \"Radiation Detox\". It looks like, in the hands of a skilled medic, it might be useful for treating someone suffering from acute radiation poisoning", + aliases: vec!("detox"), + charge_data: Some(ChargeData { + max_charges: 5, + charge_name_prefix: "treatment", + charge_name_suffix: "worth of supplies", + ..Default::default() + }), + use_data: Some(UseData { + uses_skill: SkillType::Medic, + diff_level: 12.0, + 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 treat {} with a rad detox kit, but fucks it up badly\n", + &player.display_for_sentence(1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(1, false) + } + ), + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 0, base_effect: -2, skill_multiplier: -3.0, + max_effect: -5, + parameter: EffectParameter::Health, + message: Box::new( + |target| + format!( + "Fuck! The trauma kit makes {}'s condition worse", + target.display_for_sentence(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 treat {} with a rad detox kit\n", + &player.display_for_sentence(1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(1, false) + } + ) + ) + }, + ) + }), + success_effects: Some(EffectSet { + effect_type: EffectType::RadDetox, + effects: vec!( + Effect::BroadcastMessage { + delay_secs: 0, + messagef: Box::new(|player, _item, target| + format!( + "{} expertly treats {} with a rad detox kit\n", + &player.display_for_sentence(1, true), + &if target.item_type == player.item_type && target.item_code == player.item_code { + player.pronouns.intensive.clone() + } else { + target.display_for_sentence(1, false) + } + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 0, base_effect: -200, skill_multiplier: -8.0, + max_effect: -1000, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 10, base_effect: -200, skill_multiplier: -7.0, + max_effect: -900, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 20, base_effect: -100, skill_multiplier: -6.0, + max_effect: -700, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 30, base_effect: -100, skill_multiplier: -5.0, + max_effect: -600, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 40, base_effect: -100, skill_multiplier: -4.0, + max_effect: -600, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 50, base_effect: 0, skill_multiplier: -3.0, + max_effect: -300, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::ChangeTargetParameter { + delay_secs: 60, base_effect: 0, skill_multiplier: -2.0, + max_effect: -200, + parameter: EffectParameter::Raddamage, + message: Box::new( + |target| + format!( + "A drip from a rad detox kit taped to {}'s arm bubbles away", + target.display_for_sentence(1, false), + ) + ) + }, + Effect::BroadcastMessage { + delay_secs: 60, + messagef: Box::new(|_player, _item, target| + format!( + "The rad detox drip attached to {} crumbles and fall away, their detoxifying capabilities fully expended.\n", + target.display_for_sentence(1, false) + ) + ) + } + ), + }), + errorf: Box::new( + |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.iter().any(|e| e.0 == EffectType::Bandages) { + Some(format!( + "You see no reason to use {} on {}", + item.display_for_sentence(1, false), + target.display_for_sentence(1, false) + )) + } else { + None + }), + needs_consent_check: Some(ConsentType::Medicine), + ..Default::default() + }), + becomes_on_spent: Some(EmptyMedicalBox), + ..Default::default() + } ) )) } diff --git a/blastmud_game/src/static_content/room/general_hospital.rs b/blastmud_game/src/static_content/room/general_hospital.rs index 5c3cb93..c42ff8a 100644 --- a/blastmud_game/src/static_content/room/general_hospital.rs +++ b/blastmud_game/src/static_content/room/general_hospital.rs @@ -1,7 +1,7 @@ use crate::{ message_handler::user_commands::UResult, models::{ - effect::{Effect, EffectSet, EffectType}, + effect::{Effect, EffectParameter, EffectSet, EffectType}, task::{Task, TaskDetails, TaskMeta}, }, regular_tasks::{queued_command::QueuedCommandContext, TaskHandler, TaskRunContext}, @@ -115,29 +115,33 @@ impl TaskHandler for SeePatientTaskHandler { &EffectSet { effect_type: EffectType::Bandages, effects: vec![ - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 0, base_effect: 10, skill_multiplier: 0.0, + parameter: EffectParameter::Health, max_effect: 10, message: Box::new(|_item| "That feels better".to_owned()), }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 10, base_effect: 9, skill_multiplier: 0.0, + parameter: EffectParameter::Health, max_effect: 10, message: Box::new(|_item| "That feels better".to_owned()), }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 20, + parameter: EffectParameter::Health, base_effect: 8, skill_multiplier: 0.0, max_effect: 10, message: Box::new(|_item| "That feels better".to_owned()), }, - Effect::ChangeTargetHealth { + Effect::ChangeTargetParameter { delay_secs: 30, + parameter: EffectParameter::Health, base_effect: 7, skill_multiplier: 0.0, max_effect: 10, diff --git a/blastmud_game/src/static_content/room/melbs.yaml b/blastmud_game/src/static_content/room/melbs.yaml index 2f80411..4d00220 100644 --- a/blastmud_game/src/static_content/room/melbs.yaml +++ b/blastmud_game/src/static_content/room/melbs.yaml @@ -1700,6 +1700,8 @@ list_price: 2500 - possession_type: !RadSuit list_price: 4000 + - possession_type: !RadDetox + list_price: 100 - zone: melbs code: melbs_williamsst_collinsst name: Williams St & Collins St