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.
This commit is contained in:
Condorra 2024-04-07 00:37:39 +11:00
parent 36086b809c
commit 5d008bb52a
9 changed files with 344 additions and 82 deletions

View File

@ -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 {

View File

@ -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<Effect>,
}
#[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<dyn Fn(&Item, &Item, &Item) -> 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<dyn Fn(&Item) -> 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()),
}
}

View File

@ -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<DelayedHealthEffect>,
effect_series: VecDeque<DelayedParameterEffect>,
},
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",

View File

@ -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),

View File

@ -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<Option<time::Duration>> {
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();
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::<String, VecDeque<DelayedHealthEffect>>::new();
let mut target_health_series = BTreeMap::<String, VecDeque<DelayedParameterEffect>>::new();
let mut target_message_series = BTreeMap::<String, VecDeque<DelayedMessageEffect>>::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 {
match parameter {
EffectParameter::Health => {
change_health(
trans,
health_impact,
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<EffectType, EffectSet> {
)
)
},
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<EffectType, EffectSet> {
)
)
},
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<EffectType, EffectSet> {
)
)
},
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<EffectType, EffectSet> {
)
)
},
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<EffectType, EffectSet> {
)
)
},
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<EffectType, EffectSet> {
)
)
},
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" },

View File

@ -437,6 +437,7 @@ pub enum PossessionType {
// Medical
MediumTraumaKit,
EmptyMedicalBox,
RadDetox,
// Corporate
NewCorpLicence,
CertificateOfIncorporation,

View File

@ -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()
}
)
))
}

View File

@ -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,

View File

@ -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