Get hearts working again
This commit is contained in:
parent
bbf4e41abc
commit
80720c7fbf
3 changed files with 221 additions and 129 deletions
|
@ -26,8 +26,6 @@ import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
||||||
import net.minecraftforge.client.event.RenderGuiOverlayEvent;
|
import net.minecraftforge.client.event.RenderGuiOverlayEvent;
|
||||||
import net.minecraftforge.client.gui.overlay.ForgeGui;
|
import net.minecraftforge.client.gui.overlay.ForgeGui;
|
||||||
import net.minecraftforge.client.gui.overlay.GuiOverlayManager;
|
import net.minecraftforge.client.gui.overlay.GuiOverlayManager;
|
||||||
|
@ -41,6 +39,8 @@ import java.util.Random;
|
||||||
public class HeartsRenderer {
|
public class HeartsRenderer {
|
||||||
private static final ResourceLocation ICON_HEARTS = new ResourceLocation(WatchMyDurability.MODID,
|
private static final ResourceLocation ICON_HEARTS = new ResourceLocation(WatchMyDurability.MODID,
|
||||||
"textures/gui/hearts.png");
|
"textures/gui/hearts.png");
|
||||||
|
private static final ResourceLocation ICON_ABSORB = new ResourceLocation(WatchMyDurability.MODID,
|
||||||
|
"textures/gui/absorb.png");
|
||||||
private static final ResourceLocation ICON_VANILLA = Gui.GUI_ICONS_LOCATION;
|
private static final ResourceLocation ICON_VANILLA = Gui.GUI_ICONS_LOCATION;
|
||||||
|
|
||||||
private final Minecraft mc = Minecraft.getInstance();
|
private final Minecraft mc = Minecraft.getInstance();
|
||||||
|
@ -64,64 +64,12 @@ public class HeartsRenderer {
|
||||||
* @param width Width to draw
|
* @param width Width to draw
|
||||||
* @param height Height to draw
|
* @param height Height to draw
|
||||||
*/
|
*/
|
||||||
private void blit(GuiGraphics matrixStack, int x, int y, int textureX, int textureY, int width, int height) {
|
private void blit(GuiGraphics matrixStack, int x, int y, int textureX, int textureY, int width, int height, ResourceLocation resource) {
|
||||||
matrixStack.blit(ICON_HEARTS, x, y, textureX, textureY, width, height);
|
matrixStack.blit(resource, x, y, textureX, textureY, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderHearts(GuiGraphics pGuiGraphics, Player pPlayer, int pX, int pY, int pHeight, int pOffsetHeartIndex, float pMaxHealth, int pCurrentHealth, int pDisplayHealth, int pAbsorptionAmount, boolean pRenderHighlight) {
|
|
||||||
Random random = new Random();
|
|
||||||
HeartType hearttype = HeartType.forPlayer(pPlayer);
|
|
||||||
int offsetX = 9 * (pPlayer.level().getLevelData().isHardcore() ? 5 : 0);
|
|
||||||
int maxHearts = Mth.ceil((double)pMaxHealth / 2.0);
|
|
||||||
int absorbHearts = Mth.ceil((double)pAbsorptionAmount / 2.0);
|
|
||||||
int maxHealth = maxHearts * 2;
|
|
||||||
|
|
||||||
for(int i1 = maxHearts + absorbHearts - 1; i1 >= 0; --i1) {
|
|
||||||
int j1 = i1 / 10;
|
|
||||||
int k1 = i1 % 10;
|
|
||||||
int x = pX + k1 * 8;
|
|
||||||
int y = pY - j1; //* pHeight;
|
|
||||||
int row = y * pHeight;
|
|
||||||
|
|
||||||
|
|
||||||
if (pCurrentHealth + pAbsorptionAmount <= 4) {
|
|
||||||
y += random.nextInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i1 < maxHearts && i1 == pOffsetHeartIndex) {
|
|
||||||
y -= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.renderHeart(pGuiGraphics, HeartType.CONTAINER, x, y, offsetX, pRenderHighlight, false, 0);
|
|
||||||
int j2 = i1 * 2;
|
|
||||||
boolean flag = i1 >= maxHearts;
|
|
||||||
if (flag) {
|
|
||||||
int k2 = j2 - maxHealth;
|
|
||||||
if (k2 < pAbsorptionAmount) {
|
|
||||||
boolean halfHeart = k2 + 1 == pAbsorptionAmount;
|
|
||||||
this.renderHeart(pGuiGraphics, hearttype == HeartType.WITHERED ? hearttype : HeartType.ABSORBING, x, y, offsetX, false, halfHeart, row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean flag3;
|
|
||||||
if (pRenderHighlight && j2 < pDisplayHealth) {
|
|
||||||
flag3 = j2 + 1 == pDisplayHealth;
|
|
||||||
this.renderHeart(pGuiGraphics, hearttype, x, y, offsetX, true, flag3, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j2 < pCurrentHealth) {
|
|
||||||
flag3 = j2 + 1 == pCurrentHealth;
|
|
||||||
this.renderHeart(pGuiGraphics, hearttype, x, y, offsetX, false, flag3, row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void renderHeart(GuiGraphics pGuiGraphics, HeartType pHeartType, int pX, int pY, int pYOffset, boolean pRenderHighlight, boolean pHalfHeart, int row) {
|
|
||||||
pGuiGraphics.blit(ICON_HEARTS, pX, pY, pHeartType.getX(row, pHalfHeart, pRenderHighlight), pHeartType.getY(), 9, 9);
|
|
||||||
}
|
|
||||||
/* HUD */
|
/* HUD */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event listener
|
* Event listener
|
||||||
*
|
*
|
||||||
|
@ -152,91 +100,235 @@ public class HeartsRenderer {
|
||||||
if (!(mc.gui instanceof ForgeGui gui) || mc.options.hideGui || !gui.shouldDrawSurvivalElements()) {
|
if (!(mc.gui instanceof ForgeGui gui) || mc.options.hideGui || !gui.shouldDrawSurvivalElements()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Entity renderViewEnity = this.mc.getCameraEntity();
|
||||||
|
if (!(renderViewEnity instanceof Player player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gui.setupOverlayRenderState(true, false);
|
||||||
|
|
||||||
|
this.mc.getProfiler().push("health");
|
||||||
|
|
||||||
// extra setup stuff from us
|
// extra setup stuff from us
|
||||||
int X = this.mc.getWindow().getGuiScaledWidth() / 2 - 91;
|
int left_height = gui.leftHeight;
|
||||||
int Y = this.mc.getWindow().getGuiScaledHeight() / 2 + 91;
|
int width = this.mc.getWindow().getGuiScaledWidth();
|
||||||
|
int height = this.mc.getWindow().getGuiScaledHeight();
|
||||||
int updateCounter = this.mc.gui.getGuiTicks();
|
int updateCounter = this.mc.gui.getGuiTicks();
|
||||||
long healthBlinkTime = this.mc.gui.healthBlinkTime;
|
|
||||||
int height = Math.max(10 - (Y - 2), 3);
|
|
||||||
int offset = -1;
|
|
||||||
Player player = Minecraft.getInstance().player;
|
|
||||||
int lastHealth = Minecraft.getInstance().gui.lastHealth;
|
|
||||||
int displayHealth = Minecraft.getInstance().gui.displayHealth;
|
|
||||||
boolean flag = healthBlinkTime > (long)updateCounter && (healthBlinkTime - (long)updateCounter) / 3L % 2L == 1L;
|
|
||||||
|
|
||||||
float maxHealth = Math.max((float)player.getAttributeValue(Attributes.MAX_HEALTH), (float)Math.max(displayHealth, lastHealth));
|
// start default forge/mc rendering
|
||||||
int absorb = Mth.ceil(player.getAbsorptionAmount());
|
// changes are indicated by comment
|
||||||
|
|
||||||
if (player.hasEffect(MobEffects.REGENERATION)) {
|
int health = Mth.ceil(player.getHealth());
|
||||||
offset = updateCounter % Mth.ceil(maxHealth + 5.0F);
|
boolean highlight = this.healthUpdateCounter > (long) updateCounter
|
||||||
|
&& (this.healthUpdateCounter - (long) updateCounter) / 3L % 2L == 1L;
|
||||||
|
|
||||||
|
if (health < this.playerHealth && player.invulnerableTime > 0) {
|
||||||
|
this.lastSystemTime = Util.getMillis();
|
||||||
|
this.healthUpdateCounter = (updateCounter + 20);
|
||||||
|
} else if (health > this.playerHealth && player.invulnerableTime > 0) {
|
||||||
|
this.lastSystemTime = Util.getMillis();
|
||||||
|
this.healthUpdateCounter = (updateCounter + 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHearts(event.getGuiGraphics(), player, X, Y, height, offset, maxHealth, lastHealth, Minecraft.getInstance().gui.displayHealth, absorb, flag);
|
if (Util.getMillis() - this.lastSystemTime > 1000L) {
|
||||||
|
this.playerHealth = health;
|
||||||
|
this.lastPlayerHealth = health;
|
||||||
|
this.lastSystemTime = Util.getMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playerHealth = health;
|
||||||
|
int healthLast = this.lastPlayerHealth;
|
||||||
|
|
||||||
|
AttributeInstance attrMaxHealth = player.getAttribute(Attributes.MAX_HEALTH);
|
||||||
|
float healthMax = attrMaxHealth == null ? 0 : (float) attrMaxHealth.getValue();
|
||||||
|
float absorb = Mth.ceil(player.getAbsorptionAmount());
|
||||||
|
|
||||||
|
// CHANGE: simulate 10 hearts max if there's more, so vanilla only renders one
|
||||||
|
// row max
|
||||||
|
healthMax = Math.min(healthMax, 20f);
|
||||||
|
health = Math.min(health, 20);
|
||||||
|
absorb = Math.min(absorb, 20);
|
||||||
|
|
||||||
|
int healthRows = Mth.ceil((healthMax + absorb) / 2.0F / 10.0F);
|
||||||
|
int rowHeight = Math.max(10 - (healthRows - 2), 3);
|
||||||
|
|
||||||
|
this.rand.setSeed(updateCounter * 312871L);
|
||||||
|
|
||||||
|
int left = width / 2 - 91;
|
||||||
|
int top = height - left_height;
|
||||||
|
// change: these are unused below, unneeded? should these adjust the Forge
|
||||||
|
// variable?
|
||||||
|
// left_height += (healthRows * rowHeight);
|
||||||
|
// if (rowHeight != 10) left_height += 10 - rowHeight;
|
||||||
|
|
||||||
|
this.regen = -1;
|
||||||
|
if (player.hasEffect(MobEffects.REGENERATION)) {
|
||||||
|
this.regen = updateCounter % 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert this.mc.level != null;
|
||||||
|
final int TOP = 9 * (this.mc.level.getLevelData().isHardcore() ? 5 : 0);
|
||||||
|
final int BACKGROUND = (highlight ? 25 : 16);
|
||||||
|
int MARGIN = 16;
|
||||||
|
if (player.hasEffect(MobEffects.POISON))
|
||||||
|
MARGIN += 36;
|
||||||
|
else if (player.hasEffect(MobEffects.WITHER))
|
||||||
|
MARGIN += 72;
|
||||||
|
float absorbRemaining = absorb;
|
||||||
|
|
||||||
|
GuiGraphics matrixStack = event.getGuiGraphics();
|
||||||
|
for (int i = Mth.ceil((healthMax + absorb) / 2.0F) - 1; i >= 0; --i) {
|
||||||
|
int row = Mth.ceil((float) (i + 1) / 10.0F) - 1;
|
||||||
|
int x = left + i % 10 * 8;
|
||||||
|
int y = top - row * rowHeight;
|
||||||
|
|
||||||
|
if (health <= 4)
|
||||||
|
y += this.rand.nextInt(2);
|
||||||
|
if (i == this.regen)
|
||||||
|
y -= 2;
|
||||||
|
|
||||||
|
this.blit(matrixStack, x, y, BACKGROUND, TOP, 9, 9, ICON_VANILLA);
|
||||||
|
|
||||||
|
if (highlight) {
|
||||||
|
if (i * 2 + 1 < healthLast) {
|
||||||
|
this.blit(matrixStack, x, y, MARGIN + 54, TOP, 9, 9, ICON_VANILLA); // 6
|
||||||
|
} else if (i * 2 + 1 == healthLast) {
|
||||||
|
this.blit(matrixStack, x, y, MARGIN + 63, TOP, 9, 9, ICON_VANILLA); // 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (absorbRemaining > 0.0F) {
|
||||||
|
if (absorbRemaining == absorb && absorb % 2.0F == 1.0F) {
|
||||||
|
this.blit(matrixStack, x, y, MARGIN + 153, TOP, 9, 9, ICON_VANILLA); // 17
|
||||||
|
absorbRemaining -= 1.0F;
|
||||||
|
} else {
|
||||||
|
this.blit(matrixStack, x, y, MARGIN + 144, TOP, 9, 9, ICON_VANILLA); // 16
|
||||||
|
absorbRemaining -= 2.0F;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (i * 2 + 1 < health) {
|
||||||
|
this.blit(matrixStack, x, y, MARGIN + 36, TOP, 9, 9, ICON_VANILLA); // 4
|
||||||
|
} else if (i * 2 + 1 == health) {
|
||||||
|
this.blit(matrixStack, x, y, MARGIN + 45, TOP, 9, 9, ICON_VANILLA); // 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderExtraHearts(matrixStack, left, top, player);
|
||||||
|
this.renderExtraAbsorption(matrixStack, left, top - rowHeight, player);
|
||||||
|
|
||||||
|
RenderSystem.setShaderTexture(0, ICON_VANILLA);
|
||||||
|
gui.leftHeight += 10;
|
||||||
|
if (absorb > 0) {
|
||||||
|
gui.leftHeight += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setCanceled(true);
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
this.mc.getProfiler().pop();
|
||||||
|
MinecraftForge.EVENT_BUS
|
||||||
|
.post(new RenderGuiOverlayEvent.Post(mc.getWindow(), event.getGuiGraphics(), event.getPartialTick(), ActualOverlay));
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
/**
|
||||||
static enum HeartType {
|
* Gets the texture from potion effects
|
||||||
CONTAINER(0, false),
|
*
|
||||||
NORMAL(1, true),
|
* @param player Player instance
|
||||||
POISONED(2, true),
|
* @return Texture offset for potion effects
|
||||||
WITHERED(3, true),
|
*/
|
||||||
ABSORBING(5, false),
|
private int getPotionOffset(Player player) {
|
||||||
FROZEN(4, false);
|
int potionOffset = 0;
|
||||||
|
MobEffectInstance potion = player.getEffect(MobEffects.WITHER);
|
||||||
private final int index;
|
if (potion != null) {
|
||||||
private final boolean canBlink;
|
potionOffset = 18;
|
||||||
|
|
||||||
private HeartType(int pIndex, boolean pCanBlink) {
|
|
||||||
this.index = pIndex;
|
|
||||||
this.canBlink = pCanBlink;
|
|
||||||
}
|
}
|
||||||
|
potion = player.getEffect(MobEffects.POISON);
|
||||||
public int getX(int rowNum, boolean halfHeart, boolean renderHighlight)
|
if (potion != null) {
|
||||||
{
|
potionOffset = 9;
|
||||||
int heart = rowNum + (halfHeart ? 1 : 0) * 9;
|
|
||||||
|
|
||||||
return heart;
|
|
||||||
}
|
}
|
||||||
|
assert this.mc.level != null;
|
||||||
|
if (this.mc.level.getLevelData().isHardcore()) {
|
||||||
|
potionOffset += 27;
|
||||||
|
}
|
||||||
|
return potionOffset;
|
||||||
|
}
|
||||||
|
|
||||||
public int getY()
|
/**
|
||||||
{
|
* Renders the health above 10 hearts
|
||||||
switch(this)
|
*
|
||||||
{
|
* @param matrixStack Matrix stack instance
|
||||||
case CONTAINER -> {
|
* @param xBasePos Health bar top corner
|
||||||
return 144;
|
* @param yBasePos Health bar top corner
|
||||||
}
|
* @param player Player instance
|
||||||
case POISONED -> {
|
*/
|
||||||
return 9;
|
private void renderExtraHearts(GuiGraphics matrixStack, int xBasePos, int yBasePos, Player player) {
|
||||||
}
|
int potionOffset = this.getPotionOffset(player);
|
||||||
case WITHERED -> {
|
|
||||||
return 18;
|
// Extra hearts
|
||||||
}
|
RenderSystem.setShaderTexture(0, ICON_HEARTS);
|
||||||
case FROZEN -> {
|
int hp = Mth.ceil(player.getHealth());
|
||||||
return 27;
|
this.renderCustomHearts(matrixStack, xBasePos, yBasePos, potionOffset, hp, false);
|
||||||
}
|
}
|
||||||
case ABSORBING -> {
|
|
||||||
return 80;
|
/**
|
||||||
}
|
* Renders the absorption health above 10 hearts
|
||||||
default -> {
|
*
|
||||||
// Normal and other unknowns
|
* @param matrixStack Matrix stack instance
|
||||||
return 0;
|
* @param xBasePos Health bar top corner
|
||||||
}
|
* @param yBasePos Health bar top corner
|
||||||
|
* @param player Player instance
|
||||||
|
*/
|
||||||
|
private void renderExtraAbsorption(GuiGraphics matrixStack, int xBasePos, int yBasePos, Player player) {
|
||||||
|
int potionOffset = this.getPotionOffset(player);
|
||||||
|
|
||||||
|
// Extra hearts
|
||||||
|
RenderSystem.setShaderTexture(0, ICON_ABSORB);
|
||||||
|
int absorb = Mth.ceil(player.getAbsorptionAmount());
|
||||||
|
this.renderCustomHearts(matrixStack, xBasePos, yBasePos, potionOffset, absorb, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the texture offset from the regen effect
|
||||||
|
*
|
||||||
|
* @param i Heart index
|
||||||
|
* @param offset Current offset
|
||||||
|
*/
|
||||||
|
private int getYRegenOffset(int i, int offset) {
|
||||||
|
return i + offset == this.regen ? -2 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared logic to render custom hearts
|
||||||
|
*
|
||||||
|
* @param matrixStack Matrix stack instance
|
||||||
|
* @param xBasePos Health bar top corner
|
||||||
|
* @param yBasePos Health bar top corner
|
||||||
|
* @param potionOffset Offset from the potion effect
|
||||||
|
* @param count Number to render
|
||||||
|
* @param absorb If true, render absorption hearts
|
||||||
|
*/
|
||||||
|
private void renderCustomHearts(GuiGraphics matrixStack, int xBasePos, int yBasePos, int potionOffset, int count,
|
||||||
|
boolean absorb) {
|
||||||
|
int regenOffset = absorb ? 10 : 0;
|
||||||
|
for (int iter = 0; iter < count / 20; iter++) {
|
||||||
|
int renderHearts = (count - 20 * (iter + 1)) / 2;
|
||||||
|
int heartIndex = iter % 11;
|
||||||
|
if (renderHearts > 10) {
|
||||||
|
renderHearts = 10;
|
||||||
}
|
}
|
||||||
}
|
for (int i = 0; i < renderHearts; i++) {
|
||||||
|
int y = this.getYRegenOffset(i, regenOffset);
|
||||||
static HeartsRenderer.HeartType forPlayer(Player pPlayer) {
|
if (absorb) {
|
||||||
HeartsRenderer.HeartType hearttype;
|
this.blit(matrixStack, xBasePos + 8 * i, yBasePos + y, 0, 54, 9, 9, ICON_ABSORB);
|
||||||
if (pPlayer.hasEffect(MobEffects.POISON)) {
|
}
|
||||||
hearttype = POISONED;
|
this.blit(matrixStack, xBasePos + 8 * i, yBasePos + y, 18 * heartIndex, potionOffset, 9, 9, ICON_HEARTS);
|
||||||
} else if (pPlayer.hasEffect(MobEffects.WITHER)) {
|
}
|
||||||
hearttype = WITHERED;
|
if (count % 2 == 1 && renderHearts < 10) {
|
||||||
} else if (pPlayer.isFullyFrozen()) {
|
int y = this.getYRegenOffset(renderHearts, regenOffset);
|
||||||
hearttype = FROZEN;
|
if (absorb) {
|
||||||
} else {
|
this.blit(matrixStack, xBasePos + 8 * renderHearts, yBasePos + y, 0, 54, 9, 9, ICON_ABSORB);
|
||||||
hearttype = NORMAL;
|
}
|
||||||
|
this.blit(matrixStack, xBasePos + 8 * renderHearts, yBasePos + y, 9 + 18 * heartIndex, potionOffset, 9, 9, ICON_HEARTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hearttype;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 5.8 KiB |
Reference in a new issue