Condition Registry [1/2]

This commit is contained in:
Kam committed 2026-02-21 02:36:41 -05:00
1 parent 8648e019a9
commit 606bc093df
4 files changed
+232 -48

No files matched your search

@@ -903,6 +903,28 @@ public class AbilityController implements IAbilityHandler {
return conditionTypes.keySet().toArray(new String[0]);
}
public List<String> getConditionNamespaces() {
LinkedHashSet<String> seen = new LinkedHashSet<>();
for (String typeId : conditionTypes.keySet()) {
String[] parts = typeId.split("\\.", 3);
if (parts.length >= 2) {
seen.add(parts[1]);
}
}
return new ArrayList<>(seen);
}
public String[] getConditionTypesByNamespace(String namespace) {
String prefix = "condition." + namespace + ".";
List<String> result = new ArrayList<>();
for (String typeId : conditionTypes.keySet()) {
if (typeId.startsWith(prefix)) {
result.add(typeId);
}
}
return result.toArray(new String[0]);
}
public void registerFlightChecker(Predicate<EntityPlayer> checker) {
flightCheckers.add(checker);
}
@@ -18,6 +18,12 @@ public class ConditionHitCount extends AbilityCondition {
this.userType = UserType.NPC_ONLY;
}
@Override
protected boolean checkEntity(EntityLivingBase entity) {
// Not used — this condition overrides check() directly
return false;
}
@Override
public boolean check(EntityLivingBase caster, EntityLivingBase target) {
if (caster instanceof EntityNPCInterface) {
@@ -4,6 +4,7 @@ import kamkeel.npcs.controllers.AbilityController;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
import kamkeel.npcs.controllers.data.ability.IEffectAction;
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
import noppes.npcs.LogWriter;
import noppes.npcs.controllers.AnimationController;
import noppes.npcs.controllers.data.Animation;
@@ -207,4 +208,36 @@ public class Register<T> {
return new Register.EffectActions(namespace);
}
}
public static class Conditions extends Register<AbilityCondition> {
private Conditions(String namespace) {
super("condition", namespace);
}
public AbilityCondition register(String name, Supplier<AbilityCondition> factory) {
entries.put(registryKey + "." + namespace + "." + name, factory);
return factory.get();
}
public void register() {
for (Map.Entry<String, Supplier<AbilityCondition>> entry : entries.entrySet()) {
AbilityController.Instance.registerCondition(entry.getValue());
}
}
public static Register.Conditions create(String namespace, String displayName) {
if (!REGISTERED_NAMESPACES.containsKey("condition"))
REGISTERED_NAMESPACES.put("condition", new ArrayList<>());
if (REGISTERED_NAMESPACES.get("condition").contains(namespace)) {
LogWriter.error("REGISTER CONDITIONS: Namespace " + namespace + " already registered!");
}
REGISTERED_NAMESPACES.get("condition").add(namespace);
NAMESPACE_DISPLAY_NAMES.put(namespace, displayName);
return new Register.Conditions(namespace);
}
}
}
@@ -2,12 +2,15 @@ package noppes.npcs.client.gui.advanced;
import kamkeel.npcs.controllers.AbilityController;
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
import kamkeel.npcs.util.Register;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.resources.I18n;
import noppes.npcs.client.gui.builder.FieldDef;
import noppes.npcs.client.gui.util.*;
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@@ -15,59 +18,138 @@ import java.util.function.Supplier;
/**
* SubGui for editing a single AbilityCondition.
*
* Type selection stays manual (we need to swap the condition object).
* Field rendering is fully delegated to AbilityCondition#getAbilityDefinitions(),
* mirroring the pattern used in SubGuiAbilityConfig.
* Split-panel layout:
* Left – Mod namespace filter + scrollable condition type list
* Right – FieldDef configuration for the selected condition
*/
public class SubGuiConditionEdit extends SubGuiInterface implements ITextfieldListener, ISubGuiListener {
public class SubGuiConditionEdit extends SubGuiInterface implements ITextfieldListener, ISubGuiListener, ICustomScrollListener {
private static final int DECLARATIVE_ID_START = 1000;
private static final int CLEAR_ID_START = 2000;
private static final int LABEL_ID_START = 3000;
private static final int BTN_TYPE = 1;
private static final int BTN_NAMESPACE = 1;
private static final int BTN_CANCEL = 10;
private static final int BTN_DONE = 11;
// Left panel
private static final int LEFT_WIDTH = 110;
private static final int LEFT_MARGIN = 5;
// Right panel
private static final int RIGHT_GAP = 5;
private AbilityCondition condition;
private AbilityCondition result;
private String selectedTypeId;
private final String[] conditionTypeIds;
private final String[] conditionTypeNames;
private int selectedTypeIndex = 0;
// Namespace filter
private int namespaceFilter = 0; // 0 = ALL, 1 = cnpc, 2+ = modded namespaces
private List<String> modNamespaces;
// Type list
private GuiCustomScroll scroll;
private final HashMap<String, String> displayNameToTypeId = new HashMap<>();
private String[] filteredTypeIds;
private AbilityFieldBuilder builder;
public SubGuiConditionEdit(AbilityCondition existing) {
this.result = null;
conditionTypeIds = AbilityController.Instance.getConditionTypes();
conditionTypeNames = new String[conditionTypeIds.length];
for (int i = 0; i < conditionTypeIds.length; i++) {
Supplier<AbilityCondition> factory = AbilityController.Instance.getConditionType(conditionTypeIds[i]);
conditionTypeNames[i] = factory != null ? factory.get().getName() : conditionTypeIds[i];
}
this.modNamespaces = getModNamespaces();
if (existing != null) {
for (int i = 0; i < conditionTypeIds.length; i++) {
if (conditionTypeIds[i].equals(existing.getTypeId())) {
selectedTypeIndex = i;
break;
}
}
this.condition = existing;
this.selectedTypeId = existing.getTypeId();
// Set namespace filter to match existing condition
setNamespaceForTypeId(selectedTypeId);
} else {
this.condition = spawnCondition(selectedTypeIndex);
// Default to first available condition
String[] allTypes = AbilityController.Instance.getConditionTypes();
if (allTypes.length > 0) {
this.selectedTypeId = allTypes[0];
this.condition = spawnCondition(selectedTypeId);
}
}
setBackground("menubg.png");
xSize = 240;
ySize = 180;
setBackground("menubg.png", 217);
xSize = 356;
ySize = 200;
}
private AbilityCondition spawnCondition(int typeIndex) {
if (typeIndex < 0 || typeIndex >= conditionTypeIds.length) return null;
Supplier<AbilityCondition> factory = AbilityController.Instance.getConditionType(conditionTypeIds[typeIndex]);
private List<String> getModNamespaces() {
List<String> result = new ArrayList<>();
List<String> registered = Register.REGISTERED_NAMESPACES.get("condition");
if (registered != null) {
result.addAll(registered);
}
return result;
}
private void setNamespaceForTypeId(String typeId) {
if (typeId == null) return;
String[] parts = typeId.split("\\.", 3);
if (parts.length < 2) return;
String ns = parts[1];
if ("cnpc".equals(ns)) {
namespaceFilter = 1;
} else {
for (int i = 0; i < modNamespaces.size(); i++) {
if (modNamespaces.get(i).equals(ns)) {
namespaceFilter = 2 + i;
return;
}
}
namespaceFilter = 0; // Fallback to ALL
}
}
private String getNamespaceFilterLabel() {
if (namespaceFilter == 0) return "ALL";
if (namespaceFilter == 1) return "CNPC";
int modIdx = namespaceFilter - 2;
if (modIdx >= 0 && modIdx < modNamespaces.size()) {
String ns = modNamespaces.get(modIdx);
String display = Register.NAMESPACE_DISPLAY_NAMES.get(ns);
return display != null ? display : ns.toUpperCase();
}
return "ALL";
}
private void cycleNamespaceFilter() {
namespaceFilter++;
// 0=ALL, 1=CNPC, 2..2+N-1=mod namespaces, then wrap
int max = 2 + modNamespaces.size();
if (namespaceFilter >= max) {
namespaceFilter = 0;
}
// Skip empty mod namespace slots
if (namespaceFilter >= 2) {
int modIdx = namespaceFilter - 2;
if (modIdx >= modNamespaces.size()) {
namespaceFilter = 0;
}
}
}
private String[] getFilteredTypes() {
if (namespaceFilter == 0) {
return AbilityController.Instance.getConditionTypes();
} else if (namespaceFilter == 1) {
return AbilityController.Instance.getConditionTypesByNamespace("cnpc");
} else {
int modIdx = namespaceFilter - 2;
if (modIdx >= 0 && modIdx < modNamespaces.size()) {
return AbilityController.Instance.getConditionTypesByNamespace(modNamespaces.get(modIdx));
}
return AbilityController.Instance.getConditionTypes();
}
}
private AbilityCondition spawnCondition(String typeId) {
if (typeId == null) return null;
Supplier<AbilityCondition> factory = AbilityController.Instance.getConditionType(typeId);
return factory != null ? factory.get() : null;
}
@@ -76,18 +158,45 @@ public class SubGuiConditionEdit extends SubGuiInterface implements ITextfieldLi
GuiNpcTextField.unfocus();
super.initGui();
int swX = guiLeft + 4;
int swY = guiTop + 5;
int swW = xSize - 8;
int swH = ySize - 35;
int y = guiTop + 5;
int labelY = guiTop + 8;
addLabel(new GuiNpcLabel(0, "gui.type", guiLeft + 8, labelY + 5));
GuiNpcButton typeBtn = new GuiNpcButton(BTN_TYPE, guiLeft + 80, labelY, 155, 20, conditionTypeNames, selectedTypeIndex);
addButton(typeBtn);
// Namespace filter button (full width at top)
addButton(new GuiNpcButton(BTN_NAMESPACE, guiLeft + LEFT_MARGIN, y, xSize - LEFT_MARGIN * 2, 20, getNamespaceFilterLabel()));
y += 24;
swY += 26;
swH -= 26;
// Build filtered type list
filteredTypeIds = getFilteredTypes();
displayNameToTypeId.clear();
List<String> scrollList = new ArrayList<>();
for (String typeId : filteredTypeIds) {
Supplier<AbilityCondition> factory = AbilityController.Instance.getConditionType(typeId);
String displayName = factory != null ? I18n.format(factory.get().getName()) : typeId;
scrollList.add(displayName);
displayNameToTypeId.put(displayName, typeId);
}
// Left panel: condition type scroll list
int scrollHeight = ySize - 58;
scroll = new GuiCustomScroll(this, 0);
scroll.guiLeft = guiLeft + LEFT_MARGIN;
scroll.guiTop = y;
scroll.setSize(LEFT_WIDTH, scrollHeight);
scroll.setUnsortedList(scrollList);
// Pre-select current condition type in scroll
if (selectedTypeId != null) {
for (int i = 0; i < filteredTypeIds.length; i++) {
if (filteredTypeIds[i].equals(selectedTypeId)) {
scroll.selected = i;
break;
}
}
}
addScroll(scroll);
// Right panel: condition config via FieldDef
int rightX = guiLeft + LEFT_MARGIN + LEFT_WIDTH + RIGHT_GAP;
int rightW = xSize - LEFT_MARGIN * 2 - LEFT_WIDTH - RIGHT_GAP;
List<FieldDef> fields = new ArrayList<>();
if (condition != null) {
@@ -97,24 +206,20 @@ public class SubGuiConditionEdit extends SubGuiInterface implements ITextfieldLi
builder = new AbilityFieldBuilder(this, fontRendererObj);
builder.startIds(DECLARATIVE_ID_START, CLEAR_ID_START, LABEL_ID_START);
builder.startY(5);
builder.buildScrollWindow(fields, rightX, y, rightW, scrollHeight);
builder.buildScrollWindow(fields, swX, swY, swW, swH);
// Bottom buttons
int btnY = guiTop + ySize - 26;
addButton(new GuiNpcButton(BTN_CANCEL, guiLeft + 8, btnY, 60, 20, "gui.cancel"));
addButton(new GuiNpcButton(BTN_DONE, guiLeft + xSize - 68, btnY, 60, 20, "gui.done"));
addButton(new GuiNpcButton(BTN_CANCEL, guiLeft + LEFT_MARGIN, btnY, 60, 20, "gui.cancel"));
addButton(new GuiNpcButton(BTN_DONE, guiLeft + xSize - LEFT_MARGIN - 60, btnY, 60, 20, "gui.done"));
}
@Override
public void buttonEvent(GuiButton guibutton) {
int id = guibutton.id;
if (id == BTN_TYPE) {
int newIndex = ((GuiNpcButton) guibutton).getValue();
if (newIndex != selectedTypeIndex) {
selectedTypeIndex = newIndex;
condition = spawnCondition(selectedTypeIndex);
}
if (id == BTN_NAMESPACE) {
cycleNamespaceFilter();
initGui();
return;
}
@@ -138,6 +243,24 @@ public class SubGuiConditionEdit extends SubGuiInterface implements ITextfieldLi
}
}
@Override
public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) {
if (guiCustomScroll.id == 0 && scroll.hasSelected()) {
String displayName = scroll.getSelected();
String typeId = displayNameToTypeId.get(displayName);
if (typeId != null && !typeId.equals(selectedTypeId)) {
selectedTypeId = typeId;
condition = spawnCondition(selectedTypeId);
initGui();
}
}
}
@Override
public void customScrollDoubleClicked(String selection, GuiCustomScroll guiCustomScroll) {
// Same as single click — user still needs to configure fields
}
@Override
public void unFocused(GuiNpcTextField textField) {
if (textField.id < DECLARATIVE_ID_START) return;