Soulbound now handled using components

This commit is contained in:
Jules 2025-01-19 17:14:04 +01:00
parent dc805a4b86
commit 46652835ef
51 changed files with 480 additions and 886 deletions

View File

@ -223,7 +223,7 @@ public class ItemStats {
// Internal Stats
public static final ItemStat<ArrayComponent<StringComponent>> NAME_PREFIXES = new DisplayNamePrefixes();
public static final ItemStat<ArrayComponent<StringComponent>> NAME_SUFFIXES = new DisplayNameSuffixes();
public static final ItemStat<ObjectComponent> SOULBOUND = new Soulbound();
public static final Soulbound SOULBOUND = new Soulbound();
public static final IntegerStat CUSTOM_DURABILITY = new CustomDurability();
public static final IntegerStat ITEM_LEVEL = new ItemLevel();
public static final IntegerStat BROWSER_DISPLAY_IDX = new BrowserDisplayIDX();

View File

@ -7,9 +7,6 @@ import io.lumine.mythic.lib.api.util.ui.QuickNumberRange;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.stat.component.builtin.MaterialComponent;
import net.Indyuce.mmoitems.stat.component.model.builtin.StringModel;
import net.Indyuce.mmoitems.util.MMOUtils;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.crafting.ConfigMMOItem;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.MMOItemPlayerIngredient;
@ -17,7 +14,8 @@ import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.stat.DisplayName;
import net.Indyuce.mmoitems.stat.data.MaterialData;
import net.Indyuce.mmoitems.stat.component.builtin.MaterialComponent;
import net.Indyuce.mmoitems.stat.component.model.builtin.StringModel;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;

View File

@ -10,10 +10,9 @@ import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.Enchants;
import net.Indyuce.mmoitems.stat.GemUpgradeScaling;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.GemstoneComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.GemstonesComponent;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.history.StatHistory;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.util.MMOUtils;
@ -120,15 +119,12 @@ public class GemStone extends UseItem {
}
gemData.setLevel(levelIdentified);
for (ItemStat stat : gemMMOItem.getStats()) {
// Skip non transferrable stats
if (!stat.isGemstoneTransferable()) continue;
final StatData data = gemMMOItem.getData(stat);
if (data instanceof Mergeable)
targetMMO.mergeData(stat, data, gemData.getUniqueId());
}
// Skip non-transferable stats
for (ItemStat stat : gemMMOItem.getStats())
if (stat.isGemstoneTransferable()) {
final StatComponent data = gemMMOItem.getComponent(stat);
targetMMO.addComponent(stat, data, gemData.getUniqueId());
}
if (!silent) {
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 2);

View File

@ -12,9 +12,8 @@ import net.Indyuce.mmoitems.api.event.GenerateLoreEvent;
import net.Indyuce.mmoitems.api.event.ItemBuildEvent;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.stat.component.builtin.MaterialComponent;
import net.Indyuce.mmoitems.stat.data.MaterialData;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.stat.history.StatHistory;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@ -139,53 +138,25 @@ public class ItemStackBuilder {
* @return Returns built NBTItem with applied tags and lore
*/
public NBTItem buildNBT(boolean forDisplay) {
// Clone as to not conflict in any way
MMOItem builtMMOItem = mmoitem.clone();
//GEM//MMOItems.log("\u00a7e+ \u00a77Building \u00a7c" + mmoitem.getType().getName() + " " + mmoitem.getId() + "\u00a77 (Size \u00a7e" + mmoitem.getStatHistories().size() + "\u00a77 Historic)");
MMOItem builtMMOItem = mmoitem.clone(); // TODO useless cloning?
/*
* As an assumption for several enchantment recognition operations,
* Enchantment data must never be clear and lack history. This is
* the basis for when an item is 'old'
*/
builtMMOItem.computeComponent(ItemStats.ENCHANTS);
builtMMOItem.computeStatHistory(ItemStats.ENCHANTS);
//GEM// else {MMOItems.log("\u00a73 -?- \u00a77Apparently found enchantment data \u00a7b" + (mmoitem.getData(ItemStats.ENCHANTS) == null ? "null" : ((EnchantListData) mmoitem.getData(ItemStats.ENCHANTS)).getEnchants().size())); }
/*
* Similar to how enchantments are saved because they can be modified
* through non-MMOItems supported sources, the name can be changed in
* an anvil, so the very original name must be saved.
*/
builtMMOItem.computeComponent(ItemStats.NAME);
builtMMOItem.computeStatHistory(ItemStats.NAME);
// [BACKWARDS COMPATIBILITY] Vanilla-modifiable stats must always save a non-empty stat history
// An important assumption is that items with no stat history on these stats are outdated, MI
// has a special script to try and break as little things as possible.
for (ItemStat<?> vanillaEditable : MMOItems.plugin.getStats().getVanillaEditableStats())
builtMMOItem.computeStatHistory(vanillaEditable);
// For every stat within this item
for (ItemStat stat : builtMMOItem.getStats())
// Attempt to add
try {
//GEM//MMOItems.log("\u00a7e -+- \u00a77Applying \u00a76" + stat.getNBTPath());
// Does the item have any stat history regarding thay?
StatHistory s = builtMMOItem.getStatHistory(stat);
// Found it?
if (s != null) {
int l = mmoitem.getUpgradeLevel();
//GEM//MMOItems.log("\u00a7a -+- \u00a77History exists...");
//GEM//s.log();
// Recalculate
//GEM//MMOItems.log(" \u00a73-\u00a7a- \u00a77ItemStack Building Recalculation \u00a73-\u00a7a-\u00a73-\u00a7a-\u00a73-\u00a7a-\u00a73-\u00a7a-");
builtMMOItem.setComponent(stat, s.recalculate(l));
// Add to NBT, if the gemstones were not purged
if (!s.isEmpty()) {
//GEM//MMOItems.log("\u00a7a -+- \u00a77Recording History");
addItemTag(new ItemTag(history_keyword + stat.getId(), s.toNBTString()));
}
// Handle stat history of said stat
StatHistory statHistory = builtMMOItem.getStatHistory(stat);
if (statHistory != null) {
int upgradeLevel = mmoitem.getUpgradeLevel();
builtMMOItem.setComponent(stat, statHistory.recalculate(upgradeLevel)); // Recalculate
if (!statHistory.isEmpty()) // Apply to NBT
addItemTag(new ItemTag(history_keyword + stat.getId(), statHistory.toNBTString()));
}
// TODO preview
@ -210,8 +181,6 @@ public class ItemStackBuilder {
// Something went wrong...
} catch (IllegalArgumentException | NullPointerException exception) {
// That
MMOItems.print(Level.SEVERE, "An error occurred while trying to generate item '$f{0}$b' with stat '$f{1}$b': {2}",
"ItemStackBuilder", builtMMOItem.getId(), stat.getId(), exception.getMessage());
}

View File

@ -12,8 +12,8 @@ import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import net.Indyuce.mmoitems.stat.component.model.builtin.StringModel;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.history.HistoryPolicy;
import net.Indyuce.mmoitems.stat.history.StatHistory;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.util.Buildable;
import org.jetbrains.annotations.NotNull;
@ -63,7 +63,7 @@ public class MMOItemBuilder extends Buildable<MMOItem> {
mmoitem = new MMOItem(template.getType(), template.getId());
// Apply base item data
template.getModels().forEach((stat, model) -> applyComponent((ItemStat) stat, stat.getComponentType().randomize(model, this)));
template.getModels().forEach((stat, model) -> mmoitem.setComponent((ItemStat) stat, stat.getComponentType().randomize(model, this)));
// Apply tier
if (tier != null) mmoitem.setComponent(ItemStats.TIER, new StringComponent(tier.getId()));
@ -115,7 +115,7 @@ public class MMOItemBuilder extends Buildable<MMOItem> {
@Override
protected MMOItem whenBuilt() {
/*
TODO name
TODO Name
if (!nameModifiers.isEmpty()) {
// Get name data
@ -143,29 +143,25 @@ public class MMOItemBuilder extends Buildable<MMOItem> {
}
/**
* Applies statData to the builder, either merges it if statData is
* mergeable like lore, abilities.. or entirely replaces current data
* Registers data from a modifier onto the item
*
* @param stat Stat owning the data
* @param component Stat component to apply
* @param component Stat data to apply
* @see MMOItem#addComponent(ItemStat, StatComponent, UUID)
*/
public <C extends StatComponent> void applyComponent(@NotNull ItemStat<C> stat, @NotNull C component) {
final StatData found = mmoitem.getData(stat);
if (found != null && found instanceof Mergeable) ((Mergeable) found).mergeWith((Mergeable) component);
else mmoitem.setComponent(stat, component);
}
public <C extends StatComponent> void addModifierComponent(@NotNull ItemStat<C> stat, @NotNull C component, @NotNull UUID modifierId) {
/**
* Registers the modifier onto the item
*
* @param stat Stat owning the data
* @param data StatData to apply
*/
public <C extends StatComponent> void addModifierData(@NotNull ItemStat<C> stat, @NotNull C data, @NotNull UUID uuid) {
// TODO Merging
//if (stat.getClearStatData() instanceof Mergeable)
mmoitem.computeStatHistory(stat).registerModifierBonus(uuid, data);
//else mmoitem.setData(stat, data);
// No stat history policy. Simply set/replace component
if (stat.getHistoryPolicy() == HistoryPolicy.NONE) {
mmoitem.setComponent(stat, component);
}
// Add to stat history
else {
StatHistory<C> sHistory = mmoitem.computeStatHistory(stat);
sHistory.registerModifierBonus(modifierId, component);
mmoitem.setComponent(stat, sHistory.recalculate(mmoitem.getUpgradeLevel()));
}
}
/**

View File

@ -0,0 +1,17 @@
package net.Indyuce.mmoitems.api.item.mmoitem;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
// TODO
@Deprecated
public class LiveItemEditor extends MMOItem {
public LiveItemEditor(NBTItem nbtItem) {
super(Type.get(item.getType()), item.getString("MMOITEMS_ITEM_ID"));
}
}

View File

@ -10,13 +10,13 @@ import net.Indyuce.mmoitems.api.item.ItemReference;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.util.MMOItemReforger;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.GemstoneComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.GemstonesComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.UpgradeComponent;
import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.history.HistoryPolicy;
import net.Indyuce.mmoitems.stat.history.StatHistory;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.tooltip.TooltipTexture;
@ -35,12 +35,20 @@ public class MMOItem implements ItemReference {
/**
* Where data about all the item stats is stored. When the item is
* generated, this map is read and all the stats are applied. The order in
* which stats are added is not very important anymore
* generated, this map is read and all the stats are applied. The
* order in which stats get applied is not relevant.
*/
@NotNull
private final Map<ItemStat<?>, StatComponent> components = new HashMap<>();
/**
* When merging stats (like applying a gemstone), the item must remember which were
* its original stats, and from which gemstone came each stat, in order to allow
* removal of gemstones in the future. This is where that is remembered.
*/
@NotNull
private final Map<ItemStat<?>, StatHistory<?>> statHistories = new HashMap<>();
/**
* Constructor used to generate an ItemStack based on some stat data
*
@ -92,39 +100,32 @@ public class MMOItem implements ItemReference {
return (C) components.computeIfAbsent(stat, ItemStat::generateEmptyComponent);
}
public <C extends StatComponent> void mergeComponentInto(@NotNull ItemStat<C> stat, @NotNull C component, @Nullable UUID associatedGemStone) {
/**
* Register data from either a gemstone, or external data, onto the item
*
* @param stat Stat owning the data
* @param component Stat data to apply
* @param associatedGemStone The gemstone historic UUID, otherwise null for external data
* @see net.Indyuce.mmoitems.api.item.build.MMOItemBuilder#addModifierComponent(ItemStat, StatComponent, UUID)
*/
public <C extends StatComponent> void addComponent(@NotNull ItemStat<C> stat, @NotNull C component, @Nullable UUID associatedGemStone) {
// No stat history policy. Simply set/replace component
if (stat.getHistoryPolicy() == HistoryPolicy.NONE) {
setComponent(stat, component);
}
// Add to stat history
else {
StatHistory<C> sHistory = computeStatHistory(stat);
if (associatedGemStone != null) sHistory.registerGemstoneData(associatedGemStone, component);
else sHistory.registerExternalData(component);
setComponent(stat, sHistory.recalculate(getUpgradeLevel()));
}
}
//region Deprecated
/**
* Will merge that data into this item:
* <p></p>
* If the item does not have this stat yet, it will be set with <code>MMOItem.setData()</code>
* <p>If this data is not <code>Mergeable</code>, it will also be set</p>
* <p></p>
* Otherwise, the data will be merged with the previous. If an UUID is provided, it will also be
* stored as a GemStone in the history, allowing to be removed from the item with that same UUID.
*/
@Deprecated
public void mergeData(@NotNull ItemStat stat, @NotNull StatData data, @Nullable UUID associatedGemStone) {
/*
TODO
// Merge if possible, otherwise write over
if (data instanceof Mergeable) {
// Prepare to merge, gather history
StatHistory sHistory = computeStatHistory(stat);
if (associatedGemStone != null) sHistory.registerGemstoneData(associatedGemStone, data);
else sHistory.registerExternalData(data);
setData(stat, sHistory.recalculate(getUpgradeLevel()));
} else setData(stat, data);
*/
}
@Deprecated
public void setData(@NotNull ItemStat stat, @NotNull StatData data) {
//TODO remove usage
@ -153,7 +154,6 @@ public class MMOItem implements ItemReference {
//endregion
//region Building
/**
@ -164,7 +164,7 @@ public class MMOItem implements ItemReference {
return new ItemStackBuilder(this);
}
/***
/**
* @return A cloned instance of this mmoitem. This does NOT clone the
* StatData instances! If you edit these statDatas, the previous
* mmoitem will be edited as well.
@ -178,32 +178,27 @@ public class MMOItem implements ItemReference {
clone.components.putAll(this.components);
// Clone stat histories
mergeableStatHistory.forEach((stat, hist) -> clone.mergeableStatHistory.put(stat, hist.clone().setParent(clone)));
statHistories.forEach((stat, hist) -> clone.statHistories.put(stat, hist.clone().setParent(clone)));
return clone;
}
//endregion
//region Stat History Stuff
/**
* When merging stats (like applying a gemstone), the item must remember which were
* its original stats, and from which gem stone came each stat, in order to allow
* removal of gem stones in the future. This is where that is remembered.
*/
@NotNull
private final Map<ItemStat<?>, StatHistory<?>> mergeableStatHistory = new HashMap<>();
//region Stat History
/**
* @see #getStatHistory(ItemStat)
* @deprecated
*/
@Deprecated
public boolean hasStatHistory(@NotNull ItemStat<?> stat) {
return stat instanceof Mergeable && mergeableStatHistory.containsKey(stat);
return stat.getHistoryPolicy() != HistoryPolicy.NONE && statHistories.containsKey(stat);
}
@Nullable
public <C extends StatComponent> StatHistory<C> getStatHistory(@NotNull ItemStat<C> stat) {
// TODO refactor
if (stat.equals(ItemStats.ENCHANTS)) return computeStatHistory(stat);
return (StatHistory<C>) mergeableStatHistory.get(stat);
return stat.getHistoryPolicy() == HistoryPolicy.NONE ? null : (StatHistory<C>) statHistories.get(stat);
}
/**
@ -214,7 +209,7 @@ public class MMOItem implements ItemReference {
*/
@NotNull
public <C extends StatComponent> StatHistory<C> computeStatHistory(@NotNull ItemStat<C> stat) {
return (StatHistory<C>) mergeableStatHistory.computeIfAbsent(stat, ignore -> {
return (StatHistory<C>) statHistories.computeIfAbsent(stat, ignore -> {
final C currentData = computeComponent(stat);
//Validate.isTrue(currentData instanceof Mergeable, "Stat " + itemStat.getId() + " is not mergeable");
return new StatHistory<>(this, stat, currentData);
@ -223,7 +218,7 @@ public class MMOItem implements ItemReference {
@NotNull
public ArrayList<StatHistory> getStatHistories() {
return new ArrayList<>(mergeableStatHistory.values());
return new ArrayList<>(statHistories.values());
}
/**
@ -233,10 +228,12 @@ public class MMOItem implements ItemReference {
* from when it was created, its gemstone stats, those
* added by which gem, and its upgrade bonuses.
*/
public void setStatHistory(@NotNull ItemStat stat, @NotNull StatHistory hist) {
mergeableStatHistory.put(stat, hist);
public <C extends StatComponent> void setStatHistory(@NotNull ItemStat<C> stat, @NotNull StatHistory<C> hist) {
statHistories.put(stat, hist);
}
//endregion
//region Other API
/**
@ -308,20 +305,20 @@ public class MMOItem implements ItemReference {
NBTItem nbtItem = newBuilder().buildNBT();
// Durability
setData(ItemStats.CUSTOM_DURABILITY, new DoubleData(maxDurability - damage));
setComponent(ItemStats.CUSTOM_DURABILITY, new IntegerComponent(maxDurability - damage));
// Scale damage
Material mat = hasComponent(ItemStats.MATERIAL) ? Objects.requireNonNull(getComponent(ItemStats.MATERIAL).getMaterial(), "Internal error") : Material.GOLD_INGOT;
double multiplier = ((double) damage) * ((double) mat.getMaxDurability()) / ((double) maxDurability);
int multiplier = (int) Math.floor(((double) damage) * ((double) mat.getMaxDurability()) / ((double) maxDurability));
if (multiplier == 0) return;
// Set to some decent amount of damage
setData(ItemStats.ITEM_DAMAGE, new DoubleData(multiplier));
setComponent(ItemStats.ITEM_DAMAGE, new IntegerComponent(multiplier));
} else {
// Use vanilla durability
setData(ItemStats.ITEM_DAMAGE, new DoubleData(damage));
setComponent(ItemStats.ITEM_DAMAGE, new IntegerComponent(damage));
}
}
//endregion
@ -428,7 +425,7 @@ public class MMOItem implements ItemReference {
if (MMOItemReforger.gemstonesRevIDWhenUnsocket) return pairs;
// Identify actual stats
for (StatHistory hist : mergeableStatHistory.values())
for (StatHistory hist : statHistories.values())
for (Pair<GemstoneComponent, MMOItem> gem : pairs) {
final StatComponent historicGemData = hist.getGemstoneData(gem.getKey().getUniqueId());
if (historicGemData != null) gem.getValue().setComponent(hist.getItemStat(), historicGemData);
@ -456,7 +453,7 @@ public class MMOItem implements ItemReference {
if (MMOItemReforger.gemstonesRevIDWhenUnsocket) return restored;
// Identify actual stats
for (StatHistory hist : mergeableStatHistory.values()) {
for (StatHistory hist : statHistories.values()) {
final StatComponent historicGemData = hist.getGemstoneData(gem.getUniqueId());
if (historicGemData != null) restored.setComponent(hist.getItemStat(), historicGemData);
}

View File

@ -30,7 +30,7 @@ public abstract class ReadMMOItem extends MMOItem {
public int getDamage() {
// Does it use custom durability?
if (hasData(ItemStats.MAX_DURABILITY))
if (hasComponent(ItemStats.MAX_DURABILITY))
return getNBT().hasTag("MMOITEMS_DURABILITY") ? getNBT().getInteger("MMOITEMS_MAX_DURABILITY") - getNBT().getInteger("MMOITEMS_DURABILITY") : 0;
// Its using vanilla durability-yo

View File

@ -207,7 +207,7 @@ public class ModifierNode implements PreloadedObject {
// VANILLA MODIFIER SECTION
models.forEach((stat, model) -> builder.addModifierData((ItemStat) stat, stat.getComponentType().randomize(model, builder), modifierId));
models.forEach((stat, model) -> builder.addModifierComponent((ItemStat) stat, stat.getComponentType().randomize(model, builder), modifierId));
// MODIFIER GROUP SECTION

View File

@ -13,7 +13,7 @@ import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.util.RandomAmount;
import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.command.MMOItemsCommandTreeRoot;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
@ -72,7 +72,7 @@ public class GiveCommandTreeNode extends CommandTreeNode {
// roll soulbound
if (random.nextDouble() < soulbound)
mmoitem.setData(ItemStats.SOULBOUND, new SoulboundData(target, 1));
mmoitem.setComponent(ItemStats.SOULBOUND, new SoulboundComponent(target, 1));
// generate item
ItemStack item = mmoitem.newBuilder().build();

View File

@ -2,6 +2,7 @@ package net.Indyuce.mmoitems.comp.mmocore.load;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
@ -11,7 +12,6 @@ import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import io.lumine.mythic.lib.api.MMOLineConfig;
public class ItemTemplateDropItem extends ItemGenerationDropItem {
@ -37,7 +37,7 @@ public class ItemTemplateDropItem extends ItemGenerationDropItem {
MMOItem mmoitem = rollMMOItem(template, rpgPlayer);
if (rollSoulbound())
mmoitem.setData(ItemStats.SOULBOUND, new SoulboundData(rpgPlayer.getPlayer(), 1));
mmoitem.setComponent(ItemStats.SOULBOUND, new SoulboundComponent(rpgPlayer.getPlayer(), 1));
ItemStack stack = rollUnidentification(mmoitem);
stack.setAmount(rollAmount());

View File

@ -15,7 +15,7 @@ import net.Indyuce.mmoitems.api.item.template.explorer.TierFilter;
import net.Indyuce.mmoitems.api.item.template.explorer.TypeFilter;
import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
@ -73,7 +73,7 @@ public class RandomItemDropItem extends ItemGenerationDropItem {
MMOItem rolled = rollMMOItem(optional.get(), rpgPlayer);
if (rollSoulbound())
rolled.setData(ItemStats.SOULBOUND, new SoulboundData(rpgPlayer.getPlayer(), 1));
rolled.setComponent(ItemStats.SOULBOUND, new SoulboundComponent(rpgPlayer.getPlayer(), 1));
ItemStack stack = rollUnidentification(rolled);
stack.setAmount(rollAmount());

View File

@ -14,15 +14,13 @@ import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.gui.edition.recipe.interpreter.RMGRI_Shaped;
import net.Indyuce.mmoitems.gui.edition.recipe.button.RBA_AmountOutput;
import net.Indyuce.mmoitems.gui.edition.recipe.button.RBA_HideFromBook;
import net.Indyuce.mmoitems.gui.edition.recipe.gui.RMG_Shaped;
import net.Indyuce.mmoitems.gui.edition.recipe.gui.RecipeEditorGUI;
import net.Indyuce.mmoitems.gui.edition.recipe.interpreter.RMGRI_Shaped;
import net.Indyuce.mmoitems.stat.component.model.Model;
import net.Indyuce.mmoitems.stat.component.model.builtin.StringModel;
import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;

View File

@ -21,8 +21,6 @@ import net.Indyuce.mmoitems.gui.edition.recipe.gui.RMG_Shapeless;
import net.Indyuce.mmoitems.gui.edition.recipe.gui.RecipeEditorGUI;
import net.Indyuce.mmoitems.stat.component.model.Model;
import net.Indyuce.mmoitems.stat.component.model.builtin.StringModel;
import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;

View File

@ -14,14 +14,12 @@ import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.gui.edition.recipe.interpreter.RMGRI_SuperShaped;
import net.Indyuce.mmoitems.gui.edition.recipe.button.RBA_AmountOutput;
import net.Indyuce.mmoitems.gui.edition.recipe.gui.RMG_SuperShaped;
import net.Indyuce.mmoitems.gui.edition.recipe.gui.RecipeEditorGUI;
import net.Indyuce.mmoitems.gui.edition.recipe.interpreter.RMGRI_SuperShaped;
import net.Indyuce.mmoitems.stat.component.model.Model;
import net.Indyuce.mmoitems.stat.component.model.builtin.StringModel;
import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;

View File

@ -17,6 +17,7 @@ import net.Indyuce.mmoitems.stat.behaviour.ItemRestriction;
import net.Indyuce.mmoitems.stat.behaviour.PlayerConsumable;
import net.Indyuce.mmoitems.stat.category.StatCategory;
import net.Indyuce.mmoitems.stat.component.type.builtin.NumberComponentType;
import net.Indyuce.mmoitems.stat.history.HistoryPolicy;
import net.Indyuce.mmoitems.stat.type.*;
import net.Indyuce.mmoitems.stat.yaml.SyntaxAdapter;
import net.Indyuce.mmoitems.util.ElementStatType;
@ -50,10 +51,11 @@ public class StatManager {
private final Map<String, SyntaxAdapter> yamlSyntaxAdapters = new HashMap<>();
/*
* These lists are sets of stats collected when the stats are registered for
* the first time to make their access easier. Check the classes
* individually to understand better
* These lists are sets of stats collected when the stats are registered
* for the first time to make their access easier. Check the classes
* individually to understand better.
*/
private final List<ItemStat<?>> vanillaEditableStats = new ArrayList<>();
private final List<DoubleStat> numericStats = new ArrayList<>();
private final List<ItemRestriction> itemRestrictions = new ArrayList<>();
private final List<ConsumableItemInteraction> consumableActions = new ArrayList<>();
@ -93,7 +95,6 @@ public class StatManager {
action.accept((T) fieldValue);
} catch (Exception exception) {
MMOItems.plugin.getLogger().log(Level.SEVERE, String.format(errorMessage, field.getName(), exception.getMessage()));
exception.printStackTrace();
}
}
@ -159,6 +160,11 @@ public class StatManager {
return result;
}
@NotNull
public List<ItemStat<?>> getVanillaEditableStats() {
return vanillaEditableStats;
}
@NotNull
public StatCategory getCategory(@NotNull String id) {
return Objects.requireNonNull(categories.get(id), "No stat category found with ID '" + id + "'");
@ -218,11 +224,7 @@ public class StatManager {
ItemStat<?> stat = stats.get(id);
if (stat != null) return stat;
// Numeric registry TODO remove this useless registry once elements are properly implemented
stat = numericStats.stream().filter(doubleStat -> doubleStat.getId().equals(id)).findFirst().orElse(null);
if (stat != null) return stat;
// Legacy liases
// Legacy aliases
stat = legacyAliases.get(id);
if (stat != null) return stat;
@ -265,6 +267,7 @@ public class StatManager {
if (stat instanceof ItemRestriction) itemRestrictions.add((ItemRestriction) stat);
if (stat instanceof ConsumableItemInteraction) consumableActions.add((ConsumableItemInteraction) stat);
if (stat instanceof PlayerConsumable) playerConsumables.add((PlayerConsumable) stat);
if (stat.getHistoryPolicy() == HistoryPolicy.VANILLA_EDITABLE) vanillaEditableStats.add(stat);
// Stat category
HasCategory statCatAnnot = stat.getClass().getAnnotation(HasCategory.class);

View File

@ -42,6 +42,12 @@ public class Crafting extends ItemStat<VoidComponent> implements TemplateOption
throw new RuntimeException("Not supported");
}
@Nullable
@Override
public VoidComponent read(ReadMMOItem mmoitem) {
throw new RuntimeException("Not supported");
}
@Deprecated
public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) {
if (event.getAction() == InventoryAction.PICKUP_ALL)
@ -56,12 +62,6 @@ public class Crafting extends ItemStat<VoidComponent> implements TemplateOption
}
}
@Nullable
@Override
public VoidComponent read(ReadMMOItem mmoitem) {
throw new RuntimeException("Not supported");
}
@Deprecated
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {

View File

@ -29,7 +29,7 @@ public class DisplayName extends StringStat {
.trimdesc("The item display name.")
.build());
setHistoryPolicy(HistoryPolicy.SAVE_BASE);
setHistoryPolicy(HistoryPolicy.VANILLA_EDITABLE);
setGemstoneTransferable(false);
}
@ -37,10 +37,6 @@ public class DisplayName extends StringStat {
@Override
public void write(ItemStackBuilder builder, @NotNull StringComponent component) {
// Make sure stat history exists
// TODO remove?
builder.getMMOItem().computeStatHistory(this);
// Get prefixes and suffixes
ArrayComponent<StringComponent> prefixes = builder.getMMOItem().getComponent(ItemStats.NAME_PREFIXES);
ArrayComponent<StringComponent> suffixes = builder.getMMOItem().getComponent(ItemStats.NAME_SUFFIXES);

View File

@ -1,15 +1,10 @@
package net.Indyuce.mmoitems.stat;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
import io.lumine.mythic.lib.api.util.ui.PlusMinusPercent;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
import net.Indyuce.mmoitems.comp.enchants.EnchantPlugin;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
@ -17,13 +12,9 @@ import net.Indyuce.mmoitems.stat.component.builtin.NumberComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.EnchantMapComponent;
import net.Indyuce.mmoitems.stat.component.type.builtin.MapComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.NumberComponentType;
import net.Indyuce.mmoitems.stat.data.EnchantListData;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo;
import net.Indyuce.mmoitems.stat.history.HistoryPolicy;
import net.Indyuce.mmoitems.stat.history.StatHistory;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.stat.type.extra.Upgradable;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
@ -34,10 +25,11 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
@HasCategory(cat = "item")
public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradable {
public class Enchants extends ItemStat<EnchantMapComponent> {
public Enchants() {
super("ENCHANTS", null);
@ -67,46 +59,30 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
setItemMetaSaved(true);
setHistoryPolicy(HistoryPolicy.SAVE_BASE);
setHistoryPolicy(HistoryPolicy.VANILLA_EDITABLE);
}
@Nullable
@Override
public EnchantMapComponent read(ReadMMOItem mmoitem) {
// Create enchant data from this items' enchantments
EnchantMapComponent enchants = new EnchantMapComponent();
// Get the Item Meta
ItemStack item = mmoitem.getNBT().getItem();
if (item.hasItemMeta()) {
// Finally, item meta
ItemMeta itemMeta = mmoitem.getNBT().getItem().getItemMeta();
// Finally, item meta
ItemMeta itemMeta = item.getItemMeta();
if (itemMeta != null) {
// For each enchantment, in the usual way
for (Map.Entry<Enchantment, Integer> entry : itemMeta.getEnchants().entrySet())
enchants.asMap().put(entry.getKey().toString(), new IntegerComponent(entry.getValue()));
// For each enchantment, in the usual way
for (Map.Entry<Enchantment, Integer> entry : itemMeta.getEnchants().entrySet()) {
enchants.asMap().put(entry.getKey().toString(), new IntegerComponent(entry.getValue()));
}
// For each enchantment 'stored' in the item
if (itemMeta instanceof EnchantmentStorageMeta) {
// For each enchantment
for (Map.Entry<Enchantment, Integer> entry : ((EnchantmentStorageMeta) itemMeta).getStoredEnchants().entrySet()) {
// Add Level
enchants.asMap().put(entry.getKey().toString(), new IntegerComponent(entry.getValue()));
}
}
}
}
// For each enchantment 'stored' in the item
if (itemMeta instanceof EnchantmentStorageMeta)
for (Map.Entry<Enchantment, Integer> entry : ((EnchantmentStorageMeta) itemMeta).getStoredEnchants().entrySet())
enchants.asMap().put(entry.getKey().toString(), new IntegerComponent(entry.getValue()));
return enchants.asMap().isEmpty() ? null : enchants;
/*
TODO this code is fucking garbage
TODO this code is fucking garbage. rewrite this in the LiveItemEditor when reading the full item NBT
// Regardless, the stat history must be kept.
ItemTag hisTag = ItemTag.getTagAtPath(ItemStackBuilder.history_keyword + getId(), mmoitem.getNBT(), SupportedNBTTagValues.STRING);
if (hisTag == null) {
@ -128,8 +104,7 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
* stat values are actually the original ones, they may be old
* enchantments applied by players before StatHistory existed.
*
*
mmoitem.computeStatHistory(ItemStats.ENCHANTS); // TODO remove?
mmoitem.computeStatHistory(ItemStats.ENCHANTS);
}
}
*/
@ -185,6 +160,8 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
writeJsonToNbt(builder, component);
}
/*
TODO upgrading
@NotNull
@Override
public UpgradeInfo loadUpgradeInfo(@Nullable Object obj) throws IllegalArgumentException {
@ -265,6 +242,7 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
// Upgraded
return original;
}
*/
/**
* Players may enchant an item via 'vanilla' means (Not Gemstones, not Upgrades).
@ -390,6 +368,7 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
return Enchantment.getByName(key);
}
/*
public static class EnchantUpgradeInfo implements UpgradeInfo {
@NotNull
HashMap<Enchantment, PlusMinusPercent> perEnchantmentOperations = new HashMap<>();
@ -407,7 +386,7 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
* <p><code>Enchantment PMP</code>
* </p><code>...</code>
* @throws IllegalArgumentException If any part of the operation goes wrong (including reading any PMP).
*/
*
@NotNull
public static EnchantUpgradeInfo GetFrom(@Nullable Object obj) throws IllegalArgumentException {
@ -522,11 +501,6 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
public EnchantUpgradeInfo() {
}
/**
* The operation every level will perform.
*
* @see PlusMinusPercent
*/
@Nullable
public PlusMinusPercent getPMP(@NotNull Enchantment ench) {
return perEnchantmentOperations.get(ench);
@ -534,17 +508,18 @@ public class Enchants extends ItemStat<EnchantMapComponent> implements Upgradabl
/**
* Includes an enchantment to be upgraded by this template.
*/
*
public void addEnchantmentOperation(@NotNull Enchantment e, @NotNull PlusMinusPercent op) {
perEnchantmentOperations.put(e, op);
}
/**
* Which enchantments have operations defined here?
*/
*
@NotNull
public Set<Enchantment> getAffectedEnchantments() {
return perEnchantmentOperations.keySet();
}
}
*/
}

View File

@ -6,6 +6,8 @@ import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.particle.api.ParticleType;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.component.builtin.ObjectComponent;
import net.Indyuce.mmoitems.stat.component.type.builtin.MapComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.NumberComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.ObjectComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.StringComponentType;
import net.Indyuce.mmoitems.stat.type.ItemStat;
@ -35,7 +37,10 @@ public class ItemParticles extends ItemStat<ObjectComponent> {
.name("Color")
.trimdesc("When using a colorable particle like REDSTONE, you also need to specify a particle color.")
.build())
.addField("Modifiers", ObjectComponentType.init().build())
.addField("Modifiers", MapComponentType.mapOf(NumberComponentType.decimal().build())
.icon(Material.PINK_STAINED_GLASS)
.name("Particle Type Modifiers")
.build())
.icon(Material.PINK_STAINED_GLASS)
.name("Item Particle Effect")
@ -44,9 +49,7 @@ public class ItemParticles extends ItemStat<ObjectComponent> {
setDisplayInLore(false);
// TODO keys: type, color{red,green,blue}, particle, <any other value wrt the type>
// TODO syntax adapting for the modifiers
// TODO add modifiers once MapComponent exists
// TODO syntax adapting for config (modifiers!!)
}
private static final Lazy<List<HintedString>> PARTICLE_TYPE_NAMES = Lazy.of(() -> Arrays.stream(ParticleType.values()).map(tt -> new HintedString(tt.name())).collect(Collectors.toList()));

View File

@ -3,7 +3,6 @@ package net.Indyuce.mmoitems.stat;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.api.util.message.Message;
@ -11,9 +10,7 @@ import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.behaviour.ItemRestriction;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.MaterialData;
import net.Indyuce.mmoitems.stat.type.IntegerStat;
import net.Indyuce.mmoitems.stat.type.extra.Upgradable;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
@ -26,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
* @author indyuce
*/
@HasCategory(cat = "durability")
public class MaximumDurability extends IntegerStat implements ItemRestriction, Upgradable {
public class MaximumDurability extends IntegerStat implements ItemRestriction {
public MaximumDurability() {
super("MAX_DURABILITY", Material.SHEARS, "Maximum Custom Durability",
"The amount of uses before your item becomes unusable/breaks.",
@ -58,17 +55,19 @@ public class MaximumDurability extends IntegerStat implements ItemRestriction, U
previewPlaceholderTodo(item);
}
/*
TODO Upgrading
@Override
public void preprocess(@NotNull MMOItem item) {
// If this has no Max Upgrade Data
if (!item.hasData(ItemStats.MAX_DURABILITY)) {
if (!item.hasComponent(ItemStats.MAX_DURABILITY)) {
// What durability will it have?
int base = 400;
// I mean bruh
if (item.hasData(ItemStats.MATERIAL)) {
if (item.hasComponent(ItemStats.MATERIAL)) {
// Use vanilla max durability
MaterialData data = (MaterialData) item.getData(ItemStats.MATERIAL);
@ -84,9 +83,10 @@ public class MaximumDurability extends IntegerStat implements ItemRestriction, U
// Set max dura
item.setData(ItemStats.MAX_DURABILITY, new DoubleData(base));
/*item.setData(ItemStats.CUSTOM_DURABILITY, new DoubleData(base));*/
//item.setData(ItemStats.CUSTOM_DURABILITY, new DoubleData(base));
}
}
*/
@Override
public boolean canUse(RPGPlayer player, NBTItem item, boolean message) {

View File

@ -18,7 +18,6 @@ public class PotionColor extends ItemStat<ColorComponent> {
super("POTION_COLOR", null);
setComponentType(ObjectComponentType.color()
.implementation(ColorComponent::new)
.name("Potion Color")
.icon(Material.POTION)
.trimdesc("The potion of your color. It is purely a cosmetic option and has no effect whatsoever on the potion effects.")

View File

@ -12,7 +12,7 @@ import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.behaviour.ConsumableItemInteraction;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.Bukkit;
@ -51,8 +51,8 @@ public class SoulbindingBreakChance extends DoubleStat implements ConsumableItem
return false;
}
// check for soulbound level
SoulboundData soulbound = (SoulboundData) targetMMO.getData(ItemStats.SOULBOUND);
// Check for soulbound level
SoulboundComponent soulbound = targetMMO.getComponent(ItemStats.SOULBOUND);
if (Math.max(1, consumable.getNBTItem().getStat(ItemStats.SOULBOUND_LEVEL.getId())) < soulbound.getLevel()) {
Message.LOW_SOULBOUND_LEVEL.format(ChatColor.RED, "#level#", MMOUtils.intToRoman(soulbound.getLevel())).send(player);
return false;

View File

@ -12,7 +12,7 @@ import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.behaviour.ConsumableItemInteraction;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.Bukkit;
@ -52,12 +52,12 @@ public class SoulbindingChance extends DoubleStat implements ConsumableItemInter
return false;
}
MMOItem targetMMO = new VolatileMMOItem(target);
if (targetMMO.hasData(ItemStats.SOULBOUND)) {
SoulboundData data = (SoulboundData) targetMMO.getData(ItemStats.SOULBOUND);
Message.CANT_BIND_ITEM.format(ChatColor.RED, "#player#", data.getName(), "#level#", MMOUtils.intToRoman(data.getLevel())).send(player);
return false;
}
MMOItem targetMMO = new VolatileMMOItem(target);
SoulboundComponent soulboundComponent = targetMMO.getComponent(ItemStats.SOULBOUND);
if (soulboundComponent != null) {
Message.CANT_BIND_ITEM.format(ChatColor.RED, "#player#", soulboundComponent.getName(), "#level#", MMOUtils.intToRoman(soulboundComponent.getLevel())).send(player);
return false;
}
if (random.nextDouble() < soulbindingChance / 100) {
ApplySoulboundEvent called = new ApplySoulboundEvent(playerData, consumable.getMMOItem(), target);
@ -66,8 +66,7 @@ public class SoulbindingChance extends DoubleStat implements ConsumableItemInter
return false;
int soulboundLevel = (int) Math.max(1, consumable.getNBTItem().getStat("SOULBOUND_LEVEL"));
(targetMMO = new LiveMMOItem(target)).setData(ItemStats.SOULBOUND,
new SoulboundData(player.getUniqueId(), player.getName(), soulboundLevel));
(targetMMO = new LiveMMOItem(target)).setComponent(ItemStats.SOULBOUND, new SoulboundComponent(player, soulboundLevel));
target.getItem().setItemMeta(targetMMO.newBuilder().build().getItemMeta());
Message.SUCCESSFULLY_BIND_ITEM
.format(ChatColor.YELLOW, "#item#", MMOUtils.getDisplayName(target.getItem()), "#level#", MMOUtils.intToRoman(soulboundLevel))

View File

@ -10,6 +10,7 @@ import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.behaviour.ItemRestriction;
import net.Indyuce.mmoitems.stat.component.builtin.ObjectComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import net.Indyuce.mmoitems.stat.component.type.builtin.NumberComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.ObjectComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.StringComponentType;
@ -23,11 +24,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
@HasCategory(cat = "soulbound")
public class Soulbound extends ItemStat<ObjectComponent> implements ItemRestriction {
public class Soulbound extends ItemStat<SoulboundComponent> implements ItemRestriction {
public Soulbound() {
super("SOULBOUND", null);
setComponentType(ObjectComponentType.init()
.implementation(SoulboundComponent::new)
.addField("UUID", StringComponentType.uuid().build())
.addField("Name", StringComponentType.init().build())
.addField("Level", NumberComponentType.decimal().build())
@ -38,21 +40,18 @@ public class Soulbound extends ItemStat<ObjectComponent> implements ItemRestrict
@Nullable
@Override
public ObjectComponent read(ReadMMOItem mmoitem) {
public SoulboundComponent read(ReadMMOItem mmoitem) {
return readJsonFromNbt(mmoitem);
}
@Override
public void write(ItemStackBuilder builder, @NotNull ObjectComponent component) {
public void write(ItemStackBuilder builder, @NotNull SoulboundComponent component) {
writeJsonToNbt(builder, component);
String name = component.get("Name").getAsString();
int level = component.get("Level").getAsInteger();
// Lore stuff
String formattedLoreTag = Message.SOULBOUND_ITEM_LORE.getFormatted()
.replace("#player#", name)
.replace("#level#", MMOUtils.intToRoman(level));
.replace("#player#", component.getName())
.replace("#level#", MMOUtils.intToRoman(component.getLevel()));
builder.getLore().insert("soulbound", formattedLoreTag.split(Pattern.quote("//")));
}

View File

@ -6,7 +6,6 @@ import net.Indyuce.mmoitems.stat.component.type.builtin.ObjectComponentType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.function.BiConsumer;
/**
@ -21,7 +20,7 @@ import java.util.function.BiConsumer;
*/
public abstract class ObjectComponent extends StatComponent {
public abstract void set(@NotNull String key, @NotNull StatComponent component);
public abstract void set(@NotNull String key, @Nullable StatComponent component);
@Nullable
public abstract StatComponent get(@NotNull String key);
@ -33,20 +32,33 @@ public abstract class ObjectComponent extends StatComponent {
*/
public abstract void forEachComponent(@NotNull BiConsumer<String, StatComponent> action);
@NotNull
public abstract Collection<String> getComponentKeys();
@Override
public boolean isEmpty() {
// By principle, objects are never empty. Can be overriden if needed
return false;
}
public void mergeWith(ObjectComponentType componentType, ObjectComponent extra) {
extra.forEachComponent((key, rhs) -> {
StatComponent lhs = get(key);
if (lhs == null) set(key, rhs); // Replace if LHS has no value
else ((ComponentType) componentType.getChildType(key)).mergeComponents(lhs, rhs); // Merge both
});
public void mergeWith(ObjectComponentType.MergeMethod mergeMethod, ObjectComponentType componentType, ObjectComponent extra) {
switch (mergeMethod) {
case MERGE_EACH:
extra.forEachComponent((key, rhs) -> {
StatComponent lhs = get(key);
if (lhs == null) set(key, rhs); // Replace if LHS has no value
else ((ComponentType) componentType.getChildType(key)).mergeComponents(lhs, rhs); // Merge both
});
return;
case REPLACE_EACH:
extra.forEachComponent(this::set);
return;
case REPLACE:
forEachComponent((key, val) -> set(key, null)); // Clear LHS
extra.forEachComponent(this::set); // Set RHS
return;
default:
throw new RuntimeException("Merge method not implemented");
}
}
}

View File

@ -13,7 +13,7 @@ public class ObjectComponentImpl extends ObjectComponent {
private final Map<String, StatComponent> components = new HashMap<>();
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
components.put(key, component);
}
@ -28,12 +28,6 @@ public class ObjectComponentImpl extends ObjectComponent {
components.forEach(action);
}
@NotNull
@Override
public Set<String> getComponentKeys() {
return components.keySet();
}
@Override
public ObjectComponentImpl clone() {
ObjectComponentImpl clone = new ObjectComponentImpl();

View File

@ -8,6 +8,8 @@ import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class StringComponent extends StatComponent {
private String value;
@ -65,6 +67,11 @@ public class StringComponent extends StatComponent {
@Nullable
public static String getValue(@Nullable StringComponent component) {
return component == null ? null : component.getValue();
return component == null ? null : component.value;
}
@Nullable
public static UUID asUniqueId(@Nullable StringComponent component) {
return component == null ? null : UUID.fromString(component.toString());
}
}

View File

@ -13,8 +13,6 @@ import net.Indyuce.mmoitems.util.PlayerAbility;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
public class AbilityComponent extends ObjectComponent {
@ -22,14 +20,14 @@ public class AbilityComponent extends ObjectComponent {
private StringComponent castingMode;
private MapComponent<DoubleComponent> modifiers;
public AbilityComponent() {
// Nothing
}
private static final String SKILL = "Id";
private static final String MODIFIERS = "Modifiers";
private static final String CASTING_MODE = "CastMode";
public AbilityComponent() {
// Nothing
}
//region API
public void setSkill(@Nullable RegisteredSkill skill) {
@ -62,7 +60,7 @@ public class AbilityComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case SKILL:
skill = (StringComponent) component;
@ -100,19 +98,13 @@ public class AbilityComponent extends ObjectComponent {
if (modifiers != null) action.accept(MODIFIERS, modifiers);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(SKILL, CASTING_MODE, MODIFIERS);
}
@Override
public StatComponent clone() {
AbilityComponent clone = new AbilityComponent();
clone.skill = StatComponent.clone(skill);
clone.castingMode = StatComponent.clone(castingMode);
clone.modifiers = StatComponent.clone(modifiers);
clone.skill = clone(skill);
clone.castingMode = clone(castingMode);
clone.modifiers = clone(modifiers);
return clone;
}

View File

@ -9,8 +9,6 @@ import org.bukkit.Color;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
/**
@ -27,14 +25,14 @@ public class ColorComponent extends ObjectComponent {
// Empty
}
//region API
public ColorComponent(Color color) {
red = new IntegerComponent(color.getRed());
green = new IntegerComponent(color.getGreen());
blue = new IntegerComponent(color.getBlue());
}
//region API
public int getRed() {
return IntegerComponent.asInteger(red);
}
@ -69,7 +67,7 @@ public class ColorComponent extends ObjectComponent {
//region
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case RED:
red = (IntegerComponent) component;
@ -107,20 +105,13 @@ public class ColorComponent extends ObjectComponent {
if (red != null) action.accept(BLUE, red);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(RED, GREEN, BLUE);
}
@Override
public StatComponent clone() {
ColorComponent clone = new ColorComponent();
clone.red = StatComponent.clone(red);
clone.green = StatComponent.clone(green);
clone.blue = StatComponent.clone(blue);
clone.red = clone(red);
clone.green = clone(green);
clone.blue = clone(blue);
return clone;
}

View File

@ -1,6 +1,5 @@
package net.Indyuce.mmoitems.stat.component.builtin.composite;
import com.sun.tools.javac.util.List;
import net.Indyuce.mmoitems.stat.annotation.InvalidComponentKeyException;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.BooleanComponent;
@ -10,7 +9,6 @@ import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.function.BiConsumer;
public class CommandComponent extends ObjectComponent {
@ -18,15 +16,15 @@ public class CommandComponent extends ObjectComponent {
private DoubleComponent delay;
private BooleanComponent console, op;
public CommandComponent() {
// Nothing
}
private static final String COMMAND = "Command";
private static final String DELAY = "Delay";
private static final String CONSOLE = "Console";
private static final String OP = "Op";
public CommandComponent() {
// Nothing
}
//region API
@Nullable
@ -51,7 +49,7 @@ public class CommandComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case COMMAND:
command = (StringComponent) component;
@ -95,20 +93,14 @@ public class CommandComponent extends ObjectComponent {
if (op != null) action.accept(OP, op);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(COMMAND, DELAY, CONSOLE, OP);
}
@Override
public StatComponent clone() {
CommandComponent clone = new CommandComponent();
clone.command = StatComponent.clone(command);
clone.delay = StatComponent.clone(delay);
clone.console = StatComponent.clone(console);
clone.op = StatComponent.clone(op);
clone.command = clone(command);
clone.delay = clone(delay);
clone.console = clone(console);
clone.op = clone(op);
return clone;
}

View File

@ -1,109 +0,0 @@
package net.Indyuce.mmoitems.stat.component.builtin.composite;
import net.Indyuce.mmoitems.stat.Enchants;
import net.Indyuce.mmoitems.stat.annotation.InvalidComponentKeyException;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.component.builtin.NumberComponent;
import net.Indyuce.mmoitems.stat.component.builtin.ObjectComponent;
import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.BiConsumer;
@Deprecated
public class EnchantComponent extends ObjectComponent {
private StringComponent enchant;
private IntegerComponent level;
private static final String ENCHANT = "Enchant";
private static final String LEVEL = "Level";
public EnchantComponent() {
// Empty constructor
}
public EnchantComponent(@Nullable Enchantment enchant, int level) {
setEnchant(enchant);
setLevel(level);
}
//region API
@Nullable
public Enchantment getEnchant() {
return enchant == null ? null : Enchants.getEnchant(enchant.toString());
}
public void setEnchant(@Nullable Enchantment enchant) {
this.enchant = enchant == null ? null : new StringComponent(enchant.getKey().toString());
}
public int getLevel() {
return level.getValue();
}
public void setLevel(int level) {
this.level = level == 0 ? null : new IntegerComponent(level);
}
//endregion
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
switch (key) {
case ENCHANT:
enchant = (StringComponent) component;
return;
case LEVEL:
level = new IntegerComponent(((NumberComponent) component).intValue());
return;
default:
throw new InvalidComponentKeyException(this, key);
}
}
@Nullable
@Override
public StatComponent get(@NotNull String key) {
switch (key) {
case ENCHANT:
return enchant;
case LEVEL:
return level;
default:
throw new InvalidComponentKeyException(this, key);
}
}
@Override
public void forEachComponent(@NotNull BiConsumer<String, StatComponent> action) {
if (enchant != null) action.accept("Enchant", enchant);
if (level != null) action.accept("Level", level);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return Arrays.asList("enchant", "level");
}
@Override
public StatComponent clone() {
EnchantComponent clone = new EnchantComponent();
clone.enchant = StatComponent.clone(enchant);
clone.level = StatComponent.clone(level);
return clone;
}
//endregion
}

View File

@ -1,11 +1,15 @@
package net.Indyuce.mmoitems.stat.component.builtin.composite;
import net.Indyuce.mmoitems.stat.Enchants;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.component.builtin.MapComponent;
import net.Indyuce.mmoitems.stat.component.builtin.NumberComponent;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class EnchantMapComponent extends MapComponent<NumberComponent> {
//region API
@ -20,6 +24,13 @@ public class EnchantMapComponent extends MapComponent<NumberComponent> {
else super.asMap().put(enchantment.getKey().toString(), new IntegerComponent(level));
}
@NotNull
public List<Enchantment> getEnchants() {
List<Enchantment> enchantments = new ArrayList<>();
for (String key : asMap().keySet()) enchantments.add(Enchants.getEnchant(key));
return enchantments;
}
//endregion
@Override

View File

@ -10,8 +10,6 @@ import net.Indyuce.mmoitems.util.MMOUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiConsumer;
@ -24,10 +22,19 @@ public class GemstoneComponent extends ObjectComponent {
private StringComponent socketColor;
private IntegerComponent level;
private static final String NAME = "Name";
private static final String HISTORIC_UUID = "History";
private static final String MMOITEM_TYPE = "Type";
private static final String MMOITEM_ID = "Id";
private static final String LEVEL = "Level";
private static final String SOCKET_COLOR = "Color";
public GemstoneComponent() {
// Nothing
}
//region API
public GemstoneComponent(@NotNull LiveMMOItem gemStoneMMOItem, @Nullable String color) {
this(gemStoneMMOItem, color, UUID.randomUUID());
}
@ -40,14 +47,6 @@ public class GemstoneComponent extends ObjectComponent {
setSocketColor(color);
}
private static final String NAME = "Name";
private static final String HISTORIC_UUID = "History";
private static final String MMOITEM_TYPE = "Type";
private static final String MMOITEM_ID = "Id";
private static final String LEVEL = "Level";
private static final String SOCKET_COLOR = "Color";
//region API
@NotNull
public String getName() {
return Objects.requireNonNull(name).toString();
@ -117,7 +116,7 @@ public class GemstoneComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case NAME:
name = (StringComponent) component;
@ -173,22 +172,16 @@ public class GemstoneComponent extends ObjectComponent {
if (level != null) action.accept(LEVEL, level);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(NAME, HISTORIC_UUID, MMOITEM_TYPE, MMOITEM_ID, SOCKET_COLOR, LEVEL);
}
@Override
public StatComponent clone() {
GemstoneComponent clone = new GemstoneComponent();
clone.name = StatComponent.clone(name);
clone.uuid = StatComponent.clone(uuid);
clone.mmoitemType = StatComponent.clone(mmoitemType);
clone.mmoitemId = StatComponent.clone(mmoitemId);
clone.socketColor = StatComponent.clone(socketColor);
clone.level = StatComponent.clone(level);
clone.name = clone(name);
clone.uuid = clone(uuid);
clone.mmoitemType = clone(mmoitemType);
clone.mmoitemId = clone(mmoitemId);
clone.socketColor = clone(socketColor);
clone.level = clone(level);
return clone;
}

View File

@ -9,7 +9,6 @@ import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
@ -100,7 +99,7 @@ public class GemstonesComponent extends ObjectComponent {
//region
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case SOCKETS:
sockets = (ArrayComponent<StringComponent>) component;
@ -132,18 +131,12 @@ public class GemstonesComponent extends ObjectComponent {
if (gemstones != null) action.accept(GEMSTONES, gemstones);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(SOCKETS, GEMSTONES);
}
@Override
public StatComponent clone() {
GemstonesComponent clone = new GemstonesComponent();
clone.gemstones = StatComponent.clone(gemstones);
clone.sockets = StatComponent.clone(sockets);
clone.gemstones = clone(gemstones);
clone.sockets = clone(sockets);
return clone;
}

View File

@ -1,4 +1,4 @@
package net.Indyuce.mmoitems.stat.component.builtin.api;
package net.Indyuce.mmoitems.stat.component.builtin.composite;
import net.Indyuce.mmoitems.stat.annotation.InvalidComponentKeyException;
import net.Indyuce.mmoitems.stat.component.StatComponent;
@ -8,10 +8,10 @@ import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
// TODO Finish
@Deprecated
public class NameComponent extends ObjectComponent {
private StringComponent baseName;
@ -27,7 +27,7 @@ public class NameComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
default:
throw new InvalidComponentKeyException(this, key);
@ -48,12 +48,6 @@ public class NameComponent extends ObjectComponent {
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of();
}
@Override
public StatComponent clone() {
return null;

View File

@ -11,8 +11,6 @@ import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
@ -21,22 +19,22 @@ public class PotionEffectComponent extends ObjectComponent {
private DoubleComponent duration;
private IntegerComponent level;
private static final String TYPE = "Type";
private static final String DURATION = "Duration";
private static final String LEVEL = "Level";
public PotionEffectComponent() {
// Empty constructor
}
//region API
public PotionEffectComponent(@NotNull PotionEffect effect) {
type = new StringComponent(effect.getType().getName());
setDuration(effect.getDuration() * 20);
setLevel(effect.getAmplifier() + 1);
}
private static final String TYPE = "Type";
private static final String DURATION = "Duration";
private static final String LEVEL = "Level";
//region API
public void setType(@Nullable PotionEffectType type) {
this.type = type == null ? null : new StringComponent(type.getName());
}
@ -75,7 +73,7 @@ public class PotionEffectComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case TYPE:
type = (StringComponent) component;
@ -113,19 +111,13 @@ public class PotionEffectComponent extends ObjectComponent {
if (level != null) action.accept(LEVEL, level);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(TYPE, DURATION, LEVEL);
}
@Override
public StatComponent clone() {
PotionEffectComponent clone = new PotionEffectComponent();
clone.type = StatComponent.clone(type);
clone.duration = StatComponent.clone(duration);
clone.level = StatComponent.clone(level);
clone.type = clone(type);
clone.duration = clone(duration);
clone.level = clone(level);
return clone;
}

View File

@ -11,8 +11,6 @@ import org.bukkit.block.banner.PatternType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
public class ShieldPatternComponent extends ObjectComponent {
@ -26,6 +24,8 @@ public class ShieldPatternComponent extends ObjectComponent {
// Empty constructor
}
//region API
public ShieldPatternComponent(@Nullable PatternType patternType, @Nullable DyeColor dyeColor) {
setDyeColor(dyeColor);
setPatternType(patternType);
@ -36,8 +36,6 @@ public class ShieldPatternComponent extends ObjectComponent {
patternType = new StringComponent(pattern.getPattern().name());
}
//region API
@Nullable
public DyeColor getDyeColor() {
return dyeColor == null ? null : DyeColor.valueOf(dyeColor.getValue());
@ -68,7 +66,7 @@ public class ShieldPatternComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case DYE_COLOR:
dyeColor = (StringComponent) component;
@ -100,18 +98,12 @@ public class ShieldPatternComponent extends ObjectComponent {
if (patternType != null) action.accept(PATTERN_TYPE, patternType);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(DYE_COLOR, PATTERN_TYPE);
}
@Override
public StatComponent clone() {
ShieldPatternComponent clone = new ShieldPatternComponent();
clone.dyeColor = StatComponent.clone(dyeColor);
clone.patternType = StatComponent.clone(patternType);
clone.dyeColor = clone(dyeColor);
clone.patternType = clone(patternType);
return clone;
}

View File

@ -12,7 +12,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
@ -20,10 +19,15 @@ public class ShieldStyleComponent extends ObjectComponent {
private StringComponent baseColor;
private ArrayComponent<ShieldPatternComponent> layers;
private static final String BASE_COLOR = "Color";
private static final String LAYERS = "Layers";
public ShieldStyleComponent() {
// Empty constructor
}
//region API
public ShieldStyleComponent(@Nullable DyeColor baseColor) {
setBaseColor(baseColor);
}
@ -33,11 +37,6 @@ public class ShieldStyleComponent extends ObjectComponent {
patternsFromBukkit(banner.getPatterns());
}
private static final String BASE_COLOR = "Color";
private static final String LAYERS = "Layers";
//region API
public void setBaseColor(@Nullable DyeColor baseColor) {
this.baseColor = baseColor == null ? null : new StringComponent(baseColor.name());
}
@ -79,7 +78,7 @@ public class ShieldStyleComponent extends ObjectComponent {
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case BASE_COLOR:
baseColor = (StringComponent) component;
@ -111,18 +110,12 @@ public class ShieldStyleComponent extends ObjectComponent {
if (layers != null) action.accept(LAYERS, layers);
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of(BASE_COLOR, LAYERS);
}
@Override
public StatComponent clone() {
ShieldStyleComponent clone = new ShieldStyleComponent();
clone.baseColor = StatComponent.clone(baseColor);
clone.layers = StatComponent.clone(layers);
clone.baseColor = clone(baseColor);
clone.layers = clone(layers);
return clone;
}

View File

@ -0,0 +1,115 @@
package net.Indyuce.mmoitems.stat.component.builtin.composite;
import net.Indyuce.mmoitems.stat.annotation.InvalidComponentKeyException;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.component.builtin.ObjectComponent;
import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import java.util.function.BiConsumer;
public class SoulboundComponent extends ObjectComponent {
private StringComponent uuid, name;
private IntegerComponent level;
private static final String UUID = "UUID";
private static final String NAME = "Name";
private static final String LEVEL = "Level";
public SoulboundComponent() {
// Empty
}
//region API
public SoulboundComponent(@NotNull Player player, int level) {
setUniqueId(player.getUniqueId());
setName(player.getName());
setLevel(level);
this.level = new IntegerComponent(level);
}
@Nullable
public UUID getUniqueId() {
return StringComponent.asUniqueId(uuid);
}
public void setUniqueId(@Nullable UUID uniqueId) {
this.uuid = uniqueId == null ? null : new StringComponent(uniqueId.toString());
}
public int getLevel() {
return IntegerComponent.asInteger(level);
}
public void setLevel(int level) {
this.level = level == 0 ? null : new IntegerComponent(level);
}
public String getName() {
return StringComponent.getValue(name);
}
public void setName(@Nullable String name) {
this.name = name == null ? null : new StringComponent(name);
}
//endregion
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case UUID:
uuid = (StringComponent) component;
return;
case NAME:
name = (StringComponent) component;
return;
case LEVEL:
level = (IntegerComponent) component;
default:
throw new InvalidComponentKeyException(this, key);
}
}
@Nullable
@Override
public StatComponent get(@NotNull String key) {
switch (key) {
case UUID:
return uuid;
case NAME:
return name;
case LEVEL:
return level;
default:
throw new InvalidComponentKeyException(this, key);
}
}
@Override
public void forEachComponent(@NotNull BiConsumer<String, StatComponent> action) {
if (uuid != null) action.accept(UUID, uuid);
if (name != null) action.accept(NAME, name);
if (level != null) action.accept(LEVEL, level);
}
@Override
public StatComponent clone() {
SoulboundComponent clone = new SoulboundComponent();
clone.uuid = clone(uuid);
clone.name = clone(name);
clone.level = clone(level);
return clone;
}
//endregion
}

View File

@ -6,24 +6,29 @@ import net.Indyuce.mmoitems.stat.component.builtin.ObjectComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
public class TemplateClass extends ObjectComponent {
private static final String SOME_KEY = "SomeKey";
private static final String SOME_OTHER_KEY = "SomeOtherKey";
private static final String LAST_KEY = "LastKey";
public TemplateClass() {
// Empty
}
//region API
//endregion
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
default:
throw new InvalidComponentKeyException(this, key);
@ -44,15 +49,9 @@ public class TemplateClass extends ObjectComponent {
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of();
}
@Override
public StatComponent clone() {
AbilityComponent clone = new AbilityComponent();
TemplateClass clone = new TemplateClass();

View File

@ -11,10 +11,10 @@ import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
// TODO Finish
@Deprecated
public class UpgradeComponent extends ObjectComponent {
private IntegerComponent level, min, max;
private StringComponent template;
@ -115,7 +115,7 @@ return template==null ? null
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @NotNull StatComponent component) {
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
default:
throw new InvalidComponentKeyException(this, key);
@ -136,11 +136,5 @@ return template==null ? null
}
@NotNull
@Override
public Collection<String> getComponentKeys() {
return List.of();
}
//endregion
}

View File

@ -28,6 +28,8 @@ public class ObjectComponentType extends ComponentType<ObjectModel, ObjectCompon
private String stringFormatSeparatorPattern;
private MergeMethod mergeMethod = MergeMethod.MERGE_EACH;
/**
* There are some stats
*
@ -181,7 +183,27 @@ public class ObjectComponentType extends ComponentType<ObjectModel, ObjectCompon
@Override
public void mergeComponents(@NotNull ObjectComponent base, @NotNull ObjectComponent extra) {
base.mergeWith(this, extra);
base.mergeWith(mergeMethod, this, extra);
}
public static enum MergeMethod {
/**
* Fully replace base component by the new component
*/
REPLACE,
/**
* Object keys are merged, old values are ignored in case of collision,
* only newest values are taken into account.
*/
REPLACE_EACH,
/**
* Object keys are kept and value pairs are merged in case of collision.
* Default merge method and most logical in most scenarios.
*/
MERGE_EACH,
}
//endregion

View File

@ -1,10 +1,9 @@
package net.Indyuce.mmoitems.stat.data;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
@Deprecated
public class DoubleData implements StatData, Mergeable<DoubleData> {
public class DoubleData implements StatData {
private double value;
public DoubleData(double value) {
@ -27,7 +26,6 @@ public class DoubleData implements StatData, Mergeable<DoubleData> {
value *= 1 + coef;
}
@Override
public void mergeWith(DoubleData data) {
value += data.value;
}

View File

@ -1,196 +0,0 @@
package net.Indyuce.mmoitems.stat.data;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@Deprecated
public class EnchantListData implements StatData, Mergeable<EnchantListData> {
private final Map<Enchantment, Integer> enchants = new HashMap<>();
@NotNull
public Set<Enchantment> getEnchants() {
return enchants.keySet();
}
public int getLevel(@NotNull Enchantment enchant) {
@Nullable Integer found = enchants.get(enchant);
return found == null ? 0 : found;
}
public void addEnchant(Enchantment enchant, int level) {
// Ignore lvl 0 enchants :wazowskibruhmoment:
if (level <= 0) {
enchants.remove(enchant);
} else {
enchants.put(enchant, level);
}
}
public void clear() {
enchants.clear();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof EnchantListData)) {
return false;
}
if (((EnchantListData) obj).enchants.size() != enchants.size()) {
return false;
}
for (Enchantment e : getEnchants()) {
// Compare
if (getLevel(e) != ((EnchantListData) obj).getLevel(e)) {
return false;
}
}
return true;
}
@Override
public void mergeWith(@NotNull EnchantListData targetData) {
for (Enchantment enchant : targetData.getEnchants())
addEnchant(enchant, Math.max(enchants.getOrDefault(enchant, 0), targetData.getLevel(enchant)));
}
@Override
@NotNull
public EnchantListData clone() {
// Start Fresh
EnchantListData ret = new EnchantListData();
// Enchant
for (Enchantment enchant : enchants.keySet()) {
ret.addEnchant(enchant, enchants.getOrDefault(enchant, 0));
}
// Thats it
return ret;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("EnchantList{");
final AtomicBoolean check = new AtomicBoolean();
enchants.forEach((enchant, lvl) -> {
if (check.get()) builder.append(",");
builder.append(enchant).append("=").append(lvl);
check.set(true);
});
builder.append("}");
return builder.toString();
}
@Override
public boolean isEmpty() {
for (int level : enchants.values())
if (level > 0)
return false;
return true;
}
/*
TODO enchants
/**
* todo We cannot yet assume (for a few months) that the Original Enchantment Data
* registered into the Stat History is actually true to the template (since it may
* be the enchantments of an old-enchanted item, put by the player).
* <br><br>
* Thus this block of code checks the enchantment data of the newly generated
* MMOItem and follows the following logic to give our best guess if the Original
* stats are actually Original:
* <br><br>
* 1: Is the item unenchantable and unrepairable? Then they must be original
* <br><br>
* 2: Does the template have no enchantments? Then they must be external
* <br><br>
* 3: Does the template have this enchantment at an unobtainable level? Then it must be original
* <br><br>
* 4: Does the template have this enchantment at a lesser level? Then it must be external (player upgraded it)
* <br><br>
* Original: Included within the template at first creation
* External: Enchanted manually by a player
*
* @param mmoItem The item, to provide context for adequate guessing.
*
public void identifyTrueOriginalEnchantments(@NotNull MMOItem mmoItem) {
//RFG//MMOItems.log(" \u00a7b> \u00a77Original Enchantments Upkeep");
// 1: The item is unenchantable and unrepairable? Cancel this operation, the cached are Original
if (mmoItem.hasData(ItemStats.DISABLE_ENCHANTING) && mmoItem.hasData(ItemStats.DISABLE_REPAIRING)) {
//RFG//MMOItems.log(" \u00a7bType-1 \u00a77Original Identification ~ no transfer");
clear();
return;
}
EnchantListData mmoData = (EnchantListData) mmoItem.computeComponent(ItemStats.ENCHANTS);
// 2: If it has data (It always has) and the amount of enchants is zero, the cached are Extraneous
if (mmoData.getEnchants().size() == 0) {
//RFG//MMOItems.log(" \u00a73Type-2 \u00a77Extraneous Identification ~ all transferred");
// All right, lets add those to cached enchantments
mmoItem.mergeData(ItemStats.ENCHANTS, this, null);
return;
}
// Which enchantments are deemed external, after all?
EnchantListData processed = new EnchantListData();
// Identify material
mmoItem.hasData(ItemStats.MATERIAL);
MaterialData mData = (MaterialData) mmoItem.getData(ItemStats.MATERIAL);
Material mat = mData.getMaterial();
// 3 & 4: Lets examine every stat
for (Enchantment e : getEnchants()) {
//RFG//MMOItems.log(" \u00a7b = \u00a77Per Enchant - \u00a7f" + e.getName());
// Lets see hmm
int current = getLevel(e);
int updated = mmoData.getLevel(e);
//RFG//MMOItems.log(" \u00a73 <=: \u00a77Current \u00a7f" + current);
//RFG//MMOItems.log(" \u00a73 <=: \u00a77Updated \u00a7f" + updated);
// 3: Is it at an unobtainable level? Then its Original
if (updated > e.getMaxLevel() || !e.getItemTarget().includes(mat)) {
//RFG//MMOItems.log(" \u00a7bType-3 \u00a77Original Identification ~ Impossible through vanilla");
continue;
}
// 4: Is it at a lesser level? Player must have enchanted, take them as External
if (updated < current) {
//RFG//MMOItems.log(" \u00a73Type-4 \u00a77Extraneous Identification ~ Improvement from the Template");
processed.addEnchant(e, current);
//noinspection UnnecessaryContinue
continue;
}
//RFG//MMOItems.log(" \u00a73Type-5 \u00a77Original Identification ~ Not improved from the template");
}
// Finish merge
if (!processed.isEmpty()) {
// As Extraneosu
mmoItem.mergeData(ItemStats.ENCHANTS, processed, null);
}
}
*/
}

View File

@ -1,40 +0,0 @@
package net.Indyuce.mmoitems.stat.data;
import net.Indyuce.mmoitems.api.item.build.MMOItemBuilder;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
@Deprecated
public class MaterialData implements StatData, RandomStatData<MaterialData> {
private Material material;
/*
* material must not be null because it is called directly in the
* MMOBuilder constructor.
*/
public MaterialData(Material material) {
Validate.notNull(material, "Material must not be null");
this.material = material;
}
public void setMaterial(Material material) {
Validate.notNull(material, "Material must not be null");
this.material = material;
}
public Material getMaterial() {
return material;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public MaterialData randomize(MMOItemBuilder builder) {
return this;
}
}

View File

@ -1,62 +0,0 @@
package net.Indyuce.mmoitems.stat.data;
import io.lumine.mythic.lib.gson.JsonObject;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@Deprecated
public class SoulboundData implements StatData {
private final UUID uuid;
private final String name;
private final int level;
public SoulboundData(Player player, int level) {
this(player.getUniqueId(), player.getName(), level);
}
public SoulboundData(UUID uuid, String name, int level) {
this.uuid = uuid;
this.name = name;
this.level = level;
}
public SoulboundData(JsonObject object) {
uuid = UUID.fromString(object.get("UUID").getAsString());
name = object.get("Name").getAsString();
level = object.get("Level").getAsInt();
}
public UUID getUniqueId() {
return uuid;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public JsonObject toJson() {
JsonObject object = new JsonObject();
object.addProperty("Level", level);
object.addProperty("Name", name);
object.addProperty("UUID", uuid.toString());
return object;
}
@NotNull
@Override
public StatData clone() {
return this;
}
@Override
public boolean isEmpty() {
return false;
}
}

View File

@ -17,14 +17,16 @@ public enum HistoryPolicy {
/**
* Always save base item data stat history, even when it is empty. This is necessary
* for options like enchants or item name, which are stats which can be modified by
* the player in-game without notifying MMOItems.
* the player in-game without notifying MMOItems. For instance:
* - renaming an item through the anvil modifies its display name
* - enchanting an item through the anvil/enchanting table modifies its enchants
*/
SAVE_BASE,
VANILLA_EDITABLE,
/**
* No stat history whatsoever will be saved. This is useful for dynamic stats like
* durability, item level or experience... Or even stats which could have stat history
* but don't for simplicity.
* durability, item level or experience... Or even stats which could have stat history,
* but don't, for sake of simplicity.
*/
NONE,
NONE;
}

View File

@ -13,7 +13,6 @@ import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.GemstoneComponent;
import net.Indyuce.mmoitems.stat.component.builtin.composite.GemstonesComponent;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
@ -77,7 +76,7 @@ public class StatHistory<C extends StatComponent> {
public boolean isEmpty() {
// Important assumption for stats like Enchants and Display Name
if (getItemStat().getHistoryPolicy() == HistoryPolicy.SAVE_BASE && !getOriginalData().isEmpty())
if (getItemStat().getHistoryPolicy() == HistoryPolicy.VANILLA_EDITABLE && !getOriginalData().isEmpty())
return false;
// Any gemstones or external SH? Then its NOT CLEAR
@ -230,7 +229,7 @@ public class StatHistory<C extends StatComponent> {
// Create Clear
C theEXSH = getItemStat().generateEmptyComponent();
for (C ex : getExternalData()) ((Mergeable) theEXSH).mergeWith((Mergeable) ex);
for (C ex : getExternalData()) itemStat.getComponentType().mergeComponents(theEXSH, ex);
// Clear and Register
getExternalData().clear();
@ -374,14 +373,8 @@ public class StatHistory<C extends StatComponent> {
// To know the stat it was
object.addProperty(ENC_STAT, getItemStat().getId());
/*
* Save the original data. It is redundant to save if it is clear though.
*
* Except for some class of stats, like Enchants or Item Name, which can be modified
* through vanilla means. Keeping track of the base item data allows to distinguish
* vanilla modifications and other modifications.
*/
if (!getOriginalData().isEmpty() || getItemStat().getHistoryPolicy() == HistoryPolicy.SAVE_BASE)
// Save the original data. It is redundant to save if it is clear though.
if (!getOriginalData().isEmpty())
object.add(ENC_OGS, getItemStat().getComponentType().toJson(getOriginalData()));
// Data from gems

View File

@ -2,7 +2,7 @@ package net.Indyuce.mmoitems.listener.reforging;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.event.MMOItemReforgeEvent;
import net.Indyuce.mmoitems.stat.data.EnchantListData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.EnchantMapComponent;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -65,8 +65,7 @@ public class RFGKeepEnchantments implements Listener {
*/
}
void log(@NotNull EnchantListData enchList, @NotNull String title) {
void log(@NotNull EnchantMapComponent enchList, @NotNull String title) {
MMOItems.print(null, " \u00a73> \u00a77" + title + ":", null);
for (Enchantment e : enchList.getEnchants()) {
MMOItems.print(null, " \u00a7b * \u00a77" + e.getName() + " \u00a7f" + enchList.getLevel(e), null);

View File

@ -2,7 +2,7 @@ package net.Indyuce.mmoitems.listener.reforging;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.event.MMOItemReforgeEvent;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.component.builtin.composite.SoulboundComponent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -16,29 +16,11 @@ public class RFGKeepSoulbound implements Listener {
@EventHandler
public void onReforge(MMOItemReforgeEvent event) {
SoulboundData soul = (SoulboundData) event.getOldMMOItem().getData(ItemStats.SOULBOUND);
if (!event.getOptions().shouldKeepSoulBind()) return;
// No data?
if (soul == null) {
SoulboundComponent soul = event.getOldMMOItem().getComponent(ItemStats.SOULBOUND);
if (soul == null) return;
// Auto soulbind active?
// if (MMOItems.plugin.getConfig().getBoolean("soulbound.auto-bind.disable-on." + event.getTypeName())) { return; }
// Need a player to exist for auto soulbind
// if (event.getPlayer() == null) { return; }
// Auto?
// if (event.getNewMMOItem().hasData(ItemStats.AUTO_SOULBIND) && !event.getNewMMOItem().hasData(ItemStats.SOULBOUND)) {
// Auto soulbound config brr
// event.getNewMMOItem().setData(ItemStats.SOULBOUND, new SoulboundData(event.getPlayer().getUniqueId(), event.getPlayer().getName(), MMOItemReforger.autoSoulbindLevel));
// }
} else if (event.getOptions().shouldKeepSoulBind()) {
//RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping Soulbound");
// Keep it
event.getNewMMOItem().setData(ItemStats.SOULBOUND, soul);
}
event.getNewMMOItem().setComponent(ItemStats.SOULBOUND, soul);
}
}