Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disease infrastructure #35843

Merged
merged 16 commits into from
Apr 4, 2020
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 @@ -1594,7 +1624,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
19 changes: 19 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,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
4 changes: 4 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "character_id.h"
#include "character_martial_arts.h"
#include "creature.h"
#include "disease.h"
Copy link
Member

@kevingranade kevingranade Apr 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to include this here, it needs to be in character.cpp and consumption.cpp

#include "game_constants.h"
#include "inventory.h"
#include "pimpl.h"
Expand Down Expand Up @@ -539,6 +540,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
8 changes: 3 additions & 5 deletions src/consumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1001,11 +1001,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 @@ -222,6 +223,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 @@ -699,6 +701,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,13 +1842,17 @@ 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" ) );
}

bool is_junkfood = false;
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 @@ -27,6 +27,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