Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
<img width="708" height="340" alt="image" src="https://github.com/user-attachments/assets/d9ff44b6-61dc-4680-8a80-03786b50d405" />


### Dynamic World Border (Admin Feature)

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.0.2</build.version>
<build.version>1.0.3</build.version>
<sonar.organization>bentobox-world</sonar.organization>
</properties>

Expand Down
24 changes: 7 additions & 17 deletions src/main/java/world/bentobox/stranger/StrangerRealms.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -20,13 +22,16 @@
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;
import org.bukkit.event.player.PlayerRespawnEvent;
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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -210,26 +220,26 @@ 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);
inTeleport.add(p.getUniqueId());
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())) {
Expand Down Expand Up @@ -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)));
}
}

Expand All @@ -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);
}
}
}