New capacity and shuffle option for block/fishing drop tables

This commit is contained in:
Jules 2024-07-26 15:03:40 -07:00
parent 5759f78924
commit b80cda490c
8 changed files with 130 additions and 96 deletions

View File

@ -84,6 +84,7 @@ public class BlockInfo {
return table != null;
}
@Deprecated
public List<ItemStack> collectDrops(LootBuilder builder) {
return table != null ? table.collect(builder) : new ArrayList<>();
}

View File

@ -1,10 +1,11 @@
package net.Indyuce.mmocore.loot;
import java.util.ArrayList;
import java.util.List;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class LootBuilder {
private final PlayerData player;
@ -12,18 +13,22 @@ public class LootBuilder {
private double capacity;
public static double DEFAULT_CAPACITY = 100;
public LootBuilder(PlayerData player) {
this(player, DEFAULT_CAPACITY);
}
/**
* Used to create loot from a drop table
*
* @param player
* Player looting
* @param capacity
* Capacity is the maximum amount of item weight generated using
* @param player Player looting
* @param capacity Capacity is the maximum amount of item weight generated using
* this table. If capacity is set to 10, this table cannot drop
* an item with 5 weight and another with 6 weight at the saeme
* time.
*/
public LootBuilder(PlayerData player, double capacity) {
public LootBuilder(@NotNull PlayerData player, double capacity) {
this.player = player;
this.capacity = capacity;
}

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmocore.loot;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.loot.droptable.dropitem.DropItem;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@ -17,23 +18,24 @@ public class RandomWeightedRoll<T extends Weighted> {
private final T rolled;
private static final Random RANDOM = new Random();
private static final double CHANCE_COEFFICIENT = 7. / 100;
public RandomWeightedRoll(PlayerData player, Collection<T> collection, double chanceWeight) {
this.collection = collection;
double partialSum = 0;
final double randomCoefficient = RANDOM.nextDouble(), chance = chanceWeight * player.getStats().getStat("CHANCE"), sum = weightedSum(chance);
final double randomCoefficient = RANDOM.nextDouble(),
effectiveLuck = DropItem.CHANCE_FACTOR * chanceWeight * player.getStats().getStat("CHANCE"),
sum = weightedSum(effectiveLuck);
for (T item : collection) {
partialSum += computeRealWeight(item, chance);
partialSum += computeRealWeight(item, effectiveLuck);
if (partialSum >= randomCoefficient * sum) {
rolled = item;
return;
}
}
throw new RuntimeException("Could not roll item, the chance is :"+chance);
throw new RuntimeException("Could not roll item, effective luck is " + effectiveLuck);
}
/**
@ -47,10 +49,10 @@ public class RandomWeightedRoll<T extends Weighted> {
return rolled;
}
private double weightedSum(double chance) {
private double weightedSum(double effectiveLuck) {
double sum = 0;
for (T item : collection)
sum += computeRealWeight(item, chance);
sum += computeRealWeight(item, effectiveLuck);
return sum;
}
@ -62,8 +64,8 @@ public class RandomWeightedRoll<T extends Weighted> {
*
* @return The real weight of an item considering the player's chance stat.
*/
private double computeRealWeight(T item, double chance) {
return Math.pow(item.getWeight(), 1 / Math.pow(1 + CHANCE_COEFFICIENT * chance, 1. / 3.));
private double computeRealWeight(T item, double effectiveLuck) {
return Math.pow(item.getWeight(), Math.pow(1 + effectiveLuck, -DropItem.CHANCE_POWER));
}
/*

View File

@ -1,27 +1,34 @@
package net.Indyuce.mmocore.loot.chest;
import io.lumine.mythic.lib.api.math.ScalingFormula;
import io.lumine.mythic.lib.util.annotation.BackwardsCompatibility;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.loot.Weighted;
import net.Indyuce.mmocore.loot.droptable.DropTable;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ChestTier implements Weighted {
private final TierEffect effect;
// TODO Capacity should be inherent to drop table
// TODO make capacity any numeric formula, parsed with a player
@BackwardsCompatibility(version = "1.12.1")
@Nullable
private final ScalingFormula capacity;
private final DropTable table;
private final double chance;
public ChestTier(ConfigurationSection config) {
effect = config.isConfigurationSection("effect") ? new TierEffect(config.getConfigurationSection("effect")) : null;
capacity = new ScalingFormula(config.get("capacity"));
capacity = config.contains("capacity") ? new ScalingFormula(config.get("capacity")) : null;
chance = config.getDouble("chance");
table = MMOCore.plugin.dropTableManager.loadDropTable(config.get("drops"));
}
public double rollCapacity(PlayerData player) {
return capacity.calculate(player.getLevel());
public double rollCapacity(@NotNull PlayerData player) {
return capacity == null ? table.getCapacity() : capacity.calculate(player.getLevel());
}
public double getChance() {
@ -33,6 +40,7 @@ public class ChestTier implements Weighted {
return chance;
}
@NotNull
public DropTable getDropTable() {
return table;
}

View File

@ -13,15 +13,17 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
public class DropTable implements PreloadedObject {
private final String id;
private final Set<DropItem> drops = new LinkedHashSet<>();
private final Set<Condition> conditions = new LinkedHashSet<>();
private final double capacity;
private final boolean shuffle;
private final List<DropItem> drops = new ArrayList<>();
private final List<Condition> conditions = new ArrayList<>();
private final PostLoadAction postLoadAction;
@ -30,11 +32,16 @@ public class DropTable implements PreloadedObject {
this.postLoadAction.cacheConfig(config);
this.id = config.getName();
this.shuffle = config.getBoolean("shuffle");
this.capacity = config.getDouble("capacity", LootBuilder.DEFAULT_CAPACITY);
Validate.isTrue(capacity >= 0, "Capacity must be positive");
}
public DropTable(String id) {
this.postLoadAction = generatePostLoadAction();
this.id = id;
this.capacity = 100;
this.shuffle = false;
}
private PostLoadAction generatePostLoadAction() {
@ -76,13 +83,27 @@ public class DropTable implements PreloadedObject {
drops.add(item);
}
public Set<DropItem> getDrops() {
public double getCapacity() {
return capacity;
}
@NotNull
public List<DropItem> getDrops() {
return drops;
}
@NotNull
public List<ItemStack> collect(LootBuilder builder) {
for (DropItem item : drops)
// Shuffle items?
final List<DropItem> items;
if (shuffle) {
items = new ArrayList<>(drops);
Collections.shuffle(items);
} else items = drops;
// Collect items
for (DropItem item : items)
if (item.rollChance(builder.getEntity()) && builder.getCapacity() >= item.getWeight()) {
item.collect(builder);
builder.reduceCapacity(item.getWeight());
@ -91,7 +112,8 @@ public class DropTable implements PreloadedObject {
return builder.getLoot();
}
public Set<Condition> getConditions() {
@NotNull
public List<Condition> getConditions() {
return conditions;
}

View File

@ -14,8 +14,6 @@ public abstract class DropItem {
private final double chance, weight;
private final RandomAmount amount;
private static final double CHANCE_COEFFICIENT = 7. / 100;
public DropItem(MMOLineConfig config) {
chance = config.args().length > 0 ? Double.parseDouble(config.args()[0]) : 1;
amount = config.args().length > 1 ? new RandomAmount(config.args()[1]) : new RandomAmount(1, 1);
@ -38,16 +36,14 @@ public abstract class DropItem {
return amount.calculateInt();
}
/**
* CHANCE stat = 0 | tier chances are unchanged
* CHANCE stat = +inf | uniform law for any drop item
* CHANCE stat = 100 | all tier chances are taken their square root
*
* @return The real weight of an item considering the player's CHANCE stat.
*/
/// TODO make it configurable
@Deprecated
public static final double CHANCE_FACTOR = 7. / 100, CHANCE_POWER = 0.33333333333;
public boolean rollChance(PlayerData player) {
double value = random.nextDouble();
return value < Math.pow(chance, 1 / Math.pow(1 + CHANCE_COEFFICIENT * MMOCore.plugin.configManager.dropItemsChanceWeight* player.getStats().getStat("CHANCE"), 1.0 / 3.0));
final double effectiveLuck = CHANCE_FACTOR * MMOCore.plugin.configManager.dropItemsChanceWeight * player.getStats().getStat("CHANCE");
final double randomValue = random.nextDouble();
return randomValue < Math.pow(chance, Math.pow(1 + effectiveLuck, CHANCE_POWER));
}
public abstract void collect(LootBuilder builder);

View File

@ -87,7 +87,7 @@ public class BlockListener implements Listener {
// Find the block drops
boolean conditionsMet = !info.hasDropTable() || info.getDropTable().areConditionsMet(new ConditionInstance(player));
List<ItemStack> drops = conditionsMet && info.hasDropTable() ? info.getDropTable().collect(new LootBuilder(PlayerData.get(player), 0)) : new ArrayList<>();
List<ItemStack> drops = conditionsMet && info.hasDropTable() ? info.getDropTable().collect(new LootBuilder(PlayerData.get(player), info.getDropTable().getCapacity())) : new ArrayList<>();
/*
* Calls the event and listen for cancel & for drops changes... also

View File

@ -170,7 +170,7 @@ public class FishingListener implements Listener {
}
// Find looted item
ItemStack collect = caught.collect(new LootBuilder(playerData, 0));
ItemStack collect = caught.collect(new LootBuilder(playerData));
if (collect == null) {
hook.getWorld().spawnParticle(VParticle.SMOKE.get(), location, 24, 0, 0, 0, .08);
return;