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

Allow mutations to affect cardio #53603

Merged
merged 2 commits into from
Jan 1, 2022
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
20 changes: 10 additions & 10 deletions data/json/mutations/mutations.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,26 +232,26 @@
"id": "GOODCARDIO",
"name": { "str": "Indefatigable" },
"points": 2,
"description": "Whether due to exercise and good diet, or due to a natural propensity to physical endurance, you tire due to physical exertion much less readily than others. Your maximum stamina is higher than usual.",
"description": "Whether due to exercise and good diet, or due to a natural propensity to physical endurance, you tire due to physical exertion much less readily than others. Your cardio fitness and maximum stamina are higher than usual.",
"starting_trait": true,
"valid": false,
"cancels": [ "BADCARDIO" ],
"changes_to": [ "GOODCARDIO2" ],
"category": [ "FISH", "LUPINE", "MOUSE", "INSECT" ],
"max_stamina_modifier": 1.25
"cardio_multiplier": 1.3
},
{
"type": "mutation",
"id": "GOODCARDIO2",
"name": { "str": "Hyperactive" },
"points": 4,
"description": "Your body's efficiency is like that of a tiny furnace, greatly increasing your maximum stamina",
"description": "Your body's efficiency is like that of a tiny furnace, greatly increasing your cardio and stamina.",
"valid": false,
"prereqs": [ "GOODCARDIO" ],
"cancels": [ "BADCARDIO" ],
"threshreq": [ "THRESH_MOUSE", "THRESH_RABBIT" ],
"category": [ "MOUSE", "RABBIT" ],
"max_stamina_modifier": 1.4
"cardio_multiplier": 1.6
},
{
"type": "mutation",
Expand Down Expand Up @@ -1024,11 +1024,11 @@
"id": "BADCARDIO",
"name": { "str": "Languorous" },
"points": -2,
"description": "Whether due to lack of exercise and poor diet, or due to a natural disinclination to physical endurance, you tire due to physical exertion much more readily than others. Your maximum stamina is lower than usual.",
"description": "Whether due to lack of exercise and poor diet, or due to a natural disinclination to physical endurance, you tire due to physical exertion much more readily than others. Your total cardio fitness and stamina are lower than normal.",
"starting_trait": true,
"valid": false,
"cancels": [ "GOODCARDIO" ],
"max_stamina_modifier": 0.75
"cardio_multiplier": 0.7
},
{
"type": "mutation",
Expand Down Expand Up @@ -7467,12 +7467,12 @@
},
{
"type": "mutation",
"id": "DEBUG_STAMINA",
"name": { "str": "Debug Stamina" },
"id": "DEBUG_CARDIO",
"name": { "str": "Debug Cardio" },
"points": 99,
"valid": false,
"max_stamina_modifier": 999999,
"description": "You can't run from the bugs, but you have enough stamina to at least try to.",
"cardio_multiplier": 999999,
"description": "You can run, but you'll never run out of breath.",
"debug": true
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"threshreq": [ "THRESH_SYLVAN" ],
"category": [ "SYLVAN" ],
"cancels": [ "BADCARDIO" ],
"max_stamina_modifier": 1.5,
"cardio_multiplier": 1.8,
"stamina_regen_modifier": 0.25
}
]
2 changes: 2 additions & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2173,6 +2173,8 @@ The `id` must be exact as it is hardcoded to look for that.
"metabolism_modifier": 0.333, // Extra metabolism rate multiplier. 1.0 doubles usage, -0.5 halves.
"fatigue_modifier": 0.5, // Extra fatigue rate multiplier. 1.0 doubles usage, -0.5 halves.
"fatigue_regen_modifier": 0.333, // Modifier for the rate at which fatigue and sleep deprivation drops when resting.
"stamina_regen_modifier": 0.1, // Increase stamina regen by this proportion (1.0 being 100% of normal regen)
"cardio_multiplier": 1.5, // Multiply total cardio fitness by this amount
"healing_awake": 1.0, // Healing rate per turn while awake.
"healing_resting": 0.5, // Healing rate per turn while resting.
"mending_modifier": 1.2 // Multiplier on how fast your limbs mend - This value would make your limbs mend 20% faster
Expand Down
65 changes: 42 additions & 23 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5704,7 +5704,7 @@ mutation_value_map = {
{ "movecost_flatground_modifier", calc_mutation_value_multiplicative<&mutation_branch::movecost_flatground_modifier> },
{ "movecost_obstacle_modifier", calc_mutation_value_multiplicative<&mutation_branch::movecost_obstacle_modifier> },
{ "attackcost_modifier", calc_mutation_value_multiplicative<&mutation_branch::attackcost_modifier> },
{ "max_stamina_modifier", calc_mutation_value_multiplicative<&mutation_branch::max_stamina_modifier> },
{ "cardio_multiplier", calc_mutation_value_multiplicative<&mutation_branch::cardio_multiplier> },
{ "weight_capacity_modifier", calc_mutation_value_multiplicative<&mutation_branch::weight_capacity_modifier> },
{ "hearing_modifier", calc_mutation_value_multiplicative<&mutation_branch::hearing_modifier> },
{ "movecost_swim_modifier", calc_mutation_value_multiplicative<&mutation_branch::movecost_swim_modifier> },
Expand Down Expand Up @@ -6192,12 +6192,13 @@ int Character::get_stamina_max() const
// Default base maximum stamina and cardio scaling are defined in data/core/game_balance.json
static const std::string player_max_stamina( "PLAYER_MAX_STAMINA_BASE" );
static const std::string player_cardiofit_stamina_scale( "PLAYER_CARDIOFIT_STAMINA_SCALING" );
static const std::string max_stamina_modifier( "max_stamina_modifier" );

// Cardiofit stamina mod defaults to 3, and get_cardiofit() should return a value in the vicinity
// of 1000-4000, so this should add somewhere between 3000 to 12000 stamina.
int max_stamina = get_option<int>( player_max_stamina ) +
get_option<int>( player_cardiofit_stamina_scale ) * get_cardiofit();
max_stamina = enchantment_cache->modify_value( enchant_vals::mod::MAX_STAMINA, max_stamina );

return max_stamina;
}

