Fixed an issue with revid rerolling too often

This commit is contained in:
Jules 2023-04-23 13:29:38 +02:00
parent cb11502189
commit 40e0c9ad21
7 changed files with 90 additions and 162 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() {
return reRoll;

View File

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

View File

@ -28,18 +28,13 @@ import java.util.ArrayList;
import java.util.Objects;
/**
* A class to manage modification of items with reference to what they used to be
* (and apparently also used to automatically apply SoulBounds):
* A class to manage modification of items with reference to what
* 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
* 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
* @author Gunging, Jules
*/
public class MMOItemReforger {
@ -361,15 +356,13 @@ public class MMOItemReforger {
public boolean reforge(@NotNull ReforgeOptions options, @Nullable RPGPlayer player) {
// Throw fail
if (!hasTemplate())
return false;
if (!hasTemplate()) return false;
// Prepare everything properly
oldMMOItem = new LiveMMOItem(getNBTItem());
// Not blacklisted right!?
if (options.isBlacklisted(getOldMMOItem().getId()))
return false;
if (options.isBlacklisted(getOldMMOItem().getId())) return false;
this.player = player;
@ -398,8 +391,7 @@ public class MMOItemReforger {
Bukkit.getPluginManager().callEvent(mmoREV);
// Cancelled? it ends there
if (mmoREV.isCancelled())
return false;
if (mmoREV.isCancelled()) return false;
// Properly recalculate all based on histories
for (StatHistory hist : getFreshMMOItem().getStatHistories())

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.random.RandomStatData;
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 org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
@ -20,7 +18,7 @@ import java.util.Random;
*
* @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 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 random Result of <code>RANDOM.nextGaussian()</code> or whatever other
* value that you actually want to pass.
*
* @return The calculated value
*/
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
double actualBase = base + (scale * levelScalingFactor);
final double actualBase = base + (scale * levelScalingFactor);
/*
* This is one pick from a gaussian distribution
* at mean 0, and standard deviation 1, multiplied
* by the spread chosen.
* This is one pick from a gaussian distribution at mean 0, and
* standard deviation 1, multiplied 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!
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;
return useRelativeSpread ? actualBase * (1 + spreadCoef) : actualBase + spreadCoef;
}
@Override
@ -235,43 +222,27 @@ public class NumericStatFormula implements RandomStatData<DoubleData>, Updatable
@NotNull
@SuppressWarnings("unchecked")
@Override
public <T extends StatData> T reroll(@NotNull ItemStat stat, @NotNull T original, int determinedItemLevel) {
//UPGRD//MMOItems.log("\u00a7a +\u00a77 Valid for Double Data procedure\u00a78 {Original:\u00a77 " + ((DoubleData) original).getValue() + "\u00a78}");
public DoubleData reroll(@NotNull ItemStat stat, @NotNull DoubleData original, int determinedItemLevel) {
// Very well, chance checking is only available for NumericStatFormula class so
double scaledBase = getBase() + (getScale() * determinedItemLevel);
// Determine current
double current = ((DoubleData) original).getValue();
// 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);
// Very well, chance checking is only available for NumericStatFormula class
final double expectedValue = getBase() + (getScale() * determinedItemLevel);
final double previousValue = original.getValue();
final double shift = previousValue - expectedValue;
final double shiftSD = useRelativeSpread ? Math.abs(shift / (getSpread() * expectedValue)) : Math.abs(shift / getSpread());
final double maxSD = getMaxSpread() / getSpread();
// Greater than max spread? Or heck, 0.1% Chance or less wth
if (sD > getMaxSpread() || sD > 3.5) {
//UPGRD//MMOItems.log("\u00a7c -\u00a77 Ridiculous Range --- reroll");
if (shiftSD > maxSD || shiftSD > 3.5) {
// Adapt within reason
double reasonableShift = getSpread() * Math.min(2, getMaxSpread());
if (shift < 0) { reasonableShift *= -1;}
// That's the data we'll use
return (T) new DoubleData(reasonableShift + scaledBase);
// Just fully reroll value
return new DoubleData(calculate(determinedItemLevel));
// Data arguably fine tbh, just use previous
} else {
//UPGRD//MMOItems.log("\u00a7a +\u00a77 Acceptable Range --- kept");
// Just clone I guess
return (T) ((Mergeable) original).cloneData(); }
return original.cloneData(); }
}
public enum FormulaSaveOption {

View File

@ -17,7 +17,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
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<>();
public RandomElementListData(ConfigurationSection config) {
@ -58,7 +58,7 @@ public class RandomElementListData implements RandomStatData<ElementListData>, U
@NotNull
@Override
@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
ElementListData elements = new ElementListData();
@ -80,6 +80,6 @@ public class RandomElementListData implements RandomStatData<ElementListData>, U
}
// 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
* 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...
*
* <p>
* Example of unobtainable data: the current numeric stat value is now out
* of the numeric formula bounds (defined by max-spread).
*
* <p>
* 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)
* to be considered 'obtainable' and thus must be removed.
*
* <p>
* If a RandomStatData does not implement this, it will never be kept when
* updating items (always be rerolled with latest settings).
*
* @author Gunging
*/
@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 original The StatData currently in the item being reforged.
*
* @param determinedItemLevel The level of the item
*
* @return The rerolled StatData if the original is unreasonable.
* <br><br>
* 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

@ -1,17 +1,12 @@
package net.Indyuce.mmoitems.listener.reforging;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.event.MMOItemReforgeEvent;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
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.StatHistory;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Prevent previous RNG rolls of base stats
@ -21,64 +16,9 @@ import org.jetbrains.annotations.Nullable;
*/
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>
* 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:
@ -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
* new stat to actually add it for the first time.
*/
@Nullable StatData shouldReRollRegardless(@NotNull ItemStat stat, @NotNull RandomStatData source, @NotNull StatData original, int determinedItemLevel) {
// Not Mergeable, impossible to keep
if (!(source instanceof UpdatableRandomStatData))
//RFG// MMOItems.log("§8Reforge §3RNG§7 Stat\u00a7f " + stat.getId() + "\u00a77 is not updatable!");
return null;
@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;
// Just pass on
return ((UpdatableRandomStatData) source).reroll(stat, original, determinedItemLevel);
}
//RFG// MMOItems.log("§8Reforge §4EFG§7 Keeping old RNG Rolls");
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);
});
}
}