Minestom/src/main/java/net/minestom/server/item/ItemStack.java

548 lines
15 KiB
Java
Raw Normal View History

2020-04-24 03:25:58 +02:00
package net.minestom.server.item;
2019-08-12 08:30:59 +02:00
2020-06-22 23:25:00 +02:00
import net.minestom.server.chat.ColoredText;
2020-04-24 03:25:58 +02:00
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
2020-05-28 23:43:12 +02:00
import net.minestom.server.item.attribute.ItemAttribute;
2020-04-24 03:25:58 +02:00
import net.minestom.server.item.rule.VanillaStackingRule;
2020-05-27 12:33:12 +02:00
import net.minestom.server.potion.PotionType;
2020-05-23 04:20:01 +02:00
import net.minestom.server.utils.validate.Check;
2019-08-29 02:15:52 +02:00
2020-05-22 21:46:50 +02:00
import java.util.*;
2020-02-13 15:14:41 +01:00
2019-08-29 02:15:52 +02:00
public class ItemStack implements DataContainer {
2019-08-12 08:30:59 +02:00
2020-05-27 12:33:12 +02:00
private static final StackingRule DEFAULT_STACKING_RULE = new VanillaStackingRule(127);
2020-04-23 12:30:49 +02:00
public static ItemStack getAirItem() {
return new ItemStack((short) 0, (byte) 0);
}
2020-03-20 19:50:22 +01:00
private static StackingRule defaultStackingRule;
2020-04-10 12:45:04 +02:00
private short materialId;
2019-08-12 08:30:59 +02:00
2019-08-13 17:52:09 +02:00
private byte amount;
2019-09-06 16:05:36 +02:00
private short damage;
2019-08-12 08:30:59 +02:00
2020-06-22 23:25:00 +02:00
private ColoredText displayName;
2019-08-22 14:52:32 +02:00
private boolean unbreakable;
2020-06-22 23:25:00 +02:00
private ArrayList<ColoredText> lore;
2019-08-22 14:52:32 +02:00
2020-05-22 21:46:50 +02:00
private Map<Enchantment, Short> enchantmentMap;
private Map<Enchantment, Short> storedEnchantmentMap;
2020-05-28 23:43:12 +02:00
private List<ItemAttribute> attributes;
private Set<PotionType> potionTypes;
2020-05-22 21:46:50 +02:00
private int hideFlag;
2020-07-06 12:39:48 +02:00
private int customModelData;
2020-04-29 20:17:04 +02:00
2020-02-17 17:33:53 +01:00
private StackingRule stackingRule;
2019-08-29 02:15:52 +02:00
private Data data;
2020-05-29 20:24:39 +02:00
{
if (defaultStackingRule == null)
defaultStackingRule = DEFAULT_STACKING_RULE;
if (this.stackingRule == null)
this.stackingRule = defaultStackingRule;
}
2020-04-10 12:45:04 +02:00
public ItemStack(short materialId, byte amount, short damage) {
2020-03-29 20:58:30 +02:00
this.materialId = materialId;
2019-08-13 17:52:09 +02:00
this.amount = amount;
2019-09-06 16:05:36 +02:00
this.damage = damage;
2020-02-13 15:14:41 +01:00
this.lore = new ArrayList<>();
2020-02-17 17:33:53 +01:00
2020-04-29 20:17:04 +02:00
this.enchantmentMap = new HashMap<>();
this.storedEnchantmentMap = new HashMap<>();
2020-05-28 23:43:12 +02:00
this.attributes = new ArrayList<>();
2020-05-27 12:33:12 +02:00
this.potionTypes = new HashSet<>();
2019-08-13 17:52:09 +02:00
}
2020-04-10 12:45:04 +02:00
public ItemStack(short materialId, byte amount) {
this(materialId, amount, (short) 0);
2020-03-29 20:58:30 +02:00
}
public ItemStack(Material material, byte amount) {
this(material.getId(), amount);
2019-08-22 14:52:32 +02:00
}
2020-05-30 01:39:52 +02:00
/**
* Get the default stacking rule for newly created ItemStack
*
* @return the default stacking rule
*/
public static StackingRule getDefaultStackingRule() {
return defaultStackingRule;
}
/**
* Change the default stacking rule for created item stack
*
* @param defaultStackingRule the default item stack
* @throws NullPointerException if {@code defaultStackingRule} is null
*/
public static void setDefaultStackingRule(StackingRule defaultStackingRule) {
Check.notNull(defaultStackingRule, "StackingRule cannot be null!");
ItemStack.defaultStackingRule = defaultStackingRule;
}
/**
* Get if the item is air
*
* @return true if the material is air, false otherwise
*/
2019-08-13 17:52:09 +02:00
public boolean isAir() {
2020-03-29 20:58:30 +02:00
return materialId == Material.AIR.getId();
2019-08-13 17:52:09 +02:00
}
2020-02-13 15:14:41 +01:00
/**
2020-05-30 01:39:52 +02:00
* Get if two items are similar.
* It does not take {@link #getAmount()} and {@link #getStackingRule()} in consideration
2020-02-13 15:14:41 +01:00
*
2020-03-29 20:58:30 +02:00
* @param itemStack The ItemStack to compare to
* @return true if both items are similar
2020-02-13 15:14:41 +01:00
*/
2020-05-29 20:24:39 +02:00
public synchronized boolean isSimilar(ItemStack itemStack) {
synchronized (itemStack) {
2020-06-22 23:25:00 +02:00
final ColoredText itemDisplayName = itemStack.getDisplayName();
final boolean displayNameCheck = (displayName == null && itemDisplayName == null) ||
(displayName != null && itemDisplayName != null && displayName.equals(itemDisplayName));
final Data itemData = itemStack.getData();
final boolean dataCheck = (data == null && itemData == null) ||
(data != null && itemData != null && data.equals(itemData));
return itemStack.getMaterialId() == materialId &&
displayNameCheck &&
itemStack.isUnbreakable() == unbreakable &&
itemStack.getDamage() == damage &&
itemStack.enchantmentMap.equals(enchantmentMap) &&
itemStack.storedEnchantmentMap.equals(storedEnchantmentMap) &&
itemStack.attributes.equals(attributes) &&
itemStack.potionTypes.equals(potionTypes) &&
itemStack.hideFlag == hideFlag &&
dataCheck;
}
2019-08-13 17:52:09 +02:00
}
2019-09-06 16:05:36 +02:00
public short getDamage() {
return damage;
}
2020-05-30 01:39:52 +02:00
/**
* Get the item amount
* <p>
* WARNING: for amount computation it would be better to use {@link StackingRule#getAmount(ItemStack)}
* to support all stacking implementation
*
* @return the item amount
*/
public byte getAmount() {
return amount;
2020-04-22 02:42:58 +02:00
}
2020-05-30 01:39:52 +02:00
/**
* Change the item amount
* <p>
* WARNING: for amount computation it would be better to use {@link StackingRule#getAmount(ItemStack)}
* to support all stacking implementation
*
* @param amount the new item amount
*/
2019-08-13 17:52:09 +02:00
public void setAmount(byte amount) {
this.amount = amount;
}
2019-09-06 16:05:36 +02:00
public void setDamage(short damage) {
this.damage = damage;
}
2020-05-30 01:39:52 +02:00
/**
* Get the item internal material id
*
* @return the item material id
*/
public short getMaterialId() {
return materialId;
}
/**
* Get the item material
*
* @return the item material
*/
public Material getMaterial() {
return Material.fromId(getMaterialId());
}
/**
* Get the item display name
*
* @return the item display name, can be null if not present
*/
2020-06-22 23:25:00 +02:00
public ColoredText getDisplayName() {
2019-08-22 14:52:32 +02:00
return displayName;
}
2020-05-30 01:39:52 +02:00
/**
* Set the item display name
*
* @param displayName the item display name
*/
2020-06-22 23:25:00 +02:00
public void setDisplayName(ColoredText displayName) {
2019-08-22 14:52:32 +02:00
this.displayName = displayName;
}
2020-05-30 01:39:52 +02:00
/**
* Get if the item has a display name
*
* @return the item display name
*/
2020-02-13 15:14:41 +01:00
public boolean hasDisplayName() {
return displayName != null;
}
2020-05-30 01:39:52 +02:00
/**
* Get the item lore
*
* @return the item lore, can be null if not present
*/
2020-06-22 23:25:00 +02:00
public ArrayList<ColoredText> getLore() {
2020-02-13 15:14:41 +01:00
return lore;
}
2020-05-30 01:39:52 +02:00
/**
* Set the item lore
*
* @param lore the item lore, can be null to remove
*/
2020-06-22 23:25:00 +02:00
public void setLore(ArrayList<ColoredText> lore) {
2020-02-13 15:14:41 +01:00
this.lore = lore;
}
2020-05-30 01:39:52 +02:00
/**
* Get if the item has a lore
*
* @return true if the item has lore, false otherwise
*/
2020-02-13 15:14:41 +01:00
public boolean hasLore() {
return lore != null && !lore.isEmpty();
}
2020-05-30 01:39:52 +02:00
/**
* Get the item enchantment map
*
* @return an unmodifiable map containing the item enchantments
*/
2020-05-22 21:46:50 +02:00
public Map<Enchantment, Short> getEnchantmentMap() {
2020-04-29 20:17:04 +02:00
return Collections.unmodifiableMap(enchantmentMap);
}
2020-05-30 01:39:52 +02:00
/**
* Set an enchantment level
*
* @param enchantment the enchantment type
* @param level the enchantment level
*/
2020-05-22 21:46:50 +02:00
public void setEnchantment(Enchantment enchantment, short level) {
2020-04-29 20:17:04 +02:00
if (level < 1) {
removeEnchantment(enchantment);
return;
}
this.enchantmentMap.put(enchantment, level);
}
2020-05-30 01:39:52 +02:00
/**
* Remove an enchantment
*
* @param enchantment the enchantment type
*/
2020-04-29 20:17:04 +02:00
public void removeEnchantment(Enchantment enchantment) {
this.enchantmentMap.remove(enchantment);
}
2020-05-30 01:39:52 +02:00
/**
* Get an enchantment level
*
* @param enchantment the enchantment type
* @return the stored enchantment level, 0 if not present
*/
2020-04-29 20:17:04 +02:00
public int getEnchantmentLevel(Enchantment enchantment) {
2020-05-22 21:46:50 +02:00
return this.enchantmentMap.getOrDefault(enchantment, (short) 0);
}
/**
2020-05-30 01:39:52 +02:00
* Get the stored enchantment map
* Stored enchantments are used on enchanted book
*
2020-05-30 01:39:52 +02:00
* @return an unmodifiable map containing the item stored enchantments
*/
public Map<Enchantment, Short> getStoredEnchantmentMap() {
return Collections.unmodifiableMap(storedEnchantmentMap);
}
2020-05-30 01:39:52 +02:00
/**
* Set a stored enchantment level
*
* @param enchantment the enchantment type
* @param level the enchantment level
*/
public void setStoredEnchantment(Enchantment enchantment, short level) {
if (level < 1) {
removeStoredEnchantment(enchantment);
return;
}
this.storedEnchantmentMap.put(enchantment, level);
}
2020-05-30 01:39:52 +02:00
/**
* Remove a stored enchantment
*
* @param enchantment the enchantment type
*/
public void removeStoredEnchantment(Enchantment enchantment) {
this.storedEnchantmentMap.remove(enchantment);
}
2020-05-30 01:39:52 +02:00
/**
* Get a stored enchantment level
*
* @param enchantment the enchantment type
* @return the stored enchantment level, 0 if not present
*/
public int getStoredEnchantmentLevel(Enchantment enchantment) {
return this.storedEnchantmentMap.getOrDefault(enchantment, (short) 0);
}
2020-05-30 01:39:52 +02:00
/**
* Get the item attributes
*
* @return an unmodifiable {@link List} containing the item attributes
*/
2020-05-28 23:43:12 +02:00
public List<ItemAttribute> getAttributes() {
return Collections.unmodifiableList(attributes);
}
2020-05-30 01:39:52 +02:00
/**
* Add an attribute to the item
*
* @param itemAttribute the attribute to add
*/
2020-05-28 23:43:12 +02:00
public void addAttribute(ItemAttribute itemAttribute) {
this.attributes.add(itemAttribute);
}
2020-05-30 01:39:52 +02:00
/**
* Remove an attribute to the item
*
* @param itemAttribute the attribute to remove
*/
public void removeAttribute(ItemAttribute itemAttribute) {
this.attributes.remove(itemAttribute);
}
/**
* Get the item potion types
*
* @return an unmodifiable {@link Set} containing the item potion types
*/
2020-05-27 12:33:12 +02:00
public Set<PotionType> getPotionTypes() {
return Collections.unmodifiableSet(potionTypes);
}
2020-05-30 01:39:52 +02:00
/**
* Add a potion type to the item
*
* @param potionType the potion type to add
*/
2020-05-27 12:33:12 +02:00
public void addPotionType(PotionType potionType) {
this.potionTypes.add(potionType);
}
2020-05-30 01:39:52 +02:00
/**
* Remove a potion type to the item
*
* @param potionType the potion type to remove
*/
public void removePotionType(PotionType potionType) {
this.potionTypes.remove(potionType);
}
/**
* Get the item hide flag
*
* @return the item hide flag
*/
2020-05-22 21:46:50 +02:00
public int getHideFlag() {
return hideFlag;
}
2020-05-30 01:39:52 +02:00
/**
* Change the item hide flag. This is the integer sent when updating the item hide flag
*
* @param hideFlag the new item hide flag
*/
2020-05-29 00:30:19 +02:00
public void setHideFlag(int hideFlag) {
this.hideFlag = hideFlag;
}
2020-07-06 12:39:48 +02:00
/**
* Get the item custom model data
*
* @return the item custom model data
*/
public int getCustomModelData() {
return customModelData;
}
/**
* Change the item custom model data
*
* @param customModelData the new item custom data model
*/
public void setCustomModelData(int customModelData) {
this.customModelData = customModelData;
}
2020-05-30 01:39:52 +02:00
/**
* Add flags to the item
*
* @param flags the flags to add
*/
public void addItemFlags(ItemFlag... flags) {
for (ItemFlag f : flags) {
2020-05-22 21:46:50 +02:00
this.hideFlag |= getBitModifier(f);
}
}
2020-05-30 01:39:52 +02:00
/**
* Remove flags from the item
*
* @param flags the flags to remove
*/
public void removeItemFlags(ItemFlag... flags) {
for (ItemFlag f : flags) {
2020-05-22 21:46:50 +02:00
this.hideFlag &= ~getBitModifier(f);
}
}
2020-05-30 01:39:52 +02:00
/**
* Get the item flags
*
* @return an unmodifiable {@link Set} containing the item flags
*/
2020-05-22 21:46:50 +02:00
public Set<ItemFlag> getItemFlags() {
Set<ItemFlag> currentFlags = EnumSet.noneOf(ItemFlag.class);
for (ItemFlag f : ItemFlag.values()) {
if (hasItemFlag(f)) {
currentFlags.add(f);
}
}
2020-05-30 01:39:52 +02:00
return Collections.unmodifiableSet(currentFlags);
2020-05-22 21:46:50 +02:00
}
2020-05-30 01:39:52 +02:00
/**
* Get if the item has an item flag
*
* @param flag the item flag
* @return true if the item has the flag {@code flag}, false otherwise
*/
2020-05-22 21:46:50 +02:00
public boolean hasItemFlag(ItemFlag flag) {
int bitModifier = getBitModifier(flag);
return (this.hideFlag & bitModifier) == bitModifier;
2020-04-29 20:17:04 +02:00
}
2020-05-30 01:39:52 +02:00
/**
* Get if the item is unbreakable
*
* @return true if the item is unbreakable, false otherwise
*/
2019-08-22 14:52:32 +02:00
public boolean isUnbreakable() {
return unbreakable;
}
2020-05-30 01:39:52 +02:00
/**
* Make the item unbreakable
*
* @param unbreakable true to make the item unbreakable, false otherwise
*/
2019-08-22 14:52:32 +02:00
public void setUnbreakable(boolean unbreakable) {
this.unbreakable = unbreakable;
}
2020-05-30 01:39:52 +02:00
/**
* Get if the item has any nbt tag
*
* @return true if the item has nbt tag, false otherwise
*/
2020-02-13 15:14:41 +01:00
public boolean hasNbtTag() {
2020-05-28 23:43:12 +02:00
return hasDisplayName() || hasLore() || isUnbreakable() ||
!enchantmentMap.isEmpty() || !storedEnchantmentMap.isEmpty() ||
2020-07-06 12:44:02 +02:00
!attributes.isEmpty() || !potionTypes.isEmpty() ||
hideFlag != 0 || customModelData != 0;
2020-02-13 15:14:41 +01:00
}
2020-05-30 01:39:52 +02:00
/**
* Clone this item stack
*
* @return a cloned item stack
*/
2020-05-29 20:24:39 +02:00
public synchronized ItemStack clone() {
2020-03-29 20:58:30 +02:00
ItemStack itemStack = new ItemStack(materialId, amount, damage);
2019-08-22 14:52:32 +02:00
itemStack.setDisplayName(displayName);
itemStack.setUnbreakable(unbreakable);
2020-02-16 19:11:36 +01:00
itemStack.setLore(getLore());
itemStack.setStackingRule(getStackingRule());
2020-04-29 20:17:04 +02:00
itemStack.enchantmentMap = new HashMap<>(enchantmentMap);
2020-05-29 18:56:42 +02:00
itemStack.storedEnchantmentMap = new HashMap<>(storedEnchantmentMap);
itemStack.attributes = new ArrayList<>(attributes);
itemStack.potionTypes = new HashSet<>(potionTypes);
itemStack.hideFlag = hideFlag;
2020-04-29 20:17:04 +02:00
2020-02-09 15:34:09 +01:00
Data data = getData();
if (data != null)
itemStack.setData(data.clone());
2019-08-22 14:52:32 +02:00
return itemStack;
2019-08-12 08:30:59 +02:00
}
2019-08-29 02:15:52 +02:00
@Override
public Data getData() {
return data;
}
@Override
public void setData(Data data) {
this.data = data;
}
2020-02-17 17:33:53 +01:00
2020-05-30 01:39:52 +02:00
/**
* Get the item stacking rule
*
* @return the item stacking rule
*/
public StackingRule getStackingRule() {
return stackingRule;
2020-02-17 17:33:53 +01:00
}
2020-05-30 01:39:52 +02:00
/**
* Change the stacking rule of the item
*
* @param stackingRule the new item stacking rule
* @throws NullPointerException if {@code stackingRule} is null
*/
2020-05-23 04:20:01 +02:00
public void setStackingRule(StackingRule stackingRule) {
Check.notNull(stackingRule, "StackingRule cannot be null!");
this.stackingRule = stackingRule;
2020-02-17 17:33:53 +01:00
}
2020-05-22 21:46:50 +02:00
private byte getBitModifier(ItemFlag hideFlag) {
return (byte) (1 << hideFlag.ordinal());
}
2019-08-12 08:30:59 +02:00
}