From 911f1854004ec38f9e93d345e48b150e23aafd3f Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 2 Feb 2026 18:26:52 -0500 Subject: [PATCH] Fields Builder for Ability GUIs --- .../controllers/data/ability/Ability.java | 105 ++++- .../data/ability/AbilityEffect.java | 7 + .../controllers/data/ability/AnchorPoint.java | 13 + .../data/ability/TargetingMode.java | 13 +- .../data/ability/gui/FieldDef.java | 52 +-- .../ability/gui/FieldDefinitionsBuilder.java | 359 ++++++++++++++++++ .../data/ability/gui/FieldType.java | 5 +- .../data/ability/gui/TabTarget.java | 1 - .../data/ability/type/AbilityCharge.java | 11 +- .../data/ability/type/AbilityCutter.java | 31 +- .../data/ability/type/AbilityDash.java | 18 +- .../data/ability/type/AbilityDisc.java | 76 ++-- .../data/ability/type/AbilityEnergyBeam.java | 71 ++-- .../data/ability/type/AbilityGuard.java | 40 +- .../data/ability/type/AbilityHazard.java | 48 ++- .../data/ability/type/AbilityHeal.java | 15 +- .../data/ability/type/AbilityHeavyHit.java | 7 +- .../data/ability/type/AbilityLaserShot.java | 55 ++- .../data/ability/type/AbilityOrb.java | 67 ++-- .../data/ability/type/AbilityProjectile.java | 23 +- .../data/ability/type/AbilityShockwave.java | 8 +- .../data/ability/type/AbilitySlam.java | 12 +- .../data/ability/type/AbilitySweeper.java | 34 +- .../data/ability/type/AbilityTeleport.java | 32 +- .../data/ability/type/AbilityTrap.java | 28 +- .../data/ability/type/AbilityVortex.java | 24 +- .../gui/advanced/SubGuiAbilityConfig.java | 358 +++++++++-------- .../npcs/client/gui/util/GuiNpcButton.java | 16 + .../assets/customnpcs/lang/en_US.lang | 299 +++++++++------ 29 files changed, 1223 insertions(+), 605 deletions(-) create mode 100644 src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDefinitionsBuilder.java diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java index b368d6267..0696674f4 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -19,10 +19,12 @@ import noppes.npcs.client.gui.advanced.SubGuiAbilityConfig; import noppes.npcs.client.gui.util.IAbilityConfigCallback; import noppes.npcs.controllers.AnimationController; import noppes.npcs.controllers.data.Animation; +import noppes.npcs.controllers.data.Frame; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.scripted.NpcAPI; import noppes.npcs.scripted.event.AbilityEvent; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -60,6 +62,7 @@ public abstract class Ability implements IAbility { // Timing (ticks) protected int cooldownTicks = 0; // Added to global cooldown after ability completes protected int windUpTicks = 20; + protected boolean syncWindupWithAnimation = true; protected int dazedTicks = 80; // Only used when interrupted during WINDUP (if interruptible) // Interruption @@ -325,49 +328,70 @@ public abstract class Ability implements IAbility { defs.add(FieldDef.stringField("gui.name", this::getName, this::setName) .tab(TabTarget.GENERAL)); defs.add(FieldDef.intField("ability.weight", this::getWeight, this::setWeight) - .tab(TabTarget.GENERAL).range(1, 1000)); - defs.add(FieldDef.enumField("ability.lockMove", LockMovementType.class, - this::getLockMovement, this::setLockMovement) + .tab(TabTarget.GENERAL).range(1, 1000).column(ColumnHint.LEFT)); + defs.add(FieldDef.boolField("gui.enabled", this::isEnabled, this::setEnabled) + .tab(TabTarget.GENERAL).column(ColumnHint.RIGHT)); + defs.add(FieldDef.section("ability.section.timing").tab(TabTarget.GENERAL)); + defs.add(FieldDef.intField("ability.windUpTicks", this::getRawWindUpTicks, this::setWindUpTicks) + .tab(TabTarget.GENERAL).range(0, 1000).column(ColumnHint.LEFT) + .visibleWhen(() -> !isSyncWindupWithAnimation())); + defs.add(FieldDef.labelField("ability.windUpTicks", () -> getWindUpTicks() + "t") + .tab(TabTarget.GENERAL).column(ColumnHint.LEFT) + .visibleWhen(this::isSyncWindupWithAnimation)); + defs.add(FieldDef.boolField("ability.syncWindup", this::isSyncWindupWithAnimation, this::setSyncWindupWithAnimation) + .tab(TabTarget.GENERAL).hover("ability.hover.sync").column(ColumnHint.RIGHT) + .visibleWhen(this::hasWindUpAnimation)); + defs.add(FieldDef.intField("ability.cooldownTicks", this::getCooldownTicks, this::setCooldownTicks) + .tab(TabTarget.GENERAL).range(0, 10000)); + defs.add(FieldDef.section("ability.section.movement").tab(TabTarget.GENERAL)); + defs.add(FieldDef.stringEnumField("ability.lockMovement", LockMovementType.getDisplayKeys(), + () -> this.getLockMovement().getDisplayKey(), + v -> { + String[] keys = LockMovementType.getDisplayKeys(); + for (int i = 0; i < keys.length; i++) { + if (keys[i].equals(v)) { this.setLockMovement(LockMovementType.fromOrdinal(i)); break; } + } + }) .tab(TabTarget.GENERAL).hover("ability.hover.lockMove")); defs.add(FieldDef.boolField("ability.interruptible", this::isInterruptible, this::setInterruptible) - .tab(TabTarget.GENERAL).hover("ability.hover.interruptible")); - defs.add(FieldDef.intField("ability.dazed", this::getDazedTicks, this::setDazedTicks) + .tab(TabTarget.GENERAL).hover("ability.hover.interruptible").column(ColumnHint.LEFT)); + defs.add(FieldDef.intField("ability.dazedTicks", this::getDazedTicks, this::setDazedTicks) .tab(TabTarget.GENERAL).range(0, 1000) - .visibleWhen(this::isInterruptible)); - defs.add(FieldDef.intField("ability.windup", this::getWindUpTicks, this::setWindUpTicks) - .tab(TabTarget.GENERAL).range(0, 1000)); - defs.add(FieldDef.intField("ability.cooldown", this::getCooldownTicks, this::setCooldownTicks) - .tab(TabTarget.GENERAL).range(0, 10000)); - defs.add(FieldDef.boolField("gui.enabled", this::isEnabled, this::setEnabled) - .tab(TabTarget.GENERAL)); + .visibleWhen(this::isInterruptible).column(ColumnHint.RIGHT)); // Target tab defs.add(FieldDef.intField("ability.minRange", () -> (int) getMinRange(), v -> setMinRange(v)) - .tab(TabTarget.TARGET).range(0, 100)); + .tab(TabTarget.TARGET).range(0, 100).column(ColumnHint.LEFT)); defs.add(FieldDef.intField("ability.maxRange", () -> (int) getMaxRange(), v -> setMaxRange(v)) - .tab(TabTarget.TARGET).range(1, 100)); + .tab(TabTarget.TARGET).range(1, 100).column(ColumnHint.RIGHT)); if (!isTargetingModeLocked()) { - defs.add(FieldDef.enumField("ability.targeting", TargetingMode.class, + defs.add(FieldDef.enumField("ability.targetingMode", TargetingMode.class, this::getTargetingMode, this::setTargetingMode) .tab(TabTarget.TARGET).hover("ability.hover.targeting")); } - // Effects tab + // Effects tab - Sounds + defs.add(FieldDef.section("ability.section.sounds").tab(TabTarget.EFFECTS)); defs.add(FieldDef.soundSubGui("ability.windUpSound", this::getWindUpSound, this::setWindUpSound) .tab(TabTarget.EFFECTS)); defs.add(FieldDef.soundSubGui("ability.activeSound", this::getActiveSound, this::setActiveSound) .tab(TabTarget.EFFECTS)); - defs.add(FieldDef.animSubGui("ability.windUpAnim", + // Effects tab - Animations + defs.add(FieldDef.section("ability.section.animations").tab(TabTarget.EFFECTS)); + defs.add(FieldDef.animSubGui("ability.windUpAnimation", this::getWindUpAnimationId, this::setWindUpAnimationId, this::getWindUpAnimationName, this::setWindUpAnimationName) .tab(TabTarget.EFFECTS)); - defs.add(FieldDef.animSubGui("ability.activeAnim", + defs.add(FieldDef.animSubGui("ability.activeAnimation", this::getActiveAnimationId, this::setActiveAnimationId, this::getActiveAnimationName, this::setActiveAnimationName) .tab(TabTarget.EFFECTS)); + // Effects tab - Telegraph TelegraphType tType = getTelegraphType(); if (tType != null && tType != TelegraphType.NONE) { + defs.add(FieldDef.section("ability.section.telegraph").tab(TabTarget.EFFECTS) + .tooltip("telegraph." + tType.name().toLowerCase())); defs.add(FieldDef.boolField("ability.showTelegraph", this::isShowTelegraph, this::setShowTelegraph) .tab(TabTarget.EFFECTS).hover("ability.hover.showTelegraph")); defs.add(FieldDef.colorSubGui("ability.windUpColor", this::getWindUpColor, this::setWindUpColor) @@ -847,6 +871,7 @@ public abstract class Ability implements IAbility { nbt.setFloat("maxRange", maxRange); nbt.setInteger("cooldown", cooldownTicks); nbt.setInteger("windUp", windUpTicks); + nbt.setBoolean("syncWindup", syncWindupWithAnimation); nbt.setInteger("recovery", dazedTicks); nbt.setBoolean("interruptible", interruptible); nbt.setInteger("lockMovement", lockMovement.ordinal()); @@ -894,6 +919,7 @@ public abstract class Ability implements IAbility { maxRange = nbt.getFloat("maxRange"); cooldownTicks = nbt.getInteger("cooldown"); windUpTicks = nbt.getInteger("windUp"); + syncWindupWithAnimation = !nbt.hasKey("syncWindup") || nbt.getBoolean("syncWindup"); dazedTicks = nbt.getInteger("recovery"); interruptible = nbt.getBoolean("interruptible"); lockMovement = LockMovementType.fromOrdinal(nbt.getInteger("lockMovement")); @@ -1036,6 +1062,10 @@ public abstract class Ability implements IAbility { } public int getWindUpTicks() { + return calculateWindupFromAnimation(); + } + + public int getRawWindUpTicks() { return windUpTicks; } @@ -1043,6 +1073,45 @@ public abstract class Ability implements IAbility { this.windUpTicks = Math.max(0, windUpTicks); } + public boolean isSyncWindupWithAnimation() { + return syncWindupWithAnimation; + } + + public boolean hasWindUpAnimation() { + return (windUpAnimationName != null && !windUpAnimationName.isEmpty()) || windUpAnimationId >= 0; + } + + public void setSyncWindupWithAnimation(boolean syncWindupWithAnimation) { + this.syncWindupWithAnimation = syncWindupWithAnimation; + } + + private int calculateWindupFromAnimation() { + if (!syncWindupWithAnimation) { + return windUpTicks; + } + + if (AnimationController.Instance == null) { + return windUpTicks; + } + + Animation animation = null; + if (windUpAnimationName != null && !windUpAnimationName.isEmpty()) { + animation = (Animation) AnimationController.Instance.get(windUpAnimationName); + } else if (windUpAnimationId >= 0) { + animation = (Animation) AnimationController.Instance.get(windUpAnimationId); + } + + if (animation == null || animation.frames.isEmpty()) { + return windUpTicks; + } + + int totalDuration = 0; + for (Frame frame : animation.frames) { + totalDuration += frame.getDuration(); + } + return totalDuration; + } + public int getDazedTicks() { return dazedTicks; } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityEffect.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityEffect.java index 47e03f10d..57f0cbf17 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityEffect.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityEffect.java @@ -38,6 +38,13 @@ public class AbilityEffect { return potionId; } + public static String[] getLangKeys() { + EffectType[] types = values(); + String[] keys = new String[types.length]; + for (int i = 0; i < types.length; i++) keys[i] = types[i].langKey; + return keys; + } + public static EffectType fromOrdinal(int ordinal) { EffectType[] values = values(); if (ordinal >= 0 && ordinal < values.length) { diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AnchorPoint.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AnchorPoint.java index 9bf408c27..1bd99d4f8 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AnchorPoint.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AnchorPoint.java @@ -52,4 +52,17 @@ public enum AnchorPoint { } return names; } + + @Override + public String toString() { + switch (this) { + case FRONT: return "ability.anchor.front"; + case CENTER: return "ability.anchor.center"; + case RIGHT_HAND: return "ability.anchor.rightHand"; + case LEFT_HAND: return "ability.anchor.leftHand"; + case ABOVE_HEAD: return "ability.anchor.aboveHead"; + case CHEST: return "ability.anchor.chest"; + default: return name(); + } + } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/TargetingMode.java b/src/main/java/kamkeel/npcs/controllers/data/ability/TargetingMode.java index bd1bb6a47..9bdbdac6f 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/TargetingMode.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/TargetingMode.java @@ -22,5 +22,16 @@ public enum TargetingMode { /** * AOE centered on target */ - AOE_TARGET + AOE_TARGET; + + @Override + public String toString() { + switch (this) { + case AGGRO_TARGET: return "ability.target.aggro_target"; + case SELF: return "ability.target.self"; + case AOE_SELF: return "ability.target.aoe_self"; + case AOE_TARGET: return "ability.target.aoe_target"; + default: return name(); + } + } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDef.java b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDef.java index 25ec6cb66..28b211c3e 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDef.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDef.java @@ -2,7 +2,6 @@ package kamkeel.npcs.controllers.data.ability.gui; import kamkeel.npcs.controllers.data.ability.AbilityEffect; import noppes.npcs.client.gui.SubGuiColorSelector; -import noppes.npcs.client.gui.advanced.ability.SubGuiAbilityEffects; import noppes.npcs.client.gui.select.GuiAnimationSelection; import noppes.npcs.client.gui.select.GuiSoundSelection; import noppes.npcs.client.gui.util.SubGuiInterface; @@ -26,6 +25,7 @@ public class FieldDef { private Supplier getter; private Consumer setter; private BooleanSupplier visibleWhen = () -> true; + private BooleanSupplier enabledWhen = () -> true; private String tooltip = null; private String hoverText = null; @@ -57,6 +57,10 @@ public class FieldDef { // STATIC FACTORIES // ═══════════════════════════════════════════════════════════════════ + public static FieldDef section(String label) { + return new FieldDef(label, FieldType.SECTION_HEADER); + } + public static FieldDef floatField(String label, Supplier getter, Consumer setter) { FieldDef def = new FieldDef(label, FieldType.FLOAT); def.getter = () -> getter.get(); @@ -102,6 +106,20 @@ public class FieldDef { return def; } + public static FieldDef labelField(String label, Supplier textSupplier) { + FieldDef def = new FieldDef(label, FieldType.LABEL); + def.getter = () -> textSupplier.get(); + return def; + } + + @SuppressWarnings("unchecked") + public static FieldDef effectsListField(String label, Supplier> getter, Consumer> setter) { + FieldDef def = new FieldDef(label, FieldType.EFFECTS_LIST); + def.getter = () -> getter.get(); + def.setter = v -> setter.accept((List) v); + return def; + } + public static FieldDef subGuiField(String label, Supplier factory, Consumer resultHandler) { FieldDef def = new FieldDef(label, FieldType.SUB_GUI); def.subGuiFactory = factory; @@ -121,15 +139,6 @@ public class FieldDef { .buttonTextColor(() -> getter.get() & 0xFFFFFF); } - public static FieldDef effectsSubGui(String label, Supplier> getter, Consumer> setter) { - return subGuiField(label, - () -> new SubGuiAbilityEffects(getter.get()), - gui -> { - List r = ((SubGuiAbilityEffects) gui).getResult(); - if (r != null) setter.accept(r); - }); - } - public static FieldDef soundSubGui(String label, Supplier getter, Consumer setter) { return subGuiField(label, () -> new GuiSoundSelection(getter.get()), @@ -139,7 +148,7 @@ public class FieldDef { }) .buttonLabel(() -> { String s = getter.get(); - return s == null || s.isEmpty() ? "gui.none" : truncateEnd(s, 10); + return s == null || s.isEmpty() ? "gui.none" : s; }) .clearable(() -> setter.accept("")); } @@ -161,7 +170,7 @@ public class FieldDef { }) .buttonLabel(() -> { String name = nameGetter.get(); - if (name != null && !name.isEmpty()) return truncateEnd(name, 8); + if (name != null && !name.isEmpty()) return name; int id = idGetter.get(); return id >= 0 ? "ID: " + id : "gui.none"; }) @@ -199,6 +208,11 @@ public class FieldDef { return this; } + public FieldDef enabledWhen(BooleanSupplier condition) { + this.enabledWhen = condition; + return this; + } + public FieldDef tooltip(String tooltip) { this.tooltip = tooltip; return this; @@ -234,6 +248,7 @@ public class FieldDef { public String getCustomTabName() { return customTabName; } public ColumnHint getColumn() { return column; } public boolean isVisible() { return visibleWhen.getAsBoolean(); } + public boolean isEnabled() { return enabledWhen.getAsBoolean(); } public String getTooltip() { return tooltip; } public String getHoverText() { return hoverText; } public float getMin() { return min; } @@ -271,17 +286,4 @@ public class FieldDef { return buttonTextColorSupplier != null ? buttonTextColorSupplier.get() : null; } - // ═══════════════════════════════════════════════════════════════════ - // HELPERS - // ═══════════════════════════════════════════════════════════════════ - - static String truncateEnd(String str, int maxLen) { - if (str == null) return ""; - if (str.length() <= maxLen) return str; - int lastSlash = str.lastIndexOf('/'); - if (lastSlash >= 0 && str.length() - lastSlash <= maxLen) { - return "..." + str.substring(lastSlash); - } - return "..." + str.substring(str.length() - maxLen + 3); - } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDefinitionsBuilder.java b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDefinitionsBuilder.java new file mode 100644 index 000000000..1ba3f2694 --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldDefinitionsBuilder.java @@ -0,0 +1,359 @@ +package kamkeel.npcs.controllers.data.ability.gui; + +import kamkeel.npcs.controllers.data.ability.AbilityEffect; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.util.StatCollector; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.client.gui.util.GuiNpcButtonYesNo; +import noppes.npcs.client.gui.util.GuiNpcLabel; +import noppes.npcs.client.gui.util.GuiNpcTextField; +import noppes.npcs.client.gui.util.GuiScrollWindow; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Reusable builder that converts a list of {@link FieldDef} into positioned + * GUI widgets inside a {@link GuiScrollWindow}. + */ +public class FieldDefinitionsBuilder { + + // Layout defaults + private int contentRight = 330; + private int colLLabel = 5; + private int colLField = 75; + private int colLWidth = 70; + private int colRLabel = 178; + private int colRField = 248; + private int colRWidth = 70; + private int rowHeight = 24; + private int labelPadding = 8; + + // Dependencies + private final GuiScreen parent; + private final GuiScrollWindow sw; + private final FontRenderer fontRenderer; + + // ID counters + private int widgetId; + private int clearId; + private int labelId; + + // Starting Y + private int startY = 5; + + // Output maps + private final Map buttonFieldMap = new HashMap<>(); + private final Map textFieldMap = new HashMap<>(); + private final Map clearFieldMap = new HashMap<>(); + + // Effects list metadata: widgetId -> [effectIndex, action] + // action: 0=type, 1=duration(text), 2=amp, 3=delete, 4=add + private final Map effectWidgetMeta = new HashMap<>(); + + public FieldDefinitionsBuilder(GuiScreen parent, GuiScrollWindow sw, FontRenderer fontRenderer) { + this.parent = parent; + this.sw = sw; + this.fontRenderer = fontRenderer; + } + + // ═══════════════════════════════════════════════════════════════════ + // CONFIG CHAINING + // ═══════════════════════════════════════════════════════════════════ + + public FieldDefinitionsBuilder contentRight(int v) { this.contentRight = v; return this; } + public FieldDefinitionsBuilder startIds(int widget, int clear, int label) { + this.widgetId = widget; this.clearId = clear; this.labelId = label; return this; + } + public FieldDefinitionsBuilder startY(int y) { this.startY = y; return this; } + public FieldDefinitionsBuilder rowHeight(int h) { this.rowHeight = h; return this; } + + // ═══════════════════════════════════════════════════════════════════ + // BUILD + // ═══════════════════════════════════════════════════════════════════ + + /** + * Processes all fields and adds widgets to the scroll window. + * @return the final Y position after all fields + */ + public int build(List fields) { + buttonFieldMap.clear(); + textFieldMap.clear(); + clearFieldMap.clear(); + effectWidgetMeta.clear(); + + int y = startY; + + for (int i = 0; i < fields.size(); i++) { + FieldDef def = fields.get(i); + + // Section header + if (def.getType() == FieldType.SECTION_HEADER) { + y += 3; + String sectionText = StatCollector.translateToLocal(def.getLabel()); + if (def.getTooltip() != null && !def.getTooltip().isEmpty()) { + sectionText += " (" + StatCollector.translateToLocal(def.getTooltip()) + ")"; + } + sw.addLabel(new GuiNpcLabel(labelId++, sectionText, colLLabel, y + 2, 0xFFFF55)); + y += 15; + continue; + } + + // Effects list renders its own multi-row block + if (def.getType() == FieldType.EFFECTS_LIST) { + y = renderEffectsList(def, y); + continue; + } + + // Two-column pair: LEFT followed by RIGHT + if (def.getColumn() == ColumnHint.LEFT + && i + 1 < fields.size() + && fields.get(i + 1).getColumn() == ColumnHint.RIGHT + && fields.get(i + 1).getType() != FieldType.SECTION_HEADER) { + renderFieldAt(def, colLLabel, colLField, colLWidth, y); + + i++; + FieldDef rightDef = fields.get(i); + renderFieldAt(rightDef, colRLabel, colRField, colRWidth, y); + + y += rowHeight; + } else { + // Full-width: compute fieldX from label pixel width + int fieldW = getFullFieldWidth(def); + String translated = StatCollector.translateToLocal(def.getLabel()); + int labelW = fontRenderer.getStringWidth(translated); + int fieldX = colLLabel + labelW + labelPadding; + + renderFieldAt(def, colLLabel, fieldX, fieldW, y); + y += rowHeight; + } + } + + return y; + } + + // ═══════════════════════════════════════════════════════════════════ + // FIELD RENDERING + // ═══════════════════════════════════════════════════════════════════ + + @SuppressWarnings({"unchecked", "rawtypes"}) + private void renderFieldAt(FieldDef def, int labelX, int fieldX, int fieldW, int y) { + sw.addLabel(new GuiNpcLabel(labelId++, def.getLabel(), labelX, y + 5, 0xFFFFFF)); + + String hover = def.getHoverText() != null ? def.getHoverText() : def.getTooltip(); + + switch (def.getType()) { + case FLOAT: { + float fVal = def.getValue() instanceof Number ? ((Number) def.getValue()).floatValue() : 0f; + GuiNpcTextField tf = new GuiNpcTextField(widgetId, parent, fontRenderer, fieldX, y, fieldW, 20, String.valueOf(fVal)); + tf.setFloatsOnly(); + if (def.hasRange()) tf.setMinMaxDefaultFloat(def.getMin(), def.getMax(), fVal); + if (!def.isEnabled()) tf.setEnabled(false); + if (hover != null) tf.setHoverText(hover); + sw.addTextField(tf); + textFieldMap.put(widgetId, def); + widgetId++; + clearId++; + break; + } + case INT: { + int iVal = def.getValue() instanceof Number ? ((Number) def.getValue()).intValue() : 0; + GuiNpcTextField tf = new GuiNpcTextField(widgetId, parent, fontRenderer, fieldX, y, fieldW, 20, String.valueOf(iVal)); + tf.setIntegersOnly(); + if (def.hasRange()) tf.setMinMaxDefault((int) def.getMin(), (int) def.getMax(), iVal); + if (!def.isEnabled()) tf.setEnabled(false); + if (hover != null) tf.setHoverText(hover); + sw.addTextField(tf); + textFieldMap.put(widgetId, def); + widgetId++; + clearId++; + break; + } + case STRING: { + String sVal = def.getValue() != null ? def.getValue().toString() : ""; + GuiNpcTextField tf = new GuiNpcTextField(widgetId, parent, fontRenderer, fieldX, y, fieldW, 20, sVal); + if (!def.isEnabled()) tf.setEnabled(false); + if (hover != null) tf.setHoverText(hover); + sw.addTextField(tf); + textFieldMap.put(widgetId, def); + widgetId++; + clearId++; + break; + } + case LABEL: { + String text = def.getValue() != null ? def.getValue().toString() : ""; + sw.addLabel(new GuiNpcLabel(labelId++, text, fieldX, y + 5, 0xAAAAAA)); + widgetId++; + clearId++; + break; + } + case BOOLEAN: { + boolean bVal = def.getValue() instanceof Boolean ? (Boolean) def.getValue() : false; + GuiNpcButtonYesNo btn = new GuiNpcButtonYesNo(widgetId, fieldX, y, fieldW, 20, bVal); + if ("gui.enabled".equals(def.getLabel()) && def.getTab() == TabTarget.GENERAL) { + btn.setTextColor(bVal ? 0x00FF00 : 0xFF0000); + } + if (!def.isEnabled()) btn.setEnabled(false); + if (hover != null) btn.setHoverText(hover); + sw.addButton(btn); + buttonFieldMap.put(widgetId, def); + widgetId++; + clearId++; + break; + } + case ENUM: { + Class> enumClass = def.getEnumClass(); + if (enumClass != null) { + Enum[] constants = enumClass.getEnumConstants(); + String[] names = new String[constants.length]; + for (int i = 0; i < constants.length; i++) names[i] = constants[i].toString(); + int selected = def.getValue() instanceof Enum ? ((Enum) def.getValue()).ordinal() : 0; + GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, fieldW, 20, names, selected); + if (!def.isEnabled()) btn.setEnabled(false); + if (hover != null) btn.setHoverText(hover); + sw.addButton(btn); + buttonFieldMap.put(widgetId, def); + } + widgetId++; + clearId++; + break; + } + case STRING_ENUM: { + String[] values = def.getStringEnumValues(); + if (values != null && values.length > 0) { + String curVal = def.getStringEnumValue(); + int selected = 0; + for (int i = 0; i < values.length; i++) { + if (values[i].equals(curVal)) { selected = i; break; } + } + GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, fieldW, 20, values, selected); + if (!def.isEnabled()) btn.setEnabled(false); + if (hover != null) btn.setHoverText(hover); + sw.addButton(btn); + buttonFieldMap.put(widgetId, def); + } + widgetId++; + clearId++; + break; + } + case SUB_GUI: { + String btnText = def.getButtonLabel(); + int btnW; + if (def.hasClearAction()) { + btnW = contentRight - 22 - fieldX; + GuiNpcButton clearBtn = new GuiNpcButton(clearId, contentRight - 20, y, 20, 20, "X"); + sw.addButton(clearBtn); + clearFieldMap.put(clearId, def); + } else { + btnW = contentRight - fieldX; + } + GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, btnW, 20, btnText); + Integer textColor = def.getButtonTextColor(); + if (textColor != null) btn.setTextColor(textColor); + if (!def.isEnabled()) btn.setEnabled(false); + if (hover != null) btn.setHoverText(hover); + sw.addButton(btn); + buttonFieldMap.put(widgetId, def); + widgetId++; + clearId++; + break; + } + } + } + + // ═══════════════════════════════════════════════════════════════════ + // EFFECTS LIST RENDERING + // ═══════════════════════════════════════════════════════════════════ + + @SuppressWarnings("unchecked") + private int renderEffectsList(FieldDef def, int y) { + // Section header + y += 3; + sw.addLabel(new GuiNpcLabel(labelId++, def.getLabel(), colLLabel, y + 2, 0xFFFF55)); + y += 15; + + List effects = (List) def.getValue(); + if (effects == null) effects = new ArrayList<>(); + + String[] typeNames = AbilityEffect.EffectType.getLangKeys(); + String[] ampValues = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}; + + for (int e = 0; e < effects.size() && e < 5; e++) { + AbilityEffect effect = effects.get(e); + + // Type selector button + int typeIdx = effect.getType().ordinal(); + GuiNpcButton typeBtn = new GuiNpcButton(widgetId, colLLabel, y, 100, 20, typeNames, typeIdx); + sw.addButton(typeBtn); + buttonFieldMap.put(widgetId, def); + effectWidgetMeta.put(widgetId, new int[]{e, 0}); + widgetId++; + + // Duration text field + GuiNpcTextField durField = new GuiNpcTextField(widgetId, parent, fontRenderer, + colLLabel + 104, y, 50, 20, String.valueOf(effect.getDurationTicks())); + durField.setIntegersOnly(); + durField.setMinMaxDefault(1, 12000, 60); + sw.addTextField(durField); + textFieldMap.put(widgetId, def); + effectWidgetMeta.put(widgetId, new int[]{e, 1}); + widgetId++; + + // Amplifier selector (0-10) + GuiNpcButton ampBtn = new GuiNpcButton(widgetId, colLLabel + 158, y, 40, 20, ampValues, effect.getAmplifier()); + sw.addButton(ampBtn); + buttonFieldMap.put(widgetId, def); + effectWidgetMeta.put(widgetId, new int[]{e, 2}); + widgetId++; + + // Delete button + GuiNpcButton delBtn = new GuiNpcButton(clearId, colLLabel + 202, y, 20, 20, "X"); + sw.addButton(delBtn); + clearFieldMap.put(clearId, def); + effectWidgetMeta.put(clearId, new int[]{e, 3}); + clearId++; + + y += rowHeight; + } + + // Add button (if < 5 effects) + if (effects.size() < 5) { + GuiNpcButton addBtn = new GuiNpcButton(widgetId, colLLabel, y, 50, 20, "gui.add"); + sw.addButton(addBtn); + buttonFieldMap.put(widgetId, def); + effectWidgetMeta.put(widgetId, new int[]{effects.size(), 4}); + widgetId++; + clearId++; + y += rowHeight; + } + + return y; + } + + private int getFullFieldWidth(FieldDef def) { + switch (def.getType()) { + case FLOAT: + case INT: return 60; + case STRING: return 200; + case BOOLEAN: return 50; + case LABEL: return 60; + case ENUM: + case STRING_ENUM: return 120; + case SUB_GUI: return def.hasClearAction() ? 175 : 200; + default: return 80; + } + } + + // ═══════════════════════════════════════════════════════════════════ + // GETTERS + // ═══════════════════════════════════════════════════════════════════ + + public Map getButtonFieldMap() { return buttonFieldMap; } + public Map getTextFieldMap() { return textFieldMap; } + public Map getClearFieldMap() { return clearFieldMap; } + public Map getEffectWidgetMeta() { return effectWidgetMeta; } + public int getNextLabelId() { return labelId; } +} diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldType.java b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldType.java index b908d9b88..019b24bdf 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldType.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/FieldType.java @@ -7,5 +7,8 @@ public enum FieldType { ENUM, STRING_ENUM, SUB_GUI, - STRING + STRING, + SECTION_HEADER, + LABEL, + EFFECTS_LIST } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/TabTarget.java b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/TabTarget.java index aba49646b..978f15224 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/gui/TabTarget.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/gui/TabTarget.java @@ -5,6 +5,5 @@ public enum TabTarget { TYPE, TARGET, EFFECTS, - VISUAL, CUSTOM } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCharge.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCharge.java index 8241fab86..48ac3c374 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCharge.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCharge.java @@ -16,6 +16,7 @@ import net.minecraft.util.Vec3; import net.minecraft.world.World; import noppes.npcs.entity.EntityNPCInterface; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityCharge; @@ -403,11 +404,11 @@ public class AbilityCharge extends Ability implements IAbilityCharge { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("stats.speed", this::getChargeSpeed, this::setChargeSpeed), - FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.floatField("ability.hitWidth", this::getHitWidth, this::setHitWidth), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("ability.chargeSpeed", this::getChargeSpeed, this::setChargeSpeed).column(ColumnHint.RIGHT), + FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback).column(ColumnHint.LEFT), + FieldDef.floatField("ability.hitWidth", this::getHitWidth, this::setHitWidth).column(ColumnHint.RIGHT), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCutter.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCutter.java index 29ae4aa22..7e265b7d1 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCutter.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCutter.java @@ -13,6 +13,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityCutter; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -30,7 +31,16 @@ public class AbilityCutter extends Ability implements IAbilityCutter { public enum SweepMode { SWIPE, - SPIN + SPIN; + + @Override + public String toString() { + switch (this) { + case SWIPE: return "ability.sweep.swipe"; + case SPIN: return "ability.sweep.spin"; + default: return name(); + } + } } private float arcAngle = 90.0f; @@ -308,20 +318,21 @@ public class AbilityCutter extends Ability implements IAbilityCutter { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("gui.range", this::getRange, this::setRange), - FieldDef.floatField("ability.arcAngle", this::getArcAngle, this::setArcAngle), - FieldDef.floatField("ability.innerRadius", this::getInnerRadius, this::setInnerRadius), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("gui.range", this::getRange, this::setRange).column(ColumnHint.RIGHT), FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.floatField("ability.sweepSpeed", this::getSweepSpeed, this::setSweepSpeed), + FieldDef.section("ability.section.sweep"), FieldDef.enumField("ability.sweepMode", SweepMode.class, this::getSweepModeEnum, this::setSweepModeEnum) .hover("ability.hover.sweepMode"), - FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing) - .hover("ability.hover.piercing"), - FieldDef.intField("ability.duration", this::getSpinDurationTicks, this::setSpinDurationTicks) + FieldDef.floatField("ability.arcAngle", this::getArcAngle, this::setArcAngle).column(ColumnHint.LEFT), + FieldDef.floatField("ability.innerRadius", this::getInnerRadius, this::setInnerRadius).column(ColumnHint.RIGHT), + FieldDef.floatField("ability.sweepSpeed", this::getSweepSpeed, this::setSweepSpeed), + FieldDef.intField("ability.spinDuration", this::getSpinDurationTicks, this::setSpinDurationTicks) .range(1, 1000) .visibleWhen(() -> this.getSweepModeEnum() == SweepMode.SPIN), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing) + .hover("ability.hover.piercing"), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java index 3684a8b73..307f8aed2 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java @@ -12,6 +12,7 @@ import net.minecraft.util.Vec3; import net.minecraft.world.World; import noppes.npcs.entity.EntityNPCInterface; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityDash; @@ -31,7 +32,16 @@ public class AbilityDash extends Ability implements IAbilityDash { */ public enum DashMode { AGGRESSIVE, - DEFENSIVE + DEFENSIVE; + + @Override + public String toString() { + switch (this) { + case AGGRESSIVE: return "ability.dash.aggressive"; + case DEFENSIVE: return "ability.dash.defensive"; + default: return name(); + } + } } /** @@ -347,10 +357,10 @@ public class AbilityDash extends Ability implements IAbilityDash { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.enumField("ability.mode", DashMode.class, this::getDashModeEnum, this::setDashModeEnum) + FieldDef.enumField("ability.dashMode", DashMode.class, this::getDashModeEnum, this::setDashModeEnum) .hover("ability.hover.dashMode"), - FieldDef.floatField("ability.dashDist", this::getDashDistance, this::setDashDistance), - FieldDef.floatField("ability.dashSpeed", this::getDashSpeed, this::setDashSpeed) + FieldDef.floatField("ability.dashDistance", this::getDashDistance, this::setDashDistance).column(ColumnHint.LEFT), + FieldDef.floatField("ability.dashSpeed", this::getDashSpeed, this::setDashSpeed).column(ColumnHint.RIGHT) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java index fb2f94646..f08092bd9 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java @@ -17,8 +17,8 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.Vec3; import net.minecraft.world.World; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; -import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityDisc; import java.util.Arrays; @@ -280,51 +280,53 @@ public class AbilityDisc extends Ability implements IAbilityDisc { public List getFieldDefinitions() { return Arrays.asList( // Type tab - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("stats.speed", this::getSpeed, this::setSpeed), - FieldDef.floatField("ability.discRadius", this::getDiscRadius, this::setDiscRadius), - FieldDef.floatField("ability.discThickness", this::getDiscThickness, this::setDiscThickness), - FieldDef.boolField("ability.homing", this::isHoming, this::setHoming) - .hover("ability.hover.homing"), - FieldDef.floatField("ability.homingStr", this::getHomingStrength, this::setHomingStrength) - .visibleWhen(this::isHoming), - FieldDef.boolField("ability.boomerang", this::isBoomerang, this::setBoomerang) - .hover("ability.hover.boomerang"), - FieldDef.intField("ability.boomerangDelay", this::getBoomerangDelay, this::setBoomerangDelay) - .visibleWhen(this::isBoomerang), - FieldDef.boolField("ability.explosive", this::isExplosive, this::setExplosive) - .hover("ability.hover.explosive"), - FieldDef.floatField("ability.explosionRad", this::getExplosionRadius, this::setExplosionRadius) - .visibleWhen(this::isExplosive), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("stats.speed", this::getSpeed, this::setSpeed).column(ColumnHint.RIGHT), FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.floatField("ability.maxDist", this::getMaxDistance, this::setMaxDistance), - FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects), + FieldDef.section("ability.section.disc"), + FieldDef.floatField("gui.radius", this::getDiscRadius, this::setDiscRadius).column(ColumnHint.LEFT), + FieldDef.floatField("gui.thickness", this::getDiscThickness, this::setDiscThickness).column(ColumnHint.RIGHT), + FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).column(ColumnHint.LEFT), + FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.homing"), + FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming) + .hover("ability.hover.homing"), + FieldDef.floatField("gui.strength", this::getHomingStrength, this::setHomingStrength) + .visibleWhen(this::isHoming), + FieldDef.section("ability.section.boomerang"), + FieldDef.boolField("gui.enabled", this::isBoomerang, this::setBoomerang) + .hover("ability.hover.boomerang"), + FieldDef.intField("gui.delay", this::getBoomerangDelay, this::setBoomerangDelay) + .visibleWhen(this::isBoomerang), + FieldDef.section("ability.section.explosive"), + FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive) + .hover("ability.hover.explosive"), + FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius) + .visibleWhen(this::isExplosive), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects), // Visual tab FieldDef.enumField("ability.anchorPoint", AnchorPoint.class, this::getAnchorPointEnum, this::setAnchorPointEnum) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), + FieldDef.section("ability.section.colors").customTab("ability.tab.visual"), FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor) - .tab(TabTarget.VISUAL), - FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), + FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled), FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth) - .tab(TabTarget.VISUAL) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled).column(ColumnHint.LEFT), FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha) - .tab(TabTarget.VISUAL).range(0, 1) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual").range(0, 1).visibleWhen(this::isOuterColorEnabled).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.effects").customTab("ability.tab.visual"), FieldDef.floatField("ability.rotationSpeed", this::getRotationSpeed, this::setRotationSpeed) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect) - .tab(TabTarget.VISUAL), - FieldDef.floatField("ability.lightningDensity", this::getLightningDensity, this::setLightningDensity) - .tab(TabTarget.VISUAL).range(0.01f, 5.0f) - .visibleWhen(this::hasLightningEffect), - FieldDef.floatField("ability.lightningRadius", this::getLightningRadius, this::setLightningRadius) - .tab(TabTarget.VISUAL).range(0.1f, 10.0f) - .visibleWhen(this::hasLightningEffect) + .customTab("ability.tab.visual"), + FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity) + .customTab("ability.tab.visual").range(0.01f, 5.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.LEFT), + FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius) + .customTab("ability.tab.visual").range(0.1f, 10.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.RIGHT) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityEnergyBeam.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityEnergyBeam.java index 89fe6acfc..c47f6e0f0 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityEnergyBeam.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityEnergyBeam.java @@ -17,8 +17,8 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.Vec3; import net.minecraft.world.World; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; -import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityEnergyBeam; import java.util.Arrays; @@ -283,49 +283,50 @@ public class AbilityEnergyBeam extends Ability implements IAbilityEnergyBeam { public List getFieldDefinitions() { return Arrays.asList( // Type tab - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("stats.speed", this::getSpeed, this::setSpeed), - FieldDef.floatField("ability.beamWidth", this::getBeamWidth, this::setBeamWidth), - FieldDef.floatField("ability.headSize", this::getHeadSize, this::setHeadSize), - FieldDef.boolField("ability.homing", this::isHoming, this::setHoming) - .hover("ability.hover.homing"), - FieldDef.floatField("ability.homingStr", this::getHomingStrength, this::setHomingStrength) - .visibleWhen(this::isHoming), - FieldDef.floatField("ability.homingRange", this::getHomingRange, this::setHomingRange) - .visibleWhen(this::isHoming), - FieldDef.floatField("ability.maxDist", this::getMaxDistance, this::setMaxDistance), - FieldDef.boolField("ability.explosive", this::isExplosive, this::setExplosive) - .hover("ability.hover.explosive"), - FieldDef.floatField("ability.explosionRad", this::getExplosionRadius, this::setExplosionRadius) - .visibleWhen(this::isExplosive), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("stats.speed", this::getSpeed, this::setSpeed).column(ColumnHint.RIGHT), FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects), + FieldDef.section("ability.section.beam"), + FieldDef.floatField("ability.beamWidth", this::getBeamWidth, this::setBeamWidth).column(ColumnHint.LEFT), + FieldDef.floatField("ability.headSize", this::getHeadSize, this::setHeadSize).column(ColumnHint.RIGHT), + FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).column(ColumnHint.LEFT), + FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.homing"), + FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming) + .hover("ability.hover.homing"), + FieldDef.floatField("gui.strength", this::getHomingStrength, this::setHomingStrength) + .visibleWhen(this::isHoming).column(ColumnHint.LEFT), + FieldDef.floatField("gui.range", this::getHomingRange, this::setHomingRange) + .visibleWhen(this::isHoming).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.explosive"), + FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive) + .hover("ability.hover.explosive"), + FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius) + .visibleWhen(this::isExplosive), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects), // Visual tab FieldDef.enumField("ability.anchorPoint", AnchorPoint.class, this::getAnchorPointEnum, this::setAnchorPointEnum) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), + FieldDef.section("ability.section.colors").customTab("ability.tab.visual"), FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor) - .tab(TabTarget.VISUAL), - FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), + FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled), FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth) - .tab(TabTarget.VISUAL) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled).column(ColumnHint.LEFT), FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha) - .tab(TabTarget.VISUAL).range(0, 1) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual").range(0, 1).visibleWhen(this::isOuterColorEnabled).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.effects").customTab("ability.tab.visual"), FieldDef.floatField("ability.rotationSpeed", this::getRotationSpeed, this::setRotationSpeed) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect) - .tab(TabTarget.VISUAL), - FieldDef.floatField("ability.lightningDensity", this::getLightningDensity, this::setLightningDensity) - .tab(TabTarget.VISUAL).range(0.01f, 5.0f) - .visibleWhen(this::hasLightningEffect), - FieldDef.floatField("ability.lightningRadius", this::getLightningRadius, this::setLightningRadius) - .tab(TabTarget.VISUAL).range(0.1f, 10.0f) - .visibleWhen(this::hasLightningEffect) + .customTab("ability.tab.visual"), + FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity) + .customTab("ability.tab.visual").range(0.01f, 5.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.LEFT), + FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius) + .customTab("ability.tab.visual").range(0.1f, 10.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.RIGHT) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java index bfa7b0f34..bdf95f882 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java @@ -12,6 +12,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityGuard; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -44,7 +45,16 @@ public class AbilityGuard extends Ability implements IAbilityGuard { public enum CounterType { FLAT, - PERCENT + PERCENT; + + @Override + public String toString() { + switch (this) { + case FLAT: return "ability.counter.flat"; + case PERCENT: return "ability.counter.percent"; + default: return name(); + } + } } public AbilityGuard() { @@ -287,23 +297,21 @@ public class AbilityGuard extends Ability implements IAbilityGuard { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks) - .range(1, 1000), - FieldDef.floatField("ability.dmgReduce", this::getDamageReduction, this::setDamageReduction), - FieldDef.boolField("ability.canCounter", this::isCanCounter, this::setCanCounter) - .hover("ability.hover.canCounter"), - FieldDef.enumField("ability.counterType", CounterType.class, this::getCounterTypeEnum, this::setCounterTypeEnum) - .hover("ability.hover.counterType") + FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks).range(1, 1000), + FieldDef.floatField("ability.damageReduction", this::getDamageReduction, this::setDamageReduction), + FieldDef.section("ability.section.counter"), + FieldDef.boolField("gui.enabled", this::isCanCounter, this::setCanCounter).hover("ability.hover.canCounter"), + FieldDef.enumField("gui.type", CounterType.class, this::getCounterTypeEnum, this::setCounterTypeEnum) + .hover("ability.hover.counterType").visibleWhen(this::isCanCounter), + FieldDef.floatField("gui.value", this::getCounterValue, this::setCounterValue) + .visibleWhen(this::isCanCounter).column(ColumnHint.LEFT), + FieldDef.floatField("gui.chance", this::getCounterChance, this::setCounterChance) + .visibleWhen(this::isCanCounter).column(ColumnHint.RIGHT), + FieldDef.stringField("gui.sound", this::getCounterSound, this::setCounterSound) .visibleWhen(this::isCanCounter), - FieldDef.floatField("ability.counterValue", this::getCounterValue, this::setCounterValue) + FieldDef.intField("gui.animation", this::getCounterAnimationId, this::setCounterAnimationId) .visibleWhen(this::isCanCounter), - FieldDef.floatField("ability.counterChance", this::getCounterChance, this::setCounterChance) - .visibleWhen(this::isCanCounter), - FieldDef.stringField("ability.counterSound", this::getCounterSound, this::setCounterSound) - .visibleWhen(this::isCanCounter), - FieldDef.intField("ability.counterAnim", this::getCounterAnimationId, this::setCounterAnimationId) - .visibleWhen(this::isCanCounter), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java index cd5194b29..11e37d68a 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java @@ -16,6 +16,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityHazard; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -34,14 +35,35 @@ public class AbilityHazard extends Ability implements IAbilityHazard { public enum HazardShape { CIRCLE, RING, - CONE + CONE; + + @Override + public String toString() { + switch (this) { + case CIRCLE: return "ability.shape.circle"; + case RING: return "ability.shape.ring"; + case CONE: return "ability.shape.cone"; + default: return name(); + } + } } public enum PlacementMode { AT_CASTER, AT_TARGET, FOLLOW_CASTER, - FOLLOW_TARGET + FOLLOW_TARGET; + + @Override + public String toString() { + switch (this) { + case AT_CASTER: return "ability.placement.atCaster"; + case AT_TARGET: return "ability.placement.atTarget"; + case FOLLOW_CASTER: return "ability.placement.followCaster"; + case FOLLOW_TARGET: return "ability.placement.followTarget"; + default: return name(); + } + } } private int durationTicks = 100; @@ -612,22 +634,24 @@ public class AbilityHazard extends Ability implements IAbilityHazard { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks) - .range(1, 2000), + FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks).range(1, 2000), FieldDef.enumField("ability.shape", HazardShape.class, this::getShapeEnum, this::setShapeEnum) .hover("ability.hover.shape"), FieldDef.enumField("ability.placement", PlacementMode.class, this::getPlacementEnum, this::setPlacementEnum) .hover("ability.hover.placement"), - FieldDef.floatField("ability.radius", this::getRadius, this::setRadius), - FieldDef.floatField("ability.innerRadius", this::getInnerRadius, this::setInnerRadius), - FieldDef.floatField("ability.dmgPerSec", this::getDamagePerSecond, this::setDamagePerSecond), - FieldDef.intField("ability.dmgInterval", this::getDamageInterval, this::setDamageInterval), - FieldDef.intField("ability.slownessLvl", this::getSlownessLevel, this::setSlownessLevel), - FieldDef.intField("ability.debuffDur", this::getDebuffDuration, this::setDebuffDuration), - FieldDef.intField("ability.poisonLvl", this::getPoisonLevel, this::setPoisonLevel), + FieldDef.section("ability.section.area"), + FieldDef.floatField("gui.radius", this::getRadius, this::setRadius).column(ColumnHint.LEFT), + FieldDef.floatField("ability.innerRadius", this::getInnerRadius, this::setInnerRadius).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.damage"), + FieldDef.floatField("ability.damagePerSecond", this::getDamagePerSecond, this::setDamagePerSecond).column(ColumnHint.LEFT), + FieldDef.intField("ability.damageInterval", this::getDamageInterval, this::setDamageInterval).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.debuffs"), + FieldDef.intField("ability.slownessLevel", this::getSlownessLevel, this::setSlownessLevel).column(ColumnHint.LEFT), + FieldDef.intField("ability.debuffDuration", this::getDebuffDuration, this::setDebuffDuration).column(ColumnHint.RIGHT), + FieldDef.intField("ability.poisonLevel", this::getPoisonLevel, this::setPoisonLevel), FieldDef.boolField("ability.affectsCaster", this::isAffectsCaster, this::setAffectsCaster) .hover("ability.hover.affectsCaster"), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java index 543a26821..764bef280 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java @@ -11,6 +11,7 @@ import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import noppes.npcs.entity.EntityNPCInterface; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityHeal; @@ -285,18 +286,18 @@ public class AbilityHeal extends Ability implements IAbilityHeal { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.boolField("dialog.instant", this::isInstantHeal, this::setInstantHeal) + FieldDef.boolField("ability.instantHeal", this::isInstantHeal, this::setInstantHeal) .hover("ability.hover.instant"), FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks) - .range(1, 1000) - .visibleWhen(() -> !this.isInstantHeal()), - FieldDef.floatField("ability.healAmount", this::getHealAmount, this::setHealAmount), - FieldDef.floatField("ability.healPercent", this::getHealPercent, this::setHealPercent), + .range(1, 1000).visibleWhen(() -> !this.isInstantHeal()), + FieldDef.section("ability.section.healing"), + FieldDef.floatField("ability.healAmount", this::getHealAmount, this::setHealAmount).column(ColumnHint.LEFT), + FieldDef.floatField("ability.healPercent", this::getHealPercent, this::setHealPercent).column(ColumnHint.RIGHT), FieldDef.floatField("ability.healRadius", this::getHealRadius, this::setHealRadius), FieldDef.boolField("ability.healSelf", this::isHealSelf, this::setHealSelf) - .hover("ability.hover.healSelf"), + .hover("ability.hover.healSelf").column(ColumnHint.LEFT), FieldDef.boolField("ability.healAllies", this::isHealAllies, this::setHealAllies) - .hover("ability.hover.healAllies") + .hover("ability.hover.healAllies").column(ColumnHint.RIGHT) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeavyHit.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeavyHit.java index a86662304..0eca4f4c7 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeavyHit.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeavyHit.java @@ -7,6 +7,7 @@ import kamkeel.npcs.controllers.data.telegraph.TelegraphType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityHeavyHit; @@ -110,9 +111,9 @@ public class AbilityHeavyHit extends Ability implements IAbilityHeavyHit { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback).column(ColumnHint.RIGHT), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java index 4d3ce9fbe..bc33f689b 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java @@ -6,8 +6,8 @@ import kamkeel.npcs.controllers.data.ability.Ability; import kamkeel.npcs.controllers.data.ability.LockMovementType; import kamkeel.npcs.controllers.data.ability.TargetingMode; import kamkeel.npcs.controllers.data.ability.data.EnergyColorData; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; -import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData; import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData; import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData; @@ -231,39 +231,34 @@ public class AbilityLaserShot extends Ability implements IAbilityLaserShot { return Arrays.asList( // Type tab FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.laserWidth", this::getLaserWidth, this::setLaserWidth), - FieldDef.floatField("ability.expansionSpeed", this::getExpansionSpeed, this::setExpansionSpeed), - FieldDef.intField("ability.lingerTicks", this::getLingerTicks, this::setLingerTicks), - FieldDef.floatField("ability.maxDist", this::getMaxDistance, this::setMaxDistance), - FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime), - FieldDef.boolField("ability.explosive", this::isExplosive, this::setExplosive) - .hover("ability.hover.explosive"), - FieldDef.floatField("ability.explosionRad", this::getExplosionRadius, this::setExplosionRadius) - .visibleWhen(this::isExplosive), - FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.floatField("ability.knockbackUp", this::getKnockbackUp, this::setKnockbackUp), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects), + FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback).column(ColumnHint.LEFT), + FieldDef.floatField("ability.knockbackUp", this::getKnockbackUp, this::setKnockbackUp).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.beam"), + FieldDef.floatField("ability.laserWidth", this::getLaserWidth, this::setLaserWidth).column(ColumnHint.LEFT), + FieldDef.floatField("ability.expansionSpeed", this::getExpansionSpeed, this::setExpansionSpeed).column(ColumnHint.RIGHT), + FieldDef.intField("ability.lingerTicks", this::getLingerTicks, this::setLingerTicks).column(ColumnHint.LEFT), + FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).column(ColumnHint.RIGHT), + FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance), + FieldDef.section("ability.section.explosive"), + FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"), + FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius).visibleWhen(this::isExplosive), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects), // Visual tab - FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor) - .tab(TabTarget.VISUAL), + FieldDef.section("ability.section.colors").customTab("ability.tab.visual"), + FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor).customTab("ability.tab.visual"), + FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled).customTab("ability.tab.visual"), FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) - .tab(TabTarget.VISUAL), - FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled), FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth) - .tab(TabTarget.VISUAL) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled).column(ColumnHint.LEFT), FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha) - .tab(TabTarget.VISUAL).range(0, 1) - .visibleWhen(this::isOuterColorEnabled), - FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect) - .tab(TabTarget.VISUAL), - FieldDef.floatField("ability.lightningDensity", this::getLightningDensity, this::setLightningDensity) - .tab(TabTarget.VISUAL).range(0.01f, 5.0f) - .visibleWhen(this::hasLightningEffect), - FieldDef.floatField("ability.lightningRadius", this::getLightningRadius, this::setLightningRadius) - .tab(TabTarget.VISUAL).range(0.1f, 10.0f) - .visibleWhen(this::hasLightningEffect) + .customTab("ability.tab.visual").range(0, 1).visibleWhen(this::isOuterColorEnabled).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.effects").customTab("ability.tab.visual"), + FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect).customTab("ability.tab.visual"), + FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity) + .customTab("ability.tab.visual").range(0.01f, 5.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.LEFT), + FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius) + .customTab("ability.tab.visual").range(0.1f, 10.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.RIGHT) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java index f4c156f49..66ebdf2c7 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java @@ -17,8 +17,8 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.Vec3; import net.minecraft.world.World; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; -import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityOrb; import java.util.Arrays; @@ -419,47 +419,38 @@ public class AbilityOrb extends Ability implements IAbilityOrb { public List getFieldDefinitions() { return Arrays.asList( // Type tab - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("stats.speed", this::getOrbSpeed, this::setOrbSpeed), - FieldDef.floatField("stats.size", this::getOrbSize, this::setOrbSize), - FieldDef.floatField("ability.maxDist", this::getMaxDistance, this::setMaxDistance), - FieldDef.boolField("ability.homing", this::isHoming, this::setHoming) - .hover("ability.hover.homing"), - FieldDef.floatField("ability.homingStr", this::getHomingStrength, this::setHomingStrength) - .visibleWhen(this::isHoming), - FieldDef.boolField("ability.explosive", this::isExplosive, this::setExplosive) - .hover("ability.hover.explosive"), - FieldDef.floatField("ability.explosionRad", this::getExplosionRadius, this::setExplosionRadius) - .visibleWhen(this::isExplosive), - FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("stats.speed", this::getOrbSpeed, this::setOrbSpeed).column(ColumnHint.RIGHT), + FieldDef.floatField("stats.size", this::getOrbSize, this::setOrbSize).column(ColumnHint.LEFT), + FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback).column(ColumnHint.RIGHT), + FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).column(ColumnHint.LEFT), + FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.homing"), + FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming).hover("ability.hover.homing"), + FieldDef.floatField("gui.strength", this::getHomingStrength, this::setHomingStrength).visibleWhen(this::isHoming), + FieldDef.section("ability.section.explosive"), + FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"), + FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius).visibleWhen(this::isExplosive), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects), // Visual tab FieldDef.enumField("ability.anchorPoint", AnchorPoint.class, this::getAnchorPointEnum, this::setAnchorPointEnum) - .tab(TabTarget.VISUAL), - FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor) - .tab(TabTarget.VISUAL), - FieldDef.floatField("ability.rotationSpeed", this::getRotationSpeed, this::setRotationSpeed) - .tab(TabTarget.VISUAL), - FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled) - .tab(TabTarget.VISUAL), - FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha) - .tab(TabTarget.VISUAL).range(0, 1) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual"), + FieldDef.section("ability.section.colors").customTab("ability.tab.visual"), + FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor).customTab("ability.tab.visual"), + FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled).customTab("ability.tab.visual"), FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) - .tab(TabTarget.VISUAL) - .visibleWhen(this::isOuterColorEnabled), + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled), FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth) - .tab(TabTarget.VISUAL) - .visibleWhen(this::isOuterColorEnabled), - FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect) - .tab(TabTarget.VISUAL), - FieldDef.floatField("ability.lightningDensity", this::getLightningDensity, this::setLightningDensity) - .tab(TabTarget.VISUAL).range(0.01f, 5.0f) - .visibleWhen(this::hasLightningEffect), - FieldDef.floatField("ability.lightningRadius", this::getLightningRadius, this::setLightningRadius) - .tab(TabTarget.VISUAL).range(0.1f, 10.0f) - .visibleWhen(this::hasLightningEffect) + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled).column(ColumnHint.LEFT), + FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha) + .customTab("ability.tab.visual").range(0, 1).visibleWhen(this::isOuterColorEnabled).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.effects").customTab("ability.tab.visual"), + FieldDef.floatField("ability.rotationSpeed", this::getRotationSpeed, this::setRotationSpeed).customTab("ability.tab.visual"), + FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect).customTab("ability.tab.visual"), + FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity) + .customTab("ability.tab.visual").range(0.01f, 5.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.LEFT), + FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius) + .customTab("ability.tab.visual").range(0.1f, 10.0f).visibleWhen(this::hasLightningEffect).column(ColumnHint.RIGHT) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityProjectile.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityProjectile.java index 03b815441..902c9a194 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityProjectile.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityProjectile.java @@ -12,6 +12,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityProjectile; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -255,19 +256,17 @@ public class AbilityProjectile extends Ability implements IAbilityProjectile { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("stats.speed", this::getSpeed, this::setSpeed), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("stats.speed", this::getSpeed, this::setSpeed).column(ColumnHint.RIGHT), FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.stringEnumField("ability.projType", new String[]{"fireball", "arrow", "magic"}, this::getProjectileType, this::setProjectileType), - FieldDef.boolField("ability.explosive", this::isExplosive, this::setExplosive) - .hover("ability.hover.explosive"), - FieldDef.floatField("ability.explosionRad", this::getExplosionRadius, this::setExplosionRadius) - .visibleWhen(this::isExplosive), - FieldDef.boolField("ability.homing", this::isHoming, this::setHoming) - .hover("ability.hover.homing"), - FieldDef.floatField("ability.homingStr", this::getHomingStrength, this::setHomingStrength) - .visibleWhen(this::isHoming), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.stringEnumField("ability.projectileType", new String[]{"fireball", "arrow", "magic"}, this::getProjectileType, this::setProjectileType), + FieldDef.section("ability.section.homing"), + FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming).hover("ability.hover.homing"), + FieldDef.floatField("gui.strength", this::getHomingStrength, this::setHomingStrength).visibleWhen(this::isHoming), + FieldDef.section("ability.section.explosive"), + FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"), + FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius).visibleWhen(this::isExplosive), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityShockwave.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityShockwave.java index 83af93df7..768496ec7 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityShockwave.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityShockwave.java @@ -11,6 +11,7 @@ import net.minecraft.util.DamageSource; import net.minecraft.world.World; import noppes.npcs.entity.EntityNPCInterface; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityShockwave; @@ -171,10 +172,11 @@ public class AbilityShockwave extends Ability implements IAbilityShockwave { public List getFieldDefinitions() { return Arrays.asList( FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.pushRadius", this::getPushRadius, this::setPushRadius), - FieldDef.floatField("ability.pushStrength", this::getPushStrength, this::setPushStrength), + FieldDef.section("ability.section.push"), + FieldDef.floatField("gui.radius", this::getPushRadius, this::setPushRadius).column(ColumnHint.LEFT), + FieldDef.floatField("gui.strength", this::getPushStrength, this::setPushStrength).column(ColumnHint.RIGHT), FieldDef.intField("ability.maxTargets", this::getMaxTargets, this::setMaxTargets), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java index b26e203c1..d5a2599fd 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java @@ -18,6 +18,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilitySlam; import somehussar.gui.annotationHandling.GuiEditable; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import java.util.Arrays; @@ -572,11 +573,12 @@ public class AbilitySlam extends Ability implements IAbilitySlam { public List getFieldDefinitions() { return Arrays.asList( FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.radius", this::getRadius, this::setRadius), - FieldDef.floatField("ability.knockback", this::getKnockbackStrength, this::setKnockbackStrength), - FieldDef.floatField("ability.leapSpeed", this::getLeapSpeed, this::setLeapSpeed), - FieldDef.floatField("ability.leapHeight", this::getLeapHeight, this::setLeapHeight), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.floatField("gui.radius", this::getRadius, this::setRadius).column(ColumnHint.LEFT), + FieldDef.floatField("ability.knockback", this::getKnockbackStrength, this::setKnockbackStrength).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.leap"), + FieldDef.floatField("gui.speed", this::getLeapSpeed, this::setLeapSpeed).column(ColumnHint.LEFT), + FieldDef.floatField("gui.height", this::getLeapHeight, this::setLeapHeight).column(ColumnHint.RIGHT), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySweeper.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySweeper.java index ef00df35e..783f0c1f6 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySweeper.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySweeper.java @@ -4,8 +4,8 @@ import kamkeel.npcs.controllers.data.ability.Ability; import kamkeel.npcs.controllers.data.ability.LockMovementType; import kamkeel.npcs.controllers.data.ability.TargetingMode; import kamkeel.npcs.controllers.data.ability.data.EnergyColorData; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; -import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import kamkeel.npcs.controllers.data.telegraph.Telegraph; import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance; import kamkeel.npcs.controllers.data.telegraph.TelegraphType; @@ -203,29 +203,29 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper { @Override public List getFieldDefinitions() { return Arrays.asList( - // Type tab - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.intField("ability.dmgInterval", this::getDamageInterval, this::setDamageInterval), - FieldDef.floatField("ability.beamLength", this::getBeamLength, this::setBeamLength), - FieldDef.floatField("ability.beamWidth", this::getBeamWidth, this::setBeamWidth), - FieldDef.floatField("ability.beamHeight", this::getBeamHeight, this::setBeamHeight), - FieldDef.floatField("ability.sweepSpeed", this::getSweepSpeed, this::setSweepSpeed), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.intField("ability.damageInterval", this::getDamageInterval, this::setDamageInterval).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.beam"), + FieldDef.floatField("gui.length", this::getBeamLength, this::setBeamLength).column(ColumnHint.LEFT), + FieldDef.floatField("gui.width", this::getBeamWidth, this::setBeamWidth).column(ColumnHint.RIGHT), + FieldDef.floatField("gui.height", this::getBeamHeight, this::setBeamHeight).column(ColumnHint.LEFT), + FieldDef.floatField("ability.sweepSpeed", this::getSweepSpeed, this::setSweepSpeed).column(ColumnHint.RIGHT), FieldDef.intField("ability.rotations", this::getNumberOfRotations, this::setNumberOfRotations), FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing) - .hover("ability.hover.piercing"), + .hover("ability.hover.piercing").column(ColumnHint.LEFT), FieldDef.boolField("ability.lockTarget", this::isLockOnTarget, this::setLockOnTarget) - .hover("ability.hover.lockTarget"), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects), + .hover("ability.hover.lockTarget").column(ColumnHint.RIGHT), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects), // Visual tab + FieldDef.section("ability.section.colors").customTab("ability.tab.visual"), FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor) - .tab(TabTarget.VISUAL), - FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled) - .tab(TabTarget.VISUAL), + .customTab("ability.tab.visual"), + FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled), FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth) - .tab(TabTarget.VISUAL) - .visibleWhen(this::isOuterColorEnabled) + .customTab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java index 284bdec03..28ad00cd2 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java @@ -16,6 +16,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityTeleport; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -34,7 +35,17 @@ public class AbilityTeleport extends Ability implements IAbilityTeleport { public enum TeleportMode { BLINK, BEHIND, - SINGLE + SINGLE; + + @Override + public String toString() { + switch (this) { + case BLINK: return "ability.teleport.blink"; + case BEHIND: return "ability.teleport.behind"; + case SINGLE: return "ability.teleport.single"; + default: return name(); + } + } } private static final Random RANDOM = new Random(); @@ -534,21 +545,22 @@ public class AbilityTeleport extends Ability implements IAbilityTeleport { FieldDef.floatField("ability.blinkRadius", this::getBlinkRadius, this::setBlinkRadius) .visibleWhen(() -> this.getModeEnum() == TeleportMode.BLINK || this.getModeEnum() == TeleportMode.SINGLE), FieldDef.intField("ability.blinkCount", this::getBlinkCount, this::setBlinkCount) - .visibleWhen(() -> this.getModeEnum() == TeleportMode.BLINK), + .visibleWhen(() -> this.getModeEnum() == TeleportMode.BLINK).column(ColumnHint.LEFT), FieldDef.intField("ability.blinkDelay", this::getBlinkDelayTicks, this::setBlinkDelayTicks) - .visibleWhen(() -> this.getModeEnum() == TeleportMode.BLINK), + .visibleWhen(() -> this.getModeEnum() == TeleportMode.BLINK).column(ColumnHint.RIGHT), FieldDef.floatField("ability.behindDistance", this::getBehindDistance, this::setBehindDistance) .visibleWhen(() -> this.getModeEnum() == TeleportMode.BEHIND), FieldDef.boolField("ability.lineOfSight", this::isRequireLineOfSight, this::setRequireLineOfSight) .hover("ability.hover.lineOfSight") .visibleWhen(() -> this.getModeEnum() == TeleportMode.BLINK || this.getModeEnum() == TeleportMode.SINGLE), - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.dmgRadius", this::getDamageRadius, this::setDamageRadius), - FieldDef.boolField("ability.dmgAtStart", this::isDamageAtStart, this::setDamageAtStart) - .hover("ability.hover.dmgAtStart"), - FieldDef.boolField("ability.dmgAtEnd", this::isDamageAtEnd, this::setDamageAtEnd) - .hover("ability.hover.dmgAtEnd"), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.section("ability.section.damage"), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("gui.radius", this::getDamageRadius, this::setDamageRadius).column(ColumnHint.RIGHT), + FieldDef.boolField("ability.damageAtStart", this::isDamageAtStart, this::setDamageAtStart) + .hover("ability.hover.dmgAtStart").column(ColumnHint.LEFT), + FieldDef.boolField("ability.damageAtEnd", this::isDamageAtEnd, this::setDamageAtEnd) + .hover("ability.hover.dmgAtEnd").column(ColumnHint.RIGHT), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java index 2cb7a8a26..e27e28740 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java @@ -14,6 +14,7 @@ import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityTrap; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; @@ -33,7 +34,17 @@ public class AbilityTrap extends Ability implements IAbilityTrap { public enum TrapPlacement { AT_CASTER, AT_TARGET, - AHEAD_OF_CASTER + AHEAD_OF_CASTER; + + @Override + public String toString() { + switch (this) { + case AT_CASTER: return "ability.trapPlace.atCaster"; + case AT_TARGET: return "ability.trapPlace.atTarget"; + case AHEAD_OF_CASTER: return "ability.trapPlace.aheadOfCaster"; + default: return name(); + } + } } private int durationTicks = 200; @@ -465,13 +476,16 @@ public class AbilityTrap extends Ability implements IAbilityTrap { .range(1, 2000), FieldDef.enumField("ability.placement", TrapPlacement.class, this::getPlacementEnum, this::setPlacementEnum) .hover("ability.hover.placement"), - FieldDef.floatField("ability.triggerRad", this::getTriggerRadius, this::setTriggerRadius), - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.dmgRadius", this::getDamageRadius, this::setDamageRadius), - FieldDef.intField("ability.armTime", this::getArmTime, this::setArmTime), - FieldDef.intField("ability.maxTriggers", this::getMaxTriggers, this::setMaxTriggers), + FieldDef.section("ability.section.trigger"), + FieldDef.floatField("gui.radius", this::getTriggerRadius, this::setTriggerRadius).column(ColumnHint.LEFT), + FieldDef.intField("ability.armTime", this::getArmTime, this::setArmTime).column(ColumnHint.RIGHT), + FieldDef.intField("ability.maxTriggers", this::getMaxTriggers, this::setMaxTriggers).column(ColumnHint.LEFT), + FieldDef.intField("ability.triggerCooldown", this::getTriggerCooldown, this::setTriggerCooldown).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.damage"), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("gui.radius", this::getDamageRadius, this::setDamageRadius).column(ColumnHint.RIGHT), FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityVortex.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityVortex.java index 61e246bc9..3304cad74 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityVortex.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityVortex.java @@ -10,6 +10,7 @@ import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import noppes.npcs.entity.EntityNPCInterface; +import kamkeel.npcs.controllers.data.ability.gui.ColumnHint; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import noppes.npcs.api.ability.type.IAbilityVortex; @@ -304,17 +305,22 @@ public class AbilityVortex extends Ability implements IAbilityVortex { @Override public List getFieldDefinitions() { return Arrays.asList( - FieldDef.floatField("ability.pullRadius", this::getPullRadius, this::setPullRadius), - FieldDef.floatField("ability.pullStrength", this::getPullStrength, this::setPullStrength), - FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), - FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback), - FieldDef.boolField("ability.aoe", this::isAoe, this::setAoe) + FieldDef.floatField("ability.pullRadius", this::getPullRadius, this::setPullRadius).column(ColumnHint.LEFT), + FieldDef.floatField("ability.pullStrength", this::getPullStrength, this::setPullStrength).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.damage"), + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage).column(ColumnHint.LEFT), + FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback).column(ColumnHint.RIGHT), + FieldDef.section("ability.section.aoe"), + FieldDef.boolField("gui.enabled", this::isAoe, this::setAoe) .hover("ability.hover.aoe"), - FieldDef.intField("ability.maxTargets", this::getMaxTargets, this::setMaxTargets), - FieldDef.boolField("ability.dmgOnPull", this::isDamageOnPull, this::setDamageOnPull) + FieldDef.intField("ability.maxTargets", this::getMaxTargets, this::setMaxTargets) + .visibleWhen(this::isAoe), + FieldDef.section("ability.section.pullDamage"), + FieldDef.boolField("gui.enabled", this::isDamageOnPull, this::setDamageOnPull) .hover("ability.hover.dmgOnPull"), - FieldDef.floatField("ability.pullDamage", this::getPullDamage, this::setPullDamage), - FieldDef.effectsSubGui("ability.effects", this::getEffects, this::setEffects) + FieldDef.floatField("enchantment.damage", this::getPullDamage, this::setPullDamage) + .visibleWhen(this::isDamageOnPull), + FieldDef.effectsListField("ability.effects", this::getEffects, this::setEffects) ); } } diff --git a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityConfig.java b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityConfig.java index 9c862e3b0..04fa3f7f9 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityConfig.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityConfig.java @@ -1,8 +1,10 @@ package noppes.npcs.client.gui.advanced; import kamkeel.npcs.controllers.data.ability.Ability; +import kamkeel.npcs.controllers.data.ability.AbilityEffect; import kamkeel.npcs.controllers.data.ability.Condition; import kamkeel.npcs.controllers.data.ability.gui.FieldDef; +import kamkeel.npcs.controllers.data.ability.gui.FieldDefinitionsBuilder; import kamkeel.npcs.controllers.data.ability.gui.FieldType; import kamkeel.npcs.controllers.data.ability.gui.TabTarget; import net.minecraft.client.gui.GuiButton; @@ -19,58 +21,62 @@ import noppes.npcs.client.gui.util.ITextfieldListener; import noppes.npcs.client.gui.util.SubGuiInterface; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldListener, ISubGuiListener { private static final int DECLARATIVE_ID_START = 1000; private static final int CLEAR_ID_START = 2000; + private static final int LABEL_ID_START = 3000; + // Fixed tab indices private static final int TAB_GENERAL = 0; private static final int TAB_TYPE = 1; private static final int TAB_TARGET = 2; private static final int TAB_EFFECTS = 3; - private static final int TAB_VISUAL = 4; + // Custom tabs start at index 4 + + private static final int L_LABEL_X = 5; + private static final int ROW_H = 24; private final Ability ability; private final IAbilityConfigCallback callback; private int activeTab = TAB_GENERAL; private List fieldDefs; - private boolean hasTypeFields; - private boolean hasVisualFields; + private List customTabNames; - // Conditions are complex (list with add/edit/remove) - cached separately private List conditions; private int editingConditionIndex = -1; - // Event routing - private final Map buttonFieldMap = new HashMap<>(); - private final Map textFieldMap = new HashMap<>(); - private final Map clearFieldMap = new HashMap<>(); + private FieldDefinitionsBuilder builder; private FieldDef activeSubGuiField = null; + // Scroll position preservation per tab + private float[] tabScrollY; + public SubGuiAbilityConfig(Ability ability, IAbilityConfigCallback callback) { this.ability = ability; this.callback = callback; this.conditions = new ArrayList<>(ability.getConditions()); this.fieldDefs = ability.getAllFieldDefinitions(); - this.hasTypeFields = hasFieldsForTab(TabTarget.TYPE); - this.hasVisualFields = hasFieldsForTab(TabTarget.VISUAL); + discoverCustomTabs(); setBackground("menubg.png", 217); xSize = 356; ySize = 200; } - private boolean hasFieldsForTab(TabTarget target) { + private void discoverCustomTabs() { + customTabNames = new ArrayList<>(); for (FieldDef def : fieldDefs) { - if (def.getTab() == target) return true; + if (def.getTab() == TabTarget.CUSTOM && def.getCustomTabName() != null + && !customTabNames.contains(def.getCustomTabName())) { + customTabNames.add(def.getCustomTabName()); + } } - return false; + tabScrollY = new float[4 + customTabNames.size()]; } // ═══════════════════════════════════════════════════════════════════════════ @@ -79,23 +85,24 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi @Override public void initGui() { - super.initGui(); - buttonFieldMap.clear(); - textFieldMap.clear(); - clearFieldMap.clear(); + // Save scroll position before super clears scroll windows + GuiScrollWindow oldSw = getScrollableGui(0); + if (oldSw != null && activeTab < tabScrollY.length) { + tabScrollY[activeTab] = oldSw.nextScrollY; + } - // Tab buttons + super.initGui(); + + // Tab buttons — always show General, Type, Target, Effects GuiMenuTopButton generalTab = new GuiMenuTopButton(90, guiLeft + 4, guiTop - 17, "menu.general"); generalTab.active = (activeTab == TAB_GENERAL); addTopButton(generalTab); GuiMenuTopButton lastTab = generalTab; - if (hasTypeFields) { - GuiMenuTopButton typeTab = new GuiMenuTopButton(91, lastTab, "gui.type"); - typeTab.active = (activeTab == TAB_TYPE); - addTopButton(typeTab); - lastTab = typeTab; - } + GuiMenuTopButton typeTab = new GuiMenuTopButton(91, lastTab, "gui.type"); + typeTab.active = (activeTab == TAB_TYPE); + addTopButton(typeTab); + lastTab = typeTab; GuiMenuTopButton targetTab = new GuiMenuTopButton(92, lastTab, "script.target"); targetTab.active = (activeTab == TAB_TARGET); @@ -107,194 +114,117 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi addTopButton(effectsTab); lastTab = effectsTab; - if (hasVisualFields) { - GuiMenuTopButton visualTab = new GuiMenuTopButton(94, lastTab, "ability.tab.visual"); - visualTab.active = (activeTab == TAB_VISUAL); - addTopButton(visualTab); + // Custom tabs (Visual, etc.) + for (int i = 0; i < customTabNames.size(); i++) { + GuiMenuTopButton ct = new GuiMenuTopButton(94 + i, lastTab, customTabNames.get(i)); + ct.active = (activeTab == 4 + i); + addTopButton(ct); + lastTab = ct; } GuiMenuTopButton closeBtn = new GuiMenuTopButton(-1000, guiLeft + xSize - 22, guiTop - 17, "X"); addTopButton(closeBtn); - // Scroll window for tab content + // Scroll window int swX = guiLeft + 4; int swY = guiTop + 5; int swW = xSize - 8; int swH = ySize - 10; GuiScrollWindow sw = new GuiScrollWindow(this, swX, swY, swW, swH, 0); + sw.backgroundColor = 0x88000000; + addScrollableGui(0, sw); - TabTarget tabTarget = tabTargetForIndex(activeTab); int y = 5; - int idCounter = DECLARATIVE_ID_START; - int clearCounter = CLEAR_ID_START; + int labelCounter = LABEL_ID_START; - // General tab: show type label at top + // General tab: type label at top if (activeTab == TAB_GENERAL) { - sw.addLabel(new GuiNpcLabel(1, "gui.type", 5, y + 5)); - sw.addLabel(new GuiNpcLabel(2, ability.getTypeId(), 50, y + 5)); - y += 24; + sw.addLabel(new GuiNpcLabel(labelCounter++, "gui.type", 5, y + 5, 0xFFFFFF)); + sw.addLabel(new GuiNpcLabel(labelCounter++, ability.getTypeId(), 55, y + 5, 0xFFFFFF)); + y += ROW_H; } - // Render declarative fields for this tab - List tabFields = getVisibleFieldsForTab(tabTarget); - for (FieldDef def : tabFields) { - int widgetId = idCounter++; - int clearId = clearCounter++; - y = renderField(sw, def, widgetId, clearId, y); - } + // Build declarative fields using the builder + List tabFields = getVisibleFieldsForTab(activeTab); + builder = new FieldDefinitionsBuilder(this, sw, fontRendererObj) + .startIds(DECLARATIVE_ID_START, CLEAR_ID_START, labelCounter) + .startY(y); - // Target tab: append conditions list + y = builder.build(tabFields); + + // Conditions on target tab if (activeTab == TAB_TARGET) { - y = renderConditions(sw, y); + y = renderConditions(sw, y, builder.getNextLabelId()); } sw.maxScrollY = Math.max(y - swH, 0); - addScrollableGui(0, sw); - } - private TabTarget tabTargetForIndex(int tab) { - switch (tab) { - case TAB_GENERAL: return TabTarget.GENERAL; - case TAB_TYPE: return TabTarget.TYPE; - case TAB_TARGET: return TabTarget.TARGET; - case TAB_EFFECTS: return TabTarget.EFFECTS; - case TAB_VISUAL: return TabTarget.VISUAL; - default: return TabTarget.TYPE; + // Restore scroll position + if (activeTab < tabScrollY.length) { + float restored = Math.min(tabScrollY[activeTab], sw.maxScrollY); + sw.nextScrollY = restored; + sw.scrollY = restored; } } - private List getVisibleFieldsForTab(TabTarget target) { + private List getVisibleFieldsForTab(int tabIndex) { List result = new ArrayList<>(); - for (FieldDef def : fieldDefs) { - if (def.getTab() == target && def.isVisible()) { - result.add(def); + + if (tabIndex >= 4) { + // Custom tab — match by custom tab name + int customIndex = tabIndex - 4; + if (customIndex >= 0 && customIndex < customTabNames.size()) { + String tabName = customTabNames.get(customIndex); + for (FieldDef def : fieldDefs) { + if (def.getTab() == TabTarget.CUSTOM + && tabName.equals(def.getCustomTabName()) + && def.isVisible()) { + result.add(def); + } + } + } + } else { + // Fixed tab + TabTarget target; + switch (tabIndex) { + case TAB_GENERAL: target = TabTarget.GENERAL; break; + case TAB_TYPE: target = TabTarget.TYPE; break; + case TAB_TARGET: target = TabTarget.TARGET; break; + case TAB_EFFECTS: target = TabTarget.EFFECTS; break; + default: target = TabTarget.TYPE; break; + } + for (FieldDef def : fieldDefs) { + if (def.getTab() == target && def.isVisible()) { + result.add(def); + } } } + return result; } // ═══════════════════════════════════════════════════════════════════════════ - // FIELD RENDERING + // CONDITIONS (Target tab) // ═══════════════════════════════════════════════════════════════════════════ - @SuppressWarnings({"unchecked", "rawtypes"}) - private int renderField(GuiScrollWindow sw, FieldDef def, int widgetId, int clearId, int y) { - int labelX = 5; - int fieldX = 100; - int fieldW = 60; - - sw.addLabel(new GuiNpcLabel(widgetId, def.getLabel(), labelX, y + 5)); - - switch (def.getType()) { - case FLOAT: { - float fVal = def.getValue() instanceof Number ? ((Number) def.getValue()).floatValue() : 0f; - GuiNpcTextField tf = new GuiNpcTextField(widgetId, this, fontRendererObj, fieldX, y, fieldW, 20, String.format("%.2f", fVal)); - tf.setFloatsOnly(); - if (def.hasRange()) tf.setMinMaxDefaultFloat(def.getMin(), def.getMax(), fVal); - if (def.getTooltip() != null) tf.setHoverText(def.getTooltip()); - sw.addTextField(tf); - textFieldMap.put(widgetId, def); - break; - } - case INT: { - int iVal = def.getValue() instanceof Number ? ((Number) def.getValue()).intValue() : 0; - GuiNpcTextField tf = new GuiNpcTextField(widgetId, this, fontRendererObj, fieldX, y, fieldW, 20, String.valueOf(iVal)); - tf.setIntegersOnly(); - if (def.hasRange()) tf.setMinMaxDefault((int) def.getMin(), (int) def.getMax(), iVal); - if (def.getTooltip() != null) tf.setHoverText(def.getTooltip()); - sw.addTextField(tf); - textFieldMap.put(widgetId, def); - break; - } - case STRING: { - String sVal = def.getValue() != null ? def.getValue().toString() : ""; - GuiNpcTextField tf = new GuiNpcTextField(widgetId, this, fontRendererObj, fieldX, y, fieldW + 40, 20, sVal); - if (def.getTooltip() != null) tf.setHoverText(def.getTooltip()); - sw.addTextField(tf); - textFieldMap.put(widgetId, def); - break; - } - case BOOLEAN: { - boolean bVal = def.getValue() instanceof Boolean ? (Boolean) def.getValue() : false; - GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, 40, 20, new String[]{"gui.no", "gui.yes"}, bVal ? 1 : 0); - if (def.getHoverText() != null) btn.setHoverText(def.getHoverText()); - sw.addButton(btn); - buttonFieldMap.put(widgetId, def); - break; - } - case ENUM: { - Class> enumClass = def.getEnumClass(); - if (enumClass != null) { - Enum[] constants = enumClass.getEnumConstants(); - String[] names = new String[constants.length]; - for (int i = 0; i < constants.length; i++) names[i] = constants[i].toString(); - int selected = def.getValue() instanceof Enum ? ((Enum) def.getValue()).ordinal() : 0; - GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, 80, 20, names, selected); - if (def.getHoverText() != null) btn.setHoverText(def.getHoverText()); - sw.addButton(btn); - buttonFieldMap.put(widgetId, def); - } - break; - } - case STRING_ENUM: { - String[] values = def.getStringEnumValues(); - if (values != null && values.length > 0) { - String curVal = def.getStringEnumValue(); - int selected = 0; - for (int i = 0; i < values.length; i++) { - if (values[i].equals(curVal)) { selected = i; break; } - } - GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, 80, 20, values, selected); - if (def.getHoverText() != null) btn.setHoverText(def.getHoverText()); - sw.addButton(btn); - buttonFieldMap.put(widgetId, def); - } - break; - } - case SUB_GUI: { - String btnText = def.getButtonLabel(); - int btnW = def.hasClearAction() ? 60 : 80; - GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, btnW, 20, btnText); - Integer textColor = def.getButtonTextColor(); - if (textColor != null) btn.setTextColor(textColor); - if (def.getHoverText() != null) btn.setHoverText(def.getHoverText()); - sw.addButton(btn); - buttonFieldMap.put(widgetId, def); - - if (def.hasClearAction()) { - GuiNpcButton clearBtn = new GuiNpcButton(clearId, fieldX + btnW + 5, y, 20, 20, "X"); - sw.addButton(clearBtn); - clearFieldMap.put(clearId, def); - } - break; - } - } - return y + 24; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // CONDITIONS (Target tab special handling) - // ═══════════════════════════════════════════════════════════════════════════ - - private int renderConditions(GuiScrollWindow sw, int y) { - int x = 5; - - sw.addLabel(new GuiNpcLabel(50, "ability.conditions", x, y)); - y += 14; + private int renderConditions(GuiScrollWindow sw, int y, int labelCounter) { + y += 3; + sw.addLabel(new GuiNpcLabel(labelCounter, "ability.conditions", L_LABEL_X, y + 2, 0xFFFF55)); + y += 15; for (int i = 0; i < conditions.size() && i < 3; i++) { Condition cond = conditions.get(i); String condName = getConditionDisplayName(cond); - sw.addButton(new GuiNpcButton(50 + i * 10, x, y, 140, 20, condName)); - sw.addButton(new GuiNpcButton(51 + i * 10, x + 145, y, 40, 20, "gui.edit")); - sw.addButton(new GuiNpcButton(52 + i * 10, x + 190, y, 20, 20, "X")); + sw.addButton(new GuiNpcButton(50 + i * 10, L_LABEL_X, y, 140, 20, condName)); + sw.addButton(new GuiNpcButton(51 + i * 10, L_LABEL_X + 145, y, 40, 20, "gui.edit")); + sw.addButton(new GuiNpcButton(52 + i * 10, L_LABEL_X + 190, y, 20, 20, "X")); y += 22; } if (conditions.size() < 3) { - sw.addButton(new GuiNpcButton(80, x, y, 50, 20, "gui.add")); - y += 24; + sw.addButton(new GuiNpcButton(80, L_LABEL_X, y, 50, 20, "gui.add")); + y += ROW_H; } return y; @@ -334,12 +264,17 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi public void buttonEvent(GuiButton guibutton) { int id = guibutton.id; - // Tab switching + // Tab switching — fixed tabs if (id == 90) { activeTab = TAB_GENERAL; initGui(); return; } if (id == 91) { activeTab = TAB_TYPE; initGui(); return; } if (id == 92) { activeTab = TAB_TARGET; initGui(); return; } if (id == 93) { activeTab = TAB_EFFECTS; initGui(); return; } - if (id == 94) { activeTab = TAB_VISUAL; initGui(); return; } + // Custom tabs (94+) + if (id >= 94 && id < 94 + customTabNames.size()) { + activeTab = 4 + (id - 94); + initGui(); + return; + } if (id == -1000) { close(); return; } // Condition buttons (50-80) @@ -367,9 +302,19 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi return; } - // Clear buttons + // Clear buttons (effects delete or SUB_GUI clear) if (id >= CLEAR_ID_START) { - FieldDef def = clearFieldMap.get(id); + // Check effects list delete first + int[] meta = builder.getEffectWidgetMeta().get(id); + if (meta != null && meta[1] == 3) { + FieldDef def = builder.getClearFieldMap().get(id); + if (def != null && def.getType() == FieldType.EFFECTS_LIST) { + handleEffectButton(def, meta, guibutton); + return; + } + } + // SUB_GUI clear + FieldDef def = builder.getClearFieldMap().get(id); if (def != null && def.hasClearAction()) { def.getClearAction().run(); initGui(); @@ -379,9 +324,16 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi // Declarative field buttons if (id >= DECLARATIVE_ID_START) { - FieldDef def = buttonFieldMap.get(id); + FieldDef def = builder.getButtonFieldMap().get(id); if (def == null) return; + // Effects list buttons (type, amp, add) + int[] meta = builder.getEffectWidgetMeta().get(id); + if (meta != null && def.getType() == FieldType.EFFECTS_LIST) { + handleEffectButton(def, meta, guibutton); + return; + } + switch (def.getType()) { case BOOLEAN: def.setValue(((GuiNpcButton) guibutton).getValue() == 1); @@ -412,16 +364,63 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi } } + @SuppressWarnings("unchecked") + private void handleEffectButton(FieldDef def, int[] meta, GuiButton btn) { + List effects = (List) def.getValue(); + if (effects == null) return; + int effectIdx = meta[0]; + int action = meta[1]; + + switch (action) { + case 0: // Type changed + if (effectIdx < effects.size()) { + effects.get(effectIdx).setType(AbilityEffect.EffectType.fromOrdinal(((GuiNpcButton) btn).getValue())); + } + break; + case 2: // Amp changed + if (effectIdx < effects.size()) { + effects.get(effectIdx).setAmplifier(((GuiNpcButton) btn).getValue()); + } + break; + case 3: // Delete + if (effectIdx < effects.size()) { + effects.remove(effectIdx); + initGui(); + } + break; + case 4: // Add + if (effects.size() < 5) { + effects.add(new AbilityEffect(AbilityEffect.EffectType.SLOWNESS, 60, 0)); + initGui(); + } + break; + } + } + // ═══════════════════════════════════════════════════════════════════════════ // TEXT FIELD EVENTS // ═══════════════════════════════════════════════════════════════════════════ @Override + @SuppressWarnings("unchecked") public void unFocused(GuiNpcTextField textField) { int id = textField.id; if (id < DECLARATIVE_ID_START) return; - FieldDef def = textFieldMap.get(id); + // Check effects list duration fields + int[] meta = builder.getEffectWidgetMeta().get(id); + if (meta != null && meta[1] == 1) { + FieldDef def = builder.getTextFieldMap().get(id); + if (def != null && def.getType() == FieldType.EFFECTS_LIST) { + List effects = (List) def.getValue(); + if (effects != null && meta[0] < effects.size()) { + effects.get(meta[0]).setDurationTicks(textField.getInteger()); + } + return; + } + } + + FieldDef def = builder.getTextFieldMap().get(id); if (def == null) return; switch (def.getType()) { @@ -480,10 +479,8 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi } protected void applyToAbility() { - // Conditions are the only cached values - write them back ability.getConditions().clear(); for (Condition c : conditions) ability.addCondition(c); - // All other fields write directly to ability via FieldDef lambdas } public void loadAbility(Ability loadedAbility) { @@ -494,8 +491,7 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi this.conditions = new ArrayList<>(ability.getConditions()); this.fieldDefs = ability.getAllFieldDefinitions(); - this.hasTypeFields = hasFieldsForTab(TabTarget.TYPE); - this.hasVisualFields = hasFieldsForTab(TabTarget.VISUAL); + discoverCustomTabs(); initGui(); } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java b/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java index d09bddecf..4ebafd54f 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java @@ -2,6 +2,7 @@ package noppes.npcs.client.gui.util; import kamkeel.npcs.util.TextSplitter; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.RenderHelper; @@ -151,8 +152,23 @@ public class GuiNpcButton extends GuiButton { public void drawButton(Minecraft mc, int mouseX, int mouseY) { if (!this.visible) return; + // Auto-truncate display text if wider than button + String original = this.displayString; + FontRenderer fr = mc.fontRenderer; + if (fr != null) { + int maxTextWidth = this.width - 6; + if (fr.getStringWidth(original) > maxTextWidth) { + String ellipsis = "..."; + int targetWidth = maxTextWidth - fr.getStringWidth(ellipsis); + if (targetWidth > 0) { + this.displayString = fr.trimStringToWidth(original, targetWidth) + ellipsis; + } + } + } // First, draw the button normally using the superclass method. super.drawButton(mc, mouseX, mouseY); + // Restore original display string + this.displayString = original; // Then, if an icon texture is set, draw it. if (iconTexture != null) { mc.getTextureManager().bindTexture(iconTexture); diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index fef02e070..3706e3fc1 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1537,6 +1537,48 @@ ability.target.aggro_target=Target ability.target.self=Self ability.target.aoe_self=AOE Self ability.target.aoe_target=AOE Target +ability.syncWindup=Sync Windup + +# Anchor Point +ability.anchor.front=Front +ability.anchor.center=Center +ability.anchor.rightHand=Right Hand +ability.anchor.leftHand=Left Hand +ability.anchor.aboveHead=Above Head +ability.anchor.chest=Chest + +# Sweep Mode +ability.sweep.swipe=Swipe +ability.sweep.spin=Spin + +# Dash Mode +ability.dash.aggressive=Aggressive +ability.dash.defensive=Defensive + +# Counter Type +ability.counter.flat=Flat +ability.counter.percent=Percent + +# Hazard Shape +ability.shape.circle=Circle +ability.shape.ring=Ring +ability.shape.cone=Cone + +# Placement Mode +ability.placement.atCaster=At Caster +ability.placement.atTarget=At Target +ability.placement.followCaster=Follow Caster +ability.placement.followTarget=Follow Target + +# Teleport Mode +ability.teleport.blink=Blink +ability.teleport.behind=Behind +ability.teleport.single=Single + +# Trap Placement +ability.trapPlace.atCaster=At Caster +ability.trapPlace.atTarget=At Target +ability.trapPlace.aheadOfCaster=Ahead of Caster telegraph.circle=Circle telegraph.ring=Ring @@ -1577,136 +1619,173 @@ ability.cnpc.disc=Disc ability.cnpc.laser_shot=Laser Shot ability.cnpc.sweeper=Sweeper -# Ability GUI - Type Tab +# ═══════════════════════════════════════════════════════════════ +# Ability Config GUI +# ═══════════════════════════════════════════════════════════════ + +# General Tab +ability.weight=Weight +ability.windUpTicks=Wind Up +ability.cooldownTicks=Cooldown +ability.lockMovement=Lock Movement +ability.interruptible=Interruptible +ability.dazedTicks=Dazed Ticks +ability.targetingMode=Targeting + +# Target Tab +ability.minRange=Min Range +ability.maxRange=Max Range +ability.conditions=Conditions + +# Effects Tab +ability.windUpSound=Wind Up Sound +ability.activeSound=Active Sound +ability.windUpAnimation=Wind Up Animation +ability.activeAnimation=Active Animation +ability.showTelegraph=Show Telegraph +ability.windUpColor=Wind Up Color +ability.activeColor=Active Color + +# Section Headers +ability.section.timing=Timing +ability.section.movement=Movement +ability.section.sounds=Sounds +ability.section.animations=Animations +ability.section.telegraph=Telegraph +ability.section.beam=Beam +ability.section.sweep=Sweep +ability.section.push=Push +ability.section.leap=Leap +ability.section.disc=Disc +ability.section.homing=Homing +ability.section.boomerang=Boomerang +ability.section.explosive=Explosive +ability.section.colors=Colors +ability.section.effects=Effects +ability.section.counter=Counter +ability.section.area=Area +ability.section.damage=Damage +ability.section.debuffs=Debuffs +ability.section.healing=Healing +ability.section.trigger=Trigger +ability.section.aoe=AOE +ability.section.pullDamage=Pull Damage + +# Shared Sub-Section Labels (used under section headers) +gui.radius=Radius +gui.thickness=Thickness +gui.rotationSpeed=Rotation Speed +gui.strength=Strength +gui.range=Range +gui.delay=Delay +gui.density=Density +gui.length=Length +gui.width=Width +gui.height=Height +gui.value=Value +gui.chance=Chance +gui.sound=Sound +gui.animation=Animation +gui.duration=Duration +gui.amp=Amp # Common Type Settings ability.radius=Radius ability.knockback=Knockback -ability.knockbackUp=KB Up -ability.maxDist=Max Dist -ability.minDist=Min Dist +ability.knockbackUp=Knockback Up ability.distance=Distance +ability.maxDistance=Max Distance ability.lifetime=Lifetime -ability.stunTicks=Stun -ability.leapHeight=Leap Height +ability.stunTicks=Stun Ticks ability.hitWidth=Hit Width ability.effects=Effects -gui.duration=Duration -gui.amp=Amp +ability.mode=Mode +ability.duration=Duration +ability.anchorPoint=Anchor Point +ability.rotationSpeed=Rotation Speed -# Slam Settings +# Slam ability.leapSpeed=Leap Speed +ability.leapHeight=Leap Height -# Charge Settings -ability.hitRadius=Hit Radius - -# Cutter Settings +# Cutter +ability.range=Range +ability.sweepMode=Sweep Mode +ability.spinDuration=Spin Duration ability.arcAngle=Arc Angle -ability.sweepWaves=Waves -ability.rotSpeed=Rot Speed -ability.piercing=Piercing +ability.innerRadius=Inner Radius -# Orb Settings -ability.homing=Homing -ability.explosive=Explosive +# Projectile +ability.projectileType=Projectile Type -# Vortex Settings -ability.pullRadius=Radius -ability.pullStrength=Strength -ability.pullTo=Pull To -ability.aoe=AOE +# Charge (no unique keys) + +# Dash (no unique keys) + +# Teleport +ability.blinkRadius=Blink Radius +ability.blinkCount=Blink Count +ability.blinkDelay=Blink Delay +ability.behindDistance=Behind Distance +ability.lineOfSight=Line of Sight +ability.damageAtStart=Damage At Start +ability.damageAtEnd=Damage At End + +# Vortex +ability.pullRadius=Pull Radius +ability.pullStrength=Pull Strength ability.maxTargets=Max Targets -# Dash Settings -ability.dash.aggressive=Aggressive -ability.dash.defensive=Defensive -ability.mode=Mode -ability.behindDistance=Behind Dist +# Shockwave +ability.pushStrength=Push Strength -# Teleport Settings -ability.blinkCount=Blinks -ability.blinkDelay=Delay -ability.blinkRadius=Radius -ability.minRadius=Min Rad -ability.dmgRadius=Dmg Radius -ability.lineOfSight=Line Sight -ability.dmgAtStart=Dmg Start +# Guard +ability.damageReduction=Damage Reduction -# Projectile Settings -ability.explRadius=Expl Rad - -# Beam Settings -ability.beamLength=Length -ability.beamWidth=Width -ability.beamHeight=Height -ability.dmgInterval=Interval -ability.sweepAngle=Sweep Ang -ability.sweepSpeed=Sweep Spd -ability.lockTarget=Lock Tgt -ability.rotations=Rotations - -# Guard Settings -ability.dmgReduce=Dmg Reduce -ability.counterDmg=Counter Dmg -ability.canCounter=Counter -ability.counterChance=Counter % -ability.counterType=Counter Type -ability.counterValue=Counter Value -ability.counterSound=Counter Sound -ability.counterAnim=Counter Anim - -# Heal Settings -ability.healAmount=Amount -ability.healPercent=Percent -ability.healRadius=Radius +# Heal +ability.instantHeal=Instant Heal +ability.healAmount=Heal Amount +ability.healPercent=Heal Percent +ability.healRadius=Heal Radius ability.healSelf=Heal Self ability.healAllies=Heal Allies -# Hazard Settings -ability.dmgPerSec=Dmg/Sec -ability.debuffDur=Debuff Dur +# Hazard ability.shape=Shape ability.placement=Placement -ability.affectsCaster=Hit Caster -ability.ignoreInvuln=Ign Invuln -ability.place.caster=At Caster -ability.place.target=At Target -ability.place.followCaster=Follow Caster -ability.place.followTarget=Follow Target -ability.place.ahead=Ahead +ability.damagePerSecond=Damage/Sec +ability.damageInterval=Damage Interval +ability.slownessLevel=Slowness Level +ability.debuffDuration=Debuff Duration +ability.poisonLevel=Poison Level +ability.affectsCaster=Affects Caster -# Trap Settings -ability.triggerRadius=Trig Rad +# Trap ability.armTime=Arm Time -ability.maxTriggers=Max Trig +ability.maxTriggers=Max Triggers +ability.triggerCooldown=Trigger Cooldown -# Disc Settings -ability.discRadius=Disc Rad -ability.discThickness=Thickness -ability.boomerang=Boomerang -ability.boomerangDelay=Return Delay -ability.homingStr=Homing Str -ability.homingRange=Homing Rng -ability.explosionRad=Expl Rad -ability.rotationSpeed=Rot Speed +# Sweeper +ability.sweepSpeed=Sweep Speed +ability.rotations=Rotations +ability.piercing=Piercing +ability.lockTarget=Lock Target + +# Beam / Laser +ability.beamWidth=Beam Width +ability.headSize=Head Size +ability.attachedToOwner=Attached to Owner +ability.laserWidth=Laser Width +ability.expansionSpeed=Expansion Speed +ability.lingerTicks=Linger Ticks + +# Visual Settings ability.innerColor=Inner Color ability.outerColor=Outer Color ability.outerEnabled=Outer Glow ability.outerWidth=Outer Width +ability.outerAlpha=Outer Alpha ability.lightning=Lightning -ability.lightningDensity=Density -ability.lightningRadius=Radius -ability.anchorPoint=Anchor Point -ability.noVisualSettings=No visual settings for this ability type - -# Laser Shot Settings -ability.laserWidth=Laser Width -ability.expansionSpeed=Expansion Spd -ability.lingerTicks=Linger Ticks - -# Energy Beam Settings -ability.headSize=Head Size -ability.attachedToOwner=Attached # Ability Save/Load ability.save.confirm=Save ability preset @@ -1718,25 +1797,9 @@ gui.load=Load # Global Abilities Manager ability.manager.title=Ability Manager -# Explosive Hover Text -ability.explosive.noBlockDamage=Explosions do not destroy blocks - -# Additional Orb Settings -ability.explRadius=Expl Radius - -# Telegraph Colors (for abilities that support separate colors) -ability.windUpColor=Wind Up Color -ability.activeColor=Active Color - -# Shockwave Settings -ability.pushRadius=Push Radius -ability.pushStrength=Push Str -ability.shockwaveFalloff=Damage/knockback drop off with distance - -# Ability Config GUI -ability.typeId=Type ID -ability.conditions=Conditions -ability.noTypeSettings=No type-specific settings +# ═══════════════════════════════════════════════════════════════ +# Ability Hover Tooltips +# ═══════════════════════════════════════════════════════════════ ability.hover.weight=Higher weight = more likely to be chosen when multiple abilities available ability.hover.lockMove=Prevents NPC from moving while ability is charging or active