mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 13:07:29 +00:00
Added ConditionItem and increased the max amount of conditions an ability can hold to 5
This commit is contained in:
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);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
Reference in new issue
Block a user