diff --git a/.github/workflows/milestone-close.yml b/.github/workflows/milestone-close.yml index 59b52640f..8be8f3cc3 100644 --- a/.github/workflows/milestone-close.yml +++ b/.github/workflows/milestone-close.yml @@ -44,6 +44,7 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: + token: ${{ secrets.RELEASE_TOKEN }} tag_name: ${{ github.event.milestone.title }} name: Release milestone ${{ github.event.milestone.title }} generate_release_notes: true @@ -54,14 +55,13 @@ jobs: ${{ github.event.milestone.description }} files: builds/** - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} - name: Deploy to server - uses: rexlmanu/pterodactyl-upload-action@v2.4 + uses: blobles-dev/pterodactyl-upload-action@pterodactyl-file-upload-modification-allow-http-200-fileupload with: panel-host: ${{ secrets.PANEL_HOST }} api-key: ${{ secrets.API_KEY }} server-id: ${{ secrets.SERVER_ID }} source: builds/OpenMC.jar - target: "./plugins/OpenMC.jar" + target: "./plugins/update/" + command: omcrestart diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46f8f9b12..59efa4a56 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,5 +68,6 @@ Les noms doivent être en anglais, peu importe la chose nommée. | Package | Toujours en miniscules et tous les mots sont collés sans caractère entre (pas d'`_`). Le nom doit être simple et sans caractères spéciaux (uniquement les caractères ASCII, donc pas d'accents). Le nom du package doit au possible rester un seul mot. | `fr.communaywen.commands` | | Classes, Interfaces, Enums, Records, Annotations | Toujours en PascalCase. Utilisez seulement des noms. Évitez les acronymes ou abréviations. | `class SpawnManager;` | | Methodes | Son nom commence par un verbe. En `camelCase` | `getInstance()` `clone()` | +| Methodes de Tests | Son nom commence par test + une méthode testée. | `testFormat_avoid0()` `testGetNamedTextColor_success()`| | Variables | Toujours en `camelCase`, évitez les abréviations et les noms raccourcis comme `nbr`. Pas d'`_` ou de `$` au début. L'utilité de la variable devrait être compréhensible en regardant le nom. | `int numberOfPlayers` `String playerName` | | Constantes et membres d'enums | Les constantes dont la déclaration commence par `static final` sont nommées en UPPER_SNAKE_CASE, avec comme pour les variables, un nom qui décrit clairement leur utilité. | `static final int MIN_WIDTH = 4;` | diff --git a/src/main/java/fr/openmc/api/menulib/MenuLib.java b/src/main/java/fr/openmc/api/menulib/MenuLib.java index 8a9ad6f78..503da8c01 100644 --- a/src/main/java/fr/openmc/api/menulib/MenuLib.java +++ b/src/main/java/fr/openmc/api/menulib/MenuLib.java @@ -218,6 +218,11 @@ public void onClose(InventoryCloseEvent e) { if (!(e.getPlayer() instanceof Player player)) return; if (e.getInventory().getHolder(false) instanceof PaginatedMenu paginatedMenu) { paginatedMenu.onClose(e); + Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { + if (!(e.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PaginatedMenu)) { + MenuLib.clearHistory(player); + } + }, 1L); return; } diff --git a/src/main/java/fr/openmc/core/CommandsManager.java b/src/main/java/fr/openmc/core/CommandsManager.java index dad54dcc6..3af0c9e03 100644 --- a/src/main/java/fr/openmc/core/CommandsManager.java +++ b/src/main/java/fr/openmc/core/CommandsManager.java @@ -8,9 +8,11 @@ import fr.openmc.core.commands.fun.Playtime; import fr.openmc.core.commands.utils.*; import fr.openmc.core.features.adminshop.AdminShopCommand; +import fr.openmc.core.features.animations.DebugAnimationCommand; import fr.openmc.core.features.credits.CreditsCommand; import fr.openmc.core.features.cube.CubeCommands; -import fr.openmc.core.features.events.halloween.commands.HalloweenCommands; +import fr.openmc.core.features.events.commands.EventCommand; +import fr.openmc.core.features.events.contents.halloween.commands.HalloweenCommands; import fr.openmc.core.features.friend.FriendCommand; import fr.openmc.core.features.mailboxes.MailboxCommand; import fr.openmc.core.features.mainmenu.commands.MainMenuCommand; @@ -59,7 +61,9 @@ private static void registerCommands() { new Cooldowns(), new CreditsCommand(), new CubeCommands(), - new HalloweenCommands() + new HalloweenCommands(), + new DebugAnimationCommand(), + new EventCommand() ); } } diff --git a/src/main/java/fr/openmc/core/ListenersManager.java b/src/main/java/fr/openmc/core/ListenersManager.java index c4a5a44d1..511e0a076 100644 --- a/src/main/java/fr/openmc/core/ListenersManager.java +++ b/src/main/java/fr/openmc/core/ListenersManager.java @@ -40,12 +40,12 @@ public static void init() { new EquipableItemListener(), new NoMoreRabbit(), new ArmorListener(), - new SpawnerExtractorListener(), new BlockBreakListener() ); if (!OMCPlugin.isUnitTestVersion()) { registerEvents( + new SpawnerExtractorListener(), new ItemsAddersListener(), new TicketListener() ); diff --git a/src/main/java/fr/openmc/core/OMCPlugin.java b/src/main/java/fr/openmc/core/OMCPlugin.java index 3518e9882..c731d53a4 100644 --- a/src/main/java/fr/openmc/core/OMCPlugin.java +++ b/src/main/java/fr/openmc/core/OMCPlugin.java @@ -15,7 +15,6 @@ import fr.openmc.core.features.city.sub.notation.NotationManager; import fr.openmc.core.features.city.sub.statistics.CityStatisticsManager; import fr.openmc.core.features.city.sub.war.WarManager; -import fr.openmc.core.features.contest.managers.ContestManager; import fr.openmc.core.features.cube.multiblocks.MultiBlockManager; import fr.openmc.core.features.displays.TabList; import fr.openmc.core.features.displays.bossbar.BossbarManager; @@ -25,7 +24,9 @@ import fr.openmc.core.features.dream.generation.DreamDimensionManager; import fr.openmc.core.features.economy.BankManager; import fr.openmc.core.features.economy.EconomyManager; -import fr.openmc.core.features.events.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; import fr.openmc.core.features.homes.HomesManager; import fr.openmc.core.features.homes.icons.HomeIconCacheManager; import fr.openmc.core.features.leaderboards.LeaderboardManager; @@ -132,7 +133,8 @@ public void onEnable() { TPAQueue.initCommand(); FreezeManager.init(); QuestProgressSaveManager.init(); - TabList.init(); + if (!isUnitTestVersion()) + TabList.init(); AdminShopManager.init(); BossbarManager.init(); AnimationsManager.init(); @@ -160,6 +162,7 @@ public void loadWithItemsAdder() { QuestsManager.init(); CityManager.init(); ContestManager.init(); + WeeklyEventsManager.init(); DreamManager.init(); MultiBlockManager.init(); if (WorldGuardHook.isHasWorldGuard()) { diff --git a/src/main/java/fr/openmc/core/features/animations/DebugAnimationCommand.java b/src/main/java/fr/openmc/core/features/animations/DebugAnimationCommand.java new file mode 100644 index 000000000..820de0d44 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/animations/DebugAnimationCommand.java @@ -0,0 +1,17 @@ +package fr.openmc.core.features.animations; + +import fr.openmc.core.features.adminshop.AdminShopManager; +import fr.openmc.core.features.animations.listeners.EmoteListener; +import org.bukkit.entity.Player; +import revxrsal.commands.annotation.Command; +import revxrsal.commands.annotation.Description; +import revxrsal.commands.bukkit.annotation.CommandPermission; + + +public class DebugAnimationCommand { + @Command("debug animation resetHead") + @CommandPermission("omc.debug.animation.resetHead") + public void resetHead(Player player) { + EmoteListener.sendCamera(player, player); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/features/animations/listeners/EmoteListener.java b/src/main/java/fr/openmc/core/features/animations/listeners/EmoteListener.java index add12cedf..2fe3241c6 100644 --- a/src/main/java/fr/openmc/core/features/animations/listeners/EmoteListener.java +++ b/src/main/java/fr/openmc/core/features/animations/listeners/EmoteListener.java @@ -96,6 +96,8 @@ private void animationEnd(Player player) { PlayerAnimationInfo info = playingAnimations.remove(player); if (info == null) return; + player.setInvulnerable(false); + sendCamera(player, player); if (info.getArmorStand() != null) info.getArmorStand().remove(); @@ -112,7 +114,7 @@ private void animationEnd(Player player) { * @param player The player to send the packet to. * @param entity The entity to set the camera to. */ - private void sendCamera(Player player, Entity entity) { + public static void sendCamera(Player player, Entity entity) { PacketContainer packet = new PacketContainer(PacketType.Play.Server.CAMERA); packet.getIntegers().write(0, entity.getEntityId()); ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet); diff --git a/src/main/java/fr/openmc/core/features/animations/listeners/PlayerFinishJoiningListener.java b/src/main/java/fr/openmc/core/features/animations/listeners/PlayerFinishJoiningListener.java index dc40d728a..78ad65def 100644 --- a/src/main/java/fr/openmc/core/features/animations/listeners/PlayerFinishJoiningListener.java +++ b/src/main/java/fr/openmc/core/features/animations/listeners/PlayerFinishJoiningListener.java @@ -4,6 +4,8 @@ import fr.openmc.core.OMCPlugin; import fr.openmc.core.features.animations.Animation; import fr.openmc.core.features.animations.PlayerAnimationInfo; +import fr.openmc.core.features.settings.PlayerSettingsManager; +import fr.openmc.core.features.settings.SettingType; import org.bukkit.GameMode; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -19,16 +21,26 @@ public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); boolean onGround = player.getLocation().subtract(0, 1, 0).getBlock().getType().isSolid(); + if (!(boolean) PlayerSettingsManager.getPlayerSettings(player.getUniqueId()).getSetting(SettingType.JOIN_ANIMATION)) return; if (player.isFlying() || !onGround || player.getGameMode().equals(GameMode.SPECTATOR)) return; playingAnimations.put(player, new PlayerAnimationInfo()); EmoteListener.setupHead(player); + player.setInvulnerable(true); new BukkitRunnable() { @Override public void run() { - player.playSound(player, "omc_sounds:ambient.join_rift", 1.0f, 1.0f); - CustomPlayer.playEmote(player, Animation.JOIN_RIFT.getNameAnimation()); + if (!player.isOnline()) return; + + player.getWorld().playSound(player.getLocation(), "omc_sounds:ambient.join_rift", 1.0f, 1.0f); + try { + CustomPlayer.playEmote(player, Animation.JOIN_RIFT.getNameAnimation()); + } catch (Exception e) { + playingAnimations.remove(player); + EmoteListener.sendCamera(player, player); + player.setInvulnerable(false); + } } }.runTaskLater(OMCPlugin.getInstance(), 11L); } diff --git a/src/main/java/fr/openmc/core/features/city/actions/CityUnclaimAction.java b/src/main/java/fr/openmc/core/features/city/actions/CityUnclaimAction.java index 0d59ae137..645c34a33 100644 --- a/src/main/java/fr/openmc/core/features/city/actions/CityUnclaimAction.java +++ b/src/main/java/fr/openmc/core/features/city/actions/CityUnclaimAction.java @@ -9,6 +9,7 @@ import fr.openmc.core.utils.messages.MessagesManager; import fr.openmc.core.utils.messages.Prefix; import net.kyori.adventure.text.Component; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -26,7 +27,7 @@ public static int calculateAywenite(int chunkCount) { public static void startUnclaim(Player sender, int chunkX, int chunkZ) { City city = CityManager.getPlayerCity(sender.getUniqueId()); - org.bukkit.World bWorld = sender.getWorld(); + World bWorld = sender.getWorld(); if (!bWorld.getName().equals("world")) { MessagesManager.sendMessage(sender, Component.text("Tu ne peux pas étendre ta ville ici"), Prefix.CITY, MessageType.ERROR, false); return; @@ -42,14 +43,17 @@ public static void startUnclaim(Player sender, int chunkX, int chunkZ) { return; } - int price = calculatePrice(city.getChunks().size()); - int ayweniteNb = calculateAywenite(city.getChunks().size()); - - EconomyManager.addBalance(sender.getUniqueId(), price, "Unclaim de chunk de ville"); - ItemStack aywenite = ayweniteItemStack.clone(); - aywenite.setAmount(ayweniteNb); - for (ItemStack item : ItemUtils.splitAmountIntoStack(aywenite)) { - sender.dropItem(item); + // si on unclaim des claims gratuits on ne rend rien, sinon on rend une partie de l'argent et d'aywenite + if (city.getChunks().size() > CityCreateAction.FREE_CLAIMS+1) { + int price = calculatePrice(city.getChunks().size()); + int ayweniteNb = calculateAywenite(city.getChunks().size()); + + EconomyManager.addBalance(sender.getUniqueId(), price, "Unclaim de chunk de ville"); + ItemStack aywenite = ayweniteItemStack.clone(); + aywenite.setAmount(ayweniteNb); + for (ItemStack item : ItemUtils.splitAmountIntoStack(aywenite)) { + sender.dropItem(item); + } } city.removeChunk(chunkX, chunkZ); diff --git a/src/main/java/fr/openmc/core/features/city/listeners/protections/HangingProtection.java b/src/main/java/fr/openmc/core/features/city/listeners/protections/HangingProtection.java index 8b4c82f4f..a2b185cde 100644 --- a/src/main/java/fr/openmc/core/features/city/listeners/protections/HangingProtection.java +++ b/src/main/java/fr/openmc/core/features/city/listeners/protections/HangingProtection.java @@ -7,6 +7,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.hanging.HangingBreakEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; public class HangingProtection implements Listener { @@ -22,8 +23,18 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { @EventHandler(ignoreCancelled = true) public void onHangingBreakByEntity(HangingBreakByEntityEvent event) { - if (event.getRemover() instanceof Player player) { - ProtectionsManager.verify(player, event, event.getEntity().getLocation()); + Entity remover = event.getRemover(); + if (remover != null) { + ProtectionsManager.verify(remover, event, event.getEntity().getLocation()); + } + } + + @EventHandler(ignoreCancelled = true) + public void onHangingBreak(HangingBreakEvent event) { + if (event.getCause() == HangingBreakEvent.RemoveCause.EXPLOSION) { + if (!ProtectionsManager.canExplodeNaturally(event.getEntity().getLocation())) { + event.setCancelled(true); + } } } } diff --git a/src/main/java/fr/openmc/core/features/city/listeners/protections/InteractProtection.java b/src/main/java/fr/openmc/core/features/city/listeners/protections/InteractProtection.java index 62fa495da..01d3f6fa4 100644 --- a/src/main/java/fr/openmc/core/features/city/listeners/protections/InteractProtection.java +++ b/src/main/java/fr/openmc/core/features/city/listeners/protections/InteractProtection.java @@ -29,7 +29,6 @@ public void onInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); if (event.getAction() == Action.PHYSICAL) return; - if (event.getHand() != EquipmentSlot.HAND) return; if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; Block clickedBlock = event.getClickedBlock(); diff --git a/src/main/java/fr/openmc/core/features/city/menu/CityChunkMenu.java b/src/main/java/fr/openmc/core/features/city/menu/CityChunkMenu.java index 553bf95fa..b444fdaa2 100644 --- a/src/main/java/fr/openmc/core/features/city/menu/CityChunkMenu.java +++ b/src/main/java/fr/openmc/core/features/city/menu/CityChunkMenu.java @@ -11,6 +11,7 @@ import fr.openmc.core.features.city.CityManager; import fr.openmc.core.features.city.CityPermission; import fr.openmc.core.features.city.actions.CityClaimAction; +import fr.openmc.core.features.city.actions.CityCreateAction; import fr.openmc.core.features.city.actions.CityUnclaimAction; import fr.openmc.core.features.economy.EconomyManager; import fr.openmc.core.utils.ChunkInfo; @@ -264,9 +265,9 @@ private ItemBuilder createProtectedChunkItem(Material material, int chunkX, int } private ItemBuilder createPlayerCityChunkItem(Material material, City city, int chunkX, int chunkZ) { - return new ItemBuilder(this, material, itemMeta -> { - itemMeta.displayName(Component.text("§9Claim de votre ville")); - itemMeta.lore(List.of( + List lore; + if (city.getChunks().size() > CityCreateAction.FREE_CLAIMS+1) { + lore = List.of( Component.text("§7Ville : §d" + city.getName()), Component.text("§7Position : §f" + chunkX + ", " + chunkZ), Component.empty(), @@ -275,7 +276,19 @@ private ItemBuilder createPlayerCityChunkItem(Material material, City city, int Component.text("§8- §d" + CityUnclaimAction.calculateAywenite(playerCity.getChunks().size()) + " d'Aywenite"), Component.empty(), Component.text("§e§lCLIQUEZ POUR UNCLAIM") - )); + ); + } else { + lore = List.of( + Component.text("§7Ville : §d" + city.getName()), + Component.text("§7Position : §f" + chunkX + ", " + chunkZ), + Component.empty(), + Component.text("§e§lCLIQUEZ POUR UNCLAIM") + ); + } + + return new ItemBuilder(this, material, itemMeta -> { + itemMeta.displayName(Component.text("§9Claim de votre ville")); + itemMeta.lore(lore); }).setOnClick(event -> handleChunkUnclaimClick(player, chunkX, chunkZ, hasPermissionClaim)); } diff --git a/src/main/java/fr/openmc/core/features/city/sub/notation/NotationManager.java b/src/main/java/fr/openmc/core/features/city/sub/notation/NotationManager.java index 5f9db1242..a4fff03a2 100644 --- a/src/main/java/fr/openmc/core/features/city/sub/notation/NotationManager.java +++ b/src/main/java/fr/openmc/core/features/city/sub/notation/NotationManager.java @@ -338,7 +338,7 @@ public static void giveReward(String weekStr) { * Planifie l'exécution de la tâche de minuit qui calcule les scores et attribue les récompenses. */ private static void scheduleMidnightTask() { - long delayInTicks = DateUtils.getSecondsUntilDayOfWeekMidnight(APPLY_NOTATION_DAY) * 20; + long delayInTicks = DateUtils.getSecondsUntilDayOfWeekTime(APPLY_NOTATION_DAY, 0, 0, 0) * 20; Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { if (!isApplied) { String weekStr = DateUtils.getWeekFormat(); diff --git a/src/main/java/fr/openmc/core/features/city/sub/notation/menu/NotationDialog.java b/src/main/java/fr/openmc/core/features/city/sub/notation/menu/NotationDialog.java index 04b6d3b0f..c36447249 100644 --- a/src/main/java/fr/openmc/core/features/city/sub/notation/menu/NotationDialog.java +++ b/src/main/java/fr/openmc/core/features/city/sub/notation/menu/NotationDialog.java @@ -104,9 +104,13 @@ public static DialogBody lineCityNotation(City city, String weekStr) { Component base = Component.empty(); if (notation != null) { - String activity = String.format("%.2f/" + NotationNote.NOTE_ACTIVITY.getMaxNote(), Math.round(notation.getNoteActivity() * 100.0) / 100.0); - String eco = String.format("%.2f/" + NotationNote.NOTE_PIB.getMaxNote(), Math.round(notation.getNoteEconomy() * 100.0) / 100.0); - String military = String.format("%.2f/" + NotationNote.NOTE_MILITARY.getMaxNote(), Math.round(notation.getNoteMilitary() * 100.0) / 100.0); + double noteActivity = notation.getNoteActivity() != null ? notation.getNoteActivity() : 0; + double noteEconomy = notation.getNoteEconomy() != null ? notation.getNoteEconomy() : 0; + double noteMilitary = notation.getNoteMilitary() != null ? notation.getNoteMilitary() : 0; + + String activity = String.format("%.2f/" + NotationNote.NOTE_ACTIVITY.getMaxNote(), Math.round(noteActivity * 100.0) / 100.0); + String eco = String.format("%.2f/" + NotationNote.NOTE_PIB.getMaxNote(), Math.round(noteEconomy * 100.0) / 100.0); + String military = String.format("%.2f/" + NotationNote.NOTE_MILITARY.getMaxNote(), Math.round(noteMilitary * 100.0) / 100.0); String arch = String.format("%.2f/" + NotationNote.NOTE_ARCHITECTURAL.getMaxNote(), Math.round(notation.getNoteArchitectural() * 100.0) / 100.0); String coh = String.format("%.2f/" + NotationNote.NOTE_COHERENCE.getMaxNote(), Math.round(notation.getNoteCoherence() * 100.0) / 100.0); String total = String.format("%.2f/%.0f", Math.round(notation.getTotalNote() * 100.0) / 100.0, NotationNote.getMaxTotalNote()); @@ -150,13 +154,18 @@ public static Component getHoverTotal(CityNotation notation) { return Component.text("Aucun total pour vous"); } + + double noteActivity = notation.getNoteActivity() != null ? notation.getNoteActivity() : 0; + double noteEconomy = notation.getNoteEconomy() != null ? notation.getNoteEconomy() : 0; + double noteMilitary = notation.getNoteMilitary() != null ? notation.getNoteMilitary() : 0; + return Component.text("§6§lDétails") .appendNewline() - .append(Component.text("§8Activité " + notation.getNoteActivity())) + .append(Component.text("§8Activité " + noteActivity)) .appendNewline() - .append(Component.text("§8Économie " + notation.getNoteEconomy())) + .append(Component.text("§8Économie " + noteEconomy)) .appendNewline() - .append(Component.text("§8Militaire " + notation.getNoteMilitary())) + .append(Component.text("§8Militaire " + noteMilitary)) .appendNewline() .append(Component.text("§8Architecture " + notation.getNoteArchitectural())) .appendNewline() diff --git a/src/main/java/fr/openmc/core/features/cube/Cube.java b/src/main/java/fr/openmc/core/features/cube/Cube.java index 53b00c9c4..d15d4ac9d 100644 --- a/src/main/java/fr/openmc/core/features/cube/Cube.java +++ b/src/main/java/fr/openmc/core/features/cube/Cube.java @@ -25,6 +25,8 @@ // - iambibi_ public class Cube extends MultiBlock { public BukkitTask corruptedBubbleTask; + public BukkitTask particuleBubbleTask; + public ReproductionTask reproductionTask; public BossBar cubeBossBar; public boolean showBossBar; @@ -199,15 +201,17 @@ public void startMagneticShock() { } } - public final int RADIUS_BUBBLE = this.radius * 3; + public final int RADIUS_BUBBLE = this.radius * 4; public void startCorruptedBubble() { Location center = this.getCenter(); - int totalTicks = 20 * 3600; + int totalTicks = 20 * 1300; startBubbleParticles(); + if (corruptedBubbleTask != null) corruptedBubbleTask.cancel(); + int intervalCorruption = 20 * 15; corruptedBubbleTask = new BukkitRunnable() { int elapsed = 0; @@ -264,31 +268,36 @@ public void startBubbleParticles() { Location center = this.getCenter(); double radius = RADIUS_BUBBLE; - Bukkit.getScheduler().runTaskTimer(OMCPlugin.getInstance(), () -> { - for (int i = 0; i < 50; i++) { - double theta = Math.random() * 2 * Math.PI; - double phi = Math.random() * Math.PI; - double x = radius * Math.sin(phi) * Math.cos(theta); - double y = radius * Math.cos(phi); - double z = radius * Math.sin(phi) * Math.sin(theta); + if (particuleBubbleTask != null) particuleBubbleTask.cancel(); - Location particleLoc = center.clone().add(x, y, z); - world.spawnParticle(Particle.OMINOUS_SPAWNING, particleLoc, 1, 0.1, 0.1, 0.1, 0); - } - - for (double theta = 0; theta < Math.PI; theta += Math.PI / 16) { - for (double phi = 0; phi < 2 * Math.PI; phi += Math.PI / 16) { - double x = radius * Math.sin(theta) * Math.cos(phi); - double y = radius * Math.cos(theta); - double z = radius * Math.sin(theta) * Math.sin(phi); + particuleBubbleTask = new BukkitRunnable() { + @Override + public void run() { + for (int i = 0; i < 50; i++) { + double theta = Math.random() * 2 * Math.PI; + double phi = Math.random() * Math.PI; + double x = radius * Math.sin(phi) * Math.cos(theta); + double y = radius * Math.cos(phi); + double z = radius * Math.sin(phi) * Math.sin(theta); Location particleLoc = center.clone().add(x, y, z); + world.spawnParticle(Particle.OMINOUS_SPAWNING, particleLoc, 1, 0.1, 0.1, 0.1, 0); + } - Vector dir = center.clone().subtract(particleLoc).toVector().normalize(); - world.spawnParticle(Particle.SNEEZE, particleLoc, 1, dir.getX(), dir.getY(), dir.getZ(), 0.1); + for (double theta = 0; theta < Math.PI; theta += Math.PI / 16) { + for (double phi = 0; phi < 2 * Math.PI; phi += Math.PI / 16) { + double x = radius * Math.sin(theta) * Math.cos(phi); + double y = radius * Math.cos(theta); + double z = radius * Math.sin(theta) * Math.sin(phi); + + Location particleLoc = center.clone().add(x, y, z); + + Vector dir = center.clone().subtract(particleLoc).toVector().normalize(); + world.spawnParticle(Particle.SNEEZE, particleLoc, 1, dir.getX(), dir.getY(), dir.getZ(), 0.1); + } } } - }, 0L, 20L); + }.runTaskTimer(OMCPlugin.getInstance(), 0L, 20L); } public void startReproduction() { @@ -345,14 +354,13 @@ public void startEventsCycle() { public void run() { double roll = ThreadLocalRandom.current().nextDouble(); - if (roll < 0.5) { + if (roll < 0.7) { startMagneticShock(); } else { - if (corruptedBubbleTask == null) - startCorruptedBubble(); + startCorruptedBubble(); } } - }.runTaskTimer(OMCPlugin.getInstance(), 20L * 60 * 5, 20L * 60 * 20); + }.runTaskTimer(OMCPlugin.getInstance(), 20L * 60 * 5, 20L * 60 * 10); } public void startSoundCycle() { diff --git a/src/main/java/fr/openmc/core/features/cube/CubeCommands.java b/src/main/java/fr/openmc/core/features/cube/CubeCommands.java index 5fcd49b89..0167c6619 100644 --- a/src/main/java/fr/openmc/core/features/cube/CubeCommands.java +++ b/src/main/java/fr/openmc/core/features/cube/CubeCommands.java @@ -47,6 +47,41 @@ public void startCorruptedBubble( MessagesManager.sendMessage(player, Component.text("Bulle corrompue lancé"), Prefix.STAFF, MessageType.SUCCESS, false); } + @Subcommand("stopShock") + @CommandPermission("omc.admins.commands.cube.shock") + public void stopShock( + Player player, + @Named("cubeLoc") @SuggestWith(CubeLocationAutoComplete.class) String cubeLoc + ) { + Cube cube = getInputCubes(player, cubeLoc); + + if (cube == null) return; + + MessagesManager.sendMessage(player, Component.text("Choc éléctro-magnétique arreté"), Prefix.STAFF, MessageType.SUCCESS, false); + } + + @Subcommand("stopBubble") + @CommandPermission("omc.admins.commands.cube.bubble") + public void stopCorruptedBubble( + Player player, + @Named("cubeLoc") @SuggestWith(CubeLocationAutoComplete.class) String cubeLoc + ) { + Cube cube = getInputCubes(player, cubeLoc); + + if (cube == null) return; + + if (cube.particuleBubbleTask != null) { + cube.particuleBubbleTask.cancel(); + cube.particuleBubbleTask = null; + } + + if (cube.corruptedBubbleTask != null) { + cube.corruptedBubbleTask.cancel(); + cube.corruptedBubbleTask = null; + } + MessagesManager.sendMessage(player, Component.text("Bulle corrompue arreté"), Prefix.STAFF, MessageType.SUCCESS, false); + } + @Subcommand("reproduce") @CommandPermission("omc.admins.commands.cube.reproduce") public void reproduceCube( diff --git a/src/main/java/fr/openmc/core/features/cube/ReproductionTask.java b/src/main/java/fr/openmc/core/features/cube/ReproductionTask.java index 7845cd234..0b392ff0b 100644 --- a/src/main/java/fr/openmc/core/features/cube/ReproductionTask.java +++ b/src/main/java/fr/openmc/core/features/cube/ReproductionTask.java @@ -40,6 +40,10 @@ public void run() { world.playSound(babyOrigin, Sound.ENTITY_ENDER_DRAGON_GROWL, 0.3f, 0.2f); world.spawnParticle(Particle.EXPLOSION_EMITTER, babyCube.getCenter(), 3, 1, 1, 1, 0); + for (int i = 0; i <= 10; i++) { + world.strikeLightningEffect(babyCube.getCenter()); + } + MultiBlockManager.register(babyCube); cancel(); parent.reproductionTask = null; diff --git a/src/main/java/fr/openmc/core/features/cube/multiblocks/MultiBlockManager.java b/src/main/java/fr/openmc/core/features/cube/multiblocks/MultiBlockManager.java index b1adcf75c..ca264d4e1 100644 --- a/src/main/java/fr/openmc/core/features/cube/multiblocks/MultiBlockManager.java +++ b/src/main/java/fr/openmc/core/features/cube/multiblocks/MultiBlockManager.java @@ -81,6 +81,8 @@ public static void load() { } public static void save() { + if (config == null) return; + List> list = new ArrayList<>(); for (MultiBlock mb : multiBlocks) { Map map = new HashMap<>(); diff --git a/src/main/java/fr/openmc/core/features/displays/scoreboards/sb/MainScoreboard.java b/src/main/java/fr/openmc/core/features/displays/scoreboards/sb/MainScoreboard.java index 92d08fca7..81e835944 100644 --- a/src/main/java/fr/openmc/core/features/displays/scoreboards/sb/MainScoreboard.java +++ b/src/main/java/fr/openmc/core/features/displays/scoreboards/sb/MainScoreboard.java @@ -9,11 +9,14 @@ import fr.openmc.api.scoreboard.SternalBoard; import fr.openmc.core.features.city.City; import fr.openmc.core.features.city.CityManager; -import fr.openmc.core.features.contest.managers.ContestManager; -import fr.openmc.core.features.contest.models.Contest; import fr.openmc.core.features.displays.scoreboards.BaseScoreboard; import fr.openmc.core.features.economy.EconomyManager; -import fr.openmc.core.features.events.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestPhase; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestData; import fr.openmc.core.utils.DateUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -40,19 +43,21 @@ public void update(Player player, SternalBoard board) { List lines = new ArrayList<>(getDefaultLines(player)); // Contest - Contest data = ContestManager.data; - if (data.getPhase() != 1) { - lines.add(MiniMessage.miniMessage().deserialize("%s".formatted(textToSmall("contest"))).decoration(TextDecoration.BOLD, true)); - lines.add(text(" • ", NamedTextColor.DARK_GRAY) - .append(text(textToSmall(data.getCamp1()), data.getColor1AsNamedTextColor())) - .append(text(textToSmall(" VS "), NamedTextColor.GRAY)) - .append(text(textToSmall(data.getCamp2()), data.getColor2AsNamedTextColor())) - ); - lines.add(Component.text(" • ", NamedTextColor.DARK_GRAY) - .append(Component.text(textToSmall("fin:"), NamedTextColor.GRAY)) - .appendSpace() - .append(text(DateUtils.getTimeUntilNextDay(DayOfWeek.MONDAY), TextColor.color(0xFF8F06))) - ); + if (WeeklyEventsManager.getCurrentEvent() instanceof Contest) { + ContestData data = ContestManager.data; + if (WeeklyEventsManager.getCurrentPhase() != ContestPhase.VOTE_CAMP.getPhase()) { + lines.add(MiniMessage.miniMessage().deserialize("%s".formatted(textToSmall("contest"))).decoration(TextDecoration.BOLD, true)); + lines.add(text(" • ", NamedTextColor.DARK_GRAY) + .append(text(textToSmall(data.getCamp1()), data.getColor1AsNamedTextColor())) + .append(text(textToSmall(" VS "), NamedTextColor.GRAY)) + .append(text(textToSmall(data.getCamp2()), data.getColor2AsNamedTextColor())) + ); + lines.add(Component.text(" • ", NamedTextColor.DARK_GRAY) + .append(Component.text(textToSmall("fin:"), NamedTextColor.GRAY)) + .appendSpace() + .append(text(DateUtils.getTimeUntilNextDay(DayOfWeek.MONDAY), TextColor.color(0xFF8F06))) + ); + } } lines.add(empty()); diff --git a/src/main/java/fr/openmc/core/features/dream/DreamManager.java b/src/main/java/fr/openmc/core/features/dream/DreamManager.java index 991b0dc94..ec28869e2 100644 --- a/src/main/java/fr/openmc/core/features/dream/DreamManager.java +++ b/src/main/java/fr/openmc/core/features/dream/DreamManager.java @@ -42,6 +42,7 @@ import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -231,6 +232,7 @@ public static void addDreamPlayer(Player player, Location oldLocation) throws IO } public static void removeDreamPlayer(Player player, Location dreamLocation) { + player.closeInventory(); player.clearActivePotionEffects(); // supprime les effets des armures des reves DreamPlayer dreamPlayer = dreamPlayerData.remove(player.getUniqueId()); diff --git a/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerDamageListener.java b/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerDamageListener.java index ddc990ffe..d2ad79d0e 100644 --- a/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerDamageListener.java +++ b/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerDamageListener.java @@ -1,19 +1,27 @@ package fr.openmc.core.features.dream.listeners.dream; import fr.openmc.core.features.dream.DreamUtils; +import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageEvent; public class PlayerDamageListener implements Listener { - @EventHandler public void onFall(EntityDamageEvent event) { if (event.getCause() != EntityDamageEvent.DamageCause.FALL) return; if (!(event.getEntity() instanceof Player player)) return; if (DreamUtils.isInDream(player)) { + double fallDistance = player.getFallDistance(); + if (fallDistance < 5) return; + + long secondsLost = (long) (fallDistance * 1.5); + + DreamUtils.removeDreamTime(player, secondsLost, true); + player.playSound(player.getEyeLocation(), Sound.ENTITY_PLAYER_BIG_FALL, 1f, 1f); + event.setCancelled(true); } } diff --git a/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerSleepListener.java b/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerSleepListener.java index c3a2a52c0..744175869 100644 --- a/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerSleepListener.java +++ b/src/main/java/fr/openmc/core/features/dream/listeners/dream/PlayerSleepListener.java @@ -8,62 +8,56 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerBedLeaveEvent; import org.bukkit.event.world.TimeSkipEvent; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitScheduler; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; public class PlayerSleepListener implements Listener { - private final Set playersDreaming = new HashSet<>(); + private final Set isPlayerSleeping = new HashSet<>(); @EventHandler - public void onPlayerSleep(PlayerBedEnterEvent event) { + public void onPlayerEnterBed(PlayerBedEnterEvent event) { Player player = event.getPlayer(); if (!event.getBedEnterResult().equals(PlayerBedEnterEvent.BedEnterResult.OK)) return; - if (playersDreaming.contains(player.getUniqueId())) return; - - Random random = new Random(); - double randomValue = random.nextDouble(); - - if (randomValue < DreamManager.calculateDreamProbability(player)) return; - - player.addPotionEffect(new PotionEffect( - PotionEffectType.NAUSEA, - 20 * 10, - 1, - false, - false, - false - )); - playersDreaming.add(player.getUniqueId()); - + if (isPlayerSleeping.contains(player)) return; + isPlayerSleeping.add(player); + } + @EventHandler + public void onPlayerLeaveBed(PlayerBedLeaveEvent event) { + isPlayerSleeping.remove(event.getPlayer()); } @EventHandler public void onNightSkip(TimeSkipEvent event) { - for (UUID uuid : playersDreaming) { - Player player = Bukkit.getPlayer(uuid); - if (player == null) continue; - DBDreamPlayer dbDreamPlayer = DreamManager.getCacheDreamPlayer(player); - new BukkitRunnable() { - @Override - public void run() { + if (event.getSkipReason() == TimeSkipEvent.SkipReason.NIGHT_SKIP) { + if (isPlayerSleeping.isEmpty()) { + return; + } + for (Player player : isPlayerSleeping) { + if (ThreadLocalRandom.current().nextDouble() < DreamManager.calculateDreamProbability(player)) { + Random r = new Random(); + DBDreamPlayer dbDreamPlayer = DreamManager.getCacheDreamPlayer(player); if (dbDreamPlayer == null || (dbDreamPlayer.getDreamX() == null || dbDreamPlayer.getDreamY() == null || dbDreamPlayer.getDreamZ() == null)) { DreamManager.tpPlayerDream(player); } else { DreamManager.tpPlayerToLastDreamLocation(player); } } - }.runTaskLater(OMCPlugin.getInstance(), 20L * 5); + } + + isPlayerSleeping.clear(); } - playersDreaming.clear(); } } diff --git a/src/main/java/fr/openmc/core/features/dream/mecanism/metaldetector/MetalDetectorListener.java b/src/main/java/fr/openmc/core/features/dream/mecanism/metaldetector/MetalDetectorListener.java index ee322b4d2..d3d17e093 100644 --- a/src/main/java/fr/openmc/core/features/dream/mecanism/metaldetector/MetalDetectorListener.java +++ b/src/main/java/fr/openmc/core/features/dream/mecanism/metaldetector/MetalDetectorListener.java @@ -115,8 +115,8 @@ public static Location findRandomChestLocation(Location origin) { Random random = new Random(); for (int i = 0; i < 30; i++) { - int dx = random.nextInt(20); - int dz = random.nextInt(20); + int dx = random.nextInt(41) - 20; + int dz = random.nextInt(41) - 20; Location tryLoc = origin.clone().add(dx, 0, dz); int y = world.getHighestBlockYAt(tryLoc); tryLoc.setY(y); @@ -126,6 +126,6 @@ public static Location findRandomChestLocation(Location origin) { } } - return origin.clone().add(5, 0, 5); + return origin.clone().add(random.nextInt(41) - 20, 0, random.nextInt(41) - 20); } } diff --git a/src/main/java/fr/openmc/core/features/dream/registries/items/tools/SoulAxe.java b/src/main/java/fr/openmc/core/features/dream/registries/items/tools/SoulAxe.java index aae13e003..16f619070 100644 --- a/src/main/java/fr/openmc/core/features/dream/registries/items/tools/SoulAxe.java +++ b/src/main/java/fr/openmc/core/features/dream/registries/items/tools/SoulAxe.java @@ -13,7 +13,7 @@ public SoulAxe(String name) { @Override public DreamRarity getRarity() { - return DreamRarity.COMMON; + return DreamRarity.RARE; } @Override diff --git a/src/main/java/fr/openmc/core/features/economy/BankManager.java b/src/main/java/fr/openmc/core/features/economy/BankManager.java index 0e1240d3c..6086a0850 100644 --- a/src/main/java/fr/openmc/core/features/economy/BankManager.java +++ b/src/main/java/fr/openmc/core/features/economy/BankManager.java @@ -198,7 +198,8 @@ public static double calculatePlayerInterest(UUID playerUUID) { double interest = .01; // base interest is 1% if (MayorManager.phaseMayor == 2) { - if (PerkManager.hasPerk(CityManager.getPlayerCity(playerUUID).getMayor(), Perks.BUSINESS_MAN.getId())) { + City city = CityManager.getPlayerCity(playerUUID); + if (city != null && PerkManager.hasPerk(city.getMayor(), Perks.BUSINESS_MAN.getId())) { interest += .02; // interest is +2% when perk Business Man enabled } } diff --git a/src/main/java/fr/openmc/core/features/events/commands/EventCommand.java b/src/main/java/fr/openmc/core/features/events/commands/EventCommand.java new file mode 100644 index 000000000..2248a2155 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/commands/EventCommand.java @@ -0,0 +1,18 @@ +package fr.openmc.core.features.events.commands; + +import fr.openmc.core.features.events.menus.CalendarMenu; +import org.bukkit.entity.Player; +import revxrsal.commands.annotation.Command; +import revxrsal.commands.annotation.CommandPlaceholder; +import revxrsal.commands.annotation.Cooldown; +import revxrsal.commands.annotation.Description; + +@Command("events") +@Description("Ouvre l'interface des événement (journalier et weekly") +public class EventCommand { + @Cooldown(2) + @CommandPlaceholder() + public static void mainCommand(Player player) { + new CalendarMenu(player).open(); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/halloween/commands/HalloweenCommands.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/commands/HalloweenCommands.java similarity index 70% rename from src/main/java/fr/openmc/core/features/events/halloween/commands/HalloweenCommands.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/commands/HalloweenCommands.java index 549b56a71..11bde75c0 100644 --- a/src/main/java/fr/openmc/core/features/events/halloween/commands/HalloweenCommands.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/commands/HalloweenCommands.java @@ -1,6 +1,6 @@ -package fr.openmc.core.features.events.halloween.commands; +package fr.openmc.core.features.events.contents.halloween.commands; -import fr.openmc.core.features.events.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; import revxrsal.commands.annotation.Command; import revxrsal.commands.annotation.Subcommand; import revxrsal.commands.bukkit.annotation.CommandPermission; diff --git a/src/main/java/fr/openmc/core/features/events/halloween/listeners/HalloweenNPCListener.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/listeners/HalloweenNPCListener.java similarity index 80% rename from src/main/java/fr/openmc/core/features/events/halloween/listeners/HalloweenNPCListener.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/listeners/HalloweenNPCListener.java index 9c5943936..f5a7c3c83 100644 --- a/src/main/java/fr/openmc/core/features/events/halloween/listeners/HalloweenNPCListener.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/listeners/HalloweenNPCListener.java @@ -1,7 +1,7 @@ -package fr.openmc.core.features.events.halloween.listeners; +package fr.openmc.core.features.events.contents.halloween.listeners; import de.oliver.fancynpcs.api.events.NpcInteractEvent; -import fr.openmc.core.features.events.halloween.menus.HalloweenPumpkinDepositMenu; +import fr.openmc.core.features.events.contents.halloween.menus.HalloweenPumpkinDepositMenu; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; diff --git a/src/main/java/fr/openmc/core/features/events/halloween/managers/HalloweenManager.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/managers/HalloweenManager.java similarity index 97% rename from src/main/java/fr/openmc/core/features/events/halloween/managers/HalloweenManager.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/managers/HalloweenManager.java index bfbf96700..f845ebf68 100644 --- a/src/main/java/fr/openmc/core/features/events/halloween/managers/HalloweenManager.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/managers/HalloweenManager.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.events.halloween.managers; +package fr.openmc.core.features.events.contents.halloween.managers; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; @@ -10,8 +10,8 @@ import fr.openmc.api.hooks.FancyNpcsHook; import fr.openmc.core.OMCPlugin; import fr.openmc.core.features.economy.EconomyManager; -import fr.openmc.core.features.events.halloween.listeners.HalloweenNPCListener; -import fr.openmc.core.features.events.halloween.models.HalloweenData; +import fr.openmc.core.features.events.contents.halloween.listeners.HalloweenNPCListener; +import fr.openmc.core.features.events.contents.halloween.models.HalloweenData; import fr.openmc.core.features.leaderboards.LeaderboardManager; import fr.openmc.core.features.mailboxes.MailboxManager; import fr.openmc.core.registry.items.CustomItemRegistry; diff --git a/src/main/java/fr/openmc/core/features/events/halloween/menus/HalloweenPumpkinDepositMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/menus/HalloweenPumpkinDepositMenu.java similarity index 96% rename from src/main/java/fr/openmc/core/features/events/halloween/menus/HalloweenPumpkinDepositMenu.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/menus/HalloweenPumpkinDepositMenu.java index d2c0142a1..6b25b72c1 100644 --- a/src/main/java/fr/openmc/core/features/events/halloween/menus/HalloweenPumpkinDepositMenu.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/menus/HalloweenPumpkinDepositMenu.java @@ -1,9 +1,9 @@ -package fr.openmc.core.features.events.halloween.menus; +package fr.openmc.core.features.events.contents.halloween.menus; import fr.openmc.api.menulib.Menu; import fr.openmc.api.menulib.utils.InventorySize; import fr.openmc.api.menulib.utils.ItemBuilder; -import fr.openmc.core.features.events.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; import fr.openmc.core.utils.ItemUtils; import fr.openmc.core.utils.messages.MessageType; import fr.openmc.core.utils.messages.MessagesManager; diff --git a/src/main/java/fr/openmc/core/features/events/halloween/models/HalloweenData.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/models/HalloweenData.java similarity index 90% rename from src/main/java/fr/openmc/core/features/events/halloween/models/HalloweenData.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/models/HalloweenData.java index 038ad60a5..ffc4cac6e 100644 --- a/src/main/java/fr/openmc/core/features/events/halloween/models/HalloweenData.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/models/HalloweenData.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.events.halloween.models; +package fr.openmc.core.features.events.contents.halloween.models; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/WeeklyEventsManager.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/WeeklyEventsManager.java new file mode 100644 index 000000000..7441bd546 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/WeeklyEventsManager.java @@ -0,0 +1,260 @@ +package fr.openmc.core.features.events.contents.weeklyevents; + +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEvent; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventsData; +import fr.openmc.core.utils.DateUtils; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; + +import java.sql.SQLException; +import java.util.List; + +public class WeeklyEventsManager { + + private static final List EVENTS = List.of( + new Contest() + ); + + private static Dao dao; + private static WeeklyEventsData data; + private static BukkitTask currentTask = null; + + /** + * Initialise la gestion des WeeklyEvents. + * Au restart : si on est déjà le bon jour pour la phase courante et que l'event + * était actif, on relance l'action immédiatement. + */ + public static void init() { + data = load(); + + WeeklyEventPhase currentPhase = getCurrentPhase(); + if (data.isActive() && currentPhase != null && DateUtils.getCurrentDayOfWeek().equals(currentPhase.getStartDay())) { + Runnable action = currentPhase.runAction(); + if (action != null) action.run(); + } + + scheduleNextPhase(); + } + + /** + * Initialise la BDD : crée la table si nécessaire, charge les données, gère le cas restart + */ + public static void initDB(ConnectionSource connectionSource) throws SQLException { + dao = DaoManager.createDao(connectionSource, WeeklyEventsData.class); + TableUtils.createTableIfNotExists(connectionSource, WeeklyEventsData.class); + } + + /** + * Charge les données depuis la BDD, ou crée une ligne par défaut si inexistante. + */ + public static WeeklyEventsData load() { + try { + WeeklyEventsData data = dao.queryForId(1); + if (data == null) { + data = new WeeklyEventsData(0, 0); + dao.create(data); + } + return data; + } catch (SQLException e) { + throw new RuntimeException("Erreur lors du chargement de WeeklyEventData", e); + } + } + + /** + * Sauvegarde les données en BDD. + */ + public static void save(WeeklyEventsData data) { + try { + dao.createOrUpdate(data); + } catch (SQLException e) { + throw new RuntimeException("Erreur lors de la sauvegarde de WeeklyEventData", e); + } + } + + /** + * Retourne l'event en cours. + */ + public static WeeklyEvent getCurrentEvent() { + return EVENTS.get(data.getCurrentEventIndex()); + } + + /** + * Retourne l'instance de WeeklyEvent correspondant à la classe donnée, ou null si introuvable. + */ + public static WeeklyEvent getEvent(Class eventClass) { + return EVENTS.stream() + .filter(event -> event.getClass().equals(eventClass)) + .findFirst() + .orElse(null); + } + + /** + * Retourne la phase en cours selon l'index en BDD, ou null si invalide. + */ + public static WeeklyEventPhase getCurrentPhase() { + List phases = getCurrentEvent().getPhases(); + int index = data.getCurrentPhaseIndex(); + if (index < 0 || index >= phases.size()) return null; + return phases.get(index); + } + + /** + * Retourne true si un event est actuellement actif (flag BDD). + * Source de vérité : le flag active, pas le temps. + */ + public static boolean isEventActive() { + return data.isActive(); + } + + /** + * Planifie la prochaine phase. + * Cancel la task précédente pour éviter les doublons. + * Guard intégré : si findNextPhase() a changé entre le schedule et l'exécution + * (suite à un force), on se recalibre sans exécuter la mauvaise action. + */ + public static void scheduleNextPhase() { + if (currentTask != null) { + currentTask.cancel(); + currentTask = null; + } + + WeeklyEventPhase nextPhase = findNextPhase(); + if (nextPhase == null) return; + + long delayTicks = DateUtils.getSecondsUntilDayOfWeekTime( + nextPhase.getStartDay(), + nextPhase.getStartHour(), + nextPhase.getStartMinutes(), + 0 + ) * 20L; + + if (delayTicks <= 0) { + runPhase(nextPhase); + return; + } + + currentTask = Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { + if (findNextPhase() != nextPhase) { + scheduleNextPhase(); + return; + } + runPhase(nextPhase); + }, delayTicks); + } + + /** + * Exécute l'action de la phase, marque l'event comme actif, + * avance l'état, puis schedule la suivante. + */ + private static void runPhase(WeeklyEventPhase phase) { + data.setActive(true); + save(data); + + Runnable action = phase.runAction(); + if (action != null) action.run(); + + advancePhase(); + scheduleNextPhase(); + } + + /** + * Avance à la phase suivante. + * Si c'était la dernière phase, passe à l'event suivant (et marque inactif). + */ + private static void advancePhase() { + List phases = getCurrentEvent().getPhases(); + int nextPhaseIndex = data.getCurrentPhaseIndex() + 1; + + if (nextPhaseIndex >= phases.size()) { + advanceToNextEvent(); + } else { + data.setCurrentPhaseIndex(nextPhaseIndex); + save(data); + } + } + + /** + * Force un event à une phase spécifique. + * Met à jour la BDD, exécute l'action, gère le cas dernière phase, + * puis reschedule proprement. + */ + public static void forceEventAtPhase(WeeklyEvent event, WeeklyEventPhase phase) { + int eventIndex = EVENTS.indexOf(event); + int phaseIndex = event.getPhases().indexOf(phase); + + if (eventIndex == -1 || phaseIndex == -1) { + OMCPlugin.getInstance().getSLF4JLogger().error("[WeeklyEvents] Event ou phase non trouvé"); + return; + } + + data.setCurrentEventIndex(eventIndex); + data.setCurrentPhaseIndex(phaseIndex); + data.setActive(true); + save(data); + + Runnable action = phase.runAction(); + if (action != null) action.run(); + + boolean isLastPhase = phaseIndex == event.getPhases().size() - 1; + if (isLastPhase) { + advanceToNextEvent(); + } + + scheduleNextPhase(); + + OMCPlugin.getInstance().getSLF4JLogger().info("[WeeklyEvents] Event forcé : {} à la phase {}", + PlainTextComponentSerializer.plainText().serialize(event.getName()), + PlainTextComponentSerializer.plainText().serialize(phase.getName())); + } + + /** + * Passe à l'event suivant, réinitialise la phase à 0 et marque l'event comme inactif. + */ + private static void advanceToNextEvent() { + data.setActive(false); + int nextIndex = (data.getCurrentEventIndex() + 1) % EVENTS.size(); + data.setCurrentEventIndex(nextIndex); + data.setCurrentPhaseIndex(0); + save(data); + + OMCPlugin.getInstance().getSLF4JLogger().info("[WeeklyEvents] Passage à l'event suivant : {}", + PlainTextComponentSerializer.plainText().serialize(getCurrentEvent().getName())); + } + + /** + * Cherche la prochaine phase à venir en parcourant tous les events cycliquement. + * Commence à la phase courante de l'event courant, puis les events suivants depuis 0. + */ + private static WeeklyEventPhase findNextPhase() { + int totalEvents = EVENTS.size(); + + for (int i = 0; i < totalEvents; i++) { + int eventIdx = (data.getCurrentEventIndex() + i) % totalEvents; + WeeklyEvent event = EVENTS.get(eventIdx); + List phases = event.getPhases(); + + int phaseStart = (i == 0) ? data.getCurrentPhaseIndex() : 0; + + for (int j = phaseStart; j < phases.size(); j++) { + WeeklyEventPhase phase = phases.get(j); + long delay = DateUtils.getSecondsUntilDayOfWeekTime( + phase.getStartDay(), + phase.getStartHour(), + phase.getStartMinutes(), + 0 + ); + if (delay >= 0) return phase; + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/Contest.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/Contest.java new file mode 100644 index 000000000..f7db60aa1 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/Contest.java @@ -0,0 +1,38 @@ +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest; + +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEvent; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; +import fr.openmc.core.registry.items.CustomItemRegistry; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; + +public class Contest extends WeeklyEvent { + @Override + public Component getName() { + return Component.text("Contest", NamedTextColor.GOLD, TextDecoration.BOLD); + } + + @Override + public List getDescription() { + return List.of( + Component.text("2 camps s'affrontent pendant le weekend", NamedTextColor.GRAY), + Component.text("Votez pour votre camp, et participez à l'affrontement !", NamedTextColor.GRAY), + Component.text("Le camp gagnant remportera des récompenses exclusives !", NamedTextColor.GRAY) + ); + } + + @Override + public ItemStack getIcon() { + return CustomItemRegistry.getByName("omc_contest:contest_shell").getBest(); + } + + @Override + public List getPhases() { + return Arrays.stream(ContestPhase.values()).map(ContestPhase::getPhase).toList(); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/ContestPhase.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/ContestPhase.java new file mode 100644 index 000000000..8dbdc77f1 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/ContestPhase.java @@ -0,0 +1,125 @@ +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest; + +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.time.DayOfWeek; +import java.util.Arrays; +import java.util.List; + +@Getter +public enum ContestPhase { + VOTE_CAMP(new WeeklyEventPhase() { + @Override + public Component getName() { + return Component.text("Les votes"); + } + + @Override + public List getDescription() { + return Arrays.asList( + Component.text("§7Tout les vendredi, le contest commence"), + Component.text("§7Et les votes s'ouvrent, et il faut choisir"), + Component.text("§7Entre 2 camps, une ambience se crée dans le spawn...") + ); + } + + @Override + public DayOfWeek getStartDay() { + return DayOfWeek.FRIDAY; + } + + @Override + public int getStartHour() { + return 0; + } + + @Override + public int getStartMinutes() { + return 0; + } + + @Override + public Runnable runAction() { + return ContestManager::initPhase1; + } + }), + TRADE_PHASE(new WeeklyEventPhase() { + @Override + public Component getName() { + return Component.text("L'affrontement"); + } + + @Override + public List getDescription() { + return Arrays.asList( + Component.text("§7La nuit tombe sur le spawn pendant 2 jours"), + Component.text("§7Que la fête commence !"), + Component.text("§7Des trades sont disponible"), + Component.text("§7Donnant des coquillages de contest !")); + } + + @Override + public DayOfWeek getStartDay() { + return DayOfWeek.SATURDAY; + } + + @Override + public int getStartHour() { + return 0; + } + + @Override + public int getStartMinutes() { + return 0; + } + + @Override + public Runnable runAction() { + return ContestManager::initPhase2; + } + }), + END_PHASE(new WeeklyEventPhase() { + @Override + public Component getName() { + return Component.text("Les résultats"); + } + + @Override + public List getDescription() { + return Arrays.asList( + Component.text("§7Le levé de soleil sur le spawn !"), + Component.text("§7Les résultats tombent, et un camp"), + Component.text("§7sera gagnant. Et des récompenses seront attribuées"), + Component.text(("§7à chacun."))); + } + + @Override + public DayOfWeek getStartDay() { + return DayOfWeek.MONDAY; + } + + @Override + public int getStartHour() { + return 0; + } + + @Override + public int getStartMinutes() { + return 0; + } + + @Override + public Runnable runAction() { + return ContestManager::initPhase3; + } + }) + ; + + private final WeeklyEventPhase phase; + ContestPhase(WeeklyEventPhase phase) { + this.phase = phase; + } +} diff --git a/src/main/java/fr/openmc/core/features/contest/commands/ContestCommand.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/ContestCommand.java similarity index 54% rename from src/main/java/fr/openmc/core/features/contest/commands/ContestCommand.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/ContestCommand.java index 608af262f..9f82c3ea3 100644 --- a/src/main/java/fr/openmc/core/features/contest/commands/ContestCommand.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/ContestCommand.java @@ -1,13 +1,18 @@ -package fr.openmc.core.features.contest.commands; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands; import fr.openmc.core.commands.autocomplete.OnlinePlayerAutoComplete; -import fr.openmc.core.features.contest.commands.autocomplete.ColorContestAutoComplete; -import fr.openmc.core.features.contest.commands.autocomplete.TradeContestAutoComplete; -import fr.openmc.core.features.contest.managers.ContestManager; -import fr.openmc.core.features.contest.managers.ContestPlayerManager; -import fr.openmc.core.features.contest.managers.TradeYMLManager; -import fr.openmc.core.features.contest.menu.ContributionMenu; -import fr.openmc.core.features.contest.menu.VoteMenu; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestPhase; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.autocomplete.ColorContestAutoComplete; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.autocomplete.ContestPhaseAutoComplete; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.autocomplete.TradeContestAutoComplete; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestPlayerManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.TradeYMLManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu.ContributionMenu; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu.VoteMenu; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; import fr.openmc.core.utils.DateUtils; import fr.openmc.core.utils.messages.MessageType; import fr.openmc.core.utils.messages.MessagesManager; @@ -18,9 +23,10 @@ import revxrsal.commands.annotation.*; import revxrsal.commands.bukkit.annotation.CommandPermission; -import java.time.DayOfWeek; -import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; @Command("contest") @Description("Ouvre l'interface des festivals, et quand un festival commence, vous pouvez choisir votre camp") @@ -28,39 +34,33 @@ public class ContestCommand { @Cooldown(4) @CommandPlaceholder() public static void mainCommand(Player player) { - int phase = ContestManager.data.getPhase(); - if ((phase >= 2 && ContestManager.dataPlayer.get(player.getUniqueId()) == null) || (phase == 2)) { - VoteMenu menu = new VoteMenu(player); - menu.open(); - } else if (phase == 3 && ContestManager.dataPlayer.get(player.getUniqueId()) != null) { - ContributionMenu menu = new ContributionMenu(player); - menu.open(); + Contest contest = (Contest) WeeklyEventsManager.getEvent(Contest.class); - } else { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E", Locale.FRENCH); - DayOfWeek dayStartContestOfWeek = DayOfWeek.from(formatter.parse(ContestManager.data.getStartdate())); + if (!contest.isActive()) { + int days = (ContestPhase.VOTE_CAMP.getPhase().getStartDay().getValue() + - DateUtils.getCurrentDayOfWeek().getValue() + 7) % 7; + MessagesManager.sendMessage(player, Component.text("§cIl n'y a aucun Contest ! Revenez dans " + days + " jour(s)."), Prefix.CONTEST, MessageType.ERROR, true); + return; + } - int days = (dayStartContestOfWeek.getValue() - DateUtils.getCurrentDayOfWeek().getValue() + 7) % 7; + WeeklyEventPhase activePhase = contest.getActivePhase(); - MessagesManager.sendMessage(player, Component.text("§cIl n'y a aucun Contest ! Revenez dans " + days + " jour(s)."), Prefix.CONTEST, MessageType.ERROR, true); + if (activePhase == ContestPhase.VOTE_CAMP.getPhase()) { + new VoteMenu(player).open(); + } else if (activePhase == ContestPhase.TRADE_PHASE.getPhase()) { + if (ContestManager.dataPlayer.get(player.getUniqueId()) != null) { + new ContributionMenu(player).open(); + } else { + new VoteMenu(player).open(); + } } } @Subcommand("setphase") @Description("Permet de lancer une procédure de phase") @CommandPermission("omc.admin.commands.contest.setphase") - public void setPhase(@Named("phase") @Suggest({"1", "2", "3"}) Integer phase) { - switch(phase) { - case 2: - ContestManager.initPhase2(); - break; - case 3: - ContestManager.initPhase3(); - break; - default: - ContestManager.initPhase1(); - break; - } + public void setPhase(@Named("phase") @SuggestWith(ContestPhaseAutoComplete.class) String phase) { + WeeklyEventsManager.forceEventAtPhase(WeeklyEventsManager.getEvent(Contest.class), ContestPhase.valueOf(phase).getPhase()); } @Subcommand("setcontest") @@ -73,19 +73,25 @@ public void setContest( @Named("nom du camp 2") String camp2, @Named("couleur du camp 2") @SuggestWith(ColorContestAutoComplete.class) String color2 ) { - int phase = ContestManager.data.getPhase(); - if (phase == 1) { - // It is unique, but it is for performance reasons - if (new HashSet<>(ContestManager.getColorContestList()).containsAll(Arrays.asList(color1, color2))) { - ContestManager.clearDB(); - ContestManager.insertCustomContest(camp1, color1, camp2, color2); - - MessagesManager.sendMessage(player, Component.text("§aLe Contest : " + camp1 + " VS " + camp2 + " a bien été sauvegardé\nMerci d'attendre que les données en cache s'actualise."), Prefix.STAFF, MessageType.SUCCESS, true); - } else { - MessagesManager.sendMessage(player, Component.text("§c/contest setcontest et color doit comporter une couleur valide"), Prefix.STAFF, MessageType.ERROR, true); - } + Contest contest = (Contest) WeeklyEventsManager.getEvent(Contest.class); + + if (!contest.isActive()) { + MessagesManager.sendMessage(player, Component.text("§cVous ne pouvez pas définir un contest lorsqu'il n'est pas actif"), Prefix.STAFF, MessageType.ERROR, true); + return; + } + + if (contest.getActivePhase() != ContestPhase.VOTE_CAMP.getPhase()) { + MessagesManager.sendMessage(player, Component.text("§cVous ne pouvez pas définir un contest lorsqu'il a commencé"), Prefix.STAFF, MessageType.ERROR, true); + return; + } + + // It is unique, but it is for performance reasons + if (new HashSet<>(ContestManager.getColorContestList()).containsAll(Arrays.asList(color1, color2))) { + ContestManager.clearDB(); + ContestManager.insertCustomContest(camp1, color1, camp2, color2); + MessagesManager.sendMessage(player, Component.text("§aLe Contest : " + camp1 + " VS " + camp2 + " a bien été sauvegardé\nMerci d'attendre que les données en cache s'actualise."), Prefix.STAFF, MessageType.SUCCESS, true); } else { - MessagesManager.sendMessage(player, Component.text("§cVous pouvez pas définir un contest lorsqu'il a commencé"), Prefix.STAFF, MessageType.ERROR, true); + MessagesManager.sendMessage(player, Component.text("§c/contest setcontest et color doit comporter une couleur valide"), Prefix.STAFF, MessageType.ERROR, true); } } @@ -127,7 +133,9 @@ public void addPoints( @Named("membre") @SuggestWith(OnlinePlayerAutoComplete.class) Player target, @Named("points") Integer points ) { - if (ContestManager.data.getPhase()!=3) { + Contest contest = (Contest) WeeklyEventsManager.getEvent(Contest.class); + + if (contest.getActivePhase() != ContestPhase.TRADE_PHASE.getPhase()) { MessagesManager.sendMessage(player, Component.text("§cVous ne pouvez pas donner des points lorsque le contest n'a pas commencé"), Prefix.STAFF, MessageType.ERROR, true); return; } @@ -137,7 +145,7 @@ public void addPoints( return; } - if (points<=0) { + if (points <= 0) { MessagesManager.sendMessage(player, Component.text("§cVous ne pouvez pas donner des points négatifs ou égal à 0"), Prefix.STAFF, MessageType.ERROR, true); return; } diff --git a/src/main/java/fr/openmc/core/features/contest/commands/autocomplete/ColorContestAutoComplete.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/ColorContestAutoComplete.java similarity index 71% rename from src/main/java/fr/openmc/core/features/contest/commands/autocomplete/ColorContestAutoComplete.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/ColorContestAutoComplete.java index 8375b2f0c..8f1a59d2a 100644 --- a/src/main/java/fr/openmc/core/features/contest/commands/autocomplete/ColorContestAutoComplete.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/ColorContestAutoComplete.java @@ -1,6 +1,6 @@ -package fr.openmc.core.features.contest.commands.autocomplete; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.autocomplete; -import fr.openmc.core.features.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; import org.jetbrains.annotations.NotNull; import revxrsal.commands.autocomplete.SuggestionProvider; import revxrsal.commands.bukkit.actor.BukkitCommandActor; diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/ContestPhaseAutoComplete.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/ContestPhaseAutoComplete.java new file mode 100644 index 000000000..2ed0801db --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/ContestPhaseAutoComplete.java @@ -0,0 +1,20 @@ +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.autocomplete; + +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestPhase; +import org.jetbrains.annotations.NotNull; +import revxrsal.commands.autocomplete.SuggestionProvider; +import revxrsal.commands.bukkit.actor.BukkitCommandActor; +import revxrsal.commands.node.ExecutionContext; + +import java.util.Arrays; +import java.util.List; + +public class ContestPhaseAutoComplete implements SuggestionProvider { + + @Override + public @NotNull List getSuggestions(@NotNull ExecutionContext context) { + return Arrays.stream(ContestPhase.values()) + .map(Enum::name) + .toList(); + } +} diff --git a/src/main/java/fr/openmc/core/features/contest/commands/autocomplete/TradeContestAutoComplete.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/TradeContestAutoComplete.java similarity index 71% rename from src/main/java/fr/openmc/core/features/contest/commands/autocomplete/TradeContestAutoComplete.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/TradeContestAutoComplete.java index d416fd1d8..6bd985dd2 100644 --- a/src/main/java/fr/openmc/core/features/contest/commands/autocomplete/TradeContestAutoComplete.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/commands/autocomplete/TradeContestAutoComplete.java @@ -1,6 +1,6 @@ -package fr.openmc.core.features.contest.commands.autocomplete; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.autocomplete; -import fr.openmc.core.features.contest.managers.TradeYMLManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.TradeYMLManager; import org.jetbrains.annotations.NotNull; import revxrsal.commands.autocomplete.SuggestionProvider; import revxrsal.commands.bukkit.actor.BukkitCommandActor; diff --git a/src/main/java/fr/openmc/core/features/contest/ContestEndEvent.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/events/ContestEndEvent.java similarity index 82% rename from src/main/java/fr/openmc/core/features/contest/ContestEndEvent.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/events/ContestEndEvent.java index c15a04e1b..56b0de067 100644 --- a/src/main/java/fr/openmc/core/features/contest/ContestEndEvent.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/events/ContestEndEvent.java @@ -1,6 +1,6 @@ -package fr.openmc.core.features.contest; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.events; -import fr.openmc.core.features.contest.models.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestData; import lombok.Getter; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -19,7 +19,7 @@ public class ContestEndEvent extends Event { /** * Les données associées au contest. */ - private final Contest contestData; + private final ContestData contestData; /** * Liste statique des handlers pour l'événement. @@ -43,7 +43,7 @@ public class ContestEndEvent extends Event { * @param winners La liste des gagnants * @param losers La liste des perdants */ - public ContestEndEvent(Contest contestData, List winners, List losers) { + public ContestEndEvent(ContestData contestData, List winners, List losers) { this.contestData = contestData; this.winners = winners; this.losers = losers; diff --git a/src/main/java/fr/openmc/core/features/contest/listeners/ContestIntractEvents.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/listeners/ContestIntractEvents.java similarity index 88% rename from src/main/java/fr/openmc/core/features/contest/listeners/ContestIntractEvents.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/listeners/ContestIntractEvents.java index 05e9e6c49..d02d878ed 100644 --- a/src/main/java/fr/openmc/core/features/contest/listeners/ContestIntractEvents.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/listeners/ContestIntractEvents.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.contest.listeners; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.listeners; import dev.lone.itemsadder.api.Events.FurnitureInteractEvent; import org.bukkit.Bukkit; diff --git a/src/main/java/fr/openmc/core/features/contest/managers/ContestManager.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/ContestManager.java similarity index 87% rename from src/main/java/fr/openmc/core/features/contest/managers/ContestManager.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/ContestManager.java index 3b5ae4cd8..8b33069e2 100644 --- a/src/main/java/fr/openmc/core/features/contest/managers/ContestManager.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/ContestManager.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.contest.managers; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; @@ -8,21 +8,20 @@ import fr.openmc.api.menulib.Menu; import fr.openmc.core.CommandsManager; import fr.openmc.core.OMCPlugin; -import fr.openmc.core.features.contest.ContestEndEvent; -import fr.openmc.core.features.contest.commands.ContestCommand; -import fr.openmc.core.features.contest.listeners.ContestIntractEvents; -import fr.openmc.core.features.contest.menu.ContributionMenu; -import fr.openmc.core.features.contest.menu.MoreInfoMenu; -import fr.openmc.core.features.contest.menu.TradeMenu; -import fr.openmc.core.features.contest.menu.VoteMenu; -import fr.openmc.core.features.contest.models.Contest; -import fr.openmc.core.features.contest.models.ContestPlayer; import fr.openmc.core.features.economy.EconomyManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.ContestCommand; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.events.ContestEndEvent; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.listeners.ContestIntractEvents; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu.ContributionMenu; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu.MoreInfoMenu; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu.TradeMenu; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu.VoteMenu; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestData; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestPlayer; import fr.openmc.core.features.leaderboards.LeaderboardManager; import fr.openmc.core.features.mailboxes.MailboxManager; import fr.openmc.core.registry.items.CustomItemRegistry; import fr.openmc.core.utils.ColorUtils; -import fr.openmc.core.utils.DateUtils; import fr.openmc.core.utils.ParticleUtils; import fr.openmc.core.utils.cache.CacheOfflinePlayer; import fr.openmc.core.utils.database.DatabaseManager; @@ -42,7 +41,6 @@ import java.sql.SQLException; import java.text.DecimalFormat; -import java.time.DayOfWeek; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -51,11 +49,7 @@ public class ContestManager { - private static final DayOfWeek START_CONTEST_DAY = DayOfWeek.FRIDAY; - private static final DayOfWeek START_TRADE_CONTEST_DAY = DayOfWeek.SATURDAY; - private static final DayOfWeek END_CONTEST_DAY = DayOfWeek.MONDAY; - - public static Contest data; + public static ContestData data; public static Map dataPlayer = new HashMap<>(); private static final List colorContest = Arrays.asList( @@ -94,19 +88,14 @@ public static void init() { ); // ** MANAGER EXTERNE ** - new TradeYMLManager(); + TradeYMLManager.init(); // ** LOAD DATAS ** initContestData(); loadContestPlayerData(); - - // ** SCHEDULE TASK ** - scheduleStartContest(); - scheduleStartTradeContest(); - scheduleEndContest(); } - private static Dao contestDao; + private static Dao contestDao; private static Dao playerDao; /** @@ -114,8 +103,8 @@ public static void init() { * (création des tables si elles n’existent pas encore) */ public static void initDB(ConnectionSource connectionSource) throws SQLException { - TableUtils.createTableIfNotExists(connectionSource, Contest.class); - contestDao = DaoManager.createDao(connectionSource, Contest.class); + TableUtils.createTableIfNotExists(connectionSource, ContestData.class); + contestDao = DaoManager.createDao(connectionSource, ContestData.class); TableUtils.createTableIfNotExists(connectionSource, ContestPlayer.class); playerDao = DaoManager.createDao(connectionSource, ContestPlayer.class); @@ -129,7 +118,7 @@ public static void initContestData() { try { data = contestDao.queryForFirst(); if (data == null) { - data = new Contest("Mayonnaise", "Ketchup", "YELLOW", "RED", 1, "ven.", 0, 0); + data = new ContestData("Mayonnaise", "Ketchup", "YELLOW", "RED", 0, 0); contestDao.create(data); } } catch (SQLException e) { @@ -180,7 +169,7 @@ public static void saveContestPlayerData() { */ public static void clearDB() { try { - TableUtils.clearTable(DatabaseManager.getConnectionSource(), Contest.class); + TableUtils.clearTable(DatabaseManager.getConnectionSource(), ContestData.class); TableUtils.clearTable(DatabaseManager.getConnectionSource(), ContestPlayer.class); } catch (SQLException e) { throw new RuntimeException(e); @@ -194,7 +183,6 @@ public static void clearDB() { * - Diffuse un message et joue un son aux joueurs connectés */ public static void initPhase1() { - data.setPhase(2); ParticleUtils.color1 = null; ParticleUtils.color2 = null; @@ -230,8 +218,6 @@ public static void initPhase2() { TradeYMLManager.updateColumnBooleanFromRandomTrades(true, (String) trade.get("ress")); } - data.setPhase(3); - Bukkit.broadcast(Component.text(""" §8§m §r §7 @@ -256,8 +242,6 @@ public static void initPhase2() { * - Réinitialise les données en DB pour le prochain contest */ public static void initPhase3() { - data.setPhase(4); - ParticleUtils.color1 = null; ParticleUtils.color2 = null; @@ -571,60 +555,6 @@ public static List getColorContestList() { * Insère un contest personnalisé dans la DB avec 2 camps et leurs couleurs. */ public static void insertCustomContest(String camp1, String color1, String camp2, String color2) { - data = new Contest(camp1, camp2, color1, color2, 1, "ven.", 0, 0); - } - - /** - * Programme le lancement de la phase 1 (votes) chaque vendredi à minuit. - */ - private static void scheduleStartContest() { - long delayInTicks = DateUtils.getSecondsUntilDayOfWeekMidnight(START_CONTEST_DAY) * 20; - - if (data.getPhase() == 1 && DateUtils.getCurrentDayOfWeek().equals(START_CONTEST_DAY)) { - ContestManager.initPhase1(); - } - - Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { - if (data.getPhase() != 1) return; - - ContestManager.initPhase1(); - scheduleStartContest(); - }, delayInTicks); - } - - /** - * Programme le lancement de la phase 2 (contributions) chaque samedi à minuit. - */ - private static void scheduleStartTradeContest() { - long delayInTicks = DateUtils.getSecondsUntilDayOfWeekMidnight(START_TRADE_CONTEST_DAY) * 20; - - if (data.getPhase() == 2 && DateUtils.getCurrentDayOfWeek().equals(START_TRADE_CONTEST_DAY)) { - ContestManager.initPhase2(); - } - - Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { - if (data.getPhase() != 2) return; - - ContestManager.initPhase2(); - scheduleStartTradeContest(); - }, delayInTicks); - } - - /** - * Programme la fin du contest (phase 3) chaque lundi à minuit. - */ - private static void scheduleEndContest() { - long delayInTicks = DateUtils.getSecondsUntilDayOfWeekMidnight(END_CONTEST_DAY) * 20; - - if (data.getPhase() == 3 && DateUtils.getCurrentDayOfWeek().equals(END_CONTEST_DAY)) { - ContestManager.initPhase3(); - } - - Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { - if (data.getPhase() != 3) return; - - ContestManager.initPhase3(); - scheduleEndContest(); - }, delayInTicks); + data = new ContestData(camp1, camp2, color1, color2, 0, 0); } } diff --git a/src/main/java/fr/openmc/core/features/contest/managers/ContestPlayerManager.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/ContestPlayerManager.java similarity index 97% rename from src/main/java/fr/openmc/core/features/contest/managers/ContestPlayerManager.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/ContestPlayerManager.java index 3690b36dd..743943cae 100644 --- a/src/main/java/fr/openmc/core/features/contest/managers/ContestPlayerManager.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/ContestPlayerManager.java @@ -1,6 +1,6 @@ -package fr.openmc.core.features.contest.managers; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers; -import fr.openmc.core.features.contest.models.ContestPlayer; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestPlayer; import lombok.Setter; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; diff --git a/src/main/java/fr/openmc/core/features/contest/managers/TradeYMLManager.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/TradeYMLManager.java similarity index 96% rename from src/main/java/fr/openmc/core/features/contest/managers/TradeYMLManager.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/TradeYMLManager.java index 725830124..374f57e73 100644 --- a/src/main/java/fr/openmc/core/features/contest/managers/TradeYMLManager.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/managers/TradeYMLManager.java @@ -1,7 +1,7 @@ -package fr.openmc.core.features.contest.managers; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers; import fr.openmc.core.OMCPlugin; -import fr.openmc.core.features.contest.models.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestData; import fr.openmc.core.utils.YmlUtils; import lombok.Getter; import org.bukkit.configuration.file.FileConfiguration; @@ -33,7 +33,7 @@ public class TradeYMLManager { * Constructeur de TradeYMLManager. * Initialise le fichier contest.yml et charge sa configuration. */ - public TradeYMLManager() { + public static void init() { contestFile = new File(OMCPlugin.getInstance().getDataFolder() + "/data", "contest.yml"); loadContestConfig(); } @@ -197,13 +197,11 @@ public static void selectRandomlyContest() { Random random = new Random(); Map selectedContest = leastSelectedContests.get(random.nextInt(leastSelectedContests.size())); - ContestManager.data = new Contest( + ContestManager.data = new ContestData( (String) selectedContest.get("camp1"), (String) selectedContest.get("camp2"), (String) selectedContest.get("color1"), (String) selectedContest.get("color2"), - 1, - "ven.", 0, 0 ); diff --git a/src/main/java/fr/openmc/core/features/contest/menu/ContributionMenu.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/ContributionMenu.java similarity index 96% rename from src/main/java/fr/openmc/core/features/contest/menu/ContributionMenu.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/ContributionMenu.java index 2048a6d74..b3b4300d9 100644 --- a/src/main/java/fr/openmc/core/features/contest/menu/ContributionMenu.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/ContributionMenu.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.contest.menu; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu; import dev.lone.itemsadder.api.CustomStack; import dev.lone.itemsadder.api.FontImages.FontImageWrapper; @@ -6,8 +6,8 @@ import fr.openmc.api.menulib.Menu; import fr.openmc.api.menulib.utils.InventorySize; import fr.openmc.api.menulib.utils.ItemBuilder; -import fr.openmc.core.features.contest.managers.ContestManager; -import fr.openmc.core.features.contest.managers.ContestPlayerManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestPlayerManager; import fr.openmc.core.registry.items.CustomItemRegistry; import fr.openmc.core.utils.ColorUtils; import fr.openmc.core.utils.ItemUtils; diff --git a/src/main/java/fr/openmc/core/features/contest/menu/MoreInfoMenu.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/MoreInfoMenu.java similarity index 86% rename from src/main/java/fr/openmc/core/features/contest/menu/MoreInfoMenu.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/MoreInfoMenu.java index e979d9d20..da09f0c34 100644 --- a/src/main/java/fr/openmc/core/features/contest/menu/MoreInfoMenu.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/MoreInfoMenu.java @@ -1,10 +1,12 @@ -package fr.openmc.core.features.contest.menu; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu; import dev.lone.itemsadder.api.FontImages.FontImageWrapper; import fr.openmc.api.menulib.Menu; import fr.openmc.api.menulib.utils.InventorySize; import fr.openmc.api.menulib.utils.ItemBuilder; -import fr.openmc.core.features.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestPhase; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -68,10 +70,10 @@ public void onInventoryClick(InventoryClickEvent click) { ); - int phase = ContestManager.data.getPhase(); + WeeklyEventPhase phase = WeeklyEventsManager.getCurrentPhase(); - boolean ench0 = phase == 2; - boolean ench1 = phase == 3; + boolean ench0 = phase == ContestPhase.VOTE_CAMP.getPhase(); + boolean ench1 = phase == ContestPhase.TRADE_PHASE.getPhase(); inventory.put(11, new ItemBuilder(this, Material.BLUE_STAINED_GLASS_PANE, itemMeta -> { itemMeta.displayName(Component.text("§r§1Les votes - Vendredi")); diff --git a/src/main/java/fr/openmc/core/features/contest/menu/TradeMenu.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/TradeMenu.java similarity index 96% rename from src/main/java/fr/openmc/core/features/contest/menu/TradeMenu.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/TradeMenu.java index 812c11753..0f567f2fe 100644 --- a/src/main/java/fr/openmc/core/features/contest/menu/TradeMenu.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/TradeMenu.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.contest.menu; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu; import dev.lone.itemsadder.api.CustomStack; import dev.lone.itemsadder.api.FontImages.FontImageWrapper; @@ -6,9 +6,9 @@ import fr.openmc.api.menulib.Menu; import fr.openmc.api.menulib.utils.InventorySize; import fr.openmc.api.menulib.utils.ItemBuilder; -import fr.openmc.core.features.contest.managers.ContestManager; -import fr.openmc.core.features.contest.managers.ContestPlayerManager; -import fr.openmc.core.features.contest.managers.TradeYMLManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestPlayerManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.TradeYMLManager; import fr.openmc.core.features.mailboxes.MailboxManager; import fr.openmc.core.registry.items.CustomItemRegistry; import fr.openmc.core.utils.ItemUtils; diff --git a/src/main/java/fr/openmc/core/features/contest/menu/VoteMenu.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/VoteMenu.java similarity index 97% rename from src/main/java/fr/openmc/core/features/contest/menu/VoteMenu.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/VoteMenu.java index c1c580989..6fd2860ad 100644 --- a/src/main/java/fr/openmc/core/features/contest/menu/VoteMenu.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/menu/VoteMenu.java @@ -1,12 +1,12 @@ -package fr.openmc.core.features.contest.menu; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.menu; import dev.lone.itemsadder.api.FontImages.FontImageWrapper; import fr.openmc.api.menulib.Menu; import fr.openmc.api.menulib.template.ConfirmMenu; import fr.openmc.api.menulib.utils.InventorySize; import fr.openmc.api.menulib.utils.ItemBuilder; -import fr.openmc.core.features.contest.managers.ContestManager; -import fr.openmc.core.features.contest.models.ContestPlayer; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestPlayer; import fr.openmc.core.utils.ColorUtils; import fr.openmc.core.utils.messages.MessageType; import fr.openmc.core.utils.messages.MessagesManager; diff --git a/src/main/java/fr/openmc/core/features/contest/models/Contest.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/models/ContestData.java similarity index 83% rename from src/main/java/fr/openmc/core/features/contest/models/Contest.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/models/ContestData.java index 43fd7b0c7..54419d3c6 100644 --- a/src/main/java/fr/openmc/core/features/contest/models/Contest.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/models/ContestData.java @@ -1,18 +1,17 @@ -package fr.openmc.core.features.contest.models; - -import lombok.Getter; -import lombok.Setter; - -import java.util.Objects; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; +import lombok.Getter; +import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import java.util.Objects; + @Getter @DatabaseTable(tableName = "contests") -public class Contest { +public class ContestData { @DatabaseField(id = true) private int id; // required for Dao.update function @@ -26,29 +25,21 @@ public class Contest { private String color2; @Setter @DatabaseField(canBeNull = false) - private int phase; - @DatabaseField(canBeNull = false) - private String startdate; - @Setter - @DatabaseField(canBeNull = false) private int points1; @Setter @DatabaseField(canBeNull = false) private int points2; - Contest() { + ContestData() { // required for ORMLite } - public Contest(String camp1, String camp2, String color1, String color2, int phase, String startdate, int points1, - int points2) { + public ContestData(String camp1, String camp2, String color1, String color2, int points1, int points2) { this.id = 1; // we will only be storing one row, so we need a constant id this.camp1 = camp1; this.camp2 = camp2; this.color1 = color1; this.color2 = color2; - this.phase = phase; - this.startdate = startdate; this.points1 = points1; this.points2 = points2; } diff --git a/src/main/java/fr/openmc/core/features/contest/models/ContestPlayer.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/models/ContestPlayer.java similarity index 92% rename from src/main/java/fr/openmc/core/features/contest/models/ContestPlayer.java rename to src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/models/ContestPlayer.java index 87649a3ce..6d079ffef 100644 --- a/src/main/java/fr/openmc/core/features/contest/models/ContestPlayer.java +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/contents/contest/models/ContestPlayer.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.contest.models; +package fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEvent.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEvent.java new file mode 100644 index 000000000..7cacae58a --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEvent.java @@ -0,0 +1,35 @@ +package fr.openmc.core.features.events.contents.weeklyevents.models; + +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.models.Event; + +import java.util.List; + +public abstract class WeeklyEvent extends Event { + public abstract List getPhases(); + + /** + * Retourne true si on est temporellement dans une phase active de cet event. + */ + public boolean isActive() { + return WeeklyEventsManager.getCurrentEvent() == this + && WeeklyEventsManager.isEventActive(); + } + + /** + * Retourne la phase active de cet event, ou null si l'event n'est pas actif. + */ + public WeeklyEventPhase getActivePhase() { + if (!isActive()) return null; + return WeeklyEventsManager.getCurrentPhase(); + } + + /** + * Retourne le nombre de semaines d'offset entre la semaine actuelle et la semaine de référence pour cet event. + * Utile pour prédire les événements lointin + * @return Offset + */ + public int getWeekOffset() { + return 0; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEventPhase.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEventPhase.java new file mode 100644 index 000000000..31db7af69 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEventPhase.java @@ -0,0 +1,15 @@ +package fr.openmc.core.features.events.contents.weeklyevents.models; + +import net.kyori.adventure.text.Component; + +import java.time.DayOfWeek; +import java.util.List; + +public abstract class WeeklyEventPhase { + public abstract Component getName(); + public abstract List getDescription(); + public abstract DayOfWeek getStartDay(); + public abstract int getStartHour(); + public abstract int getStartMinutes(); + public abstract Runnable runAction(); +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEventsData.java b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEventsData.java new file mode 100644 index 000000000..c68f3772a --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/weeklyevents/models/WeeklyEventsData.java @@ -0,0 +1,33 @@ +package fr.openmc.core.features.events.contents.weeklyevents.models; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; +import lombok.Getter; +import lombok.Setter; + +@Getter +@DatabaseTable(tableName = "weekly_event_data") +public class WeeklyEventsData { + + @DatabaseField(id = true, columnName = "id") + private int id = 1; + + @Setter + @DatabaseField(columnName = "current_event_index") + private int currentEventIndex; + + @Setter + @DatabaseField(columnName = "current_phase_index") + private int currentPhaseIndex; + + @Setter + @DatabaseField(columnName = "is_active") + private boolean active; + + public WeeklyEventsData() {} + + public WeeklyEventsData(int currentEventIndex, int currentPhaseIndex) { + this.currentEventIndex = currentEventIndex; + this.currentPhaseIndex = currentPhaseIndex; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/managers/CalendarManager.java b/src/main/java/fr/openmc/core/features/events/managers/CalendarManager.java new file mode 100644 index 000000000..ccc0230df --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/managers/CalendarManager.java @@ -0,0 +1,92 @@ +package fr.openmc.core.features.events.managers; + +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEvent; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; +import fr.openmc.core.features.events.models.Event; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.time.DayOfWeek; +import java.util.ArrayList; +import java.util.List; + +public class CalendarManager { + + public static List getUpcomingEvents(int slots) { + List events = new ArrayList<>(List.of( + WeeklyEventsManager.getCurrentEvent() + )); + + //todo: implement DailyEvent in upcoming events + + if (events.getLast() instanceof WeeklyEvent we) { + for (int i = events.size(); i <= slots; i++) { + int weekOffset = i; + + events.add(new WeeklyEvent() { + + @Override + public int getWeekOffset() { + return weekOffset; + } + + @Override + public List getPhases() { + return List.of(new WeeklyEventPhase() { + @Override + public Component getName() { + return Component.text("Inconnu"); + } + + @Override + public List getDescription() { + return List.of(); + } + + @Override + public DayOfWeek getStartDay() { + return we.getPhases().getFirst().getStartDay(); + } + + @Override + public int getStartHour() { + return 0; + } + + @Override + public int getStartMinutes() { + return 0; + } + + @Override + public Runnable runAction() { + return null; + } + }); + } + + @Override + public Component getName() { + return Component.text("Evenement du Weekend", NamedTextColor.YELLOW, TextDecoration.BOLD); + } + + @Override + public List getDescription() { + return List.of(); + } + + @Override + public ItemStack getIcon() { + return ItemStack.of(Material.GOLD_BLOCK); + } + }); + } + } + + return events; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/menus/CalendarMenu.java b/src/main/java/fr/openmc/core/features/events/menus/CalendarMenu.java new file mode 100644 index 000000000..4058c9705 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/menus/CalendarMenu.java @@ -0,0 +1,130 @@ +package fr.openmc.core.features.events.menus; + +import fr.openmc.api.menulib.PaginatedMenu; +import fr.openmc.api.menulib.utils.InventorySize; +import fr.openmc.api.menulib.utils.ItemBuilder; +import fr.openmc.api.menulib.utils.StaticSlots; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEvent; +import fr.openmc.core.features.events.contents.weeklyevents.models.WeeklyEventPhase; +import fr.openmc.core.features.events.managers.CalendarManager; +import fr.openmc.core.features.events.models.Event; +import fr.openmc.core.registry.items.CustomItemRegistry; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.TextStyle; +import java.time.temporal.TemporalAdjusters; +import java.util.*; + +public class CalendarMenu extends PaginatedMenu { + public CalendarMenu(Player owner) { + super(owner); + } + + @Override + public @Nullable Material getBorderMaterial() { + return Material.AIR; + } + + @Override + public @NotNull List getStaticSlots() { + return StaticSlots.getStandardSlots(getInventorySize()); + } + + @Override + public List getItems() { + List items = new ArrayList<>(); + for (Event event : CalendarManager.getUpcomingEvents(14)) { + items.add(new ItemBuilder(this, event.getIcon(), meta -> { + meta.customName(event.getName().decoration(TextDecoration.ITALIC, false)); + meta.lore(getEventLore(event)); + })); + } + return items; + } + + @Override + public List getTakableSlot() { + return List.of(); + } + + @Override + public @NotNull InventorySize getInventorySize() { + return InventorySize.LARGE; + } + + @Override + public int getSizeOfItems() { + return getItems().size(); + } + + @Override + public Map getButtons() { + Map map = new HashMap<>(); + map.put(33, new ItemBuilder(this, Objects.requireNonNull(CustomItemRegistry.getByName("_iainternal:icon_cancel")).getBest(), itemMeta -> itemMeta.displayName(Component.text("§7Fermer"))).setCloseButton()); + return map; + } + + private List getEventLore(Event event) { + List eventLore = new ArrayList<>(event.getDescription()); + + if (event instanceof WeeklyEvent we) { + eventLore.add(Component.empty()); + eventLore.add(Component.text("Phases :", NamedTextColor.GOLD, TextDecoration.BOLD).decoration(TextDecoration.ITALIC, false)); + for (WeeklyEventPhase phase : we.getPhases()) { + LocalDateTime now = LocalDateTime.now(); + + LocalDate nextDate = now.toLocalDate() + .with(TemporalAdjusters.nextOrSame(phase.getStartDay())) + .plusWeeks(we.getWeekOffset()); + + LocalDateTime dateEvent = nextDate.atTime( + phase.getStartHour(), + phase.getStartMinutes() + ); + + String formattedDate = dateEvent.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.FRANCE) + + " " + dateEvent.getDayOfMonth() + " " + + dateEvent.getMonth().getDisplayName(TextStyle.FULL, Locale.FRANCE); + // ex. Vendredi 13 Mars + + eventLore.add(Component.text("- ") + .append(phase.getName()) + .append(Component.text(" le " + formattedDate + " à " + + phase.getStartHour() + "h" + String.format("%02d", phase.getStartMinutes()))) // ex. 0h00 + .color(NamedTextColor.GRAY) + .decoration(TextDecoration.ITALIC, false) + ); + } + } + return eventLore; + } + + @Override + public @NotNull String getName() { + return "Calendrier Evenementiel"; + } + + @Override + public String getTexture() { + return null; + } + + @Override + public void onInventoryClick(InventoryClickEvent e) {} + + @Override + public void onClose(InventoryCloseEvent event) { + //empty + } +} diff --git a/src/main/java/fr/openmc/core/features/events/models/Event.java b/src/main/java/fr/openmc/core/features/events/models/Event.java new file mode 100644 index 000000000..feec507be --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/models/Event.java @@ -0,0 +1,13 @@ +package fr.openmc.core.features.events.models; + +import net.kyori.adventure.text.Component; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public abstract class Event { + public abstract Component getName(); + public abstract List getDescription(); + public abstract ItemStack getIcon(); + +} diff --git a/src/main/java/fr/openmc/core/features/friend/FriendCommand.java b/src/main/java/fr/openmc/core/features/friend/FriendCommand.java index 6256f50c4..903d7683a 100644 --- a/src/main/java/fr/openmc/core/features/friend/FriendCommand.java +++ b/src/main/java/fr/openmc/core/features/friend/FriendCommand.java @@ -164,7 +164,7 @@ public void listCommand( .color(isOnline ? NamedTextColor.GREEN : NamedTextColor.YELLOW) .decoration(TextDecoration.BOLD, isOnline)) .hoverEvent(HoverEvent.showText( - Component.text("§7Vile : §e" + (city != null ? city.getName() : "Aucune") + + Component.text("§7Ville : §e" + (city != null ? city.getName() : "Aucune") + "\n§7Argent : §e" + formattedMoney + "\n§7Statut : " + (isOnline ? "§aEn ligne" : "§cHors ligne") ))) diff --git a/src/main/java/fr/openmc/core/features/homes/HomesManager.java b/src/main/java/fr/openmc/core/features/homes/HomesManager.java index d3fbb5bce..3d0921222 100644 --- a/src/main/java/fr/openmc/core/features/homes/HomesManager.java +++ b/src/main/java/fr/openmc/core/features/homes/HomesManager.java @@ -5,6 +5,7 @@ import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; import fr.openmc.core.CommandsManager; +import fr.openmc.core.OMCPlugin; import fr.openmc.core.features.homes.command.*; import fr.openmc.core.features.homes.models.Home; import fr.openmc.core.features.homes.models.HomeLimit; @@ -21,6 +22,8 @@ @Getter public class HomesManager { + private static boolean isInitialized = false; + public static final List homes = new ArrayList<>(); public static final List homeLimits = new ArrayList<>(); @@ -39,6 +42,8 @@ public static void init() { loadHomeLimit(); loadHomes(); + + isInitialized = true; } public static void saveHomesData() { @@ -130,6 +135,10 @@ private static void loadHomeLimit() { } private static void saveHomeLimit() { + if (!isInitialized) { + OMCPlugin.getInstance().getSLF4JLogger().warn("Tentative de sauvegarde des HomeLimits avant l'initialisation du manager, opération ignorée."); + return; + } try { TableUtils.clearTable(DatabaseManager.getConnectionSource(), HomeLimit.class); limitsDao.create(homeLimits); @@ -147,6 +156,10 @@ private static void loadHomes() { } private static void saveHomes() { + if (!isInitialized) { + OMCPlugin.getInstance().getSLF4JLogger().warn("Tentative de sauvegarde des Homes avant l'initialisation du manager, opération ignorée."); + return; + } try { TableUtils.clearTable(DatabaseManager.getConnectionSource(), Home.class); for (Home home : homes) { diff --git a/src/main/java/fr/openmc/core/features/leaderboards/LeaderboardManager.java b/src/main/java/fr/openmc/core/features/leaderboards/LeaderboardManager.java index fa688a272..6ac6e32fc 100644 --- a/src/main/java/fr/openmc/core/features/leaderboards/LeaderboardManager.java +++ b/src/main/java/fr/openmc/core/features/leaderboards/LeaderboardManager.java @@ -7,8 +7,8 @@ import fr.openmc.core.features.economy.BankManager; import fr.openmc.core.features.economy.EconomyManager; import fr.openmc.core.features.economy.models.EconomyPlayer; -import fr.openmc.core.features.events.halloween.managers.HalloweenManager; -import fr.openmc.core.features.events.halloween.models.HalloweenData; +import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.models.HalloweenData; import fr.openmc.core.features.leaderboards.commands.LeaderboardCommands; import fr.openmc.core.utils.DateUtils; import fr.openmc.core.utils.entities.TextDisplay; diff --git a/src/main/java/fr/openmc/core/features/mainmenu/menus/Page1.java b/src/main/java/fr/openmc/core/features/mainmenu/menus/Page1.java index d81ce2858..1b7e92aa8 100644 --- a/src/main/java/fr/openmc/core/features/mainmenu/menus/Page1.java +++ b/src/main/java/fr/openmc/core/features/mainmenu/menus/Page1.java @@ -13,10 +13,13 @@ import fr.openmc.core.features.city.City; import fr.openmc.core.features.city.CityManager; import fr.openmc.core.features.city.commands.CityCommands; -import fr.openmc.core.features.contest.commands.ContestCommand; -import fr.openmc.core.features.contest.managers.ContestManager; -import fr.openmc.core.features.contest.models.Contest; import fr.openmc.core.features.dream.DreamUtils; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestPhase; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.commands.ContestCommand; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.models.ContestData; import fr.openmc.core.features.homes.command.TpHomeCommand; import fr.openmc.core.features.mailboxes.MailboxCommand; import fr.openmc.core.features.mainmenu.listeners.PacketListener; @@ -107,9 +110,9 @@ public Page1(Player player) { MILESTONES_SLOTS.forEach(slot -> content.put(slot, milestonesItem)); ItemStack contestItem = new ItemStack(Material.PAPER); - Contest data = ContestManager.data; - int phase = data.getPhase(); - if (phase != 1) { + ContestData data = ContestManager.data; + + if (WeeklyEventsManager.getCurrentEvent() instanceof Contest && WeeklyEventsManager.getCurrentPhase() != ContestPhase.END_PHASE.getPhase()) { contestItem.editMeta(meta -> { meta.setItemModel(NamespacedKey.minecraft("air")); meta.itemName(data.getCampVSComponent()); diff --git a/src/main/java/fr/openmc/core/features/mainmenu/menus/Page2.java b/src/main/java/fr/openmc/core/features/mainmenu/menus/Page2.java index a07177541..6651adecd 100644 --- a/src/main/java/fr/openmc/core/features/mainmenu/menus/Page2.java +++ b/src/main/java/fr/openmc/core/features/mainmenu/menus/Page2.java @@ -102,7 +102,7 @@ public Page2() { leaderboardItem.editMeta(meta -> { meta.setItemModel(NamespacedKey.minecraft("air")); meta.itemName(Component.text("Leaderboard", NamedTextColor.YELLOW)); - meta.lore(List.of(Component.text("/leaderboard", NamedTextColor.DARK_GRAY), Component.text("En cours de développement", NamedTextColor.RED))); + meta.lore(List.of(Component.text("/leaderboard", NamedTextColor.DARK_GRAY))); }); LEADERBOARD_SLOTS.forEach(slot -> content.put(slot, leaderboardItem)); diff --git a/src/main/java/fr/openmc/core/features/milestones/MilestonesManager.java b/src/main/java/fr/openmc/core/features/milestones/MilestonesManager.java index 61f0c0ecc..fe9bc5672 100644 --- a/src/main/java/fr/openmc/core/features/milestones/MilestonesManager.java +++ b/src/main/java/fr/openmc/core/features/milestones/MilestonesManager.java @@ -86,7 +86,13 @@ public static void loadMilestonesProgress() { for (Milestone milestone : milestones) { // Pour tous les joueurs du milestone, la progression est chargée à l'étape actuelle for (Map.Entry playerData : milestone.getPlayerData().entrySet()) { - milestone.getSteps().get(playerData.getValue().getStep()).setProgress(playerData.getKey(), playerData.getValue().getProgress()); + int step = playerData.getValue().getStep(); + + if (step >= milestone.getSteps().size()) continue; + + milestone.getSteps() + .get(step) + .setProgress(playerData.getKey(), playerData.getValue().getProgress()); } } } diff --git a/src/main/java/fr/openmc/core/features/milestones/tutorial/quests/HomeUpgradeQuest.java b/src/main/java/fr/openmc/core/features/milestones/tutorial/quests/HomeUpgradeQuest.java index d6dbf1740..f0c0bbe20 100644 --- a/src/main/java/fr/openmc/core/features/milestones/tutorial/quests/HomeUpgradeQuest.java +++ b/src/main/java/fr/openmc/core/features/milestones/tutorial/quests/HomeUpgradeQuest.java @@ -23,7 +23,8 @@ public HomeUpgradeQuest() { super( "Améliorer votre limite de homes", List.of( - "§fTapez §d/upgradehome §fou bien aller dans le §dmenu des homes (/homes) §fpour pouvoir améliorer votre limite de homes", + "§fTapez §d/upgradehome §fou bien aller dans le §dmenu des homes (/homes)", + "§fpour pouvoir améliorer votre limite de homes", "§8§oCela vous permettra d'avoir plus de homes !" ), CustomItemRegistry.getByName("omc_homes:omc_homes_icon_upgrade").getBest(), diff --git a/src/main/java/fr/openmc/core/features/quests/menus/QuestsMenu.java b/src/main/java/fr/openmc/core/features/quests/menus/QuestsMenu.java index ab5e90d70..d56a576c7 100644 --- a/src/main/java/fr/openmc/core/features/quests/menus/QuestsMenu.java +++ b/src/main/java/fr/openmc/core/features/quests/menus/QuestsMenu.java @@ -260,6 +260,7 @@ else if (isCompleted) quest.getDescription(playerUUID).forEach(string -> { lore.add(Component.text(" §f" + string)); }); + lore.addAll(quest.getAdditionalLore()); if (currentTier.getSteps() != null && !currentTier.getSteps().isEmpty()) { lore.add(Component.empty()); lore.add(Component.text("§6◆ §eAvancement:")); diff --git a/src/main/java/fr/openmc/core/features/quests/objects/Quest.java b/src/main/java/fr/openmc/core/features/quests/objects/Quest.java index 9d6861400..e38f616cf 100644 --- a/src/main/java/fr/openmc/core/features/quests/objects/Quest.java +++ b/src/main/java/fr/openmc/core/features/quests/objects/Quest.java @@ -10,6 +10,7 @@ import fr.openmc.core.utils.messages.MessagesManager; import fr.openmc.core.utils.messages.Prefix; import lombok.Getter; +import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; @@ -31,6 +32,8 @@ public class Quest { private final String name; private final List baseDescription; + @Setter + private List additionalLore; private final ItemStack icon; private final boolean isLargeActionBar; private final List tiers = new ArrayList<>(); @@ -50,6 +53,7 @@ public class Quest { public Quest(String name, List baseDescription, ItemStack icon) { this.name = name; this.baseDescription = baseDescription; + this.additionalLore = List.of(); this.icon = icon; this.isLargeActionBar = false; } @@ -64,6 +68,7 @@ public Quest(String name, List baseDescription, ItemStack icon) { public Quest(String name, List baseDescription, Material icon) { this.name = name; this.baseDescription = baseDescription; + this.additionalLore = List.of(); this.icon = new ItemStack(icon); this.isLargeActionBar = false; } @@ -79,6 +84,7 @@ public Quest(String name, List baseDescription, Material icon) { public Quest(String name, List baseDescription, ItemStack icon, boolean isLargeActionBar) { this.name = name; this.baseDescription = baseDescription; + this.additionalLore = List.of(); this.icon = icon; this.isLargeActionBar = isLargeActionBar; } @@ -94,6 +100,7 @@ public Quest(String name, List baseDescription, ItemStack icon, boolean public Quest(String name, List baseDescription, Material icon, boolean isLargeActionBar) { this.name = name; this.baseDescription = baseDescription; + this.additionalLore = List.of(); this.icon = new ItemStack(icon); this.isLargeActionBar = isLargeActionBar; } diff --git a/src/main/java/fr/openmc/core/features/quests/quests/BreakLogQuest.java b/src/main/java/fr/openmc/core/features/quests/quests/BreakLogQuest.java index ac6106c0a..1334b7854 100644 --- a/src/main/java/fr/openmc/core/features/quests/quests/BreakLogQuest.java +++ b/src/main/java/fr/openmc/core/features/quests/quests/BreakLogQuest.java @@ -4,6 +4,10 @@ import fr.openmc.core.features.quests.objects.QuestTier; import fr.openmc.core.features.quests.rewards.QuestItemReward; import fr.openmc.core.features.quests.rewards.QuestMoneyReward; +import fr.openmc.core.utils.ItemUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.enchantments.Enchantment; @@ -13,33 +17,49 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; public class BreakLogQuest extends Quest implements Listener { - private final Map logWeights = new EnumMap<>(Material.class); - - /** - * Crée une hâche enchantée pour les récompenses. - * @param material Le matériau de la hache (ex: Material.IRON_AXE) - * @return ItemStack enchanté - */ - private ItemStack getEnchantedAxe(Material material) { - ItemStack axe = ItemStack.of(material); - axe.editMeta(meta -> { - meta.addEnchant(Enchantment.EFFICIENCY, 3, true); // Efficacité III - meta.addEnchant(Enchantment.UNBREAKING, 2, true); // Solidité II - }); - return axe; + private final static Map logWeights = new EnumMap<>(Material.class); + static { + // Définir les poids (progression plus rapide pour les bûches rares) + logWeights.put(Material.OAK_LOG, 1); + logWeights.put(Material.BIRCH_LOG, 1); + logWeights.put(Material.SPRUCE_LOG, 1); + logWeights.put(Material.ACACIA_LOG, 2); + logWeights.put(Material.DARK_OAK_LOG, 3); + logWeights.put(Material.JUNGLE_LOG, 3); + logWeights.put(Material.MANGROVE_LOG, 4); + logWeights.put(Material.CHERRY_LOG, 4); + logWeights.put(Material.CRIMSON_STEM, 5); + logWeights.put(Material.WARPED_STEM, 5); } + private static List getLore() { + List lore = new ArrayList<>(); + + for (var entry : logWeights.entrySet()) { + lore.add(Component.text("- ").color(NamedTextColor.DARK_GRAY) + .append(ItemUtils.getItemTranslation(entry.getKey())) + .append(Component.text(" : ")) + .append(Component.text(entry.getValue()).color(NamedTextColor.AQUA)) + .append(Component.text(" points")) + .decoration(TextDecoration.ITALIC, false) + .color(NamedTextColor.GRAY) + ); + } + + return lore; + } public BreakLogQuest() { super("Bûcheron de l'extrême", List.of("Casser {target} bûches"), new ItemStack(Material.IRON_AXE)); - + this.setAdditionalLore(getLore()); this.addTiers( new QuestTier(500, new QuestMoneyReward(300), new QuestItemReward(Material.IRON_AXE, 1)), new QuestTier(1500, new QuestMoneyReward(500), new QuestItemReward(getEnchantedAxe(Material.IRON_AXE), 1)), @@ -47,19 +67,6 @@ public BreakLogQuest() { new QuestTier(15000, new QuestMoneyReward(3000), new QuestItemReward(getEnchantedAxe(Material.DIAMOND_AXE), 1)) ); - - - // Définir les poids (progression plus rapide pour les bûches rares) - logWeights.put(Material.OAK_LOG, 1); - logWeights.put(Material.BIRCH_LOG, 1); - logWeights.put(Material.SPRUCE_LOG, 1); - logWeights.put(Material.ACACIA_LOG, 2); - logWeights.put(Material.DARK_OAK_LOG, 3); - logWeights.put(Material.JUNGLE_LOG, 3); - logWeights.put(Material.MANGROVE_LOG, 4); - logWeights.put(Material.CHERRY_LOG, 4); - logWeights.put(Material.CRIMSON_STEM, 5); - logWeights.put(Material.WARPED_STEM, 5); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -72,4 +79,18 @@ public void onPlayerBreak(BlockBreakEvent event) { this.incrementProgress(event.getPlayer().getUniqueId(), progress); } } + + /** + * Crée une hâche enchantée pour les récompenses. + * @param material Le matériau de la hache (ex: Material.IRON_AXE) + * @return ItemStack enchanté + */ + private ItemStack getEnchantedAxe(Material material) { + ItemStack axe = ItemStack.of(material); + axe.editMeta(meta -> { + meta.addEnchant(Enchantment.EFFICIENCY, 3, true); // Efficacité III + meta.addEnchant(Enchantment.UNBREAKING, 2, true); // Solidité II + }); + return axe; + } } diff --git a/src/main/java/fr/openmc/core/features/quests/quests/WinContestQuest.java b/src/main/java/fr/openmc/core/features/quests/quests/WinContestQuest.java index 484b5af98..01939956e 100644 --- a/src/main/java/fr/openmc/core/features/quests/quests/WinContestQuest.java +++ b/src/main/java/fr/openmc/core/features/quests/quests/WinContestQuest.java @@ -1,6 +1,6 @@ package fr.openmc.core.features.quests.quests; -import fr.openmc.core.features.contest.ContestEndEvent; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.events.ContestEndEvent; import fr.openmc.core.features.quests.objects.Quest; import fr.openmc.core.features.quests.objects.QuestTier; import fr.openmc.core.features.quests.rewards.QuestItemReward; diff --git a/src/main/java/fr/openmc/core/features/settings/SettingType.java b/src/main/java/fr/openmc/core/features/settings/SettingType.java index cc24a9530..fdad9ccc6 100644 --- a/src/main/java/fr/openmc/core/features/settings/SettingType.java +++ b/src/main/java/fr/openmc/core/features/settings/SettingType.java @@ -32,7 +32,9 @@ public enum SettingType { Material.NOTE_BLOCK, Material.GRAY_DYE, "Activer ou désactiver les sons des messages privés"), TELEPORT_TITLE_FADE(ValueType.BOOLEAN, true, "Fondu du titre lors des téléportations", Material.ENDER_PEARL, Material.GRAY_DYE, "Activer ou désactiver le fondu du titre lors des téléportations"), - + JOIN_ANIMATION(ValueType.BOOLEAN, true, "Animation de connexion", + Material.GLOW_INK_SAC, Material.INK_SAC, "Activer ou désactiver l'animation de connexion"), + ; private final ValueType valueType; diff --git a/src/main/java/fr/openmc/core/listeners/AsyncChatListener.java b/src/main/java/fr/openmc/core/listeners/AsyncChatListener.java index db9b57aca..655dbe4c3 100644 --- a/src/main/java/fr/openmc/core/listeners/AsyncChatListener.java +++ b/src/main/java/fr/openmc/core/listeners/AsyncChatListener.java @@ -1,11 +1,12 @@ package fr.openmc.core.listeners; -import fr.openmc.core.OMCPlugin; import fr.openmc.api.hooks.LuckPermsHook; +import fr.openmc.core.OMCPlugin; import io.papermc.paper.event.player.AsyncChatEvent; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.luckperms.api.LuckPerms; import net.luckperms.api.cacheddata.CachedMetaData; import org.bukkit.Bukkit; @@ -33,16 +34,17 @@ public void onChat(AsyncChatEvent event) { final Player player = event.getPlayer(); final CachedMetaData metaData = this.luckperms.getPlayerAdapter(Player.class).getMetaData(player); - String message = ((TextComponent) event.message()).content(); + String message = PlainTextComponentSerializer.plainText().serialize(event.message()); String rawMessage = plugin.getConfig().getString("chat.message", "{prefix}{name}§7: {message}") .replace("{prefix}", LuckPermsHook.getFormattedPAPIPrefix(player)) .replace("{suffix}", metaData.getSuffix() != null ? metaData.getSuffix() : "") .replace("{name}", player.getName()) .replace("{message}", message); - final String formattedMessage = colorize(translateHexColorCodes(rawMessage)); + final Component formattedMessage = LegacyComponentSerializer.legacySection() + .deserialize(colorize(translateHexColorCodes(rawMessage))); - event.renderer((source, sourceDisplayName, component, viewer) -> Component.text(formattedMessage)); + event.renderer((source, sourceDisplayName, component, viewer) -> formattedMessage); for (Player p : Bukkit.getOnlinePlayers()) { if (message.contains(p.getName())) diff --git a/src/main/java/fr/openmc/core/listeners/EquipableItemListener.java b/src/main/java/fr/openmc/core/listeners/EquipableItemListener.java index 913c8ae96..9b67bcb06 100644 --- a/src/main/java/fr/openmc/core/listeners/EquipableItemListener.java +++ b/src/main/java/fr/openmc/core/listeners/EquipableItemListener.java @@ -3,12 +3,9 @@ import fr.openmc.core.OMCPlugin; import fr.openmc.core.events.ArmorEquipEvent; import fr.openmc.core.features.dream.DreamUtils; -import fr.openmc.core.features.dream.registries.DreamItemRegistry; import fr.openmc.core.registry.items.CustomItem; import fr.openmc.core.registry.items.CustomItemRegistry; import fr.openmc.core.registry.items.options.EquipableItem; -import fr.openmc.core.utils.ArmorType; -import fr.openmc.core.utils.ItemUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -20,11 +17,12 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public class EquipableItemListener implements Listener { + private final Map> appliedEffectsByPlayer = new HashMap<>(); + @EventHandler public void onEquip(ArmorEquipEvent event) { if (event.getType() == null) return; @@ -57,6 +55,8 @@ public void onQuit(PlayerQuitEvent event) { equipable.removeEffects(player); } } + + appliedEffectsByPlayer.remove(player.getUniqueId()); } @EventHandler @@ -76,6 +76,15 @@ public void onDreamTeleport(PlayerTeleportEvent event) { } private void recalc(Player player, ItemStack oldPiece) { + UUID playerId = player.getUniqueId(); + + Set previouslyApplied = appliedEffectsByPlayer.get(playerId); + if (previouslyApplied != null) { + for (PotionEffectType type : previouslyApplied) { + player.removePotionEffect(type); + } + } + if (oldPiece != null && !oldPiece.getType().isAir()) { CustomItem customItem = CustomItemRegistry.getByItemStack(oldPiece); @@ -101,6 +110,8 @@ private void recalc(Player player, ItemStack oldPiece) { } } + Set newApplied = new HashSet<>(); + for (var entry : effects.entrySet()) { PotionEffectType type = entry.getKey(); int amplifier = entry.getValue(); @@ -115,6 +126,10 @@ private void recalc(Player player, ItemStack oldPiece) { true ) ); + + newApplied.add(type); } + + appliedEffectsByPlayer.put(playerId, newApplied); } } \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/items/contents/Hammer.java b/src/main/java/fr/openmc/core/registry/items/contents/Hammer.java index ee8fd486f..33bf35dbc 100644 --- a/src/main/java/fr/openmc/core/registry/items/contents/Hammer.java +++ b/src/main/java/fr/openmc/core/registry/items/contents/Hammer.java @@ -36,9 +36,12 @@ private static BlockFace getTargetFace(Player player) { private static Vector rotateOffset(int x, int y, int z, BlockFace face) { return switch (face) { - case NORTH, SOUTH -> new Vector(x, y, z); - case EAST, WEST -> new Vector(z, y, x); - case UP, DOWN -> new Vector(x, z, y); + case SOUTH -> new Vector(x, y, z); + case NORTH -> new Vector(x, y, -z); + case EAST -> new Vector(z, y, x); + case WEST -> new Vector(-z, y, x); + case UP -> new Vector(x, z, y); + case DOWN -> new Vector(x, -z, y); default -> new Vector(0, 0, 0); }; } @@ -51,7 +54,7 @@ private void breakArea(Player player, Block origin, BlockFace face, ItemStack to for (int dx = -radius; dx <= radius; dx++) { for (int dy = -radius; dy <= radius; dy++) { - for (int dz = -depth; dz <= depth; dz++) { + for (int dz = 0; dz <= depth; dz++) { if (dx == 0 && dy == 0 && dz == 0) continue; diff --git a/src/main/java/fr/openmc/core/utils/DateUtils.java b/src/main/java/fr/openmc/core/utils/DateUtils.java index 608245495..3fd482348 100644 --- a/src/main/java/fr/openmc/core/utils/DateUtils.java +++ b/src/main/java/fr/openmc/core/utils/DateUtils.java @@ -173,10 +173,10 @@ public static String getTimeUntilNextDay(DayOfWeek day) { return String.format("%dd %dh %dm", days, hours, minutes); } - public static long getSecondsUntilDayOfWeekMidnight(DayOfWeek dayOfWeek) { + public static long getSecondsUntilDayOfWeekTime(DayOfWeek dayOfWeek, int hour, int minute, int second) { LocalDateTime now = LocalDateTime.now(); LocalDateTime nextDayOfWeekMidnight = now.with(TemporalAdjusters.nextOrSame(dayOfWeek)) - .withHour(0).withMinute(0).withSecond(0).withNano(0); + .withHour(hour).withMinute(minute).withSecond(second).withNano(0); if (!now.isBefore(nextDayOfWeekMidnight)) { nextDayOfWeekMidnight = nextDayOfWeekMidnight.plusWeeks(1); diff --git a/src/main/java/fr/openmc/core/utils/ParticleUtils.java b/src/main/java/fr/openmc/core/utils/ParticleUtils.java index c9bd808e8..b498b6575 100644 --- a/src/main/java/fr/openmc/core/utils/ParticleUtils.java +++ b/src/main/java/fr/openmc/core/utils/ParticleUtils.java @@ -6,7 +6,10 @@ import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import fr.openmc.core.OMCPlugin; -import fr.openmc.core.features.contest.managers.ContestManager; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.Contest; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestPhase; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; import net.kyori.adventure.text.format.NamedTextColor; import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; @@ -54,7 +57,8 @@ public static void spawnParticlesInRegion(String regionId, World world, Particle new BukkitRunnable() { @Override public void run() { - if (ContestManager.data.getPhase() == 3) return; + if (!(WeeklyEventsManager.getCurrentEvent() instanceof Contest)) return; + if (WeeklyEventsManager.getCurrentPhase() == ContestPhase.END_PHASE.getPhase()) return; for (int i = 0; i < amountPer2Tick; i++) { double x = RandomUtils.randomBetween(minLocation.getX(), maxLocation.getX()); @@ -123,7 +127,8 @@ public static void spawnContestParticlesInRegion(String regionId, World world, i new BukkitRunnable() { @Override public void run() { - if (ContestManager.data.getPhase() != 3) return; + if (!(WeeklyEventsManager.getCurrentEvent() instanceof Contest)) return; + if (WeeklyEventsManager.getCurrentPhase() == ContestPhase.END_PHASE.getPhase()) return; if (color1 == null || color2 == null) { String camp1Color = ContestManager.data.getColor1(); diff --git a/src/main/java/fr/openmc/core/utils/database/DatabaseManager.java b/src/main/java/fr/openmc/core/utils/database/DatabaseManager.java index 4fdacadc6..6b3309d6b 100644 --- a/src/main/java/fr/openmc/core/utils/database/DatabaseManager.java +++ b/src/main/java/fr/openmc/core/utils/database/DatabaseManager.java @@ -12,12 +12,13 @@ import fr.openmc.core.features.city.sub.rank.CityRankManager; import fr.openmc.core.features.city.sub.statistics.CityStatisticsManager; import fr.openmc.core.features.city.sub.war.WarManager; -import fr.openmc.core.features.contest.managers.ContestManager; import fr.openmc.core.features.dream.DreamManager; import fr.openmc.core.features.economy.BankManager; import fr.openmc.core.features.economy.EconomyManager; import fr.openmc.core.features.economy.TransactionsManager; -import fr.openmc.core.features.events.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; +import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; import fr.openmc.core.features.friend.FriendSQLManager; import fr.openmc.core.features.homes.HomesManager; import fr.openmc.core.features.mailboxes.MailboxManager; @@ -63,6 +64,7 @@ public static void init() { AnalyticsManager.initDB(connectionSource); MailboxManager.initDB(connectionSource); ContestManager.initDB(connectionSource); + WeeklyEventsManager.initDB(connectionSource); EconomyManager.initDB(connectionSource); HomesManager.initDB(connectionSource); FriendSQLManager.initDB(connectionSource); diff --git a/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java b/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java new file mode 100644 index 000000000..c0443cd71 --- /dev/null +++ b/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java @@ -0,0 +1,66 @@ +package fr.openmc.core.features.economy; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Locale; + +class EconomyFormattingTest { + + @BeforeAll + static void setUpLocale() { + Locale.setDefault(Locale.US); + } + + @Test + @DisplayName("Format zero balance") + void testFormat_Zero() { + Assertions.assertEquals("0", EconomyManager.getFormattedSimplifiedNumber(0)); + } + + @Test + @DisplayName("Format small number without suffix") + void testFormat_SmallNumber() { + Assertions.assertEquals("500", EconomyManager.getFormattedSimplifiedNumber(500)); + } + + @Test + @DisplayName("Format thousands with k suffix") + void testFormat_Thousands() { + String result = EconomyManager.getFormattedSimplifiedNumber(1500); + Assertions.assertEquals("1.5k", result); + } + + @Test + @DisplayName("Format exact thousand") + void testFormat_ExactThousand() { + Assertions.assertEquals("1k", EconomyManager.getFormattedSimplifiedNumber(1000)); + } + + @Test + @DisplayName("Format millions with M suffix") + void testFormat_Millions() { + Assertions.assertEquals("3M", EconomyManager.getFormattedSimplifiedNumber(3_000_000)); + } + + @Test + @DisplayName("Format billions with B suffix") + void testFormat_Billions() { + Assertions.assertEquals("1B", EconomyManager.getFormattedSimplifiedNumber(1_000_000_000)); + } + + @Test + @DisplayName("Format with decimal truncation") + void testFormat_Decimal() { + String result = EconomyManager.getFormattedSimplifiedNumber(2_500_000); + Assertions.assertEquals("2.5M", result); + } + + @Test + @DisplayName("Format number under 1000") + void testFormat_Under1000() { + Assertions.assertEquals("999", EconomyManager.getFormattedSimplifiedNumber(999)); + } +} diff --git a/src/test/java/fr/openmc/core/features/economy/EconomyManagerTest.java b/src/test/java/fr/openmc/core/features/economy/EconomyManagerTest.java index 5675f2650..a9ddcfb13 100644 --- a/src/test/java/fr/openmc/core/features/economy/EconomyManagerTest.java +++ b/src/test/java/fr/openmc/core/features/economy/EconomyManagerTest.java @@ -75,7 +75,7 @@ public void testSetBalance() { @Test public void testAddBalanceWithReasonRegistersTransaction() { EconomyManager.addBalance(player1.getUniqueId(), 100.0, "Test Reason"); - server.getScheduler().performTicks(20L); + server.getScheduler().waitAsyncTasksFinished(); List transactions = TransactionsManager.getTransactionsByPlayers(player1.getUniqueId()); boolean found = transactions.stream().anyMatch(t -> @@ -91,11 +91,11 @@ public void testAddBalanceWithReasonRegistersTransaction() { public void testWithdrawBalanceWithReasonRegistersTransaction() { EconomyManager.setBalance(player1.getUniqueId(), 200.0); EconomyManager.withdrawBalance(player1.getUniqueId(), 50.0, "Withdrawal Reason"); - server.getScheduler().performTicks(20L); + server.getScheduler().waitAsyncTasksFinished(); List transactions = TransactionsManager.getTransactionsByPlayers(player1.getUniqueId()); - boolean found = transactions.stream().anyMatch(t -> + boolean found = transactions.stream().anyMatch(t -> t.sender.equals(player1.getUniqueId().toString()) && t.amount == 50.0 && t.reason.equals("Withdrawal Reason") @@ -108,10 +108,10 @@ public void testWithdrawBalanceWithReasonRegistersTransaction() { public void testWithdrawBalanceWithoutReasonDoesNotRegisterTransaction() { EconomyManager.setBalance(player1.getUniqueId(), 200.0); EconomyManager.withdrawBalance(player1.getUniqueId(), 50.0); - server.getScheduler().performTicks(20L); + server.getScheduler().waitAsyncTasksFinished(); List transactions = TransactionsManager.getTransactionsByPlayers(player1.getUniqueId()); - boolean found = transactions.stream().anyMatch(t -> + boolean found = transactions.stream().anyMatch(t -> t.sender.equals(player1.getUniqueId().toString()) && t.amount == 50.0 ); @@ -145,7 +145,7 @@ public void testFailedTransferBalanceDueToInsufficientFunds() { public void testTransferBalanceWithReasonRegistersTransaction() { EconomyManager.setBalance(player1.getUniqueId(), 400.0); EconomyManager.transferBalance(player1.getUniqueId(), player2.getUniqueId(), 150.0, "Gift"); - server.getScheduler().performTicks(120L); + server.getScheduler().waitAsyncTasksFinished(); List transactions = TransactionsManager.getTransactionsByPlayers(player1.getUniqueId()); diff --git a/src/test/java/fr/openmc/core/utils/ColorUtilsTest.java b/src/test/java/fr/openmc/core/utils/ColorUtilsTest.java new file mode 100644 index 000000000..da0958d96 --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/ColorUtilsTest.java @@ -0,0 +1,78 @@ +package fr.openmc.core.utils; + +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ColorUtilsTest { + + @Test + @DisplayName("getNamedTextColor valid color") + void testGetNamedTextColor_Valid() { + Assertions.assertEquals(NamedTextColor.RED, ColorUtils.getNamedTextColor("red")); + Assertions.assertEquals(NamedTextColor.BLUE, ColorUtils.getNamedTextColor("blue")); + Assertions.assertEquals(NamedTextColor.GREEN, ColorUtils.getNamedTextColor("green")); + } + + @Test + @DisplayName("getNamedTextColor null returns WHITE") + void testGetNamedTextColor_Null() { + Assertions.assertEquals(NamedTextColor.WHITE, ColorUtils.getNamedTextColor(null)); + } + + @Test + @DisplayName("getNamedTextColor invalid returns WHITE") + void testGetNamedTextColor_Invalid() { + Assertions.assertEquals(NamedTextColor.WHITE, ColorUtils.getNamedTextColor("not_a_color")); + } + + @Test + @DisplayName("getColorCode returns correct codes") + void testGetColorCode() { + Assertions.assertEquals("§c", ColorUtils.getColorCode(NamedTextColor.RED)); + Assertions.assertEquals("§a", ColorUtils.getColorCode(NamedTextColor.GREEN)); + Assertions.assertEquals("§9", ColorUtils.getColorCode(NamedTextColor.BLUE)); + Assertions.assertEquals("§f", ColorUtils.getColorCode(NamedTextColor.WHITE)); + Assertions.assertEquals("§0", ColorUtils.getColorCode(NamedTextColor.BLACK)); + } + + @Test + @DisplayName("getNameFromColor returns French names") + void testGetNameFromColor() { + Assertions.assertEquals("§cRouge", ColorUtils.getNameFromColor(NamedTextColor.RED)); + Assertions.assertEquals("§fBlanc", ColorUtils.getNameFromColor(NamedTextColor.WHITE)); + Assertions.assertEquals("§6Orange", ColorUtils.getNameFromColor(NamedTextColor.GOLD)); + } + + @Test + @DisplayName("getMaterialFromColor returns correct wool") + void testGetMaterialFromColor() { + Assertions.assertEquals(Material.RED_WOOL, ColorUtils.getMaterialFromColor(NamedTextColor.RED)); + Assertions.assertEquals(Material.WHITE_WOOL, ColorUtils.getMaterialFromColor(NamedTextColor.WHITE)); + Assertions.assertEquals(Material.BLACK_WOOL, ColorUtils.getMaterialFromColor(NamedTextColor.BLACK)); + } + + @Test + @DisplayName("getReadableColor maps correctly") + void testGetReadableColor() { + Assertions.assertEquals(NamedTextColor.DARK_GRAY, ColorUtils.getReadableColor(NamedTextColor.BLACK)); + Assertions.assertEquals(NamedTextColor.GRAY, ColorUtils.getReadableColor(NamedTextColor.WHITE)); + Assertions.assertEquals(NamedTextColor.GOLD, ColorUtils.getReadableColor(NamedTextColor.YELLOW)); + Assertions.assertEquals(NamedTextColor.RED, ColorUtils.getReadableColor(NamedTextColor.RED)); + } + + @Test + @DisplayName("getRGBFromNamedTextColor returns correct RGB") + void testGetRGBFromNamedTextColor() { + int[] red = ColorUtils.getRGBFromNamedTextColor(NamedTextColor.RED); + Assertions.assertArrayEquals(new int[]{255, 85, 85}, red); + + int[] black = ColorUtils.getRGBFromNamedTextColor(NamedTextColor.BLACK); + Assertions.assertArrayEquals(new int[]{0, 0, 0}, black); + + int[] white = ColorUtils.getRGBFromNamedTextColor(NamedTextColor.WHITE); + Assertions.assertArrayEquals(new int[]{255, 255, 255}, white); + } +} diff --git a/src/test/java/fr/openmc/core/utils/DateUtilsTest.java b/src/test/java/fr/openmc/core/utils/DateUtilsTest.java index 3d3ef954a..865ea00a4 100644 --- a/src/test/java/fr/openmc/core/utils/DateUtilsTest.java +++ b/src/test/java/fr/openmc/core/utils/DateUtilsTest.java @@ -9,10 +9,84 @@ class DateUtilsTest { @Test @DisplayName("Time to Ticks") void testConvertTime() { - Assertions.assertEquals( - "20m", - DateUtils.convertTime(24000) - ); + Assertions.assertEquals("20m", DateUtils.convertTime(24000)); } -} \ No newline at end of file + @Test + @DisplayName("Convert 0 seconds") + void testConvertSecondToTime_Zero() { + Assertions.assertEquals("0s", DateUtils.convertSecondToTime(0)); + } + + @Test + @DisplayName("Convert seconds only") + void testConvertSecondToTime_SecondsOnly() { + Assertions.assertEquals("45s", DateUtils.convertSecondToTime(45)); + } + + @Test + @DisplayName("Convert minutes and seconds") + void testConvertSecondToTime_MinutesAndSeconds() { + Assertions.assertEquals("2m 30s", DateUtils.convertSecondToTime(150)); + } + + @Test + @DisplayName("Convert hours, minutes and seconds") + void testConvertSecondToTime_HoursMinutesSeconds() { + Assertions.assertEquals("1h 5m 10s", DateUtils.convertSecondToTime(3910)); + } + + @Test + @DisplayName("Convert days, hours, minutes and seconds") + void testConvertSecondToTime_Full() { + Assertions.assertEquals("3j 4h 2m 38s", DateUtils.convertSecondToTime(273758)); + } + + @Test + @DisplayName("Convert exact hours") + void testConvertSecondToTime_ExactHours() { + Assertions.assertEquals("2h", DateUtils.convertSecondToTime(7200)); + } + + @Test + @DisplayName("Convert millis to time") + void testConvertMillisToTime() { + Assertions.assertEquals("1m 30s", DateUtils.convertMillisToTime(90000)); + } + + @Test + @DisplayName("Convert 0 millis") + void testConvertMillisToTime_Zero() { + Assertions.assertEquals("0s", DateUtils.convertMillisToTime(0)); + } + + @Test + @DisplayName("isBefore - same year, earlier week") + void testIsBefore_SameYearEarlierWeek() { + Assertions.assertTrue(DateUtils.isBefore("2025-10", "2025-20")); + } + + @Test + @DisplayName("isBefore - same year, same week") + void testIsBefore_SameWeek() { + Assertions.assertTrue(DateUtils.isBefore("2025-10", "2025-10")); + } + + @Test + @DisplayName("isBefore - same year, later week") + void testIsBefore_SameYearLaterWeek() { + Assertions.assertFalse(DateUtils.isBefore("2025-20", "2025-10")); + } + + @Test + @DisplayName("isBefore - earlier year") + void testIsBefore_EarlierYear() { + Assertions.assertTrue(DateUtils.isBefore("2024-50", "2025-1")); + } + + @Test + @DisplayName("isBefore - later year") + void testIsBefore_LaterYear() { + Assertions.assertFalse(DateUtils.isBefore("2026-1", "2025-50")); + } +} diff --git a/src/test/java/fr/openmc/core/utils/EnumUtilsTest.java b/src/test/java/fr/openmc/core/utils/EnumUtilsTest.java new file mode 100644 index 000000000..499489e03 --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/EnumUtilsTest.java @@ -0,0 +1,47 @@ +package fr.openmc.core.utils; + +import org.bukkit.Material; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class EnumUtilsTest { + + @Test + @DisplayName("Match valid enum value") + void testMatch_Valid() { + Assertions.assertEquals(Material.STONE, EnumUtils.match("stone", Material.class)); + } + + @Test + @DisplayName("Match valid enum value case insensitive") + void testMatch_CaseInsensitive() { + Assertions.assertEquals(Material.DIAMOND, EnumUtils.match("diamond", Material.class)); + Assertions.assertEquals(Material.DIAMOND, EnumUtils.match("DIAMOND", Material.class)); + Assertions.assertEquals(Material.DIAMOND, EnumUtils.match("Diamond", Material.class)); + } + + @Test + @DisplayName("Match invalid key returns null") + void testMatch_InvalidReturnsNull() { + Assertions.assertNull(EnumUtils.match("not_a_material", Material.class)); + } + + @Test + @DisplayName("Match invalid key returns default value") + void testMatch_InvalidReturnsDefault() { + Assertions.assertEquals(Material.AIR, EnumUtils.match("not_a_material", Material.class, Material.AIR)); + } + + @Test + @DisplayName("Match null key returns default") + void testMatch_NullKey() { + Assertions.assertEquals(Material.STONE, EnumUtils.match(null, Material.class, Material.STONE)); + } + + @Test + @DisplayName("Match null key returns null without default") + void testMatch_NullKeyNoDefault() { + Assertions.assertNull(EnumUtils.match(null, Material.class)); + } +} diff --git a/src/test/java/fr/openmc/core/utils/InputUtilsTest.java b/src/test/java/fr/openmc/core/utils/InputUtilsTest.java index e72417864..a399df5f3 100644 --- a/src/test/java/fr/openmc/core/utils/InputUtilsTest.java +++ b/src/test/java/fr/openmc/core/utils/InputUtilsTest.java @@ -11,51 +11,79 @@ class InputUtilsTest { @Test @DisplayName("Conversion Sign Input to Money") void testConvertSignInputToMoney_ShouldGiveTheAmountInFloat() { - Assertions.assertEquals( - 3000000.0, - InputUtils.convertToMoneyValue("3m") - ); - Assertions.assertEquals( - 3000.0, - InputUtils.convertToMoneyValue("3k") - ); - Assertions.assertEquals( - 3000000.0, - InputUtils.convertToMoneyValue("3M") - ); - Assertions.assertEquals( - 3000.0, - InputUtils.convertToMoneyValue("3K") - ); - Assertions.assertEquals( - 1.0, - InputUtils.convertToMoneyValue("1") - ); - Assertions.assertEquals( - 3000.0, - InputUtils.convertToMoneyValue("3000") - ); + Assertions.assertEquals(3000000.0, InputUtils.convertToMoneyValue("3m")); + Assertions.assertEquals(3000.0, InputUtils.convertToMoneyValue("3k")); + Assertions.assertEquals(3000000.0, InputUtils.convertToMoneyValue("3M")); + Assertions.assertEquals(3000.0, InputUtils.convertToMoneyValue("3K")); + Assertions.assertEquals(1.0, InputUtils.convertToMoneyValue("1")); + Assertions.assertEquals(3000.0, InputUtils.convertToMoneyValue("3000")); + } + + @Test + @DisplayName("Conversion with decimals") + void testConvertToMoneyValue_Decimals() { + Assertions.assertEquals(2500000.0, InputUtils.convertToMoneyValue("2.5m")); + Assertions.assertEquals(1500.0, InputUtils.convertToMoneyValue("1.5k")); + Assertions.assertEquals(99.99, InputUtils.convertToMoneyValue("99.99")); } @ParameterizedTest @DisplayName("Conversion of input sign to -1") - @ValueSource(strings = {"-3", "-1", "489y", "4,5"}) + @ValueSource(strings = {"-3", "-1", "489y", "4,5", "NaN", "Infinity", "-Infinity"}) void testConvertSignInputToMoney_ShouldGiveAnError(String input) { Assertions.assertEquals(-1, InputUtils.convertToMoneyValue(input)); } + @Test + @DisplayName("Conversion null and empty returns -1") + void testConvertToMoneyValue_NullAndEmpty() { + Assertions.assertEquals(-1, InputUtils.convertToMoneyValue(null)); + Assertions.assertEquals(-1, InputUtils.convertToMoneyValue("")); + } + @ParameterizedTest @DisplayName("Check is returned value is true") - @ValueSource(strings = {"1", "3m", "3k", "3M", "3K", "3000"}) + @ValueSource(strings = {"1", "3m", "3k", "3M", "3K", "3000", "2.5m", "0.5k"}) void testIsInputMoney_MustReturnTrue(String input) { Assertions.assertTrue(InputUtils.isInputMoney(input)); } @ParameterizedTest @DisplayName("Check is returned value is false") - @ValueSource(strings = {"0", "-3", "-1", "489y", "4,5"}) + @ValueSource(strings = {"0", "-3", "-1", "489y", "4,5", "NaN", "Infinity", "-Infinity"}) void testIsInputMoney_MustReturnFalse(String input) { Assertions.assertFalse(InputUtils.isInputMoney(input)); } + @Test + @DisplayName("isInputMoney null and empty") + void testIsInputMoney_NullAndEmpty() { + Assertions.assertFalse(InputUtils.isInputMoney(null)); + Assertions.assertFalse(InputUtils.isInputMoney("")); + } + + @Test + @DisplayName("Pluralize with count > 1 adds s") + void testPluralize_Plural() { + Assertions.assertEquals("joueurs", InputUtils.pluralize("joueur", 5)); + } + + @Test + @DisplayName("Pluralize with count 1 no s") + void testPluralize_Singular() { + Assertions.assertEquals("joueur", InputUtils.pluralize("joueur", 1)); + } + + @Test + @DisplayName("Pluralize with count 0 no s") + void testPluralize_Zero() { + Assertions.assertEquals("joueur", InputUtils.pluralize("joueur", 0)); + } + + @Test + @DisplayName("Pluralize long variant") + void testPluralize_Long() { + Assertions.assertEquals("blocs", InputUtils.pluralize("bloc", 100L)); + Assertions.assertEquals("bloc", InputUtils.pluralize("bloc", 1L)); + } } diff --git a/src/test/java/fr/openmc/core/utils/MathUtilsTest.java b/src/test/java/fr/openmc/core/utils/MathUtilsTest.java new file mode 100644 index 000000000..0453bcb0e --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/MathUtilsTest.java @@ -0,0 +1,52 @@ +package fr.openmc.core.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MathUtilsTest { + + @Test + @DisplayName("lerpColor t=0 returns start color") + void testLerpColor_Start() { + int start = 0xFF0000; + int end = 0x0000FF; + Assertions.assertEquals(start, MathUtils.lerpColor(start, end, 0.0)); + } + + @Test + @DisplayName("lerpColor t=1 returns end color") + void testLerpColor_End() { + int start = 0xFF0000; + int end = 0x0000FF; + Assertions.assertEquals(end, MathUtils.lerpColor(start, end, 1.0)); + } + + @Test + @DisplayName("lerpColor t=0.5 returns midpoint") + void testLerpColor_Midpoint() { + int start = 0x000000; + int end = 0xFEFEFE; + int result = MathUtils.lerpColor(start, end, 0.5); + Assertions.assertEquals(0x7F7F7F, result); + } + + @Test + @DisplayName("lerpColor same colors returns same") + void testLerpColor_SameColors() { + int color = 0xABCDEF; + Assertions.assertEquals(color, MathUtils.lerpColor(color, color, 0.5)); + } + + @Test + @DisplayName("lerpColor black to white at 0.25") + void testLerpColor_QuarterWay() { + int result = MathUtils.lerpColor(0x000000, 0xFFFFFF, 0.25); + int r = (result >> 16) & 0xFF; + int g = (result >> 8) & 0xFF; + int b = result & 0xFF; + Assertions.assertEquals(63, r); + Assertions.assertEquals(63, g); + Assertions.assertEquals(63, b); + } +} diff --git a/src/test/java/fr/openmc/core/utils/MotdUtilsTest.java b/src/test/java/fr/openmc/core/utils/MotdUtilsTest.java index b6902cf2d..8449ae767 100644 --- a/src/test/java/fr/openmc/core/utils/MotdUtilsTest.java +++ b/src/test/java/fr/openmc/core/utils/MotdUtilsTest.java @@ -31,9 +31,9 @@ private String getComponentContent(Component component) { @Test @DisplayName("MOTD switch") void testMOTD() { + server.motd(Component.text("default")); String motd = getComponentContent(server.motd()); - new MotdUtils(); server.getScheduler().performTicks(12001L); Assertions.assertNotEquals(getComponentContent(server.motd()), motd); diff --git a/src/test/java/fr/openmc/core/utils/PaddingUtilsTest.java b/src/test/java/fr/openmc/core/utils/PaddingUtilsTest.java new file mode 100644 index 000000000..14639345b --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/PaddingUtilsTest.java @@ -0,0 +1,46 @@ +package fr.openmc.core.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PaddingUtilsTest { + + @Test + @DisplayName("Center text in given width") + void testFormat_Centered() { + String result = PaddingUtils.format("hi", 10); + Assertions.assertEquals(10, result.length()); + Assertions.assertEquals(" hi ", result); + } + + @Test + @DisplayName("Text longer than width returns text without extra padding") + void testFormat_TextLongerThanWidth() { + String result = PaddingUtils.format("hello world", 5); + Assertions.assertEquals("hello world", result); + } + + @Test + @DisplayName("Text exactly equals width") + void testFormat_ExactWidth() { + String result = PaddingUtils.format("abcde", 5); + Assertions.assertEquals("abcde", result); + } + + @Test + @DisplayName("Odd padding distributes correctly") + void testFormat_OddPadding() { + String result = PaddingUtils.format("ab", 5); + Assertions.assertEquals(5, result.length()); + Assertions.assertEquals(" ab ", result); + } + + @Test + @DisplayName("Empty string gets full padding") + void testFormat_EmptyString() { + String result = PaddingUtils.format("", 4); + Assertions.assertEquals(4, result.length()); + Assertions.assertEquals(" ", result); + } +} diff --git a/src/test/java/fr/openmc/core/utils/QueueTest.java b/src/test/java/fr/openmc/core/utils/QueueTest.java new file mode 100644 index 000000000..e00c1764d --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/QueueTest.java @@ -0,0 +1,78 @@ +package fr.openmc.core.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class QueueTest { + + @Test + @DisplayName("Add and get element") + void testAddAndGet() { + Queue queue = new Queue<>(5); + queue.add("a", 1); + Assertions.assertEquals(1, queue.get("a")); + } + + @Test + @DisplayName("Remove element") + void testRemove() { + Queue queue = new Queue<>(5); + queue.add("a", 1); + queue.remove("a"); + Assertions.assertNull(queue.get("a")); + } + + @Test + @DisplayName("Get non-existent key returns null") + void testGet_NonExistent() { + Queue queue = new Queue<>(5); + Assertions.assertNull(queue.get("missing")); + } + + @Test + @DisplayName("Eviction when exceeding size") + void testEviction() { + Queue queue = new Queue<>(3); + queue.add("a", 1); + queue.add("b", 2); + queue.add("c", 3); + queue.add("d", 4); + + Assertions.assertNull(queue.get("a")); + Assertions.assertEquals(2, queue.get("b")); + Assertions.assertEquals(3, queue.get("c")); + Assertions.assertEquals(4, queue.get("d")); + } + + @Test + @DisplayName("Queue respects max size") + void testMaxSize() { + Queue queue = new Queue<>(2); + queue.add(1, "one"); + queue.add(2, "two"); + queue.add(3, "three"); + + Assertions.assertEquals(2, queue.getQueue().size()); + } + + @Test + @DisplayName("Overwrite existing key") + void testOverwrite() { + Queue queue = new Queue<>(5); + queue.add("a", 1); + queue.add("a", 99); + Assertions.assertEquals(99, queue.get("a")); + } + + @Test + @DisplayName("Queue size 1 keeps only last element") + void testSizeOne() { + Queue queue = new Queue<>(1); + queue.add("first", "1"); + queue.add("second", "2"); + + Assertions.assertNull(queue.get("first")); + Assertions.assertEquals("2", queue.get("second")); + } +} diff --git a/src/test/java/fr/openmc/core/utils/RandomUtilsTest.java b/src/test/java/fr/openmc/core/utils/RandomUtilsTest.java new file mode 100644 index 000000000..29c74d7bb --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/RandomUtilsTest.java @@ -0,0 +1,47 @@ +package fr.openmc.core.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +class RandomUtilsTest { + + @RepeatedTest(50) + @DisplayName("randomBetween int stays in bounds") + void testRandomBetweenInt_InBounds() { + int result = RandomUtils.randomBetween(5, 10); + Assertions.assertTrue(result >= 5 && result <= 10, + "Expected between 5 and 10, got: " + result); + } + + @RepeatedTest(50) + @DisplayName("randomBetween double stays in bounds") + void testRandomBetweenDouble_InBounds() { + double result = RandomUtils.randomBetween(1.0, 5.0); + Assertions.assertTrue(result >= 1.0 && result < 5.0, + "Expected between 1.0 and 5.0, got: " + result); + } + + @RepeatedTest(50) + @DisplayName("randomBetween float stays in bounds") + void testRandomBetweenFloat_InBounds() { + float result = RandomUtils.randomBetween(0.0f, 1.0f); + Assertions.assertTrue(result >= 0.0f && result < 1.0f, + "Expected between 0.0 and 1.0, got: " + result); + } + + @Test + @DisplayName("randomBetween int same min max returns that value") + void testRandomBetweenInt_SameMinMax() { + Assertions.assertEquals(7, RandomUtils.randomBetween(7, 7)); + } + + @RepeatedTest(50) + @DisplayName("randomBetween int negative range") + void testRandomBetweenInt_NegativeRange() { + int result = RandomUtils.randomBetween(-10, -5); + Assertions.assertTrue(result >= -10 && result <= -5, + "Expected between -10 and -5, got: " + result); + } +} diff --git a/src/test/java/fr/openmc/core/utils/YmlUtilsTest.java b/src/test/java/fr/openmc/core/utils/YmlUtilsTest.java new file mode 100644 index 000000000..426fcb2a3 --- /dev/null +++ b/src/test/java/fr/openmc/core/utils/YmlUtilsTest.java @@ -0,0 +1,89 @@ +package fr.openmc.core.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class YmlUtilsTest { + + @Test + @DisplayName("Deep copy simple map") + void testDeepCopy_SimpleMap() { + Map original = new HashMap<>(); + original.put("key1", "value1"); + original.put("key2", 42); + + Map copy = YmlUtils.deepCopy(original); + + Assertions.assertEquals(original, copy); + Assertions.assertNotSame(original, copy); + } + + @Test + @DisplayName("Deep copy nested map") + void testDeepCopy_NestedMap() { + Map inner = new HashMap<>(); + inner.put("nested", "value"); + + Map original = new HashMap<>(); + original.put("outer", inner); + + Map copy = YmlUtils.deepCopy(original); + + Assertions.assertEquals("value", ((Map) copy.get("outer")).get("nested")); + + inner.put("nested", "modified"); + Assertions.assertEquals("value", ((Map) copy.get("outer")).get("nested")); + } + + @Test + @DisplayName("Deep copy with list") + void testDeepCopy_WithList() { + List list = new ArrayList<>(); + list.add("a"); + list.add("b"); + + Map original = new HashMap<>(); + original.put("items", list); + + Map copy = YmlUtils.deepCopy(original); + + list.add("c"); + List copiedList = (List) copy.get("items"); + Assertions.assertEquals(2, copiedList.size()); + } + + @Test + @DisplayName("Deep copy primitives are preserved") + void testDeepCopyObject_Primitives() { + Assertions.assertEquals("hello", YmlUtils.deepCopyObject("hello")); + Assertions.assertEquals(42, YmlUtils.deepCopyObject(42)); + Assertions.assertEquals(3.14, YmlUtils.deepCopyObject(3.14)); + Assertions.assertTrue((Boolean) YmlUtils.deepCopyObject(true)); + } + + @Test + @DisplayName("Deep copy empty map") + void testDeepCopy_EmptyMap() { + Map copy = YmlUtils.deepCopy(new HashMap<>()); + Assertions.assertTrue(copy.isEmpty()); + } + + @Test + @DisplayName("Deep copy null key is skipped") + void testDeepCopy_NullKey() { + Map original = new HashMap<>(); + original.put(null, "value"); + original.put("key", "val"); + + Map copy = YmlUtils.deepCopy(original); + + Assertions.assertFalse(copy.containsKey(null)); + Assertions.assertEquals("val", copy.get("key")); + } +}