@Grond0 I wonder if it's by design to ensure all HP rolls are at least half the hit dice, similar to the "NWN style" tweak. Perhaps the designers thought it would enhance game enjoyability for a majority of players.
@Angulimala it's not a deliberate change by Beamdog. Nor is it a simple algorithm change. It's still possible to get low HP rolls - they just don't occur as often as high ones.
That sure looks like a design change. I'm guessing people were grumbling that their fighters were getting crap HP rolls and being taken out by gibberlings.
How do we know it's not deliberate? If it were deliberate, it would be documented, and there would be some information as to what the game is actually doing to roll hit points.
For a bit of data and a demonstration of what it looks like, here are 140 level-up rolls for a fighter (neutral Con, Core Rules, automatic max HP at level 1 excluded):
For a while there, I thought that rolls of 1 or 2 were completely impossible - but no, they're just rare. All rolls are possible; they're just not equally likely. The standard random number generation a computer uses is an approximate uniform distribution; with that, it's incredibly easy to generate (approximately) equally probable outcomes. Getting anything else requires doing something more complicated, and that's very weird here. Rolling hit points should be simple.
I think what's causing the unexpected results is this rule:
When rolling a simple die for hit points, the engine rolls said die twice and takes the higher of the two rolls. Example of 140 simulated hitpoint rolls using this rule & the engine's rng function:
@JuliusBorisov from what was said in the review by Beamdog before (and what wasn't said in patch notes) this did not appear to be an intended change - though I imagine that reverting to vanilla behavior might not be that popular now people have had so long to get used to this behavior. Will you update your bug report, so the issue can be considered by developers?
Yes, the behavior of rolling twice and using the higher of the two rolls was already considered when the issue was first looked at in 2017, but it is still unclear why this behavior did not occur prior to the v2.0 update because according to the developers' investigation, the way the hitpoints are rolled didn't change between versions. In any case, I've updated the bug report.
I should just leave it there, but the rolling function did change quite significantly between v1.3 and the current version of BG2EE.
See the core part of v1.3's rolling function, (roughly translated from assembly to pseudo code, in part because of compiler optimizations):
roll1 = (call rand) % nSides
roll2 = (call rand) % nSides
if roll1 <= roll2 then
roll3 = (call rand) % nSides
::FIRST_CONDITION::
roll3 = roll3 + 1
if nMinRoll <= roll3 then
roll4 = (call rand) % nSides
roll5 = (call rand) % nSides
if roll4 <= roll5 then
roll6 = (call rand) % nSides
::SECOND_CONDITION::
roll6 = roll6 + 1
(roll6 is the only value that survives)
return roll6
else
(same result as true condition evaluation: why is there a branch here?!)
roll6 = (call rand) % nSides
goto ::SECOND_CONDITION::
end
else
(nMinRoll is the only value that survives)
return nMinRoll
end
else
(same result as true condition evaluation: why is there a branch here?!)
roll3 = (call rand) % nSides
goto ::FIRST_CONDITION::
end
Two things:
It calls rand WAY too many times, and to no real result. roll6 and nMinRoll are the only possible values that survive, so in essence this function "works" as intended, (it returns the result of 1 die roll), but it does a ton of junk operations.
The nMinRoll is checked against roll3, but not against roll6, so in many cases nMinRoll is ignored.
Much better, as you can see. All the junk is gone, with the only thing remaining being the double roll. I'm sure the devs know their way around the engine, but this is the function in question:
CRuleTables::RollHitPoints(CRuleTables *this, int nSides, int nRolls, int nLevel, int nMinRoll, int nModifier)
@Bubb forgive the ignorance of someone who knows nothing about coding. However, looking at the statements above would suggest to me that you shouldn't be able to get a minimum HP roll as the higher_of_two function doesn't just take the higher of two rolls, but adds 1 to it as well (there's then a further comparison with nMinRoll - possibly to set max HPs if that option is set in Game Options?). It is though possible in the game to get minimum HPs. What am I missing?
What you're missing? The mod operator (%) has a range from zero to the second argument minus 1. To get a roll from 1 to nSides, we need (rand % nsides) +1 anyway. Putting the +1 in the higher_of_two function rather than applying it to the rolls first as a human would is confusing, but it works.
is making sure that the minimum allowed value is met. For example, if higher_of_two got set to 1, and yet the minimum allowed roll value is 5 because of godlike constitution, this line would "bump" the roll up to a 5.
In fact, this whole rolling function isn't even used if the Max HP rolls option is active.
For example, if higher_of_two got set to 1, and yet the minimum allowed roll value is 5 because of godlike constitution, this line would "bump" the roll up to a 5.
Even godlike constitution (25) only gives you a minimum of 4 under 2nd edition rules, but I get the point . Thanks for your help.
And what would you consider "fixed"? Using the better of two rolls is something that goes way back - I'm pretty sure it was used to roll HP for the BG2 companions. (See here for the circumstantial evidence)
I suspect the code change noted above restored the originally intended behavior. All that's left, really, is to document it so that people will see it that way.
Whether or not it's really a bug, I'd like to have the option to revert to the old behavior. Perhaps it'd be possible to add it as an option in 2.6? Could someone make a reversion patch like bubb did with the pathfinding code?
Comments
For a bit of data and a demonstration of what it looks like, here are 140 level-up rolls for a fighter (neutral Con, Core Rules, automatic max HP at level 1 excluded): For a while there, I thought that rolls of 1 or 2 were completely impossible - but no, they're just rare. All rolls are possible; they're just not equally likely. The standard random number generation a computer uses is an approximate uniform distribution; with that, it's incredibly easy to generate (approximately) equally probable outcomes. Getting anything else requires doing something more complicated, and that's very weird here. Rolling hit points should be simple.
When rolling a simple die for hit points, the engine rolls said die twice and takes the higher of the two rolls. Example of 140 simulated hitpoint rolls using this rule & the engine's rng function:
Eerily similar to @jmerry's stats, wouldn't you say? And for good measure, here's what the rng is like without that extra roll:
...what we would more or less expect.
@JuliusBorisov from what was said in the review by Beamdog before (and what wasn't said in patch notes) this did not appear to be an intended change - though I imagine that reverting to vanilla behavior might not be that popular now people have had so long to get used to this behavior. Will you update your bug report, so the issue can be considered by developers?
See the core part of v1.3's rolling function, (roughly translated from assembly to pseudo code, in part because of compiler optimizations):
Two things:
Compare that to the core of v2.5.16.6's function:
Much better, as you can see. All the junk is gone, with the only thing remaining being the double roll. I'm sure the devs know their way around the engine, but this is the function in question:
I'll stop talking and let the devs work now
@Bubb forgive the ignorance of someone who knows nothing about coding. However, looking at the statements above would suggest to me that you shouldn't be able to get a minimum HP roll as the higher_of_two function doesn't just take the higher of two rolls, but adds 1 to it as well (there's then a further comparison with nMinRoll - possibly to set max HPs if that option is set in Game Options?). It is though possible in the game to get minimum HPs. What am I missing?
@Grond0: The following line:
is making sure that the minimum allowed value is met. For example, if higher_of_two got set to 1, and yet the minimum allowed roll value is 5 because of godlike constitution, this line would "bump" the roll up to a 5.
In fact, this whole rolling function isn't even used if the Max HP rolls option is active.
Even godlike constitution (25) only gives you a minimum of 4 under 2nd edition rules, but I get the point . Thanks for your help.
I suspect the code change noted above restored the originally intended behavior. All that's left, really, is to document it so that people will see it that way.