diff --git a/src/main/java/kamkeel/npcs/controllers/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/AbilityController.java index 87b4c235f..f706ff585 100644 --- a/src/main/java/kamkeel/npcs/controllers/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/AbilityController.java @@ -1,15 +1,6 @@ package kamkeel.npcs.controllers; -import kamkeel.npcs.controllers.data.ability.Ability; -import kamkeel.npcs.controllers.data.ability.AbilityPhase; -import kamkeel.npcs.controllers.data.ability.AbilityVariant; -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.IEffectAction; -import kamkeel.npcs.controllers.data.ability.IChainedAbilityFieldProvider; -import kamkeel.npcs.controllers.data.ability.UserType; +import kamkeel.npcs.controllers.data.ability.*; import kamkeel.npcs.controllers.data.ability.type.AbilityCharge; import kamkeel.npcs.controllers.data.ability.type.AbilityCutter; import kamkeel.npcs.controllers.data.ability.type.AbilityDash; @@ -43,14 +34,7 @@ import noppes.npcs.controllers.data.PlayerData; import noppes.npcs.util.NBTJsonUtil; import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Pattern; @@ -70,6 +54,7 @@ public class AbilityController implements IAbilityHandler { private final List fieldProviders = new ArrayList<>(); private final List chainedFieldProviders = new ArrayList<>(); private final List extenders = new ArrayList<>(); + private final Map> conditionTypes = new HashMap<>(); private final List> flightCheckers = new ArrayList<>(); // ── Legacy Migration ───────────────────────────────────────────────────── @@ -90,6 +75,7 @@ public class AbilityController implements IAbilityHandler { public AbilityController() { registerBuiltinTypes(); + registerBuiltinConditionTypes(); } // ═══════════════════════════════════════════════════════════════════════════ @@ -160,6 +146,10 @@ public class AbilityController implements IAbilityHandler { registerType("cnpc:slicer", AbilitySlicer::new); } + private void registerBuiltinConditionTypes() { + registerCondition(IAbilityCondition.ConditionHPAbove::new); + } + // ═══════════════════════════════════════════════════════════════════════════ // BUILT-IN ABILITIES // ═══════════════════════════════════════════════════════════════════════════ @@ -890,6 +880,23 @@ public class AbilityController implements IAbilityHandler { return extenders; } + public void registerCondition(Supplier conditionFactory) { + IAbilityCondition temp = conditionFactory.get(); + String typeId = temp.getTypeId(); + if (conditionTypes.containsKey(typeId)) { + LogWriter.info("AbilityController: Overwriting Condition type: " + typeId); + } + conditionTypes.put(typeId, conditionFactory); + } + + public Supplier getConditionType(String key) { + return conditionTypes.get(key); + } + + public String[] getConditionTypes() { + return conditionTypes.keySet().toArray(new String[0]); + } + public void registerFlightChecker(Predicate checker) { flightCheckers.add(checker); } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/ConditionField.java b/src/main/java/kamkeel/npcs/controllers/data/ability/ConditionField.java new file mode 100644 index 000000000..cf4d2ad8f --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/ConditionField.java @@ -0,0 +1,196 @@ +package kamkeel.npcs.controllers.data.ability; + +import net.minecraft.nbt.NBTTagCompound; + +public abstract class ConditionField { + protected final String key; + protected final String label; + protected final Class type; // Float.class, Integer.class, etc + protected T value; + + public ConditionField(String key, String label, Class type, T defaultValue) { + this.key = key; + this.label = label; + this.type = type; + this.value = defaultValue; + } + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } + + public String getKey() { + return key; + } + + public String getLabel() { + return label; + } + + public Class getType() { + return type; + } + + public abstract void writeNBT(NBTTagCompound nbt); + + public abstract void readNBT(NBTTagCompound nbt); + + public static ConditionField intField(String key, String label, int defaultValue) { + return intField(key, label, defaultValue, 0, Integer.MAX_VALUE); + } + + public static ConditionField intField(String key, String label, int defaultValue, int min, int max) { + return new NumberField<>(key, label, Integer.class, defaultValue, min, max); + } + + public static ConditionField doubleField(String key, String label, double defaultValue) { + return doubleField(key, label, defaultValue, 0, Double.MAX_VALUE); + } + + public static ConditionField doubleField(String key, String label, double defaultValue, double min, double max) { + return new NumberField<>(key, label, Double.class, defaultValue, min, max); + } + + public static ConditionField floatField(String key, String label, float defaultValue) { + return floatField(key, label, defaultValue, 0, Float.MAX_VALUE); + } + + public static ConditionField floatField(String key, String label, float defaultValue, float min, float max) { + return new NumberField<>(key, label, Float.class, defaultValue, min, max); + } + + public static ConditionField stringField(String key, String label) { + return new StringField(key, label, ""); + } + + public static ConditionField stringField(String key, String label, String defaultValue) { + return new StringField(key, label, defaultValue); + } + + public static ConditionField booleanField(String key, String label, boolean defaultValue) { + return new BoolField(key, label, defaultValue); + } + + public static > ConditionField enumField(String key, String label, E defaultValue, Class enumClass) { + return new EnumField<>(key, label, defaultValue, enumClass); + } + + public static > ConditionField enumField(String key, String label, E defaultValue) { + return new EnumField<>(key, label, defaultValue, defaultValue.getDeclaringClass()); + } + + private static class NumberField> extends ConditionField { + private T min; + private T max; + + public NumberField(String key, String label, Class type, T defaultValue, T min, T max) { + super(key, label, type, defaultValue); + this.min = min; + this.max = max; + } + + @Override + public void setValue(T value) { + if (value.compareTo(min) < 0) { + this.value = min; + } else if (value.compareTo(max) > 0) { + this.value = max; + } else { + this.value = value; + } + } + + public NumberField min(T value) { + this.min = value; + return this; + } + + public NumberField max(T value) { + this.max = value; + return this; + } + + @Override + public void writeNBT(NBTTagCompound nbt) { + if (value instanceof Integer) nbt.setInteger(key, (Integer) value); + else if (value instanceof Float) nbt.setFloat(key, (Float) value); + else if (value instanceof Double) nbt.setDouble(key, (Double) value); + else if (value instanceof Long) nbt.setLong(key, (Long) value); + } + + @Override + @SuppressWarnings("unchecked") + public void readNBT(NBTTagCompound nbt) { + if (!nbt.hasKey(key)) return; + T v; + if (type == Integer.class) v = (T) (Integer) nbt.getInteger(key); + else if (type == Float.class) v = (T) (Float) nbt.getFloat(key); + else if (type == Double.class) v = (T) (Double) nbt.getDouble(key); + else if (type == Long.class) v = (T) (Long) nbt.getLong(key); + else return; + setValue(v); + } + } + + private static class StringField extends ConditionField { + public StringField(String key, String label, String defaultValue) { + super(key, label, String.class, defaultValue); + } + + @Override + public void writeNBT(NBTTagCompound nbt) { + nbt.setString(key, value); + } + + @Override + public void readNBT(NBTTagCompound nbt) { + setValue(nbt.getString(key)); + } + } + + private static class BoolField extends ConditionField { + public BoolField(String key, String label, boolean defaultValue) { + super(key, label, Boolean.class, defaultValue); + } + + @Override + public void writeNBT(NBTTagCompound nbt) { + nbt.setBoolean(key, value); + } + + @Override + public void readNBT(NBTTagCompound nbt) { + setValue(nbt.getBoolean(key)); + } + } + + private static class EnumField> extends ConditionField { + private final E[] options; + + public EnumField(String key, String label, E defaultValue, Class enumClass) { + super(key, label, enumClass, defaultValue); + this.options = enumClass.getEnumConstants(); + } + + public E[] getOptions() { + return options; + } + + @Override + public void writeNBT(NBTTagCompound nbt) { + nbt.setString(key, value.name()); + } + + @Override + public void readNBT(NBTTagCompound nbt) { + if (!nbt.hasKey(key)) return; + try { + setValue(Enum.valueOf(type, nbt.getString(key))); + } catch (IllegalArgumentException ignored) {} + } + } +} diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityCondition.java b/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityCondition.java new file mode 100644 index 000000000..2c1b7e670 --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityCondition.java @@ -0,0 +1,94 @@ +package kamkeel.npcs.controllers.data.ability; + +import kamkeel.npcs.controllers.AbilityController; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.LogWriter; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +public interface IAbilityCondition { + /** + * Check if this condition is met. + * + * @param caster The entity that would use the ability (NPC or Player) + * @param target The current target (may be null for self-targeting abilities or players) + * @return true if the condition is satisfied + */ + boolean check(EntityLivingBase caster, EntityLivingBase target); + + UserType getUserType(); + + String getTypeId(); + + List> getFields(); + + default NBTTagCompound writeNBT() { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setInteger("userType", getUserType().ordinal()); + nbt.setString("type", getTypeId()); + for (ConditionField field : getFields()) { + field.writeNBT(nbt); + } + return nbt; + } + + default void readNBT(NBTTagCompound nbt) { + for (ConditionField field : getFields()) { + field.readNBT(nbt); + } + } + + static IAbilityCondition fromNBT(NBTTagCompound nbt) { + String typeId = nbt.getString("type"); + Supplier factory = AbilityController.Instance.getConditionType(typeId); + if (factory == null) { + LogWriter.info("AbilityController: Unknown condition type: " + typeId); + return null; + } + IAbilityCondition condition = factory.get(); + condition.readNBT(nbt); + + return condition; + } + + class ConditionHPAbove implements IAbilityCondition { + ConditionField threshold = ConditionField.floatField("threshold", "threshold", 0.5f); + + public ConditionHPAbove() { + } + + @Override + public boolean check(EntityLivingBase caster, EntityLivingBase target) { + return caster.getHealth() / caster.getMaxHealth() > threshold.getValue(); + } + + @Override + public UserType getUserType() { + return UserType.BOTH; + } + + @Override + public String getTypeId() { + return "hp_above"; + } + + public float getThreshold() { + return threshold.getValue(); + } + + public void setThreshold(float threshold) { + this.threshold.setValue(threshold); + } + + @Override + public List> getFields() { + return Arrays.asList( + threshold + ); + } + } +}