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

Hunger and starvation improvements #24210

Merged
merged 20 commits into from Aug 5, 2018

Conversation

Projects
None yet
@bolaft
Copy link
Contributor

commented Jul 5, 2018

This PR addresses a few long-standing issues with hunger (see #11550, #11514):

  • If you don't eat for a week, you do not need a week's worth of food to be satiated.
  • It's not possible to recover instantly from severe starvation just by having a very large meal.
  • It should not be possible to eat a week's worth of food in one sitting.

In order to solve this, this PR separates the starvation logic from the hunger stat and links it to a newly introduced hidden stat, starvation. The process is simple: hunger is now capped at 400 (just above 'famished'). When hunger is at its maximum, starvation slowly increases. When hunger is NOT maxed out, starvation slowly decreases. Stat penalties and death are now tied to starvation, not just hunger. In terms of realism one could think of the starvation stat as an abstraction of body fat and how much nutrients are in the PC's system, while hunger is merely an indicator of the fullness of the stomach.

Gameplay effects

These relatively small changes have a few interesting gameplay effects:

  1. Starving characters can't eat thirty full meals in one sitting anymore. The amount of food a character can eat at once is limited to a much more reasonable amount. For example, previously it was technically possible to eat 500 bananas in a row. Now 34 bananas will be enough to fill up even the most banana-starved character.
  2. Starvation effects remain for a while. Without this update, eating enough food can instantly bring a character from near death to perfect condition. With this update, a character who starved for a long time will not instantly lose all negative modifiers. Instead, the penalties will diminish and disappear gradually. Recovery time is roughly equal to the time spent starving (depending on metabolism modifiers).
  3. Rationing food in times of scarcity now makes sense. For instance, previously a character who planned to hide for two weeks (for example, to get rid of a meth addiction) with only one week's worth of food had no incentive to ration it: whether all the food was eaten in the first week or spread across the two weeks made no difference in the end. With this update, it is advisable to spread out meals to slow down the starvation meter.

Display

The player can see its character's starvation penalties in the character's page. They are displayed under the "Malnourished" effect. On the sidebar, the "hungry", "very hungry" and "famished" messages appear the same as before, depending on hunger. However, the "near starving" and "starving!" messages only appear if hunger is capped AND starvation is high enough. Therefore, a character with a full belly won't see those messages appear, even if its starvation is high. This reflects the fact that the character is not hungry, even though he or she still suffers from malnutrition penalties, and the player can't do anything about it at the moment anyway. As for the 'Hunger' speed modifier display, in order to avoid confusing players whose PC is not hungry (but still suffers from starvation), the label is changed to 'inanition'.

screen_cdda_pr

Balance

This PR does not affect established numbers. Characters will die from starvation exactly as fast as before (unless they ration food), and the speed, strength, dexterity etc. penalties from lack of food remain the same.

These changes should not affect balance much. They make the game a little bit easier by making it possible to ration food to alleviate starvation penalties, but they also make the game a little bit harder since it now takes some time to get rid of those penalties.

Justification for the choice of 400 as a hunger cap

The only number I introduced in this PR is the 400 cap for nutrition. Here's why:

  1. It's just above the current threshold for 'famished', which is the last threshold before the concept of starvation is introduced in the game's messages.
  2. The world record for most bananas eaten in a row is 30. 30 bananas times 12 nutrition points per banana equals 360.
  3. The game must offer players the opportunity to break the world's record for most bananas eaten in one sitting (pretty sure fans of the game would riot otherwise).
  4. Current hunger thresholds are all rounded to the nearest hundred. 360 rounded to the nearest hundred is 400.

Note

This is the first time I look into CDDA's code, and I don't usually use C++, so all comments and suggestions about the code are welcome. I tested the changes in game, they seem to work fine and everything behaves as expected, but I'd appreciate it if a CDDA veteran double checked to make sure I didn't break anything.

Updates

See comments.

  • Added "Severely Malnourished" effect.
  • Added descriptive text to "Malnourished" and "Severely Malnourished".
  • Lowered hunger cap from 400 to 300.
  • Lowered "famished" threshold from 300 to 250 (otherwise it would never display).
@Night-Pryanik

This comment has been minimized.

Copy link
Member

commented Jul 5, 2018

Very interesting!

@DracoGriffin

This comment has been minimized.

Copy link
Member

commented Jul 5, 2018

Great addition, extremely clear reasoning and details. Very good work.

edit: I think changing "inanition" would be a good idea, but honestly, I can't think of anything better. It is quite appropriate. However, I feel outside of translations, it's going to be a bit difficult for ESL people having a difficult time understand what it means (that is, it's not readily accessible, although honestly, I bet there will be English speakers that won't know the term either). Which can be fine, it's a good way to learn vocabulary and there are other things, but meh. This is really the only nitpick I have for this PR. Eagerly looking forward to it.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 5, 2018

Thanks for the feedback!

Yeah I'm not too happy with "inanition" either actually. I didn't think of ESL people though, even though I am one myself. Initially I intended to use two differents modifiers, a "Hunger" speed modifier and a "Malnourished" modifier. However the hunger_speed_penalty() function is not linear, so hunger_speed_penalty(get_hunger()) + hunger_speed_penalty(get_starvation()) does not equal hunger_speed_penalty(get_starvation() + get_hunger()).

I could tweak it so there are two separate speed modifiers, but I think I would have a hard time getting exactly the same modifier values as before.

Or, I could just change "inanition" to something else, but I'm not sure what. "lack of food" maybe?

@draeath

This comment has been minimized.

Copy link
Contributor

commented Jul 5, 2018

Could this be integrated with nutrition instead? They are already added/removed from food/lack-of-food, and decoupled from hunger itself. It would make sense that a "calorie" nutrient be added and used for this, instead of a new (hidden) statistic.

It would also make sense from a data perspective - food would have, in addition to nutrients, a caloric value. So long as this is kept reasonable to real values, this would make it intuitive for folks adding new foods.

I wonder if you might even scale the depletion based on player activity, as well.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 5, 2018

@draeath Maybe I'm not understanding your suggestion right, but I don't really see how using the nutrition system for this would address the starvation issues? If "calories" became a nutrient similar to "Vitamin C" for example, it would not really solve the issue of the "bottomless pit stomach", nutrition needs are linear currently. So if a character misses a week's worth of calories, they would need to eat a week's worth of calories to go back to normal. Moreover, I don't think the nutrition system allows for gradual recovery: if a character has scurvy, taking three multivitamins fixes it instantly, and the same would go for calories: taking a week's worth of food at once would be required to fix it. And if the hunger is capped, then it would be impossible to keep up with caloric nutritional need, which would keep going down while the character is unable to eat any more.

Moreover, I think I remember reading on a comment thread that nutrition values are already based on calories, so adding a new "calorie nutrient" would be redundant. Furthermore, adding a new nutrient type that would need a value for every single foodstuff sounds like a lot of JSON editing work to me, and would mean that every mod adding food items would need to be updated, doesn't it?

Again I'm not sure I understood your suggestion correctly, please correct me if I'm wrong.

@Vasyan2006

This comment has been minimized.

Copy link
Contributor

commented Jul 5, 2018

Nice addition, but I have some comments.
400 nutritions is too much for a hunger cap. Default character needs 288 nutritions per day. It is technically possible to eat 1.5 day rations in few minutes and not to vomit from overeating, but most of nutritions will be lost, because digestional tract cannot work at such speed. This value must be set by something like stomach_capacity().
Overeating after long starving can be deadly. The weakened body cannot handle huge ammount of proteins, fats and sugar so this overloading will signifficantly damage methabolism even more. But not sure if such level of realism is required.
Bananas are certified to be used only as arbitrary dose of ionizing radiation (Banana equivalent dose). Using them as a metric of nutritions may lead to misunderstandings.

UPD: their is function get_calories() to get calories from nutritions

/** 1 nutr ~= 8.7kcal (1 nutr/5min = 288 nutr/day at 2500kcal/day) */

@nexusmrsep

This comment has been minimized.

Copy link
Contributor

commented Jul 5, 2018

Fantastic! Quick idea: Malnourished effect (and other effect levels if you plan them) should also have some descriptive text as a feedback for the player who might think "Satiated" for hunger and "Malnourished" for starvation as a potential bug.

Something along the lines of: "You haven't eaten for so long, that your body is weak, and you are a shadow of your former self. Only time and regular meals will help you recover."

@kevingranade

This comment has been minimized.

Copy link
Member

commented Jul 5, 2018

Neat, slightly different angle on it than I had anticipated, but a good one.
A few previous discussions on the subject:
track nutrition seperately from hunger
revamp to hunger
Energy, satiation, and hydration

tl;dr
three aspects of hunger:

  1. How hungry do I feel.
    This one normally acts as a signal of "you need to eat" or "no need to eat at the moment", but can be subverted by e.g. stimulants or other effects. Separating this one from general hunger isn't addressed in this PR, and that's ok.
  2. How much food can I eat/how much stuff is in my stomach.
    This one acts as a hard cap on how much the player can eat at a time, and also does things like mediate how much food is lost due to vomiting or similar. Currently entangled with both other aspects of hunger.
  3. How much energy reserves do I have.
    Called starvation in this PR, tracks depletion of energy and nutrition reserves and bodily deterioration.
@Mecares

This comment has been minimized.

Copy link
Contributor

commented Jul 6, 2018

I am not a expert, but is possible intake of calories really capped by amount of calories in reality or is it more a question of volume of food i can fit into my digestive system? Would making the choice between high energy density and low energy density food in a starving situation matter?

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 6, 2018

Ok, many good suggestions/comments in this thread. So to sum up:

  • The "Inanition" label should be changed to something else (@DracoGriffin)
  • Integrating this with the nutrition system instead (@Dreath)
  • Lowering the 400 threshold (@Vasyan2006)
  • Overeating after a long starvation period can be deadly (@Vasyan2006)
  • Adding descriptive text for the "Malnourished" effect (@nexusmrsep)
  • 'Hunger' as in, the feeling of hunger, and how much food is in the stomach are different things (@kevingranade)
  • Calorie intake might be based on nutritional value, while max stomach content might be based on volume (@Mecares)

I added a commit to the PR based on @nexusmrsep's suggestions:

  • The "Malnourished" effect now has descriptive text ("Your body is weakened by starvation. Only time and regular meals will help you recover."). I didn't use the "You haven't eaten for so long" part of the suggestion because it is possible to be malnourished even though the character just ate a meal.
  • I added a "Severely Malnourished" effect. It works the same, but shows up when starvation is very advanced. The text message reflects this ("Your body is severely weakened by starvation. You might die if you don't start eating regular meals!").

A couple questions:

  • Do I change the "Inanition" speed modifier label? To what?
  • Do I lower the Hunger cap? To what?
  • Should I make special rules for special situations? At the moment, I made it so taking mutagen affects starvation directly if hunger is high enough, but there are other cases in which such effects could be debatable (such as plant mutants etc.)

Some of the other suggestions might make for an interesting separate PR:

  • Separating "hunger" from "stomach contents". Hunger would only be a subjective indicator, subject to special effects (like marijuana), only used for display. Lack of actual food in the stomach would be the actual trigger for starvation and penalties.
  • Stomach content would be limited by food volume, rather than nutritional value, making it smart to eat high density foods before leaving for a long trip.
  • Maybe other realism improvements, but I'm not sure it would be the best idea. It would add too much complexity for too little gameplay benefits IMO.
@DracoGriffin

This comment has been minimized.

Copy link
Member

commented Jul 6, 2018

Clarification is probably warranted in here regarding the difference between food nutritional value (ie., vitamins) and food nutrition which isn't the best name for "calories".

I am not a expert, but is possible intake of calories really capped by amount of calories in reality or is it more a question of volume of food i can fit into my digestive system? Would making the choice between high energy density and low energy density food in a starving situation matter?

Volume of food is a super slippery slope and extremely variable in human beings already, notwithstanding mutations and CBMs and everything else.

In terms of starvation recovery (like, actual starvation), is that you can harm yourself from overindulging (refeeding syndrome), even though "overindulgence" amount may look like a normal size meal for other people.

So for example, my first link shows the average human stomach can stretch to 4 liters, yet how much could a starved person's stomach contain? Also, then you gotta consider extreme cases like this one. So how will this all affect survivors with traits/mutations that affect metabolism and such -- do bovine post-thresholds still only eat this volume or something else?

You bring up a good point. The nutritional and caloric density value of a food is also important -- some foods would be much more suitable for starvation recovery and such.

Anywho, there's my shotgun blast of information. And lastly, an interesting food calculator that spits out more information you could ever want. (which just calls from USDA Food Composition Databases but converts some of the information into a more ... digestible format.)

For funsies, here's what one liter (1 L) of sliced bananas offers:

From kilocalories(kcal) kilojoule(kJ)
Carbohydrate 521.31 2 181.16
Fat 17.51 73.27
Protein 23.22 97.15
Other 2.23 9.32
Total 564.27 2 360.91
NutrientFind foods rich in specific nutrients Unit Value per634.01 g
Proximates
Water g 474.94
Energy kcal 564
Protein g 6.91
Total lipid (fat) g 2.09
Carbohydrate, by difference g 144.81
Fiber, total dietary g 16.5
Sugars, total g 77.54
Minerals
Calcium, Ca mg 32
Iron, Fe mg 1.65
Magnesium, Mg mg 171
Phosphorus, P mg 139
Potassium, K mg 2270
Sodium, Na mg 6
Zinc, Zn mg 0.95
Vitamins
Vitamin C, total ascorbic acid mg 55.2
Thiamin mg 0.197
Riboflavin mg 0.463
Niacin mg 4.216
Vitamin B-6 mg 2.327
Vitamin B-12 μg 0
Vitamin A, RAE μg 19
Vitamin A, IU IU 406
Lipids
Fatty acids, total saturated g 0.71
Fatty acids, total monounsaturated g 0.203
Fatty acids, total polyunsaturated g 0.463
Cholesterol mg 0
Other
Caffeine mg 0
@DracoGriffin

This comment has been minimized.

Copy link
Member

commented Jul 6, 2018

The "Inanition" label should be changed for something else (@DracoGriffin)

Honestly, keep it because it is extremely accurate (exhaustion from lack of nourishment; starvation). I was just saying out of all the work you did, that was really my only "non-issue" with it. But there really isn't a better descriptor that doesn't obfuscate other similar terms already being used (just like Malnourished can be confused with lack of food, lack of eating, or lack of vitamins, but we really should make these terms more stringent -- starvation and adjectives, hungry and adjectives, and hypovitaminosis and adjectives for more specific vitamin deficiencies, respectively -- malnourished could fall under starvation or hypovitaminosis, but then it'd be more appropos to use the term malnutrition).

@DracoGriffin

This comment has been minimized.

Copy link
Member

commented Jul 6, 2018

Yes maybe it would be better to do away with it, but the reason why I used "malnourished" rather than "starving" or "starvation" is because I felt the latter implies the character is currently starving, rather than possibly suffering from the effects of a previous period of starvation.

Keep those too, Malnourished/Severely Malnourished also make sense in this context. We already have hypervitaminosis and the appropriate hypovits here.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 6, 2018

I removed this comment previously but since you answered it I put it back so the conversation makes sense:

just like Malnourished can be confused with lack of food, lack of eating, or lack of vitamins, but we really should make these terms more stringent -- starvation and adjectives, hungry and adjective (...)

Yes maybe it would be better to do away with it, but the reason why I used "malnourished" rather than "starving" or "starvation" is because I felt the latter implies the character is currently starving, rather than possibly suffering from the effects of a previous period of starvation.

But I can change it.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 6, 2018

gorgon-ghprb and continuous-integration/appveyor/pr fail, what do those check for? What are "astyle regressions"? gorgon-ghprb finds weird errors, like:

..\src\sidebar.cpp(318): error C2146: syntax error: missing ';' before identifier 'get_starvation' [C:\Projects\Cataclysm-DDA\msvc-full-features\Cataclysm.vcxproj]

But the line is :

    if( get_hunger() >= 400 and get_starvation() > 2400 ) {

Putting a ; before get_starvation would make no sense.

(I have no trouble compiling on my system).

@DracoGriffin

This comment has been minimized.

Copy link
Member

commented Jul 6, 2018

  1. Do I change the "Inanition" speed modifier label? To what?
  2. Do I lower the Hunger cap? To what?
  3. Should I make special rules for special situations? At the moment, I made it so taking mutagen affects starvation directly if hunger is high enough, but there are other cases in which such effects could be debatable (such as plant mutants etc.)
  1. So, keep "Inanition". That's done.

  2. I'd suggest changing the hunger cap to 300 due to values being chosen on this line of information/code:

/** 1 nutr ~= 8.7kcal (1 nutr/5min = 288 nutr/day at 2500kcal/day) */

so that 12 nutrition can fluctuate for the player in terms of under/overeating if they choose.

And lastly, there are quite a few variables so I don't know them all but one that might be an issue is metabolism_modifier.

  1. I'd suggest making adjustments that the hunger cap is increased half of the metabolism_modifier value so characters with those traits don't end up rapidly starving to death. I am probably misunderstanding something, though.
@@ -315,9 +315,9 @@ void player::disp_status( const catacurses::window &w, const catacurses::window
}

wmove( w, sideStyle ? 1 : 2, 0 );
if( get_hunger() > 2800 ) {
if( get_hunger() >= 400 and get_starvation() > 2400 ) {

This comment has been minimized.

Copy link
@ZhilkinSerg

ZhilkinSerg Jul 6, 2018

Contributor

Use && instead of and here as logical AND.

wprintz( w, c_red, _( "Starving!" ) );
} else if( get_hunger() > 1400 ) {
} else if( get_hunger() >= 400 and get_starvation() > 1000 ) {

This comment has been minimized.

Copy link
@ZhilkinSerg

ZhilkinSerg Jul 6, 2018

Contributor

Use && instead of and here as logical AND.

This comment has been minimized.

Copy link
@bolaft

bolaft Jul 6, 2018

Author Contributor

Will do.

This comment has been minimized.

Copy link
@ZhilkinSerg

ZhilkinSerg Jul 6, 2018

Contributor

This should be one of the reasons why VS build is failing in AppVeyor - VC++ doesn't respect standards.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 7, 2018

Well if we limit this PR to basic functionality (which I think is a good idea), then it's pretty much done, I don't see what else needs to change.

I tested it for a several hours on my current playthrough without issues. I also did several tests on the debug mod. The only odd thing I noticed was that gradual changes were slightly off (I tested it by giving a survivor a lot of fatigue and making him sleep for over three days):

Without PR:

  • Hunger 475
  • Thirst 476

With PR:

  • Hunger 300 + Starvation 180 = 480 (=> 1% difference)
  • Thirst 497 (=> 4% difference)

I looked into it and I'm not sure what is causing these differences, especially concerning thirst since I didn't touched thirst at all. Debug messages show the same metabolic rate and the same thirst rate. I'll try to figure it out by displaying more variables as soon if I have time today. Edit: not a bug, it's normal behaviour.

Also, gorgon-ghprb fails because of "astyle regressions", but I don't know what that means?

p->mod_thirst( m_category.mutagen_thirst );
p->mod_fatigue( m_category.mutagen_fatigue );

if ( m_category.mutagen_hunger + p->get_hunger() > 300 ) {

This comment has been minimized.

Copy link
@nexusmrsep

nexusmrsep Jul 7, 2018

Contributor

astyle doesn't want white spaces between 'if' and '('

@nexusmrsep

This comment has been minimized.

Copy link
Contributor

commented Jul 7, 2018

Astyle regression means code style is not in standard for this repository. See CODE_STYLE.md in docs.
It's sometimes hard to see style "errors" by a visual inspection, so AStyle comes to the rescue.
Jenkins' log shows what files are affected.

Download your branch locally so you have it on you harddrive. From a command line run AStyle binnary from tools folder using CODE_STYLE.md parameters and target files: src/iuse.cpp, src/player_display.cpp, src/consumption.cpp. Inspect changes and commit them. I personaly use GitHub Desktop to control my local branches, as it has intuitive visual interface.

[edit: actualy you might want to download AStyle first... silly me]

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 8, 2018

Ok I will do that soon, thanks for the explanation. I'm a bit busy this weekend but worst case scenario I'll push a commit monday.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 8, 2018

Fixed code style, all checks pass.

@cainiaowu

This comment has been minimized.

Copy link
Contributor

commented Jul 8, 2018

Starvation texts need to be translateable.

@AMurkin

This comment has been minimized.

Copy link
Contributor

commented Jul 9, 2018

In the src/player_display.cpp, I suppose.

@cainiaowu

This comment has been minimized.

Copy link
Contributor

commented Jul 9, 2018

Yeah, currently on a patato phone so I cant comment on specific line right now.

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 9, 2018

New commit: made starvation descriptions translatable.

@bolaft bolaft changed the title [CR] Hunger and starvation improvements Hunger and starvation improvements Jul 9, 2018

@bolaft

This comment has been minimized.

Copy link
Contributor Author

commented Jul 10, 2018

I did more testing, the small discrepancies I found earlier are not an issue, there is an element of randomness to hunger/thirst increase. Two tests on the version of the game without this PR also give slightly different numbers (~5% difference). So as far as I can tell everything behaves as expected, I didn't find any bugs.

I think this PR is ready.

ZhilkinSerg added some commits Aug 5, 2018

@ZhilkinSerg ZhilkinSerg merged commit 8e02486 into CleverRaven:master Aug 5, 2018

0 of 3 checks passed

continuous-integration/appveyor/pr Waiting for AppVeyor build to complete
Details
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
gorgon-ghprb Build triggered for merge commit.
Details
@Night-Pryanik

This comment has been minimized.

Copy link
Member

commented Aug 15, 2018

how do i disable this in my game

You make your own fork, you get compiler, you disable all that you don't like, you compile the game.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.