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

[WIP] Create cardio #50339

Closed
wants to merge 27 commits into from
Closed

[WIP] Create cardio #50339

wants to merge 27 commits into from

Conversation

KorGgenT
Copy link
Member

@KorGgenT KorGgenT commented Jul 30, 2021

Summary

Features "Implements cardio to adjust weariness and stamina based on fitness"

see #44506 for all relevant discussion and original PR.
Closes #44506

Co-authored-by: George Karfakis <[email protected]>
@KorGgenT KorGgenT changed the title Create cardio [WIP] Create cardio Jul 30, 2021
@actual-nh actual-nh added <Enhancement / Feature> New features, or enhancements on existing [C++] Changes (can be) made in C++. Previously named `Code` Game: Balance Balancing of (existing) in-game features. Mechanics: Character / Player Character / Player mechanics Mechanics: Effects / Skills / Stats Effects / Skills / Stats Mechanics: Enchantments / Spells Enchantments and spells Mutations / Traits / Professions/ Hobbies Mutations / Traits / Professions/ Hobbies labels Jul 30, 2021
@KorGgenT KorGgenT removed Mechanics: Enchantments / Spells Enchantments and spells Mutations / Traits / Professions/ Hobbies Mutations / Traits / Professions/ Hobbies labels Jul 31, 2021
@Broken27
Copy link
Contributor

Broken27 commented Aug 2, 2021

What are the expected values of Cardio/stamina at different stages of the game?

Eg, an out of chargen survivor with 0 athletics, or a midgame survivor with athletics 5 and a good diet?

@I-am-Erk
Copy link
Member

See #44370 for original issue and design.

Copy link
Member

@I-am-Erk I-am-Erk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a max cardio stat, and per some of Kevin's concerns, perhaps we should change the general name to 'cardiofit' for 'cardio fitness' to keep meanings clear.

src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
src/vehicle.cpp Outdated Show resolved Hide resolved
src/vehicle.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@actual-nh actual-nh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! (There do not appear to be any effects of health on NPC stamina, but I doubt it matters.)

src/avatar.cpp Outdated Show resolved Hide resolved
Comment on lines 3584 to 3588

