Add Geiger Counter and Radsuit
This commit is contained in:
parent
f37baf187e
commit
36086b809c
@ -255,20 +255,28 @@ pub async fn describe_normal_item(
|
||||
}
|
||||
|
||||
if item.item_type == "possession" {
|
||||
if let Some(charge_data) = item
|
||||
if let Some(poss_data) = item
|
||||
.possession_type
|
||||
.as_ref()
|
||||
.and_then(|pt| possession_data().get(&pt))
|
||||
.and_then(|pd| pd.charge_data.as_ref())
|
||||
{
|
||||
let unit = if item.charges == 1 {
|
||||
charge_data.charge_name_prefix.to_owned() + " " + charge_data.charge_name_suffix
|
||||
} else {
|
||||
language::pluralise(charge_data.charge_name_prefix)
|
||||
+ " "
|
||||
+ charge_data.charge_name_suffix
|
||||
};
|
||||
contents_desc.push_str(&format!("It has {} {} left.\n", item.charges, unit));
|
||||
if let Some(describer) = poss_data.computed_extra_details {
|
||||
contents_desc.push_str(
|
||||
&describer
|
||||
.describe_for(&ctx.trans, item, player_item)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
if let Some(charge_data) = poss_data.charge_data.as_ref() {
|
||||
let unit = if item.charges == 1 {
|
||||
charge_data.charge_name_prefix.to_owned() + " " + charge_data.charge_name_suffix
|
||||
} else {
|
||||
language::pluralise(charge_data.charge_name_prefix)
|
||||
+ " "
|
||||
+ charge_data.charge_name_suffix
|
||||
};
|
||||
contents_desc.push_str(&format!("It has {} {} left.\n", item.charges, unit));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(recipe_craft_data) = item
|
||||
|
@ -61,7 +61,7 @@ async fn check_removeable(ctx: &mut QueuedCommandContext<'_>, item: &Item) -> UR
|
||||
covers_parts.contains(&part)
|
||||
&& other_item
|
||||
.action_type_started
|
||||
.map(|other_worn_since| other_worn_since < my_worn_since)
|
||||
.map(|other_worn_since| other_worn_since > my_worn_since)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
services::{charging, combat, effect, environment, idlepark, sharing, spawn, tempbuff, urges},
|
||||
static_content::{
|
||||
npc::{self, computer_museum_npcs},
|
||||
possession_type::lights,
|
||||
possession_type::utilities,
|
||||
room::general_hospital,
|
||||
},
|
||||
DResult,
|
||||
@ -70,7 +70,7 @@ fn task_handler_registry(
|
||||
("IdlePark", idlepark::IDLEPARK_HANDLER),
|
||||
("HospitalERSeePatient", general_hospital::SEE_PATIENT_TASK),
|
||||
("ExpireBuff", tempbuff::EXPIRE_BUFF_TASK),
|
||||
("DischargeLight", lights::DISCHARGE_TASK),
|
||||
("DischargeLight", utilities::DISCHARGE_TASK),
|
||||
("ChargeItem", charging::TASK_HANDLER),
|
||||
(
|
||||
"ApplyEnvironmentalEffects",
|
||||
|
@ -79,8 +79,9 @@ pub async fn soak_damage<DamageDist: DamageDistribution>(
|
||||
break;
|
||||
}
|
||||
let soak_amount: f64 = ((soak.max_soak - soak.min_soak)
|
||||
* rand::thread_rng().gen::<f64>())
|
||||
.min(damage_amount);
|
||||
* rand::thread_rng().gen::<f64>()
|
||||
+ soak.min_soak)
|
||||
.min(damage_amount);
|
||||
damage_amount -= soak_amount;
|
||||
let clothes_damage = ((0..(soak_amount as i64))
|
||||
.filter(|_| rand::thread_rng().gen::<f64>() < soak.damage_probability_per_soak)
|
||||
|
@ -3,6 +3,7 @@ use std::time;
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use log::warn;
|
||||
use rand::Rng;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
@ -10,14 +11,17 @@ use crate::{
|
||||
movement::attempt_move_immediate, CommandHandlingError, UResult,
|
||||
},
|
||||
models::{
|
||||
item::{Item, SkillType},
|
||||
item::{Item, LocationActionType, SkillType},
|
||||
task::{Task, TaskDetails, TaskMeta},
|
||||
},
|
||||
regular_tasks::{
|
||||
queued_command::{MovementSource, QueueCommand, QueuedCommandContext},
|
||||
TaskHandler, TaskRunContext,
|
||||
},
|
||||
static_content::room::{room_map_by_code, Direction, MaterialType, Room},
|
||||
static_content::{
|
||||
possession_type::{possession_data, DamageType},
|
||||
room::{room_map_by_code, Direction, MaterialType, Room},
|
||||
},
|
||||
DResult,
|
||||
};
|
||||
|
||||
@ -257,7 +261,28 @@ pub async fn rad_environment_effects(
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
player_item.raddamage += room.environment.radiation;
|
||||
let clothes = ctx
|
||||
.trans
|
||||
.find_by_action_and_location(&player_item.refstr(), &LocationActionType::Worn)
|
||||
.await?;
|
||||
let shielding: u64 = clothes
|
||||
.iter()
|
||||
.filter_map(|c| {
|
||||
c.possession_type
|
||||
.as_ref()
|
||||
.and_then(|pt| possession_data().get(&pt))
|
||||
.and_then(|pd| pd.wear_data.as_ref())
|
||||
.and_then(|wd| wd.soaks.get(&DamageType::Radiation))
|
||||
.map(|sd| {
|
||||
(sd.max_soak - sd.min_soak) * rand::thread_rng().gen::<f64>() + sd.min_soak
|
||||
})
|
||||
})
|
||||
.sum::<f64>() as u64;
|
||||
if shielding >= room.environment.radiation {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
player_item.raddamage += room.environment.radiation - shielding;
|
||||
let existing_task = ctx
|
||||
.trans
|
||||
.fetch_specific_task("ApplyEnvironmentalConsequences", &player_item.refstr())
|
||||
|
@ -29,13 +29,13 @@ mod food;
|
||||
pub mod head_armour;
|
||||
mod junk;
|
||||
mod keys;
|
||||
pub mod lights;
|
||||
pub mod lock;
|
||||
pub mod lower_armour;
|
||||
mod meat;
|
||||
mod testpapers;
|
||||
pub mod torso_armour;
|
||||
mod trauma_kit;
|
||||
pub mod utilities;
|
||||
mod whip;
|
||||
|
||||
pub type AttackMessageChoice = Vec<Box<dyn Fn(&Item, &Item) -> String + 'static + Sync + Send>>;
|
||||
@ -57,6 +57,7 @@ pub enum DamageType {
|
||||
Pierce,
|
||||
Shock,
|
||||
Bullet,
|
||||
Radiation,
|
||||
}
|
||||
|
||||
impl DamageType {
|
||||
@ -69,6 +70,7 @@ impl DamageType {
|
||||
Pierce => "pierce",
|
||||
Shock => "shock",
|
||||
Bullet => "bullet",
|
||||
Radiation => "radiation",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -323,6 +325,11 @@ pub struct SitData {
|
||||
pub stress_impact: i16,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Describer {
|
||||
async fn describe_for(&self, trans: &DBTrans, item: &Item, player: &Item) -> UResult<String>;
|
||||
}
|
||||
|
||||
pub struct PossessionData {
|
||||
pub weapon_data: Option<WeaponData>,
|
||||
pub display: &'static str,
|
||||
@ -347,6 +354,7 @@ pub struct PossessionData {
|
||||
pub eat_data: Option<EatData>,
|
||||
pub sit_data: Option<SitData>,
|
||||
pub static_special_data: Option<ItemStaticSpecialData>,
|
||||
pub computed_extra_details: Option<&'static (dyn Describer + Sync + Send)>,
|
||||
}
|
||||
|
||||
impl Default for PossessionData {
|
||||
@ -375,6 +383,7 @@ impl Default for PossessionData {
|
||||
eat_data: None,
|
||||
sit_data: None,
|
||||
static_special_data: None,
|
||||
computed_extra_details: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -402,13 +411,14 @@ pub enum PossessionType {
|
||||
// Special values that substitute for possessions.
|
||||
Fangs, // Default weapon for certain animals
|
||||
// Real possessions from here on:
|
||||
// Armour
|
||||
// Armour / Clothes
|
||||
RustyMetalPot,
|
||||
HockeyMask,
|
||||
Shirt,
|
||||
LeatherJacket,
|
||||
Jeans,
|
||||
LeatherPants,
|
||||
RadSuit,
|
||||
// Weapons: Whips
|
||||
AntennaWhip,
|
||||
LeatherWhip,
|
||||
@ -454,8 +464,9 @@ pub enum PossessionType {
|
||||
// Recipes
|
||||
CulinaryEssentials,
|
||||
GrilledSteakRecipe,
|
||||
// Lights
|
||||
// Utilities
|
||||
ElectricLantern,
|
||||
GeigerCounter,
|
||||
}
|
||||
|
||||
impl Into<Item> for PossessionType {
|
||||
@ -579,7 +590,7 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, &'static Possessio
|
||||
.map(|v| ((*v).0.clone(), &(*v).1)),
|
||||
)
|
||||
.chain(books::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(lights::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(utilities::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
@ -65,5 +65,39 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
|
||||
..Default::default()
|
||||
}
|
||||
),
|
||||
(
|
||||
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",
|
||||
aliases: vec!("rad suit", "radsuit"),
|
||||
weight: 2000,
|
||||
wear_data: Some(WearData {
|
||||
covers_parts: vec!(
|
||||
BodyPart::Head,
|
||||
BodyPart::Face,
|
||||
BodyPart::Hands,
|
||||
BodyPart::Arms,
|
||||
BodyPart::Chest,
|
||||
BodyPart::Back,
|
||||
BodyPart::Groin,
|
||||
BodyPart::Legs,
|
||||
BodyPart::Feet
|
||||
),
|
||||
thickness: 4.0,
|
||||
dodge_penalty: 1.0,
|
||||
soaks: vec!(
|
||||
(DamageType::Radiation,
|
||||
SoakData {
|
||||
min_soak: 500.0,
|
||||
max_soak: 600.0,
|
||||
damage_probability_per_soak: 0.01
|
||||
}
|
||||
),
|
||||
).into_iter().collect(),
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
),
|
||||
))
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#[double]
|
||||
use crate::db::DBTrans;
|
||||
use crate::{
|
||||
message_handler::user_commands::{user_error, UResult, VerbContext},
|
||||
models::{
|
||||
@ -6,16 +8,17 @@ use crate::{
|
||||
},
|
||||
regular_tasks::{TaskHandler, TaskRunContext},
|
||||
services::comms::broadcast_to_room,
|
||||
static_content::possession_type::ChargeData,
|
||||
static_content::{possession_type::ChargeData, room::room_map_by_code},
|
||||
DResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use log::warn;
|
||||
use mockall_double::double;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::time;
|
||||
|
||||
use super::{PossessionData, PossessionType, TurnToggleHandler};
|
||||
use super::{Describer, PossessionData, PossessionType, TurnToggleHandler};
|
||||
|
||||
pub struct LanternHandler {}
|
||||
|
||||
@ -163,21 +166,55 @@ impl TaskHandler for DischargeTaskHandler {
|
||||
}
|
||||
pub static DISCHARGE_TASK: &(dyn TaskHandler + Sync + Send) = &DischargeTaskHandler;
|
||||
|
||||
pub struct GeigerCounterDescriber;
|
||||
|
||||
#[async_trait]
|
||||
impl Describer for GeigerCounterDescriber {
|
||||
async fn describe_for(&self, _trans: &DBTrans, _item: &Item, player: &Item) -> UResult<String> {
|
||||
let (loc_type, loc_code) = match player.location.split_once("/") {
|
||||
None => return Ok("It doesn't currently show a reading.\n".to_owned()),
|
||||
Some(v) => v,
|
||||
};
|
||||
let level = if loc_type != "room" {
|
||||
0
|
||||
} else {
|
||||
room_map_by_code()
|
||||
.get(loc_code)
|
||||
.map(|r| r.environment.radiation)
|
||||
.unwrap_or(0)
|
||||
};
|
||||
Ok(format!(
|
||||
"It shows a reading of {} millisieverts/hour.\n",
|
||||
level * 360
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
|
||||
static D: OnceCell<Vec<(PossessionType, PossessionData)>> = OnceCell::new();
|
||||
&D.get_or_init(|| vec![(PossessionType::ElectricLantern, PossessionData {
|
||||
display: "electric lantern",
|
||||
aliases: vec!["lantern"],
|
||||
details: "A rechargeable electric lantern. It is made of yellow plastic, interspersed with banks of tiny flat white LEDs. It looks like it would illuminate a dark room well, although it probably isn't bright enough to let you see anything in the next room over.",
|
||||
weight: 300,
|
||||
turn_toggle_handler: Some(&LANTERN_HANDLER),
|
||||
charge_data: Some(ChargeData {
|
||||
max_charges: 20,
|
||||
charge_name_prefix: "bar",
|
||||
charge_name_suffix: "of power",
|
||||
electric_recharge: true,
|
||||
..ChargeData::default()
|
||||
&D.get_or_init(|| vec![
|
||||
(PossessionType::ElectricLantern, PossessionData {
|
||||
display: "electric lantern",
|
||||
aliases: vec!["lantern"],
|
||||
details: "A rechargeable electric lantern. It is made of yellow plastic, interspersed with banks of tiny flat white LEDs. It looks like it would illuminate a dark room well, although it probably isn't bright enough to let you see anything in the next room over.",
|
||||
weight: 300,
|
||||
turn_toggle_handler: Some(&LANTERN_HANDLER),
|
||||
charge_data: Some(ChargeData {
|
||||
max_charges: 20,
|
||||
charge_name_prefix: "bar",
|
||||
charge_name_suffix: "of power",
|
||||
electric_recharge: true,
|
||||
..ChargeData::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})])
|
||||
(PossessionType::GeigerCounter, PossessionData {
|
||||
display: "geiger counter",
|
||||
aliases: vec!["counter"],
|
||||
details: "A sturdy black rectangular box with a yellow rubber protective case. On the front is a display showing numbers. Beneath the display is the text: 'OORANS approved Geiger Counter. Calibrated to predict effective dose of radiation from exposure to standard fallout. For use by trained personnel only'. ",
|
||||
weight: 300,
|
||||
computed_extra_details: Some(&GeigerCounterDescriber),
|
||||
..Default::default()
|
||||
})
|
||||
])
|
||||
}
|
@ -1631,9 +1631,10 @@
|
||||
y: 0
|
||||
z: 0
|
||||
exits:
|
||||
- direction: west
|
||||
- direction: east
|
||||
- direction: north
|
||||
- direction: east
|
||||
- direction: south
|
||||
- direction: west
|
||||
- zone: oorans
|
||||
code: oorans_training
|
||||
name: OORANS Training Centre
|
||||
@ -1695,8 +1696,10 @@
|
||||
exits:
|
||||
- direction: north
|
||||
stock_list:
|
||||
- possession_type: !RadSafetyTest
|
||||
list_price: 1000
|
||||
- possession_type: !GeigerCounter
|
||||
list_price: 2500
|
||||
- possession_type: !RadSuit
|
||||
list_price: 4000
|
||||
- zone: melbs
|
||||
code: melbs_williamsst_collinsst
|
||||
name: Williams St & Collins St
|
||||
|
Loading…
Reference in New Issue
Block a user