Merge remote-tracking branch 'origin/master'

This commit is contained in:
Jules 2023-04-23 21:42:41 +02:00
commit fca42be9bf
37 changed files with 793 additions and 917 deletions

View File

@ -119,7 +119,8 @@ public class ReforgeOptions {
} }
/** /**
* Keeps the display name of the item. * If the item should be rerolled when updated. In the contrary,
* the previous RNG will be transferred onto the newest item.
*/ */
public boolean shouldReRoll() { public boolean shouldReRoll() {
return reRoll; return reRoll;

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.api; package net.Indyuce.mmoitems.api;
import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.player.modifier.ModifierSource; import io.lumine.mythic.lib.player.modifier.ModifierSource;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
@ -298,10 +299,9 @@ public class Type {
*/ */
@Nullable @Nullable
public static Type get(@Nullable String id) { public static Type get(@Nullable String id) {
if (id == null) { if (id == null) return null;
return null;
} String format = UtilityMethods.enumName(id);
String format = id.toUpperCase().replace("-", "_").replace(" ", "_");
return MMOItems.plugin.getTypes().has(format) ? MMOItems.plugin.getTypes().get(format) : null; return MMOItems.plugin.getTypes().has(format) ? MMOItems.plugin.getTypes().get(format) : null;
} }

View File

@ -94,8 +94,10 @@ public class CraftingStatus {
public void remove(CraftingInfo craft) { public void remove(CraftingInfo craft) {
int index = crafts.indexOf(craft); int index = crafts.indexOf(craft);
if (index != -1) if (index != -1)
for (int j = index; j < crafts.size(); j++) for (int j = index + 1; j < crafts.size(); j++) {
crafts.get(j).removeDelay(Math.max(0, craft.getLeft() - craft.getElapsed())); CraftingInfo nextCraft = crafts.get(j);
nextCraft.delay = Math.max(0, nextCraft.delay - craft.getLeft());
}
crafts.remove(craft); crafts.remove(craft);
} }

View File

@ -30,7 +30,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class CraftingRecipe extends Recipe { public class CraftingRecipe extends Recipe {
@NotNull public static final String UNSPECIFIED = "N/A"; @NotNull
public static final String UNSPECIFIED = "N/A";
public CraftingRecipe(@NotNull ConfigurationSection config) throws IllegalArgumentException { public CraftingRecipe(@NotNull ConfigurationSection config) throws IllegalArgumentException {
super(config); super(config);
@ -55,7 +56,12 @@ public class CraftingRecipe extends Recipe {
if (sweetOutput == null) { if (sweetOutput == null) {
// Throw message // Throw message
throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> {
if (message instanceof FriendlyFeedbackMessage) {
return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get());
}
return "";
}), ""));
} }
// Accept // Accept
@ -71,7 +77,12 @@ public class CraftingRecipe extends Recipe {
if (sweetOutput == null) { if (sweetOutput == null) {
// Throw message // Throw message
throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> {
if (message instanceof FriendlyFeedbackMessage) {
return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get());
}
return "";
}), ""));
} }
// Accept // Accept
@ -88,14 +99,24 @@ public class CraftingRecipe extends Recipe {
if (!output.isValid(ffp)) { if (!output.isValid(ffp)) {
// Throw message // Throw message
throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> {
if (message instanceof FriendlyFeedbackMessage) {
return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get());
}
return "";
}), ""));
} }
// Valid UIFilter? // Valid UIFilter?
if (output.getItemStack(ffp) == null) { if (output.getItemStack(ffp) == null) {
// Throw message // Throw message
throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> {
if (message instanceof FriendlyFeedbackMessage) {
return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get());
}
return "";
}), ""));
} }
// Its a MMOItem UIFilter, then? // Its a MMOItem UIFilter, then?
@ -121,21 +142,35 @@ public class CraftingRecipe extends Recipe {
* way to save an MMOItem in the config file TODO save as ItemStack * way to save an MMOItem in the config file TODO save as ItemStack
*/ */
private final double craftingTime; private final double craftingTime;
public double getCraftingTime() { return craftingTime; }
public boolean isInstant() { return craftingTime <= 0; } public double getCraftingTime() {
return craftingTime;
}
public boolean isInstant() {
return craftingTime <= 0;
}
/** /**
* @return The item specified by the player that will be produced by this recipe. * @return The item specified by the player that will be produced by this recipe.
*/ */
@NotNull public ProvidedUIFilter getOutput() { return output; } @NotNull
@NotNull private final ProvidedUIFilter output; public ProvidedUIFilter getOutput() {
return output;
}
@NotNull
private final ProvidedUIFilter output;
@Nullable
ConfigMMOItem identifiedMMO;
@Nullable ConfigMMOItem identifiedMMO;
/** /**
* @return The output ItemStack from this * @return The output ItemStack from this
*/ */
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@NotNull public ItemStack getOutputItemStack(@Nullable RPGPlayer rpg) { @NotNull
public ItemStack getOutputItemStack(@Nullable RPGPlayer rpg) {
// Generate as MMOItem // Generate as MMOItem
if (identifiedMMO != null && rpg != null) { if (identifiedMMO != null && rpg != null) {
@ -151,10 +186,12 @@ public class CraftingRecipe extends Recipe {
// Generate from ProvidedUIFilter, guaranteed to not be null don't listen to the inspection. // Generate from ProvidedUIFilter, guaranteed to not be null don't listen to the inspection.
return output.getItemStack(null); return output.getItemStack(null);
} }
/** /**
* @return The preview ItemStack from this * @return The preview ItemStack from this
*/ */
@NotNull public ItemStack getPreviewItemStack() { @NotNull
public ItemStack getPreviewItemStack() {
// Generate as MMOItem // Generate as MMOItem
if (identifiedMMO != null) { if (identifiedMMO != null) {
@ -175,10 +212,14 @@ public class CraftingRecipe extends Recipe {
ItemMeta itemMeta = gen.getItemMeta(); ItemMeta itemMeta = gen.getItemMeta();
if (itemMeta != null) { if (itemMeta != null) {
itemMeta.setDisplayName(SilentNumbers.getItemName(gen, false) + "\u00a7\u02ab"); itemMeta.setDisplayName(SilentNumbers.getItemName(gen, false) + "\u00a7\u02ab");
gen.setItemMeta(itemMeta); } gen.setItemMeta(itemMeta);
}
return gen; return gen;
} }
public int getOutputAmount() { return output.getAmount(1); }
public int getOutputAmount() {
return output.getAmount(1);
}
@Override @Override
public boolean whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) { public boolean whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) {
@ -190,7 +231,6 @@ public class CraftingRecipe extends Recipe {
* and directly add the output to the player's inventory * and directly add the output to the player's inventory
*/ */
if (isInstant()) { if (isInstant()) {
ItemStack result = hasOption(RecipeOption.OUTPUT_ITEM) ? getOutputItemStack(data.getRPG()) : null; ItemStack result = hasOption(RecipeOption.OUTPUT_ITEM) ? getOutputItemStack(data.getRPG()) : null;
PlayerUseCraftingStationEvent event = new PlayerUseCraftingStationEvent(data, station, recipe, result); PlayerUseCraftingStationEvent event = new PlayerUseCraftingStationEvent(data, station, recipe, result);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
@ -216,7 +256,7 @@ public class CraftingRecipe extends Recipe {
/* /*
* If the recipe is not instant, add the item to the crafting queue * If the recipe is not instant, add the item to the crafting queue
*/ */
} else { }
PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(data, station, recipe); PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(data, station, recipe);
Bukkit.getPluginManager().callEvent(called); Bukkit.getPluginManager().callEvent(called);
@ -232,7 +272,6 @@ public class CraftingRecipe extends Recipe {
// Recipe was successfully used // Recipe was successfully used
return true; return true;
} }
}
@Override @Override
public boolean canUse(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) { public boolean canUse(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) {
@ -252,7 +291,9 @@ public class CraftingRecipe extends Recipe {
} }
@Override @Override
public ItemStack display(CheckedRecipe recipe) { return ConfigItems.CRAFTING_RECIPE_DISPLAY.newBuilder(recipe).build(); } public ItemStack display(CheckedRecipe recipe) {
return ConfigItems.CRAFTING_RECIPE_DISPLAY.newBuilder(recipe).build();
}
@Override @Override
public CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv) { public CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv) {

View File

@ -33,10 +33,9 @@ public class ItemSkin extends UseItem {
if (targetType == Type.SKIN) if (targetType == Type.SKIN)
return new ApplyResult(ResultType.NONE); return new ApplyResult(ResultType.NONE);
if (MMOItems.plugin.getConfig().getBoolean("locked-skins") && target.getBoolean("MMOITEMS_HAS_SKIN")) { if (MMOItems.plugin.getConfig().getBoolean("locked-skins") && MMOUtils.isNonEmpty(target.getString(ItemSkin.SKIN_ID_TAG))) {
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1);
Message.SKIN_REJECTED.format(ChatColor.RED, "#item#", MMOUtils.getDisplayName(target.getItem())) Message.SKIN_REJECTED.format(ChatColor.RED, "#item#", MMOUtils.getDisplayName(target.getItem())).send(player);
.send(player);
return new ApplyResult(ResultType.NONE); return new ApplyResult(ResultType.NONE);
} }
@ -102,8 +101,6 @@ public class ItemSkin extends UseItem {
return new ApplyResult(item); return new ApplyResult(item);
} }
public static final String HAS_SKIN_TAG = "MMOITEMS_HAS_SKIN";
/** /**
* When applying a skin to an item, the skin item ID is saved * When applying a skin to an item, the skin item ID is saved
* in the target item so that if deskined, it can be retrieved * in the target item so that if deskined, it can be retrieved
@ -113,118 +110,67 @@ public class ItemSkin extends UseItem {
/** /**
* Applies the skin information from a skin consumable onto any item. * Applies the skin information from a skin consumable onto any item.
* <p>
* This methods also works if the provided VolatileMMOItem matches an item
* that already has a skin information stored inside of it, in which case it
* will fetch the value of the {@link #SKIN_ID_TAG} nbttag.
* *
* @param target Target item that the skin has been <b>successfully</b> applied to * @param target Target item that the skin has been <b>successfully</b> applied to
* @param skinItemMMO Skin consumable * @param volSkin Skin consumable
* @return Built ItemStack from the target NBT but with the skin data contained in the skin consumable * @return Built ItemStack from the target NBT but with the skin data contained in the skin consumable
* @deprecated Badly implemented. This handles individual stats and should use some SkinStat interface
*/ */
@Deprecated
@NotNull @NotNull
public static ItemStack applySkin(@NotNull NBTItem target, @NotNull VolatileMMOItem skinItemMMO) { public static ItemStack applySkin(@NotNull NBTItem target, @NotNull VolatileMMOItem volSkin) {
final NBTItem skinItemNBT = skinItemMMO.getNBT(); final NBTItem nbtSkin = volSkin.getNBT();
target.addTag(new ItemTag(HAS_SKIN_TAG, true)); // Apply skin ID to new item
target.addTag(new ItemTag(SKIN_ID_TAG, skinItemNBT.getString("MMOITEMS_ITEM_ID"))); @Nullable String appliedSkinId = volSkin.getNBT().getString(SKIN_ID_TAG);
if (skinItemNBT.getInteger("CustomModelData") != 0) appliedSkinId = MMOUtils.isNonEmpty(appliedSkinId) ? appliedSkinId : nbtSkin.getString("MMOITEMS_ITEM_ID");
target.addTag(new ItemTag("CustomModelData", skinItemNBT.getInteger("CustomModelData"))); target.addTag(new ItemTag(SKIN_ID_TAG, appliedSkinId));
if (!skinItemNBT.getString("MMOITEMS_ITEM_PARTICLES").isEmpty()) // Custom model data
target.addTag(new ItemTag("MMOITEMS_ITEM_PARTICLES", skinItemNBT.getString("MMOITEMS_ITEM_PARTICLES"))); if (nbtSkin.getInteger("CustomModelData") != 0)
target.addTag(new ItemTag("CustomModelData", nbtSkin.getInteger("CustomModelData")));
ItemStack item = target.toItem(); // Particles
if (item.getType() != skinItemNBT.getItem().getType()) if (!nbtSkin.getString("MMOITEMS_ITEM_PARTICLES").isEmpty())
item.setType(skinItemNBT.getItem().getType()); target.addTag(new ItemTag("MMOITEMS_ITEM_PARTICLES", nbtSkin.getString("MMOITEMS_ITEM_PARTICLES")));
ItemMeta meta = item.getItemMeta(); final ItemStack item = target.toItem();
ItemMeta skinMeta = skinItemNBT.getItem().getItemMeta(); if (item.getType() != nbtSkin.getItem().getType())
item.setType(nbtSkin.getItem().getType());
final ItemMeta meta = item.getItemMeta();
final ItemMeta skinMeta = nbtSkin.getItem().getItemMeta();
if (skinMeta != null && meta != null) { if (skinMeta != null && meta != null) {
// TODO factorize with a ItemSkinStat stat interface // TODO SkinStat interface
// Unbreakable & durability
if (skinMeta.isUnbreakable()) { if (skinMeta.isUnbreakable()) {
meta.setUnbreakable(true); meta.setUnbreakable(true);
if (meta instanceof Damageable && skinMeta instanceof Damageable) if (meta instanceof Damageable && skinMeta instanceof Damageable)
((Damageable) meta).setDamage(((Damageable) skinMeta).getDamage()); ((Damageable) meta).setDamage(((Damageable) skinMeta).getDamage());
} }
// Leather armor
if (skinMeta instanceof LeatherArmorMeta && meta instanceof LeatherArmorMeta) if (skinMeta instanceof LeatherArmorMeta && meta instanceof LeatherArmorMeta)
((LeatherArmorMeta) meta).setColor(((LeatherArmorMeta) skinMeta).getColor()); ((LeatherArmorMeta) meta).setColor(((LeatherArmorMeta) skinMeta).getColor());
if (skinItemMMO.hasData(ItemStats.SKULL_TEXTURE) && item.getType() == VersionMaterial.PLAYER_HEAD.toMaterial() // Skull texture
&& skinItemNBT.getItem().getType() == VersionMaterial.PLAYER_HEAD.toMaterial()) { if (volSkin.hasData(ItemStats.SKULL_TEXTURE)
&& item.getType() == VersionMaterial.PLAYER_HEAD.toMaterial()
&& nbtSkin.getItem().getType() == VersionMaterial.PLAYER_HEAD.toMaterial())
try { try {
Field profileField = meta.getClass().getDeclaredField("profile"); final Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true); profileField.setAccessible(true);
profileField.set(meta, profileField.set(meta,
((SkullTextureData) skinItemMMO.getData(ItemStats.SKULL_TEXTURE)).getGameProfile()); ((SkullTextureData) volSkin.getData(ItemStats.SKULL_TEXTURE)).getGameProfile());
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
MMOItems.plugin.getLogger().warning("Could not read skull texture"); MMOItems.plugin.getLogger().warning("Could not read skull texture");
} }
}
item.setItemMeta(meta);
}
return item;
}
/**
* Copies a skin from one item to another
*
* @param target Target item that you are copying the skin onto
* @param originalItemNBT Item with a skin already, as NBT. Operation will fail
* if it doesnt have a skin.
* @return Built ItemStack from the target NBT but with the skin data contained in the skin consumable
* @author Gunging
*/
@Nullable
public static ItemStack applySkin(@NotNull NBTItem target, @NotNull NBTItem originalItemNBT) {
// No skin no service
if (!originalItemNBT.getBoolean(HAS_SKIN_TAG)) {
return null;
}
// Copy over data
target.addTag(new ItemTag(HAS_SKIN_TAG, true));
target.addTag(new ItemTag(SKIN_ID_TAG, originalItemNBT.getString("MMOITEMS_ITEM_ID")));
if (originalItemNBT.getInteger("CustomModelData") != 0) {
target.addTag(new ItemTag("CustomModelData", originalItemNBT.getInteger("CustomModelData")));
}
if (!originalItemNBT.getString("MMOITEMS_ITEM_PARTICLES").isEmpty()) {
target.addTag(new ItemTag("MMOITEMS_ITEM_PARTICLES", originalItemNBT.getString("MMOITEMS_ITEM_PARTICLES")));
}
// ItemMeta values copy-over
ItemStack item = target.toItem();
if (item.getType() != originalItemNBT.getItem().getType()) {
item.setType(originalItemNBT.getItem().getType());
}
ItemMeta meta = item.getItemMeta();
ItemMeta originalMeta = originalItemNBT.getItem().getItemMeta();
if (originalMeta != null && meta != null) {
if (originalMeta.isUnbreakable()) {
meta.setUnbreakable(true);
if (meta instanceof Damageable && originalMeta instanceof Damageable)
((Damageable) meta).setDamage(((Damageable) originalMeta).getDamage());
}
if (originalMeta instanceof LeatherArmorMeta && meta instanceof LeatherArmorMeta)
((LeatherArmorMeta) meta).setColor(((LeatherArmorMeta) originalMeta).getColor());
VolatileMMOItem originalVolatile = new VolatileMMOItem(originalItemNBT);
if (originalVolatile.hasData(ItemStats.SKULL_TEXTURE) && item.getType() == VersionMaterial.PLAYER_HEAD.toMaterial()
&& originalItemNBT.getItem().getType() == VersionMaterial.PLAYER_HEAD.toMaterial()) {
try {
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(meta,
((SkullTextureData) originalVolatile.getData(ItemStats.SKULL_TEXTURE)).getGameProfile());
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
MMOItems.plugin.getLogger().warning("Could not read skull texture");
}
}
item.setItemMeta(meta); item.setItemMeta(meta);
} }

View File

@ -176,39 +176,21 @@ public class MMOItemBuilder {
* clears less priority modifiers * clears less priority modifiers
* *
* @param modifier Name modifier which needs to be added * @param modifier Name modifier which needs to be added
* @param mod UUID of storage into the Stat History of name * @param modifierId UUID of storage into the Stat History of name
*/ */
public void addModifier(@NotNull NameModifier modifier, @NotNull UUID mod) { public void addModifier(@NotNull NameModifier modifier, @NotNull UUID modifierId) {
// Might overwrite a modifier yes // Might overwrite a modifier
ArrayList<UUID> removedObs = new ArrayList<>(); final Iterator<NameModifier> ite = nameModifiers.values().iterator();
for (UUID cUID : nameModifiers.keySet()) { while (ite.hasNext()) {
final NameModifier obs = ite.next();
// Remove obs?
NameModifier obs = nameModifiers.get(cUID);
// Are they the same type?
if (obs.getType() == modifier.getType()) { if (obs.getType() == modifier.getType()) {
if (obs.getPriority() > modifier.getPriority()) return;
// Choose greater priority else if (obs.getPriority() < modifier.getPriority()) ite.remove();
if (obs.getPriority() > modifier.getPriority()) {
// Keep old one
return;
} else if (obs.getPriority() < modifier.getPriority()) {
// Remove old one and add new one
removedObs.add(cUID);
}
} }
} }
// Remove nameModifiers.put(modifierId, modifier);
for (UUID ro : removedObs) {
nameModifiers.remove(ro);
}
nameModifiers.put(mod, modifier);
} }
/** /**

View File

@ -4,16 +4,17 @@ import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.util.message.Message; import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.type.ItemRestriction; import net.Indyuce.mmoitems.stat.type.ItemRestriction;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* Interface class between RPG core plugins like Heroes, MythicCore, SkillAPI * Interface class between RPG core plugins like
* and MMOItems * Heroes, MythicCore, SkillAPI, MMOCore and MMOItems
* *
* @author indyuce * @author Jules
*/ */
public abstract class RPGPlayer { public abstract class RPGPlayer {
private final PlayerData playerData; private final PlayerData playerData;
@ -106,7 +107,9 @@ public abstract class RPGPlayer {
* if it fails (returning true even if it is not met). * if it fails (returning true even if it is not met).
* @see ItemRestriction#isDynamic() * @see ItemRestriction#isDynamic()
*/ */
public boolean canUse(NBTItem item, boolean message, boolean allowDynamic) { public boolean canUse(@NotNull NBTItem item, boolean message, boolean allowDynamic) {
// Unidentification
if (item.hasTag("MMOITEMS_UNIDENTIFIED_ITEM")) { if (item.hasTag("MMOITEMS_UNIDENTIFIED_ITEM")) {
if (message) { if (message) {
Message.UNIDENTIFIED_ITEM.format(ChatColor.RED).send(player.getPlayer()); Message.UNIDENTIFIED_ITEM.format(ChatColor.RED).send(player.getPlayer());
@ -115,13 +118,13 @@ public abstract class RPGPlayer {
return false; return false;
} }
//REQ//MMOItems. Log("Checking REQS"); // Item has been disabled
for (ItemRestriction condition : MMOItems.plugin.getStats().getItemRestrictionStats()) if (MMOItems.plugin.getLanguage().disableRemovedItems && MMOUtils.hasBeenRemoved(item)) return false;
if (!condition.isDynamic() || !allowDynamic)
if (!condition.canUse(this, item, message)) // Stat-based requirements
return false; for (ItemRestriction condition : MMOItems.plugin.getStats().getItemRestrictionStats())
if (!condition.isDynamic() || !allowDynamic) if (!condition.canUse(this, item, message)) return false;
//REQ//MMOItems. Log(" \u00a7a> Success use");
return true; return true;
} }
} }

View File

@ -28,18 +28,13 @@ import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
/** /**
* A class to manage modification of items with reference to what they used to be * A class to manage modification of items with reference to what
* (and apparently also used to automatically apply SoulBounds): * they used to be. Updating/reforging refers to changing the base
* stats of a MMOItem instance to what the template currently has,
* usually keeping gem stones and upgrade level. This won't reroll
* RNG stats unless the specific option is toggled on.
* *
* <p><code><b>updating</b></code> refers to changing the base stats * @author Gunging, Jules
* of a MMOItem instance to what the template currently has, usually
* keeping gem stones and upgrade level. This wont reroll RNG stats.</p>
*
* <p><code><b>reforging</b></code> same thing as updating, but rerolling
* the RNG stats - basically transferring the data specified by the
* {@link ReforgeOptions} into a new item of the same Type-ID</p>
*
* @author Gunging
*/ */
public class MMOItemReforger { public class MMOItemReforger {
@ -361,42 +356,18 @@ public class MMOItemReforger {
public boolean reforge(@NotNull ReforgeOptions options, @Nullable RPGPlayer player) { public boolean reforge(@NotNull ReforgeOptions options, @Nullable RPGPlayer player) {
// Throw fail // Throw fail
if (!hasTemplate()) if (!hasTemplate()) return false;
return false;
// Prepare everything properly // Prepare everything properly
oldMMOItem = new LiveMMOItem(getNBTItem()); oldMMOItem = new LiveMMOItem(getNBTItem());
// Not blacklisted right!? // Not blacklisted right!?
if (options.isBlacklisted(getOldMMOItem().getId())) if (options.isBlacklisted(getOldMMOItem().getId())) return false;
return false;
this.player = player; this.player = player;
/*
* This chunk will determine the level the item was, and
* regenerate a new one based on that level ~ the "Item Level" which
*
*
* which I honestly don't know how to use I've just been
* copying and pasting this around, leaving this code untouched
* since I first started polishing the RevID workings.
*
* - gunging
*/
int iLevel = MMOItemReforger.defaultItemLevel;
// What level with the regenerated item will be hmmmm..... // What level with the regenerated item will be hmmmm.....
generationItemLevel = generationItemLevel = (getOldMMOItem().hasData(ItemStats.ITEM_LEVEL) ? (int) ((DoubleData) getOldMMOItem().getData(ItemStats.ITEM_LEVEL)).getValue() : 0);
// No default level specified?
(iLevel == -32767) ?
// Does the item have level?
(getOldMMOItem().hasData(ItemStats.ITEM_LEVEL) ? (int) ((DoubleData) getOldMMOItem().getData(ItemStats.ITEM_LEVEL)).getValue() : 0)
// Default level was specified, use that.
: iLevel;
// Identify tier. // Identify tier.
ItemTier tier = ItemTier tier =
@ -420,8 +391,7 @@ public class MMOItemReforger {
Bukkit.getPluginManager().callEvent(mmoREV); Bukkit.getPluginManager().callEvent(mmoREV);
// Cancelled? it ends there // Cancelled? it ends there
if (mmoREV.isCancelled()) if (mmoREV.isCancelled()) return false;
return false;
// Properly recalculate all based on histories // Properly recalculate all based on histories
for (StatHistory hist : getFreshMMOItem().getStatHistories()) for (StatHistory hist : getFreshMMOItem().getStatHistories())
@ -455,12 +425,10 @@ public class MMOItemReforger {
} }
//region Config Values //region Config Values
public static int defaultItemLevel = -32767;
public static boolean keepTiersWhenReroll = true; public static boolean keepTiersWhenReroll = true;
public static boolean gemstonesRevIDWhenUnsocket = false; public static boolean gemstonesRevIDWhenUnsocket = false;
public static void reload() { public static void reload() {
defaultItemLevel = MMOItems.plugin.getConfig().getInt("item-revision.default-item-level", -32767);
keepTiersWhenReroll = MMOItems.plugin.getConfig().getBoolean("item-revision.keep-tiers"); keepTiersWhenReroll = MMOItems.plugin.getConfig().getBoolean("item-revision.keep-tiers");
gemstonesRevIDWhenUnsocket = MMOItems.plugin.getConfig().getBoolean("item-revision.regenerate-gems-when-unsocketed", false); gemstonesRevIDWhenUnsocket = MMOItems.plugin.getConfig().getBoolean("item-revision.regenerate-gems-when-unsocketed", false);
} }

View File

@ -5,8 +5,6 @@ import net.Indyuce.mmoitems.api.item.build.MMOItemBuilder;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData; import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.random.UpdatableRandomStatData; import net.Indyuce.mmoitems.stat.data.random.UpdatableRandomStatData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.ItemStat; import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
@ -20,7 +18,7 @@ import java.util.Random;
* *
* @author indyuce * @author indyuce
*/ */
public class NumericStatFormula implements RandomStatData<DoubleData>, UpdatableRandomStatData { public class NumericStatFormula implements RandomStatData<DoubleData>, UpdatableRandomStatData<DoubleData> {
private final double base, scale, spread, maxSpread; private final double base, scale, spread, maxSpread;
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
@ -157,32 +155,21 @@ public class NumericStatFormula implements RandomStatData<DoubleData>, Updatable
* @param levelScalingFactor Level to scale the scale with * @param levelScalingFactor Level to scale the scale with
* @param random Result of <code>RANDOM.nextGaussian()</code> or whatever other * @param random Result of <code>RANDOM.nextGaussian()</code> or whatever other
* value that you actually want to pass. * value that you actually want to pass.
*
* @return The calculated value * @return The calculated value
*/ */
public double calculate(double levelScalingFactor, double random) { public double calculate(double levelScalingFactor, double random) {
if (useRelativeSpread) {
//SPRD//if (spread > 0) MMOItems.log("\u00a7c༺\u00a77 Using \u00a7eRelative\u00a77 spread formula: \u00a76μ=" + (base + scale * levelScalingFactor) + "\u00a77, \u00a73σ=" + (spread * (base + scale * levelScalingFactor) + "\u00a7b=" + spread + "×" + (base + scale * levelScalingFactor)) + " \u00a7c@" + random + "\u00a7e = " + (base + scale * levelScalingFactor) * (1 + Math.min(Math.max(random * spread, -maxSpread), maxSpread)));
return (base + scale * levelScalingFactor) * (1 + Math.min(Math.max(random * spread, -maxSpread), maxSpread));
}
// The mean, the center of the distribution // The mean, the center of the distribution
double actualBase = base + (scale * levelScalingFactor); final double actualBase = base + (scale * levelScalingFactor);
/* /*
* This is one pick from a gaussian distribution * This is one pick from a gaussian distribution at mean 0, and
* at mean 0, and standard deviation 1, multiplied * standard deviation 1, multiplied by the spread chosen.
* by the spread chosen. * Does it exceed the max spread (positive or negative)? Not anymore!
*/ */
double flatSpread = random * spread; final double spreadCoef = Math.min(Math.max(random * spread, -maxSpread), maxSpread);
// Does it exceed the max spread (positive or negative)? Not anymore! return useRelativeSpread ? actualBase * (1 + spreadCoef) : actualBase + spreadCoef;
flatSpread = Math.min(Math.max(flatSpread, -maxSpread), maxSpread);
// That's it
//SPRD//if (spread > 0) MMOItems.log("\u00a7c༺\u00a77 Using \u00a7aAdditive\u00a77 spread formula, \u00a76μ=" + (base + scale * levelScalingFactor) + "\u00a77, \u00a73σ=" + (spread) + " \u00a7c@" + random + "\u00a7e = " + (actualBase + gaussSpread));
return actualBase + flatSpread;
} }
@Override @Override
@ -235,43 +222,27 @@ public class NumericStatFormula implements RandomStatData<DoubleData>, Updatable
@NotNull @NotNull
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T extends StatData> T reroll(@NotNull ItemStat stat, @NotNull T original, int determinedItemLevel) { public DoubleData reroll(@NotNull ItemStat stat, @NotNull DoubleData original, int determinedItemLevel) {
//UPGRD//MMOItems.log("\u00a7a +\u00a77 Valid for Double Data procedure\u00a78 {Original:\u00a77 " + ((DoubleData) original).getValue() + "\u00a78}");
// Very well, chance checking is only available for NumericStatFormula class so // Very well, chance checking is only available for NumericStatFormula class
double scaledBase = getBase() + (getScale() * determinedItemLevel); final double expectedValue = getBase() + (getScale() * determinedItemLevel);
final double previousValue = original.getValue();
// Determine current final double shift = previousValue - expectedValue;
double current = ((DoubleData) original).getValue(); final double shiftSD = useRelativeSpread ? Math.abs(shift / (getSpread() * expectedValue)) : Math.abs(shift / getSpread());
final double maxSD = getMaxSpread() / getSpread();
// What was the shift?
double shift = current - scaledBase;
// How many standard deviations away?
double sD = Math.abs(shift / getSpread());
if (useRelativeSpread) { sD = Math.abs(shift / (getSpread() * scaledBase)); }
//UPGRD//MMOItems.log("\u00a7b *\u00a77 Base: \u00a73" + base);
//UPGRD//MMOItems.log("\u00a7b *\u00a77 Curr: \u00a73" + current);
//UPGRD//MMOItems.log("\u00a7b *\u00a77 Shft: \u00a73" + shift);
//UPGRD//MMOItems.log("\u00a7b *\u00a77 SDev: \u00a73" + sD);
// Greater than max spread? Or heck, 0.1% Chance or less wth // Greater than max spread? Or heck, 0.1% Chance or less wth
if (sD > getMaxSpread() || sD > 3.5) { if (shiftSD > maxSD || shiftSD > 3.5) {
//UPGRD//MMOItems.log("\u00a7c -\u00a77 Ridiculous Range --- reroll");
// Adapt within reason // Just fully reroll value
double reasonableShift = getSpread() * Math.min(2, getMaxSpread()); return new DoubleData(calculate(determinedItemLevel));
if (shift < 0) { reasonableShift *= -1;}
// That's the data we'll use
return (T) new DoubleData(reasonableShift + scaledBase);
// Data arguably fine tbh, just use previous // Data arguably fine tbh, just use previous
} else { } else {
//UPGRD//MMOItems.log("\u00a7a +\u00a77 Acceptable Range --- kept"); //UPGRD//MMOItems.log("\u00a7a +\u00a77 Acceptable Range --- kept");
// Just clone I guess // Just clone I guess
return (T) ((Mergeable) original).cloneData(); } return original.cloneData(); }
} }
public enum FormulaSaveOption { public enum FormulaSaveOption {

View File

@ -13,7 +13,7 @@ public class GenerateCommandHandler {
public boolean hasArgument(String key) { public boolean hasArgument(String key) {
for (String argument : arguments) for (String argument : arguments)
if (argument.startsWith("-" + key + ":")) if (argument.startsWith("-" + key))
return true; return true;
return false; return false;
} }

View File

@ -49,29 +49,29 @@ public class GenerateCommandTreeNode extends CommandTreeNode {
Validate.notNull(give, "You cannot use -gimme"); Validate.notNull(give, "You cannot use -gimme");
final RPGPlayer rpgPlayer = PlayerData.get(target).getRPG(); final RPGPlayer rpgPlayer = PlayerData.get(target).getRPG();
final int itemLevel = handler.hasArgument("level") ? Integer.parseInt(handler.getValue("level")) final int itemLevel = handler.hasArgument("level:") ? Integer.parseInt(handler.getValue("level"))
: (handler.hasArgument("matchlevel") ? MMOItems.plugin.getTemplates().rollLevel(rpgPlayer.getLevel()) : 1 + random.nextInt(100)); : (handler.hasArgument("matchlevel") ? MMOItems.plugin.getTemplates().rollLevel(rpgPlayer.getLevel()) : 1 + random.nextInt(100));
final @Nullable ItemTier itemTier = handler.hasArgument("tierset") ? null : handler.hasArgument("tier") final @Nullable ItemTier itemTier = handler.hasArgument("tierset") ? null : handler.hasArgument("tier:")
? MMOItems.plugin.getTiers().getOrThrow(handler.getValue("tier").toUpperCase().replace("-", "_")) ? MMOItems.plugin.getTiers().getOrThrow(handler.getValue("tier").toUpperCase().replace("-", "_"))
: MMOItems.plugin.getTemplates().rollTier(); : MMOItems.plugin.getTemplates().rollTier();
final TemplateExplorer builder = new TemplateExplorer(); final TemplateExplorer builder = new TemplateExplorer();
if (handler.hasArgument("matchclass")) if (handler.hasArgument("matchclass"))
builder.applyFilter(new ClassFilter(rpgPlayer)); builder.applyFilter(new ClassFilter(rpgPlayer));
if (handler.hasArgument("class")) if (handler.hasArgument("class:"))
builder.applyFilter(new ClassFilter(handler.getValue("class").replace("-", " ").replace("_", " "))); builder.applyFilter(new ClassFilter(handler.getValue("class").replace("-", " ").replace("_", " ")));
String type = null; String type = null;
if (handler.hasArgument("tierset")) { if (handler.hasArgument("tierset:")) {
String format = UtilityMethods.enumName(handler.getValue("tierset")); String format = UtilityMethods.enumName(handler.getValue("tierset"));
Validate.isTrue(MMOItems.plugin.getTiers().has(format), "Could not find tier with ID '" + format + "'"); Validate.isTrue(MMOItems.plugin.getTiers().has(format), "Could not find tier with ID '" + format + "'");
builder.applyFilter(new TierFilter(format)); builder.applyFilter(new TierFilter(format));
} }
if (handler.hasArgument("type")) { if (handler.hasArgument("type:")) {
type = handler.getValue("type"); type = handler.getValue("type");
Validate.isTrue(Type.isValid(type), "Could not find type with ID '" + type + "'"); Validate.isTrue(Type.isValid(type), "Could not find type with ID '" + type + "'");
builder.applyFilter(new TypeFilter(Type.get(type))); builder.applyFilter(new TypeFilter(Type.get(type)));
} }
if (handler.hasArgument("id")) { if (handler.hasArgument("id:")) {
Validate.isTrue(type != null, "You have to specify a type if using the id option!"); Validate.isTrue(type != null, "You have to specify a type if using the id option!");
builder.applyFilter(new IDFilter(handler.getValue("id"))); builder.applyFilter(new IDFilter(handler.getValue("id")));
} }

View File

@ -80,7 +80,7 @@ public class ArrowParticlesEdition extends EditionInventory {
if (particle != null) { if (particle != null) {
ConfigurationSection section = getEditedSection().getConfigurationSection("arrow-particles"); ConfigurationSection section = getEditedSection().getConfigurationSection("arrow-particles");
if (ParticleData.isColorable(particle)) { if (MMOUtils.isColorable(particle)) {
int red = section.getInt("color.red"); int red = section.getInt("color.red");
int green = section.getInt("color.green"); int green = section.getInt("color.green");
int blue = section.getInt("color.blue"); int blue = section.getInt("color.blue");

View File

@ -6,7 +6,6 @@ import net.Indyuce.mmoitems.util.MMOUtils;
import net.Indyuce.mmoitems.api.edition.StatEdition; import net.Indyuce.mmoitems.api.edition.StatEdition;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.particle.api.ParticleType; import net.Indyuce.mmoitems.particle.api.ParticleType;
import net.Indyuce.mmoitems.stat.data.ParticleData;
import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.util.AltChar; import io.lumine.mythic.lib.api.util.AltChar;
@ -103,7 +102,7 @@ public class ParticlesEdition extends EditionInventory {
} }
} }
if (ParticleData.isColorable(particle)) { if (MMOUtils.isColorable(particle)) {
int red = getEditedSection().getInt("item-particles.color.red"); int red = getEditedSection().getInt("item-particles.color.red");
int green = getEditedSection().getInt("item-particles.color.green"); int green = getEditedSection().getInt("item-particles.color.green");
int blue = getEditedSection().getInt("item-particles.color.blue"); int blue = getEditedSection().getInt("item-particles.color.blue");

View File

@ -42,7 +42,7 @@ public class ConfigManager implements Reloadable {
private final Map<PotionEffectType, String> potionNames = new HashMap<>(); private final Map<PotionEffectType, String> potionNames = new HashMap<>();
// Cached config options // Cached config options
public boolean replaceMushroomDrops, worldGenEnabled, upgradeRequirementsCheck, keepSoulboundOnDeath, rerollOnItemUpdate, opStatsEnabled; public boolean replaceMushroomDrops, worldGenEnabled, upgradeRequirementsCheck, keepSoulboundOnDeath, rerollOnItemUpdate, opStatsEnabled, disableRemovedItems;
public String abilitySplitter; public String abilitySplitter;
public double soulboundBaseDamage, soulboundPerLvlDamage, levelSpread; public double soulboundBaseDamage, soulboundPerLvlDamage, levelSpread;
public NumericStatFormula defaultItemCapacity; public NumericStatFormula defaultItemCapacity;
@ -174,6 +174,7 @@ public class ConfigManager implements Reloadable {
keepSoulboundOnDeath = MMOItems.plugin.getConfig().getBoolean("soulbound.keep-on-death"); keepSoulboundOnDeath = MMOItems.plugin.getConfig().getBoolean("soulbound.keep-on-death");
rerollOnItemUpdate = MMOItems.plugin.getConfig().getBoolean("item-revision.reroll-when-updated"); rerollOnItemUpdate = MMOItems.plugin.getConfig().getBoolean("item-revision.reroll-when-updated");
levelSpread = MMOItems.plugin.getConfig().getDouble("item-level-spread"); levelSpread = MMOItems.plugin.getConfig().getDouble("item-level-spread");
disableRemovedItems = MMOItems.plugin.getConfig().getBoolean("disable-removed-items");
opStatsEnabled = MMOItems.plugin.getConfig().getBoolean("op-item-stats.enabled"); opStatsEnabled = MMOItems.plugin.getConfig().getBoolean("op-item-stats.enabled");
opStats.clear(); opStats.clear();

View File

@ -64,9 +64,9 @@ public class Abilities extends ItemStat<RandomAbilityListData, AbilityListData>
for (String modifier : ability.getModifiers()) { for (String modifier : ability.getModifiers()) {
item.getLore().registerPlaceholder("ability_" + ability.getAbility().getHandler().getId().toLowerCase() + "_" + modifier, item.getLore().registerPlaceholder("ability_" + ability.getAbility().getHandler().getId().toLowerCase() + "_" + modifier,
MythicLib.plugin.getMMOConfig().decimal.format(ability.getModifier(modifier))); MythicLib.plugin.getMMOConfig().decimals.format(ability.getModifier(modifier)));
abilityLore.add(modifierFormat.replace("{modifier}", ability.getAbility().getModifierName(modifier)).replace("{value}", abilityLore.add(modifierFormat.replace("{modifier}", ability.getAbility().getModifierName(modifier)).replace("{value}",
MythicLib.plugin.getMMOConfig().decimal.format(ability.getModifier(modifier)))); MythicLib.plugin.getMMOConfig().decimals.format(ability.getModifier(modifier))));
} }
if (splitter) if (splitter)

View File

@ -48,7 +48,7 @@ public class ArrowParticles extends ItemStat<ArrowParticlesData, ArrowParticlesD
int amount = config.getInt("amount"); int amount = config.getInt("amount");
double offset = config.getDouble("offset"); double offset = config.getDouble("offset");
return ParticleData.isColorable(particle) return MMOUtils.isColorable(particle)
? new ArrowParticlesData(particle, amount, offset, config.getInt("color.red"), config.getInt("color.green"), ? new ArrowParticlesData(particle, amount, offset, config.getInt("color.red"), config.getInt("color.green"),
config.getInt("color.blue")) config.getInt("color.blue"))
: new ArrowParticlesData(particle, amount, offset, config.getDouble("speed")); : new ArrowParticlesData(particle, amount, offset, config.getDouble("speed"));
@ -105,7 +105,7 @@ public class ArrowParticles extends ItemStat<ArrowParticlesData, ArrowParticlesD
double offset = json.get("Offset").getAsDouble(); double offset = json.get("Offset").getAsDouble();
// Ist it colorable' // Ist it colorable'
if (ParticleData.isColorable(particle)) { if (MMOUtils.isColorable(particle)) {
// Return as colourable // Return as colourable
return new ArrowParticlesData(particle, amount, offset, json.get("Red").getAsInt(), json.get("Green").getAsInt(), json.get("Blue").getAsInt()); return new ArrowParticlesData(particle, amount, offset, json.get("Red").getAsInt(), json.get("Green").getAsInt(), json.get("Blue").getAsInt());
@ -187,7 +187,7 @@ public class ArrowParticles extends ItemStat<ArrowParticlesData, ArrowParticlesD
lore.add(ChatColor.GRAY + "* Offset: " + ChatColor.WHITE + cast.getOffset()); lore.add(ChatColor.GRAY + "* Offset: " + ChatColor.WHITE + cast.getOffset());
lore.add(""); lore.add("");
if (ParticleData.isColorable(cast.getParticle())) if (MMOUtils.isColorable(cast.getParticle()))
lore.add(ChatColor.translateAlternateColorCodes('&', lore.add(ChatColor.translateAlternateColorCodes('&',
"&7* Color: &c&l" + cast.getRed() + "&7 - &a&l" + cast.getGreen() + "&7 - &9&l" + cast.getBlue())); "&7* Color: &c&l" + cast.getRed() + "&7 - &a&l" + cast.getGreen() + "&7 - &9&l" + cast.getBlue()));
else else

View File

@ -7,6 +7,7 @@ import io.lumine.mythic.lib.api.util.SmartGive;
import io.lumine.mythic.lib.version.VersionMaterial; import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmoitems.ItemStats; import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.interaction.ItemSkin;
import net.Indyuce.mmoitems.util.MMOUtils; import net.Indyuce.mmoitems.util.MMOUtils;
import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.interaction.Consumable; import net.Indyuce.mmoitems.api.interaction.Consumable;
@ -38,15 +39,14 @@ public class CanDeskin extends BooleanStat implements ConsumableItemInteraction
@Override @Override
public boolean handleConsumableEffect(@NotNull InventoryClickEvent event, @NotNull PlayerData playerData, @NotNull Consumable consumable, @NotNull NBTItem target, Type targetType) { public boolean handleConsumableEffect(@NotNull InventoryClickEvent event, @NotNull PlayerData playerData, @NotNull Consumable consumable, @NotNull NBTItem target, Type targetType) {
final String skinId = target.getString("MMOITEMS_SKIN_ID"); final String skinId = target.getString(ItemSkin.SKIN_ID_TAG);
Player player = playerData.getPlayer(); Player player = playerData.getPlayer();
if (consumable.getNBTItem().getBoolean("MMOITEMS_CAN_DESKIN") && !skinId.isEmpty()) { if (consumable.getNBTItem().getBoolean("MMOITEMS_CAN_DESKIN") && !skinId.isEmpty()) {
// Set target item to default skin // Set target item to default skin
String targetItemId = target.getString("MMOITEMS_ITEM_ID"); String targetItemId = target.getString("MMOITEMS_ITEM_ID");
target.removeTag("MMOITEMS_HAS_SKIN"); target.removeTag(ItemSkin.SKIN_ID_TAG);
target.removeTag("MMOITEMS_SKIN_ID");
MMOItemTemplate targetTemplate = MMOItems.plugin.getTemplates().getTemplateOrThrow(targetType, targetItemId); MMOItemTemplate targetTemplate = MMOItems.plugin.getTemplates().getTemplateOrThrow(targetType, targetItemId);
MMOItem originalMmoitem = targetTemplate.newBuilder(playerData.getRPG()).build(); MMOItem originalMmoitem = targetTemplate.newBuilder(playerData.getRPG()).build();

View File

@ -6,12 +6,12 @@ import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.util.NumericStatFormula; import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.gui.edition.EditionInventory; import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.InternalStat; import net.Indyuce.mmoitems.stat.type.InternalStat;
import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.version.VersionMaterial; import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmoitems.stat.type.ItemStat; import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -31,23 +31,22 @@ public class ItemLevel extends ItemStat<NumericStatFormula, DoubleData> implemen
@Nullable @Nullable
@Override @Override
public NumericStatFormula whenInitialized(Object object) { public NumericStatFormula whenInitialized(Object object) {
// not supported throw new NotImplementedException();
return null;
} }
@Override @Override
public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) { public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) {
// not supported throw new NotImplementedException();
} }
@Override @Override
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) { public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {
// not supported throw new NotImplementedException();
} }
@Override @Override
public void whenDisplayed(List<String> lore, Optional<NumericStatFormula> statData) { public void whenDisplayed(List<String> lore, Optional<NumericStatFormula> statData) {
// not supported throw new NotImplementedException();
} }
@NotNull @NotNull

View File

@ -4,10 +4,8 @@ import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.gui.edition.EditionInventory; import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.data.StoredTagsData; import net.Indyuce.mmoitems.stat.data.StoredTagsData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData; import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.GemStoneStat; import net.Indyuce.mmoitems.stat.type.GemStoneStat;
import net.Indyuce.mmoitems.stat.type.InternalStat; import net.Indyuce.mmoitems.stat.type.InternalStat;
import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.ItemTag;
@ -22,6 +20,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
/**
* This is another fictive item stat that makes sure that all
* the NBT tags from the previous item are transferred towards
* the new item.
*/
public class StoredTags extends ItemStat<RandomStatData<StoredTagsData>, StoredTagsData> implements InternalStat, GemStoneStat { public class StoredTags extends ItemStat<RandomStatData<StoredTagsData>, StoredTagsData> implements InternalStat, GemStoneStat {
public StoredTags() { public StoredTags() {
super("STORED_TAGS", VersionMaterial.OAK_SIGN.toMaterial(), "Stored Tags", super("STORED_TAGS", VersionMaterial.OAK_SIGN.toMaterial(), "Stored Tags",

View File

@ -25,9 +25,16 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
/**
* This is not really a stat data since abilities are always
* stored as lists inside of items. This is more of a commonly
* used utility class which defines the castable implementation
* of the {@link Skill} interface for MMOItems.
*/
public class AbilityData extends Skill { public class AbilityData extends Skill {
private final RegisteredSkill ability; private final RegisteredSkill ability;
@NotNull private final Map<String, Double> modifiers = new HashMap<>(); @NotNull
private final Map<String, Double> modifiers = new HashMap<>();
public AbilityData(@NotNull JsonObject object) { public AbilityData(@NotNull JsonObject object) {
super(MMOUtils.backwardsCompatibleTriggerType(object.get("CastMode").getAsString())); super(MMOUtils.backwardsCompatibleTriggerType(object.get("CastMode").getAsString()));
@ -91,16 +98,13 @@ public class AbilityData extends Skill {
String barChar = MMOItems.plugin.getConfig().getString("cooldown-progress-bar-char"); String barChar = MMOItems.plugin.getConfig().getString("cooldown-progress-bar-char");
for (int j = 0; j < 10; j++) for (int j = 0; j < 10; j++)
progressBar.append(progress >= j ? ChatColor.GREEN : ChatColor.WHITE).append(barChar); progressBar.append(progress >= j ? ChatColor.GREEN : ChatColor.WHITE).append(barChar);
Message.SPELL_ON_COOLDOWN.format(ChatColor.RED, "#left#", MythicLib.plugin.getMMOConfig().decimal.format(info.getRemaining() / 1000d), "#progress#", Message.SPELL_ON_COOLDOWN.format(ChatColor.RED, "#left#", MythicLib.plugin.getMMOConfig().decimal.format(info.getRemaining() / 1000d), "#progress#", progressBar.toString(), "#s#", (info.getRemaining() > 1999 ? "s" : "")).send(player);
progressBar.toString(), "#s#", (info.getRemaining() > 1999 ? "s" : "")).send(player);
} }
return false; return false;
} }
// Check for permission // Check for permission
if (MMOItems.plugin.getConfig().getBoolean("permissions.abilities") if (MMOItems.plugin.getConfig().getBoolean("permissions.abilities") && !player.hasPermission("mmoitems.ability." + getHandler().getLowerCaseId()) && !player.hasPermission("mmoitems.bypass.ability"))
&& !player.hasPermission("mmoitems.ability." + getHandler().getLowerCaseId())
&& !player.hasPermission("mmoitems.bypass.ability"))
return false; return false;
// Check for mana cost // Check for mana cost
@ -124,17 +128,14 @@ public class AbilityData extends Skill {
RPGPlayer rpgPlayer = playerData.getRPG(); RPGPlayer rpgPlayer = playerData.getRPG();
// Apply mana cost // Apply mana cost
if (hasModifier("mana")) if (hasModifier("mana")) rpgPlayer.giveMana(-meta.getModifier("mana"));
rpgPlayer.giveMana(-meta.getModifier("mana"));
// Apply stamina cost // Apply stamina cost
if (hasModifier("stamina")) if (hasModifier("stamina")) rpgPlayer.giveStamina(-meta.getModifier("stamina"));
rpgPlayer.giveStamina(-meta.getModifier("stamina"));
// Apply cooldown // Apply cooldown
double cooldown = meta.getModifier("cooldown") * (1 - Math.min(.8, meta.getCaster().getStat("COOLDOWN_REDUCTION") / 100)); double cooldown = meta.getModifier("cooldown") * (1 - Math.min(.8, meta.getCaster().getStat("COOLDOWN_REDUCTION") / 100));
if (cooldown > 0) if (cooldown > 0) meta.getCaster().getData().getCooldownMap().applyCooldown(this, cooldown);
meta.getCaster().getData().getCooldownMap().applyCooldown(this, cooldown);
} }
@Override @Override

View File

@ -72,6 +72,11 @@ public class ArrowParticlesData implements StatData, RandomStatData<ArrowParticl
return blue; return blue;
} }
@Override
public boolean isEmpty() {
return false;
}
@Override @Override
public String toString() { public String toString() {
JsonObject object = new JsonObject(); JsonObject object = new JsonObject();

View File

@ -45,6 +45,11 @@ public class ColorData implements StatData, RandomStatData<ColorData> {
return Color.fromRGB(red, green, blue); return Color.fromRGB(red, green, blue);
} }
@Override
public boolean isEmpty() {
return false;
}
@Override @Override
public String toString() { public String toString() {
return "{Red=" + red + ",Green=" + green + ",Blue=" + blue + "}"; return "{Red=" + red + ",Green=" + green + ",Blue=" + blue + "}";

View File

@ -11,7 +11,8 @@ import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class CommandListData implements StatData, Mergeable, RandomStatData<CommandListData> { public class CommandListData implements StatData, Mergeable, RandomStatData<CommandListData> {
@NotNull private final List<CommandData> commands; @NotNull
private final List<CommandData> commands;
public CommandListData(@NotNull List<CommandData> commands) { public CommandListData(@NotNull List<CommandData> commands) {
this.commands = commands; this.commands = commands;
@ -28,7 +29,8 @@ public class CommandListData implements StatData, Mergeable, RandomStatData<Comm
this.commands.add(data); this.commands.add(data);
} }
@NotNull public List<CommandData> getCommands() { @NotNull
public List<CommandData> getCommands() {
return commands; return commands;
} }

View File

@ -129,8 +129,8 @@ public class GemSocketsData implements StatData, Mergeable<GemSocketsData>, Rand
public void merge(GemSocketsData data) { public void merge(GemSocketsData data) {
// Combine both actual gems, and empty slots // Combine both actual gems, and empty slots
emptySlots.addAll(((GemSocketsData) data).emptySlots); emptySlots.addAll(data.emptySlots);
gems.addAll(((GemSocketsData) data).getGemstones()); gems.addAll(data.gems);
} }
@Override @Override

View File

@ -28,6 +28,11 @@ public class MaterialData implements StatData, RandomStatData<MaterialData> {
return material; return material;
} }
@Override
public boolean isEmpty() {
return false;
}
@Override @Override
public MaterialData randomize(MMOItemBuilder builder) { public MaterialData randomize(MMOItemBuilder builder) {
return this; return this;

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.Location; import org.bukkit.Location;
@ -119,7 +120,7 @@ public class ParticleData implements StatData, RandomStatData<ParticleData> {
object.addProperty("Particle", getParticle().name()); object.addProperty("Particle", getParticle().name());
object.addProperty("Type", getType().name()); object.addProperty("Type", getType().name());
if (isColorable(particle)) { if (MMOUtils.isColorable(particle)) {
JsonObject color = new JsonObject(); JsonObject color = new JsonObject();
color.addProperty("Red", getColor().getRed()); color.addProperty("Red", getColor().getRed());
color.addProperty("Green", getColor().getGreen()); color.addProperty("Green", getColor().getGreen());
@ -133,10 +134,9 @@ public class ParticleData implements StatData, RandomStatData<ParticleData> {
return object; return object;
} }
// TODO Allow Note to be colored and allow BLOCK_DUST/ITEM_DUST to pick a block/item. @Override
public static boolean isColorable(Particle particle) { public boolean isEmpty() {
// || particle == Particle.NOTE return false;
return particle == Particle.REDSTONE || particle == Particle.SPELL_MOB || particle == Particle.SPELL_MOB_AMBIENT;
} }
@Override @Override

View File

@ -2,13 +2,10 @@ package net.Indyuce.mmoitems.stat.data;
import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.interaction.ItemSkin;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.stat.data.type.Mergeable; import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -17,50 +14,33 @@ import java.util.List;
public class StoredTagsData implements StatData, Mergeable<StoredTagsData> { public class StoredTagsData implements StatData, Mergeable<StoredTagsData> {
private final List<ItemTag> tags = new ArrayList<>(); private final List<ItemTag> tags = new ArrayList<>();
private static final List<String> ignoreList = Arrays.asList("Unbreakable", "BlockEntityTag", "display", "Enchantments", "HideFlags", "Damage", private static final List<String> IGNORED_TAGS = Arrays.asList(
"Unbreakable", "BlockEntityTag", "display", "Enchantments", "HideFlags", "Damage",
"AttributeModifiers", "SkullOwner", "CanDestroy", "PickupDelay", "Age"); "AttributeModifiers", "SkullOwner", "CanDestroy", "PickupDelay", "Age");
@Deprecated
private static final List<String> SAVED_MMOITEMS_TAGS = Arrays.asList(
"MMOITEMS_SKIN_ID");
@Deprecated @Deprecated
public StoredTagsData(ItemStack stack) { public StoredTagsData(ItemStack stack) {
this(NBTItem.get(stack)); this(NBTItem.get(stack));
} }
public StoredTagsData(List<ItemTag> tgs) { tags.addAll(tgs); } public StoredTagsData(List<ItemTag> tgs) { tags.addAll(tgs); }
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StoredTagsData)) { return false; }
if (((StoredTagsData) obj).getTags().size() != getTags().size()) { return false; }
for (ItemTag tag : ((StoredTagsData) obj).getTags()) {
if (tag == null) { continue; }
boolean unmatched = true;
for (ItemTag tg : getTags()) {
if (tag.equals(tg)) { unmatched = false; break; } }
if (unmatched) { return false; } }
return true;
}
public StoredTagsData(NBTItem nbt) { public StoredTagsData(NBTItem nbt) {
for (String tag : nbt.getTags()) { for (String tag : nbt.getTags()) {
// Usually ignore mmoitems // Usually ignore mmoitems
if (tag.startsWith("MMOITEMS_")) { if (tag.startsWith("MMOITEMS_") && !SAVED_MMOITEMS_TAGS.contains(tag))
// Do not delete the skin tags (save them here)
if (!ItemSkin.HAS_SKIN_TAG.equals(tag) && !ItemSkin.SKIN_ID_TAG.equals(tag)) {
// Not either of the skin tags, skip this.
// Must be handled by its respective stat. // Must be handled by its respective stat.
continue; continue;
}
}
// Any vanilla or MMOItem tag should be ignored as those are // Any vanilla or MMOItem tag should be ignored as those are
// automatically handled. Same for the History stat ones. // automatically handled. Same for the History stat ones.
if (ignoreList.contains(tag) || tag.startsWith(ItemStackBuilder.history_keyword)) if (IGNORED_TAGS.contains(tag) || tag.startsWith(ItemStackBuilder.history_keyword))
continue; continue;
// As more methods are added we can add more types here // As more methods are added we can add more types here
@ -134,4 +114,21 @@ public class StoredTagsData implements StatData, Mergeable<StoredTagsData> {
public boolean isEmpty() { public boolean isEmpty() {
return tags.isEmpty(); return tags.isEmpty();
} }
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StoredTagsData)) { return false; }
if (((StoredTagsData) obj).getTags().size() != getTags().size()) { return false; }
for (ItemTag tag : ((StoredTagsData) obj).getTags()) {
if (tag == null) { continue; }
boolean unmatched = true;
for (ItemTag tg : getTags()) {
if (tag.equals(tg)) { unmatched = false; break; } }
if (unmatched) { return false; } }
return true;
}
} }

View File

@ -5,7 +5,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers; import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import org.apache.commons.lang.Validate;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
@ -13,12 +12,11 @@ import net.Indyuce.mmoitems.api.item.build.MMOItemBuilder;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData; import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable; import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.bouncycastle.util.StringList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class StringListData implements StatData, RandomStatData<StringListData>, Mergeable<StringListData> { public class StringListData implements StatData, RandomStatData<StringListData>, Mergeable<StringListData> {
@NotNull private List<String> list; @NotNull private final List<String> list;
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
@ -80,34 +78,12 @@ public class StringListData implements StatData, RandomStatData<StringListData>,
/** /**
* @param str Entry to remove * @param str Entry to remove
*
* @return If the value was actually removed. If it wasn't there * @return If the value was actually removed. If it wasn't there
* in the first place, this will return false. * in the first place, this will return false.
*
* @deprecated Deprecated * @deprecated Deprecated
*/ */
@Deprecated @Deprecated
public boolean remove(@Nullable String str) { public boolean remove(@Nullable String str) {
if (!list.contains(str)) { return false; }
if (removeGuarantee) {
// Remove that sh
return list.remove(str);
} else {
// OK
try {
return list.remove(str);
} catch (UnsupportedOperationException ignored) {
list = new ArrayList<>(list);
removeGuarantee = true;
return list.remove(str); return list.remove(str);
} }
}
}
boolean removeGuarantee = false;
} }

View File

@ -17,7 +17,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class RandomElementListData implements RandomStatData<ElementListData>, UpdatableRandomStatData { public class RandomElementListData implements RandomStatData<ElementListData>, UpdatableRandomStatData<ElementListData> {
private final Map<Pair<Element, ElementStatType>, NumericStatFormula> stats = new LinkedHashMap<>(); private final Map<Pair<Element, ElementStatType>, NumericStatFormula> stats = new LinkedHashMap<>();
public RandomElementListData(ConfigurationSection config) { public RandomElementListData(ConfigurationSection config) {
@ -58,7 +58,7 @@ public class RandomElementListData implements RandomStatData<ElementListData>, U
@NotNull @NotNull
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends StatData> T reroll(@NotNull ItemStat stat, @NotNull T original, int determinedItemLevel) { public ElementListData reroll(@NotNull ItemStat stat, @NotNull ElementListData original, int determinedItemLevel) {
// Start brand new // Start brand new
ElementListData elements = new ElementListData(); ElementListData elements = new ElementListData();
@ -80,6 +80,6 @@ public class RandomElementListData implements RandomStatData<ElementListData>, U
} }
// THats it // THats it
return (T) elements; return elements;
} }
} }

View File

@ -10,33 +10,30 @@ import org.jetbrains.annotations.NotNull;
* looking into Random Stat Data, because one wouldn't want to reroll a good * looking into Random Stat Data, because one wouldn't want to reroll a good
* roll in a player's item... unless this roll was unobtainable now, perhaps * roll in a player's item... unless this roll was unobtainable now, perhaps
* the reason the item is getting updated is to fix that roll being too good... * the reason the item is getting updated is to fix that roll being too good...
* * <p>
* Example of unobtainable data: the current numeric stat value is now out * Example of unobtainable data: the current numeric stat value is now out
* of the numeric formula bounds (defined by max-spread). * of the numeric formula bounds (defined by max-spread).
* * <p>
* This interface will tell the {@link net.Indyuce.mmoitems.api.util.MMOItemReforger} * This interface will tell the {@link net.Indyuce.mmoitems.api.util.MMOItemReforger}
* if the current roll may be kept, or it is too extreme (under the updated metrics) * if the current roll may be kept, or it is too extreme (under the updated metrics)
* to be considered 'obtainable' and thus must be removed. * to be considered 'obtainable' and thus must be removed.
* * <p>
* If a RandomStatData does not implement this, it will never be kept when * If a RandomStatData does not implement this, it will never be kept when
* updating items (always be rerolled with latest settings). * updating items (always be rerolled with latest settings).
* *
* @author Gunging * @author Gunging
*/ */
@FunctionalInterface @FunctionalInterface
public interface UpdatableRandomStatData { public interface UpdatableRandomStatData<S extends StatData> {
/** /**
*
* @param stat In case its relevant, the stat this Stat Data is linked to * @param stat In case its relevant, the stat this Stat Data is linked to
*
* @param original The StatData currently in the item being reforged. * @param original The StatData currently in the item being reforged.
*
* @param determinedItemLevel The level of the item * @param determinedItemLevel The level of the item
*
* @return The rerolled StatData if the original is unreasonable. * @return The rerolled StatData if the original is unreasonable.
* <br><br> * <br><br>
* If the original is reasonable, a clone of it, probably using {@link Mergeable#cloneData()} * If the original is reasonable, a clone of it, probably using {@link Mergeable#cloneData()}
*/ */
@NotNull <T extends StatData> T reroll(@NotNull ItemStat stat, @NotNull T original, int determinedItemLevel); @NotNull
S reroll(@NotNull ItemStat stat, @NotNull S original, int determinedItemLevel);
} }

View File

@ -11,10 +11,7 @@ import io.lumine.mythic.lib.skill.trigger.TriggerType;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.Type;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.bukkit.ChatColor; import org.bukkit.*;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
@ -33,11 +30,33 @@ import java.util.*;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class MMOUtils { public class MMOUtils {
public MMOUtils() { public MMOUtils() {
throw new IllegalArgumentException("This class cannot be instantiated."); throw new IllegalArgumentException("This class cannot be instantiated.");
} }
public static boolean isColorable(Particle particle) {
return particle.getDataType() == Particle.DustOptions.class;
}
/**
* Should cancel interaction if one of the two cases:
* - the item type no longer exists
* - no template with the given (type, id) pair can be found
*
* @param item Target item
* @return If the item USED to exist, but no longer does
*/
public static boolean hasBeenRemoved(@NotNull NBTItem item) {
if (!item.hasType()) return false;
final @Nullable String type = item.getType();
return MMOUtils.isNonEmpty(type) && (!Type.isValid(type) || !MMOItems.plugin.getTemplates().hasTemplate(Type.get(type), item.getString("MMOITEMS_ITEM_ID")));
}
public static boolean isNonEmpty(@Nullable String str) {
return str != null && !str.isEmpty();
}
/** /**
* @return The skull texture URL from a given player head * @return The skull texture URL from a given player head
*/ */

View File

@ -28,6 +28,7 @@ public class MMOItemsBukkit {
if (plugin.getConfig().getBoolean("dropped-items.tier-glow") || plugin.getConfig().getBoolean("dropped-items.hints")) if (plugin.getConfig().getBoolean("dropped-items.tier-glow") || plugin.getConfig().getBoolean("dropped-items.hints"))
Bukkit.getPluginManager().registerEvents(new DroppedItems(plugin.getConfig().getConfigurationSection("dropped-items")), plugin); Bukkit.getPluginManager().registerEvents(new DroppedItems(plugin.getConfig().getConfigurationSection("dropped-items")), plugin);
if (plugin.getLanguage().disableRemovedItems)
Bukkit.getPluginManager().registerEvents(new DisabledItemsListener(plugin), plugin); Bukkit.getPluginManager().registerEvents(new DisabledItemsListener(plugin), plugin);
Bukkit.getScheduler().runTaskTimer(plugin, () -> Bukkit.getOnlinePlayers().forEach(player -> PlayerData.get(player).updateStats()), 100, 20); Bukkit.getScheduler().runTaskTimer(plugin, () -> Bukkit.getOnlinePlayers().forEach(player -> PlayerData.get(player).updateStats()), 100, 20);

View File

@ -6,6 +6,7 @@ import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.damage.MeleeAttackMetadata; import io.lumine.mythic.lib.damage.MeleeAttackMetadata;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.entity.Arrow; import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
@ -21,6 +22,8 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* mmoitems * mmoitems
@ -41,7 +44,7 @@ public class DisabledItemsListener implements Listener {
if (!event.hasItem()) if (!event.hasItem())
return; return;
NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getItem()); NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getItem());
if (this.shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
@ -51,7 +54,7 @@ public class DisabledItemsListener implements Listener {
return; return;
ItemStack weaponUsed = event.getPlayer().getInventory().getItem(((MeleeAttackMetadata) event.getAttack()).getHand().toBukkit()); ItemStack weaponUsed = event.getPlayer().getInventory().getItem(((MeleeAttackMetadata) event.getAttack()).getHand().toBukkit());
NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(weaponUsed); NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(weaponUsed);
if (this.shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
@ -62,7 +65,7 @@ public class DisabledItemsListener implements Listener {
return; return;
final NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(player.getInventory().getItemInMainHand()); final NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(player.getInventory().getItemInMainHand());
if (this.shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
@ -73,7 +76,7 @@ public class DisabledItemsListener implements Listener {
return; return;
NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(player.getInventory().getItemInMainHand()); NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(player.getInventory().getItemInMainHand());
if (this.shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
@ -84,7 +87,7 @@ public class DisabledItemsListener implements Listener {
return; return;
NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getCursor()); NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getCursor());
if (this.shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
@ -94,21 +97,14 @@ public class DisabledItemsListener implements Listener {
return; return;
final NBTItem item = NBTItem.get(event.getBow()); final NBTItem item = NBTItem.get(event.getBow());
if (shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void handleVanillaEatenConsumables(PlayerItemConsumeEvent event) { public void handleVanillaEatenConsumables(PlayerItemConsumeEvent event) {
NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getItem()); NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getItem());
if (shouldCancel(item)) if (MMOUtils.hasBeenRemoved(item))
event.setCancelled(true); event.setCancelled(true);
} }
private boolean shouldCancel(NBTItem item) {
if (!item.hasType() || !plugin.getConfig().getBoolean("disable-removed-items", true))
return false;
return !Type.isValid(item.getType());
}
} }

View File

@ -98,9 +98,10 @@ public class PlayerListener implements Listener {
@EventHandler @EventHandler
public void onArmorEquip(ArmorEquipEvent event) { public void onArmorEquip(ArmorEquipEvent event) {
Player player = event.getPlayer(); if (event.getNewArmorPiece() == null)
NBTItem item = NBTItem.get(event.getNewArmorPiece()); return;
if (!PlayerData.get(player).getRPG().canUse(item, true))
if (!PlayerData.get(event.getPlayer()).getRPG().canUse(NBTItem.get(event.getNewArmorPiece()), true))
event.setCancelled(true); event.setCancelled(true);
} }

View File

@ -3,13 +3,15 @@ package net.Indyuce.mmoitems.listener.reforging;
import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.event.MMOItemReforgeFinishEvent; import net.Indyuce.mmoitems.api.event.MMOItemReforgeFinishEvent;
import net.Indyuce.mmoitems.api.interaction.ItemSkin; import net.Indyuce.mmoitems.api.interaction.ItemSkin;
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
/** /**
* Transfers the lore from the old MMOItem to the new one. * Transfers the lore from the old MMOItem to the new one.
* * <p>
* This operation is intended to allow refreshing the lore, * This operation is intended to allow refreshing the lore,
* but keeping external things too. * but keeping external things too.
* *
@ -19,22 +21,15 @@ public class RFFKeepSkins implements Listener {
@EventHandler @EventHandler
public void onReforge(MMOItemReforgeFinishEvent event) { public void onReforge(MMOItemReforgeFinishEvent event) {
if (!event.getOptions().shouldKeepSkins()) { return; } if (!event.getOptions().shouldKeepSkins()) return;
//RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping Skins"); //RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping Skins");
// Got skin?
if (!event.getReforger().getNBTItem().getBoolean(ItemSkin.HAS_SKIN_TAG)) { return; }
//RFG// MMOItems.log("§8Reforge §4EFG§7 Item has skin");
// Apply skin to result // Apply skin to result
final @Nullable String tagValue = event.getReforger().getNBTItem().getString(ItemSkin.SKIN_ID_TAG);
if (tagValue != null && !tagValue.isEmpty()) {
NBTItem resultAsNBT = NBTItem.get(event.getFinishedItem()); NBTItem resultAsNBT = NBTItem.get(event.getFinishedItem());
ItemStack ret = ItemSkin.applySkin(resultAsNBT, new VolatileMMOItem(event.getReforger().getNBTItem()));
// Apply skin event.setFinishedItem(ret);
ItemStack ret = ItemSkin.applySkin(resultAsNBT, event.getReforger().getNBTItem()); }
// Success?
if (ret != null) {
//RFG// MMOItems.log("§8Reforge §4EFG§7 Success");
event.setFinishedItem(ret); }
} }
} }

View File

@ -1,17 +1,12 @@
package net.Indyuce.mmoitems.listener.reforging; package net.Indyuce.mmoitems.listener.reforging;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.event.MMOItemReforgeEvent; import net.Indyuce.mmoitems.api.event.MMOItemReforgeEvent;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData; import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.random.UpdatableRandomStatData; import net.Indyuce.mmoitems.stat.data.random.UpdatableRandomStatData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.stat.type.StatHistory; import net.Indyuce.mmoitems.stat.type.StatHistory;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Prevent previous RNG rolls of base stats * Prevent previous RNG rolls of base stats
@ -21,64 +16,9 @@ import org.jetbrains.annotations.Nullable;
*/ */
public class RFGKeepRNG implements Listener { public class RFGKeepRNG implements Listener {
@EventHandler
public void onReforge(MMOItemReforgeEvent event) {
// Rerolling stats? Nevermind
if (event.getOptions().shouldReRoll()) {
// event.setCancelled(true);
//RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping new item (Complete RNG Reroll)");
return;
}
//RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping old RNG Rolls");
/*
* Proceed to go through all stats
*/
event.getOldMMOItem()
.getStats()
.stream()
// Skip if it cant merge
.filter(stat -> stat.getClearStatData() instanceof Mergeable)
/*
* These stats are exempt from this 'keeping' operation.
* Probably because there is a ReforgeOption specifically
* designed for them that keeps them separately
*/
.filter(stat -> !(ItemStats.LORE.equals(stat) ||
ItemStats.NAME.equals(stat) ||
ItemStats.UPGRADE.equals(stat) ||
ItemStats.ENCHANTS.equals(stat) ||
ItemStats.SOULBOUND.equals(stat) ||
ItemStats.GEM_SOCKETS.equals(stat)))
.forEach(stat -> {
// Stat history in the old item
StatHistory hist = StatHistory.from(event.getOldMMOItem(), stat);
// Alr what the template say, this roll too rare to be kept?
RandomStatData source = event.getReforger().getTemplate().getBaseItemData().get(stat);
/*
* Decide if this data is too far from RNG to
* preserve its rolls, even if it should be
* preserving the rolls.
*/
StatData keptData = shouldReRollRegardless(stat, source, hist.getOriginalData(), event.getReforger().getGenerationItemLevel());
// Old roll is ridiculously low probability under the new parameters. Forget.
if (keptData == null)
return;
// Fetch History from the new item
StatHistory clear = StatHistory.from(event.getNewMMOItem(), stat);
// Replace original data of the new one with the roll from the old one
clear.setOriginalData(keptData);
});
}
/** /**
* @return The item is supposedly being updated, but that doesnt mean all its stats must remain the same. * The item is supposedly being updated, but that doesnt mean all its stats must remain the same.
* <p> * <p>
* In contrast to reforging, in which it is expected its RNG to be rerolled, updating should not do it * In contrast to reforging, in which it is expected its RNG to be rerolled, updating should not do it
* except in the most dire scenarios: * except in the most dire scenarios:
@ -91,14 +31,41 @@ public class RFGKeepRNG implements Listener {
* + There is a new stat: The original data is null so this method cannot be called, will roll the * + There is a new stat: The original data is null so this method cannot be called, will roll the
* new stat to actually add it for the first time. * new stat to actually add it for the first time.
*/ */
@Nullable StatData shouldReRollRegardless(@NotNull ItemStat stat, @NotNull RandomStatData source, @NotNull StatData original, int determinedItemLevel) { @EventHandler
// Not Mergeable, impossible to keep public void onReforge(MMOItemReforgeEvent event) {
if (!(source instanceof UpdatableRandomStatData)) // Rerolling stats? Nevermind
//RFG// MMOItems.log("§8Reforge §3RNG§7 Stat\u00a7f " + stat.getId() + "\u00a77 is not updatable!"); if (event.getOptions().shouldReRoll())
return null; // event.setCancelled(true);
//RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping new item (Complete RNG Reroll)");
return;
// Just pass on //RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping old RNG Rolls");
return ((UpdatableRandomStatData) source).reroll(stat, original, determinedItemLevel);
event.getOldMMOItem().getStats()
.forEach(stat -> {
// Check if stat can be transferred over new item
final RandomStatData source = event.getReforger().getTemplate().getBaseItemData().get(stat);
if (source == null || !(source instanceof UpdatableRandomStatData))
return;
/*
* Decide if this data is too far from RNG to
* preserve its rolls, even if it should be
* preserving the rolls.
*/
final StatHistory hist = StatHistory.from(event.getOldMMOItem(), stat);
final StatData keptData = ((UpdatableRandomStatData) source).reroll(stat, hist.getOriginalData(), event.getReforger().getGenerationItemLevel());
// Old roll is ridiculously low probability under the new parameters. Forget.
if (keptData == null)
return;
// Fetch History from the new item
final StatHistory clear = StatHistory.from(event.getNewMMOItem(), stat);
// Replace original data of the new one with the roll from the old one
clear.setOriginalData(keptData);
});
} }
} }

View File

@ -269,13 +269,6 @@ disable-vanilla-recipes: []
# Options for the Item Revision System # Options for the Item Revision System
item-revision: item-revision:
# This is the value to set the Item Level to when
# items are updated by the revision system.
# This can be set to -1 to use the items previous level.
# Please note, that this value has no effect if
# ´reroll-when-updated´ is set to true.
default-item-level: -1
# Unsocketing gems picks them up with the stats they had # Unsocketing gems picks them up with the stats they had
# when first put into the item, disable this option to # when first put into the item, disable this option to
# force them to be regenerated. # force them to be regenerated.