fix(hud): rewrite PotionEffectsHud.EffectEntry to match obfuscated semantics

The previous EffectEntry had two render-killing inversions: show()
animated heightAnim to the target row height and widthAnim to 100,
but the render loop uses heightAnim as the 0..1 alpha factor
(multiplied by 80/140/160/185) and widthAnim as the actual row
height. That made every entry render with massively saturated alpha
and a 100-pixel-tall pill. getTotalWidth() also returned the height
animation value, so the sort order was wrong too.

Rewrite to mirror Recaf's PotionEffectsHud$EffectEntry:
- show(h): heightAnim -> 1.0 (alpha), widthAnim -> h (row height).
- startRemove() animates both back to 0; isRemoveDone() checks anim
  isDone() instead of a hand-rolled alpha field.
- tick() drops the custom alpha decay and keeps the duration text
  fresh for non-instantaneous effects.
- getTotalWidth() actually measures the text (icon + name + duration
  + padding) so sort-by-width works.
- updateEffect() bumps originalDuration when the new instance has a
  longer remaining duration (stacked refresh).
- refreshDisplayText() delegates to the outer HUD for the infinity
  symbol and roman-numeral amplifier formatting.
- Rename instance -> effectInstance and update render-site reads.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Shirona1337
2026-05-21 23:23:01 +08:00
parent 783273fd5c
commit e02133547c
@@ -23,68 +23,80 @@ import shit.zen.event.EventTarget;
public class PotionEffectsHud
extends HudElement {
public static final class EffectEntry {
public final PotionEffectsHud parent;
public MobEffectInstance instance;
public String effectName = "";
public String durationText = "";
public String amplifierText = "";
public int originalDuration = 1;
public final PotionEffectsHud outer;
public MobEffectInstance effectInstance;
public long originalDuration;
public String effectName;
public String durationText;
public String amplifierText;
public final shit.zen.utils.animation.SmoothAnimationTimer fadeAnim = new shit.zen.utils.animation.SmoothAnimationTimer();
public final shit.zen.utils.animation.SmoothAnimationTimer heightAnim = new shit.zen.utils.animation.SmoothAnimationTimer();
public final shit.zen.utils.animation.SmoothAnimationTimer widthAnim = new shit.zen.utils.animation.SmoothAnimationTimer();
public float alpha = 1.0f;
public float targetY;
public float currentY;
public boolean removing = false;
public boolean visible = true;
public EffectEntry(PotionEffectsHud parent, MobEffectInstance instance) {
this.parent = parent;
this.instance = instance;
this.originalDuration = Math.max(1, instance.getDuration());
this.updateText();
public EffectEntry(PotionEffectsHud outer, MobEffectInstance instance) {
this.outer = outer;
this.effectInstance = instance;
this.originalDuration = instance.getDuration();
this.refreshDisplayText();
this.heightAnim.setCurrentValue(0.0);
this.widthAnim.setCurrentValue(0.0);
}
public int getDuration() {
return this.effectInstance.getDuration();
}
public net.minecraft.world.effect.MobEffect getEffect() {
return this.instance.getEffect();
return this.effectInstance.getEffect();
}
public void updateEffect(MobEffectInstance instance) {
this.instance = instance;
this.updateText();
if (instance.getDuration() > this.effectInstance.getDuration()) {
this.originalDuration = instance.getDuration();
}
this.effectInstance = instance;
this.refreshDisplayText();
}
private void updateText() {
this.effectName = this.instance.getEffect().getDisplayName().getString();
this.durationText = net.minecraft.world.effect.MobEffectUtil.formatDuration(this.instance, 1.0f).getString();
int amp = this.instance.getAmplifier();
this.amplifierText = amp > 0 ? String.valueOf(amp + 1) : "";
}
public void show(float targetHeight) {
this.visible = false;
this.heightAnim.animate(targetHeight, 0.2, shit.zen.utils.math.Easings.EASE_OUT_POW3);
this.widthAnim.animate(100.0, 0.2, shit.zen.utils.math.Easings.EASE_OUT_POW3);
public void refreshDisplayText() {
this.effectName = this.effectInstance.getEffect().getDisplayName().getString();
this.durationText = this.outer.formatDuration(this.effectInstance);
this.amplifierText = this.outer.formatAmplifier(this.effectInstance.getAmplifier() + 1);
}
public float getTotalWidth() {
return this.widthAnim.getValueF();
float iconBoxWidth = 22.0f;
float padding = 5.0f;
float nameWidth = GlHelper.getStringWidth(this.effectName, this.outer.effectNameFont);
float durationWidth = GlHelper.getStringWidth(this.durationText, this.outer.amplifierFont);
return iconBoxWidth + nameWidth + durationWidth + padding * 3.0f;
}
public void show(float targetHeight) {
this.heightAnim.animate(1.0, 0.3, shit.zen.utils.math.Easings.EASE_OUT_POW3);
this.widthAnim.animate(targetHeight, 0.3, shit.zen.utils.math.Easings.EASE_OUT_POW3);
}
public void startRemove() {
if (this.removing) return;
this.removing = true;
this.heightAnim.animate(0.0, 0.2, shit.zen.utils.math.Easings.EASE_IN_POW3);
this.widthAnim.animate(0.0, 0.2, shit.zen.utils.math.Easings.EASE_IN_POW3);
}
public boolean isRemoveDone() {
return this.removing && this.alpha <= 0.01f;
return this.removing && this.heightAnim.isDone() && this.widthAnim.isDone();
}
public void tick() {
float target = this.removing ? 0.0f : 1.0f;
this.alpha += (target - this.alpha) * 0.18f;
this.fadeAnim.tick();
this.heightAnim.tick();
this.widthAnim.tick();
if (!this.effectInstance.getEffect().isInstantenous()) {
this.refreshDisplayText();
}
}
}
@@ -184,12 +196,12 @@ extends HudElement {
currentY += animWidth + (animWidth > 0.0f ? spacing : 0.0f);
continue;
}
int effectColor = this.getEffectColor(entry.instance.getEffect());
int effectColor = this.getEffectColor(entry.effectInstance.getEffect());
float barWidth = totalWidth - iconBoxWidth;
float barX = x + iconBoxWidth;
this.iconBgPaint.setColor(ColorUtil.fromARGB(30, 30, 35, (int)(80.0f * animHeight)));
drawContext.drawRoundedRect(RoundedRectangle.ofXYWHRadii(barX, entryY, barWidth, animWidth, new float[]{0.0f, 0.0f, 4.5f, 4.5f, 4.5f, 4.5f, 0.0f, 0.0f}), this.iconBgPaint);
float durationPct = entry.instance.isInfiniteDuration() ? 1.0f : (float)entry.instance.getDuration() / (float)entry.originalDuration;
float durationPct = entry.effectInstance.isInfiniteDuration() ? 1.0f : (float)entry.effectInstance.getDuration() / (float)entry.originalDuration;
this.effectIconPaint.setColor(ColorUtil.fromARGB(effectColor >> 16 & 0xFF, effectColor >> 8 & 0xFF, effectColor & 0xFF, (int)(140.0f * animHeight)));
if (durationPct > 0.0f) {
drawContext.drawRoundedRect(RoundedRectangle.ofXYWHRadii(barX, entryY, barWidth * durationPct, animWidth, new float[]{0.0f, 0.0f, cornerRadius * durationPct, cornerRadius * durationPct, cornerRadius * durationPct, cornerRadius * durationPct, 0.0f, 0.0f}), this.effectIconPaint);