From 4c5e44fb2249eee82515da6c9fc88f0b37e16b5f Mon Sep 17 00:00:00 2001 From: Kam Date: Fri, 6 Feb 2026 22:43:07 -0500 Subject: [PATCH] Add anchor offset methods and update ability variant selection UI --- .../data/ability/AbilityVariant.java | 15 +++ .../data/ability/type/AbilityBeam.java | 3 + .../data/ability/type/AbilityDisc.java | 3 + .../data/ability/type/AbilityOrb.java | 3 + src/main/java/kamkeel/npcs/util/Register.java | 3 +- .../advanced/SubGuiAbilityVariantSelect.java | 126 +++++++++++++----- .../npcs/client/gui/util/GuiCustomScroll.java | 17 ++- 7 files changed, 134 insertions(+), 36 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityVariant.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityVariant.java index 91d151a85..797e6b20b 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityVariant.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityVariant.java @@ -13,6 +13,7 @@ import java.util.function.Consumer; public class AbilityVariant { private final String displayKey; + private final String group; private final Consumer configurator; /** @@ -20,7 +21,17 @@ public class AbilityVariant { * @param configurator Lambda that configures the ability with preset values */ public AbilityVariant(String displayKey, Consumer configurator) { + this(displayKey, null, configurator); + } + + /** + * @param displayKey Lang key for the variant display name + * @param group Optional group header shown in the variant selection GUI (e.g., "DBC Addon") + * @param configurator Lambda that configures the ability with preset values + */ + public AbilityVariant(String displayKey, String group, Consumer configurator) { this.displayKey = displayKey; + this.group = group; this.configurator = configurator; } @@ -28,6 +39,10 @@ public class AbilityVariant { return displayKey; } + public String getGroup() { + return group; + } + /** * Apply this variant's preset configuration to the given ability. */ diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java index 5f12cc183..91b0287eb 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java @@ -521,6 +521,9 @@ public class AbilityBeam extends Ability implements IAbilityEnergyBeam { public int getAnchorPoint(int index) { return projectiles[clampIndex(index)].anchor.anchorPoint.ordinal(); } public void setAnchorPoint(int index, int point) { projectiles[clampIndex(index)].anchor.anchorPoint = AnchorPoint.fromOrdinal(point); } + public void setAnchorOffsetX(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetX = offset; } + public void setAnchorOffsetY(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetY = offset; } + public void setAnchorOffsetZ(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetZ = offset; } private int clampIndex(int index) { return Math.max(0, Math.min(index, projectileCount - 1)); diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java index d38d6a0b2..e0ed1b43c 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java @@ -551,6 +551,9 @@ public class AbilityDisc extends Ability implements IAbilityDisc { public int getAnchorPoint(int index) { return projectiles[clampIndex(index)].anchor.anchorPoint.ordinal(); } public void setAnchorPoint(int index, int point) { projectiles[clampIndex(index)].anchor.anchorPoint = AnchorPoint.fromOrdinal(point); } + public void setAnchorOffsetX(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetX = offset; } + public void setAnchorOffsetY(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetY = offset; } + public void setAnchorOffsetZ(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetZ = offset; } private int clampIndex(int index) { return Math.max(0, Math.min(index, projectileCount - 1)); diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java index 024fe2912..e0f6bc19f 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java @@ -536,6 +536,9 @@ public class AbilityOrb extends Ability implements IAbilityOrb { public int getAnchorPoint(int index) { return projectiles[clampIndex(index)].anchor.anchorPoint.ordinal(); } public void setAnchorPoint(int index, int point) { projectiles[clampIndex(index)].anchor.anchorPoint = AnchorPoint.fromOrdinal(point); } + public void setAnchorOffsetX(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetX = offset; } + public void setAnchorOffsetY(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetY = offset; } + public void setAnchorOffsetZ(int index, float offset) { projectiles[clampIndex(index)].anchor.anchorOffsetZ = offset; } private int clampIndex(int index) { return Math.max(0, Math.min(index, projectileCount - 1)); diff --git a/src/main/java/kamkeel/npcs/util/Register.java b/src/main/java/kamkeel/npcs/util/Register.java index c97188316..ff6b0f24e 100644 --- a/src/main/java/kamkeel/npcs/util/Register.java +++ b/src/main/java/kamkeel/npcs/util/Register.java @@ -29,7 +29,8 @@ public class Register { public static boolean isEmpty(String registryKey) { if (REGISTERED_NAMESPACES.isEmpty()) return true; - if (REGISTERED_NAMESPACES.get(registryKey).isEmpty()) return true; + List list = REGISTERED_NAMESPACES.get(registryKey); + if (list == null || list.isEmpty()) return true; return false; } diff --git a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java index 09d875dbe..1c65d0dc6 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java @@ -2,10 +2,10 @@ package noppes.npcs.client.gui.advanced; import kamkeel.npcs.controllers.data.ability.AbilityVariant; import net.minecraft.client.gui.GuiButton; -import net.minecraft.util.StatCollector; +import noppes.npcs.client.gui.util.GuiCustomScroll; import noppes.npcs.client.gui.util.GuiNpcButton; import noppes.npcs.client.gui.util.GuiNpcLabel; -import noppes.npcs.client.gui.util.GuiScrollWindow; +import noppes.npcs.client.gui.util.ICustomScrollListener; import noppes.npcs.client.gui.util.SubGuiInterface; import java.util.ArrayList; @@ -13,23 +13,18 @@ import java.util.List; /** * SubGui for selecting a variant/template when creating a new ability. - * Uses a scroll window so external mods can register many variants. + * Uses a scroll list with group headers for external addon variants. */ -public class SubGuiAbilityVariantSelect extends SubGuiInterface { - - private static final int VARIANT_BTN_START = 10; - private static final int BTN_H = 20; - private static final int BTN_PAD = 2; +public class SubGuiAbilityVariantSelect extends SubGuiInterface implements ICustomScrollListener { private final List variants; - private final List displayNames = new ArrayList<>(); private int selectedIndex = -1; + // Maps each scroll list index to a variant list index (-1 for non-selectable entries) + private final List scrollToVariantIndex = new ArrayList<>(); + public SubGuiAbilityVariantSelect(List variants) { this.variants = variants; - for (AbilityVariant variant : variants) { - displayNames.add(StatCollector.translateToLocal(variant.getDisplayKey())); - } setBackground("menubg.png"); xSize = 200; ySize = 216; @@ -41,26 +36,78 @@ public class SubGuiAbilityVariantSelect extends SubGuiInterface { addLabel(new GuiNpcLabel(0, "ability.selectVariant", guiLeft + 10, guiTop + 8)); - // Scroll window for variant buttons - int swX = guiLeft + 5; - int swY = guiTop + 22; - int swW = 190; - int swH = 155; + // Build the unsorted scroll list with group headers + List scrollList = new ArrayList<>(); + scrollToVariantIndex.clear(); - GuiScrollWindow sw = new GuiScrollWindow(this, swX, swY, swW, swH, 0); - addScrollableGui(0, sw); // Must register BEFORE adding components (initGui clears them) + boolean hasBaseVariants = false; + boolean hasGroupedVariants = false; - int localY = 2; - int btnW = swW - 14; // Leave room for scrollbar - for (int i = 0; i < displayNames.size(); i++) { - GuiNpcButton btn = new GuiNpcButton(VARIANT_BTN_START + i, 2, localY, btnW, BTN_H, displayNames.get(i)); - if (i == selectedIndex) { - btn.packedFGColour = 0x55FF55; - } - sw.addButton(btn); - localY += BTN_H + BTN_PAD; + for (AbilityVariant v : variants) { + if (v.getGroup() == null) + hasBaseVariants = true; + else + hasGroupedVariants = true; } - sw.maxScrollY = Math.max(localY - swH, 0); + + // Add base (non-grouped) variants first + for (int i = 0; i < variants.size(); i++) { + if (variants.get(i).getGroup() == null) { + scrollList.add(variants.get(i).getDisplayKey()); + scrollToVariantIndex.add(i); + } + } + + // Blank separator between base and grouped variants + if (hasBaseVariants && hasGroupedVariants) { + scrollList.add(""); + scrollToVariantIndex.add(-1); + } + + // Add grouped variants with group headers + String lastGroup = null; + for (int i = 0; i < variants.size(); i++) { + AbilityVariant v = variants.get(i); + if (v.getGroup() == null) + continue; + + if (!v.getGroup().equals(lastGroup)) { + scrollList.add(v.getGroup()); + scrollToVariantIndex.add(-1); + lastGroup = v.getGroup(); + } + + scrollList.add(v.getDisplayKey()); + scrollToVariantIndex.add(i); + } + + GuiCustomScroll scroll = new GuiCustomScroll(this, 0); + scroll.guiLeft = guiLeft + 5; + scroll.guiTop = guiTop + 22; + scroll.setUnsortedList(scrollList); + scroll.setSize(190, 155); + + // Mark blank separators and group headers as non-interactive colored entries + scroll.colors.put("", 0x000000); + if (lastGroup != null) { + for (int i = 0; i < variants.size(); i++) { + String group = variants.get(i).getGroup(); + if (group != null) + scroll.colors.put(group, 0xFFFF00); // Yellow + } + } + + // Restore selection in scroll + if (selectedIndex >= 0) { + for (int s = 0; s < scrollToVariantIndex.size(); s++) { + if (scrollToVariantIndex.get(s) == selectedIndex) { + scroll.selected = s; + break; + } + } + } + + addScroll(scroll); // Select / Cancel buttons addButton(new GuiNpcButton(0, guiLeft + 5, guiTop + 188, 90, 20, "gui.select")); @@ -68,6 +115,24 @@ public class SubGuiAbilityVariantSelect extends SubGuiInterface { addButton(new GuiNpcButton(1, guiLeft + 105, guiTop + 188, 90, 20, "gui.cancel")); } + @Override + public void customScrollClicked(int i, int j, int k, GuiCustomScroll scroll) { + if (scroll.id == 0 && scroll.selected >= 0 && scroll.selected < scrollToVariantIndex.size()) { + int variantIdx = scrollToVariantIndex.get(scroll.selected); + if (variantIdx >= 0) { + selectedIndex = variantIdx; + getButton(0).setEnabled(true); + } + } + } + + @Override + public void customScrollDoubleClicked(String selection, GuiCustomScroll scroll) { + if (selectedIndex >= 0) { + close(); + } + } + @Override public void buttonEvent(GuiButton guibutton) { int id = guibutton.id; @@ -76,9 +141,6 @@ public class SubGuiAbilityVariantSelect extends SubGuiInterface { } else if (id == 1) { selectedIndex = -1; close(); - } else if (id >= VARIANT_BTN_START && id < VARIANT_BTN_START + displayNames.size()) { - selectedIndex = id - VARIANT_BTN_START; - initGui(); } } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java index e726239b1..f9b3df568 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java @@ -200,7 +200,8 @@ public class GuiCustomScroll extends GuiScreen { int k = (14 * i + 4) - scrollY; if (k >= 4 && k + 12 < ySize) { int xOffset = scrollHeight < ySize - 8 ? 0 : 10; - String displayString = StatCollector.translateToLocal(list.get(i)); + String rawEntry = list.get(i); + String displayString = StatCollector.translateToLocal(rawEntry); String text = ""; float maxWidth = (xSize + xOffset - 8) * 0.8f; @@ -215,7 +216,13 @@ public class GuiCustomScroll extends GuiScreen { text += "..."; } else text = displayString; - if ((multipleSelection && selectedList.contains(text)) || (!multipleSelection && selected == i)) { + + // Entries with a custom color are non-interactive (headers/separators) + Integer customColor = colors.get(rawEntry); + if (customColor != null) { + if (!text.isEmpty()) + fontRendererObj.drawString(text, j, k, customColor); + } else if ((multipleSelection && selectedList.contains(text)) || (!multipleSelection && selected == i)) { drawVerticalLine(j - 2, k - 4, k + 10, 0xffffffff); drawVerticalLine(j + xSize - 18 + xOffset, k - 4, k + 10, 0xffffffff); drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k - 3, 0xffffffff); @@ -239,13 +246,17 @@ public class GuiCustomScroll extends GuiScreen { private int getMouseOver(int i, int j) { i -= guiLeft; j -= guiTop; - //fontRenderer.drawString(".", i , j, 0xffffff); if (i >= 4 && i < xSize - 4 && j >= 4 && j < ySize) { for (int j1 = 0; j1 < list.size(); j1++) { if (!mouseInOption(i, j, j1)) { continue; } + // Skip non-interactive entries (colored headers/separators) + if (colors.containsKey(list.get(j1))) { + continue; + } + return j1; }