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

fix: repair nanobots not working at all on broken limbs #3787

Merged
merged 1 commit into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion data/json/bionics.json
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@
"id": "bio_nanobots",
"type": "bionic",
"name": { "str": "Repair Nanobots" },
"description": "Inside your body is a fleet of tiny dormant robots. While activated they will reduce the intensity of one bleed effect every 30 seconds, and heal all injured body parts by 1 HP (or broken limbs by 1%) every 2 minutes if you have 3 kJ bionic power and 5 kcal per body part. Broken limbs must still be splinted (unless you have specific mutations) to benefit. If you don't have enough, they will prioritize based on the resources you have.",
"description": "Inside your body is a fleet of tiny dormant robots. While activated they will reduce the intensity of one bleed effect every 30 seconds, and heal all injured body parts by 1 HP (or broken limbs by 1%) every 2 minutes if you have 3 kJ bionic power and 5 kcal per body part. If you don't have enough, they will prioritize based on the resources you have.",
"occupied_bodyparts": [ [ "torso", 10 ] ],
"flags": [ "BIONIC_TOGGLED", "BIONIC_NPC_USABLE", "BIONIC_SLEEP_FRIENDLY" ],
"act_cost": "300 J",
Expand Down
2 changes: 1 addition & 1 deletion data/json/items/bionics.json
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@
"type": "BIONIC_ITEM",
"name": { "str": "Repair Nanobots CBM" },
"looks_like": "bio_int_enhancer",
"description": "A fleet of tiny dormant robots. While activated they will reduce the intensity of one bleed effect every 30 seconds, and heal all injured body parts by 1 HP (or broken limbs by 1%) every 2 minutes if you have 3 kJ bionic power and 5 kcal per body part. Broken limbs must still be splinted (unless you have specific mutations) to benefit. If you don't have enough, they will prioritize based on the resources you have.",
"description": "A fleet of tiny dormant robots. While activated they will reduce the intensity of one bleed effect every 30 seconds, and heal all injured body parts by 1 HP (or broken limbs by 1%) every 2 minutes if you have 3 kJ bionic power and 5 kcal per body part. If you don't have enough, they will prioritize based on the resources you have.",
"price": "9500 USD",
"weight": "200 g",
"difficulty": 6
Expand Down
47 changes: 26 additions & 21 deletions src/bionics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#include "point.h"
#include "projectile.h"
#include "requirements.h"
#include "regen.h"
#include "rng.h"
#include "sounds.h"
#include "string_formatter.h"
Expand Down Expand Up @@ -1662,28 +1663,32 @@ void Character::process_bionic( bionic &bio )
}
}
if( calendar::once_every( 2_minutes ) ) {
std::vector<bodypart_id> damaged_hp_parts;
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 );
}
}
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.
std::sort( damaged_hp_parts.begin(), damaged_hp_parts.end(),
[this]( const bodypart_id & a, const bodypart_id & b ) {
return ( get_part_hp_cur( a ) - a->essential * 10 ) < ( get_part_hp_cur( b ) - b->essential * 10 );
} );
for( bodypart_id &bpid : damaged_hp_parts ) {
if( !can_use_bionic() ) {
return;
}
heal( bpid, 1 );
mod_power_level( -bio.info().power_trigger );
mod_stored_kcal( -bio.info().kcal_trigger );
// 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.
const auto effective_hp = [this]( const bodypart_id & bp ) -> int {
return get_part_hp_cur( bp ) - bp->essential * 10;
};
const auto should_heal = [this]( const bodypart_id & bp ) -> bool {
return get_part_hp_cur( bp ) < get_part_hp_max( bp );
};
const auto sort_by = [effective_hp]( const bodypart_id & a, const bodypart_id & b ) -> bool {
return effective_hp( a ) < effective_hp( b );
};
const auto damaged_parts = [this, should_heal, sort_by]() {
const auto xs = get_all_body_parts( true );
auto ys = std::vector<bodypart_id> {};
std::copy_if( xs.begin(), xs.end(), std::back_inserter( ys ), should_heal );
std::sort( ys.begin(), ys.end(), sort_by );
return ys;
};

for( bodypart_id &bp : damaged_parts() ) {
if( !can_use_bionic() ) {
return;
}
heal_adjusted( *this, bp, 1 );
mod_power_level( -bio.info().power_trigger );
mod_stored_kcal( -bio.info().kcal_trigger );
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions src/character.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "character.h"
#include "bodypart.h"
#include "character_encumbrance.h"

#include <algorithm>
Expand Down Expand Up @@ -82,6 +81,7 @@
#include "profession.h"
#include "recipe_dictionary.h"
#include "ret_val.h"
#include "regen.h"
#include "rng.h"
#include "scent_map.h"
#include "skill.h"
Expand Down Expand Up @@ -5067,14 +5067,11 @@ void Character::regen( int rate_multiplier )
float heal_rate = healing_rate( rest ) * to_turns<int>( 5_minutes );
const float broken_regen_mod = clamp( mutation_value( "mending_modifier" ), 0.25f, 1.0f );
if( heal_rate > 0.0f ) {
const int base_heal = roll_remainder( rate_multiplier * heal_rate );
const int broken_heal = roll_remainder( base_heal * broken_regen_mod );
const int heal = roll_remainder( rate_multiplier * heal_rate );

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 );
const int actually_healed = heal_adjusted( *this, bp, heal );
mod_part_healed_total( bp, actually_healed );
}
} else if( heal_rate < 0.0f ) {
int rot_rate = roll_remainder( rate_multiplier * -heal_rate );
Expand Down
35 changes: 35 additions & 0 deletions src/regen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "regen.h"
#include "character.h"
#include "rng.h"

const flag_id flag_SPLINT( "SPLINT" );

namespace
{

/// Limb is broken without splint
auto has_broken_limb_penalty( const Character &c, const bodypart_id &bp ) -> bool
{
return c.is_limb_broken( bp )
&& !c.worn_with_flag( flag_SPLINT, bp );
}

/// Broken limbs without splint heal slower up to 25%
auto mending_modifier( const Character &c ) -> float
{
return clamp( c.mutation_value( "mending_modifier" ), 0.25f, 1.0f );
}

} // namespace

auto heal_adjusted( Character &c, const bodypart_id &bp, const int heal ) -> int
{
const float broken_regen_mod = mending_modifier( c );
const int broken_heal = roll_remainder( heal * broken_regen_mod );
const bool is_broken = has_broken_limb_penalty( c, bp );
const int actual_heal = is_broken ? broken_heal : heal;

c.heal( bp, actual_heal );

return actual_heal;
}
18 changes: 18 additions & 0 deletions src/regen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#ifndef CATA_SRC_REGEN_H
#define CATA_SRC_REGEN_H

#include "type_id.h"

class Character;

/// like heal, but actually takes account of
/// - whether limb suffers from being broken without splint
/// - `mending_modifier`
///
/// @return actually healed amount. used for `mod_part_healed_total`
///
/// TODO: merge into `Character::heal`?
auto heal_adjusted( Character &c, const bodypart_id &bp, const int heal ) -> int;

#endif // CATA_SRC_REGEN_H
Loading