From 8d89d622c9365ec81e17797c0af8e820dfbee6d4 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 25 Jan 2025 07:57:28 -0800 Subject: [PATCH 1/2] Paste water at spawn_here sign location if sign is waterlogged Prevents the air block around the user when they spawn. --- .../bentobox/util/DefaultPasteUtil.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 2de5ae600..4b4020a9f 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -18,6 +18,7 @@ import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.type.WallSign; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; @@ -26,6 +27,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; +import org.bukkit.spawner.TrialSpawnerConfiguration; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.localization.TextVariables; @@ -141,6 +143,9 @@ else if (bs instanceof InventoryHolder holder) { else if (bs instanceof CreatureSpawner spawner) { setSpawner(spawner, bpBlock.getCreatureSpawner()); } + else if (bs instanceof TrialSpawnerConfiguration spawner) { + bpBlock.getTrialSpawner().configTrialSpawner(spawner); + } // Banners else if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) { bpBlock.getBannerPatterns().removeIf(Objects::isNull); @@ -261,21 +266,20 @@ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island * @param bpSign - BlueprintBlock that is the sign * @param side - the side being written */ + @SuppressWarnings("deprecation") public static void writeSign(Island island, final Block block, BlueprintBlock bpSign, Side side) { List lines = bpSign.getSignLines(side); boolean glow = bpSign.isGlowingText(side); - - BlockFace bf; - if (block.getType().name().contains("WALL_SIGN")) { - WallSign wallSign = (WallSign) block.getBlockData(); - bf = wallSign.getFacing(); - } else { - org.bukkit.block.data.type.Sign sign = (org.bukkit.block.data.type.Sign) block.getBlockData(); - bf = sign.getRotation(); - } + BlockData bd = block.getBlockData(); + BlockFace bf = (bd instanceof WallSign ws) ? ws.getFacing() + : ((org.bukkit.block.data.type.Sign) bd).getRotation(); // Handle spawn sign if (side == Side.FRONT && island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) { - block.setType(Material.AIR); + if (bd instanceof Waterlogged wl && wl.isWaterlogged()) { + block.setType(Material.WATER); + } else { + block.setType(Material.AIR); + } // Orient to face same direction as sign Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(), block.getZ() + 0.5D, Util.blockFaceToFloat(bf.getOppositeFace()), 30F); From 105549b901935158d85461b64c82f954637284c8 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 26 Jan 2025 18:42:13 -0800 Subject: [PATCH 2/2] Add support for Trial Spawners to Blueprints --- .../blueprints/BlueprintClipboard.java | 9 + .../dataobjects/BlueprintBlock.java | 20 ++ .../dataobjects/BlueprintTrialSpawner.java | 310 ++++++++++++++++++ .../bentobox/util/DefaultPasteUtil.java | 10 +- 4 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintTrialSpawner.java diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index 2b3c6c43e..34be11d43 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -19,6 +19,7 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; +import org.bukkit.block.TrialSpawner; import org.bukkit.block.data.Attachable; import org.bukkit.block.sign.Side; import org.bukkit.entity.Entity; @@ -38,6 +39,7 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintTrialSpawner; import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.MythicMobsHook; import world.bentobox.bentobox.hooks.ZNPCsPlusHook; @@ -294,6 +296,13 @@ private BlueprintBlock bluePrintBlock(Vector pos, Block block, boolean copyBiome if (blockState instanceof CreatureSpawner spawner) { b.setCreatureSpawner(getSpawner(spawner)); } + if (blockState instanceof TrialSpawner spawner) { + if (spawner.isOminous()) { + b.setTrialSpawner(new BlueprintTrialSpawner(true, spawner.getOminousConfiguration())); + } else { + b.setTrialSpawner(new BlueprintTrialSpawner(false, spawner.getNormalConfiguration())); + } + } // Banners if (blockState instanceof Banner banner) { b.setBannerPatterns(banner.getPatterns()); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java index 4bb7e9a68..b61223a67 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java @@ -34,6 +34,12 @@ public class BlueprintBlock { private Map inventory; @Expose private BlueprintCreatureSpawner creatureSpawner; + /** + * @since 3.4.2 + */ + @Expose + private BlueprintTrialSpawner trialSpawner; + /** * Since 1.15.2 */ @@ -217,4 +223,18 @@ public void setSignLines(Side side, List signLines) { this.signLines2 = signLines; } } + + /** + * @return the trialSpawner + */ + public BlueprintTrialSpawner getTrialSpawner() { + return trialSpawner; + } + + /** + * @param trialSpawner the trialSpawner to set + */ + public void setTrialSpawner(BlueprintTrialSpawner trialSpawner) { + this.trialSpawner = trialSpawner; + } } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintTrialSpawner.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintTrialSpawner.java new file mode 100644 index 000000000..2d4f22a68 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintTrialSpawner.java @@ -0,0 +1,310 @@ +package world.bentobox.bentobox.blueprints.dataobjects; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.block.spawner.SpawnRule; +import org.bukkit.block.spawner.SpawnerEntry; +import org.bukkit.entity.EntitySnapshot; +import org.bukkit.entity.EntityType; +import org.bukkit.loot.LootTable; +import org.bukkit.spawner.TrialSpawnerConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.google.gson.annotations.Expose; + +import world.bentobox.bentobox.BentoBox; + +/** + * @author tastybento + * @since 3.4.2 + */ +public class BlueprintTrialSpawner { + + @Expose + private boolean ominous; + @Expose + private EntityType spawnedType; + @Expose + private double addSimulEnts; + @Expose + private double addSpawnsB4Cool; + @Expose + private double baseSimEnts; + @Expose + private int delay; + @Expose + private @NotNull Map lootTableMap = new HashMap<>(); + @Expose + private int spawnRange; + @Expose + private int requiredPlayerRange; + @Expose + private int playerRange; + @Expose + private double baseSpawnsB4Cool; + @Expose + private List potentialSpawns; + + /** + * Record of nameSpace and key + */ + record LootTableSerial(@Expose String nameSpace, @Expose String key) { + } + + record PotentialSpawns(@Expose String snapshot, @Expose Map spawnrule, @Expose int spawnWeight) { + } + + public BlueprintTrialSpawner(boolean ominous, TrialSpawnerConfiguration spawner) { + this.ominous = ominous; + this.addSimulEnts = spawner.getAdditionalSimultaneousEntities(); + this.addSpawnsB4Cool = spawner.getAdditionalSpawnsBeforeCooldown(); + this.baseSimEnts = spawner.getBaseSimultaneousEntities(); + this.baseSpawnsB4Cool = spawner.getBaseSpawnsBeforeCooldown(); + this.lootTableMap = convertToLootTableSerial(spawner.getPossibleRewards()); + this.spawnRange = spawner.getSpawnRange(); + this.requiredPlayerRange = spawner.getRequiredPlayerRange(); + // Spawns + potentialSpawns = spawner.getPotentialSpawns().stream().map(se -> { + EntitySnapshot snapshot = se.getSnapshot(); + SpawnRule spawnRule = se.getSpawnRule(); + return new PotentialSpawns(snapshot.getAsString(), spawnRule == null ? null : spawnRule.serialize(), + se.getSpawnWeight()); + // Missing + // se.getEquipment().getEquipmentLootTable(); + }).toList(); + + if (potentialSpawns.isEmpty()) { + potentialSpawns = null; + this.spawnedType = spawner.getSpawnedType(); + } + } + + private Map convertToLootTableSerial(Map possibleRewards) { + // Use streams to map entries from LootTable to LootTableSerial + return possibleRewards.entrySet().stream().collect(Collectors.toMap(entry -> { // Convert LootTable to LootTableSerial + NamespacedKey key = entry.getKey().getKey(); + return new LootTableSerial(key.getNamespace(), key.getKey()); + }, Map.Entry::getValue // Keep the Integer value unchanged + )); + } + + // Method to convert Map back to Map + private Map convertToLootTable(Map serializedRewards) { + Map result = new HashMap<>(); + for (Map.Entry entry : serializedRewards.entrySet()) { + LootTableSerial lootTableSerial = entry.getKey(); + Integer value = Math.max(1, entry.getValue()); // weight has to be at least 1 + + // Reconstruct the NamespacedKey + NamespacedKey key = new NamespacedKey(lootTableSerial.nameSpace(), lootTableSerial.key()); + + // Fetch the LootTable using Bukkit + LootTable lootTable = Bukkit.getLootTable(key); + + if (lootTable != null) { // Ensure the LootTable exists + result.put(lootTable, value); + } else { + System.err.println("LootTable not found for key: " + key); + } + } + return result; + } + + /** + * Configure the trial spawner + * @param spawner trial spawner config + * @return true if trial spawner is ominous, false if normal + */ + public boolean configTrialSpawner(TrialSpawnerConfiguration spawner) { + spawner.setAdditionalSimultaneousEntities((float) addSimulEnts); + spawner.setAdditionalSpawnsBeforeCooldown((float) addSpawnsB4Cool); + spawner.setBaseSimultaneousEntities((float) baseSimEnts); + spawner.setBaseSpawnsBeforeCooldown((float) baseSpawnsB4Cool); + spawner.setDelay(delay); + spawner.setSpawnRange(spawnRange); + spawner.setPossibleRewards(convertToLootTable(lootTableMap)); // Note to future me: if the weight in the map is zero code stops running at this pojnt! + spawner.setRequiredPlayerRange(requiredPlayerRange); + // Either/or spawned type + if (spawnedType != null) { + spawner.setSpawnedType(spawnedType); + } else { + spawner.setPotentialSpawns(this.potentialSpawns.stream().map(ps -> { + EntitySnapshot snapshot = Bukkit.getEntityFactory().createEntitySnapshot(ps.snapshot()); + SpawnRule rule = ps.spawnrule() != null ? SpawnRule.deserialize(ps.spawnrule()) : null; + return new SpawnerEntry(snapshot, ps.spawnWeight(), rule); + }).collect(Collectors.toList())); + } + return this.isOminous(); + } + + /** + * @return the ominous + */ + public boolean isOminous() { + return ominous; + } + + /** + * @param ominous the ominous to set + */ + public void setOminous(boolean ominous) { + this.ominous = ominous; + } + + /** + * @return the spawnedType + */ + public EntityType getSpawnedType() { + return spawnedType; + } + + /** + * @param spawnedType the spawnedType to set + */ + public void setSpawnedType(EntityType spawnedType) { + this.spawnedType = spawnedType; + } + + /** + * @return the addSimulEnts + */ + public double getAddSimulEnts() { + return addSimulEnts; + } + + /** + * @param addSimulEnts the addSimulEnts to set + */ + public void setAddSimulEnts(double addSimulEnts) { + this.addSimulEnts = addSimulEnts; + } + + /** + * @return the addSpawnsB4Cool + */ + public double getAddSpawnsB4Cool() { + return addSpawnsB4Cool; + } + + /** + * @param addSpawnsB4Cool the addSpawnsB4Cool to set + */ + public void setAddSpawnsB4Cool(double addSpawnsB4Cool) { + this.addSpawnsB4Cool = addSpawnsB4Cool; + } + + /** + * @return the baseSimEnts + */ + public double getBaseSimEnts() { + return baseSimEnts; + } + + /** + * @param baseSimEnts the baseSimEnts to set + */ + public void setBaseSimEnts(double baseSimEnts) { + this.baseSimEnts = baseSimEnts; + } + + /** + * @return the delay + */ + public int getDelay() { + return delay; + } + + /** + * @param delay the delay to set + */ + public void setDelay(int delay) { + this.delay = delay; + } + + /** + * @return the lootTableMap + */ + public Map getLootTableMap() { + return lootTableMap; + } + + /** + * @param lootTableMap the lootTableMap to set + */ + public void setLootTableMap(Map lootTableMap) { + this.lootTableMap = lootTableMap; + } + + /** + * @return the spawnRange + */ + public int getSpawnRange() { + return spawnRange; + } + + /** + * @param spawnRange the spawnRange to set + */ + public void setSpawnRange(int spawnRange) { + this.spawnRange = spawnRange; + } + + /** + * @return the requiredPlayerRange + */ + public int getRequiredPlayerRange() { + return requiredPlayerRange; + } + + /** + * @param requiredPlayerRange the requiredPlayerRange to set + */ + public void setRequiredPlayerRange(int requiredPlayerRange) { + this.requiredPlayerRange = requiredPlayerRange; + } + + /** + * @return the playerRange + */ + public int getPlayerRange() { + return playerRange; + } + + /** + * @param playerRange the playerRange to set + */ + public void setPlayerRange(int playerRange) { + this.playerRange = playerRange; + } + + /** + * @return the baseSpawnsB4Cool + */ + public double getBaseSpawnsB4Cool() { + return baseSpawnsB4Cool; + } + + /** + * @param baseSpawnsB4Cool the baseSpawnsB4Cool to set + */ + public void setBaseSpawnsB4Cool(double baseSpawnsB4Cool) { + this.baseSpawnsB4Cool = baseSpawnsB4Cool; + } + + @Override + public String toString() { + return "BlueprintTrialSpawner [ominous=" + ominous + ", " + + (spawnedType != null ? "spawnedType=" + spawnedType + ", " : "") + "addSimulEnts=" + addSimulEnts + + ", addSpawnsB4Cool=" + addSpawnsB4Cool + ", baseSimEnts=" + baseSimEnts + ", delay=" + delay + ", " + + (lootTableMap != null ? "lootTableMap=" + lootTableMap + ", " : "") + "spawnRange=" + spawnRange + + ", requiredPlayerRange=" + requiredPlayerRange + ", playerRange=" + playerRange + + ", baseSpawnsB4Cool=" + baseSpawnsB4Cool + "]"; + } + +} diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 4b4020a9f..2403c5c2c 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -17,6 +17,7 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; +import org.bukkit.block.TrialSpawner; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.type.WallSign; @@ -28,6 +29,7 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.spawner.TrialSpawnerConfiguration; +import org.jetbrains.annotations.NotNull; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.localization.TextVariables; @@ -143,8 +145,12 @@ else if (bs instanceof InventoryHolder holder) { else if (bs instanceof CreatureSpawner spawner) { setSpawner(spawner, bpBlock.getCreatureSpawner()); } - else if (bs instanceof TrialSpawnerConfiguration spawner) { - bpBlock.getTrialSpawner().configTrialSpawner(spawner); + else if (bs instanceof TrialSpawner ts) { + TrialSpawnerConfiguration config = ts.getNormalConfiguration(); + ts.setOminous(bpBlock.getTrialSpawner().configTrialSpawner(config)); + if (!bs.update(true, false)) { + BentoBox.getInstance().logError("Trial Spawner update failed!"); + } } // Banners else if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {