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

Use sqrt of sum of squares to calculate total morale #29396

Merged
merged 4 commits into from
Apr 12, 2019

Conversation

ifreund
Copy link
Contributor

@ifreund ifreund commented Apr 8, 2019

Summary

SUMMARY: Balance "Use sqrt of sum of squares to calculate total morale."

Describe the solution

Implements a new formula for total morale suggested by @mlangsdorf and @I-am-Erk.
Calculates the square root of the sum of negative and positive bonuses squared, then takes the difference.

sqrt( sum_positive_squares ) - sqrt( sum_negative_squares )

The idea behind this is to make the stacking of many positive/negative bonuses less effective.

Describe alternatives you've considered

An alternative formula would be taking the square root after preforming the subtraction

sqrt( sum_positive_squares - sum_negative_squares )

This has the disadvantage of large positive/negative bonuses completely voiding a small opposing bonus.

@ifreund ifreund added Game: Balance Balancing of (existing) in-game features. [C++] Changes (can be) made in C++. Previously named `Code` Mechanics: Character / Player Character / Player mechanics labels Apr 8, 2019
@I-am-Erk
Copy link
Member

I-am-Erk commented Apr 8, 2019

@mlangsdorf deserves all the credit for suggesting the formula. I just repeated him at a good time

@Ilysen
Copy link
Contributor

Ilysen commented Apr 8, 2019

If I'm reading right, this massively reduces all kinds of morale gains. A character with a morale of +120 (elated) would only gain roughly 11 effective morale, and vice-versa for negatives. Wouldn't this make keeping morale up or down effectively pointless, since you gain nothing from keeping it up and lose nothing from letting it stay down?

@ifreund
Copy link
Contributor Author

ifreund commented Apr 8, 2019

Depends on where you got that +120 morale from. If it's all from one bonus you would have

sqrt( 120^2 ) - sqrt( 0^2 ) = 120 morale

if it was from 3 separate +40 bonuses you would have

sqrt( 40^2 + 40^2 + 40^2 ) - sqrt( 0^2 ) = 69 morale

This formula is certainly subject to review and would definitely benefit from some playtesting.

@ifreund ifreund changed the title Use sqrt of sum of squares to calculate total morale [CR] Use sqrt of sum of squares to calculate total morale Apr 8, 2019
@Ilysen
Copy link
Contributor

Ilysen commented Apr 8, 2019

I failed math, be gentle. Alright, that's a lot more reasonable than what I thought was the circumstance. Sorry if I was silly.

@kevingranade
Copy link
Member

kevingranade commented Apr 9, 2019

A little more detail about the rationale for this.
It is currently WAY too easy to get really high levels of morale from stacking bonuses from large numbers of sources, and it's also really easy to get really deep penalties.

