Enable first chemistry crafting

This commit is contained in:
Condorra 2024-06-19 22:28:09 +10:00
parent fd29618643
commit 5c2462a396
20 changed files with 832 additions and 231 deletions

View File

@ -10,7 +10,7 @@ struct PluralRule<'l> {
pub fn pluralise(orig_input: &str) -> String {
let mut extra_suffix: &str = "";
let mut input: &str = orig_input;
'wordsplit: for split_word in vec!["pair"] {
'wordsplit: for split_word in vec!["pair", "box", "jar", "tube"] {
for (idx, _) in input.match_indices(split_word) {
let end_idx = idx + split_word.len();
if end_idx == input.len() {
@ -322,6 +322,8 @@ mod test {
("brown pair of pants", "brown pairs of pants"),
("good pair", "good pairs"),
("repair kit", "repair kits"),
("box of wolves", "boxes of wolves"),
("jar of acid", "jars of acid"),
] {
assert_eq!(super::pluralise(word), plural);
}

View File

@ -217,6 +217,10 @@ pub async fn check_room_access(trans: &DBTrans, player: &Item, room: &Item) -> U
return Ok(());
}
if player.flags.contains(&ItemFlag::EnterWithoutConsent) {
return Ok(());
}
if owner_t == "corp" {
let corp = match trans.find_corp_by_name(owner_c).await? {
None => return Ok(()), // Defunct corp HQ somehow...

View File

@ -1,4 +1,4 @@
use super::item::Item;
use super::item::{Item, ItemFlag};
use ansi_markup::parse_ansi_markup;
use serde::{Deserialize, Serialize};
@ -25,6 +25,19 @@ pub enum EffectParameter {
Raddamage,
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum EffectMitigant {
MitigantClothing { with_flag: ItemFlag },
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct EffectMitigation {
// Pre-multiplies the magnitude by the factor if the mitigant matches.
// In 1000ths.
pub amplitude_multiplier: u64,
pub mitigant: EffectMitigant,
}
pub enum Effect {
// messagef takes player, causative item, target as the 3 parameters. Returns message.
BroadcastMessage {
@ -42,8 +55,12 @@ pub enum Effect {
skill_multiplier: f64,
max_effect: i64,
parameter: EffectParameter,
mitigations: Vec<EffectMitigation>,
message: Box<dyn Fn(&Item) -> String + Sync + Send>,
},
CancelEffect {
other_effect: EffectType,
},
}
#[derive(Serialize, Deserialize)]
@ -63,6 +80,7 @@ pub enum SimpleEffect {
skill_multiplier: f64,
max_effect: i64,
parameter: EffectParameter,
mitigations: Vec<EffectMitigation>,
message: String,
},
}
@ -96,6 +114,7 @@ impl From<&SimpleEffect> for Effect {
skill_multiplier,
max_effect,
parameter,
mitigations,
message,
} => {
let messagem = parse_ansi_markup(message).unwrap() + "\n";
@ -105,6 +124,7 @@ impl From<&SimpleEffect> for Effect {
skill_multiplier: *skill_multiplier,
max_effect: *max_effect,
parameter: parameter.clone(),
mitigations: (*mitigations).clone(),
message: Box::new(move |_| messagem.clone()),
}
}

View File

@ -344,6 +344,9 @@ pub enum ItemFlag {
NoIdlePark,
DontCounterattack,
EnableCombatAi,
HalvesToxicFumes,
ProtectsToxicFumes,
EnterWithoutConsent,
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]

View File

@ -3,8 +3,10 @@ use super::{combat::change_health, comms::broadcast_to_room};
use crate::db::DBTrans;
use crate::{
models::{
effect::{Effect, EffectParameter, EffectSet, EffectType},
item::Item,
effect::{
Effect, EffectMitigant, EffectMitigation, EffectParameter, EffectSet, EffectType,
},
item::{Item, LocationActionType},
task::{Task, TaskDetails, TaskMeta},
},
regular_tasks::{TaskHandler, TaskRunContext},
@ -18,14 +20,18 @@ use log::info;
use mockall_double::double;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, VecDeque};
use std::time;
use std::{
collections::{BTreeMap, VecDeque},
sync::Arc,
};
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct DelayedParameterEffect {
magnitude: i64,
delay: u64,
parameter: EffectParameter,
mitigations: Vec<EffectMitigation>,
message: String,
}
@ -76,10 +82,13 @@ impl TaskHandler for DelayedParameterTaskHandler {
magnitude,
message,
parameter,
ref mitigations,
delay,
..
}) => {
let mut item_mut = (*item).clone();
let magnitude =
apply_effect_mitigations(&ctx.trans, &item, mitigations, magnitude).await?;
match parameter {
EffectParameter::Health => {
change_health(ctx.trans, magnitude, &mut item_mut, &message).await?;
@ -200,6 +209,42 @@ impl TaskHandler for DispelEffectTaskHandler {
pub static DISPEL_EFFECT_HANDLER: &'static (dyn TaskHandler + Sync + Send) =
&DispelEffectTaskHandler;
pub async fn apply_effect_mitigations(
trans: &DBTrans,
player: &Item,
mitigations: &Vec<EffectMitigation>,
initial_value: i64,
) -> DResult<i64> {
let mut value = initial_value as f64;
let mut clothing_cache: Option<Vec<Arc<Item>>> = None;
for mitigation in mitigations {
let is_mitigated = match &mitigation.mitigant {
EffectMitigant::MitigantClothing { with_flag } => {
let clothing = match clothing_cache {
None => {
let res = trans
.find_by_action_and_location(
&player.refstr(),
&LocationActionType::Worn,
)
.await?;
clothing_cache = Some(res);
clothing_cache.as_ref().unwrap()
}
Some(ref v) => v,
};
clothing.iter().any(|cl| cl.flags.contains(&with_flag))
}
};
if is_mitigated {
value *= mitigation.amplitude_multiplier as f64 / 1000.0;
}
}
Ok(value as i64)
}
pub async fn run_effects(
trans: &DBTrans,
effects: &EffectSet,
@ -286,6 +331,7 @@ pub async fn run_effects(
skill_multiplier,
max_effect,
parameter,
mitigations,
message,
} => {
let param_impact = *base_effect + ((skill_multiplier * level) as i64);
@ -294,6 +340,8 @@ pub async fn run_effects(
} else {
param_impact.max(*max_effect)
};
let param_impact =
apply_effect_mitigations(trans, player, &mitigations, param_impact).await?;
let msg = message(target.as_ref().map(|t| &**t).unwrap_or(player));
if *delay_secs == 0 {
match parameter {
@ -320,6 +368,7 @@ pub async fn run_effects(
magnitude: param_impact,
delay: *delay_secs,
parameter: (*parameter).clone(),
mitigations: mitigations.clone(),
message: msg,
};
target_health_series
@ -328,6 +377,16 @@ pub async fn run_effects(
.or_insert(VecDeque::from([fx]));
}
}
Effect::CancelEffect { other_effect } => {
let actual_target: &mut Item = *target.as_mut().unwrap_or(&mut player);
for (etype, effnum) in actual_target
.active_effects
.iter()
.filter(|(etype, _effnum)| etype == other_effect)
{
cancel_effect(trans, actual_target, &(etype.clone(), *effnum)).await?;
}
}
}
}
@ -447,6 +506,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} pulses from {}'s wound",
if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" },
@ -460,6 +520,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} pulses from {}'s wound",
if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" },
@ -473,6 +534,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} pulses from {}'s wound",
if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" },
@ -486,6 +548,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -6,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} pulses from {}'s wound",
if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" },
@ -499,6 +562,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -4,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} pulses from {}'s wound",
if target.species == SpeciesType::Robot { "Coolant" } else { "Blood" },
@ -512,6 +576,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -2,
parameter: EffectParameter::Health,
mitigations: vec![],
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" },
@ -560,6 +625,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -6,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks a bit unwell",
target.display_for_sentence(1, false),
@ -572,6 +638,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks pretty unwell",
target.display_for_sentence(1, false),
@ -584,6 +651,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -18,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks very sick",
target.display_for_sentence(1, false),
@ -596,6 +664,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -18,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks very sick",
target.display_for_sentence(1, false),
@ -608,6 +677,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks pretty unwell",
target.display_for_sentence(1, false),
@ -620,6 +690,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks pretty unwell",
target.display_for_sentence(1, false),
@ -632,6 +703,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -6,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} looks a bit unwell",
target.display_for_sentence(1, false),
@ -644,6 +716,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -6,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} feels the final effects of snake venom as it leaves {} body",
target.display_for_sentence(1, false),
@ -662,6 +735,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} howls out in pain from a sting",
target.display_for_sentence(1, false),
@ -674,6 +748,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} howls out in pain from a sting",
target.display_for_sentence(1, false),
@ -686,6 +761,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} howls out in pain from a sting",
target.display_for_sentence(1, false),
@ -698,6 +774,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
@ -710,6 +787,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
@ -722,6 +800,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
@ -734,6 +813,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{}'s cries as the sting really hurts",
target.display_for_sentence(1, false),
@ -746,6 +826,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{}'s cries as the sting really hurts",
target.display_for_sentence(1, false),
@ -758,6 +839,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{}'s cries as the sting really hurts",
target.display_for_sentence(1, false),
@ -770,6 +852,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} sobs as the sting still hurts",
target.display_for_sentence(1, false),
@ -782,6 +865,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} sobs as the sting still hurts",
target.display_for_sentence(1, false),
@ -794,6 +878,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} sobs as the sting still hurts",
target.display_for_sentence(1, false),
@ -806,6 +891,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} whimpers as the sting starts to heal",
target.display_for_sentence(1, false),
@ -818,6 +904,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} whimpers as the sting is almost healed",
target.display_for_sentence(1, false),
@ -830,6 +917,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
skill_multiplier: 0.0,
max_effect: -8,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target|
format!("{} whimpers as the sting is finally healed",
target.display_for_sentence(1, false),

View File

@ -25,7 +25,10 @@ use crate::{
DResult,
};
use super::{combat::change_health, comms::broadcast_to_room, skills::skill_check_and_grind};
use super::{
combat::change_health, comms::broadcast_to_room, effect::apply_effect_mitigations,
skills::skill_check_and_grind,
};
pub struct EffectEnvironmentHandler;
pub struct ConsequenceEnvironmentHandler;
@ -316,18 +319,24 @@ pub async fn passive_health_environment_effects(
return Ok(false);
}
let adjusted_passive_health = apply_effect_mitigations(
&ctx.trans,
player_item,
&room.environment.passive_health_mitigation,
room.environment.passive_health as i64,
)
.await?;
if adjusted_passive_health == 0 {
return Ok(false);
}
let msg = format!(
"{} {}",
player_item.display_for_sentence(1, true),
room.environment.passive_health_message
);
change_health(
ctx.trans,
room.environment.passive_health as i64,
player_item,
&msg,
)
.await?;
change_health(ctx.trans, adjusted_passive_health, player_item, &msg).await?;
Ok(true)
}

