mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 04:17:36 +00:00
Ability Wheel Accepts - Chained Abilities
Lang File Cleanup / Merging Chained Ability Icon System
This commit is contained in:
1 parent
4510b3bbd1
commit
ca5461c94f
6 files changed
+179
-50
No files matched your search
@@ -7,6 +7,7 @@ import kamkeel.npcs.controllers.data.ability.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.IAbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.IAbilityExtender;
|
||||
import kamkeel.npcs.controllers.data.ability.IAbilityFieldProvider;
|
||||
import kamkeel.npcs.controllers.data.ability.IChainedAbilityFieldProvider;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.type.*;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityBeam;
|
||||
@@ -46,6 +47,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
// ── Extension Points ─────────────────────────────────────────────────────
|
||||
private final Map<String, List<AbilityVariant>> externalVariants = new LinkedHashMap<>();
|
||||
private final List<IAbilityFieldProvider> fieldProviders = new ArrayList<>();
|
||||
private final List<IChainedAbilityFieldProvider> chainedFieldProviders = new ArrayList<>();
|
||||
private final List<IAbilityExtender> extenders = new ArrayList<>();
|
||||
private final List<Predicate<EntityPlayer>> flightCheckers = new ArrayList<>();
|
||||
|
||||
@@ -807,6 +809,14 @@ public class AbilityController implements IAbilityHandler {
|
||||
return fieldProviders;
|
||||
}
|
||||
|
||||
public void registerChainedFieldProvider(IChainedAbilityFieldProvider provider) {
|
||||
chainedFieldProviders.add(provider);
|
||||
}
|
||||
|
||||
public List<IChainedAbilityFieldProvider> getChainedFieldProviders() {
|
||||
return chainedFieldProviders;
|
||||
}
|
||||
|
||||
public void registerExtender(IAbilityExtender extender) {
|
||||
extenders.add(extender);
|
||||
}
|
||||
|
||||
@@ -44,15 +44,15 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
/** Maximum range for NPC eligibility check. */
|
||||
private float maxRange = 20;
|
||||
|
||||
/** Which entity types can use this chained ability. */
|
||||
private UserType allowedBy = UserType.BOTH;
|
||||
|
||||
/** Chain-level conditions (individual ability conditions are ignored). */
|
||||
private List<Condition> conditions = new ArrayList<>();
|
||||
|
||||
/** Ordered list of ability entries to execute. */
|
||||
private List<ChainedAbilityEntry> entries = new ArrayList<>();
|
||||
|
||||
/** Extension data for external mods (e.g., icons, DBC stats). */
|
||||
private NBTTagCompound customData = new NBTTagCompound();
|
||||
|
||||
public ChainedAbility() {}
|
||||
|
||||
public ChainedAbility(String name) {
|
||||
@@ -145,12 +145,27 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
this.maxRange = Math.max(0, maxRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the allowed user type from child abilities.
|
||||
* A chain allows players if ALL resolved entries allow players.
|
||||
* A chain allows NPCs if ALL resolved entries allow NPCs.
|
||||
* Unresolvable entries are skipped (don't restrict).
|
||||
*/
|
||||
public UserType getAllowedBy() {
|
||||
return allowedBy;
|
||||
}
|
||||
|
||||
public void setAllowedBy(UserType allowedBy) {
|
||||
this.allowedBy = allowedBy != null ? allowedBy : UserType.BOTH;
|
||||
if (entries.isEmpty()) return UserType.BOTH;
|
||||
boolean allAllowPlayer = true;
|
||||
boolean allAllowNpc = true;
|
||||
for (ChainedAbilityEntry entry : entries) {
|
||||
Ability a = entry.resolve();
|
||||
if (a == null) continue;
|
||||
UserType ut = a.getAllowedBy();
|
||||
if (!ut.allowsPlayer()) allAllowPlayer = false;
|
||||
if (!ut.allowsNpc()) allAllowNpc = false;
|
||||
}
|
||||
if (allAllowPlayer && allAllowNpc) return UserType.BOTH;
|
||||
if (allAllowPlayer) return UserType.PLAYER_ONLY;
|
||||
if (allAllowNpc) return UserType.NPC_ONLY;
|
||||
return UserType.NPC_ONLY;
|
||||
}
|
||||
|
||||
public List<Condition> getConditions() {
|
||||
@@ -169,6 +184,10 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
this.entries = entries != null ? entries : new ArrayList<>();
|
||||
}
|
||||
|
||||
public NBTTagCompound getCustomData() {
|
||||
return customData;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// IChainedAbility INTERFACE
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -343,7 +362,6 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
nbt.setInteger("CooldownTicks", cooldownTicks);
|
||||
nbt.setFloat("MinRange", minRange);
|
||||
nbt.setFloat("MaxRange", maxRange);
|
||||
nbt.setInteger("AllowedBy", allowedBy.ordinal());
|
||||
|
||||
// Conditions
|
||||
NBTTagList condList = new NBTTagList();
|
||||
@@ -359,6 +377,9 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
}
|
||||
nbt.setTag("Entries", entryList);
|
||||
|
||||
// Custom data (for external mods)
|
||||
nbt.setTag("customData", (NBTTagCompound) customData.copy());
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@@ -372,7 +393,7 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
cooldownTicks = nbt.hasKey("CooldownTicks") ? nbt.getInteger("CooldownTicks") : 100;
|
||||
minRange = nbt.getFloat("MinRange");
|
||||
maxRange = nbt.hasKey("MaxRange") ? nbt.getFloat("MaxRange") : 20;
|
||||
allowedBy = UserType.fromOrdinal(nbt.getInteger("AllowedBy"));
|
||||
// AllowedBy is computed from child abilities — old NBT data silently ignored
|
||||
|
||||
// Conditions
|
||||
conditions.clear();
|
||||
@@ -393,5 +414,8 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Custom data (for external mods)
|
||||
customData = nbt.getCompoundTag("customData");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* External field definition provider for chained abilities.
|
||||
* Called during GUI initialization to allow mods to inject custom tabs/fields
|
||||
* into the chained ability configuration GUI.
|
||||
* <p>
|
||||
* Note: Although this interface references FieldDef (client-only), it is safe to
|
||||
* register on both sides because the generic type is erased at bytecode level.
|
||||
* The addFieldDefinitions method is only ever called from client-side GUI code.
|
||||
*/
|
||||
public interface IChainedAbilityFieldProvider {
|
||||
/**
|
||||
* Add field definitions to the chained ability's GUI.
|
||||
* Fields added here should use .tab() to assign them to a custom tab.
|
||||
*
|
||||
* @param chain The chained ability being configured
|
||||
* @param defs The mutable list of field definitions to add to
|
||||
*/
|
||||
void addFieldDefinitions(ChainedAbility chain, List<FieldDef> defs);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import kamkeel.npcs.controllers.data.ability.AbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.ChainedAbilityEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.Condition;
|
||||
import kamkeel.npcs.controllers.data.ability.IChainedAbilityFieldProvider;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -25,11 +26,14 @@ import noppes.npcs.client.gui.util.SubGuiInterface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* SubGui for editing a {@link ChainedAbility}.
|
||||
* Three tabs: General, Entries, Conditions.
|
||||
* Core tabs: General, Entries, Conditions.
|
||||
* Additional tabs are injected dynamically by {@link IChainedAbilityFieldProvider}s.
|
||||
*/
|
||||
public class SubGuiChainedAbilityConfig extends SubGuiInterface implements ITextfieldListener, ISubGuiListener, IAbilityConfigCallback {
|
||||
|
||||
@@ -38,15 +42,17 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
private static final int CLEAR_ID_START = 2000;
|
||||
private static final int LABEL_ID_START = 3000;
|
||||
|
||||
// ── Tab constants ─────────────────────────────────────────────────────────
|
||||
// ── Tab constants (core tabs) ────────────────────────────────────────────
|
||||
private static final int TAB_GENERAL = 0;
|
||||
private static final int TAB_ENTRIES = 1;
|
||||
private static final int TAB_CONDITIONS = 2;
|
||||
private static final int CORE_TAB_COUNT = 3;
|
||||
|
||||
// ── Tab button IDs ────────────────────────────────────────────────────────
|
||||
private static final int BTN_TAB_GENERAL = 90;
|
||||
private static final int BTN_TAB_ENTRIES = 91;
|
||||
private static final int BTN_TAB_CONDITIONS = 92;
|
||||
private static final int BTN_TAB_EXTRA_BASE = 93;
|
||||
private static final int BTN_CLOSE = -1000;
|
||||
|
||||
// ── Entries tab: per-entry ID encoding ─────────────────────────────────────
|
||||
@@ -80,8 +86,11 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
private List<ChainedAbilityEntry> entries;
|
||||
private List<Condition> conditions;
|
||||
|
||||
// Dynamic tabs injected by field providers (e.g., "Icon")
|
||||
private List<String> extraTabs = new ArrayList<>();
|
||||
|
||||
private GuiFieldBuilder builder;
|
||||
private float[] tabScrollY = new float[3];
|
||||
private float[] tabScrollY;
|
||||
|
||||
// Entry editing
|
||||
private int editingEntryIndex = -1;
|
||||
@@ -128,7 +137,57 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
FieldDef.floatField("ability.minRange", chain::getMinRange, chain::setMinRange).range(0, 64),
|
||||
FieldDef.floatField("ability.maxRange", chain::getMaxRange, chain::setMaxRange).range(0, 64)
|
||||
).tab("General"));
|
||||
fieldDefs.add(FieldDef.enumField("ability.allowedBy", UserType.class, chain::getAllowedBy, chain::setAllowedBy).tab("General"));
|
||||
|
||||
// Computed compatibility label (derived from child abilities)
|
||||
fieldDefs.add(FieldDef.labelField("ability.compatible", () -> {
|
||||
UserType ut = computeAllowedBy();
|
||||
return StatCollector.translateToLocal("ability.compatible") + ": \u00A7e" + ut.name();
|
||||
}).tab("General"));
|
||||
|
||||
// External field providers (e.g., DBC Addon injecting an "Icon" tab)
|
||||
if (AbilityController.Instance != null) {
|
||||
for (IChainedAbilityFieldProvider provider : AbilityController.Instance.getChainedFieldProviders()) {
|
||||
provider.addFieldDefinitions(chain, fieldDefs);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect extra tab names from provider-injected fields
|
||||
Set<String> tabNames = new LinkedHashSet<>();
|
||||
for (FieldDef def : fieldDefs) {
|
||||
String tab = def.getTab();
|
||||
if (tab != null && !tab.isEmpty() && !"General".equals(tab)) {
|
||||
tabNames.add(tab);
|
||||
}
|
||||
}
|
||||
extraTabs = new ArrayList<>(tabNames);
|
||||
|
||||
// Size scroll array for all tabs
|
||||
tabScrollY = new float[CORE_TAB_COUNT + extraTabs.size()];
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// COMPUTED USERTYPE
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Compute the allowed UserType from the current (possibly modified) entries list.
|
||||
* Mirrors ChainedAbility.getAllowedBy() but uses the local entries.
|
||||
*/
|
||||
private UserType computeAllowedBy() {
|
||||
if (entries.isEmpty()) return UserType.BOTH;
|
||||
boolean allAllowPlayer = true;
|
||||
boolean allAllowNpc = true;
|
||||
for (ChainedAbilityEntry entry : entries) {
|
||||
Ability a = entry.resolve();
|
||||
if (a == null) continue;
|
||||
UserType ut = a.getAllowedBy();
|
||||
if (!ut.allowsPlayer()) allAllowPlayer = false;
|
||||
if (!ut.allowsNpc()) allAllowNpc = false;
|
||||
}
|
||||
if (allAllowPlayer && allAllowNpc) return UserType.BOTH;
|
||||
if (allAllowPlayer) return UserType.PLAYER_ONLY;
|
||||
if (allAllowNpc) return UserType.NPC_ONLY;
|
||||
return UserType.NPC_ONLY;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
@@ -145,7 +204,7 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
|
||||
super.initGui();
|
||||
|
||||
// Tab buttons
|
||||
// Tab buttons — core tabs
|
||||
GuiMenuTopButton generalTab = new GuiMenuTopButton(BTN_TAB_GENERAL, guiLeft + 4, guiTop - 17, "menu.general");
|
||||
generalTab.active = (activeTab == TAB_GENERAL);
|
||||
addTopButton(generalTab);
|
||||
@@ -158,6 +217,16 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
conditionsTab.active = (activeTab == TAB_CONDITIONS);
|
||||
addTopButton(conditionsTab);
|
||||
|
||||
// Dynamic extra tabs from field providers
|
||||
GuiMenuTopButton prevTab = conditionsTab;
|
||||
for (int i = 0; i < extraTabs.size(); i++) {
|
||||
int tabIndex = CORE_TAB_COUNT + i;
|
||||
GuiMenuTopButton extraTab = new GuiMenuTopButton(BTN_TAB_EXTRA_BASE + i, prevTab, extraTabs.get(i));
|
||||
extraTab.active = (activeTab == tabIndex);
|
||||
addTopButton(extraTab);
|
||||
prevTab = extraTab;
|
||||
}
|
||||
|
||||
GuiMenuTopButton closeBtn = new GuiMenuTopButton(BTN_CLOSE, guiLeft + xSize - 22, guiTop - 17, "X");
|
||||
addTopButton(closeBtn);
|
||||
|
||||
@@ -168,22 +237,27 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
int swH = ySize - 10;
|
||||
|
||||
if (activeTab == TAB_GENERAL) {
|
||||
buildGeneralTab(swX, swY, swW, swH);
|
||||
buildFieldDefTab("General", swX, swY, swW, swH);
|
||||
} else if (activeTab == TAB_ENTRIES) {
|
||||
buildEntriesTab(swX, swY, swW, swH);
|
||||
} else if (activeTab == TAB_CONDITIONS) {
|
||||
buildConditionsTab(swX, swY, swW, swH);
|
||||
} else if (activeTab >= CORE_TAB_COUNT) {
|
||||
int extraIndex = activeTab - CORE_TAB_COUNT;
|
||||
if (extraIndex < extraTabs.size()) {
|
||||
buildFieldDefTab(extraTabs.get(extraIndex), swX, swY, swW, swH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// GENERAL TAB
|
||||
// FIELDDEF-BASED TAB (General + dynamic provider tabs)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void buildGeneralTab(int swX, int swY, int swW, int swH) {
|
||||
private void buildFieldDefTab(String tabName, int swX, int swY, int swW, int swH) {
|
||||
List<FieldDef> tabFields = new ArrayList<>();
|
||||
for (FieldDef def : fieldDefs) {
|
||||
if ("General".equals(def.getTab()) && def.isVisible()) {
|
||||
if (tabName.equals(def.getTab()) && def.isVisible()) {
|
||||
tabFields.add(def);
|
||||
}
|
||||
}
|
||||
@@ -342,12 +416,19 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
public void buttonEvent(GuiButton guibutton) {
|
||||
int id = guibutton.id;
|
||||
|
||||
// Tab switching
|
||||
// Tab switching — core tabs
|
||||
if (id == BTN_TAB_GENERAL) { activeTab = TAB_GENERAL; initGui(); return; }
|
||||
if (id == BTN_TAB_ENTRIES) { activeTab = TAB_ENTRIES; initGui(); return; }
|
||||
if (id == BTN_TAB_CONDITIONS) { activeTab = TAB_CONDITIONS; initGui(); return; }
|
||||
if (id == BTN_CLOSE) { saveOnClose = true; close(); return; }
|
||||
|
||||
// Tab switching — extra tabs
|
||||
if (id >= BTN_TAB_EXTRA_BASE && id < BTN_TAB_EXTRA_BASE + extraTabs.size()) {
|
||||
activeTab = CORE_TAB_COUNT + (id - BTN_TAB_EXTRA_BASE);
|
||||
initGui();
|
||||
return;
|
||||
}
|
||||
|
||||
// Entries tab
|
||||
if (activeTab == TAB_ENTRIES) {
|
||||
if (handleEntryButton(id)) return;
|
||||
@@ -358,8 +439,8 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
if (handleConditionButton(id)) return;
|
||||
}
|
||||
|
||||
// General tab — declarative field handling
|
||||
if (activeTab == TAB_GENERAL && builder != null) {
|
||||
// FieldDef-based tabs (General + extra tabs) — declarative field handling
|
||||
if (isFieldDefTab() && builder != null) {
|
||||
if (builder.handleButtonEvent(id, guibutton)) {
|
||||
if (!hasSubGui()) {
|
||||
initGui();
|
||||
@@ -369,6 +450,13 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current active tab uses the declarative FieldDef system.
|
||||
*/
|
||||
private boolean isFieldDefTab() {
|
||||
return activeTab == TAB_GENERAL || activeTab >= CORE_TAB_COUNT;
|
||||
}
|
||||
|
||||
private boolean handleEntryButton(int id) {
|
||||
// Add entry
|
||||
if (id == BTN_ADD_ENTRY) {
|
||||
@@ -456,8 +544,8 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
public void unFocused(GuiNpcTextField textField) {
|
||||
int id = textField.id;
|
||||
|
||||
// General tab — declarative fields
|
||||
if (activeTab == TAB_GENERAL && builder != null && id >= DECLARATIVE_ID_START) {
|
||||
// FieldDef-based tabs — declarative fields
|
||||
if (isFieldDefTab() && builder != null && id >= DECLARATIVE_ID_START) {
|
||||
if (builder.handleTextFieldEvent(id, textField)) {
|
||||
initGui();
|
||||
}
|
||||
@@ -479,8 +567,8 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
|
||||
@Override
|
||||
public void subGuiClosed(SubGuiInterface subgui) {
|
||||
// General tab — declarative sub-gui handling
|
||||
if (activeTab == TAB_GENERAL && builder != null) {
|
||||
// FieldDef-based tabs — declarative sub-gui handling
|
||||
if (isFieldDefTab() && builder != null) {
|
||||
if (builder.handleSubGuiClosed(subgui)) {
|
||||
initGui();
|
||||
return;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class SubGuiAuctionBid extends SubGuiInterface implements ITextfieldListe
|
||||
int totalBtnWidth = btnWidth * 2 + btnSpacing;
|
||||
int btnX = centerX - totalBtnWidth / 2;
|
||||
|
||||
addButton(new GuiNpcButton(btnBidId, btnX, y, btnWidth, 20, "auction.bid.placeBid"));
|
||||
addButton(new GuiNpcButton(btnBidId, btnX, y, btnWidth, 20, "auction.bid.place"));
|
||||
addButton(new GuiNpcButton(btnCancelId, btnX + btnWidth + btnSpacing, y, btnWidth, 20, "gui.cancel"));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user