return string_format( "Weariness: %s Max Full Exert: %s Mult: %g\nBMR: %d %s Thresh: %d At: %d\nCal: %d/%d Fatigue: %d Morale: %d Wgt: %d (BMI %.1f)",
amt, max_act, move_mult, bmr, weary_internals, thresh, current, get_stored_kcal(),
get_healthy_kcal(), fatigue, morale, weight, bmi );
return string_format( "Weariness: %s Max Exertion: %s Mult: %g\nCARDIO FITNESS: %d %s Thresh: %d At: %d\n Calories: %d/%d Fatigue: %d Morale: %d Wgt: %d (BMI %.1f)",
amt, max_act, move_mult, cardio_mult, activity_history.debug_weary_info(), thresh, current,
get_stored_kcal(), get_healthy_kcal(), fatigue, morale, weight, bmi );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After finishing testing cardio, could the original information be restored in addition to cardio (ideally in lines that aren't too terribly long)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably have both I would think, I added both back last time. Would you be willing to post a suggestion? My screen time is at an end.

@actual-nh
Copy link
Contributor

The test failure for the Basic Build is that, when it tries to access metabolic_rate_base() for base_bmr(), via a float option, something goes wrong (and causes a coredump, so I don't think it's just it not able to find something).

Copy link
Contributor

@actual-nh actual-nh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of places where base_bmr(), possibly modified, may be what is needed instead of get_bmr(), which includes effects from the current activity level.

src/character.cpp Outdated Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@actual-nh actual-nh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a preliminary suggestion.

src/character.cpp Show resolved Hide resolved
src/character.cpp Outdated Show resolved Hide resolved
I-am-Erk and others added 2 commits September 9, 2021 08:59
@I-am-Erk
Copy link
Member

I-am-Erk commented Sep 9, 2021

Those seem reasonable to me for now. It's a debug line so if it's a bit hard to read it's not the end of the world.

src/character.cpp Outdated Show resolved Hide resolved
This allows it to scale differently than base stamina so it doesn't always take 6 minutes to catch your breath.
src/character.cpp Outdated Show resolved Hide resolved
@I-am-Erk
Copy link
Member

I-am-Erk commented Sep 17, 2021

OK. This is intense, I think this actually works. I had to adjust how the stamina regeneration equation works from my initial proposal, which actually simplifies this PR. your stamina regeneration rate now starts at 20 as before, but is multiplied by (get_cardiofitness()/base_bmr()), so that as your cardio fitness exceeds BMR your regen increases, or vice versa. This means if you suddenly get bigger your regeneration drops.

With the present balance, I did three tests:

  1. A character with 0 health, 0 athletics, no traits, etc - can sprint for about 1:30 and recovers to full stamina in 6:45 This is comparable to previous.
  2. A character with -200 health, 0 athletics - can sprint for about 1:10 and recovers to full stamina in 7:03. This doesn't represent a very low drop in cardiofit, it would be nice to test the lower ranges better.
  3. A character with 0 health, 200 athletics - can run for 2:44, recovers to full stamina in 5:12.

The previous standard has a newly generated character running for 1:10 and recovering to full stamina in 7:30, which means I may need to slightly adjust the default stamina regeneration as I don't want to buff starting characters too strongly. Otherwise this seems to be working properly and well. The magnitude of effect might be low, if anything. With further debug tools to directly set cardio it would be easier to test but I do not think play balance will tank if we merge this as is.

@I-am-Erk
Copy link
Member

I am sure there will be some CI tests very angry about this.

From my perspective aside from solving the upcoming failed tests, this PR is ready to merge. I think we need a follow up PR relatively soon with:

  • some kind of @ menu information on your current cardio fitness, not a direct report but a little bit of information.
  • a debug command to view and set your current cardio_acc
  • the ability to adjust starting cardio_acc in professions so that some professions can begin more fit than others
    Then we'll also need some new tests. However, I think this PR as it exists is probably ready for playtesting by the unsuspecting player base.

@wapcaplet
Copy link
Contributor

wapcaplet commented Sep 18, 2021

The "Custom" sidebar stamina graphs aren't quite accurate now with the flexible maximum - but the fix is easy, just remove the "var_max" field from both "stamina_graph" and "stamina_graph_classic", and the graphs will default to using Character::get_stamina_max() for their maxima:

"var_max": 10000,

"var_max": 10000,

P.S. This is resolved now

Copy link
Contributor

@actual-nh actual-nh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Barring unexpected errors, I have the first stage of accommodating sleep better with weariness (as in not in 5-minute blocks of "sleeping" or "not sleeping"). The next stage will be intermediate effects between LIGHT_EXERCISE, NO_EXERCISE, and SLEEP_EXERCISE. After that, mutations.

src/avatar.cpp Outdated Show resolved Hide resolved
src/activity_actor.cpp Outdated Show resolved Hide resolved
src/activity_actor.cpp Outdated Show resolved Hide resolved
void avatar::update_cardio_acc()
{
const int prev_cardio_acc = get_cardio_acc();
const int last_24h_kcal = calorie_diary.begin()->spent;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const int last_24h_kcal = calorie_diary.begin()->spent;
const int last_24h_kcal = calorie_diary.front().spent;

Since you don't need an iterator here, front() would be more appropriate I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if I'm not mistaken, the front of this array is only the spent calories since midnight - not the previous 24 hours.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so:

    if( is_avatar() && ticks_between( from, to, 24_hours ) > 0 ) {
        as_avatar()->advance_daily_calories();
    }

in character_body.cpp and:

void avatar::advance_daily_calories()
{
    calorie_diary.push_front( daily_calories{} );
    if( calorie_diary.size() > 30 ) {
        calorie_diary.pop_back();
    }
}

in avatar.cpp.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I mean - every 24 hours, the front of this list gets a new (empty) calorie log. In order for this update_cardio_acc to work properly, I think it would need to be invoked from Character::update_body just before advance_daily_calories(), to capture the previous day's cardio level.

@@ -1461,6 +1461,19 @@ bool avatar::invoke_item( item *used, const std::string &method )
return Character::invoke_item( used, method );
}

void avatar::update_cardio_acc()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function ever called? I don't see any invocations; this looks like dead code.

Copy link
Member

@I-am-Erk I-am-Erk Sep 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaict that's an error if so, I wonder if it got missed during one of the conflict merges

As I recall the first implementation wasn't going to include the accumulator so I wonder if it's not fully done. Seems like it would be easy to fix

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now the comment from gkarfakis19 on the previous unfinished PR (repeated several times):

I do NOT plan on adding the exercise (cardio_acc) aspect of Cardio in this PR. It will come later.

It looks like it was implemented as requested, just not hooked up to anything. As noted in my other comment on this function, I think it would make sense to invoke it from Character::update_body just before the advance_daily_calories() call; that way it ought to include the previous 24 hours of activity from the diary.

@wapcaplet
Copy link
Contributor

I have been doing some playtesting with this using #51701 to see what's going on with get_cardiofit and get_cardio_acc. This is a freshly created default scenario character:

image

I believe this large random value may be because cardio_acc is uninitialized in the Character constructor.

After saving and loading, the values are much more reasonable:

image

@gkarfakis19
Copy link
Contributor

I just saw this pull request. It's great to see my old code (hopefully it wasn't too messy!) get used by someone more experienced that is able to properly support such a game-changing feature.

Excited to see where this leads!

@@ -482,6 +482,7 @@ Character::Character() :
}
// Only call these if game is initialized
if( !!g && json_flag::is_ready() ) {
set_cardio_acc( base_bmr() / 2 );
Copy link
Contributor

@wapcaplet wapcaplet Sep 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on my comment:

I believe this large random value may be because cardio_acc is uninitialized in the Character constructor.

Here is where the cardio_acc initialization should occur - but it is gated by json_flag::is_ready(), which for whatever reason is false when the Character constructor is called. I believe the reason for stipulating this condition is that base_bmr() needs metabolic_rate_base(), which reads from game_balance.json the setting for PLAYER_HUNGER_RATE (default 1.0) - so it needs this is_ready() flag to be true in order to read that balance setting.

If we can find a way to initialize cardio_acc to a reasonable value without depending on base_bmr(), that should resolve the problem with new characters having wildly random cardio values. Or, find a way to ensure json_flag::is_ready() is true before we reach this constructor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will figure out a solution for this in the new PR.

@wapcaplet
Copy link
Contributor

After consulting with @KorGgenT and @I-am-Erk , I went ahead and started another fresh pull request with a clean rebase from a more recent point in master, including everything from this PR as well as some test cases and new sidebar widgets: #51880

Sorry to play musical chairs on everyone following this feature, but let us continue the review and conversation over there.

@wapcaplet wapcaplet closed this Sep 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[C++] Changes (can be) made in C++. Previously named `Code` <Enhancement / Feature> New features, or enhancements on existing Game: Balance Balancing of (existing) in-game features. Mechanics: Character / Player Character / Player mechanics Mechanics: Effects / Skills / Stats Effects / Skills / Stats
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants