2025-02-24 16:08:22 -05:00
|
|
|
|
package noppes.npcs.attribute;
|
|
|
|
|
|
|
2025-02-26 22:49:26 -05:00
|
|
|
|
import cpw.mods.fml.relauncher.Side;
|
|
|
|
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
|
|
|
|
import net.minecraft.client.Minecraft;
|
2025-02-24 16:08:22 -05:00
|
|
|
|
import net.minecraft.item.ItemStack;
|
|
|
|
|
|
import net.minecraft.nbt.NBTTagCompound;
|
2025-02-26 22:49:26 -05:00
|
|
|
|
import net.minecraft.util.EnumChatFormatting;
|
|
|
|
|
|
import net.minecraft.util.StatCollector;
|
|
|
|
|
|
import noppes.npcs.client.ClientProxy;
|
2025-02-27 00:30:39 -05:00
|
|
|
|
import noppes.npcs.controllers.MagicController;
|
|
|
|
|
|
import noppes.npcs.controllers.data.Magic;
|
2025-02-26 22:49:26 -05:00
|
|
|
|
import org.lwjgl.input.Keyboard;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
|
|
import static noppes.npcs.attribute.AttributeDefinition.AttributeSection.INFO;
|
|
|
|
|
|
import static noppes.npcs.attribute.AttributeDefinition.AttributeSection.MODIFIER;
|
2025-02-24 16:08:22 -05:00
|
|
|
|
|
|
|
|
|
|
/**
|
2025-02-24 20:07:32 -05:00
|
|
|
|
* Provides helper functions to write/read item attributes to/from NBT.
|
|
|
|
|
|
* Non–magic attributes are stored under a compound tag (TAG_ROOT),
|
|
|
|
|
|
* and magic attributes are stored as a compound mapping (by magic ID) under specific keys.
|
2025-02-24 16:08:22 -05:00
|
|
|
|
*/
|
2025-02-25 17:14:55 -05:00
|
|
|
|
public class ItemAttributeHelper {
|
2025-02-26 22:49:26 -05:00
|
|
|
|
public static final String RPGItemAttributes = "RPGCoreAttributes";
|
2025-02-24 16:08:22 -05:00
|
|
|
|
|
2025-02-25 17:14:55 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Applies a non–magic attribute to an item.
|
|
|
|
|
|
*/
|
2025-02-24 16:08:22 -05:00
|
|
|
|
public static void applyAttribute(ItemStack item, String attributeKey, double value) {
|
2025-02-24 20:07:32 -05:00
|
|
|
|
if (item == null) return;
|
|
|
|
|
|
if (item.stackTagCompound == null) {
|
2025-02-24 16:08:22 -05:00
|
|
|
|
item.stackTagCompound = new NBTTagCompound();
|
|
|
|
|
|
}
|
|
|
|
|
|
NBTTagCompound root = item.stackTagCompound;
|
|
|
|
|
|
NBTTagCompound attrTag;
|
2025-02-26 22:49:26 -05:00
|
|
|
|
if (root.hasKey(RPGItemAttributes)) {
|
|
|
|
|
|
attrTag = root.getCompoundTag(RPGItemAttributes);
|
2025-02-24 16:08:22 -05:00
|
|
|
|
} else {
|
|
|
|
|
|
attrTag = new NBTTagCompound();
|
|
|
|
|
|
}
|
|
|
|
|
|
attrTag.setDouble(attributeKey, value);
|
2025-02-26 22:49:26 -05:00
|
|
|
|
root.setTag(RPGItemAttributes, attrTag);
|
2025-02-24 16:08:22 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-25 17:14:55 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Removes a non–magic attribute from an item.
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static void removeAttribute(ItemStack item, String attributeKey) {
|
|
|
|
|
|
if (item == null || item.stackTagCompound == null) return;
|
|
|
|
|
|
NBTTagCompound root = item.stackTagCompound;
|
2025-02-26 22:49:26 -05:00
|
|
|
|
if (root.hasKey(RPGItemAttributes)) {
|
|
|
|
|
|
NBTTagCompound attrTag = root.getCompoundTag(RPGItemAttributes);
|
2025-02-25 17:14:55 -05:00
|
|
|
|
attrTag.removeTag(attributeKey);
|
|
|
|
|
|
// If no attributes remain, remove the root tag.
|
|
|
|
|
|
if (attrTag.func_150296_c().isEmpty()) {
|
2025-02-26 22:49:26 -05:00
|
|
|
|
root.removeTag(RPGItemAttributes);
|
2025-02-25 17:14:55 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Reads non–magic attributes from an item.
|
|
|
|
|
|
*/
|
2025-02-24 16:08:22 -05:00
|
|
|
|
public static Map<String, Double> readAttributes(ItemStack item) {
|
|
|
|
|
|
Map<String, Double> map = new HashMap<>();
|
2025-02-24 20:07:32 -05:00
|
|
|
|
if (item == null || item.stackTagCompound == null) return map;
|
2025-02-24 16:08:22 -05:00
|
|
|
|
NBTTagCompound root = item.stackTagCompound;
|
2025-02-26 22:49:26 -05:00
|
|
|
|
if (root.hasKey(RPGItemAttributes)) {
|
|
|
|
|
|
NBTTagCompound attrTag = root.getCompoundTag(RPGItemAttributes);
|
2025-02-24 16:08:22 -05:00
|
|
|
|
Set<String> keys = attrTag.func_150296_c();
|
2025-02-24 20:07:32 -05:00
|
|
|
|
for (String key : keys) {
|
2025-02-24 16:08:22 -05:00
|
|
|
|
map.put(key, attrTag.getDouble(key));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return map;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-25 17:14:55 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Reads a magic attribute map from an item.
|
|
|
|
|
|
* The given attributeTag is the key under which the compound is stored (e.g., MAGIC_DAMAGE_FLAT).
|
|
|
|
|
|
*/
|
2025-02-24 16:08:22 -05:00
|
|
|
|
public static Map<Integer, Double> readMagicAttributeMap(ItemStack item, String attributeTag) {
|
|
|
|
|
|
Map<Integer, Double> map = new HashMap<>();
|
2025-02-24 20:07:32 -05:00
|
|
|
|
if (item == null || item.stackTagCompound == null) return map;
|
|
|
|
|
|
if (item.stackTagCompound.hasKey(attributeTag)) {
|
2025-02-24 16:08:22 -05:00
|
|
|
|
NBTTagCompound magicMap = item.stackTagCompound.getCompoundTag(attributeTag);
|
|
|
|
|
|
Set<String> keys = magicMap.func_150296_c();
|
2025-02-24 20:07:32 -05:00
|
|
|
|
for (String key : keys) {
|
2025-02-24 16:08:22 -05:00
|
|
|
|
try {
|
|
|
|
|
|
int magicId = Integer.parseInt(key);
|
|
|
|
|
|
map.put(magicId, magicMap.getDouble(key));
|
2025-02-24 20:07:32 -05:00
|
|
|
|
} catch (NumberFormatException e) {
|
2025-02-24 16:08:22 -05:00
|
|
|
|
// Skip invalid key.
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return map;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-25 17:14:55 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Applies (writes) a magic attribute to an item.
|
|
|
|
|
|
* This is essentially the same as writeMagicAttribute.
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static void applyMagicAttribute(ItemStack item, String attributeTag, int magicId, double value) {
|
|
|
|
|
|
writeMagicAttribute(item, attributeTag, magicId, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Writes a magic attribute value to the given attributeTag.
|
|
|
|
|
|
*/
|
2025-02-24 16:08:22 -05:00
|
|
|
|
public static void writeMagicAttribute(ItemStack item, String attributeTag, int magicId, double value) {
|
2025-02-24 20:07:32 -05:00
|
|
|
|
if (item == null) return;
|
|
|
|
|
|
if (item.stackTagCompound == null)
|
2025-02-24 16:08:22 -05:00
|
|
|
|
item.stackTagCompound = new NBTTagCompound();
|
|
|
|
|
|
NBTTagCompound magicMap;
|
2025-02-24 20:07:32 -05:00
|
|
|
|
if (item.stackTagCompound.hasKey(attributeTag)) {
|
2025-02-24 16:08:22 -05:00
|
|
|
|
magicMap = item.stackTagCompound.getCompoundTag(attributeTag);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
magicMap = new NBTTagCompound();
|
|
|
|
|
|
}
|
|
|
|
|
|
magicMap.setDouble(String.valueOf(magicId), value);
|
|
|
|
|
|
item.stackTagCompound.setTag(attributeTag, magicMap);
|
|
|
|
|
|
}
|
2025-02-25 17:14:55 -05:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Removes a magic attribute value from the given attributeTag.
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static void removeMagicAttribute(ItemStack item, String attributeTag, int magicId) {
|
|
|
|
|
|
if (item == null || item.stackTagCompound == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
NBTTagCompound root = item.stackTagCompound;
|
|
|
|
|
|
if (root.hasKey(attributeTag)) {
|
|
|
|
|
|
NBTTagCompound magicMap = root.getCompoundTag(attributeTag);
|
|
|
|
|
|
magicMap.removeTag(String.valueOf(magicId));
|
|
|
|
|
|
// If the magic map is empty, remove the tag.
|
|
|
|
|
|
if (magicMap.func_150296_c().isEmpty())
|
|
|
|
|
|
root.removeTag(attributeTag);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-02-26 22:49:26 -05:00
|
|
|
|
|
|
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
|
|
|
|
public static List<String> getToolTip(List<String> original, NBTTagCompound compound) {
|
2025-02-27 00:30:39 -05:00
|
|
|
|
List<String> tooltip = new ArrayList<>(original);
|
2025-02-26 22:49:26 -05:00
|
|
|
|
NBTTagCompound attrTag = compound.getCompoundTag(RPGItemAttributes);
|
|
|
|
|
|
if (Keyboard.isKeyDown(ClientProxy.NPCButton.getKeyCode())) {
|
|
|
|
|
|
List<String> newTooltips = new ArrayList<>();
|
|
|
|
|
|
if (!tooltip.isEmpty()) {
|
2025-02-27 00:30:39 -05:00
|
|
|
|
newTooltips.add(tooltip.get(0));
|
2025-02-26 22:49:26 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-27 00:30:39 -05:00
|
|
|
|
// Lists for non–magic attributes
|
2025-02-26 22:49:26 -05:00
|
|
|
|
List<String> baseList = new ArrayList<>();
|
|
|
|
|
|
List<String> modifierList = new ArrayList<>();
|
|
|
|
|
|
List<String> infoList = new ArrayList<>();
|
|
|
|
|
|
List<String> extraList = new ArrayList<>();
|
|
|
|
|
|
|
2025-02-27 00:30:39 -05:00
|
|
|
|
// Process non–magic attributes (order maintained)
|
|
|
|
|
|
Iterator<String> iter = attrTag.func_150296_c().iterator();
|
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
|
String key = iter.next();
|
|
|
|
|
|
// Skip magic attribute keys; they are handled separately.
|
|
|
|
|
|
if(key.equals(ModAttributes.MAGIC_DAMAGE_KEY) || key.equals(ModAttributes.MAGIC_BOOST_KEY)
|
|
|
|
|
|
|| key.equals(ModAttributes.MAGIC_DEFENSE_KEY) || key.equals(ModAttributes.MAGIC_RESISTANCE_KEY)) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-02-26 22:49:26 -05:00
|
|
|
|
double value = attrTag.getDouble(key);
|
|
|
|
|
|
AttributeDefinition def = AttributeController.getAttribute(key);
|
|
|
|
|
|
AttributeDefinition.AttributeSection section = def != null ? def.getSection() : AttributeDefinition.AttributeSection.EXTRA;
|
2025-02-27 00:30:39 -05:00
|
|
|
|
String displayName = getTranslatedAttributeName(key, def);
|
|
|
|
|
|
String line = formatAttributeLine(def, section, value, displayName);
|
|
|
|
|
|
switch (section) {
|
|
|
|
|
|
case BASE:
|
|
|
|
|
|
baseList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case MODIFIER:
|
|
|
|
|
|
modifierList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case INFO:
|
|
|
|
|
|
infoList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
extraList.add(line);
|
|
|
|
|
|
break;
|
2025-02-26 22:49:26 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-27 00:30:39 -05:00
|
|
|
|
// Process magic attributes and merge them into the proper lists.
|
|
|
|
|
|
processMagicAttributes(compound, baseList, modifierList, infoList, extraList);
|
|
|
|
|
|
|
|
|
|
|
|
// Append sections (adds an empty line before each if not empty)
|
|
|
|
|
|
newTooltips.addAll(buildSection(baseList));
|
|
|
|
|
|
newTooltips.addAll(buildSection(modifierList));
|
|
|
|
|
|
newTooltips.addAll(buildSection(infoList));
|
|
|
|
|
|
newTooltips.addAll(buildSection(extraList));
|
|
|
|
|
|
|
2025-02-26 22:49:26 -05:00
|
|
|
|
tooltip = newTooltips;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
String keyName = Keyboard.getKeyName(ClientProxy.NPCButton.getKeyCode());
|
|
|
|
|
|
tooltip.add(EnumChatFormatting.YELLOW + "" + EnumChatFormatting.ITALIC +
|
|
|
|
|
|
StatCollector.translateToLocal("rpgcore:tooltip").replace("%key%", keyName));
|
|
|
|
|
|
}
|
|
|
|
|
|
return tooltip;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-27 00:30:39 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Processes magic attributes from the compound.
|
|
|
|
|
|
* For each magic key (from ModAttributes), it retrieves the stored magic attributes,
|
|
|
|
|
|
* formats each line (using the magic's display name) and adds it to the proper section list.
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static void processMagicAttributes(NBTTagCompound compound, List<String> baseList, List<String> modifierList,
|
|
|
|
|
|
List<String> infoList, List<String> extraList) {
|
|
|
|
|
|
String[] magicKeys = {
|
|
|
|
|
|
ModAttributes.MAGIC_DAMAGE_KEY,
|
|
|
|
|
|
ModAttributes.MAGIC_BOOST_KEY,
|
|
|
|
|
|
ModAttributes.MAGIC_DEFENSE_KEY,
|
|
|
|
|
|
ModAttributes.MAGIC_RESISTANCE_KEY
|
|
|
|
|
|
};
|
|
|
|
|
|
for (String magicKey : magicKeys) {
|
|
|
|
|
|
if (compound.hasKey(magicKey)) {
|
|
|
|
|
|
NBTTagCompound magicTag = compound.getCompoundTag(magicKey);
|
|
|
|
|
|
// Use the attribute definition for this magic key to determine its section.
|
|
|
|
|
|
AttributeDefinition def = AttributeController.getAttribute(magicKey);
|
|
|
|
|
|
AttributeDefinition.AttributeSection section = def != null ? def.getSection() : AttributeDefinition.AttributeSection.EXTRA;
|
|
|
|
|
|
Iterator<String> magicIter = magicTag.func_150296_c().iterator();
|
|
|
|
|
|
while (magicIter.hasNext()) {
|
|
|
|
|
|
String key = magicIter.next();
|
|
|
|
|
|
try {
|
|
|
|
|
|
int magicId = Integer.parseInt(key);
|
|
|
|
|
|
double value = magicTag.getDouble(key);
|
|
|
|
|
|
Magic magic = MagicController.getInstance().getMagic(magicId);
|
|
|
|
|
|
if (magic != null) {
|
2025-02-27 09:27:34 -05:00
|
|
|
|
String magicDisplayName = magic.getDisplayName() + " " + EnumChatFormatting.GRAY + getMagicAppendix(magicKey);
|
2025-02-27 00:30:39 -05:00
|
|
|
|
// Format using the magic attribute's definition and the magic's display name.
|
|
|
|
|
|
String line = formatAttributeLine(def, section, value, magicDisplayName);
|
|
|
|
|
|
switch (section) {
|
|
|
|
|
|
case BASE:
|
|
|
|
|
|
baseList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case MODIFIER:
|
|
|
|
|
|
modifierList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case INFO:
|
|
|
|
|
|
infoList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
extraList.add(line);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
|
// Skip invalid key.
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-27 09:27:34 -05:00
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
|
|
|
|
private static String getMagicAppendix(String type){
|
|
|
|
|
|
switch(type){
|
|
|
|
|
|
case ModAttributes.MAGIC_DEFENSE_KEY:
|
|
|
|
|
|
return StatCollector.translateToLocal("rpgcore:attribute.defense");
|
|
|
|
|
|
case ModAttributes.MAGIC_RESISTANCE_KEY:
|
|
|
|
|
|
return StatCollector.translateToLocal("rpgcore:attribute.resistance");
|
|
|
|
|
|
default:
|
|
|
|
|
|
return StatCollector.translateToLocal("rpgcore:attribute.damage");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-27 00:30:39 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Returns a translated attribute name.
|
|
|
|
|
|
*/
|
2025-02-27 09:27:34 -05:00
|
|
|
|
@SideOnly(Side.CLIENT)
|
2025-02-27 00:30:39 -05:00
|
|
|
|
private static String getTranslatedAttributeName(String key, AttributeDefinition def) {
|
|
|
|
|
|
String translation = null;
|
|
|
|
|
|
if (key.contains(":")) {
|
|
|
|
|
|
if (StatCollector.canTranslate(key))
|
|
|
|
|
|
translation = StatCollector.translateToLocal(key);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (StatCollector.canTranslate("rpgcore:attribute." + key))
|
|
|
|
|
|
translation = StatCollector.translateToLocal("rpgcore:attribute." + key);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (translation == null && def != null)
|
|
|
|
|
|
translation = def.getDisplayName();
|
|
|
|
|
|
else if (translation == null)
|
|
|
|
|
|
translation = key;
|
|
|
|
|
|
return translation;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Formats the attribute line based on its section and value type.
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static String formatAttributeLine(AttributeDefinition def, AttributeDefinition.AttributeSection section,
|
|
|
|
|
|
double value, String displayName) {
|
|
|
|
|
|
String formattedValue = formatDouble(value);
|
|
|
|
|
|
if (section == AttributeDefinition.AttributeSection.MODIFIER || section == AttributeDefinition.AttributeSection.INFO) {
|
|
|
|
|
|
String sign = value >= 0 ? "+" : "";
|
|
|
|
|
|
String color = value >= 0 ? EnumChatFormatting.GREEN.toString() : EnumChatFormatting.RED.toString();
|
2025-02-27 00:48:10 -05:00
|
|
|
|
String valueString = color + sign + formattedValue;
|
2025-02-27 00:30:39 -05:00
|
|
|
|
if (def != null && def.getValueType() == AttributeValueType.PERCENT)
|
|
|
|
|
|
valueString += "%";
|
2025-02-27 00:48:10 -05:00
|
|
|
|
valueString += EnumChatFormatting.GRAY;
|
2025-02-27 00:30:39 -05:00
|
|
|
|
return valueString + " " + displayName;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (def != null)
|
|
|
|
|
|
displayName = "\u00A7" + def.getColorCode() + displayName;
|
|
|
|
|
|
else
|
|
|
|
|
|
displayName = EnumChatFormatting.AQUA + displayName;
|
|
|
|
|
|
formattedValue = EnumChatFormatting.GRAY + formattedValue;
|
|
|
|
|
|
return displayName + "\u00A77: " + formattedValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Builds a section from a list of tooltip lines.
|
|
|
|
|
|
* Adds an empty line before the section if it contains lines.
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static List<String> buildSection(List<String> lines) {
|
|
|
|
|
|
List<String> section = new ArrayList<>();
|
|
|
|
|
|
if (!lines.isEmpty()) {
|
|
|
|
|
|
section.add("");
|
|
|
|
|
|
section.addAll(lines);
|
|
|
|
|
|
}
|
|
|
|
|
|
return section;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-26 22:49:26 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* Helper to format a double by removing trailing zeros.
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static String formatDouble(double value) {
|
|
|
|
|
|
return new java.math.BigDecimal(Double.toString(value)).stripTrailingZeros().toPlainString();
|
|
|
|
|
|
}
|
2025-02-24 16:08:22 -05:00
|
|
|
|
}
|