mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 22:27:32 +00:00
Add Better Info Panel and Display Damage to Abilities
This commit is contained in:
1 parent
d9f100d245
commit
6f6e080ee0
17 files changed
+226
-14
No files matched your search
@@ -302,6 +302,26 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
return hasDamage();
|
||||
}
|
||||
|
||||
// ── Display methods for inventory preview ──────────────────────
|
||||
|
||||
/** Base damage for preview display. Override in damaging types. */
|
||||
public float getDisplayDamage() { return 0; }
|
||||
|
||||
/** True if getDisplayDamage() is damage-per-second (e.g., Hazard). */
|
||||
public boolean isDisplayDamageDPS() { return false; }
|
||||
|
||||
/** Base barrier health for preview display. Override in barrier types. */
|
||||
public float getDisplayBarrierHealth() { return 0; }
|
||||
|
||||
/** True if this ability reflects projectiles. */
|
||||
public boolean isDisplayReflect() { return false; }
|
||||
|
||||
/** Reflection strength percentage (0-100). */
|
||||
public float getDisplayReflectStrength() { return 0; }
|
||||
|
||||
/** True if this ability absorbs projectiles. */
|
||||
public boolean isDisplayAbsorbing() { return false; }
|
||||
|
||||
/**
|
||||
* Override to provide variant templates for this ability type.
|
||||
* When a user creates this ability type, they will be shown a selection dialog
|
||||
|
||||
@@ -276,6 +276,9 @@ public class AbilityCharge extends AbilityMovement implements IAbilityCharge {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getKnockback() {
|
||||
return knockback;
|
||||
}
|
||||
|
||||
@@ -375,6 +375,9 @@ public class AbilityCutter extends Ability implements IAbilityCutter {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getKnockback() {
|
||||
return knockback;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,12 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard {
|
||||
this.damagePerSecond = damagePerSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damagePerSecond; }
|
||||
|
||||
@Override
|
||||
public boolean isDisplayDamageDPS() { return true; }
|
||||
|
||||
public int getDamageInterval() {
|
||||
return damageInterval;
|
||||
}
|
||||
|
||||
@@ -174,6 +174,9 @@ public class AbilityHeavyHit extends Ability implements IAbilityHeavyHit {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getKnockback() {
|
||||
return knockback;
|
||||
}
|
||||
|
||||
@@ -293,6 +293,9 @@ public class AbilityProjectile extends Ability implements IAbilityProjectile {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
@@ -197,6 +197,9 @@ public class AbilityShockwave extends Ability implements IAbilityShockwave {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public boolean isAoe() {
|
||||
return aoe;
|
||||
}
|
||||
|
||||
@@ -751,6 +751,9 @@ public class AbilitySlam extends Ability implements IAbilitySlam {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
@@ -536,6 +536,9 @@ public class AbilityTeleport extends Ability implements IAbilityTeleport {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getDamageRadius() {
|
||||
return damageRadius;
|
||||
}
|
||||
|
||||
@@ -270,6 +270,9 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getDamageRadius() {
|
||||
return damageRadius;
|
||||
}
|
||||
|
||||
@@ -349,6 +349,9 @@ public class AbilityVortex extends Ability implements IAbilityVortex {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public float getKnockback() {
|
||||
return knockback;
|
||||
}
|
||||
|
||||
@@ -213,6 +213,18 @@ public abstract class AbilityBarrier extends AbilityEnergy {
|
||||
barrierData.useHealth = useHealth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayBarrierHealth() { return barrierData.useHealth ? barrierData.maxHealth : 0; }
|
||||
|
||||
@Override
|
||||
public boolean isDisplayReflect() { return barrierData.reflect; }
|
||||
|
||||
@Override
|
||||
public float getDisplayReflectStrength() { return barrierData.reflectStrengthPct; }
|
||||
|
||||
@Override
|
||||
public boolean isDisplayAbsorbing() { return barrierData.absorbing; }
|
||||
|
||||
public int getBarrierDuration() {
|
||||
return barrierData.durationTicks;
|
||||
}
|
||||
|
||||
+3
@@ -639,6 +639,9 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
combatData.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return combatData.damage; }
|
||||
|
||||
@Override
|
||||
public float getKnockback() {
|
||||
return combatData.knockback;
|
||||
|
||||
@@ -235,6 +235,9 @@ public class AbilitySweeper extends AbilityEnergy implements IAbilitySweeper {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return damage; }
|
||||
|
||||
public int getDamageInterval() {
|
||||
return damageInterval;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,9 @@ public class AbilityWall extends AbilityBarrier {
|
||||
panelData.setLaunchDamage(damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayDamage() { return panelData.launching ? panelData.launchDamage : 0; }
|
||||
|
||||
public float getLaunchKnockback() {
|
||||
return panelData.launchKnockback;
|
||||
}
|
||||
|
||||
@@ -25,11 +25,17 @@ import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import tconstruct.client.tabs.AbstractTab;
|
||||
|
||||
import noppes.npcs.controllers.MagicController;
|
||||
import noppes.npcs.controllers.data.Magic;
|
||||
import noppes.npcs.controllers.data.MagicData;
|
||||
import noppes.npcs.controllers.data.MagicEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
private final ResourceLocation resource = new ResourceLocation("customnpcs", "textures/gui/standardbg.png");
|
||||
@@ -92,6 +98,11 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
private AbilityIcon cachedDetailIcon = null;
|
||||
private int cachedDetailIndex = -1;
|
||||
|
||||
// ═══ Info panel scroll ═══
|
||||
private int infoScrollOffset = 0;
|
||||
private int infoContentHeight = 0;
|
||||
private int lastInfoIndex = -1;
|
||||
|
||||
// ═══ Hotbar state ═══
|
||||
private int hoveredSlotIndex = -1;
|
||||
private AbilityIcon[] hotbarIcons = new AbilityIcon[AbilityHotbarData.TOTAL_SLOTS];
|
||||
@@ -436,11 +447,17 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
int infoIndex = hoveredGridIndex >= 0 ? hoveredGridIndex : selectedIndex;
|
||||
if (infoIndex < 0 || infoIndex >= filteredKeys.size()) return;
|
||||
|
||||
// Reset scroll when ability changes
|
||||
if (infoIndex != lastInfoIndex) {
|
||||
infoScrollOffset = 0;
|
||||
lastInfoIndex = infoIndex;
|
||||
}
|
||||
|
||||
FontRenderer fr = mc.fontRenderer;
|
||||
int panelInnerLeft = guiLeft + infoLeft + infoPad;
|
||||
int panelInnerTop = guiTop + infoTop + infoPad;
|
||||
int panelInnerWidth = (infoRight - infoLeft) - infoPad * 2;
|
||||
int y = panelInnerTop;
|
||||
int panelHeight = infoBottom - infoTop;
|
||||
|
||||
// Cached icon for detail preview
|
||||
if (cachedDetailIndex != infoIndex) {
|
||||
@@ -448,14 +465,28 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
cachedDetailIndex = infoIndex;
|
||||
}
|
||||
|
||||
Ability ability = filteredAbilities.get(infoIndex);
|
||||
|
||||
// Get toggle state for icon rendering and state display
|
||||
int toggleState = 0;
|
||||
PlayerData togglePlayerData = ClientCacheHandler.playerData;
|
||||
Ability infoAbility = filteredAbilities.get(infoIndex);
|
||||
if (infoAbility != null && infoAbility.isToggleable() && togglePlayerData != null && togglePlayerData.abilityData != null) {
|
||||
if (ability != null && ability.isToggleable() && togglePlayerData != null && togglePlayerData.abilityData != null) {
|
||||
toggleState = togglePlayerData.abilityData.getToggleState(filteredKeys.get(infoIndex));
|
||||
}
|
||||
|
||||
// GL Scissor to clip info panel content
|
||||
ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
int scaleFactor = sr.getScaleFactor();
|
||||
int scissorX = (guiLeft + infoLeft) * scaleFactor;
|
||||
int scissorY = mc.displayHeight - (guiTop + infoBottom) * scaleFactor;
|
||||
int scissorW = (infoRight - infoLeft) * scaleFactor;
|
||||
int scissorH = panelHeight * scaleFactor;
|
||||
|
||||
GL11.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GL11.glScissor(scissorX, scissorY, scissorW, scissorH);
|
||||
|
||||
int y = panelInnerTop - infoScrollOffset;
|
||||
|
||||
// Preview icon (centered, state-aware for multi-state toggles)
|
||||
if (cachedDetailIcon != null) {
|
||||
float drawSize = cachedDetailIcon.getDrawSize();
|
||||
@@ -490,9 +521,8 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
}
|
||||
y += 4;
|
||||
|
||||
// Type info
|
||||
Ability ability = filteredAbilities.get(infoIndex);
|
||||
if (ability != null) {
|
||||
// Type info
|
||||
String typeId = ability.getTypeId();
|
||||
if (typeId != null && !typeId.isEmpty()) {
|
||||
String translated = StatCollector.translateToLocal(typeId);
|
||||
@@ -503,6 +533,7 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle info
|
||||
if (ability.isToggleable()) {
|
||||
String toggleText = "[" + StatCollector.translateToLocal("gui.toggle") + "]";
|
||||
int textW = fr.getStringWidth(toggleText);
|
||||
@@ -510,12 +541,11 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
|
||||
// Toggle state (uses pre-computed toggleState)
|
||||
// Toggle state
|
||||
if (ability.isToggleable()) {
|
||||
String stateText;
|
||||
String prefix;
|
||||
if (toggleState > 0) {
|
||||
// Active — check for state label (multi-state toggles like Ki Weapon)
|
||||
String stateLabel = ability.getToggleStateLabel(toggleState);
|
||||
stateText = stateLabel != null ? stateLabel : StatCollector.translateToLocal("gui.toggle.active");
|
||||
prefix = "\u00A72";
|
||||
@@ -528,20 +558,110 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
|
||||
// Cooldown info
|
||||
// Cooldown info — yellow if per-ability cooldown
|
||||
if (ability.getCooldownTicks() > 0) {
|
||||
float seconds = ability.getCooldownTicks() / 20.0f;
|
||||
String cdText = StatCollector.translateToLocal("ability.cooldown") + ": " + String.format("%.1f", seconds) + "s";
|
||||
int textW2 = fr.getStringWidth(cdText);
|
||||
fr.drawStringWithShadow("\u00A77" + cdText, panelInnerLeft + (panelInnerWidth - textW2) / 2, y, 0xAAAAAA);
|
||||
boolean perAbility = ability.isPerAbilityCooldown();
|
||||
String cdColor = perAbility ? "\u00A7e" : "\u00A77";
|
||||
int cdColorInt = perAbility ? 0xFFFF55 : 0xAAAAAA;
|
||||
fr.drawStringWithShadow(cdColor + cdText, panelInnerLeft + (panelInnerWidth - textW2) / 2, y, cdColorInt);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
|
||||
if (ability.isPerAbilityCooldown()) {
|
||||
if (perAbility) {
|
||||
String perText = "[" + StatCollector.translateToLocal("ability.perAbilityCooldown") + "]";
|
||||
int ptW = fr.getStringWidth(perText);
|
||||
fr.drawStringWithShadow("\u00A7e" + perText, panelInnerLeft + (panelInnerWidth - ptW) / 2, y, 0xFFFF55);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Damage info
|
||||
float baseDamage = ability.getDisplayDamage();
|
||||
if (baseDamage > 0) {
|
||||
float displayDamage = baseDamage;
|
||||
if (AbilityController.Instance != null && mc.thePlayer != null) {
|
||||
displayDamage = AbilityController.Instance.fireModifyProjectileDamage(ability, mc.thePlayer, baseDamage);
|
||||
}
|
||||
String label = ability.isDisplayDamageDPS()
|
||||
? StatCollector.translateToLocal("gui.dps")
|
||||
: StatCollector.translateToLocal("ability.preview.damage");
|
||||
String dmgText = label + ": " + String.format("%.1f", displayDamage);
|
||||
int textW = fr.getStringWidth(dmgText);
|
||||
fr.drawStringWithShadow("\u00A7c" + dmgText, panelInnerLeft + (panelInnerWidth - textW) / 2, y, 0xFF5555);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
|
||||
// Barrier health
|
||||
float baseHealth = ability.getDisplayBarrierHealth();
|
||||
if (baseHealth > 0) {
|
||||
float displayHealth = baseHealth;
|
||||
if (AbilityController.Instance != null && mc.thePlayer != null) {
|
||||
displayHealth = AbilityController.Instance.fireModifyBarrierHealth(ability, mc.thePlayer, baseHealth);
|
||||
}
|
||||
String healthText = StatCollector.translateToLocal("ability.preview.health") + ": " + String.format("%.0f", displayHealth);
|
||||
int textW = fr.getStringWidth(healthText);
|
||||
fr.drawStringWithShadow("\u00A7a" + healthText, panelInnerLeft + (panelInnerWidth - textW) / 2, y, 0x55FF55);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
|
||||
// Barrier reflection
|
||||
if (ability.isDisplayReflect()) {
|
||||
String reflectText = StatCollector.translateToLocal("ability.reflect") + ": " + String.format("%.0f%%", ability.getDisplayReflectStrength());
|
||||
int textW = fr.getStringWidth(reflectText);
|
||||
fr.drawStringWithShadow("\u00A7d" + reflectText, panelInnerLeft + (panelInnerWidth - textW) / 2, y, 0xFF55FF);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
|
||||
// Barrier absorbing
|
||||
if (ability.isDisplayAbsorbing()) {
|
||||
String absorbText = "[" + StatCollector.translateToLocal("ability.absorbing") + "]";
|
||||
int textW = fr.getStringWidth(absorbText);
|
||||
fr.drawStringWithShadow("\u00A7b" + absorbText, panelInnerLeft + (panelInnerWidth - textW) / 2, y, 0x55FFFF);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
}
|
||||
|
||||
// Magic info
|
||||
if (ability.hasMagic()) {
|
||||
y += 2;
|
||||
MagicData magicData = ability.getMagicData();
|
||||
if (magicData.isEmpty()) {
|
||||
String magicText = StatCollector.translateToLocal("ability.preview.magic.casters");
|
||||
int textW = fr.getStringWidth(magicText);
|
||||
fr.drawStringWithShadow("\u00A79" + magicText, panelInnerLeft + (panelInnerWidth - textW) / 2, y, 0x5555FF);
|
||||
y += fr.FONT_HEIGHT + 3;
|
||||
} else {
|
||||
for (Map.Entry<Integer, MagicEntry> entry : magicData.getMagics().entrySet()) {
|
||||
Magic magic = MagicController.getInstance() != null
|
||||
? MagicController.getInstance().getMagic(entry.getKey()) : null;
|
||||
String magicName = magic != null ? magic.getDisplayName() : "Magic #" + entry.getKey();
|
||||
String splitText = magicName + ": " + Math.round(entry.getValue().split * 100) + "%";
|
||||
int textW = fr.getStringWidth(splitText);
|
||||
fr.drawStringWithShadow("\u00A79" + splitText, panelInnerLeft + (panelInnerWidth - textW) / 2, y, 0x5555FF);
|
||||
y += fr.FONT_HEIGHT + 1;
|
||||
}
|
||||
y += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track content height for scroll limits
|
||||
infoContentHeight = (y + infoScrollOffset) - panelInnerTop + infoPad * 2;
|
||||
|
||||
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
|
||||
// Scroll indicator
|
||||
if (infoContentHeight > panelHeight) {
|
||||
int maxScroll = Math.max(1, infoContentHeight - panelHeight);
|
||||
if (infoScrollOffset > maxScroll) infoScrollOffset = maxScroll;
|
||||
float scrollPercent = (float) infoScrollOffset / maxScroll;
|
||||
int barHeight = panelHeight - 4;
|
||||
int thumbHeight = Math.max(8, barHeight * panelHeight / infoContentHeight);
|
||||
int thumbY = guiTop + infoTop + 2 + (int) ((barHeight - thumbHeight) * scrollPercent);
|
||||
int barX = guiLeft + infoRight - 3;
|
||||
drawRect(barX, guiTop + infoTop + 2, barX + 2, guiTop + infoTop + 2 + barHeight, 0x40FFFFFF);
|
||||
drawRect(barX, thumbY, barX + 2, thumbY + thumbHeight, 0xA0FFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,11 +1023,26 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
|
||||
|
||||
int wheel = Mouse.getEventDWheel();
|
||||
if (wheel != 0) {
|
||||
int maxScroll = getMaxScrollRow();
|
||||
if (wheel > 0) {
|
||||
scrollRow = Math.max(0, scrollRow - 1);
|
||||
int mouseX = Mouse.getEventX() * width / mc.displayWidth;
|
||||
int mouseY = height - Mouse.getEventY() * height / mc.displayHeight - 1;
|
||||
|
||||
if (mouseX >= guiLeft + infoLeft && mouseX < guiLeft + infoRight
|
||||
&& mouseY >= guiTop + infoTop && mouseY < guiTop + infoBottom) {
|
||||
// Info panel scroll
|
||||
int panelHeight = infoBottom - infoTop;
|
||||
int maxInfoScroll = Math.max(0, infoContentHeight - panelHeight);
|
||||
if (wheel > 0) {
|
||||
infoScrollOffset = Math.max(0, infoScrollOffset - 10);
|
||||
} else {
|
||||
infoScrollOffset = Math.min(maxInfoScroll, infoScrollOffset + 10);
|
||||
}
|
||||
} else {
|
||||
scrollRow = Math.min(maxScroll, scrollRow + 1);
|
||||
int maxScroll = getMaxScrollRow();
|
||||
if (wheel > 0) {
|
||||
scrollRow = Math.max(0, scrollRow - 1);
|
||||
} else {
|
||||
scrollRow = Math.min(maxScroll, scrollRow + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user