Expand Down Expand Up @@ -6261,18 +6262,23 @@ void Character::burn_move_stamina( int moves )
void Character::update_stamina( int turns )
{
static const std::string player_base_stamina_regen_rate( "PLAYER_BASE_STAMINA_REGEN_RATE" );
static const std::string stamina_regen_modifier( "stamina_regen_modifier" );
const float base_regen_rate = get_option<float>( player_base_stamina_regen_rate );
// Your stamina regen rate works as a function of how fit you are compared to your body size. This allows it to scale more quickly
// than your stamina, so that at higher fitness levels you recover stamina faster.
// Your stamina regen rate works as a function of how fit you are compared to your body size.
// This allows it to scale more quickly than your stamina, so that at higher fitness levels you
// recover stamina faster.
const float effective_regen_rate = base_regen_rate * get_cardiofit() / base_bmr();
const int current_stim = get_stim();
float stamina_recovery = 0.0f;
// Recover some stamina every turn.
// Mutations can affect stamina regen via stamina_regen_modifier (0.0 is normal)
// Values above or below normal will increase or decrease stamina regen
const float mod_regen = mutation_value( "stamina_regen_modifier" );
// Mutated stamina works even when winded
// max stamina modifers from mutation also affect stamina multi
float stamina_multiplier = std::max<float>( 0.1f, ( !has_effect( effect_winded ) ? 1.0f : 0.1f ) +
mutation_value( stamina_regen_modifier ) + ( mutation_value( "max_stamina_modifier" ) - 1.0f ) );
const float base_multiplier = mod_regen + ( has_effect( effect_winded ) ? 0.1f : 1.0f );
// Ensure multiplier is at least 0.1
const float stamina_multiplier = std::max<float>( 0.1f, base_multiplier );

// Recover some stamina every turn. Start with zero, then increase recovery factor based on
// mutations, stimulants, and bionics before rolling random recovery based on this factor.
float stamina_recovery = 0.0f;
// But mouth encumbrance interferes, even with mutated stamina.
stamina_recovery += stamina_multiplier * std::max( 1.0f,
effective_regen_rate * get_modifier( character_modifier_stamina_recovery_breathing_mod ) );
Expand Down Expand Up @@ -6308,9 +6314,11 @@ void Character::update_stamina( int turns )
}
}

