From 25c95dd46abcd6d339aeed9bc31c30412c3d1990 Mon Sep 17 00:00:00 2001 From: akastijn Date: Mon, 15 Jun 2026 22:13:51 +0200 Subject: [PATCH] Introduce LootService for chest loot handling and ChestListener for in-game chest interactions --- .../java/com/alttd/hunger_games/Main.java | 7 ++- .../alttd/hunger_games/config/LootItems.java | 7 ++- .../alttd/hunger_games/config/Messages.java | 2 + .../event_listeners/ChestListener.java | 54 ++++++++++++++++ .../hunger_games/services/LootService.java | 62 +++++++++++++++++++ .../hunger_games/services/RoundService.java | 9 ++- 6 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/alttd/hunger_games/event_listeners/ChestListener.java create mode 100644 src/main/java/com/alttd/hunger_games/services/LootService.java diff --git a/src/main/java/com/alttd/hunger_games/Main.java b/src/main/java/com/alttd/hunger_games/Main.java index f924b34..a4ffde2 100644 --- a/src/main/java/com/alttd/hunger_games/Main.java +++ b/src/main/java/com/alttd/hunger_games/Main.java @@ -3,9 +3,11 @@ package com.alttd.hunger_games; import com.alttd.hunger_games.commands.BaseCommand; import com.alttd.hunger_games.config.Config; import com.alttd.hunger_games.config.Messages; +import com.alttd.hunger_games.event_listeners.ChestListener; import com.alttd.hunger_games.event_listeners.PlayerDamageListener; import com.alttd.hunger_games.event_listeners.PlayerDisconnectListener; import com.alttd.hunger_games.event_listeners.PlayerJoinListener; +import com.alttd.hunger_games.services.LootService; import com.alttd.hunger_games.services.PlayerService; import com.alttd.hunger_games.services.PlayerTeleporterService; import com.alttd.hunger_games.services.Round; @@ -21,6 +23,7 @@ public final class Main extends JavaPlugin { private RoundService roundService; private PlayerService playerService; private PlayerTeleporterService playerTeleporterService; + private LootService lootService; @Override public void onEnable() { @@ -34,7 +37,8 @@ public final class Main extends JavaPlugin { private void registerServices() { round = Round.createSingletonInstance(); - roundService = RoundService.createSingletonInstance(round, this); + lootService = new LootService(); + roundService = RoundService.createSingletonInstance(round, this, lootService); playerTeleporterService = PlayerTeleporterService.createSingletonInstance(); playerService = PlayerService.createSingletonInstance(round, roundService, playerTeleporterService); } @@ -53,6 +57,7 @@ public final class Main extends JavaPlugin { pluginManager.registerEvents(new PlayerDisconnectListener(playerService), this); pluginManager.registerEvents(new PlayerJoinListener(playerService), this); pluginManager.registerEvents(new PlayerDamageListener(roundService, playerService), this); + pluginManager.registerEvents(new ChestListener(roundService, lootService), this); } public void reloadConfigs() { diff --git a/src/main/java/com/alttd/hunger_games/config/LootItems.java b/src/main/java/com/alttd/hunger_games/config/LootItems.java index 8cafddb..1b35424 100644 --- a/src/main/java/com/alttd/hunger_games/config/LootItems.java +++ b/src/main/java/com/alttd/hunger_games/config/LootItems.java @@ -29,10 +29,15 @@ public class LootItems extends AbstractConfig { private static final HashMap> ITEMS = new HashMap<>(); + public static List get(RARITY rarity) { + return ITEMS.get(rarity); + } + @SuppressWarnings("unused") private static void load() { for (RARITY rarity : RARITY.values()) { - List materialList = config.getList(prefix, rarity.getConfigName(), ITEMS.get(rarity).stream().map(Material::name)); + List materialNameList = ITEMS.get(rarity).stream().map(Material::name).toList(); + List materialList = config.getList(prefix, rarity.getConfigName(), materialNameList); ITEMS.put(rarity, materialList.stream().map(Material::getMaterial).toList()); } } diff --git a/src/main/java/com/alttd/hunger_games/config/Messages.java b/src/main/java/com/alttd/hunger_games/config/Messages.java index 4b57fda..2d1f46e 100644 --- a/src/main/java/com/alttd/hunger_games/config/Messages.java +++ b/src/main/java/com/alttd/hunger_games/config/Messages.java @@ -76,12 +76,14 @@ public class Messages extends AbstractConfig { public static String WARMUP = "The Hunger Games are starting soon! Get ready!"; public static String STARTED = "The Hunger Games has begun! Good luck!"; public static String BORDER_SHRINK = "The border is shrinking to blocks!"; + public static String CHEST_EMPTY = "This chest is empty!"; @SuppressWarnings("unused") private static void load() { WARMUP = config.getString(prefix, "warmup", WARMUP); STARTED = config.getString(prefix, "started", STARTED); BORDER_SHRINK = config.getString(prefix, "border-shrink", BORDER_SHRINK); + CHEST_EMPTY = config.getString(prefix, "chest-empty", CHEST_EMPTY); } } diff --git a/src/main/java/com/alttd/hunger_games/event_listeners/ChestListener.java b/src/main/java/com/alttd/hunger_games/event_listeners/ChestListener.java new file mode 100644 index 0000000..ed5d26e --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/event_listeners/ChestListener.java @@ -0,0 +1,54 @@ +package com.alttd.hunger_games.event_listeners; + +import com.alttd.hunger_games.config.Messages; +import com.alttd.hunger_games.data_objects.PLAYER_STATE; +import com.alttd.hunger_games.data_objects.ROUND_STATE; +import com.alttd.hunger_games.services.LootService; +import com.alttd.hunger_games.services.RoundService; +import lombok.RequiredArgsConstructor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; + +@RequiredArgsConstructor +public class ChestListener implements Listener { + + private final RoundService roundService; + private final LootService lootService; + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { + return; + } + + Block block = event.getClickedBlock(); + if (block == null || block.getType() != Material.CHEST) { + return; + } + + ROUND_STATE roundState = roundService.getRoundState(); + if (roundState != ROUND_STATE.SAFE_PHASE && roundState != ROUND_STATE.KILL_PHASE && roundState != ROUND_STATE.FINALE) { + return; + } + + Player player = event.getPlayer(); + if (roundService.getPlayerState(player.getUniqueId()).orElse(null) != PLAYER_STATE.REGISTERED) { + return; + } + + event.setCancelled(true); + + Inventory inventory = lootService.getChestInventory(block.getLocation()); + if (inventory.isEmpty()) { + player.sendRichMessage(Messages.GAME.CHEST_EMPTY); + } else { + player.openInventory(inventory); + } + } +} diff --git a/src/main/java/com/alttd/hunger_games/services/LootService.java b/src/main/java/com/alttd/hunger_games/services/LootService.java new file mode 100644 index 0000000..5cc3c26 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/services/LootService.java @@ -0,0 +1,62 @@ +package com.alttd.hunger_games.services; + +import com.alttd.hunger_games.config.LootItems; +import com.alttd.hunger_games.data_objects.RARITY; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +public class LootService { + + private final HashMap chestInventories = new HashMap<>(); + private final Random random = ThreadLocalRandom.current(); + + public Inventory getChestInventory(Location location) { + if (chestInventories.containsKey(location)) { + return chestInventories.get(location); + } + + Inventory inventory = generateLoot(); + chestInventories.put(location, inventory); + return inventory; + } + + private Inventory generateLoot() { + Inventory inventory = Bukkit.createInventory(null, 27, Component.text("Chest")); + RARITY rarity = decideRarity(); + List possibleItems = LootItems.ITEMS.get(rarity); + + if (possibleItems.isEmpty()) { + return inventory; + } + + int itemCount = random.nextInt(3, 7); + for (int i = 0; i < itemCount; i++) { + int slot = random.nextInt(27); + Material material = possibleItems.get(random.nextInt(possibleItems.size())); + inventory.setItem(slot, new ItemStack(material)); + } + + return inventory; + } + + private RARITY decideRarity() { + double roll = random.nextDouble(); + if (roll < 0.05) return RARITY.EPIC; // 5% + if (roll < 0.15) return RARITY.RARE; // 10% + if (roll < 0.40) return RARITY.UNCOMMON;// 25% + return RARITY.COMMON; // 60% + } + + public void clear() { + chestInventories.clear(); + } +} diff --git a/src/main/java/com/alttd/hunger_games/services/RoundService.java b/src/main/java/com/alttd/hunger_games/services/RoundService.java index a99c1bb..92beb4e 100644 --- a/src/main/java/com/alttd/hunger_games/services/RoundService.java +++ b/src/main/java/com/alttd/hunger_games/services/RoundService.java @@ -17,22 +17,24 @@ public class RoundService implements RoundListener { private static RoundService instance = null; private PlayerMovementListener playerMovementListener; private final Main main; + private final LootService lootService; @Getter private ROUND_STATE roundState; private final HashMap players = new HashMap<>(); - public static RoundService createSingletonInstance(Round round, Main main) { + public static RoundService createSingletonInstance(Round round, Main main, LootService lootService) { if (instance != null) { throw new IllegalStateException("RoundService is already initialized."); } - instance = new RoundService(round, main); + instance = new RoundService(round, main, lootService); return instance; } - private RoundService(Round round, Main main) { + private RoundService(Round round, Main main, LootService lootService) { this.roundState = round.register(this); this.main = main; + this.lootService = lootService; } public Set getPlayers(PLAYER_STATE playerState) { @@ -74,6 +76,7 @@ public class RoundService implements RoundListener { stopFreeze(); if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) { clear(); + lootService.clear(); } if (roundState.equals(ROUND_STATE.COUNTDOWN)) { startFreeze();