Skip to content

Commit

Permalink
Disease infrastructure (#35843)
Browse files Browse the repository at this point in the history
* affected_bodyparts
* expose_to_disease()
* foodborne diseases
  • Loading branch information
Fris0uman authored Apr 4, 2020
1 parent 6393a6d commit 2bfe097
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 11 deletions.
12 changes: 12 additions & 0 deletions data/json/disease.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"type": "disease_type",
"id": "bad_food",
"min_duration": "6 m",
"max_duration": "1 h",
"min_intensity": 1,
"max_intensity": 1,
"health_threshold": 100,
"symptoms": "foodpoison"
}
]
2 changes: 1 addition & 1 deletion data/json/items/comestibles/dairy.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"copy-from": "milk",
"spoils_in": "12 hours",
"price": 19,
"contamination": 5,
"contamination": [ { "disease": "bad_food", "probability": 5 } ],
"description": "This is raw, unhomogenized and unpasteurized milk from a cow. It couldn't be any fresher unless you drank it straight from the cow, which might upset it. Depending on your dietary sensibilities, you might want to pasteurize or even boil this before drinking."
},
{
Expand Down
32 changes: 31 additions & 1 deletion doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Use the `Home` key to return to the top.
* [`data/json/` JSONs](#datajson-jsons)
+ [Bionics](#bionics)
+ [Dreams](#dreams)
+ [Disease](#disease_type)
+ [Item Groups](#item-groups)
+ [Item Category](#item-category)
+ [Materials](#materials)
Expand Down Expand Up @@ -188,6 +189,7 @@ Here's a quick summary of what each of the JSON files contain, broken down by fo
| default_blacklist.json | a standard blacklist of joke monsters
| doll_speech.json | talk doll speech messages
| dreams.json | dream text and linked mutation categories
| disease.json | disease definitions
| effects.json | common effects and their effects
| emit.json | smoke and gas emissions
| flags.json | common flags and their descriptions
Expand Down Expand Up @@ -457,6 +459,34 @@ When adding a new bionic, if it's not included with another one, you must also a
}
```

### Disease

| Identifier | Description
|--- |---
| id | Unique ID. Must be one continuous word, use underscores if necessary.
| min_duration | The minimum duration the disease can last. Uses strings "x m", "x s","x d".
| max_duration | The maximum duration the disease can last.
| min_intensity | The minimum intensity of the effect applied by the disease
| max_intensity | The maximum intensity of the effect.
| health_threshold | The amount of health above which one is immune to the disease. Must be between -200 and 200. (optional )
| symptoms | The effect applied by the disease.
| affected_bodyparts | The list of bodyparts on which the effect is applied. (optional, default to num_bp)


```json
{
"type": "disease_type",
"id": "bad_food",
"min_duration": "6 m",
"max_duration": "1 h",
"min_intensity": 1,
"max_intensity": 1,
"affected_bodyparts": [ "TORSO" ],
"health_threshold": 100,
"symptoms": "foodpoison"
}
```

### Item Groups

Item groups have been expanded, look at [the detailed docs](ITEM_SPAWN.md) to their new description.
Expand Down Expand Up @@ -1603,7 +1633,7 @@ CBMs can be defined like this:
"freezing_point": 32, // (Optional) Temperature in F at which item freezes, default is water (32F/0C)
"cooks_like": "meat_cooked" // (Optional) If the item is used in a recipe, replaces it with its cooks_like
"parasites": 10, // (Optional) Probability of becoming parasitised when eating
"contamination": 5, // (Optional) Probability to get food poisoning from this comestible. Values must be in the [0, 100] range.
"contamination": [ { "disease": "bad_food", "probability": 5 } ], // (Optional) List of diseases carried by this comestible and their associated probability. Values must be in the [0, 100] range.
```
### Containers
Expand Down
20 changes: 20 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "construction.h"
#include "coordinate_conversions.h"
#include "debug.h"
#include "disease.h"
#include "effect.h"
#include "event_bus.h"
#include "field.h"
Expand Down Expand Up @@ -1485,6 +1486,25 @@ void Character::add_effect( const efftype_id &eff_id, const time_duration &dur,
Creature::add_effect( eff_id, dur, bp, permanent, intensity, force, deferred );
}

void Character::expose_to_disease( const diseasetype_id dis_type )
{
const cata::optional<int> &healt_thresh = dis_type->health_threshold;
if( healt_thresh && healt_thresh.value() < get_healthy() ) {
return;
}
const std::set<body_part> &bps = dis_type->affected_bodyparts;
if( !bps.empty() ) {
for( const body_part &bp : bps ) {
add_effect( dis_type->symptoms, rng( dis_type->min_duration, dis_type->max_duration ), bp, false,
rng( dis_type->min_intensity, dis_type->max_intensity ) );
}
} else {
add_effect( dis_type->symptoms, rng( dis_type->min_duration, dis_type->max_duration ), num_bp,
false,
rng( dis_type->min_intensity, dis_type->max_intensity ) );
}
}

void Character::process_turn()
{
for( bionic &i : *my_bionics ) {
Expand Down
3 changes: 3 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,9 @@ class Character : public Creature, public visitable<Character>
void add_effect( const efftype_id &eff_id, const time_duration &dur, body_part bp = num_bp,
bool permanent = false,
int intensity = 0, bool force = false, bool deferred = false ) override;

/**Determine if character is susceptible to dis_type and if so apply the symptoms*/
void expose_to_disease( const diseasetype_id dis_type );
/**
* Handles end-of-turn processing.
*/
Expand Down
9 changes: 4 additions & 5 deletions src/consumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "calendar.h"
#include "cata_utility.h"
#include "debug.h"
#include "disease.h"
#include "game.h"
#include "itype.h"
#include "map.h"
Expand Down Expand Up @@ -1001,11 +1002,9 @@ bool player::eat( item &food, bool force )
}
}

// Chance to get food poisoning from bacterial contamination
if( !will_vomit && !has_bionic( bio_digestion ) ) {
const int contamination = food.get_comestible()->contamination;
if( rng( 1, 100 ) <= contamination ) {
add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) );
for( const std::pair<diseasetype_id, int> &elem : food.get_comestible()->contamination ) {
if( rng( 1, 100 ) <= elem.second ) {
expose_to_disease( elem.first );
}
}

Expand Down
61 changes: 61 additions & 0 deletions src/disease.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "disease.h"

#include "assign.h"
#include "generic_factory.h"

namespace
{
generic_factory<disease_type> disease_factory( "disease_type" );
} // namespace

template<>
const disease_type &string_id<disease_type>::obj() const
{
return disease_factory.obj( *this );
}

template<>
bool string_id<disease_type>::is_valid() const
{
return disease_factory.is_valid( *this );
}

void disease_type::load_disease_type( const JsonObject &jo, const std::string &src )
{
disease_factory.load( jo, src );
}

void disease_type::load( const JsonObject &jo, const std::string & )
{
disease_type new_disease;

assign( jo, "id", id );
assign( jo, "min_duration", min_duration );
assign( jo, "max_duration", max_duration );
assign( jo, "min_intensity", min_intensity );
assign( jo, "max_intensity", max_intensity );
assign( jo, "health_threshold", health_threshold );
assign( jo, "symptoms", symptoms );

JsonArray jsr = jo.get_array( "affected_bodyparts" );
while( jsr.has_more() ) {
new_disease.affected_bodyparts.emplace( get_body_part_token( jsr.next_string() ) );
}

}

const std::vector<disease_type> &disease_type::get_all()
{
return disease_factory.get_all();
}

void disease_type::check_disease_consistency()
{
for( const disease_type &dis : get_all() ) {
const efftype_id &symp = dis.symptoms;
if( !symp.is_valid() ) {
debugmsg( "disease_type %s has invalid efftype_id %s in symptoms", dis.id.c_str(), symp.c_str() );
}
}
}

33 changes: 33 additions & 0 deletions src/disease.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once
#ifndef DISEASE_H
#define DISEASE_H

#include "bodypart.h"
#include "effect.h"
#include "type_id.h"
#include "json.h"

class disease_type
{
public:
static void load_disease_type( const JsonObject &jo, const std::string &src );
void load( const JsonObject &jo, const std::string & );
static const std::vector<disease_type> &get_all();
static void check_disease_consistency();
bool was_loaded;

diseasetype_id id;
time_duration min_duration = 1_turns;
time_duration max_duration = 1_turns;
int min_intensity = 1;
int max_intensity = 1;
/**Affected body parts*/
std::set<body_part> affected_bodyparts;
/**If not empty this sets the health threshold above which you're immune to the disease*/
cata::optional<int> health_threshold;
/**effect applied by this disease*/
efftype_id symptoms;

};
#endif

3 changes: 3 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "creature.h"
#include "debug.h"
#include "dialogue.h"
#include "disease.h"
#include "effect.h"
#include "emit.h"
#include "event_statistics.h"
Expand Down Expand Up @@ -221,6 +222,7 @@ void DynamicDataLoader::initialize()
add( "enchantment", &enchantment::load_enchantment );
add( "hit_range", &Creature::load_hit_range );
add( "scent_type", &scent_type::load_scent_type );
add( "disease_type", &disease_type::load_disease_type );

// json/colors.json would be listed here, but it's loaded before the others (see init_colors())
// Non Static Function Access
Expand Down Expand Up @@ -696,6 +698,7 @@ void DynamicDataLoader::check_consistency( loading_ui &ui )
{ _( "Statistics" ), &event_statistic::check_consistency },
{ _( "Scent types" ), &scent_type::check_scent_consistency },
{ _( "Scores" ), &score::check_consistency },
{ _( "Disease types" ), &disease_type::check_disease_consistency },
{ _( "Factions" ), &faction_template::check_consistency },
}
};
Expand Down
14 changes: 13 additions & 1 deletion src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,14 @@ void Item_factory::finalize_post( itype &obj )
}
}

if( obj.comestible ) {
for( const std::pair<diseasetype_id, int> elem : obj.comestible->contamination ) {
const diseasetype_id dtype = elem.first;
if( !dtype.is_valid() ) {
debugmsg( "contamination in %s contains invalid diseasetype_id %s.", obj.id, dtype.c_str() );
}
}
}
for( std::string &line : obj.ascii_picture ) {
if( utf8_width( remove_color_tags( line ) ) > ascii_art_width ) {
line = trim_by_length( line, ascii_art_width );
Expand Down Expand Up @@ -1834,12 +1842,16 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std
assign( jo, "fatigue_mod", slot.fatigue_mod, strict );
assign( jo, "healthy", slot.healthy, strict );
assign( jo, "parasites", slot.parasites, strict, 0 );
assign( jo, "contamination", slot.contamination, strict, 0, 100 );
assign( jo, "radiation", slot.radiation, strict );
assign( jo, "freezing_point", slot.freeze_point, strict );
assign( jo, "spoils_in", slot.spoils, strict, 1_hours );
assign( jo, "cooks_like", slot.cooks_like, strict );
assign( jo, "smoking_result", slot.smoking_result, strict );

for( const JsonObject &jsobj : jo.get_array( "contamination" ) ) {
slot.contamination.emplace( diseasetype_id( jsobj.get_string( "disease" ) ),
jsobj.get_int( "probability" ) );
}

if( jo.has_member( "primary_material" ) ) {
std::string mat = jo.get_string( "primary_material" );
Expand Down
6 changes: 3 additions & 3 deletions src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,15 @@ struct islot_comestible {
/** chance (odds) of becoming parasitised when eating (zero if never occurs) */
int parasites = 0;

/** probability [0, 100] to get food poisoning from this comestible */
int contamination = 0;

/**Amount of radiation you get from this comestible*/
int radiation = 0;

/** freezing point in degrees Fahrenheit, below this temperature item can freeze */
int freeze_point = temperatures::freezing;

/**List of diseases carried by this comestible and their associated probability*/
std::map<diseasetype_id, int> contamination;

//** specific heats in J/(g K) and latent heat in J/g */
float specific_heat_liquid = 4.186;
float specific_heat_solid = 2.108;
Expand Down
3 changes: 3 additions & 0 deletions src/type_id.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ using efftype_id = string_id<effect_type>;
class scent_type;
using scenttype_id = string_id<scent_type>;

class disease_type;
using diseasetype_id = string_id<disease_type>;

class emit;
using emit_id = string_id<emit>;

Expand Down

0 comments on commit 2bfe097

Please sign in to comment.