mod_stamina( roll_remainder( stamina_recovery * turns ) );
add_msg_debug( debugmode::DF_CHARACTER, "Stamina recovery: %d",
roll_remainder( stamina_recovery * turns ) );
// Roll to determine actual stamina recovery over this period
int recover_amount = roll_remainder( stamina_recovery * turns );
mod_stamina( recover_amount );
add_msg_debug( debugmode::DF_CHARACTER, "Stamina recovery: %d", recover_amount );

// Cap at max
set_stamina( std::min( std::max( get_stamina(), 0 ), max_stam ) );
}
Expand All @@ -6324,19 +6332,30 @@ int Character::get_cardiofit() const
const int bmr = base_bmr();
const int athletics_mod = get_skill_level( skill_swimming ) * 10;
const int health_effect = get_healthy();
// Traits now exclusively affect cardio, NOT max_stamina directly. In the future, make cardio_acc also be affected by cardio traits so that they don't become less impactful.
const int trait_mod = mutation_value( "max_stamina_modifier" );

// FIXME: Delete this untruth
// Traits now exclusively affect cardio, NOT max_stamina directly. In the future, make
// cardio_acc also be affected by cardio traits so that they don't become less impactful.
//const int trait_mod = 0;

// At some point we might have proficiencies that affect this.
const int prof_mod = 0;
const int cardio_acc_mod = get_cardio_acc();
int final_cardio_fitness = bmr / 2 + athletics_mod + health_effect + trait_mod + prof_mod +
cardio_acc_mod;
if( final_cardio_fitness > 3 * ( bmr + trait_mod ) ) {
// Set a large sane upper limit to cardio fitness. This could be done asymptotically instead of as a sharp cutoff, but the gradual
// growth rate of cardio_acc_mod should accomplish that naturally. The BMR will mostly determine this as it is based on the
// size of the character, but mutations might push it up.
final_cardio_fitness = 3 * ( bmr + trait_mod );
}

// Base formula for cardio fitness
int base_cardio_fitness = bmr / 2 + athletics_mod + health_effect + prof_mod + cardio_acc_mod;

// Apply trait modifier as a scaling factor to total cardio
// FIXME: Do this additively as a trait_mod using the original formula, somehow
const float scale = mutation_value( "cardio_multiplier" );
const float scaled_fitness = base_cardio_fitness * scale;

// Set a large sane upper limit to cardio fitness. This could be done asymptotically instead of
// as a sharp cutoff, but the gradual growth rate of cardio_acc_mod should accomplish that
// naturally. The BMR will mostly determine this as it is based on the size of the character,
// but mutations might push it up.
int final_cardio_fitness = static_cast<int>( std::min( scaled_fitness, 3 * bmr * scale ) );

return final_cardio_fitness;
}

Expand Down
2 changes: 1 addition & 1 deletion src/mutation.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ struct mutation_branch {
cata::optional<float> movecost_flatground_modifier = cata::nullopt;
cata::optional<float> movecost_obstacle_modifier = cata::nullopt;
cata::optional<float> attackcost_modifier = cata::nullopt;
cata::optional<float> max_stamina_modifier = cata::nullopt;
cata::optional<float> cardio_multiplier = cata::nullopt;
cata::optional<float> weight_capacity_modifier = cata::nullopt;
cata::optional<float> hearing_modifier = cata::nullopt;
cata::optional<float> movecost_swim_modifier = cata::nullopt;
Expand Down
2 changes: 1 addition & 1 deletion src/mutation_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ void mutation_branch::load( const JsonObject &jo, const std::string & )
optional( jo, was_loaded, "movecost_obstacle_modifier", movecost_obstacle_modifier, cata::nullopt );
optional( jo, was_loaded, "movecost_swim_modifier", movecost_swim_modifier, cata::nullopt );
optional( jo, was_loaded, "attackcost_modifier", attackcost_modifier, cata::nullopt );
optional( jo, was_loaded, "max_stamina_modifier", max_stamina_modifier, cata::nullopt );
optional( jo, was_loaded, "cardio_multiplier", cardio_multiplier, cata::nullopt );
optional( jo, was_loaded, "weight_capacity_modifier", weight_capacity_modifier, cata::nullopt );
optional( jo, was_loaded, "hearing_modifier", hearing_modifier, cata::nullopt );
optional( jo, was_loaded, "noise_modifier", noise_modifier, cata::nullopt );
Expand Down
Loading