diff --git a/README.md b/README.md index 172034e..38bd275 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ As a BentoBox game mode, survival hinges on laying claim to your piece of the wo ### The Warped Compass * **Chunk Regeneration:** Discover the custom recipe for the **Warped Compass**. When a player holds this powerful item while traveling through a Nether Portal, it forces the regeneration of chunks in all directions, ensuring you always have fresh territory to explore based on the Overworld. +The recipe is: +image + ### Dynamic World Border (Admin Feature) diff --git a/pom.xml b/pom.xml index 8b82624..9bf4650 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ -LOCAL - 1.0.2 + 1.0.3 bentobox-world diff --git a/src/main/java/world/bentobox/stranger/StrangerRealms.java b/src/main/java/world/bentobox/stranger/StrangerRealms.java index a236794..5b1ccd2 100644 --- a/src/main/java/world/bentobox/stranger/StrangerRealms.java +++ b/src/main/java/world/bentobox/stranger/StrangerRealms.java @@ -20,10 +20,12 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.scheduler.BukkitTask; import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.NotNull; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand; import world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand; @@ -57,7 +59,8 @@ public class StrangerRealms extends GameModeAddon { private static final String THE_END = "_the_end"; // Define a static key for the custom item, primarily for referencing its material later if needed. - public static final Material WARPED_COMPASS_MATERIAL = Material.COMPASS; + private static final Material WARPED_COMPASS_MATERIAL = Material.COMPASS; + public static final @NotNull NamespacedKey WARPED_COMPASS_RECIPE = new NamespacedKey(BentoBox.getInstance(), "warped_compass"); // Settings @@ -223,24 +226,12 @@ private World getWorld(String worldName2, Environment env) { } private void setSpawnRates(World w) { - if (getSettings().getSpawnLimitMonsters() > 0) { w.setSpawnLimit(SpawnCategory.MONSTER, getSettings().getSpawnLimitMonsters()); - } - if (getSettings().getSpawnLimitAmbient() > 0) { w.setSpawnLimit(SpawnCategory.AMBIENT, getSettings().getSpawnLimitAmbient()); - } - if (getSettings().getSpawnLimitAnimals() > 0) { - w.setSpawnLimit(SpawnCategory.ANIMAL, getSettings().getSpawnLimitAnimals()); - } - if (getSettings().getSpawnLimitWaterAnimals() > 0) { - w.setSpawnLimit(SpawnCategory.WATER_ANIMAL, getSettings().getSpawnLimitWaterAnimals()); - } - if (getSettings().getTicksPerAnimalSpawns() > 0) { + w.setSpawnLimit(SpawnCategory.ANIMAL, getSettings().getSpawnLimitAnimals()); + w.setSpawnLimit(SpawnCategory.WATER_ANIMAL, getSettings().getSpawnLimitWaterAnimals()); w.setTicksPerSpawns(SpawnCategory.ANIMAL, getSettings().getTicksPerAnimalSpawns()); - } - if (getSettings().getTicksPerMonsterSpawns() > 0) { w.setTicksPerSpawns(SpawnCategory.MONSTER, getSettings().getTicksPerMonsterSpawns()); - } } @Override @@ -384,8 +375,7 @@ private void registerWarpedCompassRecipe() { // --- 2. Create the NamespacedKey and ShapedRecipe --- // A NamespacedKey is required for the recipe to be uniquely identified. - NamespacedKey key = new NamespacedKey(this.getPlugin(), "warped_compass"); - ShapedRecipe recipe = new ShapedRecipe(key, warpedCompass); + ShapedRecipe recipe = new ShapedRecipe(WARPED_COMPASS_RECIPE, warpedCompass); // --- 3. Define the Recipe Shape --- recipe.shape( diff --git a/src/main/java/world/bentobox/stranger/listeners/PlayerListener.java b/src/main/java/world/bentobox/stranger/listeners/PlayerListener.java index 6548591..95ee919 100644 --- a/src/main/java/world/bentobox/stranger/listeners/PlayerListener.java +++ b/src/main/java/world/bentobox/stranger/listeners/PlayerListener.java @@ -10,8 +10,10 @@ import org.bukkit.Bukkit; import org.bukkit.GameMode; +import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -20,6 +22,8 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDismountEvent; import org.bukkit.event.entity.EntityMountEvent; +import org.bukkit.event.inventory.PrepareItemCraftEvent; +import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; @@ -27,6 +31,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.event.vehicle.VehicleMoveEvent; +import org.bukkit.inventory.Recipe; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.NumberConversions; import org.bukkit.util.RayTraceResult; @@ -71,11 +76,16 @@ public PlayerListener(StrangerRealms addon) { */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerJoin(PlayerJoinEvent e) { + // Set the recipe + updateRecipeAccess(e.getPlayer()); + Player player = e.getPlayer(); // Check if player is in the world if (!addon.inWorld(player.getWorld())) { return; } + + if (isOn(player)) { // Run one-tick after joining because metadata cannot be set otherwise Bukkit.getScheduler().runTask(addon.getPlugin(), () -> processEvent(e)); @@ -210,7 +220,7 @@ public void onPlayerLeaveIsland(PlayerMoveEvent e) { if (!outsideCheck(e.getPlayer(), from, e.getTo())) { return; } - + // If player is still in protected area, cancel movement and teleport back if (addon.getIslands().getProtectedIslandAt(from).isPresent()) { e.setCancelled(true); @@ -218,18 +228,18 @@ public void onPlayerLeaveIsland(PlayerMoveEvent e) { Util.teleportAsync(p, from).thenRun(() -> inTeleport.remove(p.getUniqueId())); return; } - + // If outside, calculate the closest safe position on border addon.getIslands().getIslandAt(p.getLocation()).ifPresent(i -> { // Calculate vector pointing from player to island center Vector unitVector = i.getProtectionCenter().toVector().subtract(p.getLocation().toVector()).normalize() .multiply(new Vector(1,0,1)); - + // Skip if no valid direction found if (unitVector.lengthSquared() <= 0D) { return; } - + // Perform ray trace to find intersection with border RayTraceResult r = i.getProtectionBoundingBox().rayTrace(p.getLocation().toVector(), unitVector, i.getRange()); if (r != null && checkFinite(r.getHitPosition())) { @@ -362,7 +372,7 @@ public void onVehicleMove(VehicleMoveEvent e) { // Remove head movement if (!e.getFrom().toVector().equals(e.getTo().toVector())) { e.getVehicle().getPassengers().stream().filter(Player.class::isInstance).map(Player.class::cast) - .filter(this::isOn).forEach(p -> show.refreshView(User.getInstance(p))); + .filter(this::isOn).forEach(p -> show.refreshView(User.getInstance(p))); } } @@ -380,4 +390,54 @@ public void onProtectionRangeChange(IslandProtectionRangeChangeEvent e) { } }); } + + /** + * Updates the player's access to the warped compass recipe based on their current world. + * @param player The player to check. + */ + private void updateRecipeAccess(Player player) { + World currentWorld = player.getWorld(); + + // Check if the player is in the custom world + if (addon.inWorld(currentWorld)) { + // Player is in the custom world: grant the recipe + if (!player.hasDiscoveredRecipe(StrangerRealms.WARPED_COMPASS_RECIPE)) { + // Use a scheduler for safety, though for this specific task it might not be strictly needed. + // It ensures the action is run on the main server thread, which is good practice. + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> { + player.discoverRecipe(StrangerRealms.WARPED_COMPASS_RECIPE); + }); + } + } else { + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> { + player.undiscoverRecipe(StrangerRealms.WARPED_COMPASS_RECIPE); + }); + } + } + + /** + * Handles players changing worlds (e.g., via /tp, portals) - add or remove recipe + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onWorldChange(PlayerChangedWorldEvent event) { + updateRecipeAccess(event.getPlayer()); + } + + /** + * Blocks the custom item from being crafted if the player is not + * in the designated custom world. + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPrepareItemCraft(PrepareItemCraftEvent event) { + Recipe recipe = event.getRecipe(); + + // Check if a recipe exists and if it is a keyed recipe + if (recipe != null && recipe instanceof Keyed keyed + && keyed.getKey().equals(StrangerRealms.WARPED_COMPASS_RECIPE) + && !addon.inWorld(event.getInventory().getLocation())) { + // BLOCK: Set the result to null + event.getInventory().setResult(null); + } + } } +