From 7d59edc8a51f65855d51932925f15d80b5ec4796 Mon Sep 17 00:00:00 2001 From: StrangeOne101 Date: Tue, 29 Jun 2021 00:24:36 +1200 Subject: [PATCH] Added Object & Jigsaw Loot Tables - Added the ability for placed objects and jigsaws to have select loot tables - Includes the ability to filter loot tables based on block type/block state - Overrides loot tables from the biome/region, but if no loot tables are provided for an object, they will still be used - Uses weight based system for determining which table to pick (so it's guaranteed rather than by chance) - Added WeightedRandom util class - Fixed loot tables not being random based on the seed - Fixed jigsaws breaking bedrock - Fixed enchantments in loot tables not working for enchanted books - Fixed mobs spawned in Jigsaws not being spawned in the center like they should be --- .../iris/generator/IrisEngineCompound.java | 4 - .../iris/generator/IrisEngineEffects.java | 3 + .../command/studio/CommandIrisStudioLoot.java | 5 +- .../volmit/iris/object/IrisEnchantment.java | 5 ++ .../com/volmit/iris/object/IrisObject.java | 8 +- .../volmit/iris/object/IrisObjectLoot.java | 72 +++++++++++++++++ .../iris/object/IrisObjectPlacement.java | 80 +++++++++++++++++++ .../volmit/iris/scaffold/engine/Engine.java | 11 +++ .../iris/scaffold/jigsaw/PlannedPiece.java | 36 ++++++++- .../scaffold/jigsaw/PlannedStructure.java | 11 +-- .../com/volmit/iris/util/WeightedRandom.java | 41 ++++++++++ 11 files changed, 261 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/volmit/iris/object/IrisObjectLoot.java create mode 100644 src/main/java/com/volmit/iris/util/WeightedRandom.java diff --git a/src/main/java/com/volmit/iris/generator/IrisEngineCompound.java b/src/main/java/com/volmit/iris/generator/IrisEngineCompound.java index 681563a38..72df72407 100644 --- a/src/main/java/com/volmit/iris/generator/IrisEngineCompound.java +++ b/src/main/java/com/volmit/iris/generator/IrisEngineCompound.java @@ -2,7 +2,6 @@ package com.volmit.iris.generator; import com.volmit.iris.Iris; import com.volmit.iris.manager.IrisDataManager; -import com.volmit.iris.nms.INMS; import com.volmit.iris.object.IrisDimension; import com.volmit.iris.object.IrisDimensionIndex; import com.volmit.iris.object.IrisPosition; @@ -15,14 +14,11 @@ import com.volmit.iris.scaffold.parallel.MultiBurst; import com.volmit.iris.util.*; import lombok.Getter; import lombok.Setter; -import net.minecraft.core.BlockPosition; -import net.minecraft.world.level.levelgen.feature.StructureGenerator; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; -import org.bukkit.craftbukkit.v1_17_R1.generator.InternalChunkGenerator; import org.bukkit.event.EventHandler; import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.generator.BlockPopulator; diff --git a/src/main/java/com/volmit/iris/generator/IrisEngineEffects.java b/src/main/java/com/volmit/iris/generator/IrisEngineEffects.java index 18cfb5fda..f6ce72e4b 100644 --- a/src/main/java/com/volmit/iris/generator/IrisEngineEffects.java +++ b/src/main/java/com/volmit/iris/generator/IrisEngineEffects.java @@ -27,6 +27,9 @@ public class IrisEngineEffects extends EngineAssignedComponent implements Engine @Override public void updatePlayerMap() { List pr = getEngine().getWorld().getPlayers(); + + if (pr == null) return; //Fix for paper returning a world with a null playerlist + for(Player i : pr) { Location l = i.getLocation(); diff --git a/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioLoot.java b/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioLoot.java index f475de168..3ffebd7fe 100644 --- a/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioLoot.java +++ b/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioLoot.java @@ -2,6 +2,7 @@ package com.volmit.iris.manager.command.studio; import com.volmit.iris.Iris; import com.volmit.iris.IrisSettings; +import com.volmit.iris.manager.IrisDataManager; import com.volmit.iris.object.InventorySlotType; import com.volmit.iris.object.IrisLootTable; import com.volmit.iris.scaffold.IrisWorlds; @@ -41,9 +42,9 @@ public class CommandIrisStudioLoot extends MortarCommand Player p = sender.player(); IrisAccess prov = IrisWorlds.access(sender.player().getWorld()); - if(prov == null) + if (!Iris.proj.isProjectOpen()) { - sender.sendMessage("You can only use /iris loot in a studio world of iris."); + sender.sendMessage("You can only use /iris studio loot in a studio world of iris."); return true; } diff --git a/src/main/java/com/volmit/iris/object/IrisEnchantment.java b/src/main/java/com/volmit/iris/object/IrisEnchantment.java index 58f795c38..6efb06c3f 100644 --- a/src/main/java/com/volmit/iris/object/IrisEnchantment.java +++ b/src/main/java/com/volmit/iris/object/IrisEnchantment.java @@ -3,6 +3,7 @@ package com.volmit.iris.object; import java.lang.reflect.Field; import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.ItemMeta; import com.volmit.iris.util.Desc; @@ -52,6 +53,10 @@ public class IrisEnchantment { if(rng.nextDouble() < chance) { + if (meta instanceof EnchantmentStorageMeta) { + ((EnchantmentStorageMeta) meta).addStoredEnchant(getEnchant(), getLevel(rng), true); + return; + } meta.addEnchant(getEnchant(), getLevel(rng), true); } } diff --git a/src/main/java/com/volmit/iris/object/IrisObject.java b/src/main/java/com/volmit/iris/object/IrisObject.java index 19aeda80d..38d86ed75 100644 --- a/src/main/java/com/volmit/iris/object/IrisObject.java +++ b/src/main/java/com/volmit/iris/object/IrisObject.java @@ -666,7 +666,12 @@ public class IrisObject extends IrisRegistrant for(BlockData k : j.getFind(rdata)) { if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { - data = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); + BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); + + if (newData.getMaterial() == data.getMaterial()) + data = data.merge(newData); + else + data = newData; } } } @@ -726,7 +731,6 @@ public class IrisObject extends IrisRegistrant { placer.setTile(xx, yy, zz, tile); } - } } readLock.unlock(); diff --git a/src/main/java/com/volmit/iris/object/IrisObjectLoot.java b/src/main/java/com/volmit/iris/object/IrisObjectLoot.java new file mode 100644 index 000000000..25c67c79e --- /dev/null +++ b/src/main/java/com/volmit/iris/object/IrisObjectLoot.java @@ -0,0 +1,72 @@ +package com.volmit.iris.object; + +import com.volmit.iris.Iris; +import com.volmit.iris.manager.IrisDataManager; +import com.volmit.iris.scaffold.cache.AtomicCache; +import com.volmit.iris.util.ArrayType; +import com.volmit.iris.util.Desc; +import com.volmit.iris.util.DontObfuscate; +import com.volmit.iris.util.KList; +import com.volmit.iris.util.RegistryListLoot; +import com.volmit.iris.util.Required; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.bukkit.block.data.BlockData; + +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Desc("Represents loot within this object or jigsaw piece") +@Data +public class IrisObjectLoot { + + @DontObfuscate + @ArrayType(min = 1, type = IrisBlockData.class) + @Desc("The list of blocks this loot table should apply to") + private KList filter = new KList<>(); + + @Desc("Exactly match the block data or not") + @DontObfuscate + private boolean exact = false; + + @DontObfuscate + @Desc("The loot table name") + @Required + @RegistryListLoot + private String name; + + @DontObfuscate + @Desc("The weight of this loot table being chosen") + private int weight = 1; + + private final transient AtomicCache> filterCache = new AtomicCache<>(); + + public KList getFilter(IrisDataManager rdata) + { + return filterCache.aquire(() -> + { + KList b = new KList<>(); + + for(IrisBlockData i : filter) + { + BlockData bx = i.getBlockData(rdata); + + if(bx != null) + { + b.add(bx); + } + } + + return b; + }); + } + + public boolean matchesFilter(IrisDataManager manager, BlockData data) { + for (BlockData filterData : getFilter(manager)) { + if (filterData.matches(data)) return true; + } + return false; + } +} diff --git a/src/main/java/com/volmit/iris/object/IrisObjectPlacement.java b/src/main/java/com/volmit/iris/object/IrisObjectPlacement.java index a5bf5102a..6d92923ea 100644 --- a/src/main/java/com/volmit/iris/object/IrisObjectPlacement.java +++ b/src/main/java/com/volmit/iris/object/IrisObjectPlacement.java @@ -1,6 +1,8 @@ package com.volmit.iris.object; +import com.volmit.iris.Iris; import com.volmit.iris.generator.noise.CNG; +import com.volmit.iris.manager.IrisDataManager; import com.volmit.iris.scaffold.cache.AtomicCache; import com.volmit.iris.scaffold.data.DataProvider; import com.volmit.iris.util.*; @@ -9,6 +11,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; @EqualsAndHashCode() @Accessors(chain = true) @@ -121,6 +125,11 @@ public class IrisObjectPlacement @Desc("Translate this object's placement") private IrisObjectTranslate translate = new IrisObjectTranslate(); + @ArrayType(min = 1, type = IrisObjectLoot.class) + @DontObfuscate + @Desc("The loot tables to apply to these objects") + private KList loot = new KList<>(); + public IrisObjectPlacement toPlacement(String... place) { IrisObjectPlacement p = new IrisObjectPlacement(); @@ -145,6 +154,7 @@ public class IrisObjectPlacement p.setSnow(snow); p.setClamp(clamp); p.setRotation(rotation); + p.setLoot(loot); return p; } @@ -191,4 +201,74 @@ public class IrisObjectPlacement public boolean isVacuum() { return getMode().equals(ObjectPlaceMode.VACUUM); } + + private transient AtomicCache cache = new AtomicCache<>(); + + private class TableCache { + transient WeightedRandom global = new WeightedRandom<>(); + transient KMap> basic = new KMap<>(); + transient KMap>> exact = new KMap<>(); + } + + private TableCache getCache(IrisDataManager manager) { + return cache.aquire(() -> { + TableCache tc = new TableCache(); + + for (IrisObjectLoot loot : getLoot()) { + IrisLootTable table = manager.getLootLoader().load(loot.getName()); + if (table == null) { + Iris.warn("Couldn't find loot table " + loot.getName()); + continue; + } + + if (loot.getFilter().isEmpty()) //Table applies to all containers + { + tc.global.put(table, loot.getWeight()); + } else if (!loot.isExact()) //Table is meant to be by type + { + for (BlockData filterData : loot.getFilter(manager)) { + if (!tc.basic.containsKey(filterData.getMaterial())) { + tc.basic.put(filterData.getMaterial(), new WeightedRandom<>()); + } + + tc.basic.get(filterData.getMaterial()).put(table, loot.getWeight()); + } + } else //Filter is exact + { + for (BlockData filterData : loot.getFilter(manager)) { + if (!tc.exact.containsKey(filterData.getMaterial())) { + tc.exact.put(filterData.getMaterial(), new KMap<>()); + } + + if (!tc.exact.get(filterData.getMaterial()).containsKey(filterData)) { + tc.exact.get(filterData.getMaterial()).put(filterData, new WeightedRandom<>()); + } + + tc.exact.get(filterData.getMaterial()).get(filterData).put(table, loot.getWeight()); + } + } + } + return tc; + }); + } + + public IrisLootTable getTable(BlockData data, IrisDataManager dataManager) { + TableCache cache = getCache(dataManager); + + if(B.isStorageChest(data)) + { + IrisLootTable picked = null; + if (cache.exact.containsKey(data.getMaterial()) && cache.exact.containsKey(data)) { + picked = cache.exact.get(data.getMaterial()).get(data).pullRandom(); + } else if (cache.basic.containsKey(data.getMaterial())) { + picked = cache.basic.get(data.getMaterial()).pullRandom(); + } else if (cache.global.getSize() > 0){ + picked = cache.global.pullRandom(); + } + + return picked; + } + + return null; + } } diff --git a/src/main/java/com/volmit/iris/scaffold/engine/Engine.java b/src/main/java/com/volmit/iris/scaffold/engine/Engine.java index 4da69706a..16f66341d 100644 --- a/src/main/java/com/volmit/iris/scaffold/engine/Engine.java +++ b/src/main/java/com/volmit/iris/scaffold/engine/Engine.java @@ -302,6 +302,17 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro int rx = b.getX(); int rz = b.getZ(); double he = getFramework().getComplex().getHeightStream().get(rx, rz); + PlacedObject po = getFramework().getEngine().getObjectPlacement(rx, b.getY(), rz); + if (po != null && po.getPlacement() != null) { + + if(B.isStorageChest(b.getBlockData())) + { + IrisLootTable table = po.getPlacement().getTable(b.getBlockData(), getData()); + if (table != null) { + return new KList<>(table); + } + } + } IrisRegion region = getFramework().getComplex().getRegionStream().get(rx, rz); IrisBiome biomeSurface = getFramework().getComplex().getTrueBiomeStream().get(rx, rz); IrisBiome biomeUnder = b.getY() < he ? getFramework().getComplex().getCaveBiomeStream().get(rx, rz) : biomeSurface; diff --git a/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedPiece.java b/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedPiece.java index 19420afa0..f6ac95609 100644 --- a/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedPiece.java +++ b/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedPiece.java @@ -1,17 +1,25 @@ package com.volmit.iris.scaffold.jigsaw; +import com.volmit.iris.Iris; +import com.volmit.iris.generator.IrisEngine; import com.volmit.iris.manager.IrisDataManager; import com.volmit.iris.object.*; import com.volmit.iris.object.tile.TileData; +import com.volmit.iris.scaffold.IrisWorlds; +import com.volmit.iris.scaffold.engine.Engine; +import com.volmit.iris.scaffold.engine.IrisAccess; import com.volmit.iris.util.AxisAlignedBB; import com.volmit.iris.util.IObjectPlacer; import com.volmit.iris.util.KList; import com.volmit.iris.util.RNG; import lombok.Data; +import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.TileState; import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.util.BlockVector; @Data @@ -129,8 +137,17 @@ public class PlannedPiece { } public void place(World world) { + IrisAccess a = IrisWorlds.access(world); + + int minY = 0; + if (a != null) { + minY = a.getCompound().getDefaultEngine().getMinHeight(); + + if (!a.getCompound().getRootDimension().isBedrock()) minY--; //If the dimension has no bedrock, allow it to go a block lower + } getPiece().getPlacementOptions().getRotation().setEnabled(false); + int finalMinY = minY; getObject().place(position.getX()+getObject().getCenter().getBlockX(), position.getY()+getObject().getCenter().getBlockY(), position.getZ()+getObject().getCenter().getBlockZ(), new IObjectPlacer() { @Override public int getHighest(int x, int z) { @@ -144,7 +161,22 @@ public class PlannedPiece { @Override public void set(int x, int y, int z, BlockData d) { - world.getBlockAt(x,y,z).setBlockData(d); + Block block = world.getBlockAt(x, y, z); + + //Prevent blocks being set in or bellow bedrock + if (y <= finalMinY || block.getType() == Material.BEDROCK) return; + + block.setBlockData(d); + + if (a != null && getPiece().getPlacementOptions().getLoot().isNotEmpty() && + block.getState() instanceof InventoryHolder) { + + IrisLootTable table = getPiece().getPlacementOptions().getTable(block.getBlockData(), getData()); + if (table == null) return; + Engine engine = a.getCompound().getEngineForHeight(y); + engine.addItems(false, ((InventoryHolder) block.getState()).getInventory(), getStructure().getRng(), + new KList<>(table), InventorySlotType.STORAGE, x, y, z, 15); + } } @Override @@ -183,6 +215,6 @@ public class PlannedPiece { tile.toBukkitTry(state); state.update(); } - }, piece.getPlacementOptions(), new RNG(), getData()); + }, piece.getPlacementOptions(), getStructure().getRng(), getData()); } } diff --git a/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedStructure.java b/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedStructure.java index 350797d0c..3c56f2a7a 100644 --- a/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedStructure.java +++ b/src/main/java/com/volmit/iris/scaffold/jigsaw/PlannedStructure.java @@ -162,14 +162,15 @@ public class PlannedStructure { { if(j.getSpawnEntity() != null) { + IrisAccess a = IrisWorlds.access(world); + if (a == null) { + Iris.warn("Cannot spawn entities from jigsaw in non Iris world!"); + break; + } IrisPosition p = i.getWorldPosition(j).add(new IrisPosition(j.getDirection().toVector().multiply(2))); IrisEntity e = getData().getEntityLoader().load(j.getSpawnEntity()); - IrisAccess a = IrisWorlds.access(world); - if(a != null) - { - e.spawn(a.getCompound().getEngineForHeight(p.getY()), new Location(world, p.getX(), p.getY(), p.getZ()), rng); - } + e.spawn(a.getCompound().getEngineForHeight(p.getY()), new Location(world, p.getX() + 0.5, p.getY(), p.getZ() + 0.5), rng); } } }); diff --git a/src/main/java/com/volmit/iris/util/WeightedRandom.java b/src/main/java/com/volmit/iris/util/WeightedRandom.java new file mode 100644 index 000000000..5b3219e2e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/WeightedRandom.java @@ -0,0 +1,41 @@ +package com.volmit.iris.util; + +import java.util.Random; + +public class WeightedRandom { + + private KList> weightedObjects = new KList<>(); + private Random random; + private int totalWeight = 0; + + public WeightedRandom(Random random) { + this.random = random; + } + + public WeightedRandom() { + this.random = new Random(); + } + + public void put(T object, int weight) { + weightedObjects.add(new KeyPair<>(object, weight)); + totalWeight += weight; + } + + public T pullRandom() { + int pull = random.nextInt(totalWeight); + int index = 0; + while (pull > 0) { + pull -= weightedObjects.get(index).getV(); + index++; + } + return weightedObjects.get(index).getK(); + } + + public int getSize() { + return weightedObjects.size(); + } + + public void shuffle() { + weightedObjects.shuffle(random); + } +}