Skip to content

Commit

Permalink
Merge pull request CleverRaven#47291 from anothersimulacrum/marv
Browse files Browse the repository at this point in the history
Expand effects infrastructure to vitamins and death
  • Loading branch information
ZhilkinSerg authored Jul 3, 2021
2 parents b7a789f + d1e83af commit 8b615f0
Show file tree
Hide file tree
Showing 16 changed files with 473 additions and 6 deletions.
18 changes: 18 additions & 0 deletions data/mods/TEST_DATA/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@
"max_intensity": 3,
"scaling_mods": { "int_mod": [ -1 ], "per_mod": [ -2 ], "speed_mod": [ -5 ] }
},
{
"type": "effect_type",
"id": "test_vitamineff",
"name": [ "Vitamins!" ],
"desc": [ "" ],
"vitamins": [
{ "vitamin": "test_vitv", "rate": [ [ 2, 2 ] ], "tick": [ "5 m" ] },
{ "vitamin": "test_vitx", "absorb_mult": [ 0.5 ] }
]
},
{
"type": "effect_type",
"id": "test_fatalism",
"name": [ "Placebo", "Plague", "Fatalism" ],
"desc": [ "" ],
"max_intensity": 3,
"chance_kill": [ [ 0, 1 ], [ 1, 10 ], [ 1, 1 ] ]
},
{
"type": "effect_type",
"id": "max_effected",
Expand Down
18 changes: 18 additions & 0 deletions data/mods/TEST_DATA/items.json
Original file line number Diff line number Diff line change
Expand Up @@ -1786,5 +1786,23 @@
],
"armor_data": { "covers": [ "torso" ], "coverage": 10, "encumbrance": 4, "material_thickness": 2 },
"flags": [ "BELTED" ]
},
{
"type": "COMESTIBLE",
"id": "test_vitfood",
"name": { "str": "Vitamin Food" },
"weight": "250 g",
"color": "red",
"spoils_in": "1 day",
"comestible_type": "FOOD",
"symbol": "O",
"calories": 250,
"description": "Delicious vitamin food.",
"price": 1600,
"price_postapoc": 200,
"material": [ "flesh" ],
"volume": "250 ml",
"vitamins": [ [ "test_vitx", 200 ] ],
"fun": -10
}
]
20 changes: 20 additions & 0 deletions data/mods/TEST_DATA/vitamins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"id": "test_vitv",
"type": "vitamin",
"vit_type": "vitamin",
"name": { "str": "Vitamin V" },
"min": -4000,
"max": 4000,
"rate": "20 days"
},
{
"id": "test_vitx",
"type": "vitamin",
"vit_type": "vitamin",
"name": { "str": "Vitamin X" },
"min": -4000,
"max": 4000,
"rate": "20 days"
}
]
41 changes: 41 additions & 0 deletions doc/EFFECTS_JSON.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,47 @@ and hurt effects triggering. "harmful_cough" means that the coughs caused by thi
"EFFECT_INVISIBLE" Character affected by an effect with this flag are invisible.
"EFFECT_IMPEDING" Character affected by an effect with this flag can't move until they break free from the effect. Breaking free requires a strength check: `x_in_y( get_str(), 6 * get_effect_int( eff_id )`

### Vitamin Mods

```json
"vitamins": [
{
"vitamin": "foo",
"rate": [ [ 1, 2 ] ],
"resist_rate": [ [ 0, 1 ] ],
"absorb_mult": [ 0.5 ],
"resist_absorb_mult": [ 0.0 ],
"tick": [ "2 m" ],
"resist_tick": [ "1 s" ],
}
],
```
- `vitamin` corresponds to the vitamin id that the following effects will be applied to
- `rate` A randomly generated number between the bounds specified (here, 1 and 2) will added to the vitamin counter of a character with this effect every `tick`.
- `absorb_mult` metabolically absorbed vitamins will be multiplied by this quantity before being added to the character.

The `resist_` variants of the above keys are the values chosen when the character is resistant to this effect.