The intent of the original system is that each morale instance has some cap applied when it is added, which is applied across any effect that is the same type of bonus. What has happened though is that morale types have proliferated over time, and since they are different types, they end up stacking instead of being merged in any kind of sensible way, so if you can find 10 +10 bonuses, that gets you to +100, which is a counter-intuitive outcome (you don't generally become ecstatic from a bunch of kind of ok things happening), as well as making morale balance impossible to maintain. Side note, in the new system 10 +10 bonuses nets you +31 morale.

How this change works is that your highest bonus is applied directly, and additional bonuses add to it, but only in a marginal way.
If you have one bonus at 10, your end result is 10.
One bonus at 10 and one at 5 gets you to 11.
+2 bonuses at 5 gets you to 12. etc

This is applied to morale penalties as well, so if you have -50 (killed zombie kid) and -20 (got rained on), that nets you -53.

Side note, multiple bonuses or penalties of the same size stack in a fairly significant way.
10, 10 = 14
10, 10, 10 = 17
10, 10, 10, 10 = 20

The major alternative to this is to group together morale effects more aggressively, which means that once a morale type hits its cap, further bonuses have no effect whatsoever.

@thquinn
Copy link
Contributor

thquinn commented Apr 9, 2019

@kevingranade 50 + 20 = 53 is a pretty massive change! Were other exponents considered? Something like (50^1.5 + 20^1.5)^(1/1.5) = ~58?

@Vasyan2006
Copy link
Contributor

Vasyan2006 commented Apr 9, 2019

This PR will decrease morale gain from different sources but will not handle stacking morale from the same action. So eating 2 different slices of pizza will be worse than eating the same pizza 2 times.

As the result it will be more efficient to stick to only one fun source instead of diversifying it. For example eating only meat pies every day instead of cooking something different.

UPD: Well, it was already said before.
Stacking morale can be done as diminishing return, so eating first slice of pizza will add +10, second will be multiplies by 0.75 and add +7.5 morale, third once again multiplied by 0.75 and will give 10x0.75x0.75=+5.62 and so on. Maximum possible bonus after eating infinite number of pizzas is the sum of geometric series and in this case will be 10/(1 - 0.75) = 40

This multiplier will last longer then morale bonus and will slowly recover over time, so sleeping over night will not reset it. Recovery time can last from few hours for hearing music to few days for eating the same food.

@I-am-Erk
Copy link
Member

I-am-Erk commented Apr 9, 2019

I'd say the solution to stacking morale for this is to stop stacking morale at all, and instead either (a) count any instance of a given morale source as independent, so each toaster pastry adds another value into the sum of squares, (b) only count the largest morale source, so eating a toaster pastry and a glass of cold water is a single food bonus, equal to the higher of the two, or (c) just ignore duplicate entries. If you already have a morale bonus from eating a toaster pastry you can't get more morale from eating another.

I prefer (a) or (c). Either make all morale sources suffer the same diminishing returns as intended, or completely remove the fairly ridiculous serving size bonus.

@kevingranade
Copy link
Member

is a pretty massive change!

Yes, and it needs to be. Do you have a rationale for it not being necessary?

For example eating only meat pies every day instead of cooking something different.

Yes, this problem already exists, and the change in this PR does not address it.

@thquinn
Copy link
Contributor

thquinn commented Apr 9, 2019

Do you have a rationale for it not being necessary?

It's definitely necessary. Just offering up the possibility of other exponents.

@I-am-Erk
Copy link
Member

I-am-Erk commented Apr 9, 2019

If tweaking is needed, the exponents could be tweaked yeah. I suppose it could even be an option at some point - specify "morale diminishing returns" where higher numbers cause it to diminish faster and 2 is the default. However after crunching numbers for a while I think sqrt(sumsq) is a pretty solid function that on paper tended to yield pretty reasonable morale numbers, we should test it before fiddling further.

@paroid01
Copy link
Contributor

Morale has a few effects that feel quite important to me when I play:

  1. Morale sets the target number that focus will regenerate to, which can significantly speed up focus gain
  2. Morale gives the player the happy or sad effect.

I haven't seen any discussion of the math that significantly nerfs high morale numbers when calculating the focus target
player.cpp focus target math

I feel that this needs to be considered when changing the formula for calculating the morale number.

Also, will it be possible to obtain the Elated level of the happy buff after applying this change? If not, I would like the scaling intensity factor of the happy/sad effects to be changed to make it possible.
effects.json happy effect

@I-am-Erk
Copy link
Member

I-am-Erk commented Apr 10, 2019

We're all well aware of the role of morale with focus.

It is basically a given that it's still possible to be elated. It is just harder to accomplish by stacking dozens of minor bonuses on top of each other, which is working as intended. For questions like that it's much more appropriate to apply it to the game and get playtest data than to try to guess what people are going to use and crunch numbers for hours.

@Inglonias
Copy link
Contributor

I played around with this in a spreadsheet and it definitely dampens the effect of several small bonuses added together.
That said, it also massively increases the effect that major positive and negative bonuses have on you. God help you if you're not a psychopath and you kill an innocent NPC, because you aren't going to be doing much of anything for a very long time, no matter how many Toast-Ems you've got.

Calculation.xlsx

@mlangsdorf
Copy link
Contributor

The morale penalty for killing an innocent NPC is supposed to be crippling, not something you shake off by eating a box of pop-tarts and having a couple of cans of soda.

@I-am-Erk
Copy link
Member

I-am-Erk commented Apr 11, 2019

In fact, being able to shrug off a massive morale debuff by eating a bit of junk food is exactly the sort of thing this is meant to reduce, so that is very much working as intended.

Bear in mind that the reverse is also true. Penalties for a bunch of trivial nuisances will not add up to crippling depression.

use a sqrt of a sum of positive/negative squares
level = sqrt( postive_sum ) - sqrt( negative_sum )
@ifreund
Copy link
Contributor Author

ifreund commented Apr 11, 2019

Stacking of the the same bonus is now also determined by the sqrt of sum of squares formula.

I think the outcome of this discussion is pretty clear at this point, and that these changes accurately fit the desired behavior of morale. Therefore I'm removing the [CR] tag and calling this ready for merge.

@ifreund ifreund changed the title [CR] Use sqrt of sum of squares to calculate total morale Use sqrt of sum of squares to calculate total morale Apr 11, 2019
@ifreund
Copy link
Contributor Author

ifreund commented Apr 11, 2019

Note that the application of the formula to stacking morale of the same bonus is in practice a little different, since there are only ever two values involved: the current bonus and the additional bonus.

When a bonus is added it is merged into the current bonus for the morale type using the formula. Therefore, if a bonus of equal magnitude is stacked repeatedly, the morale after x stacks (assuming no time decay has occurred) would be magnitute * sqrt( x ), which I think fits well here.

src/morale.cpp Outdated Show resolved Hide resolved
- apply the +/- sign after taking the sqrt
- constify a thing
- correct "temporary morale (food)" test values
- add new "stacking of bonuses" test
@ifreund
Copy link
Contributor Author

ifreund commented Apr 11, 2019

Fixed test values and added a new test case for stacking bonuses.
Also corrected a small mistake in the calculation of stacking bonuses of the same type.

@kevingranade kevingranade merged commit 375bc6c into CleverRaven:master Apr 12, 2019
@ifreund ifreund deleted the morale-level-formula branch April 13, 2019 09:03
@kevingranade
Copy link
Member

This pull request has been mentioned on Cataclysm: Dark Days Ahead. There might be relevant details there:

https://discourse.cataclysmdda.org/t/mp3-morale-is-only-1/19676/4

@l29ah
Copy link
Contributor

l29ah commented Apr 25, 2019

Morale loss from reading books still stacks :(

@ifreund
Copy link
Contributor Author

ifreund commented Apr 25, 2019

Morale loss from reading books still stacks :(

That's intentional since BOOK_MORALE is set with a cap, similarly to music

@I-am-Erk
Copy link
Member

That also means morale gain from reading books stacks, which buffs all books as entertainment quite a bit

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` Game: Balance Balancing of (existing) in-game features. Mechanics: Character / Player Character / Player mechanics
Projects
None yet
Development

Successfully merging this pull request may close these issues.