AppleSkin

AppleSkin

241M Downloads

[Suggestion] Estimated healing amount.

MastaNub opened this issue ยท 13 comments

commented

I've had this idea in mind for couple of days. Showing the estimated amount of healing based on saturation/regeneration effects if uninterrupted.
It'd only be displayed if the player is under the effect of regeneration/has enough saturation (and hunger points) to start regaining health (for 1.14).
image

commented

@squeek502 I needed some help, i implemented the calculate health for regeneration, but food level and saturation level calculate is wrong.

commented

@SAGESSE-CN Not sure I understand what you mean. Could you give an example?

commented

@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

commented

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);
commented

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;
}
commented

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.

commented

in branching with foodLevel >= 20 is a non-linear algorithm, which can be more complex if loops are not used

commented

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 ?

commented

I think we can basically do the same thing we do for the hunger HUD overlay:

// very faint background
RenderSystem.color4f(1.0F, 1.0F, 1.0F, alpha * 0.1f);
mc.ingameGUI.blit(matrixStack, x, y, 16 + background * 9, 27, 9, 9);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, alpha);
if (idx < foodLevel + hungerRestored)
mc.ingameGUI.blit(matrixStack, x, y, icon + 36, 27, 9, 9);
else if (idx == foodLevel + hungerRestored)
mc.ingameGUI.blit(matrixStack, x, y, icon + 45, 27, 9, 9);

but where the Y coordinate for the texture is 0 instead of 27.

commented

ok, I'll try that later

commented

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);
commented
2.mov

The will shake when the health is low

commented

Implemented in v2.0.0