All of these members are arrays, with each successive entry corresponding to the intensity level of an effect. If there are more intensity levels to the effect than entries in the array, the last entry in the array will be used.

As defined, this will cause non-resistant characters to gain between 1 and 2 of the vitamin foo every 2 minutes, and half their absorbtion rate of it, and resistant character to gain between 0 and 1 of this vitamin every second, and not absorb any of it from their food.

### Death

```json
"chance_kill": [ [ 1, 25 ] ],
"chance_kill_resist": [ [ 1, 250 ] ],
"death_msg": "You died.",
"death_event": "throws_up"
```

- `chance_kill` A first value in second value chance to kill the creature with this effect each turn.
- `chance_kill_resist` A first value in second value chance to kill the creature with this effect each turn, if the creature resists this effect.
- `death_msg` A message added to the log when the player dies from this effect.
- `death_event` An event that is sent when the player dies from this effect.

For `chance_kill` and `chance_kill_resist`, it accepts an array of arrays in the format described. Each entry in the array will be applied for a successive intensity level of the field. If the intensity level of the field is greater than the number of entries in the array, the last entry will be used.

### Effect effects
```C++
"base_mods" : {
Expand Down
9 changes: 9 additions & 0 deletions lang/extract_json_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,15 @@ def extract_effect_type(item):
comment = "Speed name of effect(s) '{}'.".format(', '.join(name))
writestr(outfile, item.get("speed_name"), comment=comment)

# death_msg
if "death_msg" in item:
if not name:
writestr(outfile, item.get("death_msg"))
else:
comment = "Death message of effect(s) '{}'."
comment.format(', '.json(name))
writestr(outfile, item.get("death_msg"), comment=comment)

# apply and remove memorial messages.
msg = item.get("apply_memorial_log")
if not name:
Expand Down
18 changes: 14 additions & 4 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,8 @@ float Character::stability_roll() const
bool Character::is_dead_state() const
{
return get_part_hp_cur( body_part_head ) <= 0 ||
get_part_hp_cur( body_part_torso ) <= 0;
get_part_hp_cur( body_part_torso ) <= 0 ||
is_dead;
}

void Character::on_dodge( Creature *source, float difficulty )
Expand Down Expand Up @@ -4274,6 +4275,7 @@ void Character::normalize()
void Character::die( Creature *nkiller )
{
g->set_critter_died();
is_dead = true;
set_killer( nkiller );
set_time_died( calendar::turn );
if( has_effect( effect_lightsnare ) ) {
Expand Down Expand Up @@ -5762,7 +5764,7 @@ void Character::update_stomach( const time_point &from, const time_point &to )
guts.ingest( digested_to_guts );

mod_stored_kcal( digested_to_body.nutr.kcal() );
vitamins_mod( digested_to_body.nutr.vitamins, false );
vitamins_mod( effect_vitamin_mod( digested_to_body.nutr.vitamins ), false );
log_activity_level( activity_history.average_activity() );

if( !foodless && rates.hunger > 0.0f ) {
Expand Down Expand Up @@ -12167,6 +12169,14 @@ void Character::process_one_effect( effect &it, bool is_new )
}
}

// Handle vitamins
for( const vitamin_applied_effect &vit : it.vit_effects( reduced ) ) {
if( vit.tick && vit.rate && calendar::once_every( *vit.tick ) ) {
const int mod = rng( vit.rate->first, vit.rate->second );
vitamin_mod( vit.vitamin, mod, false );
}
}

// Handle health mod
val = get_effect( "H_MOD", reduced );
if( val != 0 ) {
Expand Down Expand Up @@ -12392,8 +12402,8 @@ void Character::process_effects()
}

//Human only effects
for( auto &elem : *effects ) {
for( auto &_effect_it : elem.second ) {
for( std::pair<const efftype_id, std::map<bodypart_id, effect>> &elem : *effects ) {
for( std::pair<const bodypart_id, effect> &_effect_it : elem.second ) {
process_one_effect( _effect_it.second, false );
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -1960,6 +1960,8 @@ class Character : public Creature, public visitable
std::string name;
bool male = false;

bool is_dead = false;

std::list<item> worn;
bool nv_cached = false;
// Means player sit inside vehicle on the tile he is now
Expand Down Expand Up @@ -2350,6 +2352,10 @@ class Character : public Creature, public visitable
void vitamins_mod( const std::map<vitamin_id, int> &, bool capped = true );
/** Get vitamin usage rate (minutes per unit) accounting for bionics, mutations and effects */
time_duration vitamin_rate( const vitamin_id &vit ) const;
/** Modify vitamin intake (e.g. due to effects) */
std::map<vitamin_id, int> effect_vitamin_mod( const std::map<vitamin_id, int> & );
/** Remove all vitamins */
void clear_vitamins();

/** Handles the nutrition value for a comestible **/
int nutrition_for( const item &comest ) const;
Expand Down
35 changes: 35 additions & 0 deletions src/consumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "character.h"
#include "color.h"
#include "debug.h"
#include "effect.h"
#include "enums.h"
#include "event.h"
#include "event_bus.h"
Expand Down Expand Up @@ -486,6 +487,40 @@ time_duration Character::vitamin_rate( const vitamin_id &vit ) const
return res;
}

void Character::clear_vitamins()
{
vitamin_levels.clear();
}

std::map<vitamin_id, int> Character::effect_vitamin_mod( const std::map<vitamin_id, int> &vits )
{
std::vector<std::pair<vitamin_id, float>> mods;
// Yuck!
// Construct mods, for easy iteration over to modify vitamins
for( const std::pair<const efftype_id, std::map<bodypart_id, effect>> &elem : *effects ) {
for( const std::pair<const bodypart_id, effect> &veffect : elem.second ) {
const bool reduced = resists_effect( veffect.second );
for( const vitamin_applied_effect &rate_mod : veffect.second.vit_effects( reduced ) ) {
if( !rate_mod.absorb_mult ) {
continue;
}
mods.emplace_back( rate_mod.vitamin, *rate_mod.absorb_mult );
}
}
}

std::map<vitamin_id, int> ret = vits;
for( std::pair<const vitamin_id, int> &value : ret ) {
for( const std::pair<vitamin_id, float> &mod : mods ) {
if( value.first == mod.first ) {
value.second *= mod.second;
}
}
}

return ret;
}

int Character::vitamin_mod( const vitamin_id &vit, int qty, bool capped )
{
if( !vit.is_valid() ) {
Expand Down
16 changes: 15 additions & 1 deletion src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,20 @@ void Creature::process_effects()
if( e.get_intensity() != prev_int && e.get_duration() > 0_turns ) {
on_effect_int_change( e.get_id(), e.get_intensity(), e.get_bp() );
}

const bool reduced = resists_effect( e );
if( e.kill_roll( reduced ) ) {
add_msg_if_player( m_bad, e.get_death_message() );
if( is_player() ) {
std::map<std::string, cata_variant> event_data;
std::pair<std::string, cata_variant> data_obj( "character",
cata_variant::make<cata_variant_type::character_id>( as_player()->getID() ) );
event_data.insert( data_obj );
cata::event sent( e.death_event(), calendar::turn, std::move( event_data ) );
get_event_bus().send( sent );
}
die( e.get_source().resolve_creature() );
}
}
}

Expand All @@ -1446,7 +1460,7 @@ void Creature::process_effects()
}
}

bool Creature::resists_effect( const effect &e )
bool Creature::resists_effect( const effect &e ) const
{
for( const efftype_id &i : e.get_resist_effects() ) {
if( has_effect( i ) ) {
Expand Down
2 changes: 1 addition & 1 deletion src/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ class Creature : public location, public viewer
int get_effect_int( const efftype_id &eff_id,
const bodypart_id &bp = bodypart_str_id::NULL_ID() ) const;
/** Returns true if the creature resists an effect */
bool resists_effect( const effect &e );
bool resists_effect( const effect &e ) const;

// Methods for setting/getting misc key/value pairs.
void set_value( const std::string &key, const std::string &value );
Expand Down
Loading

0 comments on commit 8b615f0

Please sign in to comment.