Merge branch 'dev' into syntax-highlighter-unified

This commit is contained in:
Kam committed 2026-03-06 22:03:52 -05:00
commit 5669fe2fda
19 files changed
+529 -16

No files matched your search

@@ -52,6 +52,7 @@ import noppes.npcs.api.ability.IChainedAbility;
import noppes.npcs.api.handler.IAbilityHandler;
import noppes.npcs.controllers.PlayerDataController;
import noppes.npcs.controllers.CategoryManager;
import noppes.npcs.controllers.TagController;
import noppes.npcs.controllers.data.PlayerData;
import noppes.npcs.util.NBTJsonUtil;
import kamkeel.npcs.util.FileNameHelper;
@@ -369,6 +370,8 @@ public class AbilityController implements IAbilityHandler {
}
}
TagController.validateTagUUIDs(ability.getTagUUIDs());
File dir = getCustomAbilityDir(name);
File fileNew = new File(dir, name + ".json_new");
File fileCurrent = new File(dir, name + ".json");
@@ -526,6 +529,17 @@ public class AbilityController implements IAbilityHandler {
return map;
}
public HashMap<String, HashSet<UUID>> getCustomAbilityTagMapForCategory(int catId) {
HashMap<String, HashSet<UUID>> tagMap = new HashMap<>();
for (Map.Entry<String, Ability> entry : customAbilities.entrySet()) {
int assignedCat = customAbilityCatMap.getOrDefault(entry.getKey(), CategoryManager.UNCATEGORIZED_ID);
if (assignedCat == catId && !entry.getValue().getTagUUIDs().isEmpty()) {
tagMap.put(entry.getKey(), entry.getValue().getTagUUIDs());
}
}
return tagMap;
}
public void moveCustomAbilityToCategory(String name, int destCatId) {
Ability ability = customAbilities.get(name);
if (ability == null) return;
@@ -47,6 +47,7 @@ import noppes.npcs.client.gui.builder.FieldDef;
import noppes.npcs.client.gui.util.IAbilityConfigCallback;
import noppes.npcs.controllers.AnimationController;
import noppes.npcs.controllers.ScriptContainer;
import noppes.npcs.controllers.TagController;
import noppes.npcs.controllers.data.Animation;
import noppes.npcs.controllers.data.Frame;
import noppes.npcs.controllers.data.AbilityScript;
@@ -63,8 +64,10 @@ import noppes.npcs.scripted.event.AbilityEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
public abstract class Ability implements IAbility, IAbilityAction {
@@ -129,6 +132,9 @@ public abstract class Ability implements IAbility, IAbilityAction {
// Custom data for external mods
protected NBTTagCompound customData = new NBTTagCompound();
// Tags
protected HashSet<UUID> tagUUIDs = new HashSet<>();
// Magic data — defines magic types and damage splits for this ability
protected MagicData magicData = new MagicData();
@@ -1526,6 +1532,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
nbt.setString("telegraphType", telegraphType.name());
nbt.setFloat("telegraphHeightOffset", telegraphHeightOffset);
nbt.setTag("customData", (NBTTagCompound) customData.copy());
TagController.writeTagUUIDs(nbt, "TagUUIDs", tagUUIDs);
magicData.writeToNBT(nbt);
nbt.setInteger("allowedBy", allowedBy.ordinal());
nbt.setBoolean("ignoreCooldown", ignoreCooldown);
@@ -1625,6 +1632,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
}
telegraphHeightOffset = nbt.getFloat("telegraphHeightOffset");
customData = (NBTTagCompound) nbt.getCompoundTag("customData").copy();
tagUUIDs = TagController.readTagUUIDs(nbt, "TagUUIDs");
magicData.readToNBT(nbt);
allowedBy = UserType.fromOrdinal(nbt.getInteger("allowedBy"));
ignoreCooldown = nbt.getBoolean("ignoreCooldown");
@@ -2330,6 +2338,14 @@ public abstract class Ability implements IAbility, IAbilityAction {
return customData;
}
public HashSet<UUID> getTagUUIDs() {
return tagUUIDs;
}
public void setTagUUIDs(HashSet<UUID> tagUUIDs) {
this.tagUUIDs = tagUUIDs;
}
public MagicData getMagicData() {
return magicData;
}
@@ -7,7 +7,6 @@ import noppes.npcs.controllers.AnimationController;
import kamkeel.npcs.controllers.data.ability.enums.UserType;
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.controllers.data.Animation;
import noppes.npcs.api.ability.type.IAbilityCounter;
@@ -65,7 +64,7 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
protected float performDefend(EntityLivingBase attacker, float amount) {
// For PERCENT mode with DBC, use the full DBC-calculated damage, not vanilla base
float incomingDamage = amount;
if (DBCAddon.IsAvailable() && attacker instanceof EntityPlayer) {
if (DBCAddon.IsAvailable()) {
incomingDamage = DBCAddon.instance.getAttackerDBCDamage(amount);
}
@@ -56,6 +56,9 @@ public final class AbilityTogglePacket extends AbstractPacket {
int newState = playerData.abilityData.toggleAbility(key);
// Sync toggle states to client (toggle may have side effects like mutual exclusivity)
playerData.abilityData.syncToClient();
String displayName = ability.getDisplayName();
if (newState > 0) {
String stateLabel = ability.getToggleStateLabel(newState);
@@ -18,10 +18,13 @@ import noppes.npcs.constants.EnumScrollData;
import noppes.npcs.controllers.AnimationController;
import noppes.npcs.controllers.CustomEffectController;
import noppes.npcs.controllers.LinkedItemController;
import noppes.npcs.controllers.TagController;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
public final class CategoryItemsRequestPacket extends AbstractPacket {
public static String packetName = "NPC|CatItems";
@@ -67,6 +70,12 @@ public final class CategoryItemsRequestPacket extends AbstractPacket {
Map<String, Integer> items = getItemsByCat(type, categoryId);
ScrollDataPacket.sendScrollData((EntityPlayerMP) player, items, EnumScrollData.CATEGORY_GROUP);
// Send tag map for the items
HashMap<String, HashSet<UUID>> tagMap = getTagMapByCat(type, categoryId);
if (tagMap != null && !tagMap.isEmpty()) {
TagController.sendCategoryTagMap((EntityPlayerMP) player, tagMap);
}
}
private static Map<String, Integer> getItemsByCat(int type, int catId) {
@@ -79,4 +88,13 @@ public final class CategoryItemsRequestPacket extends AbstractPacket {
}
return new HashMap<>();
}
private static HashMap<String, HashSet<UUID>> getTagMapByCat(int type, int catId) {
switch (type) {
case EnumCategoryType.EFFECT: return CustomEffectController.getInstance().getItemTagMapForCategory(catId);
case EnumCategoryType.LINKED_ITEM: return LinkedItemController.getInstance().getItemTagMapForCategory(catId);
case EnumCategoryType.ABILITY: return AbilityController.Instance.getCustomAbilityTagMapForCategory(catId);
}
return null;
}
}
@@ -15,6 +15,8 @@ import noppes.npcs.client.gui.util.GuiNpcLabel;
import noppes.npcs.client.gui.util.GuiNpcTextField;
import noppes.npcs.client.gui.util.GuiScrollWindow;
import noppes.npcs.client.gui.util.ITextfieldListener;
import noppes.npcs.client.gui.SubGuiTagSelect;
import noppes.npcs.client.gui.util.GuiNpcButton;
import noppes.npcs.client.gui.util.SubGuiInterface;
import noppes.npcs.client.renderer.ImageData;
import noppes.npcs.controllers.data.CustomEffect;
@@ -63,12 +65,14 @@ public class SubGuiEffectGeneral extends SubGuiInterface implements ITextfieldLi
guiTop += 7;
int y = guiTop + 7;
int x = guiLeft + 4 + 4;
addTextField(new GuiNpcTextField(1, this, this.fontRendererObj, x + 36, y, 280, 20, effect.name));
addTextField(new GuiNpcTextField(1, this, this.fontRendererObj, x + 36, y, 210, 20, effect.name));
getTextField(1).setMaxStringLength(40);
addLabel(new GuiNpcLabel(1, "gui.name", x, y + 5));
addLabel(new GuiNpcLabel(-1, "ID", x + 320, y + 1));
addLabel(new GuiNpcLabel(-2, effect.id + "", x + 320, y + 11));
addLabel(new GuiNpcLabel(-1, "ID", x + 250, y + 1));
addLabel(new GuiNpcLabel(-2, effect.id + "", x + 250, y + 11));
addButton(new GuiNpcButton(40, getTextField(1).xPosition + getTextField(1).width + 30, y, 70, 20, "gui.tags"));
y += 23;
@@ -243,6 +247,10 @@ public class SubGuiEffectGeneral extends SubGuiInterface implements ITextfieldLi
PacketClient.sendClient(new EffectSavePacket(effect.writeToNBT(false), originalName));
GuiScriptInterface.open(parent, new EffectScript(effect.id));
}
if (id == 40) {
setSubGui(new SubGuiTagSelect(effect.tagUUIDs));
return;
}
if (id == 10) {
GuiNpcButtonYesNo button = (GuiNpcButtonYesNo) guibutton;
effect.lossOnDeath = button.getBoolean();
@@ -0,0 +1,169 @@
package noppes.npcs.client.gui;
import kamkeel.npcs.network.PacketClient;
import kamkeel.npcs.network.packets.request.tags.TagsGetPacket;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.util.StatCollector;
import noppes.npcs.client.gui.util.GuiCustomScroll;
import noppes.npcs.client.gui.util.GuiNpcButton;
import noppes.npcs.client.gui.util.GuiNpcLabel;
import noppes.npcs.client.gui.util.GuiNpcTextField;
import noppes.npcs.client.gui.util.ICustomScrollListener;
import noppes.npcs.client.gui.util.IScrollData;
import noppes.npcs.client.gui.util.ITextfieldListener;
import noppes.npcs.client.gui.util.SubGuiInterface;
import noppes.npcs.constants.EnumScrollData;
import noppes.npcs.controllers.TagController;
import noppes.npcs.controllers.data.Tag;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
/**
* Reusable SubGui for selecting tags on any taggable object.
* Pass in the current tagUUIDs, get back the modified set on close.
*/
public class SubGuiTagSelect extends SubGuiInterface implements IScrollData, ICustomScrollListener, ITextfieldListener {
private GuiCustomScroll scrollAllTags;
private GuiCustomScroll scrollSelectedTags;
private final ArrayList<String> allTagNames = new ArrayList<>();
private final ArrayList<String> selectedTagNames = new ArrayList<>();
private final HashSet<UUID> tagUUIDs;
private String search = "";
public SubGuiTagSelect(HashSet<UUID> tagUUIDs) {
this.tagUUIDs = tagUUIDs;
setBackground("menubg.png");
xSize = 430;
ySize = 220;
// Populate selected tag names from UUIDs
TagController tc = TagController.getInstance();
if (tc != null) {
for (UUID uuid : tagUUIDs) {
Tag tag = tc.getTagFromUUID(uuid);
if (tag != null) {
selectedTagNames.add(tag.name);
}
}
}
PacketClient.sendClient(new TagsGetPacket());
}
public void initGui() {
super.initGui();
addLabel(new GuiNpcLabel(1, StatCollector.translateToLocal("tags.allTags"), guiLeft + 22, guiTop + 11));
if (scrollAllTags == null) {
scrollAllTags = new GuiCustomScroll(this, 0);
scrollAllTags.setSize(150, 155);
}
scrollAllTags.guiLeft = guiLeft + 20;
scrollAllTags.guiTop = guiTop + 24;
this.addScroll(scrollAllTags);
addTextField(new GuiNpcTextField(4, this, fontRendererObj, guiLeft + 20, guiTop + 24 + 160, 150, 20, search));
addLabel(new GuiNpcLabel(2, StatCollector.translateToLocal("tags.selectedTags"), guiLeft + 252, guiTop + 11));
if (scrollSelectedTags == null) {
scrollSelectedTags = new GuiCustomScroll(this, 1);
scrollSelectedTags.setSize(150, 180);
}
scrollSelectedTags.guiLeft = guiLeft + 250;
scrollSelectedTags.guiTop = guiTop + 24;
scrollSelectedTags.setList(selectedTagNames);
this.addScroll(scrollSelectedTags);
addButton(new GuiNpcButton(10, guiLeft + 185, guiTop + 90, 55, 20, ">"));
addButton(new GuiNpcButton(11, guiLeft + 185, guiTop + 112, 55, 20, "<"));
addButton(new GuiNpcButton(12, guiLeft + 185, guiTop + 140, 55, 20, ">>"));
addButton(new GuiNpcButton(13, guiLeft + 185, guiTop + 162, 55, 20, "<<"));
}
@Override
public void buttonEvent(GuiButton guibutton) {
if (guibutton.id == 10 && scrollAllTags.hasSelected() && !selectedTagNames.contains(scrollAllTags.getSelected())) {
selectedTagNames.add(scrollAllTags.getSelected());
}
if (guibutton.id == 12) {
selectedTagNames.clear();
selectedTagNames.addAll(allTagNames);
}
if (guibutton.id == 11 && scrollSelectedTags.hasSelected()) {
selectedTagNames.remove(scrollSelectedTags.getSelected());
}
if (guibutton.id == 13) {
selectedTagNames.clear();
}
initGui();
}
@Override
public void setData(Vector<String> list, HashMap<String, Integer> data, EnumScrollData type) {
allTagNames.clear();
allTagNames.addAll(data.keySet());
allTagNames.sort(String.CASE_INSENSITIVE_ORDER);
scrollAllTags.setList(getSearchList());
initGui();
}
@Override
public void save() {
tagUUIDs.clear();
TagController tc = TagController.getInstance();
if (tc != null) {
for (String name : selectedTagNames) {
Tag tag = tc.getTagFromName(name);
if (tag != null) {
tagUUIDs.add(tag.uuid);
}
}
}
}
public void mouseClicked(int i, int j, int k) {
super.mouseClicked(i, j, k);
if (k == 0 && scrollAllTags != null)
scrollAllTags.mouseClicked(i, j, k);
}
@Override
public void setSelected(String selected) {
if (scrollAllTags != null)
scrollAllTags.setSelected(selected);
}
@Override
public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) {
}
public void keyTyped(char c, int i) {
super.keyTyped(c, i);
if (getTextField(4) != null) {
if (search.equals(getTextField(4).getText()))
return;
search = getTextField(4).getText().toLowerCase();
scrollAllTags.setList(getSearchList());
}
}
@Override
public void unFocused(GuiNpcTextField guiNpcTextField) {
}
private List<String> getSearchList() {
if (search.isEmpty()) {
return new ArrayList<>(allTagNames);
}
List<String> list = new ArrayList<>();
for (String name : allTagNames) {
if (name.toLowerCase().contains(search))
list.add(name);
}
return list;
}
}
@@ -8,6 +8,7 @@ import noppes.npcs.controllers.data.AbilityScript;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.StatCollector;
import noppes.npcs.client.gui.SubGuiTagSelect;
import noppes.npcs.client.gui.builder.FieldDef;
import noppes.npcs.client.gui.util.GuiMenuTopButton;
import noppes.npcs.client.gui.util.GuiNpcButton;
@@ -193,6 +194,11 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
}
}
// Tags button on general tab
if (activeTab == TAB_GENERAL) {
sw.addButton(new GuiNpcButton(40, sw.clipWidth - 95, 5, 80, 20, "gui.tags"));
}
// Conditions on target tab — appended after field defs
if (activeTab == TAB_TARGET) {
int condY = renderConditions(sw, builder.getLastBuildY(), builder.getNextLabelId());
@@ -323,6 +329,10 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
GuiScriptInterface.open(parent, new AbilityScript(ability.getId()));
return;
}
if (id == 40) {
setSubGui(new SubGuiTagSelect(ability.getTagUUIDs()));
return;
}
if (id == -1000) {
close();
return;
@@ -111,6 +111,17 @@ public class AbilityHotbarSlotRenderer extends Gui {
drawCircle(radius, fillR, fillG, fillB, alpha * 0.8f, true);
}
// Toggle active indicator: subtle green outline
int toggleState = getToggleState();
if (toggleState > 0) {
float glowAlpha = 0.45f * slotAlpha;
if (altTexture) {
drawRoundedRect(-radius, -radius, radius, radius, 0.2f, 0.85f, 0.3f, glowAlpha, true);
} else {
drawCircle(radius, 0.2f, 0.85f, 0.3f, glowAlpha, true);
}
}
// Ability icon
if (icon != null) {
GL11.glPushMatrix();
@@ -119,7 +130,7 @@ public class AbilityHotbarSlotRenderer extends Gui {
if (iconNaturalSize <= 0) iconNaturalSize = 16f;
float iconScale = targetSize / iconNaturalSize;
GL11.glScalef(iconScale, iconScale, 1);
icon.draw(getToggleState(), slotAlpha);
icon.draw(toggleState, slotAlpha);
GL11.glColor4f(1, 1, 1, 1);
GL11.glPopMatrix();
}
@@ -12,6 +12,7 @@ import noppes.npcs.client.ClientCacheHandler;
import noppes.npcs.client.CustomNpcResourceListener;
import noppes.npcs.client.renderer.AnimationHelper;
import noppes.npcs.client.gui.SubGuiColorSelector;
import noppes.npcs.client.gui.SubGuiTagSelect;
import net.minecraft.client.gui.GuiScreen;
import noppes.npcs.client.gui.script.GuiScriptInterface;
import noppes.npcs.client.gui.util.GuiButtonBiDirectional;
@@ -159,6 +160,8 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe
}
GuiButtonBiDirectional useActionButton = new GuiButtonBiDirectional(6, x + 120, y, 100, 20, useActions, useActionIndex);
addButton(useActionButton);
addButton(new GuiNpcButton(40, x + 230, y, 100, 20, "gui.tags"));
y += spacing;
// Armor Type: BiDirectional button (None, All, Head, Chestplate, Leggings, Boots)
@@ -336,6 +339,9 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe
PacketClient.sendClient(new LinkedItemSavePacket(linkedItem.writeToNBT(false), originalName));
GuiScriptInterface.open(this.parent, new LinkedItemScript(linkedItem.id));
return;
} else if (id == 40) {
setSubGui(new SubGuiTagSelect(linkedItem.tagUUIDs));
return;
} else if (id == 20) {
// Open confirmation for version bump.
GuiYesNo guiyesno = new GuiYesNo(this, "Confirm", "Bump version and update all linked items?", 20);
@@ -399,6 +399,14 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
boolean isDragSource = isDragging && dragSourceSlot == -1 && draggedKey != null
&& draggedKey.equals(filteredKeys.get(absIndex));
// Compute toggle state early so border can reflect it
int cellToggleState = 0;
Ability cellAbility = filteredAbilities.get(absIndex);
PlayerData cellPlayerData = ClientCacheHandler.playerData;
if (cellAbility != null && cellAbility.isToggleable() && cellPlayerData != null && cellPlayerData.abilityData != null) {
cellToggleState = cellPlayerData.abilityData.getToggleState(filteredKeys.get(absIndex));
}
// Cell background
int bgColor;
if (isDragSource) bgColor = 0x40333333;
@@ -409,7 +417,8 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
// Cell border
int borderColor;
if (isSelected) borderColor = 0xAA9999DD;
if (cellToggleState > 0) borderColor = 0x7733DD55;
else if (isSelected) borderColor = 0xAA9999DD;
else if (isHovered) borderColor = 0xAA8888CC;
else borderColor = 0x40404060;
drawHorizontalLine(cellX, cellX + gridCellSize - 2, cellY, borderColor);
@@ -430,13 +439,6 @@ public class GuiAbilities extends GuiCNPCInventory implements ISubGuiListener {
int iconCenterX = cellX + (gridCellSize - 1) / 2;
int iconCenterY = cellY + (gridCellSize - 1) / 2;
int cellToggleState = 0;
Ability cellAbility = filteredAbilities.get(absIndex);
PlayerData cellPlayerData = ClientCacheHandler.playerData;
if (cellAbility != null && cellAbility.isToggleable() && cellPlayerData != null && cellPlayerData.abilityData != null) {
cellToggleState = cellPlayerData.abilityData.getToggleState(filteredKeys.get(absIndex));
}
GL11.glPushMatrix();
GL11.glTranslatef(iconCenterX, iconCenterY, 0);
GL11.glScalef(iconScale, iconScale, 1);
@@ -0,0 +1,102 @@
package noppes.npcs.client.gui.util;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.util.StatCollector;
import noppes.npcs.controllers.TagController;
import noppes.npcs.controllers.data.Tag;
import java.util.HashMap;
import java.util.HashSet;
import java.util.UUID;
/**
* Scroll list that renders CNPC tags next to item names.
* Used in directory/fullscreen GUIs for Forms, Auras, Outlines, etc.
*/
public class GuiCustomScrollTagged extends GuiCustomScroll {
private HashMap<String, HashSet<UUID>> itemTagMap = new HashMap<>();
private boolean showTags = true;
public GuiCustomScrollTagged(GuiScreen parent, int id) {
super(parent, id);
}
public void setItemTagMap(HashMap<String, HashSet<UUID>> tagMap) {
this.itemTagMap = tagMap != null ? tagMap : new HashMap<>();
}
public HashMap<String, HashSet<UUID>> getItemTagMap() {
return itemTagMap;
}
public void setShowTags(boolean show) {
this.showTags = show;
}
@Override
protected void drawItems() {
for (int i = 0; i < list.size(); i++) {
int j = 4;
int k = (14 * i + 4) - scrollY;
if (k >= 4 && k + 12 < ySize) {
int xOffset = scrollHeight < ySize - 8 ? 0 : 10;
String rawEntry = list.get(i);
String displayString = StatCollector.translateToLocal(rawEntry);
String text = "";
float maxWidth = showTags && itemTagMap.containsKey(rawEntry)
? (xSize + xOffset - 8) * 0.5f
: (xSize + xOffset - 8) * 0.8f;
if (fontRendererObj.getStringWidth(displayString) > maxWidth) {
for (int h = 0; h < displayString.length(); h++) {
char c = displayString.charAt(h);
text += c;
if (fontRendererObj.getStringWidth(text) > maxWidth)
break;
}
if (displayString.length() > text.length())
text += "...";
} else {
text = displayString;
}
Integer customColor = colors.get(rawEntry);
if (customColor != null) {
if (!text.isEmpty())
fontRendererObj.drawString(text, j, k, customColor);
} else if ((multipleSelection && selectedList.contains(text)) || (!multipleSelection && selected == i)) {
drawVerticalLine(j - 2, k - 4, k + 10, 0xffffffff);
drawVerticalLine(j + xSize - 18 + xOffset, k - 4, k + 10, 0xffffffff);
drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k - 3, 0xffffffff);
drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k + 10, 0xffffffff);
fontRendererObj.drawString(text, j, k, 0xffffff);
} else if (i == hover) {
fontRendererObj.drawString(text, j, k, 0x00ff00);
} else {
fontRendererObj.drawString(text, j, k, 0xffffff);
}
// Draw tags after item name
if (showTags && itemTagMap.containsKey(rawEntry)) {
int tagStartX = j + fontRendererObj.getStringWidth(text) + 3;
int maxX = j + xSize - 18 + xOffset;
HashSet<UUID> tagUUIDs = itemTagMap.get(rawEntry);
TagController tc = TagController.getInstance();
if (tc != null && tagUUIDs != null) {
for (UUID uuid : tagUUIDs) {
Tag tag = tc.getTagFromUUID(uuid);
if (tag != null && !tag.hideTag) {
String tagText = "[" + tag.name + "]";
int tagWidth = fontRendererObj.getStringWidth(tagText);
if (tagStartX + tagWidth > maxX) break;
fontRendererObj.drawString(tagText, tagStartX, k, tag.color);
tagStartX += tagWidth + 3;
}
}
}
}
}
}
}
}
@@ -1,7 +1,9 @@
package noppes.npcs.client.gui.util;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.ResourceLocation;
import noppes.npcs.controllers.TagController;
import noppes.npcs.controllers.data.Category;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
@@ -20,6 +22,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
/**
@@ -32,7 +35,7 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
// ========== Scrolls ==========
protected GuiCustomScrollIcons catScroll = new GuiCustomScrollIcons(this, 2);
protected GuiCustomScroll itemScroll = new GuiCustomScroll(this, 0);
protected GuiCustomScrollTagged itemScroll = new GuiCustomScrollTagged(this, 0);
// ========== Data ==========
protected HashMap<String, Integer> catData = new HashMap<>();
@@ -463,8 +466,24 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
if (selectedCatId < 0) return new ArrayList<>();
if (itemSearch.isEmpty()) return new ArrayList<>(itemData.keySet());
List<String> list = new ArrayList<>();
HashMap<String, HashSet<UUID>> tagMap = itemScroll.getItemTagMap();
for (String name : itemData.keySet()) {
if (name.toLowerCase().contains(itemSearch)) list.add(name);
if (name.toLowerCase().contains(itemSearch)) {
list.add(name);
} else if (tagMap != null && tagMap.containsKey(name)) {
// Search by tag name — match [tagname] or just tagname
HashSet<UUID> uuids = tagMap.get(name);
TagController tc = TagController.getInstance();
if (tc != null) {
for (UUID uuid : uuids) {
noppes.npcs.controllers.data.Tag tag = tc.getTagFromUUID(uuid);
if (tag != null && tag.name.toLowerCase().contains(itemSearch.replace("[", "").replace("]", ""))) {
list.add(name);
break;
}
}
}
}
}
return list;
}
@@ -693,6 +712,21 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
@Override
public void setGuiData(NBTTagCompound compound) {
if (compound.hasKey("CategoryTagMap")) {
// Tag map for category items
HashMap<String, HashSet<UUID>> tagMap = new HashMap<>();
NBTTagList entries = compound.getTagList("CategoryTagMap", 10);
for (int i = 0; i < entries.tagCount(); i++) {
NBTTagCompound entry = entries.getCompoundTagAt(i);
String itemName = entry.getString("Name");
HashSet<UUID> uuids = TagController.readTagUUIDs(entry, "Tags");
if (!uuids.isEmpty()) {
tagMap.put(itemName, uuids);
}
}
itemScroll.setItemTagMap(tagMap);
return;
}
onItemReceived(compound);
if (pendingNewItemCatId > 0 && hasSelectedItem() && getSelectedItemId() >= 0) {
sendMovePacket(getSelectedItemId(), pendingNewItemCatId);
@@ -25,6 +25,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -657,6 +658,18 @@ public class CustomEffectController implements ICustomEffectHandler {
return map;
}
public HashMap<String, HashSet<UUID>> getItemTagMapForCategory(int catId) {
HashMap<String, HashSet<UUID>> tagMap = new HashMap<>();
List<Integer> itemIds = categoryManager.getItemsInCategory(catId, getCustomEffects().keySet());
for (int itemId : itemIds) {
CustomEffect effect = getCustomEffects().get(itemId);
if (effect != null && !effect.tagUUIDs.isEmpty()) {
tagMap.put(effect.name, effect.tagUUIDs);
}
}
return tagMap;
}
@Override
public ICustomEffect saveEffect(ICustomEffect customEffect) {
if (customEffect.getID() < 0) {
@@ -668,6 +681,7 @@ public class CustomEffectController implements ICustomEffectHandler {
while (hasOther(customEffect.getName(), customEffect.getID()))
customEffect.setName(customEffect.getName() + "_");
TagController.validateTagUUIDs(((CustomEffect) customEffect).tagUUIDs);
getCustomEffects().remove(customEffect.getID());
getCustomEffects().put(customEffect.getID(), (CustomEffect) customEffect);
@@ -20,8 +20,10 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
public class LinkedItemController implements ILinkedItemHandler {
@@ -215,6 +217,7 @@ public class LinkedItemController implements ILinkedItemHandler {
while (hasOther(linkedItem.getName(), linkedItem.getId()))
linkedItem.setName(linkedItem.getName() + "_");
TagController.validateTagUUIDs(((LinkedItem) linkedItem).tagUUIDs);
linkedItems.remove(linkedItem.getId());
linkedItems.put(linkedItem.getId(), (LinkedItem) linkedItem);
saveLinkedItemsMap();
@@ -387,4 +390,16 @@ public class LinkedItemController implements ILinkedItemHandler {
}
return map;
}
public HashMap<String, HashSet<UUID>> getItemTagMapForCategory(int catId) {
HashMap<String, HashSet<UUID>> tagMap = new HashMap<>();
List<Integer> itemIds = categoryManager.getItemsInCategory(catId, linkedItems.keySet());
for (int itemId : itemIds) {
LinkedItem item = linkedItems.get(itemId);
if (item != null && !item.tagUUIDs.isEmpty()) {
tagMap.put(item.name, item.tagUUIDs);
}
}
return tagMap;
}
}
@@ -1,5 +1,8 @@
package noppes.npcs.controllers;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.packets.data.large.GuiDataPacket;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
@@ -9,6 +12,9 @@ import noppes.npcs.api.handler.ITagHandler;
import noppes.npcs.api.handler.data.ITag;
import noppes.npcs.controllers.data.Tag;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
@@ -18,6 +24,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -234,4 +241,69 @@ public class TagController implements ITagHandler {
public HashSet<Tag> getAllTags() {
return new HashSet<Tag>(this.tags.values());
}
/**
* Write a set of tag UUIDs to an NBT compound under the given key.
*/
public static void writeTagUUIDs(NBTTagCompound compound, String key, HashSet<UUID> tagUUIDs) {
if (tagUUIDs != null && !tagUUIDs.isEmpty()) {
NBTTagList list = new NBTTagList();
for (UUID uuid : tagUUIDs) {
list.appendTag(new NBTTagString(uuid.toString()));
}
compound.setTag(key, list);
}
}
/**
* Read a set of tag UUIDs from an NBT compound under the given key.
*/
public static HashSet<UUID> readTagUUIDs(NBTTagCompound compound, String key) {
HashSet<UUID> uuids = new HashSet<>();
if (compound.hasKey(key)) {
NBTTagList list = compound.getTagList(key, 8);
for (int i = 0; i < list.tagCount(); i++) {
try {
uuids.add(UUID.fromString(list.getStringTagAt(i)));
} catch (IllegalArgumentException ignored) {}
}
}
return uuids;
}
/**
* Remove invalid tag UUIDs (those not in the tag registry).
*/
public static void validateTagUUIDs(HashSet<UUID> tagUUIDs) {
TagController tc = getInstance();
if (tc == null) return;
tagUUIDs.removeIf(uuid -> tc.getTagFromUUID(uuid) == null);
}
/**
* Build and send a CategoryTagMap GuiData packet for a set of items.
* @param player the target player
* @param itemTags map of item name -> tag UUIDs (only validated tags are sent)
*/
public static void sendCategoryTagMap(EntityPlayerMP player, HashMap<String, HashSet<UUID>> itemTags) {
NBTTagCompound compound = new NBTTagCompound();
NBTTagList entries = new NBTTagList();
TagController tc = getInstance();
for (Map.Entry<String, HashSet<UUID>> entry : itemTags.entrySet()) {
HashSet<UUID> valid = new HashSet<>();
for (UUID uuid : entry.getValue()) {
if (tc != null && tc.getTagFromUUID(uuid) != null) {
valid.add(uuid);
}
}
if (!valid.isEmpty()) {
NBTTagCompound itemEntry = new NBTTagCompound();
itemEntry.setString("Name", entry.getKey());
writeTagUUIDs(itemEntry, "Tags", valid);
entries.appendTag(itemEntry);
}
}
compound.setTag("CategoryTagMap", entries);
GuiDataPacket.sendGuiData(player, compound);
}
}
@@ -9,8 +9,11 @@ import noppes.npcs.api.entity.IPlayer;
import noppes.npcs.api.handler.data.ICustomEffect;
import noppes.npcs.constants.EnumScriptType;
import noppes.npcs.controllers.CustomEffectController;
import noppes.npcs.controllers.TagController;
import noppes.npcs.scripted.event.player.PlayerEvent;
import java.util.HashSet;
import java.util.UUID;
import java.util.function.Consumer;
public class CustomEffect implements ICustomEffect {
@@ -36,6 +39,8 @@ public class CustomEffect implements ICustomEffect {
public int index = 0;
public HashSet<UUID> tagUUIDs = new HashSet<>();
public CustomEffect() {
}
@@ -261,6 +266,8 @@ public class CustomEffect implements ICustomEffect {
compound.setInteger("iconFrameCount", frameCount);
compound.setInteger("iconFrameTime", frametime);
TagController.writeTagUUIDs(compound, "TagUUIDs", tagUUIDs);
if (saveScripts) {
NBTTagCompound scriptData = new NBTTagCompound();
EffectScript handler = getScriptHandler();
@@ -309,6 +316,8 @@ public class CustomEffect implements ICustomEffect {
if (compound.hasKey("iconFrameTime"))
frametime = Math.max(1, compound.getInteger("iconFrameTime"));
tagUUIDs = TagController.readTagUUIDs(compound, "TagUUIDs");
if (compound.hasKey("ScriptData", Constants.NBT.TAG_COMPOUND)) {
EffectScript handler = new EffectScript();
handler.readFromNBT(compound.getCompoundTag("ScriptData"));
@@ -8,9 +8,13 @@ import noppes.npcs.EventHooks;
import noppes.npcs.api.handler.data.ILinkedItem;
import noppes.npcs.api.item.IItemStack;
import noppes.npcs.controllers.LinkedItemController;
import noppes.npcs.controllers.TagController;
import noppes.npcs.scripted.NpcAPI;
import noppes.npcs.scripted.item.ScriptLinkedItem;
import java.util.HashSet;
import java.util.UUID;
public class LinkedItem implements ILinkedItem {
public static final String LINKED_VERSION_VERSION_TAG = "LinkedVersion";
public static final String LINKED_DATA_NBT_TAG = "LinkedData";
@@ -33,6 +37,8 @@ public class LinkedItem implements ILinkedItem {
public int armorType = -2; //-2: Fits in no armor slot, -1: Fits in all slots, 0 - 3: Fits in Head -> Boots slot respectively
public int enchantability;
public HashSet<UUID> tagUUIDs = new HashSet<>();
public LinkedItem() {
this("New");
}
@@ -67,6 +73,8 @@ public class LinkedItem implements ILinkedItem {
compound.setInteger("MaxItemUseDuration", this.maxItemUseDuration);
compound.setInteger("ItemUseAction", this.itemUseAction);
TagController.writeTagUUIDs(compound, "TagUUIDs", tagUUIDs);
if (saveScripts) {
NBTTagCompound scriptData = new NBTTagCompound();
LinkedItemScript handler = getScriptHandler();
@@ -97,6 +105,8 @@ public class LinkedItem implements ILinkedItem {
this.maxItemUseDuration = compound.getInteger("MaxItemUseDuration");
this.itemUseAction = compound.getInteger("ItemUseAction");
tagUUIDs = TagController.readTagUUIDs(compound, "TagUUIDs");
if (compound.hasKey("ScriptData", Constants.NBT.TAG_COMPOUND)) {
LinkedItemScript handler = new LinkedItemScript();
handler.readFromNBT(compound.getCompoundTag("ScriptData"));
@@ -2208,6 +2208,7 @@ ability.icon.layer=Layer
gui.both=Both
gui.players=Players
gui.tags=Tags
# Conditions
condition.edit=Edit Condition