diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java index 5b9d4e1..ec7c6c3 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java @@ -1,5 +1,6 @@ package me.unurled.sacredrealms.sr.commands.admin; +import static me.unurled.sacredrealms.sr.utils.Component.NOT_PLAYER; import static me.unurled.sacredrealms.sr.utils.Component.textComp; import java.util.List; @@ -7,6 +8,7 @@ import java.util.Set; import me.unurled.sacredrealms.sr.components.treasure.Treasure; import me.unurled.sacredrealms.sr.components.treasure.TreasureManager; import me.unurled.sacredrealms.sr.gui.treasure.TreasureGUI; +import me.unurled.sacredrealms.sr.managers.Manager; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.block.Block; @@ -22,6 +24,97 @@ public class TreasureCommand implements TabExecutor { public static final Set TRANSPARENT = Set.of(Material.AIR, Material.CAVE_AIR, Material.VOID_AIR, Material.WATER, Material.LAVA); + public static final String SR_TREASURE_PERMISSION = "sr.treasure."; + public static final String CREATE = "create"; + public static final String MODIFY = "modify"; + public static final String LIST = "list"; + public static final String DELETE = "delete"; + + private static void delete(@NotNull CommandSender sender, TreasureManager instance) { + Block block; + if (!(sender instanceof Player p)) { + sender.sendMessage(NOT_PLAYER); + return; + } + block = p.getTargetBlock(TRANSPARENT, 3); + if (block.getBlockData().getMaterial().equals(Material.CHEST)) { + // find the treasure chest + Treasure treasure = instance.findByLocation(block.getLocation()); + if (treasure == null) { + sender.sendMessage("No treasure chest found at this location."); + return; + } + // remove the treasure chest + instance.removeTreasure(treasure); + } + } + + private static void list(@NotNull CommandSender sender, @NotNull TreasureManager instance) { + sender.sendMessage("Treasures:"); + for (Treasure treasure : instance.getTreasures()) { + String s = + String.format( + "%sx: %s, y: %s, z: %s", + treasure.getLocation().getBlockX(), + treasure.getLocation().getBlockY(), + treasure.getLocation().getBlockZ(), + treasure.getLocation().toString(), + treasure.getLocation().getBlockX(), + treasure.getLocation().getBlockY(), + treasure.getLocation().getBlockZ()); + Component text = textComp(s); + sender.sendMessage(text); + } + } + + private static void modify(@NotNull CommandSender sender, TreasureManager instance) { + Block block; + if (!(sender instanceof Player p)) { + sender.sendMessage(NOT_PLAYER); + return; + } + block = p.getTargetBlock(TRANSPARENT, 3); + if (block.getBlockData().getMaterial().equals(Material.CHEST)) { + // find the treasure chest + Treasure treasure = instance.findByLocation(block.getLocation()); + if (treasure == null) { + sender.sendMessage("No treasure chest found at this location."); + return; + } + // open the GUI + Window window = + Window.single() + .setViewer(p) + .setTitle("Attributes") + .setGui(TreasureGUI.createGui(treasure)) + .build(); + window.open(); + } + } + + private static void create( + @NotNull CommandSender sender, @NotNull String @NotNull [] args, TreasureManager instance) { + Block block; + if (!(sender instanceof Player p)) { + sender.sendMessage(NOT_PLAYER); + return; + } + block = p.getTargetBlock(TRANSPARENT, 3); + if (block.getBlockData().getMaterial().equals(Material.CHEST)) { + // create a new treasure chest + Treasure treasure; + if (args.length == 2) { + if (!args[1].startsWith(SR_TREASURE_PERMISSION)) { + p.sendMessage(textComp("Invalid permission node. It must start with sr.treasure.")); + return; + } + treasure = new Treasure(instance.getNextId(), block.getLocation(), args[1]); + } else { + treasure = new Treasure(instance.getNextId(), block.getLocation()); + } + instance.addTreasure(treasure); + } + } /** * Executes the given command, returning its success.
@@ -39,97 +132,19 @@ public class TreasureCommand implements TabExecutor { @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, - @NotNull String[] args) { + @NotNull String @NotNull [] args) { // create, modify, list, get, delete if (args.length == 0) { sender.sendMessage("Usage: /treasure "); return true; } - Block block; + final TreasureManager instance = Manager.getInstance(TreasureManager.class); switch (args[0]) { - case "create" -> { - if (!(sender instanceof Player p)) { - sender.sendMessage("You must be a player to use this command."); - return true; - } - block = p.getTargetBlock(TRANSPARENT, 3); - if (block.getBlockData().getMaterial().equals(Material.CHEST)) { - // create a new treasure chest - Treasure treasure; - if (args.length == 2) { - treasure = new Treasure(block.getLocation(), args[1]); - } else { - treasure = new Treasure(block.getLocation()); - } - TreasureManager.getInstance(TreasureManager.class).addTreasure(treasure); - } - } - case "modify" -> { - if (!(sender instanceof Player p)) { - sender.sendMessage("You must be a player to use this command."); - return true; - } - block = p.getTargetBlock(TRANSPARENT, 3); - if (block.getBlockData().getMaterial().equals(Material.CHEST)) { - // find the treasure chest - Treasure treasure = - TreasureManager.getInstance(TreasureManager.class) - .findByLocation(block.getLocation()); - if (treasure == null) { - sender.sendMessage("No treasure chest found at this location."); - return true; - } - // open the GUI - Window window = - Window.single() - .setViewer(p) - .setTitle("Attributes") - .setGui(TreasureGUI.createGui(treasure)) - .build(); - window.open(); - } - } - case "list" -> { - sender.sendMessage("Treasures:"); - for (Treasure treasure : - TreasureManager.getInstance(TreasureManager.class).getTreasures()) { - String s = - String.format( - "%sx: %s, y: %s, z: %s", - treasure.getLocation().getBlockX(), - treasure.getLocation().getBlockY(), - treasure.getLocation().getBlockZ(), - treasure.getLocation().toString(), - treasure.getLocation().getBlockX(), - treasure.getLocation().getBlockY(), - treasure.getLocation().getBlockZ()); - Component text = textComp(s); - sender.sendMessage(text); - } - } - case "delete" -> { - if (!(sender instanceof Player p)) { - sender.sendMessage("You must be a player to use this command."); - return true; - } - block = p.getTargetBlock(TRANSPARENT, 3); - if (block.getBlockData().getMaterial().equals(Material.CHEST)) { - // find the treasure chest - Treasure treasure = - TreasureManager.getInstance(TreasureManager.class) - .findByLocation(block.getLocation()); - if (treasure == null) { - sender.sendMessage("No treasure chest found at this location."); - return true; - } - // remove the treasure chest - TreasureManager.getInstance(TreasureManager.class).removeTreasure(treasure); - } - } - default -> { - sender.sendMessage("Usage: /treasure "); - return true; - } + case CREATE -> create(sender, args, instance); + case MODIFY -> modify(sender, instance); + case LIST -> list(sender, instance); + case DELETE -> delete(sender, instance); + default -> sender.sendMessage("Usage: /treasure "); } return true; } @@ -151,7 +166,17 @@ public class TreasureCommand implements TabExecutor { @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, - @NotNull String[] args) { + @NotNull String @NotNull [] args) { + if (args.length == 0) { + return List.of(CREATE, MODIFY, LIST, DELETE); + } + if (args.length == 2 && (args[0].equals(CREATE))) { + if (args[1].startsWith(SR_TREASURE_PERMISSION)) { + return List.of(); + } + return List.of(SR_TREASURE_PERMISSION); + } + return List.of(); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java b/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java index 903b3e2..e8ab1f0 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java @@ -38,6 +38,7 @@ public class SRPlayer { private Map> itemAttributes = new EnumMap<>(Attribute.class); @Expose private List potionEffects = new ArrayList<>(); @Expose private Inventory inventory = null; + @Expose private List treasuresOpened = new ArrayList<>(); public SRPlayer(@NotNull UUID uuid) { this.uuid = uuid; @@ -259,4 +260,16 @@ public class SRPlayer { } this.experience = experience; } + + public List getTreasuresOpened() { + return treasuresOpened; + } + + public boolean hasOpenedTreasure(int id) { + return treasuresOpened.contains(id); + } + + public void addTreasureOpened(int id) { + treasuresOpened.add(id); + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/Treasure.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/Treasure.java index 0ee6ae4..9e17144 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/Treasure.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/Treasure.java @@ -1,12 +1,19 @@ package me.unurled.sacredrealms.sr.components.treasure; +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + import java.util.HashMap; import java.util.Map; +import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; public class Treasure { + private int id; + /** Chest location */ private Location location; @@ -16,26 +23,40 @@ public class Treasure { /** Permission to access this chest */ private String permission; - public Treasure() {} - - public Treasure(Location location) { + public Treasure(int id, Location location) { + this.id = id; this.location = location; this.items = new HashMap<>(); this.permission = ""; } - public Treasure(Location location, String permission) { + public Treasure(int id, Location location, String permission) { + this.id = id; this.location = location; this.items = new HashMap<>(); this.permission = permission; } - public Treasure(Location location, Map items, String permission) { + public Treasure(int id, Location location, Map items, String permission) { + this.id = id; this.location = location; this.items = items; this.permission = permission; } + /** + * Get the id of the treasure + * + * @return the id of the treasure + */ + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + /** * Get the location of the treasure * @@ -74,4 +95,12 @@ public class Treasure { public void setPermission(String permission) { this.permission = permission; } + + public Inventory getInventory() { + Inventory inv = Bukkit.createInventory(null, InventoryType.CHEST, textComp("Treasure")); + for (Map.Entry entry : items.entrySet()) { + inv.setItem(entry.getKey(), entry.getValue()); + } + return inv; + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java index 6d1ac1e..2729a73 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java @@ -43,7 +43,7 @@ public class TreasureDeserializer implements JsonDeserializer { .registerTypeAdapter(ItemStack.class, new ItemStackDeserializer()) .create(); try { - + Integer id = gson.fromJson("id", Integer.class); String loc = gson.fromJson("location", String.class); String[] parts = loc.split(","); if (parts.length != 6) { @@ -65,7 +65,7 @@ public class TreasureDeserializer implements JsonDeserializer { return null; } - return new Treasure(location, item, permission); + return new Treasure(id, location, item, permission); } catch (Exception e) { error("Error deserializing treasure: " + e.getMessage()); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java index 713eee3..8b58cbb 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java @@ -1,26 +1,43 @@ package me.unurled.sacredrealms.sr.components.treasure; +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.papermc.paper.event.packet.PlayerChunkLoadEvent; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.annotation.Nullable; +import me.unurled.sacredrealms.sr.components.player.PlayerManager; +import me.unurled.sacredrealms.sr.components.player.SRPlayer; import me.unurled.sacredrealms.sr.data.DataHandler; import me.unurled.sacredrealms.sr.data.DataManager; import me.unurled.sacredrealms.sr.managers.Manager; +import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; public class TreasureManager extends Manager { private final List treasures = new ArrayList<>(); + private int nextId = 0; + /** Save the data */ @Override public void saveData() { // save all treasures for (Treasure treasure : treasures) { // serialize treasure - DataHandler dh = DataManager.getInstance(DataManager.class).getDataHandler(); + DataHandler dh = Manager.getInstance(DataManager.class).getDataHandler(); Gson gson = new GsonBuilder().registerTypeAdapter(Treasure.class, new TreasureSerializer()).create(); dh.set("treasures." + treasures.indexOf(treasure), gson.toJson(treasure)); @@ -31,17 +48,18 @@ public class TreasureManager extends Manager { @Override public void loadData() { // load all treasures - DataHandler dh = DataManager.getInstance(DataManager.class).getDataHandler(); + DataHandler dh = Manager.getInstance(DataManager.class).getDataHandler(); Gson gson = new GsonBuilder().registerTypeAdapter(Treasure.class, new TreasureDeserializer()).create(); for (String key : dh.getKeysAll("treasures")) { // deserialize treasure - treasures.add(gson.fromJson(dh.get("treasures." + key), Treasure.class)); + addTreasure(gson.fromJson(dh.get("treasures." + key), Treasure.class)); } } public void addTreasure(Treasure treasure) { treasures.add(treasure); + nextId = Math.max(nextId, treasure.getId() + 1); } @Nullable @@ -54,12 +72,113 @@ public class TreasureManager extends Manager { return null; } + @Nullable + public Treasure findByNearbyPlayer(@NotNull Location loc) { + for (Treasure treasure : treasures) { + if (treasure.getLocation().getWorld() != loc.getWorld()) { + continue; + } + if (treasure.getLocation().distance(loc) < 5) { + return treasure; + } + } + return null; + } + @NotNull public List getTreasures() { return treasures; } public void removeTreasure(@NotNull Treasure treasure) { + Chunk chunk = treasure.getLocation().getChunk(); + Arrays.stream(chunk.getEntities()) + .map( + entity -> { + if (entity instanceof Player p) { + return p; + } + return null; + }) + .forEach( + player -> { + if (player != null) { + player.sendBlockChange(treasure.getLocation(), Material.AIR.createBlockData()); + } + }); treasures.remove(treasure); } + + public int getNextId() { + return nextId++; + } + + @EventHandler + public void onChestOpen(@NotNull PlayerInteractEvent e) { + if (e.hasBlock()) { + Block clickedBlock = e.getClickedBlock(); + if (clickedBlock == null + || !clickedBlock.getBlockData().getMaterial().equals(Material.CHEST)) { + return; + } + + onTreasureOpen(e, clickedBlock); + } + } + + private void onTreasureOpen(PlayerInteractEvent e, Block clickedBlock) { + // it is a chest, lets loop on all treasure and find if the clicked block is a treasure chest + + for (Treasure treasure : treasures) { + if (treasure.getLocation().equals(clickedBlock.getLocation())) { + // it is a treasure chest, lets open it + Player p = e.getPlayer(); + if (!p.hasPermission(treasure.getPermission())) { + p.sendMessage("You don't have permission to open this chest"); + p.playSound(p.getLocation(), Sound.BLOCK_ANVIL_BREAK, 1f, 0.5f); + e.setCancelled(true); + return; + } + e.setCancelled(true); + p.openInventory(treasure.getInventory()); + SRPlayer srPlayer = Manager.getInstance(PlayerManager.class).getPlayer(p.getUniqueId()); + if (srPlayer != null) { + srPlayer.addTreasureOpened(treasure.getId()); + } + break; + } + } + } + + @EventHandler + public void onCloseTreasure(@NotNull InventoryCloseEvent e) { + if (e.getView().title().contains(textComp("Treasure"))) { + Treasure treasure = findByNearbyPlayer(e.getPlayer().getLocation()); + if (treasure != null) { + // drop all items to the ground + for (int i = 0; i < treasure.getInventory().getSize(); i++) { + ItemStack item = treasure.getInventory().getItem(i); + if (item != null) { + e.getPlayer().getWorld().dropItemNaturally(e.getPlayer().getLocation(), item); + } + } + Player p = (Player) e.getPlayer(); + p.sendBlockChange(treasure.getLocation(), Material.AIR.createBlockData()); + } + } + } + + @EventHandler + public void onChunkLoad(@NotNull PlayerChunkLoadEvent e) { + Player p = e.getPlayer(); + Treasure treasure = findByNearbyPlayer(p.getLocation()); + if (treasure != null) { + SRPlayer srPlayer = Manager.getInstance(PlayerManager.class).getPlayer(p.getUniqueId()); + if (srPlayer != null && srPlayer.hasOpenedTreasure(treasure.getId())) { + return; + } + // display chest to clientside builds + p.sendBlockChange(treasure.getLocation(), Material.CHEST.createBlockData()); + } + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java index c7b923e..173eaed 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java @@ -31,6 +31,7 @@ public class TreasureSerializer implements JsonSerializer { @Override public JsonElement serialize(Treasure src, Type typeOfSrc, JsonSerializationContext context) { JsonObject obj = new JsonObject(); + obj.add("id", new JsonPrimitive(src.getId())); obj.add("location", new JsonPrimitive(src.getLocation().toString())); obj.add("permission", new JsonPrimitive(src.getPermission())); Gson gson = diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java index 54f965b..e65a141 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java @@ -14,6 +14,8 @@ import xyz.xenondevs.invui.item.impl.SimpleItem; public class TreasureGUI { + private TreasureGUI() {} + @NotNull public static Gui createGui(@NotNull Treasure treasure) { ItemStack itemStack = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);