diff --git a/src/main/java/fr/openmc/core/OMCPlugin.java b/src/main/java/fr/openmc/core/OMCPlugin.java index a5f4bef4d..e46a4404a 100644 --- a/src/main/java/fr/openmc/core/OMCPlugin.java +++ b/src/main/java/fr/openmc/core/OMCPlugin.java @@ -17,6 +17,7 @@ import fr.openmc.core.features.analytics.AnalyticsManager; import fr.openmc.core.features.animations.AnimationsManager; import fr.openmc.core.features.bits.BitsManager; +import fr.openmc.core.features.chatanimations.ChatAnimationManager; import fr.openmc.core.features.city.CityManager; import fr.openmc.core.features.city.sub.mascots.MascotsManager; import fr.openmc.core.features.cube.multiblocks.MultiBlockManager; @@ -123,6 +124,7 @@ public class OMCPlugin extends JavaPlugin { ContestManager::new, WeeklyEventsManager::new, DailyEventsManager::new, + ChatAnimationManager::new, EventsManager::new, DreamManager::new, MultiBlockManager::new, diff --git a/src/main/java/fr/openmc/core/features/chatanimations/ChatAnimation.java b/src/main/java/fr/openmc/core/features/chatanimations/ChatAnimation.java new file mode 100644 index 000000000..489b10e9a --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/ChatAnimation.java @@ -0,0 +1,45 @@ +package fr.openmc.core.features.chatanimations; + + +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.registry.loottable.LootReward; +import lombok.Getter; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; + +public abstract class ChatAnimation { + @Setter + private boolean finished = false; + @Getter + protected CustomLootTable reward; + + public abstract Component getName(); + public abstract Component getAnnounceStart(); + public abstract Component getDescriptionResult(); + + public abstract long getTimeBeforeEnd(); + + public void stop() { + // a override si besoin + } + + public void complete(Player winner) { + if (finished) return; + finished = true; + + LootReward loot; + if (winner == null) + loot = null; + else + loot = reward.rollLoots(winner); + + this.stop(); + ChatAnimationManager.onAnimationCompleted(this, winner, loot); + } + + public final boolean isFinished() { + return finished; + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/ChatAnimationManager.java b/src/main/java/fr/openmc/core/features/chatanimations/ChatAnimationManager.java new file mode 100644 index 000000000..3f025aa74 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/ChatAnimationManager.java @@ -0,0 +1,218 @@ +package fr.openmc.core.features.chatanimations; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.bootstrap.features.Feature; +import fr.openmc.core.bootstrap.features.annotations.Credit; +import fr.openmc.core.bootstrap.features.types.HasListeners; +import fr.openmc.core.bootstrap.features.types.LoadAfterItemsAdder; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeListener; +import fr.openmc.core.features.chatanimations.contents.challenge.types.*; +import fr.openmc.core.features.chatanimations.contents.quizz.Quizz; +import fr.openmc.core.features.chatanimations.contents.quizz.QuizzListener; +import fr.openmc.core.registry.items.keys.KeyBlock; +import fr.openmc.core.registry.loottable.LootReward; +import fr.openmc.core.utils.RandomUtils; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.BlockType; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +@Credit(developers = {"iambibi_"}) +public class ChatAnimationManager extends Feature implements LoadAfterItemsAdder, HasListeners { + + //todo: changr valeur pour delay entre animationsXXss + private static final long MIN_DELAY_TICKS = 20 * 60 * 1L; // 20 min + private static final long MAX_DELAY_TICKS = 20 * 60 * 2L; // 30 min + + private Set ANIMATIONS; + + @Getter + private static ChatAnimation currentAnimation; + @Getter + private static ChatAnimation lastAnimation; + private BukkitTask scheduleTask; + private static BukkitTask endAnimationTask; + + @Override + public Set getListeners() { + return Set.of( + new QuizzListener(), + new ChallengeListener() + ); + } + + @Override + public void init() { + ANIMATIONS = new HashSet<>(Set.of( + new MineBlocksChallenge(KeyBlock.vanilla(BlockType.DIAMOND_ORE), 2, 60L), + new MineBlocksChallenge(KeyBlock.vanilla(BlockType.SCULK), 15, 60L), + new MineBlocksChallenge(KeyBlock.vanilla(BlockType.BEEHIVE), 1, 60L), + new MineBlocksChallenge(KeyBlock.custom(OMCRegistry.CUSTOM_ITEMS.AYWENITE_BLOCK), 1, 60L), + new MineBlocksChallenge(KeyBlock.vanilla(BlockType.COBBLESTONE), 32, 90L), + + new JumpChallenge(10, 30L), + new JumpChallenge(25, 45L), + new JumpChallenge(50, 90L), + + new KillEntityChallenge(EntityType.COW, 15, 60L), + new KillEntityChallenge(EntityType.ZOMBIE, 5, 60L), + new KillEntityChallenge(EntityType.AXOLOTL, 1, 120L), + + new CraftItemChallenge(ItemStack.of(Material.STICK), 32, 45L), + new CraftItemChallenge(ItemStack.of(Material.CRAFTING_TABLE), 1, 20L), + new CraftItemChallenge(ItemStack.of(Material.BREAD), 10, 60L), + new CraftItemChallenge(OMCRegistry.CUSTOM_ITEMS.THE_MIXTURE, 1, 60L), + + new FishingChallenge(3, 90L), + new FishingChallenge(5, 120L), + + new PlaceBlocksChallenge(KeyBlock.vanilla(BlockType.OAK_PLANKS), 50, 60L), + new PlaceBlocksChallenge(KeyBlock.vanilla(BlockType.TORCH), 20, 45L), + new PlaceBlocksChallenge(KeyBlock.vanilla(BlockType.WHITE_WOOL), 10, 60L), + new PlaceBlocksChallenge(KeyBlock.custom(OMCRegistry.CUSTOM_ITEMS.ELEVATOR_GRAY), 1, 120L), + + new WalkDistanceChallenge(200, 60L), + new WalkDistanceChallenge(500, 120L), + new WalkDistanceChallenge(1000, 180L), + + new Quizz(TranslationManager.translation("quizz.tech.01"), "python"), + new Quizz(TranslationManager.translation("quizz.tech.02"), "hypertext markup language", "hyper text markup language"), + new Quizz(TranslationManager.translation("quizz.tech.03"), "microsoft"), + new Quizz(TranslationManager.translation("quizz.tech.04"), "linus torvalds", "torvalds"), + new Quizz(TranslationManager.translation("quizz.tech.05"), "8"), + new Quizz(TranslationManager.translation("quizz.tech.06"), "python"), + new Quizz(TranslationManager.translation("quizz.tech.07"), "https"), + new Quizz(TranslationManager.translation("quizz.tech.08"), "mark zuckerberg", "zuckerberg"), + new Quizz(TranslationManager.translation("quizz.tech.09"), "mojang"), + new Quizz(TranslationManager.translation("quizz.tech.10"), "png"), + new Quizz(TranslationManager.translation("quizz.tech.11"), "chrome", "google chrome"), + new Quizz(TranslationManager.translation("quizz.tech.12"), "random access memory"), + + new Quizz(TranslationManager.translation("quizz.general.01"), "paris"), + new Quizz(TranslationManager.translation("quizz.general.02"), "7"), + new Quizz(TranslationManager.translation("quizz.general.03"), "léonard de vinci", "leonard de vinci", "de vinci"), + new Quizz(TranslationManager.translation("quizz.general.04"), "pacifique", "océan pacifique", "ocean pacifique"), + new Quizz(TranslationManager.translation("quizz.general.05"), "1789"), + new Quizz(TranslationManager.translation("quizz.general.06"), "everest", "mont everest"), + new Quizz(TranslationManager.translation("quizz.general.07"), "11"), + new Quizz(TranslationManager.translation("quizz.general.08"), "mars"), + new Quizz(TranslationManager.translation("quizz.general.09"), "victor hugo", "hugo"), + new Quizz(TranslationManager.translation("quizz.general.10"), "guépard", "guepard"), + new Quizz(TranslationManager.translation("quizz.general.11"), "yen"), + new Quizz(TranslationManager.translation("quizz.general.12"), "366"), + new Quizz(TranslationManager.translation("quizz.general.13"), "italie"), + new Quizz(TranslationManager.translation("quizz.general.14"), "beethoven", "ludwig van beethoven"), + new Quizz(TranslationManager.translation("quizz.general.15"), "nil", "le nil"), + new Quizz(TranslationManager.translation("quizz.general.16"), "au"), + new Quizz(TranslationManager.translation("quizz.general.17"), "groenland"), + new Quizz(TranslationManager.translation("quizz.general.18"), "neil armstrong", "armstrong"), + new Quizz(TranslationManager.translation("quizz.general.19"), "sumo", "le sumo"), + new Quizz(TranslationManager.translation("quizz.general.20"), "8", "8 minutes", "8 min"), + new Quizz(TranslationManager.translation("quizz.general.21"), "3"), + + new Quizz(TranslationManager.translation("quizz.server.01"), "aywen1"), + new Quizz(TranslationManager.translation("quizz.server.02"), "notch", "markus persson", "persson"), + new Quizz(TranslationManager.translation("quizz.server.03"), "aywen"), + new Quizz(TranslationManager.translation("quizz.server.04"), "jorbani"), + new Quizz(TranslationManager.translation("quizz.server.05"), "2024") + )); + + scheduleNext(); + } + + @Override + public void save() { + if (scheduleTask != null) scheduleTask.cancel(); + forceStopCurrent(); + } + + private void scheduleNext() { + long delay = RandomUtils.randomBetween(MIN_DELAY_TICKS, MAX_DELAY_TICKS); + + scheduleTask = Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { + if (isAnimationRunning()) { + Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), this::scheduleNext, 20 * 30L); + return; + } + startNext(); + scheduleNext(); + }, delay); + } + + public boolean isAnimationRunning() { + return currentAnimation != null; + } + + public void startNext() { + if (isAnimationRunning()) return; + + List pool = ANIMATIONS.stream() + .filter(a -> !a.equals(lastAnimation)) + .toList(); + if (pool.isEmpty()) return; + + ChatAnimation animation = pool.get(ThreadLocalRandom.current().nextInt(pool.size())); + + launch(animation); + } + + private void launch(ChatAnimation animation) { + animation.setFinished(false); + currentAnimation = animation; + Bukkit.broadcast(currentAnimation.getAnnounceStart()); + + long timeBeforeEndTicks = animation.getTimeBeforeEnd() * 20L; + endAnimationTask = Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { + if (currentAnimation == animation && !animation.isFinished()) { + animation.complete(null); + } + }, timeBeforeEndTicks); + } + + public static void onAnimationCompleted(ChatAnimation animation, Player winner, LootReward loot) { + if (currentAnimation != animation) return; + + if (endAnimationTask != null) { + endAnimationTask.cancel(); + endAnimationTask = null; + } + + if (winner != null) { + Bukkit.broadcast(TranslationManager.translation("feature.chatanimations.winner", + winner.name().color(NamedTextColor.GOLD), + animation.getName(), + animation.getDescriptionResult(), + loot.buildComponent() + )); + } else { + Bukkit.broadcast(TranslationManager.translation("feature.chatanimations.no_winner", + animation.getName(), animation.getDescriptionResult())); + } + + lastAnimation = currentAnimation; + currentAnimation = null; + } + + public void forceStopCurrent() { + if (currentAnimation != null) { + currentAnimation.complete(null); + } + } + + public static ChatAnimation getActive() { + return currentAnimation; + } +} diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeAnimation.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeAnimation.java new file mode 100644 index 000000000..01fe9c54b --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeAnimation.java @@ -0,0 +1,46 @@ +package fr.openmc.core.features.chatanimations.contents.challenge; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.ChatAnimation; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +public abstract class ChallengeAnimation extends ChatAnimation { + private final Component description; + private final long time; + + public ChallengeAnimation(Component description, long time) { + this(description, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public ChallengeAnimation(Component description, CustomLootTable reward, long time) { + this.description = description; + this.reward = reward; + this.time = time; + } + + @Override + public Component getName() { + return TranslationManager.translation("feature.chatanimation.challenge.name"); + } + + @Override + public Component getAnnounceStart() { + return TranslationManager.translation("feature.chatanimations.challenge.announce", + description.color(NamedTextColor.GRAY), + Component.text(time, NamedTextColor.DARK_GREEN)); + } + + @Override + public Component getDescriptionResult() { + return TranslationManager.translation("feature.chatanimations.challenge.result", + description.color(NamedTextColor.GRAY)); + } + + @Override + public long getTimeBeforeEnd() { + return time; + } +} diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeListener.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeListener.java new file mode 100644 index 000000000..4ba6e73af --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeListener.java @@ -0,0 +1,146 @@ +package fr.openmc.core.features.chatanimations.contents.challenge; + +import com.destroystokyo.paper.event.player.PlayerJumpEvent; +import fr.openmc.core.features.chatanimations.ChatAnimation; +import fr.openmc.core.features.chatanimations.ChatAnimationManager; +import fr.openmc.core.features.chatanimations.contents.challenge.types.*; +import fr.openmc.core.utils.bukkit.ItemUtils; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerMoveEvent; + +public class ChallengeListener implements Listener { + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof MineBlocksChallenge challenge)) return; + if (animation.isFinished()) return; + if (!challenge.getKeyBlock().matches(event.getBlock())) return; + + Player player = event.getPlayer(); + int count = challenge.getProgress().merge(player.getUniqueId(), 1, Integer::sum); + + if (count >= challenge.getTarget()) { + Player winner = event.getPlayer(); + animation.complete(winner); + } + } + + @EventHandler + public void onJump(PlayerJumpEvent event) { + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof JumpChallenge challenge)) return; + if (animation.isFinished()) return; + + Player player = event.getPlayer(); + int count = challenge.getProgress().merge(player.getUniqueId(), 1, Integer::sum); + + if (count >= challenge.getTarget()) { + animation.complete(player); + } + } + + @EventHandler + public void onEntityDeath(EntityDeathEvent event) { + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof KillEntityChallenge challenge)) return; + if (animation.isFinished()) return; + if (event.getEntity().getType() != challenge.getEntityType()) return; + + Player killer = event.getEntity().getKiller(); + if (killer == null) return; + + int count = challenge.getProgress().merge(killer.getUniqueId(), 1, Integer::sum); + + if (count >= challenge.getTarget()) { + animation.complete(killer); + } + } + + @EventHandler + public void onCraftItem(CraftItemEvent event) { + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof CraftItemChallenge challenge)) return; + if (animation.isFinished()) return; + if (!ItemUtils.isSimilar(event.getRecipe().getResult(), challenge.getItem())) return; + if (!(event.getWhoClicked() instanceof Player player)) return; + + int count; + if (!event.isShiftClick()) { + count = challenge.getProgress().merge(player.getUniqueId(), 1, Integer::sum); + } else { + int maxCraftable = ItemUtils.getMaxCraftAmount(event.getInventory()); + int capacity = ItemUtils.getFreePlacesForItem((Player) event.getWhoClicked(), challenge.getItem()); + + maxCraftable = Math.min(maxCraftable, capacity); + if (maxCraftable == 0) return; + + count = challenge.getProgress().merge(player.getUniqueId(), maxCraftable, Integer::sum); + } + if (count >= challenge.getTarget()) { + animation.complete(player); + } + } + + @EventHandler + public void onPlayerFish(PlayerFishEvent event) { + if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) return; + + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof FishingChallenge challenge)) return; + if (animation.isFinished()) return; + + Player player = event.getPlayer(); + int count = challenge.getProgress().merge(player.getUniqueId(), 1, Integer::sum); + + if (count >= challenge.getTarget()) { + animation.complete(player); + } + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof PlaceBlocksChallenge challenge)) return; + if (animation.isFinished()) return; + if (!challenge.getKeyBlock().matches(event.getBlock())) return; + + Player player = event.getPlayer(); + int count = challenge.getProgress().merge(player.getUniqueId(), 1, Integer::sum); + + if (count >= challenge.getTarget()) { + animation.complete(player); + } + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + if (event.getFrom().distanceSquared(event.getTo()) == 0) return; + + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof WalkDistanceChallenge challenge)) return; + if (animation.isFinished()) return; + + Player player = event.getPlayer(); + double distance = event.getFrom().distance(event.getTo()); + double total = challenge.getProgress().merge(player.getUniqueId(), distance, Double::sum); + + if (total >= challenge.getTarget()) { + animation.complete(player); + } + } + +} diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeLootTable.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeLootTable.java new file mode 100644 index 000000000..bf40732da --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/ChallengeLootTable.java @@ -0,0 +1,40 @@ +package fr.openmc.core.features.chatanimations.contents.challenge; + +import fr.openmc.core.OMCRegistry; +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.registry.loottable.CustomLootTable; +import fr.openmc.core.registry.loottable.loots.CustomLoot; +import fr.openmc.core.registry.loottable.loots.ItemLoot; +import fr.openmc.core.registry.loottable.loots.MoneyLoot; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; + +import java.util.Set; + +public class ChallengeLootTable extends CustomLootTable { + @Override + public Component getName() { + return TranslationManager.translation("feature.chatanimations.challenge.table.name"); + } + + @Override + public String getNamespace() { + return "omc:challenge_table"; + } + + @Override + public Set getLoots() { + return Set.of( + new MoneyLoot(200, 450, 1), + new ItemLoot(OMCRegistry.CUSTOM_ITEMS.CONTEST_SHELL, + (player) -> WeeklyEventsManager.isEventActive() + && WeeklyEventsManager.getCurrentEvent() instanceof Contest contest + && ContestPhase.TRADE_PHASE.getPhase().equals(contest.getActivePhase()), + 1, + 100 + ) + ); + } +} diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/CraftItemChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/CraftItemChallenge.java new file mode 100644 index 000000000..575a34284 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/CraftItemChallenge.java @@ -0,0 +1,42 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.items.CustomItem; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class CraftItemChallenge extends ChallengeAnimation { + private final ItemStack item; + private final int target; + private final Map progress = new HashMap<>(); + + public CraftItemChallenge(CustomItem item, int target, long time) { + this(item.getBest(), target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public CraftItemChallenge(ItemStack item, int target, long time) { + this(item, target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public CraftItemChallenge(ItemStack item, int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.craft_item.name", + Component.text(target), Component.translatable(item.translationKey())), reward, time); + this.item = item; + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/FishingChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/FishingChallenge.java new file mode 100644 index 000000000..cbe055425 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/FishingChallenge.java @@ -0,0 +1,34 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class FishingChallenge extends ChallengeAnimation { + private final int target; + private final Map progress = new HashMap<>(); + + public FishingChallenge(int target, long time) { + this(target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public FishingChallenge(int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.fishing.name", + Component.text(target)), reward, time); + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/JumpChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/JumpChallenge.java new file mode 100644 index 000000000..c87a31a5a --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/JumpChallenge.java @@ -0,0 +1,34 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class JumpChallenge extends ChallengeAnimation { + private final int target; + private final Map progress = new HashMap<>(); + + public JumpChallenge(int target, long time) { + this(target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public JumpChallenge(int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.jump.name", + Component.text(target)), reward, time); + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/KillEntityChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/KillEntityChallenge.java new file mode 100644 index 000000000..61f6cf9e6 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/KillEntityChallenge.java @@ -0,0 +1,37 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.EntityType; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class KillEntityChallenge extends ChallengeAnimation { + private final EntityType entityType; + private final int target; + private final Map progress = new HashMap<>(); + + public KillEntityChallenge(EntityType entityType, int target, long time) { + this(entityType, target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public KillEntityChallenge(EntityType entityType, int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.kill_entity.name", + Component.text(target), Component.translatable(entityType.translationKey())), reward, time); + this.entityType = entityType; + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/MineBlocksChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/MineBlocksChallenge.java new file mode 100644 index 000000000..24f50a46c --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/MineBlocksChallenge.java @@ -0,0 +1,36 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.items.keys.KeyBlock; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class MineBlocksChallenge extends ChallengeAnimation { + private final KeyBlock keyBlock; + private final int target; + private final Map progress = new HashMap<>(); + + public MineBlocksChallenge(KeyBlock keyBlock, int target, long time) { + this(keyBlock, target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public MineBlocksChallenge(KeyBlock keyBlock, int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.mine_blocks.name", + Component.text(target), keyBlock.name()), reward, time); + this.keyBlock = keyBlock; + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/PlaceBlocksChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/PlaceBlocksChallenge.java new file mode 100644 index 000000000..d8ce646f4 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/PlaceBlocksChallenge.java @@ -0,0 +1,37 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.items.keys.KeyBlock; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class PlaceBlocksChallenge extends ChallengeAnimation { + private final KeyBlock keyBlock; + private final int target; + private final Map progress = new HashMap<>(); + + public PlaceBlocksChallenge(KeyBlock keyBlock, int target, long time) { + this(keyBlock, target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public PlaceBlocksChallenge(KeyBlock keyBlock, int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.place_blocks.name", + Component.text(target), keyBlock.name()), reward, time); + this.keyBlock = keyBlock; + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/WalkDistanceChallenge.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/WalkDistanceChallenge.java new file mode 100644 index 000000000..600aa906c --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/challenge/types/WalkDistanceChallenge.java @@ -0,0 +1,34 @@ +package fr.openmc.core.features.chatanimations.contents.challenge.types; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeAnimation; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class WalkDistanceChallenge extends ChallengeAnimation { + private final int target; + private final Map progress = new HashMap<>(); + + public WalkDistanceChallenge(int target, long time) { + this(target, OMCRegistry.CUSTOM_LOOT_TABLES.CHALLENGE, time); + } + + public WalkDistanceChallenge(int target, CustomLootTable reward, long time) { + super(TranslationManager.translation("feature.chatanimations.challenge.walk_distance.name", + Component.text(target)), reward, time); + this.target = target; + } + + @Override + public void stop() { + progress.clear(); + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/Quizz.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/Quizz.java new file mode 100644 index 000000000..d75bf37ef --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/Quizz.java @@ -0,0 +1,61 @@ +package fr.openmc.core.features.chatanimations.contents.quizz; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.chatanimations.ChatAnimation; +import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Quizz extends ChatAnimation { + + private final Component question; + @Getter + private final List answers; + private final long time; + + public Quizz(Component question, String... answers) { + this(question, + Arrays.asList(answers), + OMCRegistry.CUSTOM_LOOT_TABLES.QUIZZ, + 30L); + } + + public Quizz(Component question, List answers, CustomLootTable reward, long time) { + this.question = question; + this.answers = answers.stream() + .map(String::toLowerCase) + .collect(Collectors.toList()); + this.reward = reward; + this.time = time; + } + + @Override + public Component getName() { + return TranslationManager.translation("feature.chatanimations.quizz.name"); + } + + @Override + public Component getAnnounceStart() { + return TranslationManager.translation("feature.chatanimations.quizz.announce", + question.color(NamedTextColor.GRAY), + Component.text(time, NamedTextColor.GOLD)); + } + + @Override + public Component getDescriptionResult() { + return TranslationManager.translation("feature.chatanimations.quizz.result", + Component.text(answers.getFirst(), NamedTextColor.YELLOW)); + } + + @Override + public long getTimeBeforeEnd() { + return time; + } +} + diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/QuizzListener.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/QuizzListener.java new file mode 100644 index 000000000..11d963225 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/QuizzListener.java @@ -0,0 +1,30 @@ +package fr.openmc.core.features.chatanimations.contents.quizz; + +import fr.openmc.core.features.chatanimations.ChatAnimation; +import fr.openmc.core.features.chatanimations.ChatAnimationManager; +import io.papermc.paper.event.player.AsyncChatEvent; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class QuizzListener implements Listener { + @EventHandler + public void onChat(AsyncChatEvent event) { + ChatAnimation animation = ChatAnimationManager.getActive(); + if (animation == null) return; + if (!(animation instanceof Quizz quizz)) return; + if (animation.isFinished()) return; + + String message = PlainTextComponentSerializer.plainText() + .serialize(event.message()) + .trim() + .toLowerCase(); + + if (quizz.getAnswers().contains(message)) { + Player winner = event.getPlayer(); + animation.complete(winner); + event.setCancelled(true); + } + } +} diff --git a/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/QuizzLootTable.java b/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/QuizzLootTable.java new file mode 100644 index 000000000..7a6f7a0b4 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/chatanimations/contents/quizz/QuizzLootTable.java @@ -0,0 +1,40 @@ +package fr.openmc.core.features.chatanimations.contents.quizz; + +import fr.openmc.core.OMCRegistry; +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.registry.loottable.CustomLootTable; +import fr.openmc.core.registry.loottable.loots.CustomLoot; +import fr.openmc.core.registry.loottable.loots.ItemLoot; +import fr.openmc.core.registry.loottable.loots.MoneyLoot; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; + +import java.util.Set; + +public class QuizzLootTable extends CustomLootTable { + @Override + public Component getName() { + return TranslationManager.translation("feature.chatanimations.quizz.table.name"); + } + + @Override + public String getNamespace() { + return "omc:quizz_table"; + } + + @Override + public Set getLoots() { + return Set.of( + new MoneyLoot(100, 300, 1), + new ItemLoot(OMCRegistry.CUSTOM_ITEMS.CONTEST_SHELL, + (player) -> WeeklyEventsManager.isEventActive() + && WeeklyEventsManager.getCurrentEvent() instanceof Contest contest + && ContestPhase.TRADE_PHASE.getPhase().equals(contest.getActivePhase()), + 1, + 50 + ) + ); + } +} diff --git a/src/main/java/fr/openmc/core/features/credits/Credits.java b/src/main/java/fr/openmc/core/features/credits/Credits.java index 02ffe243f..e34f31f69 100644 --- a/src/main/java/fr/openmc/core/features/credits/Credits.java +++ b/src/main/java/fr/openmc/core/features/credits/Credits.java @@ -5,6 +5,7 @@ import fr.openmc.core.bootstrap.features.annotations.Credit; import fr.openmc.core.features.adminshop.AdminShopManager; import fr.openmc.core.features.animations.AnimationsManager; +import fr.openmc.core.features.chatanimations.ChatAnimationManager; import fr.openmc.core.features.bits.BitsManager; import fr.openmc.core.features.city.CityManager; import fr.openmc.core.features.city.sub.mascots.MascotsManager; @@ -43,6 +44,7 @@ public enum Credits { ANIMATIONS(Material.AMETHYST_BLOCK, "feature.credits.feature.animations", AnimationsManager.class), BITS(Material.LAPIS_LAZULI, "feature.credits.feature.bits", BitsManager.class), CUBE(Material.LAPIS_BLOCK, "feature.credits.feature.cube", Set.of("iambibi_")), + CHAT_ANIMATION(Material.FIREWORK_ROCKET, "feature.credits.feature.chat_animation", ChatAnimationManager.class), CITY(OMCRegistry.CUSTOM_ITEMS.HOMES_ICON_AXENQ, "feature.credits.feature.city", CityManager.class), DREAM(Material.SCULK, "feature.credits.feature.dream", DreamManager.class), DREAM_MILESTONE(DreamItemRegistry.SINGULARITY, "feature.credits.feature.dream_milestone", Set.of("gab400", "Rylo42 (histoire et dialogues)")), diff --git a/src/main/java/fr/openmc/core/features/dream/mecanism/cloudcastle/CloudVault.java b/src/main/java/fr/openmc/core/features/dream/mecanism/cloudcastle/CloudVault.java index 24e7ce35a..bfc549594 100644 --- a/src/main/java/fr/openmc/core/features/dream/mecanism/cloudcastle/CloudVault.java +++ b/src/main/java/fr/openmc/core/features/dream/mecanism/cloudcastle/CloudVault.java @@ -45,7 +45,7 @@ public void onLootGenerate(BlockDispenseLootEvent event) { if (CLOUD_VAULT_LOOT_TABLE == null) return; - List itemLoots = CLOUD_VAULT_LOOT_TABLE.rollLootsWithAmount(player, 3) + List itemLoots = CLOUD_VAULT_LOOT_TABLE.rollLootsWithAmount(player, 3).loots() .stream() .filter(loot -> loot instanceof ItemLoot) .map(item -> (ItemLoot) item).toList(); diff --git a/src/main/java/fr/openmc/core/features/dream/mecanism/cloudfishing/PlayerFishListener.java b/src/main/java/fr/openmc/core/features/dream/mecanism/cloudfishing/PlayerFishListener.java index 6b9d32da6..766f77cb6 100644 --- a/src/main/java/fr/openmc/core/features/dream/mecanism/cloudfishing/PlayerFishListener.java +++ b/src/main/java/fr/openmc/core/features/dream/mecanism/cloudfishing/PlayerFishListener.java @@ -66,7 +66,7 @@ public void run() { CustomLootTable lootTable = CloudFishingManager.FISHING_LOOT_TABLE; if (lootTable == null) return; - List rewards = lootTable.rollLoots(player); + List rewards = lootTable.rollLoots(player).loots(); for (CustomLoot loot : rewards) { if (!(loot instanceof ItemStack item)) continue; diff --git a/src/main/java/fr/openmc/core/features/dream/registries/items/tools/MetalDetector.java b/src/main/java/fr/openmc/core/features/dream/registries/items/tools/MetalDetector.java index f50848148..470d8ec72 100644 --- a/src/main/java/fr/openmc/core/features/dream/registries/items/tools/MetalDetector.java +++ b/src/main/java/fr/openmc/core/features/dream/registries/items/tools/MetalDetector.java @@ -69,7 +69,7 @@ public void onRightClick(Player player, PlayerInteractEvent event) { CustomLootTable lootTable = MetalDetectorManager.METAL_DETECTOR_LOOT_TABLE; if (lootTable == null) return; - List rewards = lootTable.rollLoots(player); + List rewards = lootTable.rollLoots(player).loots(); for (CustomLoot loot : rewards) { if (!(loot instanceof ItemStack item)) continue; diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/AncientMonster.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/AncientMonster.java index 04a36e326..e4f0bf27c 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/AncientMonster.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/AncientMonster.java @@ -46,7 +46,7 @@ public AncientMonster(String id) { Monster.class, 3, 1, - OMCRegistry.CUSTOM_LOOT_TABLES.ANCIENT_MOB.rollLoots() + OMCRegistry.CUSTOM_LOOT_TABLES.ANCIENT_MOB.rollLoots().loots() ); } diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CorruptedMonster.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CorruptedMonster.java index 5c714f9fc..d1287067f 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CorruptedMonster.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CorruptedMonster.java @@ -45,7 +45,7 @@ public CorruptedMonster(String id) { Monster.class, 1, 1, - OMCRegistry.CUSTOM_LOOT_TABLES.CORRUPTED_MOB.rollLoots() + OMCRegistry.CUSTOM_LOOT_TABLES.CORRUPTED_MOB.rollLoots().loots() ); } diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CursedMonster.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CursedMonster.java index 23813545a..9c738c9d3 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CursedMonster.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/CursedMonster.java @@ -54,7 +54,7 @@ public CursedMonster(String id) { Monster.class, 1, 1, - OMCRegistry.CUSTOM_LOOT_TABLES.CURSED_MOB.rollLoots() + OMCRegistry.CUSTOM_LOOT_TABLES.CURSED_MOB.rollLoots().loots() ); } diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/EnragedMonster.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/EnragedMonster.java index 8b1b32a13..7a0a6b5f7 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/EnragedMonster.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/bloodynight/contents/mobs/bloodytypes/EnragedMonster.java @@ -46,7 +46,7 @@ public EnragedMonster(String id) { Monster.class, 1, 1, - OMCRegistry.CUSTOM_LOOT_TABLES.ENRAGED_MOB.rollLoots() + OMCRegistry.CUSTOM_LOOT_TABLES.ENRAGED_MOB.rollLoots().loots() ); } diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/GoldenCropsListener.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/GoldenCropsListener.java index ca4a54c0f..2d3e7746e 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/GoldenCropsListener.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/GoldenCropsListener.java @@ -26,7 +26,7 @@ import org.bukkit.event.block.BlockGrowEvent; import java.util.Collection; -import java.util.Set; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; import static fr.openmc.core.features.events.contents.dailyevents.contents.goldenharvest.AbondanceArmorManager.applyDoubleCropsChance; @@ -47,7 +47,7 @@ public void onCropBreak(BlockBreakEvent event) { ItemLoot itemLoot = GoldenHarvestManager.getGoldenCropsOnBreakMapping().get(keyBlock); if (itemLoot == null) return; - Set loots = itemLoot.run(event.getPlayer(), event.getBlock().getLocation()); + List loots = itemLoot.run(event.getPlayer(), event.getBlock().getLocation()).loots(); if (loots.isEmpty()) return; giveRewards(itemLoot, event.getPlayer(), event.getBlock()); @@ -100,7 +100,7 @@ public void onCropFullyGrowed(BlockGrowEvent event) { } private void giveRewards(ItemLoot itemLoot, Player player, Block block) { - Collection loots = applyDoubleCropsChance(player, itemLoot.run(player, block.getLocation())); + Collection loots = applyDoubleCropsChance(player, itemLoot.run(player, block.getLocation()).loots()); if (loots.isEmpty()) return; player.playSound(player.getLocation(), Sound.ITEM_GOLDEN_DANDELION_USE, 1, 0.3f); diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/PlantationLootListener.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/PlantationLootListener.java index 739e6bd23..f3e317666 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/PlantationLootListener.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/goldenharvest/listeners/PlantationLootListener.java @@ -36,7 +36,7 @@ public void onCropBreak(BlockBreakEvent event) { if (!(event.getBlock().getBlockData() instanceof Ageable ageable)) return; if (ageable.getAge() != ageable.getMaximumAge()) return; - List loots = OMCRegistry.CUSTOM_LOOT_TABLES.CROPS.rollLootsWithoutGuarantee(event.getPlayer()); + List loots = OMCRegistry.CUSTOM_LOOT_TABLES.CROPS.rollLootsWithoutGuarantee(event.getPlayer()).loots(); if (loots.isEmpty()) return; diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/listeners/PlayerFishListener.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/listeners/PlayerFishListener.java index 71bc55016..c9fca61c8 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/listeners/PlayerFishListener.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/listeners/PlayerFishListener.java @@ -32,7 +32,6 @@ import java.util.Collection; import java.util.List; -import java.util.Set; public class PlayerFishListener implements Listener { @@ -54,7 +53,7 @@ public void onStartFishing(PlayerFishEvent event) { CustomLootTable fishingLootTable = OMCRegistry.CUSTOM_LOOT_TABLES.MIRACULOUS_FISHING; - List loots = fishingLootTable.rollLoots(); + List loots = fishingLootTable.rollLoots().loots(); List finalLoots = FishingAttributeManager.applyDoubleHookChance(player, loots); @@ -117,7 +116,7 @@ private void sendLoot(Player player, FishHook hook, Collection loots // * Si y'a des sous loots, alors on affiche les sous loots obtenu, // en modiant leur probabilité corresponde à la réalité if (loot instanceof TableLoot) { - Set subLoots = loot.run(player); + List subLoots = loot.run(player).loots(); for (CustomLoot subLoot : subLoots) { subLoot.setChance(loot.getChance() * subLoot.getChance()); diff --git a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/registry/SeaCreatureLoot.java b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/registry/SeaCreatureLoot.java index 4dc99262b..cce93aad9 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/registry/SeaCreatureLoot.java +++ b/src/main/java/fr/openmc/core/features/events/contents/dailyevents/contents/miraculousfishing/registry/SeaCreatureLoot.java @@ -1,6 +1,7 @@ package fr.openmc.core.features.events.contents.dailyevents.contents.miraculousfishing.registry; import fr.openmc.core.registry.items.CustomItem; +import fr.openmc.core.registry.loottable.LootReward; import fr.openmc.core.registry.loottable.loots.CustomLoot; import fr.openmc.core.registry.loottable.loots.RepresentedItem; import fr.openmc.core.registry.loottable.loots.menu.LootsInfoMenu; @@ -17,7 +18,6 @@ import org.bukkit.inventory.ItemStack; import java.util.Collections; -import java.util.Set; /** * Classe représentant le Loot étant une Sea Creature, donc un mob péchable @@ -77,9 +77,9 @@ public ItemStack getRepresentativeItem() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { // déjà lancé dans MiraculousFishingManager.simulateLaunchLoot - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } public void showLoot(Player player) { diff --git a/src/main/java/fr/openmc/core/registry/items/keys/KeyBlock.java b/src/main/java/fr/openmc/core/registry/items/keys/KeyBlock.java index 0061506b2..5e7ba7ded 100644 --- a/src/main/java/fr/openmc/core/registry/items/keys/KeyBlock.java +++ b/src/main/java/fr/openmc/core/registry/items/keys/KeyBlock.java @@ -4,8 +4,10 @@ import fr.openmc.core.OMCRegistry; import fr.openmc.core.registry.items.CustomItem; import lombok.Getter; +import net.kyori.adventure.text.Component; import org.bukkit.block.Block; import org.bukkit.block.BlockType; +import org.bukkit.inventory.ItemStack; public final class KeyBlock { @Getter @@ -52,11 +54,32 @@ public boolean isCustom() { return customBlock != null; } + public Component name() { + if (isVanilla()) return Component.translatable(blockType.translationKey()); + else { + CustomItem item = getCustomItem(); + ItemStack itemStack = item.getBest(); + if (itemStack.getItemMeta().hasItemName()) { + return itemStack.getItemMeta().itemName(); + } + return itemStack.displayName(); + } + } + + public boolean matches(Block block) { + if (block == null) return false; + + CustomBlock placed = CustomBlock.byAlreadyPlaced(block); + if (isCustom()) return customBlock.getNamespacedID().equals(placed.getNamespacedID()); + + return blockType == block.getType().asBlockType(); + } + @Override public boolean equals(Object object) { if (this == object) return true; if (!(object instanceof KeyBlock other)) return false; - return this.id.equals(other.id); + return id.equals(other.id); } @Override diff --git a/src/main/java/fr/openmc/core/registry/lootboxes/menu/LootboxOpenMenu.java b/src/main/java/fr/openmc/core/registry/lootboxes/menu/LootboxOpenMenu.java index 01f81eaef..1df44194f 100644 --- a/src/main/java/fr/openmc/core/registry/lootboxes/menu/LootboxOpenMenu.java +++ b/src/main/java/fr/openmc/core/registry/lootboxes/menu/LootboxOpenMenu.java @@ -108,7 +108,7 @@ public String getTexture() { return items; } - List weightedPool = box.getLootTable().generateWeightedPool(); + List weightedPool = box.getLootTable().generateWeightedPool().loots(); for (int i = 0; i < displaySlots.size(); i++) { int lootIndex = (itemOffset + i) % weightedPool.size(); CustomLoot loot = weightedPool.get(lootIndex); @@ -180,7 +180,7 @@ public void startAnimation() { winningLoot = box.getLootTable().selectRandomLoot(); - List weightedPool = box.getLootTable().generateWeightedPool(); + List weightedPool = box.getLootTable().generateWeightedPool().loots(); animationTask = new BukkitRunnable() { @Override diff --git a/src/main/java/fr/openmc/core/registry/loottable/CustomLootTable.java b/src/main/java/fr/openmc/core/registry/loottable/CustomLootTable.java index ef2321d28..cbc32dbc2 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/CustomLootTable.java +++ b/src/main/java/fr/openmc/core/registry/loottable/CustomLootTable.java @@ -29,17 +29,22 @@ public double getChanceOf(ItemStack item) { .sum(); } - public List rollLoots(Player receiver) { - List result = new ArrayList<>(); - - double totalChance = this.getLoots().stream() + public LootReward rollLoots(Player receiver) { + List guaranted = this.getLoots().stream() + .filter(loot -> loot.getChance() >= 1.0) + .toList(); + List result = new ArrayList<>(guaranted); + List remaining = new ArrayList<>(this.getLoots().stream() + .filter(loot -> loot.getChance() < 1.0).toList()); + + double totalChance = remaining.stream() .mapToDouble(CustomLoot::getChance) .sum(); double roll = Math.random() * totalChance; double sumChance = 0.0; - for (CustomLoot loot : this.getLoots()) { + for (CustomLoot loot : remaining) { sumChance += loot.getChance(); if (roll <= sumChance) { loot.run(receiver); @@ -54,20 +59,25 @@ public List rollLoots(Player receiver) { result.add(next); } - return result; + return LootReward.loots(result); } - public List rollLoots() { - List result = new ArrayList<>(); + public LootReward rollLoots() { + List guaranted = this.getLoots().stream() + .filter(loot -> loot.getChance() >= 1.0) + .toList(); + List result = new ArrayList<>(guaranted); + List remaining = new ArrayList<>(this.getLoots().stream() + .filter(loot -> loot.getChance() < 1.0).toList()); - double totalChance = this.getLoots().stream() + double totalChance = remaining.stream() .mapToDouble(CustomLoot::getChance) .sum(); double roll = Math.random() * totalChance; double sumChance = 0.0; - for (CustomLoot loot : this.getLoots()) { + for (CustomLoot loot : remaining) { sumChance += loot.getChance(); if (roll <= sumChance) { result.add(loot); @@ -80,20 +90,20 @@ public List rollLoots() { result.add(next); } - return result; + return LootReward.loots(result); } - public List rollLootsWithAmount(Player receiver, int amountRoll) { + public LootReward rollLootsWithAmount(Player receiver, int amountRoll) { List loot = new ArrayList<>(); for (int i = 0; i < amountRoll; i++) { - loot.addAll(rollLoots(receiver)); + loot.addAll(rollLoots(receiver).loots()); } - return loot; + return LootReward.loots(loot); } - public List rollLootsWithoutGuarantee(Player receiver) { + public LootReward rollLootsWithoutGuarantee(Player receiver) { List result = new ArrayList<>(); double roll = ThreadLocalRandom.current().nextDouble(); @@ -109,7 +119,7 @@ public List rollLootsWithoutGuarantee(Player receiver) { } } - return result; + return LootReward.loots(result); } public CustomLoot selectRandomLoot() { @@ -131,7 +141,7 @@ public CustomLoot selectRandomLoot() { return this.getLoots().stream().findFirst().orElse(null); } - public List generateWeightedPool() { + public LootReward generateWeightedPool() { List pool = new ArrayList<>(); for (CustomLoot loot : this.getLoots()) { int count = Math.max(1, (int) (loot.getChance() * 100 * 2)); @@ -140,7 +150,7 @@ public List generateWeightedPool() { } } Collections.shuffle(pool); - return pool; + return LootReward.loots(pool); } public void openMenu(Player player) { diff --git a/src/main/java/fr/openmc/core/registry/loottable/CustomLootTableRegistry.java b/src/main/java/fr/openmc/core/registry/loottable/CustomLootTableRegistry.java index 9f43a1592..5cc2abe7c 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/CustomLootTableRegistry.java +++ b/src/main/java/fr/openmc/core/registry/loottable/CustomLootTableRegistry.java @@ -6,6 +6,8 @@ import fr.openmc.core.features.bits.contents.loottables.MedievalLootTable; import fr.openmc.core.features.bits.contents.loottables.ModernLootTable; import fr.openmc.core.features.bits.contents.loottables.OfficeLootTable; +import fr.openmc.core.features.chatanimations.contents.challenge.ChallengeLootTable; +import fr.openmc.core.features.chatanimations.contents.quizz.QuizzLootTable; import fr.openmc.core.features.events.contents.dailyevents.contents.bloodynight.contents.loottable.VampireLootTable; import fr.openmc.core.features.events.contents.dailyevents.contents.bloodynight.contents.loottable.bloodymob.AncientMobLootTable; import fr.openmc.core.features.events.contents.dailyevents.contents.bloodynight.contents.loottable.bloodymob.CorruptedMobLootTable; @@ -47,6 +49,9 @@ public class CustomLootTableRegistry extends Registry i public final CustomLootTable OFFICE_BOX = register(new OfficeLootTable()); public final CustomLootTable MODERN_BOX = register(new ModernLootTable()); + public final CustomLootTable QUIZZ = register(new QuizzLootTable()); + public final CustomLootTable CHALLENGE = register(new ChallengeLootTable()); + @Override public String key(CustomLootTable registryObject) { return registryObject.getNamespace(); diff --git a/src/main/java/fr/openmc/core/registry/loottable/LootReward.java b/src/main/java/fr/openmc/core/registry/loottable/LootReward.java new file mode 100644 index 000000000..5e275b637 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/loottable/LootReward.java @@ -0,0 +1,33 @@ +package fr.openmc.core.registry.loottable; + +import fr.openmc.core.registry.loottable.loots.CustomLoot; +import net.kyori.adventure.text.Component; + +import java.util.Collection; +import java.util.List; + +public record LootReward(List loots) { + public static LootReward loots(Collection newLoots) { + return new LootReward(newLoots.stream().toList()); + } + + public LootReward copy() { + return new LootReward(List.copyOf(loots)); + } + + public Component buildComponent() { + Component component = null; + + for (CustomLoot loot : loots) { + Component lootComponent = loot.buildLootComponent(-1); + if (lootComponent == null) continue; + + if (component == null) + component = lootComponent; + else + component = component.appendNewline().append(lootComponent); + } + + return component; + } +} diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/CustomLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/CustomLoot.java index 55d6dae7c..3c603c97f 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/CustomLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/CustomLoot.java @@ -1,42 +1,48 @@ package fr.openmc.core.registry.loottable.loots; +import fr.openmc.core.registry.loottable.LootReward; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import java.util.Set; - public interface CustomLoot { Component getDisplayText(); double getChance(); void setChance(double chance); - Set run(Player receiver); + LootReward run(Player receiver); default ItemStack getRepresentativeItem() { - if (this instanceof RepresentedItem representedItem) { + if (this instanceof RepresentedItem representedItem) return representedItem.getRepresentativeItem(); - } return null; } + default Component buildLootComponent(int amount) { + if (this.getDisplayText() == null || this instanceof TableLoot) return null; + + Component base = Component.text(" - ", NamedTextColor.GRAY); + + if (amount != -1) + base = base.append(Component.text(amount + "x ")); + + return base.append(this.getDisplayText()) + .append(Component.text( + " (" + Math.round(this.getChance() * 100.0) + "% ★)", + NamedTextColor.AQUA + )); + } + /** * Envoie le message de loot contenant, le nom du loot et la chance du loot * @param player le joueur a qui envoyé le message * @param amount le nombre de loot */ default void sendLootMessage(Player player, int amount) { - Component base = Component.text(" - ", NamedTextColor.GRAY); - - if (amount != -1) - base = base.append(Component.text(amount + "x ")); - - if (this.getDisplayText() != null && - !(this instanceof TableLoot)) { - base = base.append(this.getDisplayText()) - .append(Component.text(" ("+ Math.round(this.getChance() * 100.0) +"% ★)", NamedTextColor.AQUA)); + Component component = buildLootComponent(amount); - player.sendMessage(base); + if (component != null) { + player.sendMessage(component); } } } \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/ItemLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/ItemLoot.java index 36b793b0c..9373a944b 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/ItemLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/ItemLoot.java @@ -1,6 +1,7 @@ package fr.openmc.core.registry.loottable.loots; import fr.openmc.core.registry.items.CustomItem; +import fr.openmc.core.registry.loottable.LootReward; import fr.openmc.core.utils.bukkit.ItemUtils; import lombok.Getter; import lombok.Setter; @@ -14,6 +15,7 @@ import java.util.Collections; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; @Getter @@ -21,6 +23,7 @@ public class ItemLoot implements CustomLoot, RepresentedItem { @Setter private double chance; private final Set items; + private Predicate predicateToLoot = null; private Supplier itemSupplier = null; private final ItemStack displayedItem; private final int minAmount; @@ -80,6 +83,7 @@ public ItemLoot(Material item, double chance, int amount) { amount, amount); } + public ItemLoot(CustomItem item, double chance, int amount) { if (item == null) { throw new IllegalArgumentException("CustomItem cannot be null"); @@ -91,6 +95,17 @@ public ItemLoot(CustomItem item, double chance, int amount) { amount); } + public ItemLoot(CustomItem item, Predicate predicate, double chance, int amount) { + if (item == null) throw new IllegalArgumentException("CustomItem cannot be null"); + this(Collections.singleton(item.getBest()), + null, + chance, + amount, + amount); + + this.predicateToLoot = predicate; + } + public ItemLoot(CustomItem item, double chance, int minAmount, int maxAmount) { if (item == null) { throw new IllegalArgumentException("CustomItem cannot be null"); @@ -176,7 +191,9 @@ public Component getSimpleText() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { + if (predicateToLoot != null && !predicateToLoot.test(receiver)) return null; + return runBase((item) -> { if (ItemUtils.hasEnoughSpace(receiver, item)) { receiver.getInventory().addItem(item); @@ -186,19 +203,21 @@ public Set run(Player receiver) { }); } - public Set run(Player receiver, Location location) { + public LootReward run(Player receiver, Location location) { + if (predicateToLoot != null && !predicateToLoot.test(receiver)) return null; + return runBase((item) -> receiver.getWorld().dropItemNaturally(location, item)); } - private Set runBase(Consumer consumer) { + private LootReward runBase(Consumer consumer) { if (itemSupplier != null) { ItemStack item = itemSupplier.get(); item.setAmount(this.getRandomAmount()); consumer.accept(item); - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } for (ItemStack lootItem : this.getItems()) { @@ -208,7 +227,7 @@ private Set runBase(Consumer consumer) { consumer.accept(item); } - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } @Override diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/LootboxLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/LootboxLoot.java index 3bcb37815..d0d61ca3c 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/LootboxLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/LootboxLoot.java @@ -1,6 +1,7 @@ package fr.openmc.core.registry.loottable.loots; import fr.openmc.core.registry.lootboxes.CustomLootbox; +import fr.openmc.core.registry.loottable.LootReward; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; @@ -8,7 +9,6 @@ import org.bukkit.inventory.ItemStack; import java.util.Collections; -import java.util.Set; @Getter public class LootboxLoot implements CustomLoot, RepresentedItem { @@ -27,9 +27,9 @@ public Component getDisplayText() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { lootbox.open(receiver); - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } @Override diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/MethodLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/MethodLoot.java index 877c69495..6357fd94e 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/MethodLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/MethodLoot.java @@ -1,5 +1,6 @@ package fr.openmc.core.registry.loottable.loots; +import fr.openmc.core.registry.loottable.LootReward; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; @@ -7,7 +8,6 @@ import org.bukkit.inventory.ItemStack; import java.util.Collections; -import java.util.Set; import java.util.function.Consumer; @Getter @@ -31,8 +31,8 @@ public Component getDisplayText() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { receiverAction.accept(receiver); - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } } diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/MoneyLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/MoneyLoot.java index 469ae692f..98fdc410b 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/MoneyLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/MoneyLoot.java @@ -2,6 +2,7 @@ import fr.openmc.core.OMCRegistry; import fr.openmc.core.features.economy.EconomyManager; +import fr.openmc.core.registry.loottable.LootReward; import fr.openmc.core.utils.RandomUtils; import lombok.Getter; import lombok.Setter; @@ -12,7 +13,6 @@ import org.bukkit.inventory.ItemStack; import java.util.Collections; -import java.util.Set; @Getter public class MoneyLoot implements CustomLoot, RepresentedItem { @@ -38,9 +38,9 @@ public Component getDisplayText() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { EconomyManager.addBalance(receiver.getUniqueId(), money); - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } @Override diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/TableLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/TableLoot.java index 983fd9dc6..80d0e628f 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/TableLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/TableLoot.java @@ -2,6 +2,7 @@ import fr.openmc.core.registry.items.CustomItem; import fr.openmc.core.registry.loottable.CustomLootTable; +import fr.openmc.core.registry.loottable.LootReward; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; @@ -9,8 +10,6 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import java.util.Set; - @Getter public class TableLoot implements CustomLoot, RepresentedItem { @Setter @@ -52,10 +51,10 @@ public Component getDisplayText() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { if (this.giveRewards) - return Set.copyOf(lootTable.rollLoots(receiver)); + return lootTable.rollLoots(receiver).copy(); else - return Set.copyOf(lootTable.rollLoots()); + return lootTable.rollLoots().copy(); } } \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/loottable/loots/XpLoot.java b/src/main/java/fr/openmc/core/registry/loottable/loots/XpLoot.java index d6521ccbe..1dd2a9f68 100644 --- a/src/main/java/fr/openmc/core/registry/loottable/loots/XpLoot.java +++ b/src/main/java/fr/openmc/core/registry/loottable/loots/XpLoot.java @@ -1,5 +1,6 @@ package fr.openmc.core.registry.loottable.loots; +import fr.openmc.core.registry.loottable.LootReward; import fr.openmc.core.utils.RandomUtils; import lombok.Getter; import lombok.Setter; @@ -11,7 +12,6 @@ import org.bukkit.inventory.ItemStack; import java.util.Collections; -import java.util.Set; @Getter public class XpLoot implements CustomLoot, RepresentedItem { @@ -35,9 +35,9 @@ public Component getDisplayText() { } @Override - public Set run(Player receiver) { + public LootReward run(Player receiver) { receiver.giveExp(amountExp); - return Collections.singleton(this); + return LootReward.loots(Collections.singleton(this)); } @Override diff --git a/src/main/java/fr/openmc/core/utils/RandomUtils.java b/src/main/java/fr/openmc/core/utils/RandomUtils.java index 6ba550f78..defaca97e 100644 --- a/src/main/java/fr/openmc/core/utils/RandomUtils.java +++ b/src/main/java/fr/openmc/core/utils/RandomUtils.java @@ -13,21 +13,28 @@ public class RandomUtils { * Retourne un double aléatoire entre min et max. */ public static double randomBetween(double min, double max) { - return min + random.nextDouble() * (max - min); + return ThreadLocalRandom.current().nextDouble(min, max + 1); } /** * Retourne un float aléatoire entre min et max. */ public static float randomBetween(float min, float max) { - return min + random.nextFloat() * (max - min); + return ThreadLocalRandom.current().nextFloat(min, max + 1); + } + + /** + * Retourne un long aléatoire entre min et max. + */ + public static long randomBetween(long min, long max) { + return ThreadLocalRandom.current().nextLong(min, max + 1); } /** * Retourne un int aléatoire entre min et max. */ public static int randomBetween(int min, int max) { - return min + random.nextInt((max - min) + 1); + return ThreadLocalRandom.current().nextInt(min, max + 1); } /** diff --git a/src/main/resources/translations/default/chatanimations.properties b/src/main/resources/translations/default/chatanimations.properties new file mode 100644 index 000000000..f78818124 --- /dev/null +++ b/src/main/resources/translations/default/chatanimations.properties @@ -0,0 +1,92 @@ +feature.chatanimations.quizz.announce= \ +
\ +
Nouvelle Question :\ +
%1$s\ +
\ +
Vous avez %2$s secondes \ +
Le premier à répondre gagne ! \ +
\ +
+ +feature.chatanimations.winner= \ +
\ +
%1$s a remporté le %2$s\ +
%3$s\ +
\ +
Cette personne a gagné : \ +
%4$s\ +
\ +
+ +feature.chatanimations.no_winner= \ +
\ +
Aucune personne a remporté le %1$s\ +
%2$s\ +
\ +
+ +feature.chatanimations.quizz.table.name=Récompenses des quizzs +feature.chatanimations.quizz.name=Quizz +feature.chatanimations.quizz.result=La réponse était %1$s ! + +quizz.tech.01=Quel langage de programmation a été créé par Guido van Rossum ? +quizz.tech.02=Que signifie le acronyme HTML ? +quizz.tech.03=Quelle entreprise a créé le système d'exploitation Windows ? +quizz.tech.04=Quel est le nom du créateur du noyau Linux ? +quizz.tech.05=Combien de bits y a-t-il dans un octet ? +quizz.tech.06=Quel langage de programmation a pour symbole un serpent ? +quizz.tech.07=Quel protocole permet de naviguer sur le web de façon sécurisée ? +quizz.tech.08=Qui a cofondé Facebook ? +quizz.tech.09=Quel studio a développé Minecraft à l'origine ? +quizz.tech.10=Quel format d'image est réputé pour sa compression sans perte et sa transparence ? +quizz.tech.11=Quel est le nom du navigateur web développé par Google ? +quizz.tech.12=Que signifie l'acronyme RAM ? + +quizz.general.01=Quelle est la capitale de la France ? +quizz.general.02=Combien y a-t-il de continents sur Terre ? +quizz.general.03=Qui a peint la Joconde ? +quizz.general.04=Quel est le plus grand océan du monde ? +quizz.general.05=En quelle année a eu lieu la Révolution française ? +quizz.general.06=Quel est le plus haut sommet du monde ? +quizz.general.07=Combien de joueurs une équipe de football aligne-t-elle sur le terrain ? +quizz.general.08=Quelle planète est surnommée la planète rouge ? +quizz.general.09=Qui a écrit le roman Les Misérables ? +quizz.general.10=Quel est l'animal terrestre le plus rapide du monde ? +quizz.general.11=Quelle est la monnaie utilisée au Japon ? +quizz.general.12=Combien de jours compte une année bissextile ? +quizz.general.13=Quel pays a la forme d'une botte sur la carte du monde ? +quizz.general.14=Quel compositeur allemand a écrit la 9e Symphonie ? +quizz.general.15=Quel est le plus long fleuve du monde ? +quizz.general.16=Quel est le symbole chimique de l'or ? +quizz.general.17=Quelle est la plus grande île du monde ? +quizz.general.18=Qui a été le premier homme à marcher sur la Lune ? +quizz.general.19=Quel est le sport national du Japon ? +quizz.general.20=Combien de temps met la lumière du Soleil pour atteindre la Terre, en minutes ? +quizz.general.21=Pendant un orage, par combien faut-il diviser le nombre de secondes entre l'éclair et le tonnerre pour connaître, en kilomètres, la distance approximative de l'orage ? + +quizz.server.01=Quel est le nom du compte GitHub d'Aywen ? +quizz.server.02=Qui a créé Minecraft ? +quizz.server.03=Qui a créé ce serveur Minecraft ? +quizz.server.04=Quel était le premier pseudo Minecraft d'Aywen ? +quizz.server.05=En quelle année a été créé OpenMC ? + +feature.chatanimations.challenge.announce= \ +
\ +
Defi :\ +
%1$s\ +
\ +
Vous avez %2$s secondes \ +
Le premier qui fait l'action gagne \ +
\ +
+feature.chatanimation.challenge.name=Défi +feature.chatanimations.challenge.result=Il fallait %1$s ! +feature.chatanimations.challenge.table.name=Récompenses des défis + +feature.chatanimations.challenge.mine_blocks.name=Miner %1$s %2$s +feature.chatanimations.challenge.jump.name=Sauter %1$s fois +feature.chatanimations.challenge.kill_entity.name=Tuer %1$s %2$s +feature.chatanimations.challenge.craft_item.name=Crafter %1$s %2$s +feature.chatanimations.challenge.fishing.name=Pêcher %1$s fois +feature.chatanimations.challenge.place_blocks.name=Poser %1$s %2$s +feature.chatanimations.challenge.walk_distance.name=Parcourir %1$s mètres diff --git a/src/main/resources/translations/default/credits.properties b/src/main/resources/translations/default/credits.properties index 1636d583d..c62d6509c 100644 --- a/src/main/resources/translations/default/credits.properties +++ b/src/main/resources/translations/default/credits.properties @@ -7,6 +7,7 @@ feature.credits.feature.adminshop=L'Adminshop feature.credits.feature.animations=Les Animations feature.credits.feature.bits=L'Economie Contributeur feature.credits.feature.cube=Le Cube +feature.credits.feature.chat_animation=Les Animations du Chat feature.credits.feature.city=Les Villes feature.credits.feature.dream=La Dimension des Rêves feature.credits.feature.dream_milestone=Le Milestone des Rêves diff --git a/src/main/resources/translations/en_GB/chatanimations.properties b/src/main/resources/translations/en_GB/chatanimations.properties new file mode 100644 index 000000000..8a4d133d1 --- /dev/null +++ b/src/main/resources/translations/en_GB/chatanimations.properties @@ -0,0 +1,92 @@ +feature.chatanimations.quizz.announce= \ +
\ +
New Question:\ +
%1$s\ +
\ +
You have %2$s seconds \ +
The first to answer wins! \ +
\ +
+ +feature.chatanimations.winner= \ +
\ +
%1$s won the %2$s\ +
%3$s\ +
\ +
This person won: \ +
%4$s\ +
\ +
+ +feature.chatanimations.no_winner= \ +
\ +
No one won the %1$s\ +
%2$s\ +
\ +
+ +feature.chatanimations.quizz.table.name=Quiz Rewards +feature.chatanimations.quizz.name=Quiz +feature.chatanimations.quizz.result=The answer was %1$s ! + +quizz.tech.01=Which programming language was created by Guido van Rossum? +quizz.tech.02=What does the acronym HTML stand for? +quizz.tech.03=Which company created the Windows operating system? +quizz.tech.04=What is the name of the creator of the Linux kernel? +quizz.tech.05=How many bits are there in a byte? +quizz.tech.06=Which programming language has a snake as its symbol? +quizz.tech.07=Which protocol allows you to browse the web securely? +quizz.tech.08=Who co-founded Facebook? +quizz.tech.09=Which studio originally developed Minecraft? +quizz.tech.10=Which image format is known for its lossless compression and transparency? +quizz.tech.11=What is the name of the web browser developed by Google? +quizz.tech.12=What does the acronym RAM stand for? + +quizz.general.01=What is the capital of France? +quizz.general.02=How many continents are there on Earth? +quizz.general.03=Who painted the Mona Lisa? +quizz.general.04=What is the largest ocean in the world? +quizz.general.05=In which year did the French Revolution take place? +quizz.general.06=What is the highest mountain in the world? +quizz.general.07=How many players does a football team field on the pitch? +quizz.general.08=Which planet is known as the Red Planet? +quizz.general.09=Who wrote the novel Les Misérables? +quizz.general.10=What is the fastest land animal in the world? +quizz.general.11=What currency is used in Japan? +quizz.general.12=How many days are there in a leap year? +quizz.general.13=Which country is shaped like a boot on the map? +quizz.general.14=Which German composer wrote the 9th Symphony? +quizz.general.15=What is the longest river in the world? +quizz.general.16=What is the chemical symbol for gold? +quizz.general.17=What is the largest island in the world? +quizz.general.18=Who was the first man to walk on the Moon? +quizz.general.19=What is the national sport of Japan? +quizz.general.20=How many minutes does it take for sunlight to reach Earth? +quizz.general.21=During a thunderstorm, by what number should you divide the number of seconds between lightning and thunder to determine the approximate distance of the storm in kilometers? + +quizz.server.01=What is Aywen's GitHub account name? +quizz.server.02=Who created Minecraft? +quizz.server.03=Who created this Minecraft server? +quizz.server.04=What was Aywen's first Minecraft username? +quizz.server.05=In what year was OpenMC created? + +feature.chatanimations.challenge.announce= \ +
\ +
Challenge:\ +
%1$s\ +
\ +
You have %2$s seconds \ +
The first to perform the action wins \ +
\ +
+feature.chatanimation.challenge.name=Challenge +feature.chatanimations.challenge.result=You had to %1$s ! +feature.chatanimations.challenge.table.name=Challenge Rewards + +feature.chatanimations.challenge.mine_blocks.name=Mine %1$s %2$s +feature.chatanimations.challenge.jump.name=Jump %1$s times +feature.chatanimations.challenge.kill_entity.name=Kill %1$s %2$s +feature.chatanimations.challenge.craft_item.name=Craft %1$s %2$s +feature.chatanimations.challenge.fishing.name=Fish %1$s times +feature.chatanimations.challenge.place_blocks.name=Place %1$s %2$s +feature.chatanimations.challenge.walk_distance.name=Travel %1$s meters \ No newline at end of file diff --git a/src/main/resources/translations/en_GB/credits.properties b/src/main/resources/translations/en_GB/credits.properties index e8d86e686..4d2a97c71 100644 --- a/src/main/resources/translations/en_GB/credits.properties +++ b/src/main/resources/translations/en_GB/credits.properties @@ -7,6 +7,7 @@ feature.credits.feature.adminshop=Adminshop feature.credits.feature.animations=Animations feature.credits.feature.bits=Contributor Economy feature.credits.feature.cube=The Cube +feature.credits.feature.chat_animation=Chat Animations feature.credits.feature.city=Cities feature.credits.feature.dream=The Dimension of Dreams feature.credits.feature.dream_milestone=The Milestone of Dreams diff --git a/src/main/resources/translations/en_US/chatanimations.properties b/src/main/resources/translations/en_US/chatanimations.properties new file mode 100644 index 000000000..8a4d133d1 --- /dev/null +++ b/src/main/resources/translations/en_US/chatanimations.properties @@ -0,0 +1,92 @@ +feature.chatanimations.quizz.announce= \ +
\ +
New Question:\ +
%1$s\ +
\ +
You have %2$s seconds \ +
The first to answer wins! \ +
\ +
+ +feature.chatanimations.winner= \ +
\ +
%1$s won the %2$s\ +
%3$s\ +
\ +
This person won: \ +
%4$s\ +
\ +
+ +feature.chatanimations.no_winner= \ +
\ +
No one won the %1$s\ +
%2$s\ +
\ +
+ +feature.chatanimations.quizz.table.name=Quiz Rewards +feature.chatanimations.quizz.name=Quiz +feature.chatanimations.quizz.result=The answer was %1$s ! + +quizz.tech.01=Which programming language was created by Guido van Rossum? +quizz.tech.02=What does the acronym HTML stand for? +quizz.tech.03=Which company created the Windows operating system? +quizz.tech.04=What is the name of the creator of the Linux kernel? +quizz.tech.05=How many bits are there in a byte? +quizz.tech.06=Which programming language has a snake as its symbol? +quizz.tech.07=Which protocol allows you to browse the web securely? +quizz.tech.08=Who co-founded Facebook? +quizz.tech.09=Which studio originally developed Minecraft? +quizz.tech.10=Which image format is known for its lossless compression and transparency? +quizz.tech.11=What is the name of the web browser developed by Google? +quizz.tech.12=What does the acronym RAM stand for? + +quizz.general.01=What is the capital of France? +quizz.general.02=How many continents are there on Earth? +quizz.general.03=Who painted the Mona Lisa? +quizz.general.04=What is the largest ocean in the world? +quizz.general.05=In which year did the French Revolution take place? +quizz.general.06=What is the highest mountain in the world? +quizz.general.07=How many players does a football team field on the pitch? +quizz.general.08=Which planet is known as the Red Planet? +quizz.general.09=Who wrote the novel Les Misérables? +quizz.general.10=What is the fastest land animal in the world? +quizz.general.11=What currency is used in Japan? +quizz.general.12=How many days are there in a leap year? +quizz.general.13=Which country is shaped like a boot on the map? +quizz.general.14=Which German composer wrote the 9th Symphony? +quizz.general.15=What is the longest river in the world? +quizz.general.16=What is the chemical symbol for gold? +quizz.general.17=What is the largest island in the world? +quizz.general.18=Who was the first man to walk on the Moon? +quizz.general.19=What is the national sport of Japan? +quizz.general.20=How many minutes does it take for sunlight to reach Earth? +quizz.general.21=During a thunderstorm, by what number should you divide the number of seconds between lightning and thunder to determine the approximate distance of the storm in kilometers? + +quizz.server.01=What is Aywen's GitHub account name? +quizz.server.02=Who created Minecraft? +quizz.server.03=Who created this Minecraft server? +quizz.server.04=What was Aywen's first Minecraft username? +quizz.server.05=In what year was OpenMC created? + +feature.chatanimations.challenge.announce= \ +
\ +
Challenge:\ +
%1$s\ +
\ +
You have %2$s seconds \ +
The first to perform the action wins \ +
\ +
+feature.chatanimation.challenge.name=Challenge +feature.chatanimations.challenge.result=You had to %1$s ! +feature.chatanimations.challenge.table.name=Challenge Rewards + +feature.chatanimations.challenge.mine_blocks.name=Mine %1$s %2$s +feature.chatanimations.challenge.jump.name=Jump %1$s times +feature.chatanimations.challenge.kill_entity.name=Kill %1$s %2$s +feature.chatanimations.challenge.craft_item.name=Craft %1$s %2$s +feature.chatanimations.challenge.fishing.name=Fish %1$s times +feature.chatanimations.challenge.place_blocks.name=Place %1$s %2$s +feature.chatanimations.challenge.walk_distance.name=Travel %1$s meters \ No newline at end of file diff --git a/src/main/resources/translations/en_US/credits.properties b/src/main/resources/translations/en_US/credits.properties index e8d86e686..4d2a97c71 100644 --- a/src/main/resources/translations/en_US/credits.properties +++ b/src/main/resources/translations/en_US/credits.properties @@ -7,6 +7,7 @@ feature.credits.feature.adminshop=Adminshop feature.credits.feature.animations=Animations feature.credits.feature.bits=Contributor Economy feature.credits.feature.cube=The Cube +feature.credits.feature.chat_animation=Chat Animations feature.credits.feature.city=Cities feature.credits.feature.dream=The Dimension of Dreams feature.credits.feature.dream_milestone=The Milestone of Dreams