Fields Builder for Ability GUIs

This commit is contained in:
Kam committed 2026-02-02 18:26:52 -05:00
1 parent 0ff501bb16
commit 911f185400
29 files changed
+1223 -605

No files matched your search

@@ -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;
}
@@ -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) {
@@ -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();
}
}
}
@@ -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();
}
}
}
@@ -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<Object> getter;
private Consumer<Object> 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<Float> getter, Consumer<Float> 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<String> textSupplier) {
FieldDef def = new FieldDef(label, FieldType.LABEL);
def.getter = () -> textSupplier.get();
return def;
}
@SuppressWarnings("unchecked")
public static FieldDef effectsListField(String label, Supplier<List<AbilityEffect>> getter, Consumer<List<AbilityEffect>> setter) {
FieldDef def = new FieldDef(label, FieldType.EFFECTS_LIST);
def.getter = () -> getter.get();
def.setter = v -> setter.accept((List<AbilityEffect>) v);
return def;
}
public static FieldDef subGuiField(String label, Supplier<SubGuiInterface> factory, Consumer<SubGuiInterface> 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<List<AbilityEffect>> getter, Consumer<List<AbilityEffect>> setter) {
return subGuiField(label,
() -> new SubGuiAbilityEffects(getter.get()),
gui -> {
List<AbilityEffect> r = ((SubGuiAbilityEffects) gui).getResult();
if (r != null) setter.accept(r);
});
}
public static FieldDef soundSubGui(String label, Supplier<String> getter, Consumer<String> 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);
}
}
@@ -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<Integer, FieldDef> buttonFieldMap = new HashMap<>();
private final Map<Integer, FieldDef> textFieldMap = new HashMap<>();
private final Map<Integer, FieldDef> clearFieldMap = new HashMap<>();
// Effects list metadata: widgetId -> [effectIndex, action]
// action: 0=type, 1=duration(text), 2=amp, 3=delete, 4=add
private final Map<Integer, int[]> 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<FieldDef> 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<? extends Enum<?>> 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<AbilityEffect> effects = (List<AbilityEffect>) 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<Integer, FieldDef> getButtonFieldMap() { return buttonFieldMap; }
public Map<Integer, FieldDef> getTextFieldMap() { return textFieldMap; }
public Map<Integer, FieldDef> getClearFieldMap() { return clearFieldMap; }
public Map<Integer, int[]> getEffectWidgetMeta() { return effectWidgetMeta; }
public int getNextLabelId() { return labelId; }
}
@@ -7,5 +7,8 @@ public enum FieldType {
ENUM,
STRING_ENUM,
SUB_GUI,
STRING
STRING,
SECTION_HEADER,
LABEL,
EFFECTS_LIST
}
@@ -5,6 +5,5 @@ public enum TabTarget {
TYPE,
TARGET,
EFFECTS,
VISUAL,
CUSTOM
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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)
);
}
}
@@ -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)
);
}
}
@@ -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<FieldDef> 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)
);
}
}
@@ -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<FieldDef> fieldDefs;
private boolean hasTypeFields;
private boolean hasVisualFields;
private List<String> customTabNames;
// Conditions are complex (list with add/edit/remove) - cached separately
private List<Condition> conditions;
private int editingConditionIndex = -1;
// Event routing
private final Map<Integer, FieldDef> buttonFieldMap = new HashMap<>();
private final Map<Integer, FieldDef> textFieldMap = new HashMap<>();
private final Map<Integer, FieldDef> 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<FieldDef> 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<FieldDef> 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<FieldDef> getVisibleFieldsForTab(TabTarget target) {
private List<FieldDef> getVisibleFieldsForTab(int tabIndex) {
List<FieldDef> 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<? extends Enum<?>> 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<AbilityEffect> effects = (List<AbilityEffect>) 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<AbilityEffect> effects = (List<AbilityEffect>) 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();
}
@@ -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);