View File

@ -0,0 +1,29 @@
- craft_data:
skill: !Craft
difficulty: 5.0
inputs:
- Steak
output: GrilledSteak
recipe: GrilledSteakRecipe
bench: KitchenStove
- craft_data:
skill: !Chemistry
difficulty: 6.0
inputs:
- VenomSac
- CausticSoda
- VinylAcetate
- Azobisisobutyronitrile
- Borax
output: VenomExtractionKit
recipe: VenomExtractionKitInstructions
bench: ChemistrySet
- craft_data:
skill: !Chemistry
difficulty: 6.0
inputs:
- CausticSoda
- MediumTraumaKit
output: LargeTraumaKit
recipe: SuperchargeTraumaKit
bench: ChemistrySet

View File

@ -109,7 +109,7 @@ pub fn npc_list() -> Vec<NPC> {
frequency_secs: 600,
price: 100,
}),
extra_flags: vec![ItemFlag::CanLoad],
extra_flags: vec![ItemFlag::CanLoad, ItemFlag::EnterWithoutConsent],
total_stats: vec![(StatType::Brawn, 20.0)].into_iter().collect(),
..Default::default()
}

View File

@ -15,6 +15,7 @@ use mockall_double::double;
use once_cell::sync::OnceCell;
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};
use serde_yaml::from_str as from_yaml_str;
use std::collections::{BTreeMap, BTreeSet};
mod bags;
@ -22,6 +23,7 @@ mod benches;
mod blade;
mod books;
mod bottles;
mod chemicals;
mod club;
mod corp_licence;
mod fangs;
@ -452,6 +454,7 @@ pub enum PossessionType {
// Armour / Clothes
RustyMetalPot,
HockeyMask,
GasMask,
CombatHelmet,
CombatBoots,
Shirt,
@ -485,8 +488,10 @@ pub enum PossessionType {
Z3000Pistol,
// Medical
MediumTraumaKit,
LargeTraumaKit,
EmptyMedicalBox,
RadDetox,
VenomExtractionKit,
// Corporate
NewCorpLicence,
CertificateOfIncorporation,
@ -511,11 +516,21 @@ pub enum PossessionType {
RustySpike,
VenomSac,
SnakeSkin,
// Chemicals...
CausticSoda,
VinylAcetate,
Azobisisobutyronitrile,
Borax,
HydrochloricAcid,
// Craft benches
KitchenStove,
ChemistrySet,
// Recipes
CulinaryEssentials,
GrilledSteakRecipe,
EverydayChemistry,
VenomExtractionKitInstructions,
SuperchargeTraumaKit,
// Utilities
ElectricLantern,
GeigerCounter,
@ -614,6 +629,7 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, &'static Possessio
.chain(benches::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(blade::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(bottles::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(chemicals::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(trauma_kit::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(club::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
.chain(gun::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
@ -683,7 +699,7 @@ pub fn can_butcher_possessions() -> &'static Vec<PossessionType> {
})
}
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct CraftData {
pub skill: SkillType,
pub difficulty: f64,
@ -691,6 +707,7 @@ pub struct CraftData {
pub output: PossessionType,
}
#[derive(Serialize, Deserialize)]
pub struct RecipeCraftData {
pub craft_data: CraftData,
pub recipe: PossessionType,
@ -747,16 +764,7 @@ pub fn improv_by_output() -> &'static BTreeMap<PossessionType, &'static CraftDat
pub fn recipe_craft_table() -> &'static Vec<RecipeCraftData> {
static RECIPE_CELL: OnceCell<Vec<RecipeCraftData>> = OnceCell::new();
RECIPE_CELL.get_or_init(|| {
vec![RecipeCraftData {
craft_data: CraftData {
skill: SkillType::Craft,
difficulty: 5.0,
inputs: vec![PossessionType::Steak],
output: PossessionType::GrilledSteak,
},
recipe: PossessionType::GrilledSteakRecipe,
bench: Some(PossessionType::KitchenStove),
}]
from_yaml_str::<Vec<RecipeCraftData>>(include_str!("crafting.yaml")).unwrap()
})
}
@ -902,4 +910,8 @@ mod tests {
}
}
}
fn craft_table_populated() {
assert!(recipe_craft_table().len() > 0)
}
}

View File

@ -15,6 +15,9 @@ use once_cell::sync::OnceCell;
struct NeedPowerBench;
static NEED_POWER_BENCH: NeedPowerBench = NeedPowerBench;
struct UnpoweredBench;
static UNPOWER_BENCH: UnpoweredBench = UnpoweredBench;
#[async_trait]
impl BenchData for NeedPowerBench {
async fn check_make(&self, trans: &DBTrans, bench: &Item, _recipe: &Item) -> UResult<()> {
@ -30,10 +33,18 @@ impl BenchData for NeedPowerBench {
}
}
#[async_trait]
impl BenchData for UnpoweredBench {
async fn check_make(&self, _trans: &DBTrans, _bench: &Item, _recipe: &Item) -> UResult<()> {
Ok(())
}
}
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
static D: OnceCell<Vec<(PossessionType, PossessionData)>> = OnceCell::new();
&D.get_or_init(|| {
vec![(
vec![
(
PossessionType::KitchenStove,
PossessionData {
display: "kitchen stove",
@ -42,12 +53,31 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
weight: 40000,
container_data: Some(ContainerData {
base_weight: 40000,
max_weight: 15000,
..Default::default()
}),
bench_data: Some(&NEED_POWER_BENCH),
default_flags: vec![ItemFlag::Bench],
..Default::default()
},
)]
),
(
PossessionType::ChemistrySet,
PossessionData {
display:"chemistry set",
details: ansi!("A rigid black case that opens to reveal to collection of burners, glassware, and tools that seems to be designed to help a skilled chemist to run a chemical synthesis"),
aliases: vec!["workbench"],
weight: 15000,
bench_data: Some(&UNPOWER_BENCH),
container_data: Some(ContainerData {
base_weight: 15000,
max_weight: 15000,
..Default::default()
}),
default_flags: vec![ItemFlag::Bench],
..Default::default()
}
)
]
})
}

View File

@ -59,11 +59,47 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
(PossessionType::GrilledSteakRecipe,
PossessionData {
display: "recipe for Grilled Steak",
aliases: vec!["grilled steak"],
aliases: vec!["grilled steak", "recipe"],
details: "Instructions for how to make a basic but mouthwatering steak",
weight: 10,
default_flags: vec![ItemFlag::Instructions],
..Default::default()
}),
(PossessionType::EverydayChemistry,
PossessionData {
display: "Everyday Chemistry",
aliases: vec!["book", "chemistry book"],
details: "A weathered collection of instructions on various forms of chemical synthesis",
weight: 310,
container_data: Some(ContainerData {
base_weight: 290,
default_contents: vec![
PossessionType::VenomExtractionKitInstructions,
PossessionType::SuperchargeTraumaKit,
],
checker: &RECIPES_ONLY_CHECKER,
..Default::default()
}),
default_flags: vec![ItemFlag::Book],
..Default::default()
}),
(PossessionType::VenomExtractionKitInstructions,
PossessionData {
display: "venom extraction kit instructions",
aliases: vec!["recipe", "instructions"],
details: "Instructions for how to manufacture a special polymer that sucks all the venom from a bite or sting site",
weight: 10,
default_flags: vec![ItemFlag::Instructions],
..Default::default()
}),
(PossessionType::SuperchargeTraumaKit,
PossessionData {
display: "how to supercharge a trauma kit",
aliases: vec!["trauma kit", "large trauma kit", "supercharge a trauma kit", "recipe", "instructions"],
details: "A detailed written procedure for supercharging a trauma kit. Apparently, the empire deliberately reduced the capacity of the batteries in the equipment in the kit to keep people under its thumb, but a simple replacement of some alkaline solution in the batteries can double the number of treatments",
weight: 10,
default_flags: vec![ItemFlag::Instructions],
..Default::default()
}),
))
}

View File

@ -0,0 +1,61 @@
use super::{PossessionData, PossessionType};
use ansi::ansi;
use once_cell::sync::OnceCell;
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
static D: OnceCell<Vec<(PossessionType, PossessionData)>> = OnceCell::new();
&D.get_or_init(|| {
vec![
(
PossessionType::CausticSoda,
PossessionData {
display: "bottle of caustic soda",
details: ansi!("A rigid blue plastic bottle labelled as containing caustic soda. Apparently it's highly corrosive and shouldn't be eaten."),
aliases: vec!["chemical", "sodium hydroxide", "caustic soda"],
weight: 100,
..Default::default()
},
),
(
PossessionType::VinylAcetate,
PossessionData {
display: "jar of vinyl acetate",
details: ansi!("A glass jar containing a colourless liquid, labelled as vinyl acetate. Apparently it's both flammable and harmful."),
aliases: vec!["chemical", "monomer", "vinyl acetate"],
weight: 100,
..Default::default()
},
),
(
PossessionType::Azobisisobutyronitrile,
PossessionData {
display: "tube of azobisisobutyronitrile",
details: ansi!("A small tube containing some white crystals. Apparently they are flammable and harmful, and are also known as AIBN."),
aliases: vec!["chemical", "initiator", "AIBN", "azobisisobutyronitrile"],
weight: 20,
..Default::default()
},
),
(
PossessionType::Borax,
PossessionData {
display: "box of borax",
details: ansi!("A cardbox box containing some white powder, labelled as borax. Apparently it's very poisonous to ants."),
aliases: vec!["chemical", "borax"],
weight: 100,
..Default::default()
},
),
(
PossessionType::HydrochloricAcid,
PossessionData {
display: "jar of hydrochloric acid",
details: ansi!("A glass jar containing some liquid. It's labelled as being concentrated hydrochloric acid, and apparently it's very corrosive and can cause damage to the skin, lungs and eyes."),
aliases: vec!["chemical", "acid", "hydrochloric acid"],
weight: 100,
..Default::default()
},
),
]
})
}

View File

@ -1,5 +1,5 @@
use super::{DamageType, PossessionData, PossessionType, SoakData, WearData};
use crate::static_content::species::BodyPart;
use crate::{models::item::ItemFlag, static_content::species::BodyPart};
use once_cell::sync::OnceCell;
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
@ -81,6 +81,45 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
..Default::default()
}
),
(
PossessionType::GasMask,
PossessionData {
display: "gas mask",
details: "A green mask that fits snugly over the face with two elastic bands around the back of the head. Two large round white filters at the front that give the wear something of a bug like look.",
aliases: vec!("mask"),
weight: 350,
default_flags: vec![ItemFlag::HalvesToxicFumes],
wear_data: Some(WearData {
covers_parts: vec!(BodyPart::Face),
thickness: 7.0,
dodge_penalty: 0.25,
soaks: vec!(
(DamageType::Beat,
SoakData {
min_soak: 1.0,
max_soak: 2.0,
damage_probability_per_soak: 0.1
}
),
(DamageType::Slash,
SoakData {
min_soak: 1.0,
max_soak: 2.0,
damage_probability_per_soak: 0.1
}
),
(DamageType::Pierce,
SoakData {
min_soak: 1.0,
max_soak: 2.0,
damage_probability_per_soak: 0.1
}
),
).into_iter().collect()
}),
..Default::default()
}
),
(
PossessionType::CombatHelmet,
PossessionData {

View File

@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use super::{DamageType, PossessionData, PossessionType, SoakData, WearData};
use crate::static_content::species::BodyPart;
use crate::{models::item::ItemFlag, static_content::species::BodyPart};
use once_cell::sync::OnceCell;
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
@ -69,9 +69,10 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
PossessionType::RadSuit,
PossessionData {
display: "radiation suit",
details: "A suit with a yellow outer layer and a foil lining that covers every part of the body from head to toe, featuring a tight fitting respirator, a hood that covers the head, gloves, plastic overalls, and socks. It seems designed not to let anything in from the outside except through the filter of the respirator, although it probably will hinder movement and won't protect against anything except chemicals or radiation",
details: "A suit with a yellow outer layer and a foil lining that covers every part of the body from head to toe, featuring a tight fitting respirator, a hood that covers the head, gloves, plastic overalls, and socks. It seems designed not to let anything in from the outside except through the filter of the respirator, although it probably will hinder movement and won't protect against anything except chemicals or radiation.",
aliases: vec!("rad suit", "radsuit"),
weight: 2000,
default_flags: vec![ItemFlag::ProtectsToxicFumes],
wear_data: Some(WearData {
covers_parts: vec!(
BodyPart::Head,

View File

@ -8,6 +8,221 @@ use once_cell::sync::OnceCell;
use super::PossessionType::*;
fn trau_use_data() -> UseData {
UseData {
uses_skill: SkillType::Medic,
diff_level: 10.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 heal {} with a trauma 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,
mitigations: vec![],
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 heal {} with a trauma 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::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(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: 8.0,
max_effect: 10,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target| {
format!(
"FUUUCK! It hurts {}, but also starts to soothe {}",
target.display_for_sentence(1, false),
&target.pronouns.object
)
}),
},
Effect::ChangeTargetParameter {
delay_secs: 10,
base_effect: 2,
skill_multiplier: 7.0,
max_effect: 9,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target| {
format!(
"FUUUCK! It hurts {}, but also starts to soothe {}",
target.display_for_sentence(1, false),
&target.pronouns.object
)
}),
},
Effect::ChangeTargetParameter {
delay_secs: 20,
base_effect: 1,
skill_multiplier: 6.0,
max_effect: 7,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target| {
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
}),
},
Effect::ChangeTargetParameter {
delay_secs: 30,
base_effect: 1,
skill_multiplier: 5.0,
parameter: EffectParameter::Health,
mitigations: vec![],
max_effect: 6,
message: Box::new(|target| {
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
}),
},
Effect::ChangeTargetParameter {
delay_secs: 40,
base_effect: 0,
skill_multiplier: 4.0,
max_effect: 4,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target| {
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
}),
},
Effect::ChangeTargetParameter {
delay_secs: 50,
base_effect: 0,
skill_multiplier: 3.0,
max_effect: 3,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target| {
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
}),
},
Effect::ChangeTargetParameter {
delay_secs: 60,
base_effect: 0,
skill_multiplier: 2.0,
max_effect: 2,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(|target| {
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(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(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()
}
}
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
static D: OnceCell<Vec<(PossessionType, PossessionData)>> = OnceCell::new();
&D.get_or_init(|| vec!(
@ -22,190 +237,23 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
charge_name_suffix: "worth of supplies",
..Default::default()
}),
use_data: Some(UseData {
uses_skill: SkillType::Medic,
diff_level: 10.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 heal {} with a trauma 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)
use_data: Some(trau_use_data()),
becomes_on_spent: Some(EmptyMedicalBox),
..Default::default()
}
),
)
},
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 heal {} with a trauma 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::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(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: 8.0,
max_effect: 10,
parameter: EffectParameter::Health,
message: Box::new(
|target|
format!(
"FUUUCK! It hurts {}, but also starts to soothe {}",
target.display_for_sentence(1, false),
&target.pronouns.object
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 10, base_effect: 2, skill_multiplier: 7.0,
max_effect: 9,
parameter: EffectParameter::Health,
message: Box::new(
|target|
format!(
"FUUUCK! It hurts {}, but also starts to soothe {}",
target.display_for_sentence(1, false),
&target.pronouns.object
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 20, base_effect: 1, skill_multiplier: 6.0,
max_effect: 7,
parameter: EffectParameter::Health,
message: Box::new(
|target|
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 30, base_effect: 1, skill_multiplier: 5.0,
parameter: EffectParameter::Health,
max_effect: 6,
message: Box::new(
|target|
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 40, base_effect: 0, skill_multiplier: 4.0,
max_effect: 4,
parameter: EffectParameter::Health,
message: Box::new(
|target|
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 50, base_effect: 0, skill_multiplier: 3.0,
max_effect: 3,
parameter: EffectParameter::Health,
message: Box::new(
|target|
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 60, base_effect: 0, skill_multiplier: 2.0,
max_effect: 2,
parameter: EffectParameter::Health,
message: Box::new(
|target|
format!(
"The bandages soothe {}'s wounds",
target.display_for_sentence(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(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),
(LargeTraumaKit,
PossessionData {
display: "large trauma kit",
details: "A collection of bandages and and small gadgets that look like they could, in the right hands, make an unhealthy person healthy again. It looks like when brand new, it could be used 10 times.",
aliases: vec!("trauma"),
charge_data: Some(ChargeData {
max_charges: 10,
charge_name_prefix: "treatment",
charge_name_suffix: "worth of supplies",
..Default::default()
}),
use_data: Some(trau_use_data()),
becomes_on_spent: Some(EmptyMedicalBox),
..Default::default()
}
@ -255,10 +303,11 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 0, base_effect: -2, skill_multiplier: -3.0,
max_effect: -5,
parameter: EffectParameter::Health,
mitigations: vec![],
message: Box::new(
|target|
format!(
"Fuck! The trauma kit makes {}'s condition worse",
"Fuck! The rad detox makes {}'s condition worse",
target.display_for_sentence(1, false))
)
}
@ -304,6 +353,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 0, base_effect: -200, skill_multiplier: -8.0,
max_effect: -1000,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -316,6 +366,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 10, base_effect: -200, skill_multiplier: -7.0,
max_effect: -900,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -328,6 +379,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 20, base_effect: -100, skill_multiplier: -6.0,
max_effect: -700,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -340,6 +392,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 30, base_effect: -100, skill_multiplier: -5.0,
max_effect: -600,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -352,6 +405,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 40, base_effect: -100, skill_multiplier: -4.0,
max_effect: -600,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -364,6 +418,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 50, base_effect: 0, skill_multiplier: -3.0,
max_effect: -300,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -376,6 +431,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
delay_secs: 60, base_effect: 0, skill_multiplier: -2.0,
max_effect: -200,
parameter: EffectParameter::Raddamage,
mitigations: vec![],
message: Box::new(
|target|
format!(
@ -416,6 +472,101 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
becomes_on_spent: Some(EmptyMedicalBox),
..Default::default()
}
),
(
VenomExtractionKit,
PossessionData {
display: "venom extraction kit",
details: "A medical box labelled \"Venom Extraction\". It seems to have some kind of polymer slime that can suck any kind of venom out of a bite or sting. It looks like, in the hands of a skilled medic, it might be useful for treating up to 5 envenomations",
aliases: vec!("extraction"),
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 venom extraction 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,
mitigations: vec![],
message: Box::new(
|target|
format!(
"Fuck! The venom extraction 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 venom extraction 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::Ephemeral,
effects: vec!(
Effect::BroadcastMessage {
delay_secs: 0,
messagef: Box::new(|player, _item, target|
format!(
"{} expertly treats {} with a venom extraction 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::CancelEffect {
other_effect: EffectType::SnakePoisoned,
},
Effect::CancelEffect {
other_effect: EffectType::Stung,
}
)
}),
needs_consent_check: Some(ConsentType::Medicine),
..Default::default()
}),
..Default::default()
})
))
}

View File

@ -7,7 +7,7 @@ use crate::db::DBTrans;
use crate::{
message_handler::user_commands::{CommandHandlingError, UResult, VerbContext},
models::{
effect::SimpleEffect,
effect::{EffectMitigation, SimpleEffect},
item::{DoorState, Item, ItemFlag},
journal::JournalType,
user::WristpadHack,
@ -589,6 +589,7 @@ pub struct RoomEnvironment {
pub temperature: u16, // in 100ths of degrees celsius.
pub passive_health_message: String,
pub passive_health: i16, // in health points/10s tick. Can be negative to harm.
pub passive_health_mitigation: Vec<EffectMitigation>,
}
impl Default for RoomEnvironment {
@ -598,6 +599,7 @@ impl Default for RoomEnvironment {
temperature: 2000, // 20 degrees celsius
passive_health_message: "feels healthy".to_owned(),
passive_health: 0,
passive_health_mitigation: vec![],
}
}
}

View File

@ -120,6 +120,7 @@ impl TaskHandler for SeePatientTaskHandler {
base_effect: 10,
skill_multiplier: 0.0,
parameter: EffectParameter::Health,
mitigations: vec![],
max_effect: 10,
message: Box::new(|_item| "That feels better".to_owned()),
},
@ -128,12 +129,14 @@ impl TaskHandler for SeePatientTaskHandler {
base_effect: 9,
skill_multiplier: 0.0,
parameter: EffectParameter::Health,
mitigations: vec![],
max_effect: 10,
message: Box::new(|_item| "That feels better".to_owned()),
},
Effect::ChangeTargetParameter {
delay_secs: 20,
parameter: EffectParameter::Health,
mitigations: vec![],
base_effect: 8,
skill_multiplier: 0.0,
max_effect: 10,
@ -142,6 +145,7 @@ impl TaskHandler for SeePatientTaskHandler {
Effect::ChangeTargetParameter {
delay_secs: 30,
parameter: EffectParameter::Health,
mitigations: vec![],
base_effect: 7,
skill_multiplier: 0.0,
max_effect: 10,

View File

@ -21,6 +21,46 @@ pub fn street_scavtable() -> Vec<Scavinfo> {
}]
}
pub fn toxic_dump_scavtable() -> Vec<Scavinfo> {
vec![
Scavinfo {
possession_type: PossessionType::CausticSoda,
p_present: 1.0,
difficulty_mean: 8.0,
difficulty_stdev: 1.0,
scavtype: Scavtype::Scavenge,
},
Scavinfo {
possession_type: PossessionType::VinylAcetate,
p_present: 1.0,
difficulty_mean: 8.0,
difficulty_stdev: 1.0,
scavtype: Scavtype::Scavenge,
},
Scavinfo {
possession_type: PossessionType::Azobisisobutyronitrile,
p_present: 1.0,
difficulty_mean: 8.0,
difficulty_stdev: 1.0,
scavtype: Scavtype::Scavenge,
},
Scavinfo {
possession_type: PossessionType::Borax,
p_present: 1.0,
difficulty_mean: 8.0,
difficulty_stdev: 1.0,
scavtype: Scavtype::Scavenge,
},
Scavinfo {
possession_type: PossessionType::HydrochloricAcid,
p_present: 1.0,
difficulty_mean: 8.0,
difficulty_stdev: 1.0,
scavtype: Scavtype::Scavenge,
},
]
}
pub fn fixed_items() -> Vec<FixedItem> {
vec![
FixedItem {

View File

@ -630,7 +630,7 @@
should_caption: false
scavtable: CityStreet
- zone: melbs
code: melbs_swansonst_10
code: melbs_swanstonst_10
name: Swanston Street - 10 block
short: <yellow>||<reset>
grid_coords:
@ -641,8 +641,49 @@
exits:
- direction: north
- direction: south
- direction: east
should_caption: false
scavtable: CityStreet
- zone: melbs
code: melbs_bhopal_laneway
name: Bhopal Laneway
short: <yellow>==<reset>
grid_coords:
x: 18
y: -4
z: 0
description: A laneway of slightly newer construction to the surrounding streets. If an empire-era plaque is to be believed, the laneway and attached chemical waste dump was built on the order of the emperor to celebrate his belief in the prioritisation of profitability for the elites over human health and safety, and the exemplary demonstration of this value by Union Carbide in Bhopal in the early 1980s. Apparently, as a further monument to these values, to the east he also ordered the construction of an enormous chemical waste dump to allow empire industries to cheaply dispose of unwanted toxic chemicals. It smells acrid here
repel_npc: true
exits:
- direction: east
- direction: west
should_caption: false
scavtable: CityStreet
- zone: melbs
code: melbs_toxic_dump_1
name: Toxic Dump West
short: <bggreen><black>TD<reset>
grid_coords:
x: 19
y: -4
z: 0
description: A yard that appears to be some kind of toxic waste dump, enclosed only by a chain link fence. Signs on the fence indicate that lethal toxic waste is buried here. Rusting drums lie in mud around the open air facility, while the dirt shows signs of having been disturbed
exits:
- direction: west
should_caption: true
scavtable: ToxicDump
environment:
passive_health: -5
passive_health_message: The toxins work their way into the lungs
passive_health_mitigation:
- amplitude_multiplier: 500 # Half
mitigant:
!MitigantClothing
with_flag: HalvesToxicFumes
- amplitude_multiplier: 0
mitigant:
!MitigantClothing
with_flag: ProtectsToxicFumes
- zone: melbs
code: melbs_swanstonst_20
name: Swanston Street - 20 block
@ -1454,8 +1495,29 @@
- direction: north
- direction: south
- direction: east
- direction: west
should_caption: false
scavtable: CityStreet
- zone: melbs
code: melbs_psi_eldritch
name: Psi Eldritch Scientific
short: <bggreen><blue>PE<reset>
grid_coords:
x: 4
y: 4
z: 0
description: |-
A warehouse-style shop that appears to sell all sorts of scientific equipment, much of it made of glass. Row after row of metal shelves house glassware in every imaginable shape. Another section has bottles of variously coloured liquids, many featuring triangular skull-and-crossbone pictograms on them.
It smells slightly acrid here.
Behind a counter, a serious looking tall man clad in a labcoat stands behind a counter, waiting patiently for you to select your equipment and pay him
exits:
- direction: east
stock_list:
- possession_type: ChemistrySet
list_price: 5000
poverty_discount: false
- zone: melbs
code: melbs_dustypages
name: The Dusty Pages
@ -1478,6 +1540,9 @@
- possession_type: CulinaryEssentials
list_price: 200
poverty_discount: false
- possession_type: EverydayChemistry
list_price: 400
poverty_discount: false
- zone: melbs
code: melbs_williamsst_80
name: Williams St - 80 block

View File

@ -1,6 +1,9 @@
use crate::{
models::item::Scavtype,
static_content::room::{melbs_sewers::sewer_scavtable, ronalds_house::ronalds_house_scavtable},
static_content::room::{
melbs::toxic_dump_scavtable, melbs_sewers::sewer_scavtable,
ronalds_house::ronalds_house_scavtable,
},
};
use super::{possession_type::PossessionType, room::melbs::street_scavtable};
@ -21,6 +24,7 @@ pub struct Scavinfo {
pub enum ScavtableType {
Nothing,
CityStreet,
ToxicDump,
CitySewer,
RonaldsHouse,
}
@ -31,6 +35,7 @@ pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> {
vec![
(ScavtableType::Nothing, vec![]),
(ScavtableType::CityStreet, street_scavtable()),
(ScavtableType::ToxicDump, toxic_dump_scavtable()),
(ScavtableType::CitySewer, sewer_scavtable()),
(ScavtableType::RonaldsHouse, ronalds_house_scavtable()),
]