[Suggestion] Estimated healing amount.
MastaNub opened this issue ยท 13 comments
@squeek502 I needed some help, i implemented the calculate health for regeneration, but food level and saturation level calculate is wrong.
@SAGESSE-CN Not sure I understand what you mean. Could you give an example?
@SAGESSE-CN Not sure I understand what you mean. Could you give an example?
problem is how to accurately calculate the amount of health after eating a food.
Seems to calculate based on foodLevel and foodSaturationLevel and exhaustion.
example:
health: 0.88
a food of apple: 4 hunger, 0.3 saturation modifier
eat a apple, final health: 4.9
Actually, I might be overcomplicating it. This might work just as well for the hungerLevel < 20
branch:
int availableHungerForRegen = hungerLevel - 17; // 18 or more hunger will regen
float possibleRegenTicks = saturationLevel + availableHungerForRegen;
return (float) Math.ceil(possibleRegenTicks / 1.5f);
I write an version modeled after HungerManager.update
public static float getEstimatedHealthRegenAmount(int foodLevel, float saturationLevel, float exhaustion) {
float health = 0;
while (foodLevel >= 20 && saturationLevel > 0) {
if (exhaustion > 4) {
exhaustion -= 4;
saturationLevel = Math.max(saturationLevel - 1, 0);
}
health += saturationLevel / 6;
exhaustion += saturationLevel;
}
while (foodLevel >= 18) {
if (exhaustion > 4) {
exhaustion -= 4;
foodLevel -= 1;
continue;
}
health += 1;
exhaustion += 6;
}
return health;
}
That works too. I think it should be possible to get rid of the loops, but it might be more effort than it's worth.
in branching with foodLevel >= 20
is a non-linear algorithm, which can be more complex if loops are not used
If need improve the execution efficiency maybe can cache the result of the function, based on parameters.
and maybe our need some image of mask for health ?
I think we can basically do the same thing we do for the hunger HUD overlay:
AppleSkin/java/squeek/appleskin/client/HUDOverlayHandler.java
Lines 209 to 217 in 9b19174
but where the Y coordinate for the texture is 0 instead of 27.
It's complicated. Here's the relevant section of HungerManager.update
:
if (this.exhaustion > 4.0F) {
this.exhaustion -= 4.0F;
if (this.foodSaturationLevel > 0.0F) {
this.foodSaturationLevel = Math.max(this.foodSaturationLevel - 1.0F, 0.0F);
} else if (difficulty != Difficulty.PEACEFUL) {
this.foodLevel = Math.max(this.foodLevel - 1, 0);
}
}
boolean bl = player.world.getGameRules().getBoolean(GameRules.NATURAL_REGENERATION);
if (bl && this.foodSaturationLevel > 0.0F && player.canFoodHeal() && this.foodLevel >= 20) {
++this.foodStarvationTimer;
if (this.foodStarvationTimer >= 10) {
float f = Math.min(this.foodSaturationLevel, 6.0F);
player.heal(f / 6.0F);
this.addExhaustion(f);
this.foodStarvationTimer = 0;
}
} else if (bl && this.foodLevel >= 18 && player.canFoodHeal()) {
++this.foodStarvationTimer;
if (this.foodStarvationTimer >= 80) {
player.heal(1.0F);
this.addExhaustion(6.0F);
this.foodStarvationTimer = 0;
}
}
Here's my start at a function to calculate the healing (untested, just based on my reading of the code above):
/**
* Returns the expected regen amount, assuming starting from 0 exhaustion
* and without any external changes to exhaustion (jumping, sprinting, etc)
* during regen.
* TODO: It might be better to also take the current exhaustion level
* since that can change how much is healed if it starts above 2.
*/
public static float getExpectedHealthRegenAmount(int hungerLevel, float saturationLevel)
{
if (hungerLevel < 18)
return 0.0f;
if (hungerLevel < 20)
{
// every heal tick adds 6.0 exhaustion, which is equal to 1.5 saturation,
// but it's not exactly equivalent, since the first heal tick will bring
// you to 6.0 exhaustion, which will bring saturation down by 1
// and exhaustion down by 4.0 on the next tick. The heal tick after that will
// bring exhaustion to 8.0, though, which will take 2 saturation over the next
// two ticks (and bring exhaustion back to 0.0).
// So, basically, every 2 heal ticks you'll lose 3 saturation.
//
// It ends up looking like this (where the saturation is what you have when you start healing):
// 9 sat = 6hp with 0exh left over
// 8 sat = 6hp with 4exh left over
// 7 sat = 5hp with 2exh left over
// 6 sat = 4hp with 0exh left over
// 5 sat = 4hp with 4exh left over
// 4 sat = 3hp with 2exh left over
// 3 sat = 2hp with 0exh left over
// 2 sat = 2hp with 4exh left over
// 1 sat = 1hp with 2exh left over
float expectedHealthRegen = (float)Math.ceil(saturationLevel / 1.5f);
float leftOverExhaustion = (saturationLevel % 3) * 2;
int availableHungerForRegen = hungerLevel - 17; // 18 or more hunger will regen
// TODO: everything below here could definitely be cleaned up
if (leftOverExhaustion >= 4) {
leftOverExhaustion -= 4;
availableHungerForRegen -= 1;
}
if (availableHungerForRegen > 0) {
boolean willLoseTwoHungerFromFirstRegen = leftOverExhaustion >= 2f;
if (willLoseTwoHungerFromFirstRegen)
expectedHealthRegen += 1;
else
expectedHealthRegen += (float)Math.ceil(availableHungerForRegen / 1.5f);
}
return expectedHealthRegen;
}
else
{
// this is harder, since the health/exhaustion added each
// heal tick varies once saturation is below 6.
// Saturation >= 6 works like the hungerLevel < 20 section above, though.
// TODO
return 0.0f;
}
}
It would be called like:
// Calculate the final hunger and saturation
int foodHunger = modifiedFood.getHunger(heldItem, player);
float foodSaturationIncrement = modifiedFood.getSaturationIncrement(heldItem, player);
int newFoodValue = stats.getFoodLevel() + foodHunger;
float newSaturationValue = stats.getSaturationLevel() + foodSaturationIncrement;
float expectedHealthRegen = getExpectedHealthRegenAmount(newFoodValue, newSaturationValue);