Added ConditionItem and increased the max amount of conditions an ability can hold to 5

This commit is contained in:
Vidal committed 2026-02-21 01:12:57 -03:00
1 parent fb938c2b7d
commit 36babe51cc
6 files changed
+244 -18

No files matched your search

@@ -4,6 +4,7 @@ import kamkeel.npcs.controllers.data.ability.*;
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
import kamkeel.npcs.controllers.data.ability.conditions.ConditionHPThreshold;
import kamkeel.npcs.controllers.data.ability.conditions.ConditionHitCount;
import kamkeel.npcs.controllers.data.ability.conditions.ConditionItem;
import kamkeel.npcs.controllers.data.ability.type.AbilityCharge;
import kamkeel.npcs.controllers.data.ability.type.AbilityCutter;
import kamkeel.npcs.controllers.data.ability.type.AbilityDash;
@@ -152,6 +153,7 @@ public class AbilityController implements IAbilityHandler {
private void registerBuiltinConditionTypes() {
registerCondition(ConditionHPThreshold::new);
registerCondition(ConditionHitCount::new);
registerCondition(ConditionItem::new);
}
// ═══════════════════════════════════════════════════════════════════════════
@@ -14,6 +14,8 @@ import java.util.List;
import java.util.function.Supplier;
public abstract class AbilityCondition {
public static final int MAX_CONDITIONS = 5;
protected String typeId = "";
protected String name = "";
protected UserType userType = UserType.BOTH;
@@ -69,7 +71,7 @@ public abstract class AbilityCondition {
nbt.setString("typeId", getTypeId());
nbt.setString("name", getName());
nbt.setInteger("userType", getUserType().ordinal());
nbt.setInteger("filter", getUserType().ordinal());
nbt.setInteger("filter", getFilter().ordinal());
writeTypeNBT(nbt);
return nbt;
}
@@ -0,0 +1,201 @@
package kamkeel.npcs.controllers.data.ability.conditions;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.client.gui.builder.FieldDef;
import noppes.npcs.entity.EntityNPCInterface;
import java.util.List;
public class ConditionItem extends AbilityCondition {
public enum UsageType {
ARMOR, HOLDING, AMMO, OFFHAND;
public static UsageType fromOrdinal(int ordinal) {
UsageType[] values = values();
return (ordinal >= 0 && ordinal < values.length) ? values[ordinal] : HOLDING;
}
}
public enum ArmorSlot {
BOOTS, LEGS, CHEST, HELMET;
public static ArmorSlot fromOrdinal(int ordinal) {
ArmorSlot[] values = values();
return (ordinal >= 0 && ordinal < values.length) ? values[ordinal] : BOOTS;
}
}
// Shared
private UsageType usageType = UsageType.HOLDING;
private String itemName = "";
// ARMOR only
private ArmorSlot armorSlot = ArmorSlot.BOOTS;
private boolean fullArmorSet = false;
// AMMO + PLAYER only
private int requiredCount = 1;
public ConditionItem() {
this.typeId = "condition.cnpc.item";
this.name = "condition.item";
}
@Override
public boolean check(EntityLivingBase caster, EntityLivingBase target) {
switch (getFilter()) {
case CASTER:
return checkEntity(caster);
case TARGET:
return target != null && checkEntity(target);
case BOTH:
return checkEntity(caster) && (target != null && checkEntity(target));
default:
return checkEntity(caster);
}
}
private boolean checkEntity(EntityLivingBase entity) {
switch (usageType) {
case HOLDING:
return checkHolding(entity);
case ARMOR:
return checkArmor(entity);
case AMMO:
return checkAmmo(entity);
case OFFHAND:
return checkOffhand(entity);
default:
return false;
}
}
private boolean checkHolding(EntityLivingBase entity) {
ItemStack held = entity.getHeldItem();
return matchesItem(held);
}
private boolean checkArmor(EntityLivingBase entity) {
// MC getEquipmentInSlot: 1=boots, 2=legs, 3=chest, 4=helmet
int slot = armorSlot.ordinal() + 1; // BOOTS=0, LEGS=1, CHEST=2, HELMET=3
return matchesItem(entity.getEquipmentInSlot(slot));
}
private boolean checkFullArmor(EntityLivingBase entity) {
for (int i = 0; i < 4; i++) {
int slot = i + 1;
if (!matchesItem(i, entity.getEquipmentInSlot(slot)))
return false;
}
return true;
}
private boolean checkAmmo(EntityLivingBase entity) {
if (entity instanceof EntityPlayer) {
return checkPlayerInventoryCount((EntityPlayer) entity);
}
if (entity instanceof EntityNPCInterface) {
return checkNPCProjectile((EntityNPCInterface) entity);
}
return false;
}
private boolean checkOffhand(EntityLivingBase entity) {
// No offhand for players
if (entity instanceof EntityPlayer) return false;
ItemStack item = ((EntityNPCInterface) entity).getOffHand();
return matchesItem(item);
}
private boolean checkPlayerInventoryCount(EntityPlayer player) {
int count = 0;
for (ItemStack stack : player.inventory.mainInventory) {
if (matchesItem(stack)) {
count += stack.stackSize;
}
}
return count >= requiredCount;
}
private boolean checkNPCProjectile(EntityNPCInterface npc) {
ItemStack projectile = npc.inventory.getProjectile();
return matchesItem(projectile);
}
private boolean matchesItem(ItemStack stack) {
return matchesItem(0, stack);
}
private boolean matchesItem(int index, ItemStack stack) {
if (stack == null || itemName == null || itemName.isEmpty()) return false;
String registryName = net.minecraft.item.Item.itemRegistry.getNameForObject(stack.getItem());
return itemName.equals(registryName);
}
@Override
public void getConditionDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.enumField("condition.usage_type", UsageType.class,
this::getUsageType, this::setUsageType));
defs.add(FieldDef.stringField("condition.item_name",
this::getItemName, this::setItemName));
// .hover("condition.hover.item_name"));
defs.add(FieldDef.enumField("condition.armor_slot", ArmorSlot.class,
this::getArmorSlot, this::setArmorSlot)
.visibleWhen(() -> usageType == UsageType.ARMOR));
// .hover("condition.hover.armor_slots"));
defs.add(FieldDef.intField("condition.item_count",
this::getRequiredCount, this::setRequiredCount)
.range(1, 64)
.visibleWhen(() -> usageType == UsageType.AMMO && userType.allowsPlayer()));
// .hover("condition.hover.required_count"));
}
@Override
public void writeTypeNBT(NBTTagCompound nbt) {
nbt.setString("itemName", itemName);
nbt.setInteger("usageType", usageType.ordinal());
nbt.setInteger("armorSlot", armorSlot.ordinal());
nbt.setInteger("requiredCount", requiredCount);
}
@Override
public void readTypeNBT(NBTTagCompound nbt) {
itemName = nbt.getString("itemName");
usageType = UsageType.fromOrdinal(nbt.getInteger("usageType"));
armorSlot = ArmorSlot.fromOrdinal(nbt.getInteger("armorSlot"));
requiredCount = nbt.getInteger("requiredCount");
}
public UsageType getUsageType() { return usageType; }
public void setUsageType(UsageType usageType) { this.usageType = usageType; }
public String getItemName() { return itemName; }
public void setItemName(String itemName) { this.itemName = itemName; }
public ArmorSlot getArmorSlot() { return armorSlot; }
public void setArmorSlot(ArmorSlot armorSlot) { this.armorSlot = armorSlot; }
public boolean isFullArmorSet() {
return fullArmorSet;
}
public void setFullArmorSet(boolean fullArmorSet) {
this.fullArmorSet = fullArmorSet;
}
public int getRequiredCount() { return requiredCount; }
public void setRequiredCount(int requiredCount) { this.requiredCount = Math.max(1, requiredCount); }
}
@@ -20,6 +20,8 @@ import noppes.npcs.client.gui.util.SubGuiInterface;
import java.util.ArrayList;
import java.util.List;
import static kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition.MAX_CONDITIONS;
public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldListener, ISubGuiListener {
private static final int DECLARATIVE_ID_START = 1000;
@@ -42,6 +44,12 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
private static final int L_LABEL_X = 5;
private static final int ROW_H = 24;
// Condition buttons
private static final int COND_BASE = 50;
private static final int COND_STRIDE = 10;
private static final int COND_END = COND_BASE + MAX_CONDITIONS * COND_STRIDE; // 50 + N*10
private static final int BTN_ADD_COND = COND_END;
private final Ability ability;
private final IAbilityConfigCallback callback;
private int activeTab = TAB_GENERAL;
@@ -234,17 +242,17 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
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++) {
for (int i = 0; i < conditions.size() && i < MAX_CONDITIONS; i++) {
AbilityCondition cond = conditions.get(i);
String condName = getConditionDisplayName(cond);
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"));
sw.addButton(new GuiNpcButton(COND_BASE + i * COND_STRIDE, L_LABEL_X, y, 140, 20, condName));
sw.addButton(new GuiNpcButton(COND_BASE + i * COND_STRIDE + 1, L_LABEL_X + 145, y, 40, 20, "gui.edit"));
sw.addButton(new GuiNpcButton(COND_BASE + i * COND_STRIDE + 2, L_LABEL_X + 190, y, 20, 20, "X"));
y += 22;
}
if (conditions.size() < 3) {
sw.addButton(new GuiNpcButton(80, L_LABEL_X, y, 50, 20, "gui.add"));
if (conditions.size() < MAX_CONDITIONS) {
sw.addButton(new GuiNpcButton(BTN_ADD_COND, L_LABEL_X, y, 50, 20, "gui.add"));;
y += ROW_H;
}
@@ -312,9 +320,9 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
}
private boolean handleConditionButton(int id) {
if (id >= 50 && id < 80) {
int condIndex = (id - 50) / 10;
int action = (id - 50) % 10;
if (id >= COND_BASE && id < COND_END) {
int condIndex = (id - COND_BASE) / COND_STRIDE;
int action = (id - COND_BASE) % COND_STRIDE;
if (action == 0 || action == 1) {
if (condIndex < conditions.size()) {
editingConditionIndex = condIndex;
@@ -328,8 +336,8 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
}
return true;
}
if (id == 80) {
if (conditions.size() < 3) {
if (id == BTN_ADD_COND) {
if (conditions.size() < MAX_CONDITIONS) {
editingConditionIndex = conditions.size();
setSubGui(new SubGuiConditionEdit(null));
}
@@ -32,6 +32,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition.MAX_CONDITIONS;
/**
* SubGui for editing a {@link ChainedAbility}.
@@ -70,9 +71,9 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
// Actual ID = COND_BASE + i * COND_STRIDE + offset
// offset 0 = name/click, 1 = edit, 2 = delete
private static final int COND_BASE = 50;
private static final int COND_END = 80;
private static final int COND_STRIDE = 10;
private static final int BTN_ADD_COND = 80;
private static final int COND_END = COND_BASE + MAX_CONDITIONS * COND_STRIDE; // 50 + N*10
private static final int BTN_ADD_COND = COND_END;
// ── Layout constants ──────────────────────────────────────────────────────
private static final int L_LABEL_X = 5;
@@ -427,7 +428,7 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
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++) {
for (int i = 0; i < conditions.size() && i < MAX_CONDITIONS; i++) {
AbilityCondition cond = conditions.get(i);
String condName = getConditionDisplayName(cond);
sw.addButton(new GuiNpcButton(COND_BASE + i * COND_STRIDE, L_LABEL_X, y, 140, 20, condName));
@@ -436,7 +437,7 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
y += 22;
}
if (conditions.size() < 3) {
if (conditions.size() < MAX_CONDITIONS) {
sw.addButton(new GuiNpcButton(BTN_ADD_COND, L_LABEL_X, y, 50, 20, "gui.add"));
y += ROW_H;
}
@@ -615,7 +616,7 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
return true;
}
if (id == BTN_ADD_COND) {
if (conditions.size() < 3) {
if (conditions.size() < MAX_CONDITIONS) {
editingConditionIndex = conditions.size();
setSubGui(new SubGuiConditionEdit(null));
}
@@ -2083,15 +2083,27 @@ condition.filter=Filter Type
condition.filter.target=Target
condition.filter.caster=Caster
condition.filter.both=Both
condition.hit_count=Hit Count
# Condition HP Threshold
condition.hp_threshold=HP Threshold
condition.hp_above=HP Above
condition.hp_below=HP Below
condition.threshold=Threshold
condition.threshold_type=Threshold Type
# Condition Hit Count
condition.hit_count=Hit Count
condition.required_hits=Required Hits
condition.within_ticks=Within (ticks)
# Condition Item
condition.item=Has Item
condition.usage_type=Usage Type
condition.armor_slot=Armor Slot
condition.full_armor=Full Armor
condition.item_count=Item Count
condition.item_name=Item Name
# Condition Hover Tooltips
condition.hover.type=Condition type that must be met for ability to activate
condition.hover.hpAbove=Activates when NPC health is above this percentage