|
20 | 20 | import org.bukkit.block.Biome; |
21 | 21 | import org.bukkit.block.Block; |
22 | 22 | import org.bukkit.block.BlockFace; |
23 | | -import org.bukkit.block.BrushableBlock; |
24 | 23 | import org.bukkit.block.Chest; |
25 | | -import org.bukkit.block.SuspiciousSand; |
26 | 24 | import org.bukkit.block.data.Brushable; |
27 | 25 | import org.bukkit.block.data.type.Leaves; |
28 | 26 | import org.bukkit.entity.Entity; |
|
37 | 35 | import org.bukkit.event.block.BlockFromToEvent; |
38 | 36 | import org.bukkit.event.entity.EntityInteractEvent; |
39 | 37 | import org.bukkit.event.entity.EntitySpawnEvent; |
| 38 | +import org.bukkit.event.entity.ItemSpawnEvent; |
40 | 39 | import org.bukkit.event.player.PlayerBucketFillEvent; |
41 | 40 | import org.bukkit.event.player.PlayerInteractEvent; |
42 | 41 | import org.bukkit.inventory.EquipmentSlot; |
43 | 42 | import org.bukkit.inventory.ItemStack; |
44 | | -import org.bukkit.loot.LootTables; |
45 | 43 | import org.bukkit.util.Vector; |
46 | 44 | import org.eclipse.jdt.annotation.NonNull; |
47 | 45 | import org.eclipse.jdt.annotation.Nullable; |
48 | 46 |
|
49 | | -import com.bgsoftware.wildstacker.api.loot.LootTable; |
50 | | - |
51 | 47 | import world.bentobox.aoneblock.AOneBlock; |
52 | 48 | import world.bentobox.aoneblock.dataobjects.OneBlockIslands; |
53 | 49 | import world.bentobox.aoneblock.events.MagicBlockEntityEvent; |
@@ -109,8 +105,6 @@ public class BlockListener implements Listener { |
109 | 105 | */ |
110 | 106 | public static final int SAVE_EVERY = 50; |
111 | 107 |
|
112 | | - private final Random random = new Random(); |
113 | | - |
114 | 108 | // Loot for suspicious blocks |
115 | 109 | private static final Map<Material, Double> LOOT; |
116 | 110 | static { |
@@ -247,38 +241,61 @@ public void onBlockBreak(final PlayerBucketFillEvent e) { |
247 | 241 | } |
248 | 242 | } |
249 | 243 |
|
| 244 | + |
250 | 245 | /** |
251 | | - * Drop items at the top of the block. |
252 | | - * |
253 | | - * @param event EntitySpawnEvent object. |
| 246 | + * This handler listens for items spawning. |
| 247 | + * If an item spawns exactly at an island's center block, |
| 248 | + * it cancels the spawn and re-drops the item 1 block higher |
| 249 | + * (at the center of that block) to stack it neatly. |
254 | 250 | */ |
255 | 251 | @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) |
256 | | - public void onItemStackSpawn(EntitySpawnEvent event) { |
| 252 | + public void onItemSpawn(ItemSpawnEvent event) { |
| 253 | + // --- Guard Clauses: Exit early if conditions aren't met --- |
| 254 | + |
| 255 | + // 1. Check if the "drop on top" feature is enabled. |
257 | 256 | if (!this.addon.getSettings().isDropOnTop()) { |
258 | | - // Do nothing as item spawning is not interested in this case. |
| 257 | + // Feature is disabled, so we don't need to do anything. |
259 | 258 | return; |
260 | 259 | } |
261 | 260 |
|
262 | | - if (!EntityType.ITEM.equals(event.getEntityType())) { |
263 | | - // We are interested only in dropped item entities. |
264 | | - return; |
265 | | - } |
| 261 | + // Get the spawn location once. |
| 262 | + Location spawnLocation = event.getLocation(); |
266 | 263 |
|
267 | | - if (!this.addon.inWorld(event.getLocation().getWorld())) { |
268 | | - // Not correct world |
| 264 | + // 2. Check if the spawn is happening in a world managed by the addon. |
| 265 | + if (!this.addon.inWorld(spawnLocation.getWorld())) { |
| 266 | + // Not a relevant world, ignore this event. |
269 | 267 | return; |
270 | 268 | } |
271 | 269 |
|
272 | | - Entity entity = event.getEntity(); |
273 | | - Location location = event.getLocation(); |
| 270 | + // Find an island at the spawn location. |
| 271 | + Optional<Island> optionalIsland = this.addon.getIslands().getIslandAt(spawnLocation) |
| 272 | + // Chained to the Optional: Filter the island. |
| 273 | + // Only keep it if the block the item spawned in |
| 274 | + // is *exactly* the island's center. |
| 275 | + .filter(island -> { |
| 276 | + // .getBlock().getLocation() converts a precise location |
| 277 | + // (e.g., 10.2, 64.5, 12.8) to its block's location (10.0, 64.0, 12.0). |
| 278 | + Location blockLocation = spawnLocation.getBlock().getLocation(); |
| 279 | + return blockLocation.equals(island.getCenter()); |
| 280 | + }); |
| 281 | + |
| 282 | + // If we found an island AND it passed the filter (spawned at center)... |
| 283 | + if (optionalIsland.isPresent()) { |
| 284 | + // 1. Cancel the original item spawn. |
| 285 | + event.setCancelled(true); |
274 | 286 |
|
275 | | - Optional<Island> optionalIsland = this.addon.getIslands().getIslandAt(location) |
276 | | - .filter(island -> location.getBlock().getLocation().equals(island.getCenter())); |
| 287 | + // 2. Get the island and the item stack that was supposed to spawn. |
| 288 | + Island island = optionalIsland.get(); |
| 289 | + // We use event.getEntity() which is guaranteed to be an Item. |
| 290 | + ItemStack itemStack = event.getEntity().getItemStack(); |
277 | 291 |
|
278 | | - if (optionalIsland.isPresent()) { |
279 | | - // Teleport entity to the top of magic block. |
280 | | - entity.teleport(optionalIsland.get().getCenter().add(0.5, 1, 0.5)); |
281 | | - entity.setVelocity(new Vector(0, 0, 0)); |
| 292 | + // 3. Calculate the new, clean drop location. |
| 293 | + // .add(0.5, 1, 0.5) moves it to the center of the block (0.5) |
| 294 | + // and one block up (1.0) so it sits on top. |
| 295 | + Location newDropLocation = island.getCenter().add(0.5, 1, 0.5); |
| 296 | + |
| 297 | + // 4. Drop the item stack at the new location. |
| 298 | + spawnLocation.getWorld().dropItem(newDropLocation, itemStack); |
282 | 299 | } |
283 | 300 | } |
284 | 301 |
|
@@ -607,7 +624,6 @@ private void spawnBlock(@NonNull OneBlockObject nextBlock, @NonNull Block block) |
607 | 624 |
|
608 | 625 | } |
609 | 626 |
|
610 | | - @SuppressWarnings("deprecation") |
611 | 627 | @EventHandler |
612 | 628 | public void onPlayerInteract(PlayerInteractEvent e) { |
613 | 629 | if (e.getAction() != Action.RIGHT_CLICK_BLOCK) return; |
|
0 commit comments