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

feat: Effects adding other effects on removal #3695

Merged
merged 24 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
25ff9f2
JSONized "effect from removal of other effect"
Coolthulhu Nov 15, 2023
4b5f240
Remove unused
Coolthulhu Nov 16, 2023
eab0364
Rebalance adrenaline
Coolthulhu Nov 21, 2023
2769333
Body part selection for decayed effects
Coolthulhu Nov 24, 2023
c9aa23f
style(autofix.ci): automated formatting
autofix-ci[bot] Nov 24, 2023
cffa209
Docs
Coolthulhu Nov 24, 2023
ef534ee
Merge branch 'effect-adding-effect' of https://github.com/Coolthulhu/…
Coolthulhu Nov 24, 2023
2475136
style(autofix.ci): automated formatting
autofix-ci[bot] Nov 24, 2023
2dc9053
Add test file
Coolthulhu Nov 26, 2023
491236b
git Merge branch 'effect-adding-effect' of https://github.com/Coolthu…
Coolthulhu Nov 26, 2023
e0652e3
JSONized "effect from removal of other effect"
Coolthulhu Nov 15, 2023
1074880
Remove unused
Coolthulhu Nov 16, 2023
4dd78d5
Rebalance adrenaline
Coolthulhu Nov 21, 2023
6eb5eee
Body part selection for decayed effects
Coolthulhu Nov 24, 2023
75045b3
Docs
Coolthulhu Nov 24, 2023
4957b5c
style(autofix.ci): automated formatting
autofix-ci[bot] Nov 24, 2023
1810f65
Add test file
Coolthulhu Nov 26, 2023
cf1dc7c
style(autofix.ci): automated formatting
autofix-ci[bot] Nov 24, 2023
022a28f
Remove unused comparator
Coolthulhu Nov 28, 2023
a80bf8c
Merge remote-tracking branch 'upstream/main' into effect-adding-effect
scarf005 Dec 18, 2023
7b1c3ad
fix: clang-tidy fixes
scarf005 Dec 18, 2023
2153e54
Make blood filter filter adrenaline
Coolthulhu Dec 20, 2023
169b791
Merge branch 'main' into effect-adding-effect
Coolthulhu Dec 20, 2023
5ea5117
Merge branch 'effect-adding-effect' of https://github.com/Coolthulhu/…
Coolthulhu Dec 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions data/json/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -1268,25 +1268,34 @@
{
"type": "effect_type",
"id": "adrenaline",
"name": [ "Adrenaline Comedown", "Adrenaline Rush" ],
"desc": [ "You feel completely drained.", "You feel the rush of adrenaline in your body!" ],
"name": [ "Adrenaline Rush" ],
"desc": [ "You feel the rush of adrenaline in your body!" ],
"apply_message": "You feel a surge of adrenaline!",
"rating": "good",
"removes_effects": [ "winded" ],
"max_duration": "5 m",
"base_mods": { "speed_mod": [ 20 ], "str_mod": [ 2 ], "dex_mod": [ 2 ], "int_mod": [ -8 ], "per_mod": [ 1 ], "stamina_min": [ 2 ] },
"blood_analysis_description": "Adrenaline Spike",
"effects_on_remove": [ { "allow_on_remove": true, "effect_type": "adrenaline_comedown", "duration": "1 m" } ]
},
{
"type": "effect_type",
"id": "adrenaline_comedown",
"name": [ "Adrenaline Comedown" ],
"desc": [ "You feel completely drained." ],
"apply_message": "Your adrenaline rush wears off. You feel AWFUL!",
"rating": "bad",
"decay_messages": [ [ "Your adrenaline rush wears off. You feel AWFUL!", "bad" ] ],
"miss_messages": [ [ "Your comedown throws you off.", 1 ] ],
"max_intensity": 2,
"int_dur_factor": "150 s",
"removes_effects": [ "winded" ],
"max_duration": "5 m",
"base_mods": {
"speed_mod": [ -10 ],
"str_mod": [ -2 ],
"dex_mod": [ -2 ],
"int_mod": [ -1 ],
"per_mod": [ -1 ],
"stamina_min": [ -2 ]
},
"scaling_mods": { "speed_mod": [ 30 ], "str_mod": [ 4 ], "dex_mod": [ 4 ], "int_mod": [ -7 ], "per_mod": [ 2 ], "stamina_min": [ 4 ] },
"blood_analysis_description": "Adrenaline Spike"
}
},
{
"type": "effect_type",
Expand Down
20 changes: 20 additions & 0 deletions data/mods/TEST_DATA/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,25 @@
"id": "test_high",
"morale": "morale_test",
"base_mods": { "morale": [ 25 ] }
},
{
"type": "effect_type",
"id": "test_juggling_l1",
"effects_on_remove": [ { "effect_type": "test_juggling_r1", "duration": "0 s", "body_part": "hand_r" } ]
},
{
"type": "effect_type",
"id": "test_juggling_r1",
"effects_on_remove": [ { "effect_type": "test_juggling_r2", "duration": "0 s", "body_part": "hand_r" } ]
},
{
"type": "effect_type",
"id": "test_juggling_r2",
"effects_on_remove": [ { "effect_type": "test_juggling_l2", "duration": "0 s", "body_part": "hand_l" } ]
},
{
"type": "effect_type",
"id": "test_juggling_l2",
"effects_on_remove": [ { "effect_type": "test_juggling_l1", "duration": "0 s" } ]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,31 @@ effect can hurt the player.
Type of morale effect provided. Mandatory if there is a morale effect, must not be specified
otherwise.

### Other effects on removal

```json
"effects_on_remove": [
{
"intensity_requirement": 0, - Defaults to 0
"effect_type": "cold", - (Mandatory) Effect that will be applied
"allow_on_decay": false, - Defaults to true
"allow_on_remove" true, - Defaults to false
"intensity": 5, - Defaults to 0
"inherit_intensity": false, - Defaults to false
"duration": "10 s", - Defaults to 0
"inherit_duration": true, - Defaults to true
"body_part": "hand_r, - Defaults to null
"inherit_body_part": false - Defaults to true
}
]
```

"intensity_requirement" will prevent adding the new effect if current effect has lower intensity.
"allow_on_decay" enables adding the effect if parent decayed (was removed due to 0 duration).
"allow_on_remove" enables adding the effect if parent was removed before 0 duration.
"inherit_duration", "inherit_intensity" and "inherit_body_part" cause the relevant variable to be
copied from parent effect.

### Effect effects

```json
Expand Down Expand Up @@ -491,6 +516,15 @@ Valid arguments:
"thirst_chance_bot"
"thirst_tick" - Defaults to every tick

"sleepdebt_amount" - Amount of sleep debt it can give/take.
"sleepdebt_min" - Minimal amount of sleep, certain effect will give/take
"sleepdebt_max" - if 0 or missing value will be exactly "sleepdebt_min"
"sleepdebt_min_val" - Defaults to 0, which means uncapped
"sleepdebt_max_val" - Defaults to 0, which means uncapped
"sleepdebt_chance" - Chance to give more sleep
"sleepdebt_chance_bot" - Min chance, unsure, needs auditing
"sleepdebt_tick" - Defaults to every tick

"fatigue_amount" - Amount of fatigue it can give/take. After certain amount character will need to sleep.
"fatigue_min" - Minimal amount of fatigue, certain effect will give/take
"fatigue_max" - if 0 or missing value will be exactly "fatigue_min"
Expand Down
3 changes: 1 addition & 2 deletions src/bionics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,6 @@ bool Character::activate_bionic( bionic &bio, bool eff_only, bool *close_bionics
remove_effect( eff );
}
// Purging the substance won't remove the fatigue it caused
force_comedown( get_effect( effect_adrenaline ) );
force_comedown( get_effect( effect_meth ) );
set_painkiller( 0 );
set_stim( 0 );
Expand Down Expand Up @@ -826,7 +825,7 @@ bool Character::activate_bionic( bionic &bio, bool eff_only, bool *close_bionics
return false;
} else {
add_msg_activate();
add_effect( effect_adrenaline, 20_minutes );
add_effect( effect_adrenaline, 3_minutes );
}
} else if( bio.id == bio_emp ) {
if( const std::optional<tripoint> pnt = choose_adjacent( _( "Create an EMP where?" ) ) ) {
Expand Down
10 changes: 3 additions & 7 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ void Character::set_pain( int npain )

int Character::get_perceived_pain() const
{
if( get_effect_int( effect_adrenaline ) > 1 ) {
if( has_effect( effect_adrenaline ) ) {
return 0;
}

Expand Down Expand Up @@ -5103,10 +5103,6 @@ void Character::check_needs_extremes()
}
g->events().send<event_type::dies_from_drug_overdose>( getID(), effect_jetinjector );
set_part_hp_cur( bodypart_id( "torso" ), 0 );
} else if( get_effect_dur( effect_adrenaline ) > 50_minutes ) {
add_msg_if_player( m_bad, _( "Your heart spasms and stops." ) );
g->events().send<event_type::dies_from_drug_overdose>( getID(), effect_adrenaline );
set_part_hp_cur( bodypart_id( "torso" ), 0 );
} else if( get_effect_int( effect_drunk ) > 4 ) {
add_msg_if_player( m_bad, _( "Your breathing slows down to a stop." ) );
g->events().send<event_type::dies_from_drug_overdose>( getID(), effect_drunk );
Expand Down Expand Up @@ -8782,7 +8778,7 @@ void Character::on_hurt( Creature *source, bool disturb /*= true*/ )
if( has_trait( trait_ADRENALINE ) && !has_effect( effect_adrenaline ) &&
( get_part_hp_cur( bodypart_id( "head" ) ) < 25 ||
get_part_hp_cur( bodypart_id( "torso" ) ) < 15 ) ) {
add_effect( effect_adrenaline, 20_minutes );
add_effect( effect_adrenaline, 3_minutes );
}

if( disturb ) {
Expand Down Expand Up @@ -9973,7 +9969,7 @@ void Character::on_item_takeoff( const item &it )
void Character::on_effect_int_change( const efftype_id &effect_type, int intensity,
const bodypart_str_id &bp )
{
// Adrenaline can reduce perceived pain (or increase it when you enter comedown).
// Adrenaline can reduce perceived pain (or increase it when it times out).
// See @ref get_perceived_pain()
if( effect_type == effect_adrenaline ) {
// Note that calling this does no harm if it wasn't changed.
Expand Down
63 changes: 51 additions & 12 deletions src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1297,31 +1297,44 @@ int Creature::get_effect_int( const efftype_id &eff_id, body_part bp ) const

return 0;
}

struct removed_effect {
public:
removed_effect( efftype_id type, bodypart_str_id bp, bool is_decayed ) :
type( type ), bp( bp ), is_decayed( is_decayed )
{}
efftype_id type;
bodypart_str_id bp;
bool is_decayed;
};

void Creature::process_effects()
{
process_effects_internal();

// id's and body_part's of all effects to be removed. If we ever get player or
// monster specific removals these will need to be moved down to that level and then
// passed in to this function.
std::vector<std::pair<efftype_id, bodypart_str_id>> to_remove;
std::vector<removed_effect> to_remove;

std::vector<effect> to_add;

// Decay/removal of effects
for( auto &elem : *effects ) {
for( auto &_it : elem.second ) {
if( _it.second.is_removed() ) {
to_remove.emplace_back( elem.first, _it.first );
to_remove.emplace_back( elem.first, _it.first, false );
continue;
}
// Add any effects that others remove to the removal list
for( const efftype_id &removed_effect : _it.second.get_removes_effects() ) {
to_remove.emplace_back( removed_effect, bodypart_str_id::NULL_ID() );
to_remove.emplace_back( removed_effect, bodypart_str_id::NULL_ID(), false );
}
effect &e = _it.second;
const int prev_int = e.get_intensity();
// Run decay effects, marking effects for removal as necessary.
if( e.decay( calendar::turn, is_player() ) ) {
to_remove.emplace_back( elem.first, _it.first );
to_remove.emplace_back( elem.first, _it.first, true );
}

if( e.get_intensity() != prev_int && e.get_duration() > 0_turns ) {
Expand All @@ -1331,21 +1344,47 @@ void Creature::process_effects()
}

// Run the on-remove effects
for( const std::pair<efftype_id, bodypart_str_id> &r : to_remove ) {
remove_effect( r.first, r.second );
for( const removed_effect &r : to_remove ) {
const auto &add_after = r.type->get_effects_on_remove();
if( !add_after.empty() ) {
bool found = false;
// Copypasted from get_effect, but without check for `removed` flag
auto got_outer = effects->find( r.type );
if( got_outer != effects->end() ) {
auto got_inner = got_outer->second.find( convert_bp( r.bp->token ) );
if( got_inner != got_outer->second.end() ) {
const auto &parent = got_inner->second;
const auto &decay_effects = r.is_decayed ?
parent.create_decay_effects() :
parent.create_removal_effects();
to_add.insert( to_add.end(), decay_effects.begin(), decay_effects.end() );
found = true;
}
}

if( !found ) {
debugmsg( "Couldn't find effect to remove %s", r.type.str() );
}
}

remove_effect( r.type, r.bp );
}
// Actually remove effects. This should be the last thing done in process_effects().
for( const std::pair<efftype_id, bodypart_str_id> &r : to_remove ) {
if( !r.second ) {
effects->erase( r.first );
for( const removed_effect &r : to_remove ) {
if( !r.bp ) {
effects->erase( r.type );
} else {
( *effects )[r.first].erase( r.second );
( *effects )[r.type].erase( r.bp );
// If there are no more effects of a given type remove the type map
if( ( *effects )[r.first].empty() ) {
effects->erase( r.first );
if( ( *effects )[r.type].empty() ) {
effects->erase( r.type );
}
}
}

for( const effect &eff : to_add ) {
add_effect( eff );
}
}

bool Creature::resists_effect( const effect &e ) const
Expand Down
75 changes: 75 additions & 0 deletions src/effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,14 @@
}
}

if( jo.has_array( "effects_on_remove" ) ) {
JsonArray jarr = jo.get_array( "effects_on_remove" );
for( JsonObject jo_decay : jarr ) {
new_etype.effects_on_remove.emplace_back();
new_etype.effects_on_remove.back().load_decay( jo_decay );
}
}

effect_types[new_etype.id] = new_etype;
}

Expand Down Expand Up @@ -1504,3 +1512,70 @@
}
return "";
}

void caused_effect::load_decay( const JsonObject &jo )
{
assign( jo, "allow_on_decay", allow_on_decay );
assign( jo, "allow_on_remove", allow_on_remove );
load( jo );
}

void caused_effect::load( const JsonObject &jo )
{
assign( jo, "effect_type", type );
assign( jo, "intensity_requirement", intensity_requirement );

if( assign( jo, "duration", duration ) ) {
// In case of copy-from
inherit_duration = false;
}
assign( jo, "inherit_duration", inherit_duration );
if( jo.has_member( "duration" ) && jo.has_member( "inherit_duration" ) ) {
jo.throw_error( "\"duration\" and \"inherit_duration\" can't both be set at the same time." );
}

if( assign( jo, "intensity", intensity ) ) {
inherit_intensity = false;
}
assign( jo, "inherit_intensity", inherit_intensity );
if( jo.has_member( "intensity" ) && jo.has_member( "inherit_intensity" ) ) {
jo.throw_error( "\"intensity\" and \"inherit_intensity\" can't both be set at the same time." );
}

if( assign( jo, "body_part", bp ) ) {

Check failure on line 1545 in src/effect.cpp

View workflow job for this annotation

GitHub Actions / build

escaped string literal can be written as a raw string literal [modernize-raw-string-literal,-warnings-as-errors]
inherit_body_part = false;
}
assign( jo, "inherit_body_part", inherit_body_part );
if( jo.has_member( "intensity" ) && jo.has_member( "inherit_intensity" ) ) {
jo.throw_error( "\"body_part\" and \"inherit_body_part\" can't both be set at the same time." );
}
}

Check failure on line 1553 in src/effect.cpp

View workflow job for this annotation

GitHub Actions / build

escaped string literal can be written as a raw string literal [modernize-raw-string-literal,-warnings-as-errors]
std::vector<effect> effect::create_decay_effects() const
{
return create_child_effects( true );
}

std::vector<effect> effect::create_removal_effects() const
{
return create_child_effects( false );

Check failure on line 1561 in src/effect.cpp

View workflow job for this annotation

GitHub Actions / build

escaped string literal can be written as a raw string literal [modernize-raw-string-literal,-warnings-as-errors]
}

std::vector<effect> effect::create_child_effects( bool decay ) const
{
std::vector<effect> ret;
for( const auto &new_effect : eff_type->effects_on_remove ) {
if( this->intensity < new_effect.intensity_requirement ||
( decay && !new_effect.allow_on_decay ) ||
( !decay && !new_effect.allow_on_remove ) ) {
continue;
}
const effect_type *new_effect_type = &*new_effect.type;
time_duration dur = new_effect.inherit_duration ? this->duration : new_effect.duration;
int intensity = new_effect.inherit_intensity ? this->intensity : new_effect.intensity;
bodypart_str_id bp = new_effect.inherit_body_part ? convert_bp( this->bp ) : new_effect.bp;
effect e = effect( new_effect_type, dur, bp, intensity, calendar::turn );
ret.emplace_back( e );
}
return ret;
}
Loading
Loading