From d8aa6d9696fbce4339edfccf96df85ae438a8fad Mon Sep 17 00:00:00 2001 From: Coolthulhu Date: Thu, 21 Sep 2023 15:58:59 +0200 Subject: [PATCH] Simplify broken limb mending (#3054) * Rework broken limbs and mending * Redesign mending_modifier: 1.0 is same as splint * Update comments * Update disabled effect description --------- Co-authored-by: Olanti --- data/json/effects.json | 14 +--- data/json/mutations/mutations.json | 33 ++++----- data/json/obsoletion/effects.json | 4 + src/bionics.cpp | 24 ------ src/character.cpp | 66 +++++++++-------- src/character.h | 6 +- src/creature.cpp | 4 +- src/iexamine.cpp | 15 ++-- src/iuse_actor.cpp | 12 --- src/mutation.h | 6 +- src/mutation_data.cpp | 2 +- src/npc.cpp | 3 +- src/panels.cpp | 45 ++++++----- src/player_hardcoded_effects.cpp | 7 +- src/suffer.cpp | 115 ----------------------------- tests/player_helpers.cpp | 3 + 16 files changed, 96 insertions(+), 263 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index 394bfb1a915..969cafe5046 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -1802,23 +1802,11 @@ "max_duration": "1 s", "rating": "bad" }, - { - "type": "effect_type", - "id": "mending", - "name": [ "Started recovery", "Recovering", "Mostly recovered" ], - "desc": [ "This damaged limb is slowly regaining its functions." ], - "//": "Duration is 10 days, but the actual time taken is probabilistic.", - "max_duration": "10 d", - "int_dur_factor": "60 h", - "max_intensity": 3, - "rating": "good", - "permanent": true - }, { "type": "effect_type", "id": "disabled", "name": [ { "ctxt": "physically", "str": "Disabled" } ], - "desc": [ "This limb is damaged beyond use and may require a splint to recover." ], + "desc": [ "This limb is damaged beyond use and must fully heal to recover. Using a splint may speed up the process." ], "//": "This sounds weird. We need tag or something", "apply_message": "Your limb breaks!", "remove_message": "The broken limb has mended.", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 1fe217b8559..333a471d30e 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -155,7 +155,7 @@ "category": [ "MEDICAL" ], "healing_awake": 0.2, "healing_resting": 0.5, - "mending_modifier": 2.0 + "mending_modifier": 0.5 }, { "type": "mutation", @@ -722,37 +722,34 @@ "id": "SLOWHEALER", "name": { "str": "Slow Healer" }, "points": -2, - "description": "Your wounds heal a little slower than most. Your HP whilst asleep, as well as your broken limbs, heal at 75% of the regular rate.", + "description": "Your wounds heal a little slower than most. Your HP whilst asleep heals at 75% of the regular rate.", "starting_trait": true, "types": [ "HEALING" ], - "healing_resting": -0.25, - "mending_modifier": 0.5 + "healing_resting": -0.25 }, { "type": "mutation", "id": "SLOWHEALER2", "name": { "str": "Poor Healer" }, "points": -4, - "description": "Your health recovery is severely impaired. Your HP whilst asleep, as well as your broken limbs, heal at 33% of the regular rate.", + "description": "Your health recovery is severely impaired. Your HP whilst asleep heals at 33% of the regular rate.", "starting_trait": true, "valid": false, "purifiable": false, "types": [ "HEALING" ], - "healing_resting": -0.66, - "mending_modifier": 0.33 + "healing_resting": -0.66 }, { "type": "mutation", "id": "SLOWHEALER3", "name": { "str": "Imperceptive Healer" }, "points": -8, - "description": "Wounds are incredibly dangerous to you, as they barely heal at all. Your HP whilst asleep, as well as your broken limbs, heal at 10% of the regular rate.", + "description": "Wounds are incredibly dangerous to you, as they barely heal at all. Your HP whilst asleep heals at 10% of the regular rate.", "starting_trait": true, "valid": false, "purifiable": false, "types": [ "HEALING" ], - "healing_resting": -0.9, - "mending_modifier": 0.1 + "healing_resting": -0.9 }, { "type": "mutation", @@ -1377,34 +1374,34 @@ "category": [ "PLANT", "LIZARD" ], "healing_awake": 0.66, "healing_resting": 0.5, - "mending_modifier": 4 + "mending_modifier": 0.5 }, { "type": "mutation", "id": "REGEN", "name": { "str": "Regeneration" }, "points": 6, - "description": "Your flesh regenerates from wounds incredibly quickly. You heal 150% faster whilst asleep and 200% faster whilst awake. Your broken limbs also heal 16 times faster than usual.", + "description": "Your flesh regenerates from wounds incredibly quickly. You heal 150% faster whilst asleep and 200% faster whilst awake. You do not require splints to heal broken limbs.", "types": [ "HEALING" ], "prereqs": [ "FASTHEALER2" ], "category": [ "SLIME", "TROGLOBITE" ], "healing_awake": 2, "healing_resting": 1.5, - "mending_modifier": 16 + "mending_modifier": 1 }, { "type": "mutation", "id": "REGEN_LIZ", "name": { "str": "Reptilian Healing" }, - "points": 5, + "//": "Not worth a point, barely more than flavor.", + "points": 0, "valid": false, "purifiable": false, - "description": "Your broken limbs mend themselves without significant difficulty. You do not require splints and broken limbs heal 20 times faster than usual.", - "cancels": [ "ROT1", "ROT2", "ROT3" ], - "prereqs": [ "FASTHEALER2" ], + "description": "Your broken limbs mend themselves without significant difficulty. You do not require splints.", + "prereqs": [ "FASTHEALER" ], "threshreq": [ "THRESH_LIZARD" ], "category": [ "LIZARD" ], - "mending_modifier": 20 + "mending_modifier": 1 }, { "type": "mutation", diff --git a/data/json/obsoletion/effects.json b/data/json/obsoletion/effects.json index 38c218728cd..bb666566f6b 100644 --- a/data/json/obsoletion/effects.json +++ b/data/json/obsoletion/effects.json @@ -60,5 +60,9 @@ { "type": "effect_type", "id": "took_anticonvulsant_visible" + }, + { + "type": "effect_type", + "id": "mending" } ] diff --git a/src/bionics.cpp b/src/bionics.cpp index 78827e780d7..bb2b2bd73e7 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -101,7 +101,6 @@ static const efftype_id effect_fungus( "fungus" ); static const efftype_id effect_hallu( "hallu" ); static const efftype_id effect_heating_bionic( "heating_bionic" ); static const efftype_id effect_iodine( "iodine" ); -static const efftype_id effect_mending( "mending" ); static const efftype_id effect_meth( "meth" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_operating( "operating" ); @@ -190,7 +189,6 @@ static const trait_id trait_PROF_AUTODOC( "PROF_AUTODOC" ); static const trait_id trait_PROF_MED( "PROF_MED" ); static const trait_id trait_PYROMANIA( "PYROMANIA" ); -static const trait_id trait_REGEN_LIZ( "REGEN_LIZ" ); static const trait_id trait_THRESH_MEDICAL( "THRESH_MEDICAL" ); static const flag_id flag_BIONIC_GUN( "BIONIC_GUN" ); @@ -1675,18 +1673,6 @@ void Character::process_bionic( bionic &bio ) } if( calendar::once_every( 2_minutes ) ) { std::vector damaged_hp_parts; - std::vector mending_list; - - for( const bodypart_id &bp : get_all_body_parts( true ) ) { - const int hp_cur = get_part_hp_cur( bp ); - if( !is_limb_broken( bp ) && hp_cur < get_part_hp_max( bp ) ) { - damaged_hp_parts.push_back( bp ); - } else if( has_effect( effect_mending, bp.id() ) && - ( has_trait( trait_REGEN_LIZ ) || worn_with_flag( flag_SPLINT, bp ) ) ) { - effect *e = &get_effect( effect_mending, bp->token ); - mending_list.push_back( e ); - } - } if( !damaged_hp_parts.empty() ) { // Essential parts are considered 10 HP lower than non-essential parts for the purpose of determining priority. // I'd use the essential_value, but it's tied up in the heal_actor class of iuse_actor. @@ -1703,16 +1689,6 @@ void Character::process_bionic( bionic &bio ) mod_stored_kcal( -bio.info().kcal_trigger ); } } - if( !mending_list.empty() ) { - for( effect *e : mending_list ) { - if( !can_use_bionic() ) { - return; - } - e->mod_duration( e->get_max_duration() / 100 ); - mod_power_level( -bio.info().power_trigger ); - mod_stored_kcal( -bio.info().kcal_trigger ); - } - } } } } else if( bio.id == bio_painkiller ) { diff --git a/src/character.cpp b/src/character.cpp index 6cb10461791..761e0785f44 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -140,6 +140,7 @@ static const efftype_id effect_cough_suppress( "cough_suppress" ); static const efftype_id effect_crushed( "crushed" ); static const efftype_id effect_darkness( "darkness" ); static const efftype_id effect_deaf( "deaf" ); +static const efftype_id effect_disabled( "disabled" ); static const efftype_id effect_disinfected( "disinfected" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_drunk( "drunk" ); @@ -167,7 +168,6 @@ static const efftype_id effect_lying_down( "lying_down" ); static const efftype_id effect_melatonin_supplements( "melatonin" ); static const efftype_id effect_meth( "meth" ); static const efftype_id effect_masked_scent( "masked_scent" ); -static const efftype_id effect_mending( "mending" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_nausea( "nausea" ); static const efftype_id effect_no_sight( "no_sight" ); @@ -287,7 +287,6 @@ static const trait_id trait_INFRARED( "INFRARED" ); static const trait_id trait_LEG_TENT_BRACE( "LEG_TENT_BRACE" ); static const trait_id trait_LIGHT_BONES( "LIGHT_BONES" ); static const trait_id trait_LIZ_IR( "LIZ_IR" ); -static const trait_id trait_REGEN_LIZ( "REGEN_LIZ" ); static const trait_id trait_M_DEPENDENT( "M_DEPENDENT" ); static const trait_id trait_M_IMMUNE( "M_IMMUNE" ); static const trait_id trait_M_SKIN2( "M_SKIN2" ); @@ -1257,14 +1256,15 @@ int Character::get_working_leg_count() const bool Character::is_limb_disabled( const bodypart_id &limb ) const { - return get_part_hp_cur( limb ) <= get_part_hp_max( limb ) * .125; + return is_limb_broken( limb ) || + ( get_part_hp_cur( limb ) <= get_part_hp_max( limb ) * 0.125 ); } // this is the source of truth on if a limb is broken so all code to determine // if a limb is broken should point here to make any future changes to breaking easier bool Character::is_limb_broken( const bodypart_id &limb ) const { - return get_part_hp_cur( limb ) <= 0; + return has_effect( effect_disabled, limb.id() ); } bool Character::can_run() @@ -4580,8 +4580,17 @@ void Character::regen( int rate_multiplier ) float rest = rest_quality(); float heal_rate = healing_rate( rest ) * to_turns( 5_minutes ); + const float broken_regen_mod = clamp( mutation_value( "mending_modifier" ), 0.25f, 1.0f ); if( heal_rate > 0.0f ) { - healall( roll_remainder( rate_multiplier * heal_rate ) ); + const int base_heal = roll_remainder( rate_multiplier * heal_rate ); + const int broken_heal = roll_remainder( base_heal * broken_regen_mod ); + + for( const bodypart_id &bp : get_all_body_parts() ) { + const bool is_broken = is_limb_broken( bp ) && + !worn_with_flag( flag_SPLINT, bp ); + heal( bp, is_broken ? broken_heal : base_heal ); + mod_part_healed_total( bp, is_broken ? broken_heal : base_heal ); + } } else if( heal_rate < 0.0f ) { int rot_rate = roll_remainder( rate_multiplier * -heal_rate ); // Has to be in loop because some effects depend on rounding @@ -4595,9 +4604,13 @@ void Character::regen( int rate_multiplier ) const bodypart_id &bp = convert_bp( hp_to_bp( static_cast( i ) ) ).id(); float healing = healing_rate_medicine( rest, bp ) * to_turns( 5_minutes ); - int healing_apply = roll_remainder( healing ); + const bool is_broken = is_limb_broken( bp ) && + !worn_with_flag( flag_SPLINT, bp ); + const int healing_apply = roll_remainder( is_broken ? healing *broken_regen_mod : healing ); + healed_bp( i, healing_apply ); heal( bp, healing_apply ); + if( damage_bandaged[i] > 0 ) { damage_bandaged[i] -= healing_apply; if( damage_bandaged[i] <= 0 ) { @@ -4707,9 +4720,6 @@ void Character::update_body( const time_point &from, const time_point &to ) check_needs_extremes(); update_needs( five_mins ); regen( five_mins ); - // Note: mend ticks once per 5 minutes, but wants rate in TURNS, not 5 minute intervals - // TODO: change @ref med to take time_duration - mend( five_mins * to_turns( 5_minutes ) ); } if( ticks_between( from, to, 24_hours ) > 0 ) { enforce_minimum_healing(); @@ -5800,15 +5810,11 @@ hp_part Character::body_window( const std::string &menu_header, const nc_color all_state_col = limb_color( bp, true, true, true ); // Broken means no HP can be restored, it requires surgical attention. const bool limb_is_broken = is_limb_broken( bp ); - const bool limb_is_mending = limb_is_broken && - ( worn_with_flag( flag_SPLINT, bp ) || has_trait( trait_REGEN_LIZ ) ); if( show_all ) { e.allowed = true; } else if( has_curable_effect ) { e.allowed = true; - } else if( limb_is_broken ) { - e.allowed = false; } else if( current_hp < maximal_hp && ( e.bonus != 0 || bandage_power > 0.0f || disinfectant_power > 0.0f ) ) { e.allowed = true; @@ -5837,21 +5843,21 @@ hp_part Character::body_window( const std::string &menu_header, const auto &aligned_name = std::string( max_bp_name_len - utf8_width( e.name ), ' ' ) + e.name; std::string hp_str; - if( limb_is_mending ) { - desc += colorize( _( "It is broken but has been set and just needs time to heal." ), + if( limb_is_broken ) { + const nc_color color = worn_with_flag( flag_SPLINT, bp ) || + ( mutation_value( "mending_modifier" ) >= 1.0f ) ? + c_blue : + c_light_red; + desc += colorize( _( "It is broken and must heal fully before it becomes functional again." ), c_blue ) + "\n"; - const auto &eff = get_effect( effect_mending, bp_token ); - const int mend_perc = eff.is_null() ? 0.0 : 100 * eff.get_duration() / eff.get_max_duration(); + const int mend_perc = 100 * current_hp / maximal_hp; if( precise ) { - hp_str = colorize( string_format( "=%2d%%=", mend_perc ), c_blue ); + hp_str = colorize( string_format( "=%2d%%=", mend_perc ), color ); } else { const int num = mend_perc / 20; - hp_str = colorize( std::string( num, '#' ) + std::string( 5 - num, '=' ), c_blue ); + hp_str = colorize( std::string( num, '#' ) + std::string( 5 - num, '=' ), color ); } - } else if( limb_is_broken ) { - desc += colorize( _( "It is broken. It needs a splint or surgical attention." ), c_red ) + "\n"; - hp_str = "==%=="; } else if( precise ) { hp_str = string_format( "%d", current_hp ); } else { @@ -8431,10 +8437,5 @@ void Character::apply_damage( Creature *source, bodypart_id hurt, int dam, put_into_vehicle_or_drop( *this, item_drop_reason::tumbling, remove_primary_weapon() ); } - if( has_effect( effect_mending, part_to_damage->token ) ) { - effect &e = get_effect( effect_mending, part_to_damage->token ); - float remove_mend = dam / 20.0f; - e.mod_duration( -e.get_max_duration() * remove_mend ); - } if( dam > get_painkiller() ) { on_hurt( source ); @@ -8646,10 +8647,13 @@ int Character::reduce_healing_effect( const efftype_id &eff_id, int remove_med, void Character::heal( const bodypart_id &healed, int dam ) { - if( !is_limb_broken( healed ) ) { - int effective_heal = std::min( dam, get_part_hp_max( healed ) - get_part_hp_cur( healed ) ); - mod_part_hp_cur( healed, effective_heal ); - g->events().send( getID(), effective_heal ); + const int max_hp = get_part_hp_max( healed ); + const int cur_hp = get_part_hp_cur( healed ); + const int effective_heal = std::min( dam, max_hp - cur_hp ); + mod_part_hp_cur( healed, effective_heal ); + g->events().send( getID(), effective_heal ); + if( cur_hp + dam >= max_hp ) { + remove_effect( effect_disabled, healed.id() ); } } diff --git a/src/character.h b/src/character.h index c3ff9d3f7eb..a84bf824865 100644 --- a/src/character.h +++ b/src/character.h @@ -742,10 +742,8 @@ class Character : public Creature, public visitable int get_working_arm_count() const; /** Returns the number of functioning legs */ int get_working_leg_count() const; - /** Returns true if the limb is disabled(12.5% or less hp)*/ + /** Returns true if the limb is disabled (12.5% or less hp, or broken)*/ bool is_limb_disabled( const bodypart_id &limb ) const; - /** Returns true if the limb is hindered(40% or less hp) */ - bool is_limb_hindered( hp_part limb ) const; /** Returns true if the limb is broken */ bool is_limb_broken( const bodypart_id &limb ) const; /** source of truth of whether a Character can run */ @@ -2063,8 +2061,6 @@ class Character : public Creature, public visitable void suffer(); /** Handles mitigation and application of radiation */ bool irradiate( float rads, bool bypass = false ); - /** Handles the chance for broken limbs to spontaneously heal to 1 HP */ - void mend( int rate_multiplier ); /** Creates an auditory hallucination */ void sound_hallu(); diff --git a/src/creature.cpp b/src/creature.cpp index 0d69db54329..d1c48950682 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1652,14 +1652,14 @@ void Creature::mod_part_healed_total( const bodypart_id &id, int mod ) void Creature::set_all_parts_hp_cur( const int set ) { for( std::pair &elem : body ) { - elem.second.set_hp_cur( set ); + set_part_hp_cur( elem.first, set ); } } void Creature::set_all_parts_hp_to_max() { for( std::pair &elem : body ) { - elem.second.set_hp_to_max(); + set_part_hp_cur( elem.first, get_part_hp_max( elem.first ) ); } } diff --git a/src/iexamine.cpp b/src/iexamine.cpp index cb1af8fb5a7..5295e4bf0ca 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -123,7 +123,6 @@ static const efftype_id effect_bleed( "bleed" ); static const efftype_id effect_disinfected( "disinfected" ); static const efftype_id effect_earphones( "earphones" ); static const efftype_id effect_infected( "infected" ); -static const efftype_id effect_mending( "mending" ); static const efftype_id effect_pblue( "pblue" ); static const efftype_id effect_pkill2( "pkill2" ); static const efftype_id effect_sleep( "sleep" ); @@ -5081,11 +5080,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) for( int i = 0; i < num_hp_parts; i++ ) { const bodypart_id &part = convert_bp( player::hp_to_bp( static_cast( i ) ) ).id(); const bool broken = patient.is_limb_broken( part ); - effect &existing_effect = patient.get_effect( effect_mending, part->token ); - // Skip part if not broken or already healed 50% - if( !broken || ( !existing_effect.is_null() && - existing_effect.get_duration() > - existing_effect.get_max_duration() - 5_days - 1_turns ) ) { + if( !broken ) { continue; } broken_limbs_count++; @@ -5116,9 +5111,11 @@ void iexamine::autodoc( player &p, const tripoint &examp ) patient.add_msg_player_or_npc( m_good, _( "The machine rapidly sets and splints your broken %s." ), _( "The machine rapidly sets and splints 's broken %s." ), body_part_name( part ) ); - patient.add_effect( effect_mending, 0_turns, part->token ); - effect &mending_effect = patient.get_effect( effect_mending, part->token ); - mending_effect.set_duration( mending_effect.get_max_duration() - 5_days ); + // TODO: Prevent exploits with hp draining stuff? + int heal_amt = patient.get_part_hp_max( part ) / 2 - patient.get_part_hp_cur( part ); + if( heal_amt > 0 ) { + patient.heal( part, heal_amt ); + } } } if( broken_limbs_count == 0 ) { diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index a0d6e66b7dc..4a0eb9befdb 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -3903,18 +3903,6 @@ static hp_part pick_part_to_heal( return healed_part; } - if( patient.is_limb_broken( bp ) ) { - if( healed_part == hp_arm_l || healed_part == hp_arm_r ) { - add_msg( m_info, _( "That arm is broken. It needs surgical attention or a splint." ) ); - } else if( healed_part == hp_leg_l || healed_part == hp_leg_r ) { - add_msg( m_info, _( "That leg is broken. It needs surgical attention or a splint." ) ); - } else { - add_msg( m_info, "That body part is bugged. It needs developer's attention." ); - } - - continue; - } - if( force || patient.get_part_hp_cur( bp ) < patient.get_part_hp_max( bp ) ) { return healed_part; } diff --git a/src/mutation.h b/src/mutation.h index 8e82983e2d7..8fe226d47b5 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -115,8 +115,10 @@ struct mutation_branch { // Healing per turn float healing_awake = 0.0f; float healing_resting = 0.0f; - // Limb mending bonus - float mending_modifier = 1.0f; + // Multiplier on regen of broken limbs. + // Base regen of broken limbs is 25% and 25% the low cap. + // Capped at 1.0, which makes broken limbs regen at same rate as unbroken. + float mending_modifier = 0.0f; // Bonus HP multiplier. That is, 1.0 doubles hp, -0.5 halves it. float hp_modifier = 0.0f; // Second HP modifier that stacks with first but is otherwise identical. diff --git a/src/mutation_data.cpp b/src/mutation_data.cpp index 9c95b97873e..a070de723d8 100644 --- a/src/mutation_data.cpp +++ b/src/mutation_data.cpp @@ -377,7 +377,7 @@ void mutation_branch::load( const JsonObject &jo, const std::string & ) optional( jo, was_loaded, "pain_recovery", pain_recovery, 0.0f ); optional( jo, was_loaded, "healing_awake", healing_awake, 0.0f ); optional( jo, was_loaded, "healing_resting", healing_resting, 0.0f ); - optional( jo, was_loaded, "mending_modifier", mending_modifier, 1.0f ); + optional( jo, was_loaded, "mending_modifier", mending_modifier, 0.0f ); optional( jo, was_loaded, "hp_modifier", hp_modifier, 0.0f ); optional( jo, was_loaded, "hp_modifier_secondary", hp_modifier_secondary, 0.0f ); optional( jo, was_loaded, "hp_adjustment", hp_adjustment, 0.0f ); diff --git a/src/npc.cpp b/src/npc.cpp index 36b63dd2bb4..9c31869540d 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -84,7 +84,6 @@ static const efftype_id effect_contacts( "contacts" ); static const efftype_id effect_drunk( "drunk" ); static const efftype_id effect_feral_killed_recently( "feral_killed_recently" ); static const efftype_id effect_infection( "infection" ); -static const efftype_id effect_mending( "mending" ); static const efftype_id effect_npc_flee_player( "npc_flee_player" ); static const efftype_id effect_npc_suspend( "npc_suspend" ); static const efftype_id effect_pkill_l( "pkill_l" ); @@ -1073,7 +1072,8 @@ bool npc::wear_if_wanted( const item &it, std::string &reason ) for( int i = 0; i < num_hp_parts; i++ ) { hp_part hpp = static_cast( i ); body_part bp = player::hp_to_bp( hpp ); - if( is_limb_broken( convert_bp( bp ) ) && !has_effect( effect_mending, bp ) && + if( is_limb_broken( convert_bp( bp ).id() ) && + !worn_with_flag( flag_SPLINT, convert_bp( bp ).id() ) && it->covers( convert_bp( bp ).id() ) ) { reason = _( "Thanks, I'll wear that now." ); return wear_item( std::move( it ), false ); diff --git a/src/panels.cpp b/src/panels.cpp index 41983b82ae5..9c8ae1701bc 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -61,7 +61,6 @@ #include "vpart_position.h" #include "weather.h" -static const trait_id trait_REGEN_LIZ( "REGEN_LIZ" ); static const trait_id trait_SELFAWARE( "SELFAWARE" ); static const trait_id trait_THRESH_FELINE( "THRESH_FELINE" ); static const trait_id trait_THRESH_BIRD( "THRESH_BIRD" ); @@ -771,36 +770,36 @@ static void draw_limb_health( avatar &u, const catacurses::window &w, int limb_i wprintz( w, color, sym ); } }; + const bodypart_id bp = convert_bp( avatar::hp_to_bp( static_cast( limb_index ) ) ).id(); + const int hp_cur = u.get_part_hp_cur( bp ); + const int hp_max = u.get_part_hp_max( bp ); + + std::optional color_override; + if( u.is_limb_broken( bp.id() ) && ( limb_index >= hp_arm_l && limb_index <= hp_leg_r ) ) { //Limb is broken - std::string limb = "~~%~~"; - nc_color color = c_light_red; - - if( u.worn_with_flag( json_flag_SPLINT, bp ) || u.has_trait( trait_REGEN_LIZ ) ) { - static const efftype_id effect_mending( "mending" ); - const auto &eff = u.get_effect( effect_mending, bp->token ); - const int mend_perc = eff.is_null() ? 0.0 : 100 * eff.get_duration() / eff.get_max_duration(); + const int mend_perc = 100 * hp_cur / hp_max; + bool splinted = u.worn_with_flag( json_flag_SPLINT, bp ) || + ( u.mutation_value( "mending_modifier" ) >= 1.0f ); + nc_color color = splinted ? c_blue : c_dark_gray; - if( is_self_aware || u.has_effect( effect_got_checked ) ) { - limb = string_format( "=%2d%%=", mend_perc ); - color = c_blue; - } else { - const int num = mend_perc / 20; - print_symbol_num( w, num, "#", c_blue ); - print_symbol_num( w, 5 - num, "=", c_blue ); - return; - } + if( is_self_aware || u.has_effect( effect_got_checked ) ) { + color_override = color; + } else { + const int num = mend_perc / 20; + print_symbol_num( w, num, "#", color ); + print_symbol_num( w, 5 - num, "=", color ); + return; } - - wprintz( w, color, limb ); - return; } - const int hp_cur = u.get_part_hp_cur( bp ); - const int hp_max = u.get_part_hp_max( bp ); + std::pair hp = get_hp_bar( hp_cur, hp_max ); + if( color_override ) { + hp.second = *color_override; + } if( is_self_aware || u.has_effect( effect_got_checked ) ) { wprintz( w, hp.second, "%3d ", hp_cur ); diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index 2f74c64ccf4..88d7f83de8e 100644 --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -73,7 +73,6 @@ static const efftype_id effect_hallu( "hallu" ); static const efftype_id effect_hot( "hot" ); static const efftype_id effect_infected( "infected" ); static const efftype_id effect_lying_down( "lying_down" ); -static const efftype_id effect_mending( "mending" ); static const efftype_id effect_mutating( "mutating" ); static const efftype_id effect_nausea( "nausea" ); static const efftype_id effect_narcosis( "narcosis" ); @@ -1292,12 +1291,8 @@ void Character::hardcoded_effects( effect &it ) } } } - } else if( id == effect_mending ) { - if( !is_limb_broken( convert_bp( bp ) ) ) { - it.set_duration( 0_turns ); - } } else if( id == effect_disabled ) { - if( !is_limb_broken( convert_bp( bp ) ) ) { + if( get_part_hp_cur( convert_bp( bp ) ) >= get_part_hp_max( convert_bp( bp ) ) ) { remove_effect( effect_disabled ); } } else if( id == effect_panacea ) { diff --git a/src/suffer.cpp b/src/suffer.cpp index f752944f22b..acc6a8dc50e 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -95,7 +95,6 @@ static const efftype_id effect_glowy_led( "glowy_led" ); static const efftype_id effect_hallu( "hallu" ); static const efftype_id effect_iodine( "iodine" ); static const efftype_id effect_masked_scent( "masked_scent" ); -static const efftype_id effect_mending( "mending" ); static const efftype_id effect_meth( "meth" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_nausea( "nausea" ); @@ -143,7 +142,6 @@ static const trait_id trait_RADIOACTIVE1( "RADIOACTIVE1" ); static const trait_id trait_RADIOACTIVE2( "RADIOACTIVE2" ); static const trait_id trait_RADIOACTIVE3( "RADIOACTIVE3" ); static const trait_id trait_RADIOGENIC( "RADIOGENIC" ); -static const trait_id trait_REGEN_LIZ( "REGEN_LIZ" ); static const trait_id trait_ROOTS3( "ROOTS3" ); static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" ); static const trait_id trait_SHARKTEETH( "SHARKTEETH" ); @@ -174,16 +172,6 @@ static const std::string flag_RAD_RESIST( "RAD_RESIST" ); static const std::string flag_SPLINT( "SPLINT" ); static const std::string flag_SUN_GLASSES( "SUN_GLASSES" ); -static float addiction_scaling( float at_min, float at_max, float add_lvl ) -{ - // Not addicted - if( add_lvl < MIN_ADDICTION_LEVEL ) { - return 1.0f; - } - - return lerp( at_min, at_max, ( add_lvl - MIN_ADDICTION_LEVEL ) / MAX_ADDICTION_LEVEL ); -} - void Character::suffer_water_damage( const mutation_branch &mdata ) { for( const std::pair &elem : get_body() ) { @@ -1669,109 +1657,6 @@ bool Character::irradiate( float rads, bool bypass ) return false; } -void Character::mend( int rate_multiplier ) -{ - // Wearing splints can slowly mend a broken limb back to 1 hp. - bool any_broken = false; - for( const bodypart_id &bp : get_all_body_parts() ) { - if( is_limb_broken( bp ) ) { - any_broken = true; - break; - } - } - - if( !any_broken ) { - return; - } - - double healing_factor = 1.0; - // Studies have shown that alcohol and tobacco use delay fracture healing time - // Being under effect is 50% slowdown - // Being addicted but not under effect scales from 25% slowdown to 75% slowdown - // The improvement from being intoxicated over withdrawal is intended - if( has_effect( effect_cig ) ) { - healing_factor *= 0.5; - } else { - healing_factor *= addiction_scaling( 0.25f, 0.75f, addiction_level( add_type::CIG ) ); - } - - if( has_effect( effect_drunk ) ) { - healing_factor *= 0.5; - } else { - healing_factor *= addiction_scaling( 0.25f, 0.75f, addiction_level( add_type::ALCOHOL ) ); - } - - if( get_rad() > 0 && !has_trait( trait_RADIOGENIC ) ) { - healing_factor *= clamp( ( 1000.0f - get_rad() ) / 1000.0f, 0.0f, 1.0f ); - } - - // Bed rest speeds up mending - if( has_effect( effect_sleep ) ) { - healing_factor *= 4.0; - } else if( get_fatigue() > fatigue_levels::dead_tired ) { - // but being dead tired does not... - healing_factor *= 0.75; - } else { - // If not dead tired, resting without sleep also helps - healing_factor *= 1.0f + rest_quality(); - } - - // Being healthy helps. - healing_factor *= 1.0f + get_healthy() / 200.0f; - - // Very hungry starts lowering the chance - // square rooting the value makes the numbers drop off faster when below 1 - healing_factor *= std::sqrt( static_cast( get_stored_kcal() ) / static_cast - ( max_stored_kcal() ) ); - // Similar for thirst - starts at very thirsty, drops to 0 at parched - healing_factor *= 1.0f - clamp( 1.0f * ( get_thirst() - thirst_levels::very_thirsty ) / - +thirst_levels::parched, 0.0f, 1.0f ); - - // Mutagenic healing factor! - bool needs_splint = true; - - healing_factor *= mutation_value( "mending_modifier" ); - - if( has_trait( trait_REGEN_LIZ ) ) { - needs_splint = false; - } - - add_msg( m_debug, "Limb mend healing factor: %.2f", healing_factor ); - if( healing_factor <= 0.0f ) { - // The section below assumes positive healing rate - return; - } - - for( const bodypart_id &bp : get_all_body_parts() ) { - const bool broken = is_limb_broken( bp ); - if( !broken ) { - continue; - } - - if( needs_splint && !worn_with_flag( flag_SPLINT, bp ) ) { - continue; - } - - const time_duration dur_inc = 1_turns * roll_remainder( rate_multiplier * healing_factor ); - auto &eff = get_effect( effect_mending, bp->token ); - if( eff.is_null() ) { - add_effect( effect_mending, dur_inc, bp->token ); - continue; - } - - eff.set_duration( eff.get_duration() + dur_inc ); - - if( eff.get_duration() >= eff.get_max_duration() ) { - set_part_hp_cur( bp, 1 ); - remove_effect( effect_mending, bp->token ); - g->events().send( getID(), bp->token ); - //~ %s is bodypart - add_msg_if_player( m_good, _( "Your %s has started to mend!" ), - body_part_name( bp ) ); - } - } -} - void Character::sound_hallu() { // Random 'dangerous' sound from a random direction diff --git a/tests/player_helpers.cpp b/tests/player_helpers.cpp index fc95f1696d8..307b9a8834f 100644 --- a/tests/player_helpers.cpp +++ b/tests/player_helpers.cpp @@ -98,6 +98,9 @@ void clear_character( player &dummy, bool debug_storage ) dummy.set_stamina( dummy.get_stamina_max() ); dummy.set_movement_mode( CMM_WALK ); + // Set HP to max here and also later, for disabled/broken limbs + dummy.set_all_parts_hp_to_max(); + // Make sure we don't carry around weird effects. dummy.clear_effects(); // mark effects for removal dummy.process_effects(); // actually remove them -- 2.42.0