Can now choose if a GemStone's stats are affected by Upgrading the item it is put onto.

This commit is contained in:
Gunging 2021-02-20 21:43:42 -06:00
parent 4540bd4cfc
commit 9474dcddf3
22 changed files with 701 additions and 227 deletions

View File

@ -96,7 +96,7 @@
<dependency>
<groupId>io.lumine</groupId>
<artifactId>MythicLib</artifactId>
<version>1.0.9-SNAPSHOT</version>
<version>1.0.10</version>
<scope>provided</scope>
</dependency>

View File

@ -136,7 +136,7 @@ public class ItemStats {
VANILLA_EATING_ANIMATION = new VanillaEatingAnimation(),
INEDIBLE = new Inedible(),
GEM_COLOR = new GemColor(),
//todo GEM_UPGRADE_SCALING = new GemUpgradeScaling(),
GEM_UPGRADE_SCALING = new GemUpgradeScaling(),
ITEM_TYPE_RESTRICTION = new ItemTypeRestriction(),
MAX_CONSUME = new DoubleStat("MAX_CONSUME", Material.BLAZE_POWDER, "Max Consume", new String[]{"Max amount of usage before", "item disappears."}, new String[]{"consumable"}),
SUCCESS_RATE = new SuccessRate(),

View File

@ -37,10 +37,12 @@ import net.Indyuce.mmoitems.manager.*;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.io.File;
@ -523,7 +525,7 @@ public class MMOItems extends LuminePlugin {
setRPG(plugin.load());
// Mention it
SLog("Using \u00a76" + plugin.getName() + "\u00a77 as RPG Player provider.");
Log("Using \u00a76" + plugin.getName() + "\u00a77 as RPG Player provider.");
return;
}
}
@ -568,7 +570,7 @@ public class MMOItems extends LuminePlugin {
* @return Generates an item given an item template with a
* specific item level and item tier
*/
public ItemStack getItem(Type type, String id, int itemLevel, @Nullable ItemTier itemTier) {
public ItemStack getItem(@NotNull Type type, @NotNull String id, int itemLevel, @Nullable ItemTier itemTier) {
return getMMOItem(type, id, itemLevel, itemTier).newBuilder().build();
}
@ -577,7 +579,7 @@ public class MMOItems extends LuminePlugin {
* 0 and the item will have no item tier unless one is specified in
* the base item data.
*/
public MMOItem getMMOItem(Type type, String id) {
public MMOItem getMMOItem(@NotNull Type type, @NotNull String id) {
return templateManager.getTemplate(type, id).newBuilder(0, null).build();
}
@ -586,25 +588,21 @@ public class MMOItems extends LuminePlugin {
* 0 and the item will have no item tier unless one is specified in
* the base item data.
*/
public ItemStack getItem(Type type, String id) {
public ItemStack getItem(@NotNull Type type, @NotNull String id) {
return getMMOItem(type, id).newBuilder().build();
}
/**
* Logs something into the console with a cool [MMOItems] prefix :)
* <p></p>
* Parses color codes. <b>Meant for DEV testing</b>, all of them are removed every release.
* Parses color codes. <b>Mostly for DEV testing</b>. these may removed any release.
*/
public static void Log(String message) {
plugin.getServer().getConsoleSender().sendMessage("\u00a78[" + ChatColor.YELLOW + "MMOItems\u00a78] \u00a77" + message);
}
/**
* Logs something into the console with a cool [MMOItems] prefix :)
* <p></p>
* Parses color codes, official method. Wont be removed when releasing MMOItems versions.
* @return The server's console sender.
*/
public static void SLog(String message) {
plugin.getServer().getConsoleSender().sendMessage("\u00a78[" + ChatColor.YELLOW + "MMOItems\u00a78] \u00a77" + message);
}
@NotNull public static ConsoleCommandSender getConsole() { return plugin.getServer().getConsoleSender(); }
}

View File

@ -2,9 +2,15 @@ package net.Indyuce.mmoitems.api;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
import net.Indyuce.mmoitems.stat.data.UpgradeData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.StatHistory;
@ -16,60 +22,147 @@ import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.stat.type.Upgradable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class UpgradeTemplate {
private final String id;
private final Map<ItemStat, UpgradeInfo> stats = new HashMap<>();
@NotNull private final String id;
@NotNull private final Map<ItemStat, UpgradeInfo> perStatUpgradeInfos = new HashMap<>();
public UpgradeTemplate(ConfigurationSection config) {
/**
* Loads an Upgrade Template directly from the YML file. Neat!
*/
public UpgradeTemplate(@NotNull ConfigurationSection config) {
Validate.notNull(config, "You must specify a config section.");
// Build ID
id = config.getName().toLowerCase().replace("_", "-").replace(" ", "-");
// Feedback
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FriendlyFeedbackPalette_MMOItems.get());
ffp.ActivatePrefix(true, "Upgrade Template $i&o" + config.getName());
// For ever stat
for (String key : config.getKeys(false)) {
// Get internal stat ID
String statFormat = key.toUpperCase().replace("-", "_");
// Attempt to find stat
ItemStat stat = MMOItems.plugin.getStats().get(statFormat);
Validate.notNull(stat, "Could not read stat ID " + statFormat);
Validate.isTrue(stat instanceof Upgradable, "Stat " + stat.getId() + " us not upgradable.");
if (stat == null) { ffp.Log(FriendlyFeedbackCategory.ERROR, "Stat '$r{0}$b' $fnot found$b.", statFormat); continue; }
if (!(stat instanceof Upgradable)) { ffp.Log(FriendlyFeedbackCategory.ERROR, "Stat $r{0}$b is $fnot upgradeable$b.", stat.getId()); continue; }
if (!(stat.getClearStatData() instanceof Mergeable)) { ffp.Log(FriendlyFeedbackCategory.ERROR, "Stat Data used by $r{0}$b is $fnot mergeable$b, and thus it cannot be upgradeable. Contact the dev of this ItemStat.", stat.getId()); continue; }
// Attempt to parse Upgrade Info
try {
stats.put(stat, ((Upgradable) stat).loadUpgradeInfo(config.get(key)));
// Parsed correctly? Add
perStatUpgradeInfos.put(stat, ((Upgradable) stat).loadUpgradeInfo(config.get(key)));
// Somethings up, generate exception ig
} catch (IllegalArgumentException exception) {
MMOItems.plugin.getLogger().log(Level.WARNING,
"An error occured while trying to load stat '" + key + "' from upgrade template '" + id + "': " + exception.getMessage());
// Log
ffp.Log(FriendlyFeedbackCategory.ERROR, exception.getMessage());
}
}
// Print all failures
ffp.SendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
}
public String getId() {
/**
* Get the internal ID of this template.
* <p></p>
* In the format: <code><b>upgrade-template-name</b></code>
* <p>(No spaces nor underscores, lowercase)</p>
*/
@NotNull public String getId() {
return id;
}
public Set<ItemStat> getKeys() {
return stats.keySet();
/**
* Get the <code>ItemStat</code>s that this template has <code>UpgradeInfo</code> about.
*/
@NotNull public Set<ItemStat> getKeys() {
return perStatUpgradeInfos.keySet();
}
public UpgradeInfo getUpgradeInfo(ItemStat stat) {
return stats.get(stat);
/**
* Get the <code>UpgradeInfo</code> associated with this stat.
*/
@Nullable public UpgradeInfo getUpgradeInfo(@NotNull ItemStat stat) {
return perStatUpgradeInfos.get(stat);
}
public void upgrade(MMOItem mmoitem) {
for (ItemStat stat : stats.keySet()) {
/**
* Upgrades this MMOItem by 1 level
*/
public void upgrade(@NotNull MMOItem mmoitem) {
// Yes
upgradeTo(mmoitem, mmoitem.getUpgradeLevel() + 1);
}
/**
* Upgrades this MMOItem's stats and sets the level.
* @param level Target level, which may even be negative!
*/
public void upgradeTo(@NotNull MMOItem mmoitem, int level) {
// Set the items level
UpgradeData dat;
if (mmoitem.hasData(ItemStats.UPGRADE)) { dat = (UpgradeData) mmoitem.getData(ItemStats.UPGRADE); } else { dat = new UpgradeData(null, null, false, false, 0, 100); }
dat.setLevel(level);
mmoitem.setData(ItemStats.UPGRADE, dat);
// For every Stat-UpgradeInfo pair
for (ItemStat stat : perStatUpgradeInfos.keySet()) {
// If it has the data to begin with?
if (mmoitem.hasData(stat)) {
// ONLY if mergeable
if (stat.getClearStatData() instanceof Mergeable) {
// Initializes Stat History
StatHistory<StatData> hist = StatHistory.From(mmoitem, stat);
// Initializes original stats.
StatHistory.From(mmoitem, stat);
}
// Applies changes
((Upgradable) stat).apply(mmoitem, stats.get(stat));
// The Stat History now manages applying upgrades.
mmoitem.setData(stat, hist.Recalculate());
}
}
}
/**
* @return If the user has set in the config that the stats should display how the item's upgrades have affected them.
*/
public static boolean isDisplayingUpgrades() { return MMOItems.plugin.getConfig().getBoolean("item-upgrading.display-stat-changes", false); }
/**
* The user may define how to display stat changes due to upgrades,
* as well as 'negative' and 'positive' colours.
*
* @return A string ready to just have its colors parsed and inserted into lore.
* @param value The <code>toString()</code> of this will replace all instances of <code>#stat#</code> the user specifies in the config.
* @param isNegative Should 'negative' coloration be used instead of positive? The user uses the color code <code><b>&p</b></code> in this place.
*/
@NotNull public static String getUpgradeChangeSuffix(@NotNull String value, boolean isNegative) {
// Get the base
String base = Objects.requireNonNull(MMOItems.plugin.getConfig().getString("item-upgrading.stat-change-suffix", " &8(&p#stat#&8)"));
String succ = Objects.requireNonNull(MMOItems.plugin.getConfig().getString("item-upgrading.stat-change-positive", "&a"));
String fauc = Objects.requireNonNull(MMOItems.plugin.getConfig().getString("item-upgrading.stat-change-negative", "&c"));
// Parse ig
if (isNegative) {
// Failure-colored
return base.replace("&p", fauc).replace("#stat#", value);
// Its a positive upgrade-yo
} else {
// Success-coloreds
return base.replace("&p", succ).replace("#stat#", value);
}
}
}

View File

@ -8,8 +8,10 @@ import net.Indyuce.mmoitems.api.event.item.ApplyGemStoneEvent;
import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.GemUpgradeScaling;
import net.Indyuce.mmoitems.stat.data.GemSocketsData;
import net.Indyuce.mmoitems.stat.data.GemstoneData;
import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.data.UpgradeData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
@ -41,7 +43,8 @@ public class GemStone extends UseItem {
String gemType = getNBTItem().getString(ItemStats.GEM_COLOR.getNBTPath());
GemSocketsData sockets = (GemSocketsData) targetMMO.getData(ItemStats.GEM_SOCKETS);
if (!sockets.canReceive(gemType))
String foundSocketColor = sockets.getEmptySocket(gemType);
if (foundSocketColor == null)
return new ApplyResult(ResultType.NONE);
/*
@ -73,30 +76,47 @@ public class GemStone extends UseItem {
* permanent effects. also REGISTER gem stone in the item gem stone
* list.
*/
LiveMMOItem mmo = new LiveMMOItem(getNBTItem());
GemstoneData gemData = new GemstoneData(mmo);
LiveMMOItem gemMMOItem = new LiveMMOItem(getNBTItem());
GemstoneData gemData = new GemstoneData(gemMMOItem, foundSocketColor);
sockets.apply(gemType, gemData);
//UPGRD//MMOItems. Log("Applying Gemstone: \u00a73" + foundSocketColor);
/*
* Get the item's level, important for the GemScalingStat
*/
StatData upgradeLevel = targetMMO.getData(ItemStats.UPGRADE);
if (upgradeLevel != null) { gemData.SetLevel(((UpgradeData) upgradeLevel).getLevel()); }
Integer levelIdentified = null; String scaling = GemUpgradeScaling.SUBSEQUENT;
if (gemMMOItem.hasData(ItemStats.GEM_UPGRADE_SCALING)) { scaling = gemMMOItem.getData(ItemStats.GEM_UPGRADE_SCALING).toString(); }
//UPGRD//MMOItems. Log("Scaling Identified: \u00a73" + scaling);
switch (scaling) {
case GemUpgradeScaling.HISTORIC:
levelIdentified = 0;
break;
case GemUpgradeScaling.SUBSEQUENT:
StatData upgradeLevel = targetMMO.getData(ItemStats.UPGRADE);
if (upgradeLevel != null) { levelIdentified = ((UpgradeData) upgradeLevel).getLevel(); }
break;
case GemUpgradeScaling.NEVER:
default:
levelIdentified = null;
break;
}
gemData.setLevel(levelIdentified);
//UPGRD//MMOItems. Log("Set Level: \u00a7b" + gemData.getLevel());
/*
* Only applies NON PROPER and MERGEABLE item stats
*/
for (ItemStat stat : mmo.getStats()) {
for (ItemStat stat : gemMMOItem.getStats()) {
// If it is not PROPER
if (!(stat instanceof GemStoneStat)) {
// Get the stat data
StatData data = mmo.getData(stat);
StatData data = gemMMOItem.getData(stat);
// If the data is MERGEABLE
if (data instanceof Mergeable) {
//GEM//MMOItems.Log("\u00a79>>> \u00a77Gem-Merging \u00a7c" + stat.getNBTPath());
//GEM////UPGRD//MMOItems. Log("\u00a79>>> \u00a77Gem-Merging \u00a7c" + stat.getNBTPath());
// Merge into it
targetMMO.mergeData(stat, data, gemData.getHistoricUUID());

View File

@ -9,6 +9,7 @@ import io.lumine.mythic.utils.adventure.text.Component;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.UpgradeTemplate;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.util.DynamicLore;
import net.Indyuce.mmoitems.api.util.StatFormat;
@ -112,8 +113,8 @@ public class ItemStackBuilder {
* @return Returns built NBTItem with applied tags and lore
*/
public NBTItem buildNBT() {
this.mmoitem = new StatLore(mmoitem).generateNewItem();
// Clone as to not conflict in any way
this.mmoitem = mmoitem.clone();
//GEM//MMOItems.Log("\u00a7e+ \u00a77Building \u00a7c" + mmoitem.getType().getName() + " " + mmoitem.getId() + "\u00a77 (Size \u00a7e" + mmoitem.mergeableStatHistory.size() + "\u00a77 Historic)");
// For every stat within this item
@ -202,45 +203,4 @@ public class ItemStackBuilder {
public ItemStack build() {
return new DynamicLore(buildNBT()).build();
}
//
public class StatLore {
private MMOItem mmoitem;
public StatLore(MMOItem mmoitem) {
this.mmoitem = mmoitem;
}
public MMOItem generateNewItem() {
mmoitem = mmoitem.clone();
if (MMOItems.plugin.getConfig().getBoolean("item-upgrading.display-stat-changes", false))
for (ItemStat stat : mmoitem.getStats()) {
StatHistory<StatData> data = mmoitem.getStatHistory(stat);
if (data != null
&& data.getOriginalData() instanceof DoubleData
&& mmoitem.getData(stat) instanceof DoubleData) {
double value, original, current;
try {
original = ((DoubleData) data.getOriginalData()).getValue();
current = ((DoubleData) mmoitem.getData(stat)).getValue();
value = current - original;
} catch (NullPointerException e) {
continue;
}
if (value != 0) {
lore.insert(stat.getPath(), stat.formatNumericStat(value, "#",
new StatFormat("##").format(current))
+ MythicLib.plugin.parseColors(MMOItems.plugin.getConfig()
.getString("item-upgrading.stat-change-suffix", " &e(+#stat#)").replace(
"#stat#", new StatFormat("##").format(value))));
}
}
}
return mmoitem;
}
}
}

View File

@ -1,10 +1,13 @@
package net.Indyuce.mmoitems.api.item.mmoitem;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.UpgradeTemplate;
import net.Indyuce.mmoitems.api.item.ItemReference;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.stat.data.GemSocketsData;
import net.Indyuce.mmoitems.stat.data.GemstoneData;
import net.Indyuce.mmoitems.stat.data.UpgradeData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.ItemStat;
@ -13,10 +16,7 @@ import org.apache.commons.lang.Validate;
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.UUID;
import java.util.*;
public class MMOItem implements ItemReference {
private final Type type;
@ -83,17 +83,8 @@ public class MMOItem implements ItemReference {
sHistory.registerExternalData(data);
}
// Merge or set onto main stat
if (hasData(stat)) {
// Merge
((Mergeable) getData(stat)).merge(data);
} else {
// Override Completely
setData(stat, data);
}
// Recalculate
setData(stat, sHistory.Recalculate());
// Merging means replacing if it cannot be merged
} else {
@ -115,7 +106,7 @@ public class MMOItem implements ItemReference {
stats.remove(stat);
}
@Nullable public StatData getData(@NotNull ItemStat stat) {
public StatData getData(@NotNull ItemStat stat) {
return stats.get(stat);
}
@ -199,4 +190,91 @@ public class MMOItem implements ItemReference {
mergeableStatHistory.put(stat.getNBTPath(), hist);
}
//endregion
//region Upgrading API
/**
* Upgrades this MMOItem one level.
* <p></p>
* <b>Make sure to check {@link #hasUpgradeTemplate()} before calling.</b>
*/
public void upgrade() {
// Upgrade through the template's API
getUpgradeTemplate().upgrade(this);
}
/**
* Whether or not this item has all the information
* required to call
*/
public boolean hasUpgradeTemplate() {
// Does it have Upgrade Stat?
if (hasData(ItemStats.UPGRADE)) {
// Get that data
UpgradeData data = (UpgradeData) getData(ItemStats.UPGRADE);
// A template its all that's required
return data.getTemplate() != null;
}
// Nope
return false;
}
/**
* @return The upgrade level, or 0 if there is none.
*/
public int getUpgradeLevel() {
// Does it have Upgrade Data?
if (hasData(ItemStats.UPGRADE)) {
// Return the registered level.
return ((UpgradeData) getData(ItemStats.UPGRADE)).getLevel();
}
// Nope? Well its level 0 I guess.
return 0;
}
/**
* <b>Make sure to check {@link #hasUpgradeTemplate()} before calling.</b>
* <p></p>
* This will fail and throw an exception if the MMOItem has no upgrade template.
* @return The upgrade template by which the MMOItem would upgrade normally.
*/
@NotNull public UpgradeTemplate getUpgradeTemplate() {
Validate.isTrue(hasUpgradeTemplate(), "This item has no Upgrade Information, do not call this method without checking first!");
// All Right
UpgradeData data = (UpgradeData) getData(ItemStats.UPGRADE);
// That's the template
return data.getTemplate();
}
/**
* Get the list of GemStones inserted into this item
*/
@NotNull public Set<GemstoneData> getGemStones() {
// Got gem sockets?
if (hasData(ItemStats.GEM_SOCKETS)) {
// Get Data
GemSocketsData data = (GemSocketsData) getData(ItemStats.GEM_SOCKETS);
// Thats it
return data.getGemstones();
// Has no gem sockets
} else {
// Empty Set
return new HashSet<>();
}
}
//endregion
}

View File

@ -0,0 +1,43 @@
package net.Indyuce.mmoitems.api.util.message;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackPalette;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
/**
* ' Presentation '
*/
@SuppressWarnings("unused")
public class FriendlyFeedbackPalette_MMOItems extends FriendlyFeedbackPalette {
/*
* The instance of this palette :p
*/
FriendlyFeedbackPalette_MMOItems() {}
@NotNull static FriendlyFeedbackPalette_MMOItems instance = new FriendlyFeedbackPalette_MMOItems();
@NotNull public static FriendlyFeedbackPalette_MMOItems get() { return instance; }
@NotNull @Override public String getBodyFormat() { return "§x§a§5§b§5§a§7"; }
@NotNull @Override public String consoleBodyFormat() { return ChatColor.GRAY.toString(); }
@NotNull @Override public String getExampleFormat() { return "§x§e§0§f§5§9§3"; }
@NotNull @Override public String consoleExampleFormat() { return ChatColor.YELLOW.toString(); }
@NotNull @Override public String getInputFormat() { return"§x§7§d§c§7§5§8"; }
@NotNull @Override public String consoleInputFormat() { return ChatColor.GREEN.toString(); }
@NotNull @Override public String getResultFormat() { return "§x§5§c§e§0§0§4"; }
@NotNull @Override public String consoleResultFormat() { return ChatColor.GREEN.toString(); }
@NotNull @Override public String getSuccessFormat() { return "§x§2§5§f§7§c§6"; }
@NotNull @Override public String consoleSuccessFormat() { return ChatColor.AQUA.toString(); }
@NotNull @Override public String getFailureFormat() { return "§x§f§f§6§0§2§6"; }
@NotNull @Override public String consoleFailureFormat() { return ChatColor.RED.toString(); }
@NotNull @Override public String getRawPrefix() { return "§8[§eMMOItems#s§8] "; }
@NotNull @Override public String getRawPrefixConsole() { return "§8[§eMMOItems#s§8] "; }
@NotNull @Override public String getSubdivisionFormat() { return "§x§c§c§a§3§3§3§o"; }
@NotNull @Override public String consoleSubdivisionFormat() { return "§6§o"; }
}

View File

@ -4,6 +4,8 @@ import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.ConfigFile;
import net.Indyuce.mmoitems.api.UpgradeTemplate;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
@ -33,7 +35,11 @@ public class UpgradeManager implements Reloadable {
return templates.values();
}
public UpgradeTemplate getTemplate(String id) {
/**
* Get the <code>UpgradeTemplate</code> of this name.
* @return <code>null</code> if there is no such template loaded.
*/
@Nullable public UpgradeTemplate getTemplate(@NotNull String id) {
return templates.get(id);
}

View File

@ -1,9 +1,12 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.ChooseStat;
import net.Indyuce.mmoitems.stat.type.GemStoneStat;
import io.lumine.mythic.lib.version.VersionMaterial;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
@ -34,4 +37,6 @@ public class GemUpgradeScaling extends ChooseStat implements GemStoneStat {
// Update
HintChooseableDefs(definitions);
}
@NotNull @Override public StatData getClearStatData() { return new StringData(SUBSEQUENT); }
}

View File

@ -43,12 +43,12 @@ public class ItemSetStat extends StringStat {
@Override
public void whenApplied(@NotNull ItemStackBuilder item, @NotNull StatData data) {
// Add NBT
item.addItemTag(getAppliedNBT(data));
// Display in lore
ItemSet set = MMOItems.plugin.getSets().get(data.toString());
item.getLore().insert("set", set.getLoreTag());
// Add NBT
item.addItemTag(getAppliedNBT(data));
}
@NotNull

View File

@ -166,7 +166,7 @@ public class UpgradeStat extends ItemStat implements ConsumableItemInteraction {
@NotNull
@Override
public StatData getClearStatData() { return new UpgradeData("", "", false, false, 0, 0D); }
public StatData getClearStatData() { return new UpgradeData(null, null, false, false, 0, 0D); }
@Override
public boolean handleConsumableEffect(InventoryClickEvent event, PlayerData playerData, Consumable consumable, NBTItem target, Type targetType) {

View File

@ -5,6 +5,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import io.lumine.mythic.lib.api.util.Ref;
import net.Indyuce.mmoitems.api.interaction.GemStone;
import org.apache.commons.lang.Validate;
@ -17,20 +18,31 @@ import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GemSocketsData implements StatData, Mergeable, RandomStatData {
private final Set<GemstoneData> gems = new HashSet<>();
private final List<String> emptySlots;
@NotNull private final Set<GemstoneData> gems = new HashSet<>();
@NotNull private final List<String> emptySlots;
public GemSocketsData(List<String> emptySlots) {
public GemSocketsData(@NotNull List<String> emptySlots) {
this.emptySlots = emptySlots;
}
public boolean canReceive(String gem) {
/**
* Attempts to find a slot of the same color of this gem within the item.
* <p></p>
* To know the color of the socket pass the same argument to {@link #getEmptySocket(String)}
* which checks in the same order as this method for the first success.
*/
public boolean canReceive(@NotNull String gem) {
return getEmptySocket(gem) != null;
}
public String getEmptySocket(String gem) {
/**
* Get the first emtpty gem socket that matches this color
* @return <code>null</code> if none matched.
*/
@Nullable public String getEmptySocket(@NotNull String gem) {
for (String slot : emptySlots)
if (gem.equals("") || slot.equals(MMOItems.plugin.getConfig().getString("gem-sockets.uncolored")) || gem.equals(slot))
return slot;
@ -50,11 +62,11 @@ public class GemSocketsData implements StatData, Mergeable, RandomStatData {
emptySlots.add(slot);
}
public List<String> getEmptySlots() {
@NotNull public List<String> getEmptySlots() {
return emptySlots;
}
public Set<GemstoneData> getGemstones() {
@NotNull public Set<GemstoneData> getGemstones() {
return gems;
}

View File

@ -1,15 +1,12 @@
package net.Indyuce.mmoitems.stat.data;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem;
import net.Indyuce.mmoitems.stat.GemUpgradeScaling;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -21,10 +18,10 @@ public class GemstoneData {
@NotNull private final List<PotionEffectData> effects = new ArrayList<>();
@NotNull private final Map<ItemStat, Double> stats = new HashMap<>();
@NotNull private final String name;
int levelPut = 0;
@Nullable Integer levelPut = 0;
@NotNull final UUID historicUUID;
@Nullable final String mmoitemType, mmoitemID;
@Nullable final String mmoitemType, mmoitemID, socketColor;
/**
* This constructor is not really performance friendly. It should only be
@ -59,19 +56,21 @@ public class GemstoneData {
mmoitemID = object.get("Id").getAsString();
JsonElement level = object.get("Level");
if (level != null) { levelPut = level.getAsInt(); }
} else { historicUUID = UUID.randomUUID(); mmoitemID = null; mmoitemType = null; }
if (level != null && level.isJsonPrimitive()) { levelPut = level.getAsJsonPrimitive().getAsInt(); } else { levelPut = null; }
JsonElement color = object.get("Color");
if (color != null && color.isJsonPrimitive()) { socketColor = color.getAsJsonPrimitive().getAsString(); } else { socketColor = null; }
} else { historicUUID = UUID.randomUUID(); mmoitemID = null; mmoitemType = null; socketColor = null; }
}
/**
* Create a GemStoneData from a GemStone MMOItem.
* <p></p>
* Basically extracts all the useable stats from the MMOItem, to have them ready to apply onto another MMOItem.
* @param color Color of the slot this gem was inserted onto.
*/
public GemstoneData(@NotNull LiveMMOItem gemStoneMMOItem) {
public GemstoneData(@NotNull LiveMMOItem gemStoneMMOItem, @Nullable String color) {
// Get Name to Display
name = MMOUtils.getDisplayName(gemStoneMMOItem.getNBT().getItem());
@ -87,6 +86,7 @@ public class GemstoneData {
historicUUID = UUID.randomUUID();
mmoitemID = gemStoneMMOItem.getId();
mmoitemType = gemStoneMMOItem.getType().getId();
socketColor = color;
}
/**
@ -101,32 +101,42 @@ public class GemstoneData {
this.name = name;
mmoitemID = null;
mmoitemType = null;
socketColor = null;
historicUUID = UUID.randomUUID();
}
/**
* This is at which level (of the item) the gemstone was placed onto the item.
* <p>A null level means this gem does not scale.</p>
* <p></p>
* For scaling purposes of stat {@link GemUpgradeScaling}
*/
public void SetLevel(int l) { levelPut = l; }
public void setLevel(@Nullable Integer l) { levelPut = l; }
/**
* This is at which level (of the item) the gemstone was placed onto the item.
* <p>A null level means this gem does not scale.</p>
* <p></p>
* For scaling purposes of stat {@link GemUpgradeScaling}
*/
public int GetLevel() { return levelPut; }
@Nullable public Integer getLevel() { return levelPut; }
/**
* Does this gem scale with item upgrades?
*/
public boolean isScaling() { return levelPut != null; }
/**
* This is a completely empty builder.
* <p></p>
* You may add whatever you want with <code>addAbility()</code>,<code>addPermamentEffect</code>, or most widely usedly, <code>setStat()</code>.
* @param name Name to display in the lore of the item when you put the gemstone into it.
* @param color Color of the socket this gem is inserted onto
*/
public GemstoneData(@NotNull String name, @Nullable String type, @Nullable String id) {
public GemstoneData(@NotNull String name, @Nullable String type, @Nullable String id, @Nullable String color) {
this.name = name;
mmoitemID = type;
mmoitemType = id;
socketColor = color;
historicUUID = UUID.randomUUID();
}

View File

@ -11,15 +11,23 @@ import net.Indyuce.mmoitems.stat.data.type.StatData;
import io.lumine.mythic.lib.MythicLib;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A class containing the Upgrading stuff of an item. Things like:
* <p> What level it currently is
* </p> What upgrade template it upgrades with
* <p> Chance of successful upgrade
* </p> May it get destroyed if unsucessful upgrade?
*/
public class UpgradeData implements StatData, RandomStatData {
private final String reference, template;
@Nullable private final String reference, template;
private final boolean workbench, destroy;
private final double success;
private final int max;
private int level;
public UpgradeData(@NotNull String referenc, @NotNull String templat, boolean workbenc, boolean destro, int maxx, double succes) {
public UpgradeData(@Nullable String referenc, @Nullable String templat, boolean workbenc, boolean destro, int maxx, double succes) {
reference = referenc;
template = templat;
workbench = workbenc;
@ -47,13 +55,22 @@ public class UpgradeData implements StatData, RandomStatData {
success = object.get("Success").getAsDouble();
}
public UpgradeTemplate getTemplate() {
/**
* @return The template associated to this data, if it is loaded.
*/
@Nullable public UpgradeTemplate getTemplate() {
return MMOItems.plugin.getUpgrades().getTemplate(template);
}
public int getLevel() {
return level;
}
public int getLevel() { return level; }
/**
* Dont you mean {@link UpgradeTemplate#upgradeTo(MMOItem, int)}?
* This sets the level the item thinks it is, does not apply no changes.
* <p></p>
* <b>Make sure you know what you are doing before using this</b>
*/
public void setLevel(int l) { level = l; }
public int getMaxUpgrades() {
return max;
@ -75,13 +92,22 @@ public class UpgradeData implements StatData, RandomStatData {
return reference == null || data.reference == null || reference.isEmpty() || data.reference.isEmpty() || reference.equals(data.reference);
}
public void upgrade(MMOItem mmoitem) {
if (!MMOItems.plugin.getUpgrades().hasTemplate(template)) {
/**
* Upgrade this MMOItem by 1 Level
*/
public void upgrade(@NotNull MMOItem mmoitem) {
/*
* Find Upgrade Template
*/
if (getTemplate() == null) {
MMOItems.plugin.getLogger().warning("Couldn't find upgrade template '" + template + "'. Does it exist?");
return;
}
// change display name
/*
* Display Upgrade Level
*/
String suffix = MythicLib.plugin.parseColors(MMOItems.plugin.getConfig().getString("item-upgrading.name-suffix"));
if (MMOItems.plugin.getConfig().getBoolean("item-upgrading.display-in-name"))
if (mmoitem.hasData(ItemStats.NAME)) {
@ -89,7 +115,6 @@ public class UpgradeData implements StatData, RandomStatData {
nameData.setString(level == 0 ? nameData.toString() + suffix.replace("#lvl#", "" + (level + 1))
: nameData.toString().replace(suffix.replace("#lvl#", "" + level), suffix.replace("#lvl#", "" + (level + 1))));
}
/*TODO: implement this as a new dynamic lore type
else if (mmoitem.hasData(ItemStats.LORE)) {
StringListData loreData = (StringListData) mmoitem.getData(ItemStats.LORE);
@ -101,11 +126,10 @@ public class UpgradeData implements StatData, RandomStatData {
});
}*/
// apply stat updates
/*
* Go through every stat that must be ugpraded and apply
*/
getTemplate().upgrade(mmoitem);
// increase the level
level++;
}
public JsonObject toJson() {

View File

@ -1,7 +1,15 @@
package net.Indyuce.mmoitems.stat.data.type;
import net.Indyuce.mmoitems.stat.type.StatHistory;
import net.Indyuce.mmoitems.stat.type.Upgradable;
import org.jetbrains.annotations.NotNull;
/**
* Most intuitive use is for ItemStats to not completely replace each other
* when used through Gem Stones. However, this serves a crucial internal
* role in determining which stats generate {@link StatHistory}es, which in
* turn allows them to be {@link Upgradable}.
*/
public interface Mergeable {
/**

View File

@ -1,4 +1,6 @@
package net.Indyuce.mmoitems.stat.data.type;
public interface UpgradeInfo {
}
/**
* Parsed from the configs into providing a StatData all the changes each upgrade will give it.
*/
public interface UpgradeInfo { }

View File

@ -44,21 +44,4 @@ public abstract class AttributeStat extends DoubleStat {
public double getOffset() {
return offset;
}
/**
* Only difference from <code>DoubleStat.whenApplied()</code> is that
* double stats dont bother displaying when equal to 0, but attribute
* stats do.
*/
@Override public void whenApplied(@NotNull ItemStackBuilder item, @NotNull StatData data) {
// Get its value
double value = ((DoubleData) data).getValue();
// Adds the Item Tag
item.addItemTag(getAppliedNBT(data));
// Translates in Lore
item.getLore().insert(getPath(), formatNumericStat(value, "#", new StatFormat("##").format(value)));
}
}

View File

@ -1,16 +1,23 @@
package net.Indyuce.mmoitems.stat.type;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
import io.lumine.mythic.lib.api.util.AltChar;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackMessage;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
import io.lumine.mythic.lib.api.util.ui.PlusMinusPercent;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.UpgradeTemplate;
import net.Indyuce.mmoitems.api.edition.StatEdition;
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.NumericStatFormula;
import net.Indyuce.mmoitems.api.util.StatFormat;
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
@ -28,6 +35,7 @@ import org.jetbrains.annotations.Nullable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
@ -50,6 +58,12 @@ public class DoubleStat extends ItemStat implements Upgradable {
return true;
}
/**
* Usually, a greater magnitude of stat benefits the player (more health, more attack damage).
* <p>However, its not impossible for a stat to be evil instead, who knows?
*/
public boolean moreIsBetter() { return true; }
@Override
public RandomStatData whenInitialized(Object object) {
@ -68,15 +82,65 @@ public class DoubleStat extends ItemStat implements Upgradable {
// Get Value
double value = ((DoubleData) data).getValue();
// Cancel if it equals ZERO, or its NEGATIVE and this doesnt support negative stats.
// Cancel if it its NEGATIVE and this doesn't support negative stats.
if (value < 0 && !handleNegativeStats()) { return; }
// Identify the upgrade amount
double upgradeShift = 0;
// Displaying upgrades?
if (UpgradeTemplate.isDisplayingUpgrades() && item.getMMOItem().getUpgradeLevel() != 0) {
// Get stat history
StatHistory<StatData> hist = item.getMMOItem().getStatHistory(this);
if (hist != null) {
// Get as if it had never been upgraded
DoubleData uData = (DoubleData) hist.Recalculate_Unupgraded();
// Calculate Difference
upgradeShift = value - uData.getValue();
}
}
// Display if not ZERO
if (value != 0) { item.getLore().insert(getPath(), formatNumericStat(value, "#", new StatFormat("##").format(value))); }
if (value != 0 || upgradeShift != 0) {
// Displaying upgrades?
if (upgradeShift != 0) {
item.getLore().insert(getPath(), formatNumericStat(value, "#", new StatFormat("##").format(value))
+ MythicLib.plugin.parseColors(UpgradeTemplate.getUpgradeChangeSuffix(plus(upgradeShift) + (new StatFormat("##").format(upgradeShift)), !isGood(upgradeShift))));
} else {
// Just display normally
item.getLore().insert(getPath(), formatNumericStat(value, "#", new StatFormat("##").format(value)));
}
}
// Add NBT Path
item.addItemTag(getAppliedNBT(data));
}
@NotNull String plus(double amount) { if (amount >= 0) { return "+"; } else return ""; }
/**
* Usually, a greater magnitude of stat benefits the player (more health, more attack damage).
* <p>However, its not impossible for a stat to be evil instead, who knows?
* <p></p>
* This will return true if:
* <p> > The amount is positive, and more benefits the player
* </p> > The amount is negative, and more hurts the player
*/
public boolean isGood(double amount) {
if (moreIsBetter()) {
return amount >= 0;
} else {
return amount <= 0;
}
}
@Override
public @NotNull ArrayList<ItemTag> getAppliedNBT(@NotNull StatData data) {
@ -146,7 +210,7 @@ public class DoubleStat extends ItemStat implements Upgradable {
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {
double base, scale, spread, maxSpread;
/**
/*
* Supports the old RANGE formula with a minimum and a maximum value and
* automatically makes the conversion to the newest system. This way
* users can keep using the old system if they don't want to adapt to
@ -165,7 +229,7 @@ public class DoubleStat extends ItemStat implements Upgradable {
spread = MMOUtils.truncation(.8 * maxSpread, 3);
}
/**
/*
* Newest system with gaussian values calculation
*/
else {
@ -215,45 +279,104 @@ public class DoubleStat extends ItemStat implements Upgradable {
return new DoubleData(0D);
}
@NotNull
@Override
public UpgradeInfo loadUpgradeInfo(Object obj) {
return new DoubleUpgradeInfo(obj);
public UpgradeInfo loadUpgradeInfo(@Nullable Object obj) throws IllegalArgumentException {
// Return result of thay
return DoubleUpgradeInfo.GetFrom(obj);
}
@NotNull
@Override
public void apply(MMOItem mmoitem, UpgradeInfo info) {
DoubleUpgradeInfo doubleInfo = (DoubleUpgradeInfo) info;
public StatData apply(@NotNull StatData original, @NotNull UpgradeInfo info, int level) {
if (mmoitem.hasData(this)) {
if (doubleInfo.isRelative())
((DoubleData) mmoitem.getData(this)).addRelative(doubleInfo.getAmount());
else
((DoubleData) mmoitem.getData(this)).add(doubleInfo.getAmount());
} else
mmoitem.setData(this, new DoubleData(doubleInfo.getAmount()));
// Must be DoubleData
if (original instanceof DoubleData && info instanceof DoubleUpgradeInfo) {
// Get value
double value = ((DoubleData) original).getValue();
// If leveling up
if (level > 0) {
// While still positive
while (level > 0) {
// Apply PMP Operation Positively
value = ((DoubleUpgradeInfo) info).getPMP().apply(value);
// Decrease
level--;
}
// Degrading the item
} else if (level < 0) {
// While still negative
while (level < 0) {
// Apply PMP Operation Reversibly
value = ((DoubleUpgradeInfo) info).getPMP().reverse(value);
// Decrease
level++;
}
}
// Update
((DoubleData) original).setValue(value);
}
// Upgraded
return original;
}
public static class DoubleUpgradeInfo implements UpgradeInfo {
private final boolean relative;
private final double amount;
@NotNull PlusMinusPercent pmp;
public DoubleUpgradeInfo(Object obj) {
Validate.notNull(obj, "Argument must not be null");
/**
* Generate a <code>DoubleUpgradeInfo</code> from this <code><b>String</b></code>
* that represents a {@link PlusMinusPercent}.
* <p></p>
* To keep older MMOItems versions working the same way, instead of having no prefix
* to use the <i>set</i> function of the PMP, one must use an <b><code>s</code></b> prefix.
* @param obj A <code><u>String</u></code> that encodes for a PMP.
* @throws IllegalArgumentException If any part of the operation goes wrong (including reading the PMP).
*/
@NotNull public static DoubleUpgradeInfo GetFrom(@Nullable Object obj) throws IllegalArgumentException {
// Shall not be null
Validate.notNull(obj, FriendlyFeedbackProvider.QuickForConsole(FriendlyFeedbackPalette_MMOItems.get(), "Upgrade operation must not be null"));
// Does the string exist?
String str = obj.toString();
if (str.isEmpty())
throw new IllegalArgumentException("Couldn't read amount");
if (str.isEmpty()) {
throw new IllegalArgumentException(
FriendlyFeedbackProvider.QuickForConsole(FriendlyFeedbackPalette_MMOItems.get(), "Upgrade operation is empty"));
}
relative = str.toCharArray()[str.length() - 1] == '%';
amount = relative ? MMOUtils.parseDouble(str.substring(0, str.length() - 1)) / 100 : MMOUtils.parseDouble(str);
// Adapt to PMP format
char c = str.charAt(0); if (c == 's') { str = str.substring(1); } else if (c != '+' && c != '-' && c != 'n') { str = '+' + str; }
// Is it a valid plus minus percent?
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FriendlyFeedbackPalette_MMOItems.get());
PlusMinusPercent pmpRead = PlusMinusPercent.getFromString(str, ffp);
if (pmpRead == null) {
throw new IllegalArgumentException(
ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR).get(0).forConsole(ffp.getPalette()));
}
// Success
return new DoubleUpgradeInfo(pmpRead);
}
public double getAmount() {
return amount;
}
public DoubleUpgradeInfo(@NotNull PlusMinusPercent pmp) { this.pmp = pmp; }
public boolean isRelative() {
return relative;
}
/**
* The operation every level will perform.
* @see PlusMinusPercent
*/
@NotNull public PlusMinusPercent getPMP() { return pmp; }
}
}

View File

@ -2,13 +2,15 @@ package net.Indyuce.mmoitems.stat.type;
import com.google.gson.*;
import io.lumine.mythic.lib.api.item.ItemTag;
import net.Indyuce.mmoitems.ItemStats;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.UpgradeTemplate;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
import net.Indyuce.mmoitems.stat.data.*;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -23,6 +25,7 @@ import java.util.*;
* <p></p>
* This class will store the different sources of each stat UPON being modified.
*/
@SuppressWarnings({"unused", "unchecked", "SpellCheckingInspection"})
public class StatHistory<S extends StatData> {
/*
@ -122,9 +125,9 @@ public class StatHistory<S extends StatData> {
// Found? Thats it
if (hist != null) {
//GEM//MMOItems.Log("Found Stat History of \u00a76" + ofStat.getNBTPath() + "\u00a77 in this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
//GEM////UPGRD//MMOItems. Log("Found Stat History of \u00a76" + ofStat.getNBTPath() + "\u00a77 in this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
return hist; }
//GEM//MMOItems.Log("\u00a7aCreated Hisotry of \u00a76" + ofStat.getNBTPath() + "\u00a7a of this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
//GEM////UPGRD//MMOItems. Log("\u00a7aCreated Hisotry of \u00a76" + ofStat.getNBTPath() + "\u00a7a of this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
// That is Mergeable right...
Validate.isTrue(ofStat.getClearStatData() instanceof Mergeable, "Non-Mergeable stat data wont have a Stat History; they cannot be modified dynamically in the first place.");
@ -133,11 +136,11 @@ public class StatHistory<S extends StatData> {
StatData original = ofItem.getData(ofStat);
if (original == null) {
original = ofStat.getClearStatData();
//GEM// MMOItems.Log("\u00a7e +\u00a77 Item didnt have this stat, original set as blanc.");
//GEM// //UPGRD//MMOItems. Log("\u00a7e +\u00a77 Item didnt have this stat, original set as blanc.");
}
else {
original = ((Mergeable) original).cloneData();
//GEM//MMOItems.Log("\u00a7a +\u00a77 Found original data");
//GEM////UPGRD//MMOItems. Log("\u00a7a +\u00a77 Found original data");
}
// Create new
@ -159,12 +162,33 @@ public class StatHistory<S extends StatData> {
/**
* This recalculates final value of the stats of the item.
* <p></p>
* This will not apply the changes, it will just give you the final
* <code>StatData</code> that shall be applied (used when upgrading).
*/
@NotNull public S Recalculate() {
// Double Data shall account for upgrade levels.
if (originalData instanceof DoubleData) { return Recalculate_AsDoubleData(); }
// If its upgradeable and not level ZERO, it must apply upgrades
if ((getMMOItem().getUpgradeLevel() != 0) &&
(getItemStat() instanceof Upgradable) &&
(getMMOItem().hasUpgradeTemplate())) {
// Recalculate upgrading
return Recalculate_AsUpgradeable();
}
// Merge Normally
return Recalculate_ThroughClone();
}
/**
* This recalculates values accounting only for gemstones and external data.
* <p></p>
* In case someone was wondered the contribution of upgrading the item.
*/
@NotNull public S Recalculate_Unupgraded() {
// Merge Normally
return Recalculate_ThroughClone();
}
@ -178,30 +202,74 @@ public class StatHistory<S extends StatData> {
* </p>4: Sums Gem Stone Data (which should be scaled accordingly [Upgrades are entirely merged into their data])
* <p>5: Sums external data (modifiers that are not linked to an ID, I suppose by external plugins).
*/
S Recalculate_AsDoubleData() {
private S Recalculate_AsUpgradeable() {
//UPGRD//MMOItems. Log("\u00a76|||\u00a77 Calculating as Upgradeable");
// Start out with original value?
double recalculated = ((DoubleData) getOriginalData()).getValue();
// Get Upgrade Info?
UpgradeInfo inf = getMMOItem().getUpgradeTemplate().getUpgradeInfo(getItemStat());
// Process Upgrades
UpgradeData data = (UpgradeData) getMMOItem().getData(ItemStats.UPGRADE);
if (data != null) {
// No Upgrade Information? Looks like you're calculating as a normal merge stat
if (inf == null) { return Recalculate_ThroughClone(); }
// Apply scaling
UpgradeTemplate template = data.getTemplate();
}
// Clone original
StatData ogCloned = ((Mergeable) originalData).cloneData();
// Alr time to merge all
DoubleData ret = new DoubleData(recalculated);
// Level up
int lvl = getMMOItem().getUpgradeLevel();
//UPGRD//MMOItems. Log("\u00a76 ||\u00a77 Item Level: \u00a7e" + lvl);
//UPGRD//MMOItems. Log("\u00a76 >\u00a77 Original Base: \u00a7e" + ((DoubleData) ogCloned).getValue());
S ret = (S) ((Upgradable) getItemStat()).apply(ogCloned, inf, lvl);
//UPGRD//MMOItems. Log("\u00a76 >\u00a77 Leveled Base: \u00a7e" + ((DoubleData) ret).getValue());
// Add up gemstones
for (S d : perGemstoneData.values()) { ret.merge(d); }
for (UUID d : perGemstoneData.keySet()) {
// Identify insertion level (When was the gemstone put into the item?
int level = 0;
// Whats this gemstone's upgrade level?
for (GemstoneData gData : getMMOItem().getGemStones()) {
// Find that one of matching UUID
if (gData.getHistoricUUID().equals(d)) {
if (gData.isScaling()) {
// Ok
level = gData.getLevel();
} else {
// No scaling
level = lvl;
}
}
}
// Calculate level difference
int gLevel = lvl - level;
//UPGRD//MMOItems. Log("\u00a76 |\u00a7b|\u00a76>\u00a77 Gemstone Level: \u00a7e" + gLevel + "\u00a77 (Put at \u00a7b" + level + "\u00a77)");
//UPGRD//MMOItems. Log("\u00a76 \u00a7b|>\u00a77 Gemstone Base: \u00a7e" + ((DoubleData) getGemstoneData(d)).getValue());
// Apply upgrades
StatData gRet = ((Upgradable) getItemStat()).apply(((Mergeable) getGemstoneData(d)).cloneData(), inf, gLevel);
//UPGRD//MMOItems. Log("\u00a76 \u00a7b|>\u00a77 Leveled Base: \u00a7e" + ((DoubleData) gRet).getValue());
// Merge
((Mergeable) ret).merge(gRet);
}
// Add up externals
for (S d : perExternalData) { ret.merge(d); }
for (S d : perExternalData) {
//UPGRD//MMOItems. Log("\u00a76 >\u00a7c> \u00a77 Extraneous Base: \u00a7e" + ((DoubleData) d).getValue());
// Just merge ig
((Mergeable) ret).merge(((Mergeable) d).cloneData());
}
// Return result
return (S) ret;
//UPGRD//MMOItems. Log("\u00a76:::\u00a77 Result: \u00a7e" + ((DoubleData) ret).getValue());
return ret;
}
/**
@ -213,19 +281,28 @@ public class StatHistory<S extends StatData> {
* </p>3: Sums Gem Stone Data (which should be scaled accordingly [Upgrades are entirely merged into their data])
* <p>4: Sums external data (modifiers that are not linked to an ID, I suppose by external plugins).
*/
S Recalculate_ThroughClone() {
private S Recalculate_ThroughClone() {
//UPGRD//MMOItems. Log("\u00a73|||\u00a77 Calculating as Clonium");
// Just clone bro
S ret = (S) ((Mergeable) getOriginalData()).cloneData();
//UPGRD//MMOItems. Log("\u00a73 > \u00a77 Original Base: \u00a7e" + ((DoubleData) ret).getValue());
// Add up gemstones
for (S d : perGemstoneData.values()) { ((Mergeable) ret).merge(d); }
for (S d : perGemstoneData.values()) {
//UPGRD//MMOItems. Log("\u00a73 >\u00a7b> \u00a77 Gemstone Base: \u00a7e" + ((DoubleData) d).getValue());
((Mergeable) ret).merge(d);
}
// Add up externals
for (S d : perExternalData) { ((Mergeable) ret).merge(d); }
for (S d : perExternalData) {
//UPGRD//MMOItems. Log("\u00a73 >\u00a7c> \u00a77 Extraneous Base: \u00a7e" + ((DoubleData) d).getValue());
((Mergeable) ret).merge(d);
}
// Return result
return (S) ret;
//UPGRD//MMOItems. Log("\u00a73:::\u00a77 Result: \u00a7b" + ((DoubleData) ret).getValue());
return ret;
}
/**
@ -442,9 +519,11 @@ public class StatHistory<S extends StatData> {
} catch (Throwable e) {
// Annoying
MMOItems.SLog("Error produced when getting stat history: \u00a7c" + e.getMessage() + "\u00a77 at \u00a76 " + e.getStackTrace()[0]);
// Feedbacc
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FriendlyFeedbackPalette_MMOItems.get());
ffp.ActivatePrefix(true, "Stat History");
ffp.Log(FriendlyFeedbackCategory.ERROR, "Could not get stat history: $f{0}$b at $f{1}", e.getMessage(), e.getStackTrace()[0].toString());
ffp.SendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
return null;
}
}

View File

@ -1,8 +1,16 @@
package net.Indyuce.mmoitems.stat.type;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The methods required for this ItemStat to be Upgradeable. <p></p>
* <b>It makes sense then that the <code>StatData</code> this uses
* implements {@link Mergeable}</b> and it is even assumed so.
*/
public interface Upgradable {
/*
@ -11,12 +19,32 @@ public interface Upgradable {
* increase when upgrading the item!
*/
/*
* argument 'obj' is either a primitive object like string, boolean or
* double or a configuration section. the method must check if this argument
* is of the right type. this method ONLY handles IllegalArgumentExceptions
/**
* When an {@link net.Indyuce.mmoitems.api.UpgradeTemplate} is read from a YML file,
* it loads each stat's {@link UpgradeInfo} through this method, passing on the
* object mapped to the stat in the configuration.
* <p></p>
* For example:
* <p><code>attack-damage: <b>10%</b></code>
* </p>Passes that <code><b>10%</b></code> as an object (not even as a string yet)
* <p></p>
* This method ONLY handles <code>IllegalArgumentException</code>s which
* you are free to throw.
* @param obj Is the thing itself written onto the YML file.
* <p></p>
* It is up to you to know what this is, most universally
* a string that you can get with <code>obj.toString()</code>.
* <p></p>
* If your stat is more complicated than just a number, it
* may be a {@link org.bukkit.configuration.ConfigurationSection}, who knows?
* @throws IllegalArgumentException If something (anything) goes wrong.
*/
UpgradeInfo loadUpgradeInfo(Object obj);
@NotNull UpgradeInfo loadUpgradeInfo(@Nullable Object obj) throws IllegalArgumentException;
void apply(MMOItem mmoitem, UpgradeInfo info);
/**
* Applies this upgrade info <b>level</b> times to this stat data.
* @param level The level of the returned StatData, how many times the Upgrade Info will be applied.
* May be negative if the stat supports it.
*/
@NotNull StatData apply(@NotNull StatData original, @NotNull UpgradeInfo info, int level);
}

View File

@ -86,7 +86,9 @@ item-upgrading:
# How stat changes are formatted
# in the stat suffix.
stat-change-suffix: '&f &e(+#stat#)'
stat-change-suffix: ' &8(&p#stat#&8)'
stat-change-positive: '&a'
stat-change-negative: '&c'
# Whether or not to display which
# stats are changed in the lore.