diff --git a/src/main/java/fr/openmc/core/OMCPlugin.java b/src/main/java/fr/openmc/core/OMCPlugin.java index a8ef81f23..1ed3cf097 100644 --- a/src/main/java/fr/openmc/core/OMCPlugin.java +++ b/src/main/java/fr/openmc/core/OMCPlugin.java @@ -35,7 +35,7 @@ import fr.openmc.core.features.economy.TransactionsManager; import fr.openmc.core.features.events.EventsManager; import fr.openmc.core.features.events.contents.dailyevents.DailyEventsManager; -import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.pumpkin.managers.HalloweenManager; import fr.openmc.core.features.events.contents.weeklyevents.WeeklyEventsManager; import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.ContestParticlesUtils; import fr.openmc.core.features.events.contents.weeklyevents.contents.contest.managers.ContestManager; @@ -118,6 +118,7 @@ public class OMCPlugin extends JavaPlugin { MailboxManager::new, DiscordLinkManager::new, ProfileManager::new, + () -> new fr.openmc.core.features.events.contents.halloween.festival.HalloweenManager(), () -> new ElevatorManager(), () -> new CorpseManager(), QuestsManager::new, diff --git a/src/main/java/fr/openmc/core/OMCRegistry.java b/src/main/java/fr/openmc/core/OMCRegistry.java index 51a815aed..1ba2f3918 100644 --- a/src/main/java/fr/openmc/core/OMCRegistry.java +++ b/src/main/java/fr/openmc/core/OMCRegistry.java @@ -14,6 +14,7 @@ import fr.openmc.core.registry.loottable.CustomLootTableRegistry; import fr.openmc.core.registry.mobs.CustomMobRegistry; import fr.openmc.core.registry.regions.CustomRegionRegistry; +import fr.openmc.core.registry.structures.CustomStructureRegistry; import fr.openmc.riftengine.api.registry.scanner.ScannerRegistry; import fr.openmc.core.registry.worldtemplates.WorldTemplateRegistry; import io.papermc.paper.plugin.bootstrap.BootstrapContext; @@ -32,6 +33,7 @@ public final class OMCRegistry { public static CustomLootTableRegistry CUSTOM_LOOT_TABLES; public static CustomAmbientRegistry CUSTOM_AMBIENTS; public static CustomLootboxRegistry CUSTOM_LOOTBOXES; + public static CustomStructureRegistry CUSTOM_STRUCTURES; public static CustomRegionRegistry CUSTOM_REGIONS; public static WorldTemplateRegistry WORLD_TEMPLATES; @@ -62,6 +64,8 @@ public final class OMCRegistry { RegistryLoadingType.AFTER_IA), new RegistryContext(() -> CUSTOM_MOBS = new CustomMobRegistry(), RegistryLoadingType.AFTER_IA), + new RegistryContext(() -> CUSTOM_STRUCTURES = new CustomStructureRegistry(), + RegistryLoadingType.RUNTIME), new RegistryContext(() -> WORLD_TEMPLATES = new WorldTemplateRegistry(), RegistryLoadingType.BOOTSTRAP, RegistryLoadingType.RUNTIME), new RegistryContext(() -> WEEKLY_EVENTS = new WeeklyEventsRegistry(), 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 f953efcde..f876cad0c 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 @@ -12,7 +12,7 @@ import fr.openmc.core.features.corpse.npc.CorpseNPCManager; import fr.openmc.core.features.displays.scoreboards.BaseScoreboard; import fr.openmc.core.features.economy.EconomyManager; -import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.pumpkin.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; diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/commands/HalloweenCommands.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/commands/HalloweenCommands.java deleted file mode 100644 index 11bde75c0..000000000 --- a/src/main/java/fr/openmc/core/features/events/contents/halloween/commands/HalloweenCommands.java +++ /dev/null @@ -1,15 +0,0 @@ -package fr.openmc.core.features.events.contents.halloween.commands; - -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; - -@Command("halloween") -@CommandPermission("omc.admins.commands.halloween") -public class HalloweenCommands { - @Subcommand("end") - public void endHalloweenCommand() { - HalloweenManager.endEvent(); - } -} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/CandyItem.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/CandyItem.java new file mode 100644 index 000000000..341a7d473 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/CandyItem.java @@ -0,0 +1,37 @@ +package fr.openmc.core.features.events.contents.halloween.festival; + +import fr.openmc.core.registry.items.CustomItem; +import fr.openmc.core.registry.items.CustomItemMeta; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.jspecify.annotations.NonNull; + +import java.util.HashMap; +import java.util.Map; + +public class CandyItem extends CustomItem { + + @Getter + public int diabetesIndex; + public Material baseItem; + + public static final Map candies = new HashMap<>(); + + public CandyItem(String candyID , int diabetesIndex, Material baseItem) { + super(new CustomItemMeta(candyID)); + this.diabetesIndex = diabetesIndex; + this.baseItem = baseItem; + candies.put(candyID, this); + } + + @Override + public @NonNull ItemStack getVanilla() { + ItemStack item = new ItemStack(baseItem); + item.editMeta(meta -> meta.itemName( + TranslationManager.translation("feature." + super.getId().replace(":", ".") + ".name") + )); + return item; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/HalloweenManager.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/HalloweenManager.java new file mode 100644 index 000000000..844ce2b2d --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/HalloweenManager.java @@ -0,0 +1,140 @@ +package fr.openmc.core.features.events.contents.halloween.festival; + +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.OMCRegistry; +import fr.openmc.core.bootstrap.features.Feature; +import fr.openmc.core.bootstrap.features.types.HasCommands; +import fr.openmc.core.bootstrap.features.types.HasDatabase; +import fr.openmc.core.bootstrap.features.types.HasListeners; +import fr.openmc.core.bootstrap.features.types.LoadAfterItemsAdder; +import fr.openmc.core.bootstrap.integration.OMCLogger; +import fr.openmc.core.bootstrap.listeners.ListenerFactory; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement.Floors; +import fr.openmc.core.features.events.contents.halloween.festival.listeners.CandyListener; +import fr.openmc.core.features.events.contents.halloween.festival.model.HalloweenDB; +import fr.openmc.core.features.events.contents.halloween.festival.shop.WitchShopManager; +import lombok.Getter; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.sql.SQLException; +import java.util.*; + +public class HalloweenManager extends Feature implements LoadAfterItemsAdder, HasDatabase, HasListeners, HasCommands { + + //TODO quand acheter truc dans shop, augmenté avancement bonbons pour débloqué les étages, quand tous les étages débloqué, faire obtenir des récompenses a tous les joueurs ayant contribué + + @Getter + private static HalloweenDB halloweenDB; + + private static Dao halloweenDao; + + public static List unlockedFloors; + + private static File witchHouseSpawnFile; + private static FileConfiguration witchHouseSpawnConfig; + private static Location pasteLocation; + + @Override + public void init() { + witchHouseSpawnFile = new File(OMCPlugin.getInstance().getDataFolder() + "/data", "witch_house_spawn.yml"); + loadWitchHouseSpawnConfig(); + + halloweenDB = loadEvent(); + unlockedFloors = halloweenDB.getUnlockedRooms(); + WitchShopManager.init(); + } + + @Override + public void initDB(ConnectionSource connectionSource) throws SQLException { + TableUtils.createTableIfNotExists(connectionSource, HalloweenDB.class); + halloweenDao = DaoManager.createDao(connectionSource, HalloweenDB.class); + } + + @Override + protected void save() { + saveEvent(); + } + + public static HalloweenDB loadEvent() { + try { + HalloweenDB halloweenDB = halloweenDao.queryForFirst(); + if (halloweenDB == null) + halloweenDB = new HalloweenDB(0, List.of(Floors.FLOOR_1.name()), false); + return halloweenDB; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private static void loadWitchHouseSpawnConfig() { + if(!witchHouseSpawnFile.exists()) { + witchHouseSpawnFile.getParentFile().mkdirs(); + OMCPlugin.getInstance().saveResource("data/witch_house_spawn.yml", false); + } + + witchHouseSpawnConfig = YamlConfiguration.loadConfiguration(witchHouseSpawnFile); + loadPasteLocation(); + } + + private static void loadPasteLocation() { + if (witchHouseSpawnConfig.contains("spawn")) { + World world = OMCPlugin.getInstance().getServer().getWorld(witchHouseSpawnConfig.getString("spawn.world", "world")); + double x = witchHouseSpawnConfig.getDouble("spawn.x", 0.0); + double y = witchHouseSpawnConfig.getDouble("spawn.Y", 0.0); + double z = witchHouseSpawnConfig.getDouble("spawn.z", 0.0); + + pasteLocation = new Location( + world, + x, + y, + z + ); + } + } + + private static void saveEvent() { + try { + halloweenDao.createOrUpdate(halloweenDB); + } catch (Exception e) { + OMCLogger.error("Impossible de sauvegarder l'event d'halloween pendant l'arret.", e); + } + } + + public static boolean hasUnlock(Floors floor) { + return unlockedFloors.contains(floor.name()); + } + + public static boolean unlockNewRoom(Floors floor) { + return unlockedFloors.add(floor.name()); + } + + public static void placeWitchShop() { + OMCRegistry.CUSTOM_STRUCTURES.WITCH_SHOP.place(pasteLocation); + } + + public static void removeWitchShop() { + OMCRegistry.CUSTOM_STRUCTURES.WITCH_SHOP.removeAll(); + } + + @Override + public Set getListeners() { + return Set.of( + CandyListener::new + ); + } + + @Override + public Set getCommands() { + return Set.of( + + ); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/listeners/CandyListener.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/listeners/CandyListener.java new file mode 100644 index 000000000..74e12212b --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/listeners/CandyListener.java @@ -0,0 +1,45 @@ +package fr.openmc.core.features.events.contents.halloween.festival.listeners; + +import dev.lone.itemsadder.api.CustomStack; +import fr.openmc.core.features.events.contents.halloween.festival.CandyItem; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class CandyListener implements Listener { + + private final Map sugarIndex = new HashMap<>(); + + @EventHandler + public void onCandyEat(PlayerItemConsumeEvent event) { + Player player = event.getPlayer(); + ItemStack item = event.getItem(); + + CustomStack stack = CustomStack.byItemStack(item); + + if (stack == null) return; + + if (!CandyItem.candies.containsKey(stack.getId())) return; + + CandyItem candy = CandyItem.candies.get(stack.getId()); + + UUID uuid = player.getUniqueId(); + + int sugarIdx = sugarIndex.computeIfAbsent(uuid, key -> 0); + + sugarIdx += candy.getDiabetesIndex(); + + if (sugarIdx >= 20) { + //TODO faire un truc + sugarIndex.put(uuid, 0); + } else + sugarIndex.put(uuid, sugarIdx); + } + +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/listeners/WitchHouseListener.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/listeners/WitchHouseListener.java new file mode 100644 index 000000000..66921e57a --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/listeners/WitchHouseListener.java @@ -0,0 +1,56 @@ +package fr.openmc.core.features.events.contents.halloween.festival.listeners; + +import fr.openmc.core.commands.utils.SpawnManager; +import fr.openmc.core.features.events.contents.halloween.festival.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.DimensionGenerator; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.WitchHouseManager; +import fr.openmc.core.registry.regions.CustomRegion; +import fr.openmc.core.registry.regions.events.EntityCustomRegionEnterEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerJoinEvent; + +public class WitchHouseListener implements Listener { + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + if (!player.getWorld().equals(DimensionGenerator.HALLOWEEN_WORLD)) return; + + player.teleport(SpawnManager.getSpawnLocation()); + // TODO message + } + + @EventHandler + public void onWorldChange(PlayerChangedWorldEvent event) { + Player player = event.getPlayer(); + + if (HalloweenManager.getHalloweenDB().isActive()) { + if (event.getFrom().equals(DimensionGenerator.HALLOWEEN_WORLD)) + WitchHouseManager.getWitchHouseAmbient().reset(player); + + if (player.getWorld().equals(DimensionGenerator.HALLOWEEN_WORLD)) + WitchHouseManager.getWitchHouseAmbient().apply(player); + } else { + if (player.isOp()) return; + + if (player.getWorld().equals(DimensionGenerator.HALLOWEEN_WORLD)) { + player.teleport(SpawnManager.getSpawnLocation()); + // TODO message + } + } + } + + @EventHandler + public void onHouseRegionEnter(EntityCustomRegionEnterEvent event) { + CustomRegion region = event.getRegion(); + + if (!WitchHouseManager.enabledRegions.contains(region)) return; + + if (event.getEntity() instanceof Player player) + WitchHouseManager.repulsePlayer(region, player, 0.3); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/model/HalloweenDB.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/model/HalloweenDB.java new file mode 100644 index 000000000..f4af72b57 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/model/HalloweenDB.java @@ -0,0 +1,50 @@ +package fr.openmc.core.features.events.contents.halloween.festival.model; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@DatabaseTable(tableName = "halloween_season_event_data") +public class HalloweenDB { + + @DatabaseField(columnName = "advancement") + private int currentAdvancement; + + @DatabaseField(columnName = "unlocked_room") + private String unlockedRooms; + + @DatabaseField(columnName = "is_active") + private boolean active; + + private static final Gson GSON = new Gson(); + + public HalloweenDB() { + } + + public HalloweenDB(int currentAdvancement, List unlockedRooms, boolean active) { + this.currentAdvancement = currentAdvancement; + this.unlockedRooms = GSON.toJson(unlockedRooms); + this.active = active; + } + + public void setUnlockedRooms(List unlockedRooms) { + this.unlockedRooms = GSON.toJson(unlockedRooms); + } + + public List getUnlockedRooms() { + if (unlockedRooms == null || unlockedRooms.isBlank()) { + return new ArrayList<>(); + } + + return GSON.fromJson( + unlockedRooms, + new TypeToken>() {}.getType() + ); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/ShopMoney.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/ShopMoney.java new file mode 100644 index 000000000..11a18206e --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/ShopMoney.java @@ -0,0 +1,24 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.registry.items.CustomItem; +import lombok.Getter; + +@Getter +public enum ShopMoney { + //TODO rajouter les crafts des bonbons et les autre dans le shopMoney + RED_CANDY(WitchShopManager.getMoneyItemIcon("candy_brown"), OMCRegistry.CUSTOM_ITEMS.CANDY_RED), + YELLOW_CANDY(WitchShopManager.getMoneyItemIcon("candy_brown"), OMCRegistry.CUSTOM_ITEMS.CANDY_YELLOW), + GREEN_CANDY(WitchShopManager.getMoneyItemIcon("candy_brown"), OMCRegistry.CUSTOM_ITEMS.CANDY_GREEN), + BLUE_CANDY(WitchShopManager.getMoneyItemIcon("candy_brown"), OMCRegistry.CUSTOM_ITEMS.CANDY_BLUE), + MONSTER_CANDY(WitchShopManager.getMoneyItemIcon("candy_brown"), OMCRegistry.CUSTOM_ITEMS.CANDY_BROWN), + ; + + private final String icon; + private final CustomItem item; + + ShopMoney(String icon, CustomItem item) { + this.icon = icon; + this.item = item; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/WitchShopItem.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/WitchShopItem.java new file mode 100644 index 000000000..a56308d15 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/WitchShopItem.java @@ -0,0 +1,38 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop; + +import fr.openmc.core.features.events.contents.halloween.festival.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement.Floors; +import fr.openmc.core.registry.items.CustomItem; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +public class WitchShopItem { + + @Getter public final Floors advancements; + @Getter public final CustomItem item; + public final Component name; + @Getter public final ShopMoney shopMoney; + @Getter public final int price; + + + public WitchShopItem(Floors advancements, CustomItem item, Component name, ShopMoney moneyItem, int price) { + this.advancements = advancements; + this.item = item; + this.name = name; + this.shopMoney = moneyItem; + this.price = price; + } + public WitchShopItem(Floors advancements, CustomItem item, Component name, int price) { + this(advancements, item, name, ShopMoney.MONSTER_CANDY, price); + } + + public WitchShopItem(Floors advancements, CustomItem item, int price) { + this(advancements, item, item.getBest().displayName(), price); + } + + public Component getName() { + if (HalloweenManager.hasUnlock(advancements)) + return name; + return Component.text("§knotunlockyet"); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/WitchShopManager.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/WitchShopManager.java new file mode 100644 index 000000000..6277c97cb --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/WitchShopManager.java @@ -0,0 +1,156 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop; + +import dev.lone.itemsadder.api.FontImages.FontImageWrapper; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.adminshop.AdminShopUtils; +import fr.openmc.core.features.events.contents.halloween.festival.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement.Floors; +import fr.openmc.core.hooks.itemsadder.ItemsAdderHook; +import fr.openmc.core.utils.bukkit.ItemUtils; +import fr.openmc.core.utils.text.messages.MessageType; +import fr.openmc.core.utils.text.messages.MessagesManager; +import fr.openmc.core.utils.text.messages.Prefix; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Sound; +import org.bukkit.entity.Player; + +import java.util.*; + +public class WitchShopManager { + + public static LinkedHashSet witchStoreItems; + public static Map> paginateWitchStoreItems; + + public static int maxPage; + + public static int SLOT_FACTOR = 28; + private static final Sound BUY_SOUND = Sound.ENTITY_WITCH_CELEBRATE; + private static final Sound ERROR_SOUND = Sound.ENTITY_WITCH_AMBIENT; + + public static void init() { + witchStoreItems = getWitchStoreItems(); + paginateWitchStoreItems = getPaginateWitchStoreItems(); + maxPage = getWitchStorePages(); + } + + private static Map> getPaginateWitchStoreItems() { + Map> paginateItems = new HashMap<>(); + + List list = new ArrayList<>(witchStoreItems); + + int page = 1; + + for (int i = 0; i < list.size(); i += SLOT_FACTOR) { + + List subList = list.subList(i, Math.min(i + SLOT_FACTOR, list.size())); + + paginateItems.put(page++, new HashSet<>(subList)); + } + + return paginateItems; + } + + public static Set getStoreContent(int page) { + if (paginateWitchStoreItems == null) + paginateWitchStoreItems = getPaginateWitchStoreItems(); + + if (page > maxPage) + page = maxPage; + + return paginateWitchStoreItems.get(page); + } + + private static int getWitchStorePages() { + return (int) Math.ceil((double) witchStoreItems.size() / SLOT_FACTOR); + } + + public static void buyItem(Player player, WitchShopItem item, int amount) { + if (item == null) return; + + if (amount <= 0) { + sendError(player, TranslationManager.translation("feature.adminshop.amount_sup")); //TODO + return; + } + + if (!ItemUtils.hasEnoughSpace(player, item.getItem().getBest(), amount)) { + sendError(player, TranslationManager.translation("feature.adminshop.inventory_full")); //TODO + return; + } + + if (item.getPrice() <= 0) { + sendError(player, TranslationManager.translation("feature.adminshop.item_not_sellable")); //TODO + return; + } + + int totalPrice = item.getPrice() * amount; + if (ItemUtils.hasEnoughItems(player, item.getItem().getBest(), totalPrice)) { + + ItemUtils.giveItem(player, item.getItem().getBest(), amount); + + sendInfo(player, TranslationManager.translation("feature.adminshop.player_buy_item", //TODO + Component.text(amount), item.getName(), Component.text(AdminShopUtils.formatPrice(totalPrice)) + )); + + player.playSound(player, BUY_SOUND, 1f, new Random().nextFloat(0.6f, 1.4f)); + } else { + sendError(player, TranslationManager.translation("feature.adminshop.have_enough_money")); //TODO + } + } + + private static void sendError(Player player, Component message) { + MessagesManager.sendMessage(player, message, Prefix.HALLOWEEN, MessageType.ERROR, true); + player.playSound(player, ERROR_SOUND, 1f,1f); + } + + private static void sendInfo(Player player, Component message) { + MessagesManager.sendMessage(player, message, Prefix.HALLOWEEN, MessageType.INFO, true); + } + + public static List extractLoreForItem(WitchShopItem item) { + List lore = new ArrayList<>(); + + if (HalloweenManager.hasUnlock(item.getAdvancements())) { + lore.add(TranslationManager.translation("feature.adminshop.lore_item.buy", //TODO + getPrice(item) + ).color(NamedTextColor.GREEN).decoration(TextDecoration.ITALIC, false)); + + } + else { + lore.add(TranslationManager.translation("feature.adminshop.lore_item.buy." + item.getAdvancements().name().toLowerCase()) //TODO change key + .color(NamedTextColor.GREEN).decoration(TextDecoration.ITALIC, false)); + } + + return lore; + } + + public static Component getPrice(WitchShopItem item) { + return Component.text(item.getPrice() + " " + item.getShopMoney().getIcon()); + } + + public static String getMoneyItemIcon(String icon) { + if (ItemsAdderHook.isEnable()) { + return FontImageWrapper.replaceFontImages("§f:"+icon+":"); + } else { + return "Ⓐ"; //TODO Translationmanager.translate("<...>." + icon) + } + } + + //TODO mettre les vrais items + private static LinkedHashSet getWitchStoreItems() { + LinkedHashSet items = new LinkedHashSet<>(); + + items.add(new WitchShopItem( + Floors.FLOOR_1, OMCRegistry.CUSTOM_ITEMS.AYWENITE, 10)); + + items.add(new WitchShopItem( + Floors.FLOOR_2, OMCRegistry.CUSTOM_ITEMS.KEBAB, 10)); + + items.add(new WitchShopItem( + Floors.FLOOR_2, OMCRegistry.CUSTOM_ITEMS.NETHERITE_HAMMER, 1000)); + + return items; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/ConfirmMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/ConfirmMenu.java new file mode 100644 index 000000000..97837946a --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/ConfirmMenu.java @@ -0,0 +1,205 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop.menu; + +import dev.lone.itemsadder.api.FontImages.FontImageWrapper; +import fr.openmc.api.input.dialog.DialogInput; +import fr.openmc.api.menulib.Menu; +import fr.openmc.api.menulib.utils.InventorySize; +import fr.openmc.api.menulib.utils.ItemMenuBuilder; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.adminshop.AdminShopManager; +import fr.openmc.core.features.events.contents.halloween.festival.shop.WitchShopItem; +import fr.openmc.core.features.events.contents.halloween.festival.shop.WitchShopManager; +import fr.openmc.core.registry.items.CustomItem; +import fr.openmc.core.utils.bukkit.ItemUtils; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class ConfirmMenu extends Menu { + + private final WitchShopItem shopItem; + private int quantity; + private final int maxQuantity; + + public ConfirmMenu(Player owner, WitchShopItem shopItem) { + super(owner); + this.shopItem = shopItem; + this.quantity = 1; + this.maxQuantity = getMaxBuyQuantity(owner, shopItem); + } + + @Override + public @NotNull Component getName() { + return TranslationManager.translation("feature.adminshop.menu.confirm.name"); + } + + @Override + public String getTexture() { + return FontImageWrapper.replaceFontImages("§r§f:offset_-11::adminshop:"); + } + + @Override + public @NotNull InventorySize getInventorySize() { + return InventorySize.NORMAL; + } + + @Override + public void onInventoryClick(InventoryClickEvent inventoryClickEvent) {} + + @Override + public @NotNull Map getContent() { + Map content = new HashMap<>(); + int pricePerUnit = shopItem.getPrice(); + int totalPrice = pricePerUnit * quantity; + int quantityToStack = Math.max(0, quantity / 64); + + List lore = TranslationManager.translationLore("feature.adminshop.menu.confirm.lore", + Component.text(String.valueOf(quantity), NamedTextColor.WHITE), + Component.text(String.valueOf(quantityToStack), NamedTextColor.WHITE), + quantityToStack > 1 ? Component.text("s", NamedTextColor.WHITE) : Component.empty(), + Component.text(AdminShopManager.priceFormat.format(pricePerUnit), NamedTextColor.GREEN), + Component.text(shopItem.getShopMoney().getIcon(), NamedTextColor.GREEN), + Component.text(AdminShopManager.priceFormat.format(totalPrice), NamedTextColor.GREEN), + Component.text(shopItem.getShopMoney().getIcon(), NamedTextColor.GREEN) + + ); + + content.put(9, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.REFUSE_BTN, true)); + + content.put(10, createQuantityButton("-64", OMCRegistry.CUSTOM_ITEMS.BTN_64, event -> { + if (quantity > 64) quantity -= 64; + else quantity = 1; + this.open(); + })); + + content.put(11, createQuantityButton("-10", OMCRegistry.CUSTOM_ITEMS.MINUS_BTN, event -> { + if (quantity > 10) quantity -= 10; + else quantity = 1; + this.open(); + })); + + content.put(12, createQuantityButton("-1", OMCRegistry.CUSTOM_ITEMS.BTN_1, event -> { + if (quantity > 1) quantity--; + else quantity = 1; + this.open(); + })); + + content.put(13, new ItemMenuBuilder(this, shopItem.getItem(), meta -> { + meta.displayName(shopItem.getName().color(NamedTextColor.WHITE).decoration(TextDecoration.ITALIC, false)); + meta.lore(lore); + }).setOnClick(event -> { + if (event.getClick() == ClickType.MIDDLE) { + DialogInput.sendFloat( + getOwner(), + TranslationManager.translation("feature.adminshop.menu.confirm.input"), + 1, + maxQuantity, + quantity, + input -> { + if (input != null) { + int inputQuantity = (int) input.doubleValue(); + if (inputQuantity > 0) { + quantity = Math.min(inputQuantity, maxQuantity); + this.open(); + } + } + } + ); + } + })); + + content.put(14, createQuantityButton("+1", OMCRegistry.CUSTOM_ITEMS.BTN_1, event -> increaseQuantity(1))); + + content.put(15, createQuantityButton("+10", OMCRegistry.CUSTOM_ITEMS.BTN_10, event -> increaseQuantity(10))); + + content.put(16, createQuantityButton("+64", OMCRegistry.CUSTOM_ITEMS.BTN_64, event -> increaseQuantity(64))); + + content.put(17, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.ACCEPT_BTN, meta -> { + meta.displayName(TranslationManager.translation("messages.global.accept")); + }).setOnClick(event -> { + getOwner().closeInventory(); + WitchShopManager.buyItem(getOwner(), shopItem, quantity); + })); + + return content; + } + + /** + * Creates a quantity button with the specified text and item stack. + * + * @param text The text to display on the button. + * @param itemStack The item stack to use for the button. + * @param action The action to perform when the button is clicked. + * @return The created item stack. + */ + private ItemMenuBuilder createQuantityButton(String text, ItemStack itemStack, Consumer action) { + boolean plus = text.contains("+"); + return new ItemMenuBuilder(this, itemStack, meta -> + meta.displayName(TranslationManager.translation("feature.adminshop.menu.confirm.quantity", + plus ? + TranslationManager.translation("feature.adminshop.menu.confirm.add") : + TranslationManager.translation("feature.adminshop.menu.confirm.remove"), + Component.text(text.replace("+", "").replace("-", ""))) + .color(plus ? NamedTextColor.GREEN : NamedTextColor.RED) + .decoration(TextDecoration.ITALIC, false))) + .setItemId("quantity_" + text.replace("+", "plus").replace("-", "minus")) + .setOnClick(action); + } + + /** + * Creates a quantity button with the specified text and item stack. + * + * @param text The text to display on the button. + * @param customItem The CustomItem to use for the button. + * @param action The action to perform when the button is clicked. + * @return The created item stack. + */ + private ItemMenuBuilder createQuantityButton(String text, CustomItem customItem, Consumer action) { + return this.createQuantityButton(text, customItem.getBest(), action); + } + + /** + * Increases the quantity by the specified amount, ensuring it does not exceed the maximum allowed quantity. + * + * @param amount The amount to increase the quantity by. + */ + private void increaseQuantity(int amount) { + quantity = Math.min(quantity + amount, maxQuantity); + this.open(); + } + + private int getMaxBuyQuantity(Player player, WitchShopItem shopItem) { + int freePlaces = ItemUtils.getFreePlacesForItem(player, shopItem.getItem().getBest()); + + double buyPrice = shopItem.getPrice(); + if (buyPrice <= 0) return freePlaces; + + double balance = ItemUtils.countItems(player, shopItem.getItem().getBest()); + int affordable = (int) Math.floor(balance / buyPrice); + + return Math.min(freePlaces, affordable); + } + + + @Override + public void onClose(InventoryCloseEvent event) { + //empty + } + + @Override + public List getTakableSlot() { + return List.of(); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/WitchShopMainMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/WitchShopMainMenu.java new file mode 100644 index 000000000..f4a4a4a52 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/WitchShopMainMenu.java @@ -0,0 +1,72 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop.menu; + +import fr.openmc.api.menulib.Menu; +import fr.openmc.api.menulib.utils.InventorySize; +import fr.openmc.api.menulib.utils.ItemMenuBuilder; +import fr.openmc.core.features.events.contents.halloween.festival.shop.menu.buttons.WitchHouseButton; +import fr.openmc.core.features.events.contents.halloween.festival.shop.menu.buttons.WitchStoreButton; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class WitchShopMainMenu extends Menu { + + private static final int[] WITCH_HOUSE_SLOTS = {9, 10, 11, 12, 13, 18, 19, 20, 21, 22, 27, 28, 29, 30, 31, 36, 37, 38, 39, 40, 45, 46, 47, 48, 49}; + private static final int[] WITCH_SHOP_SLOTS = {23, 24, 25, 26, 32, 33, 34, 35, 41, 42, 43, 44, 50, 51, 52, 53}; + private static final Sound WITCH_MENU_CLOSE = Sound.ENTITY_WITCH_AMBIENT; + + public WitchShopMainMenu(Player owner) { + super(owner); + } + + @Override + public @NotNull Component getName() { + return TranslationManager.translation("feature.city.menus.main.name"); //TODO + } + + @Override + public String getTexture() { + return "§r§f:offset_-48::halloween_event_main_menu:"; + } + + @Override + public @NotNull InventorySize getInventorySize() { + return InventorySize.LARGEST; + } + + @Override + public @NotNull Map getContent() { + Map inventory = new HashMap<>(); + + // ** Witch House Button + WitchHouseButton.init(this, inventory, WITCH_HOUSE_SLOTS); + + // ** Witch Store Button + WitchStoreButton.init(this, inventory, WITCH_SHOP_SLOTS); + + return inventory; + } + + @Override + public List getTakableSlot() { + return List.of(); + } + + @Override + public void onClose(InventoryCloseEvent event) { + Player player = getOwner(); + player.playSound(player, WITCH_MENU_CLOSE, 1f, 1f); + } + + @Override + public void onInventoryClick(InventoryClickEvent click) { + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/WitchShopStoreMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/WitchShopStoreMenu.java new file mode 100644 index 000000000..99efe1557 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/WitchShopStoreMenu.java @@ -0,0 +1,165 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop.menu; + +import fr.openmc.api.menulib.MenuLib; +import fr.openmc.api.menulib.PaginatedMenu; +import fr.openmc.api.menulib.utils.InventorySize; +import fr.openmc.api.menulib.utils.ItemMenuBuilder; +import fr.openmc.api.menulib.utils.StaticSlots; +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.commands.utils.Restart; +import fr.openmc.core.features.events.contents.halloween.festival.shop.WitchShopItem; +import fr.openmc.core.features.events.contents.halloween.festival.shop.WitchShopManager; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.minecraft.world.level.levelgen.Column; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +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.util.*; + +public class WitchShopStoreMenu extends PaginatedMenu { + + @Getter + private final int page; + + private final Column.Range line = new Column.Range(10, 16); + private final int column = 4; + + public WitchShopStoreMenu(Player owner, int page) { + super(owner); + this.page = page; + + if (this.page < 1) { + throw new IllegalArgumentException("Page must be greater than 0"); + } + + if (this.page > WitchShopManager.maxPage) { + throw new IllegalArgumentException("Page must be less than or equal to " + WitchShopManager.maxPage); + } + } + + @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<>(); + + Iterator contents = WitchShopManager.getStoreContent(this.page).iterator(); + + for (int c = 0; c < column; c++) { + for (int l = line.floor(); l <= line.ceiling(); l++) { + + if (!contents.hasNext()) break; + + WitchShopItem item = contents.next(); + + items.add(new ItemMenuBuilder(this, item.getItem(), itemMeta -> { + itemMeta.displayName(item.getName()); + itemMeta.lore(WitchShopManager.extractLoreForItem(item)); + }).setOnClick(event -> { + //TODO open confirm menu if has enough moneyItem + })); + } + } + + return items; + } + + @Override + public Map getButtons() { + if (Restart.isRestarting) return null; + + Map map = new HashMap<>(); + + if (hasPreviousPage()) { + map.put(45, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.ICON_BACK_ORANGE, itemMeta -> { + itemMeta.displayName(TranslationManager.translation("messages.menus.previous_page")); + }).setPreviousPageButton()); + } + + map.put(49, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.ICON_CANCEL, itemMeta -> { + itemMeta.displayName(TranslationManager.translation("messages.menus.close")); + }).setOnClick(event -> { + new WitchShopMainMenu(getOwner()).open(); + })); + + //TODO filter : + // - room + // - unlocked + // - lower cost + // - highest cost + + if (hasNextPage()) { + map.put(53, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.ICON_NEXT_ORANGE, itemMeta -> { + itemMeta.displayName(TranslationManager.translation("messages.menus.next_page")); + }).setNextPageButton()); + } + + return map; + } + + @Override + public @NotNull Component getName() { + return TranslationManager.translation("feature.city.menus.chest.name", + Component.text(""), Component.text(this.page)); //TODO + } + + @Override + public String getTexture() { + return "§r§f:offset_-48::city_template6x9:"; //TODO + } + + @Override + public void onInventoryClick(InventoryClickEvent inventoryClickEvent) { + } + + @Override + public @NotNull InventorySize getInventorySize() { + return InventorySize.LARGEST; + } + + @Override + public int getSizeOfItems() { + return getItems().size(); + } + + @Override + public void onClose(InventoryCloseEvent event) { + if (Restart.isRestarting) return; + HumanEntity humanEntity = event.getPlayer(); + if (!(humanEntity instanceof Player player)) return; + // fixes #1007 + Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), ()-> MenuLib.updateMenu(player), 5L); + } + + @Override + public List getTakableSlot() { + return List.of(); + } + + public boolean hasNextPage() { + return this.page < WitchShopManager.maxPage; + } + + public boolean hasPreviousPage() { + return this.page > 1; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/buttons/WitchHouseButton.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/buttons/WitchHouseButton.java new file mode 100644 index 000000000..64b45c17e --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/buttons/WitchHouseButton.java @@ -0,0 +1,34 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop.menu.buttons; + +import fr.openmc.api.menulib.Menu; +import fr.openmc.api.menulib.utils.ItemMenuBuilder; +import fr.openmc.api.menulib.utils.MenuUtils; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; + +import java.util.Map; + +public class WitchHouseButton { + public static void init(Menu menu, Map contents, int[] slots) { + Player player = menu.getOwner(); + + MenuUtils.createButtonItem( + contents, + slots, + new ItemMenuBuilder(menu, Material.PAPER, itemMeta -> { + itemMeta.displayName(TranslationManager.translation("feature.city.menus.main.ranks.title"));//TODO + itemMeta.lore(TranslationManager.translationLore( + "feature.city.menus.main.ranks.lore.unlocked", //TODO + Component.text("").color(NamedTextColor.LIGHT_PURPLE) + )); + itemMeta.setItemModel(NamespacedKey.minecraft("air")); + }).setCloseButton()//.setOnClick(inventoryClickEvent -> { //TODO tp dans la dimension +// inventoryClickEvent.getClickedInventory().close(); +// }) + ); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/buttons/WitchStoreButton.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/buttons/WitchStoreButton.java new file mode 100644 index 000000000..b9e3c8ba1 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/shop/menu/buttons/WitchStoreButton.java @@ -0,0 +1,35 @@ +package fr.openmc.core.features.events.contents.halloween.festival.shop.menu.buttons; + +import fr.openmc.api.menulib.Menu; +import fr.openmc.api.menulib.utils.ItemMenuBuilder; +import fr.openmc.api.menulib.utils.MenuUtils; +import fr.openmc.core.features.events.contents.halloween.festival.shop.menu.WitchShopStoreMenu; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; + +import java.util.Map; + +public class WitchStoreButton { + public static void init(Menu menu, Map contents, int[] slots) { + Player player = menu.getOwner(); + + MenuUtils.createButtonItem( + contents, + slots, + new ItemMenuBuilder(menu, Material.PAPER, itemMeta -> { + itemMeta.displayName(TranslationManager.translation("feature.city.menus.main.ranks.title")); //TODO + itemMeta.lore(TranslationManager.translationLore( + "feature.city.menus.main.ranks.lore.unlocked", //TODO + Component.text("").color(NamedTextColor.LIGHT_PURPLE) + )); + itemMeta.setItemModel(NamespacedKey.minecraft("air")); + }).setOnClick(inventoryClickEvent -> { + new WitchShopStoreMenu(player, 1).open(); + }) + ); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/DimensionGenerator.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/DimensionGenerator.java new file mode 100644 index 000000000..6c078369e --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/DimensionGenerator.java @@ -0,0 +1,51 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse; + +import fr.openmc.core.bootstrap.integration.OMCLogger; +import org.bukkit.Bukkit; +import org.bukkit.GameRules; +import org.bukkit.World; +import org.bukkit.entity.SpawnCategory; + +public class DimensionGenerator { + + public static final String DIMENSION_NAME = "world_omc_halloween_witch_house"; + public static World HALLOWEEN_WORLD; + + public static void init() { + HALLOWEEN_WORLD = Bukkit.getWorld(DIMENSION_NAME); + + setupDimension(); + } + + private static void setupDimension() { + if (!HALLOWEEN_WORLD.getName().equals(DIMENSION_NAME)) return; + + // ** SPAWNING RULES ** + HALLOWEEN_WORLD.setSpawnLimit(SpawnCategory.MONSTER, 10); + HALLOWEEN_WORLD.setSpawnLimit(SpawnCategory.AMBIENT, 10); + HALLOWEEN_WORLD.setSpawnLimit(SpawnCategory.ANIMAL, 6); + + HALLOWEEN_WORLD.setTicksPerSpawns(SpawnCategory.MONSTER, 30); + HALLOWEEN_WORLD.setTicksPerSpawns(SpawnCategory.AMBIENT, 15); + HALLOWEEN_WORLD.setTicksPerSpawns(SpawnCategory.ANIMAL, 30); + + // ** SET GAMERULE FOR THE WORLD ** + HALLOWEEN_WORLD.setGameRule(GameRules.ADVANCE_TIME, false); + HALLOWEEN_WORLD.setGameRule(GameRules.SHOW_ADVANCEMENT_MESSAGES, false); + HALLOWEEN_WORLD.setGameRule(GameRules.ADVANCE_WEATHER, false); + HALLOWEEN_WORLD.setGameRule(GameRules.RAIDS, true); + HALLOWEEN_WORLD.setGameRule(GameRules.SPAWN_PATROLS, false); + HALLOWEEN_WORLD.setGameRule(GameRules.SPAWN_WANDERING_TRADERS, false); + HALLOWEEN_WORLD.setGameRule(GameRules.NATURAL_HEALTH_REGENERATION, false); + HALLOWEEN_WORLD.setGameRule(GameRules.LOCATOR_BAR, false); + HALLOWEEN_WORLD.setGameRule(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS, false); + + // ** SET WORLD BORDER AND TIME ** + HALLOWEEN_WORLD.getWorldBorder().setSize(500); + HALLOWEEN_WORLD.setTime(14000); + HALLOWEEN_WORLD.setThundering(true); + HALLOWEEN_WORLD.setStorm(true); + + OMCLogger.infoFormatted("Dimension de la Maison de la Sorcière setup (gamerules, worldborder, time)"); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/WitchHouseManager.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/WitchHouseManager.java new file mode 100644 index 000000000..4afb4a314 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/WitchHouseManager.java @@ -0,0 +1,114 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse; + +import com.sk89q.worldedit.math.BlockVector3; +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement.Floors; +import fr.openmc.core.registry.ambient.CustomAmbient; +import fr.openmc.core.registry.regions.CustomRegion; +import fr.openmc.core.utils.text.messages.MessageType; +import fr.openmc.core.utils.text.messages.MessagesManager; +import fr.openmc.core.utils.text.messages.Prefix; +import fr.openmc.core.utils.text.messages.TranslationManager; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.util.Vector; + +import java.util.List; + +public class WitchHouseManager { + + public Floors currentFloor; + public static List enabledRegions; //TODO add values to it + public int floorAdvancement; + public boolean finalRoomReach; + + BukkitTask spawnTimeTask; + + @Getter + public static CustomAmbient witchHouseAmbient = OMCRegistry.CUSTOM_AMBIENTS.WITCH_HOUSE; + + public WitchHouseManager(Floors advancements, int countAdvancement) { + currentFloor = advancements; + this.floorAdvancement = countAdvancement; + finalRoomReach = currentFloor.equals(Floors.FLOOR_2); //TODO last floor FLOOR_15 + } + + public void updateAdvancement(int increase) { + floorAdvancement += increase; + + if (currentFloor.getRequire() > floorAdvancement) { + return; + } + int currentRoom = Integer.parseInt(currentFloor.name().replaceFirst(".*\\D", "")); + + currentRoom++; + + currentFloor = Floors.valueOf("FLOOR_" + currentRoom); + + //TODO broadcast + } + + public void prepareSpawn() { + // entre 1m et 1m30 -> dire que l'on peut spawn vague de mob, avec int min et int max + // selon l'étage plus de mob + ou - dure + + spawnTimeTask = Bukkit.getScheduler().runTaskAsynchronously(OMCPlugin.getInstance(), + () -> { + // dire au zone de check pour spawn des mobs, sinon mettre en attente + }); + + + } + + public void delayedSpawnMob() { + + } + + public void broadcast(Component text) { + for (Player player : OMCPlugin.getInstance().getServer().getOnlinePlayers()) { + MessagesManager.sendMessage( + player, + TranslationManager.translation("", text), + Prefix.HALLOWEEN, + MessageType.INFO, + true + ); + } + } + + public static void repulsePlayer(CustomRegion region, Entity entity, double force) { + BlockVector3 coords = region.getCenter(); + Location center = new Location(region.getWorld(), coords.x(), coords.y(), coords.z()); + repulsePlayer(center, entity, force); + } + + public static void repulsePlayer(Entity entity, double force) { + repulsePlayer(entity.getLocation(), entity, force); + } + + public static void repulsePlayer(Location source, Entity entity, double force) { + Location playerLocation = entity.getLocation(); + + if (source.getWorld() == null + || !source.getWorld().equals(playerLocation.getWorld())) { + return; + } + + Vector knockback = playerLocation.toVector() + .subtract(source.toVector()); + + if (knockback.lengthSquared() < 1.0E-6) { + knockback = playerLocation.getDirection(); + } + + knockback.normalize().multiply(force); + + entity.setVelocity(knockback); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/Floor.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/Floor.java new file mode 100644 index 000000000..cb74a6ae9 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/Floor.java @@ -0,0 +1,110 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.registry.mobs.CustomMobEntry; +import fr.openmc.core.registry.regions.types.Region; +import fr.openmc.core.utils.RandomUtils; +import fr.openmc.core.utils.world.LocationUtils; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.List; + +public record Floor(Region region, List entities, double radius) { + + public void spawnMob(int number) { + for (int n = 0; n < number; n++) { + Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), this::spawn, (RandomUtils.randomBetween(2, 7) + n) * 20L); + } + } + + private void spawn() { + + Location spawnLoc = getRandomPosFromPlayer( + getRandomPlayerInRegion() + ); + + new BukkitRunnable() { + private int maxPoints = 30; //gives the amount of time the mob takes to spawn, there it's 30*2 = 60 ticks = 3 seconds + private int points = 1; + + @Override + public void run() { + spawnCircle(spawnLoc, 0.8, points); + + points++; + + if (points >= maxPoints) { + cancel(); + + CustomMobEntry selectEntity = RandomUtils.getRandomObjectFromList(entities); + + selectEntity.spawn(spawnLoc); + } + } + }.runTaskTimer(OMCPlugin.getInstance(), 0L, 2L); + } + + public Player getRandomPlayerInRegion() { + return RandomUtils.getRandomObjectFromList( + OMCPlugin.getInstance().getServer() + .getOnlinePlayers() + .stream() + .filter(p -> region.contains(p.getLocation())) + .toList() + ); + } + + public Location getRandomPosFromPlayer(Player player) { + return LocationUtils.randomLocation( + new Location( + player.getWorld(), + player.getX(), + getFloorPos(player), + player.getZ() + ), + radius + ); + } + + public int getFloorPos(Player player) { + Block block = player.getLocation().getBlock(); + if (player.isFlying() || player.isFlying()) { + int minY = region.getMin().y(); + + while (block.getY() >= minY && block.getType().isAir()) + block = block.getRelative(0, -1, 0); + } + return block.getY() + 1; + } + + public static void spawnCircle(Location center, double radius, int points) { + World world = center.getWorld(); + if (world == null) return; + + for (int i = 0; i < points; i++) { + double angle = 2 * Math.PI * i / points; + + double x = center.getX() + radius * Math.cos(angle); + double z = center.getZ() + radius * Math.sin(angle); + + Location particleLocation = new Location( + world, + x, + center.getY() + 0.05, + z + ); + + world.spawnParticle( + Particle.DUST, + particleLocation, + 1, + 0, 0, 0, + 0, + new Particle.DustOptions(Color.RED, 1.0f) + ); + } + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/Floors.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/Floors.java new file mode 100644 index 000000000..60938cae3 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/Floors.java @@ -0,0 +1,31 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement; + +import fr.openmc.core.OMCRegistry; +import lombok.Getter; + +import java.util.List; + +@Getter +public enum Floors { + FLOOR_1(new Floor( + OMCRegistry.CUSTOM_REGIONS.EXAMPLE_REGION, + List.of(), + 3.0 + ), 0), + + FLOOR_2(new Floor( + OMCRegistry.CUSTOM_REGIONS.EXAMPLE_REGION, + List.of(), + 3.0 + ), 1000), + + ; + + private final Floor floor; + private final int require; + + Floors(Floor floor, int require) { + this.floor = floor; + this.require = require; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/menu/AdvancementMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/menu/AdvancementMenu.java new file mode 100644 index 000000000..fc22068bd --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/menu/AdvancementMenu.java @@ -0,0 +1,63 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement.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.ItemMenuBuilder; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AdvancementMenu extends Menu { + + protected AdvancementMenu(Player owner) { + super(owner); + } + + @Override + public @NotNull Component getName() { + return TranslationManager.translation("feature.adminshop.menu.confirm.name");//TODO + } + + @Override + public String getTexture() { + return FontImageWrapper.replaceFontImages("§r§f:offset_-11::adminshop:");//TODO jar avec des bonbons, faire selon l'avancement ? ( en vrai ouais ) + } + + @Override + public @NotNull InventorySize getInventorySize() { + return InventorySize.LARGEST; + } + + @Override + public void onInventoryClick(InventoryClickEvent e) { + + } + + @Override + public @NotNull Map getContent() { + Map content = new HashMap<>(); + + content.put(9, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.REFUSE_BTN, true)); + + return content; + } + + @Override + public void onClose(InventoryCloseEvent event) { + + } + + @Override + public List getTakableSlot() { + return List.of(); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/menu/RewardsMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/menu/RewardsMenu.java new file mode 100644 index 000000000..770b952e4 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/advancement/menu/RewardsMenu.java @@ -0,0 +1,63 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse.advancement.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.ItemMenuBuilder; +import fr.openmc.core.OMCRegistry; +import fr.openmc.core.utils.text.messages.TranslationManager; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RewardsMenu extends Menu { + + protected RewardsMenu(Player owner) { + super(owner); + } + + @Override + public @NotNull Component getName() { + return TranslationManager.translation("feature.adminshop.menu.confirm.name");//TODO + } + + @Override + public String getTexture() { + return FontImageWrapper.replaceFontImages("§r§f:offset_-11::adminshop:");//TODO + } + + @Override + public @NotNull InventorySize getInventorySize() { + return InventorySize.NORMAL; + } + + @Override + public void onInventoryClick(InventoryClickEvent e) { + + } + + @Override + public @NotNull Map getContent() { + Map content = new HashMap<>(); + + content.put(9, new ItemMenuBuilder(this, OMCRegistry.CUSTOM_ITEMS.REFUSE_BTN, true)); + + return content; + } + + @Override + public void onClose(InventoryCloseEvent event) { + + } + + @Override + public List getTakableSlot() { + return List.of(); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/ambient/WitchHouseAmbient.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/ambient/WitchHouseAmbient.java new file mode 100644 index 000000000..052154ed6 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/ambient/WitchHouseAmbient.java @@ -0,0 +1,60 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse.ambient; + +import fr.openmc.api.datapacks.builders.EnvironnementAttributeBuilder; +import fr.openmc.core.registry.ambient.CustomAmbient; +import fr.openmc.core.registry.ambient.CustomAmbientRegistry; +import fr.openmc.core.registry.ambient.builder.AmbientBuilder; +import fr.openmc.core.utils.nms.WeatherType; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.Particle; + +public class WitchHouseAmbient extends CustomAmbient { + @Override + public String getId() { + return "witch_house_ambient"; + } + + @Override + public AmbientBuilder getAmbientBuilder() { + return new AmbientBuilder(CustomAmbientRegistry.NAMESPACE, this.getId()) + .attributesBuilder(new EnvironnementAttributeBuilder() + .attributes(obj -> { + obj.addProperty("visual/sky_light_color", "#FAED5C"); + obj.addProperty("visual/ambient_light_color", "#001A19"); + + obj.addProperty("visual/sunrise_sunset_color", "#e540e58b"); + obj.addProperty("visual/sky_color", "#0D0030"); + + obj.addProperty("visual/fog_start_distance", 0); + obj.addProperty("visual/fog_end_distance", 512); + obj.addProperty("visual/fog_color", "#3C374A"); + obj.addProperty("visual/water_fog_color", "#3D2E42"); + + obj.addProperty("visual/cloud_height", 240); + }) + .ambientParticles(Particle.WITCH, 0.02) + .ambientParticles(Particle.DRAGON_BREATH, 0.02) +// .ambientSounds(new AmbientSoundBuilder() +// .additions(new AmbientSoundBuilder.AdditionsBuilder() +// .sound("minecraft:ambient.underwater.loop.additions.rare") +// .tickChance(0.001) +// ) +// ) + ) + .ambientLight(0.2f) + .skybox(DimensionType.Skybox.OVERWORLD) + .hasSkylight(true) + .waterColor("#604f7d") + .defaultClock("overworld") + .timelines("#minecraft:in_overworld") + .hasFixedTime(true, 200) + .hasPrecipitation(true, WeatherType.RAIN); + } + + @Override + public ResourceKey getTransitionDimension() { + return Level.END; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/mobs/floor1_3/HouseZombieLV1.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/mobs/floor1_3/HouseZombieLV1.java new file mode 100644 index 000000000..447527858 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/festival/witchhouse/mobs/floor1_3/HouseZombieLV1.java @@ -0,0 +1,65 @@ +package fr.openmc.core.features.events.contents.halloween.festival.witchhouse.mobs.floor1_3; + +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.WitchHouseManager; +import fr.openmc.core.registry.loottable.loots.XpLoot; +import fr.openmc.core.registry.mobs.CustomMob; +import fr.openmc.core.utils.RandomUtils; +import fr.openmc.core.utils.text.messages.TranslationManager; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.*; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class HouseZombieLV1 extends CustomMob { + + double RADIUS = 3.0; + + public HouseZombieLV1(String id) { + super(id, + TranslationManager.translation("feature.dailyevents.miraculousfishing.mob.ancient_villager"), //TODO + Zombie.class, + 100, + 67, + List.of( + new XpLoot(100, 150, 0.5) //TODO ajouté les bonbons + ) + ); + } + + @Override + public Zombie spawn(Location spawnLocation) { + Zombie zombie = this.getPreBuildMob(spawnLocation); + + zombie.getEquipment().setHelmet(new ItemStack( + RandomUtils.getRandomObject( + Material.PUMPKIN, + Material.AIR + ) + )); + + zombie.getEquipment().setLeggings(new ItemStack(Material.CHAINMAIL_LEGGINGS)); + + zombie.getEquipment().setBoots(new ItemStack( + RandomUtils.getRandomObject( + Material.LEATHER_BOOTS, + Material.COPPER_BOOTS + ) + )); + + zombie.getEquipment().setItemInMainHand(new ItemStack( + RandomUtils.getRandomObject( + Material.IRON_AXE, + Material.IRON_SHOVEL, + Material.AIR + ) + )); + + zombie.getNearbyEntities(RADIUS, RADIUS, RADIUS).stream() + .filter(entity -> entity instanceof Player) + .forEach(player -> WitchHouseManager.repulsePlayer(player, 0.3f)); + + return zombie; + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/commands/HalloweenCommands.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/commands/HalloweenCommands.java new file mode 100644 index 000000000..9d931fb51 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/commands/HalloweenCommands.java @@ -0,0 +1,22 @@ +package fr.openmc.core.features.events.contents.halloween.pumpkin.commands; + +import fr.openmc.core.features.events.contents.halloween.festival.shop.menu.WitchShopMainMenu; +import fr.openmc.core.features.events.contents.halloween.pumpkin.managers.HalloweenManager; +import org.bukkit.entity.Player; +import revxrsal.commands.annotation.Command; +import revxrsal.commands.annotation.Subcommand; +import revxrsal.commands.bukkit.annotation.CommandPermission; + +@Command("halloween") +@CommandPermission("omc.admins.commands.halloween") +public class HalloweenCommands { + @Subcommand("end") + public void endHalloweenCommand() { + HalloweenManager.endEvent(); + } + + @Subcommand("test") + public void tst(Player player) { + new WitchShopMainMenu(player).open(); + } +} diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/listeners/HalloweenNPCListener.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/listeners/HalloweenNPCListener.java similarity index 81% rename from src/main/java/fr/openmc/core/features/events/contents/halloween/listeners/HalloweenNPCListener.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/listeners/HalloweenNPCListener.java index fb954e064..4a819d3ba 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/halloween/listeners/HalloweenNPCListener.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/listeners/HalloweenNPCListener.java @@ -1,8 +1,8 @@ -package fr.openmc.core.features.events.contents.halloween.listeners; +package fr.openmc.core.features.events.contents.halloween.pumpkin.listeners; import de.oliver.fancynpcs.api.events.NpcInteractEvent; import fr.openmc.core.bootstrap.features.types.LoadIfEnable; -import fr.openmc.core.features.events.contents.halloween.menus.HalloweenPumpkinDepositMenu; +import fr.openmc.core.features.events.contents.halloween.pumpkin.menus.HalloweenPumpkinDepositMenu; import fr.openmc.core.hooks.FancyNpcsHook; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/managers/HalloweenManager.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/managers/HalloweenManager.java similarity index 96% rename from src/main/java/fr/openmc/core/features/events/contents/halloween/managers/HalloweenManager.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/managers/HalloweenManager.java index 27a5eaee2..410071651 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/halloween/managers/HalloweenManager.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/managers/HalloweenManager.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.events.contents.halloween.managers; +package fr.openmc.core.features.events.contents.halloween.pumpkin.managers; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; @@ -16,9 +16,9 @@ import fr.openmc.core.bootstrap.integration.OMCLogger; import fr.openmc.core.bootstrap.listeners.ListenerFactory; import fr.openmc.core.features.economy.EconomyManager; -import fr.openmc.core.features.events.contents.halloween.commands.HalloweenCommands; -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.events.contents.halloween.pumpkin.commands.HalloweenCommands; +import fr.openmc.core.features.events.contents.halloween.pumpkin.listeners.HalloweenNPCListener; +import fr.openmc.core.features.events.contents.halloween.pumpkin.models.HalloweenData; import fr.openmc.core.features.mailboxes.MailboxManager; import fr.openmc.core.utils.cache.CachePlayerName; import fr.openmc.core.utils.text.messages.TranslationManager; diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/menus/HalloweenPumpkinDepositMenu.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/menus/HalloweenPumpkinDepositMenu.java similarity index 95% rename from src/main/java/fr/openmc/core/features/events/contents/halloween/menus/HalloweenPumpkinDepositMenu.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/menus/HalloweenPumpkinDepositMenu.java index 008f48839..66003dd33 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/halloween/menus/HalloweenPumpkinDepositMenu.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/menus/HalloweenPumpkinDepositMenu.java @@ -1,9 +1,9 @@ -package fr.openmc.core.features.events.contents.halloween.menus; +package fr.openmc.core.features.events.contents.halloween.pumpkin.menus; import fr.openmc.api.menulib.Menu; import fr.openmc.api.menulib.utils.InventorySize; import fr.openmc.api.menulib.utils.ItemMenuBuilder; -import fr.openmc.core.features.events.contents.halloween.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.pumpkin.managers.HalloweenManager; import fr.openmc.core.utils.bukkit.ItemUtils; import fr.openmc.core.utils.text.messages.MessageType; import fr.openmc.core.utils.text.messages.MessagesManager; diff --git a/src/main/java/fr/openmc/core/features/events/contents/halloween/models/HalloweenData.java b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/models/HalloweenData.java similarity index 89% rename from src/main/java/fr/openmc/core/features/events/contents/halloween/models/HalloweenData.java rename to src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/models/HalloweenData.java index ffc4cac6e..0c7954c2a 100644 --- a/src/main/java/fr/openmc/core/features/events/contents/halloween/models/HalloweenData.java +++ b/src/main/java/fr/openmc/core/features/events/contents/halloween/pumpkin/models/HalloweenData.java @@ -1,4 +1,4 @@ -package fr.openmc.core.features.events.contents.halloween.models; +package fr.openmc.core.features.events.contents.halloween.pumpkin.models; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; diff --git a/src/main/java/fr/openmc/core/features/leaderboards/leaderboards/PumpkinCountLeaderBoard.java b/src/main/java/fr/openmc/core/features/leaderboards/leaderboards/PumpkinCountLeaderBoard.java index 2b696b5d1..172b5d150 100644 --- a/src/main/java/fr/openmc/core/features/leaderboards/leaderboards/PumpkinCountLeaderBoard.java +++ b/src/main/java/fr/openmc/core/features/leaderboards/leaderboards/PumpkinCountLeaderBoard.java @@ -2,8 +2,8 @@ import fr.openmc.api.omcplayer.OMCOfflinePlayer; import fr.openmc.core.features.economy.EconomyManager; -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.events.contents.halloween.pumpkin.managers.HalloweenManager; +import fr.openmc.core.features.events.contents.halloween.pumpkin.models.HalloweenData; import fr.openmc.core.features.leaderboards.LeaderBoard; import fr.openmc.core.utils.cache.CachePlayerName; import fr.openmc.core.utils.text.ColorUtils; diff --git a/src/main/java/fr/openmc/core/hooks/WorldEditHook.java b/src/main/java/fr/openmc/core/hooks/WorldEditHook.java new file mode 100644 index 000000000..4a6a8c61e --- /dev/null +++ b/src/main/java/fr/openmc/core/hooks/WorldEditHook.java @@ -0,0 +1,32 @@ +package fr.openmc.core.hooks; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.bootstrap.hooks.Hooks; +import org.bukkit.plugin.PluginManager; + +import java.util.Set; + +public class WorldEditHook extends Hooks { + + private static final String WE = "WorldEdit"; + private static final String FAWE = "FastAsyncWorldEdit"; + + public static boolean isEnable() { + return Hooks.isEnabled(WorldEditHook.class); + } + + @Override + protected Set getPluginsName() { + return Set.of(WE, FAWE); + } + + @Override + public boolean arePluginEnabled() { + return isLoaded(WE) || isLoaded(FAWE); + } + + private static boolean isLoaded(String name) { + PluginManager pm = OMCPlugin.getInstance().getServer().getPluginManager(); + return pm.getPlugin(name) != null && pm.isPluginEnabled(name); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java b/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java index a1f88f40b..34d3bab49 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java +++ b/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java @@ -10,6 +10,7 @@ import fr.openmc.core.features.events.contents.dailyevents.contents.bloodynight.contents.ambient.BloodyAmbient; import fr.openmc.core.features.events.contents.dailyevents.contents.goldenharvest.contents.ambient.GoldenAmbient; import fr.openmc.core.features.events.contents.dailyevents.contents.miraculousfishing.contents.ambient.BlessedAmbient; +import fr.openmc.core.features.events.contents.halloween.festival.witchhouse.ambient.WitchHouseAmbient; import fr.openmc.core.registry.ambient.contents.DarkAmbient; import fr.openmc.core.registry.ambient.contents.HellAmbient; import fr.openmc.core.registry.ambient.listeners.AmbientFixedTimeListener; @@ -37,6 +38,7 @@ public class CustomAmbientRegistry extends Registry public final CustomAmbient GOLDEN = register(new GoldenAmbient()); public final CustomAmbient BLOODY = register(new BloodyAmbient()); public final CustomAmbient BLESSED = register(new BlessedAmbient()); + public final CustomAmbient WITCH_HOUSE = register(new WitchHouseAmbient()); @Override public Set getListeners() { diff --git a/src/main/java/fr/openmc/core/registry/items/CustomItemRegistry.java b/src/main/java/fr/openmc/core/registry/items/CustomItemRegistry.java index 87c23880c..27d78bb74 100644 --- a/src/main/java/fr/openmc/core/registry/items/CustomItemRegistry.java +++ b/src/main/java/fr/openmc/core/registry/items/CustomItemRegistry.java @@ -15,6 +15,7 @@ import fr.openmc.core.features.events.contents.dailyevents.contents.miraculousfishing.contents.items.FishingFurnitureLootbox; import fr.openmc.core.features.events.contents.dailyevents.contents.miraculousfishing.contents.items.LegendaryFishingTreasureLootbox; import fr.openmc.core.features.events.contents.dailyevents.contents.miraculousfishing.contents.items.RareFishingTreasureLootbox; +import fr.openmc.core.features.events.contents.halloween.halloween.CandyItem; import fr.openmc.core.features.itemsadder.elevator.ElevatorBlock; import fr.openmc.core.features.itemsadder.elevator.ElevatorColor; import fr.openmc.core.hooks.itemsadder.ItemsAdderHook; @@ -196,6 +197,13 @@ public class CustomItemRegistry extends Registry public final CustomItem ABONDANCE_LEGGINGS = register("omc_daily_events:abondance_leggings", Material.IRON_LEGGINGS); public final CustomItem ABONDANCE_BOOTS = register("omc_daily_events:abondance_boots", Material.IRON_BOOTS); + public final CustomItem CANDY_RED = register(new CandyItem("omc_halloween:candy_red", 4, Material.APPLE)); + public final CustomItem CANDY_YELLOW = register(new CandyItem("omc_halloween:candy_yellow", 2, Material.APPLE)); + public final CustomItem CANDY_GREEN = register(new CandyItem("omc_halloween:candy_green", 3, Material.APPLE)); + public final CustomItem CANDY_BLUE = register(new CandyItem("omc_halloween:candy_blue", 5, Material.APPLE)); + public final CustomItem CANDY_BROWN = register(new CandyItem("omc_halloween:candy_brown", 20, Material.APPLE)); + + /* Lootbox */ public final CustomItem KITCHEN_BARSTOOL = register("kitchen:kitchen_barstool", Material.PAPER); public final CustomItem KITCHEN_BIN = register("kitchen:kitchen_bin", Material.PAPER); public final CustomItem KITCHEN_CHAIR = register("kitchen:kitchen_chair", Material.PAPER); diff --git a/src/main/java/fr/openmc/core/registry/regions/CustomRegion.java b/src/main/java/fr/openmc/core/registry/regions/CustomRegion.java index b5866b083..7a8ed6b42 100644 --- a/src/main/java/fr/openmc/core/registry/regions/CustomRegion.java +++ b/src/main/java/fr/openmc/core/registry/regions/CustomRegion.java @@ -10,8 +10,10 @@ public abstract class CustomRegion { public abstract World getWorld(); public abstract boolean contains(BlockVector3 pos); + public abstract BlockVector3 getPos1(); public abstract BlockVector3 getPos2(); + public abstract BlockVector3 getCenter(); public final boolean contains(Location loc) { if (loc == null || loc.getWorld() == null) return false; diff --git a/src/main/java/fr/openmc/core/registry/regions/content/ExampleRegion.java b/src/main/java/fr/openmc/core/registry/regions/content/ExampleRegion.java index 5d2322faf..0f84dd937 100644 --- a/src/main/java/fr/openmc/core/registry/regions/content/ExampleRegion.java +++ b/src/main/java/fr/openmc/core/registry/regions/content/ExampleRegion.java @@ -25,4 +25,9 @@ public BlockVector3 getPos1() { public BlockVector3 getPos2() { return null; } + + @Override + public BlockVector3 getCenter() { + return null; + } } diff --git a/src/main/java/fr/openmc/core/registry/regions/preset/SquareRegion.java b/src/main/java/fr/openmc/core/registry/regions/preset/SquareRegion.java new file mode 100644 index 000000000..921aa00ed --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/regions/preset/SquareRegion.java @@ -0,0 +1,69 @@ +package fr.openmc.core.registry.regions.preset; + +import com.sk89q.worldedit.math.BlockVector3; +import fr.openmc.core.registry.regions.CustomRegion; +import fr.openmc.core.utils.MathUtils; +import net.kyori.adventure.key.Key; +import org.bukkit.World; + +public class SquareRegion extends CustomRegion { + + public World world; + public BlockVector3 min; + public BlockVector3 max; + public Key key; + + public SquareRegion(World world, BlockVector3 corner1, BlockVector3 corner2) { + this(world, corner1, corner2, null); + } + + public SquareRegion(World world, BlockVector3 corner1, BlockVector3 corner2, Key key) { + this.world = world; + min = corner1.getMinimum(corner2); + max = corner1.getMaximum(corner2); + this.key = key; + } + + @Override + public Key getKey() { + return key; + } + + @Override + public World getWorld() { + return world; + } + + @Override + public boolean contains(BlockVector3 pos) { + double x = pos.x(); + double y = pos.y(); + double z = pos.z(); + + return x >= Math.min(getMin().x(), getMax().x()) && + x <= Math.max(getMin().x(), getMax().x()) && + y >= Math.min(getMin().y(), getMax().y()) && + y <= Math.max(getMin().y(), getMax().y()) && + z >= Math.min(getMin().z(), getMax().z()) && + z <= Math.max(getMin().z(), getMax().z()); + } + + @Override + public BlockVector3 getMin() { + return min; + } + + @Override + public BlockVector3 getMax() { + return max; + } + + @Override + public BlockVector3 getCenter() { + return new BlockVector3( + (min.x() - max.x()/2), + (min.y() - max.y()/2), + (min.z() - max.z()/2) + ); + } +} diff --git a/src/main/java/fr/openmc/core/registry/regions/types/SphericalRegion.java b/src/main/java/fr/openmc/core/registry/regions/types/SphericalRegion.java index cab10eea8..cdab13b4f 100644 --- a/src/main/java/fr/openmc/core/registry/regions/types/SphericalRegion.java +++ b/src/main/java/fr/openmc/core/registry/regions/types/SphericalRegion.java @@ -5,8 +5,6 @@ public abstract class SphericalRegion extends CustomRegion { - public abstract BlockVector3 getCenter(); - public abstract double getRadius(); @Override diff --git a/src/main/java/fr/openmc/core/registry/structures/CustomStructure.java b/src/main/java/fr/openmc/core/registry/structures/CustomStructure.java new file mode 100644 index 000000000..4bc9e05f7 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/structures/CustomStructure.java @@ -0,0 +1,320 @@ +package fr.openmc.core.registry.structures; + + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.bootstrap.integration.OMCLogger; +import fr.openmc.core.registry.structures.model.CustomStructureManager; +import fr.openmc.core.registry.structures.model.DBPlacedStructure; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.structure.Structure; +import org.bukkit.util.BlockVector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * Une structure réutilisable (ex: "maison"). Elle peut être posée autant de fois qu'on veut, + * chaque placement étant identifié par son propre UUID. + */ +public class CustomStructure { + + // Format utilisé pour stocker les undos en DB + private static final ClipboardFormat BACKUP_FORMAT = BuiltInClipboardFormat.SPONGE_V3_SCHEMATIC; + + private final OMCPlugin plugin = OMCPlugin.getInstance(); + @Getter + private final String id; + private final File file; // .schem / .schematic / .nbt + + public CustomStructure(String id, File file) { + this.id = id; + this.file = file; + } + + // ------------------------------------------------------------------ PLACE + + /** Pose la structure avec un UUID de placement généré. */ + public CompletableFuture place(Location location) { + + if (!CustomStructureManager.actionAllow()) return null; + + return place(UUID.randomUUID(), location); + } + + /** + * Pose la structure en lui donnant un identifiant UUID. + * Échoue si cet UUID est déjà utilisé par un placement existant. + * Peut être appelé depuis n'importe quel thread. + * + * @return le même UUID, une fois la structure posée et l'undo sauvegardé + */ + public CompletableFuture place(UUID placementId, Location location) { + + if (!CustomStructureManager.actionAllow()) return null; + + CompletableFuture future = new CompletableFuture<>(); + final Location loc = location.clone(); + + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + try { + if (CustomStructureManager.existsPlacement(placementId)) { + future.completeExceptionally(new IllegalStateException( + "Le placement " + placementId + " existe déjà")); + return; + } + } catch (Exception e) { + future.completeExceptionally(e); + return; + } + + Bukkit.getScheduler().runTask(plugin, () -> { + final DBPlacedStructure entry; + try { + entry = pasteWithBackup(placementId, loc); + } catch (Exception e) { + future.completeExceptionally(e); + return; + } + + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + try { + CustomStructureManager.savePlacement(entry); + future.complete(placementId); + } catch (Exception e) { + OMCLogger.error("Structure posée mais undo non sauvegardé !", e); + future.completeExceptionally(e); + } + }); + }); + }); + return future; + } + + private DBPlacedStructure pasteWithBackup(UUID placementId, Location loc) throws Exception { + + if (!CustomStructureManager.actionAllow()) return null; + + Clipboard structure = readFile(file); + com.sk89q.worldedit.world.World weWorld = BukkitAdapter.adapt(loc.getWorld()); + + // Zone du monde qui sera écrasée + BlockVector3 to = BukkitAdapter.asBlockVector(loc); + BlockVector3 offset = to.subtract(structure.getOrigin()); + + Region r = structure.getRegion(); + + BlockVector3 min = r.getMinimumPoint().add(offset); + BlockVector3 max = r.getMaximumPoint().add(offset); + + CuboidRegion area = new CuboidRegion(weWorld, min, max); + + try (EditSession session = WorldEdit.getInstance().newEditSession(weWorld)) { + // 1) sauvegarde de l'existant + BlockArrayClipboard backup = new BlockArrayClipboard(area); + ForwardExtentCopy copy = new ForwardExtentCopy(session, area, backup, min); + copy.setCopyingEntities(false); + Operations.complete(copy); + + // 2) collage + Operations.complete(new ClipboardHolder(structure) + .createPaste(session) + .to(to) + .ignoreAirBlocks(false) + .build()); + + return new DBPlacedStructure(placementId, id, loc.getWorld().getUID().toString(), + min.x(), min.y(), min.z(), serialize(backup)); + } + } + + /** Retire UN placement précis grâce à son UUID. + * @return false si introuvable. + */ + public CompletableFuture remove(UUID placementId) { + + if (!CustomStructureManager.actionAllow()) return null; + + return removeInternal(() -> CustomStructureManager.getPlacement(placementId)); + } + + /** Retire le placement le plus récent de cette structure. */ + public CompletableFuture removeLast() { + return removeInternal(() -> CustomStructureManager.getLatestPlacement(id)); + } + + /** Retire TOUS les placements de cette structure (du plus récent au plus ancien). @return le nombre retiré */ + public CompletableFuture removeAll() { + + if (!CustomStructureManager.actionAllow()) return null; + + return list().thenCompose(placements -> { + CompletableFuture chain = CompletableFuture.completedFuture(0); + for (DBPlacedStructure p : placements) { + chain = chain.thenCompose(count -> + remove(p.getId()).thenApply(ok -> ok ? count + 1 : count)); + } + return chain; + }); + } + + /** Tous les placements de cette structure, du plus récent au plus ancien. */ + public CompletableFuture> list() { + CompletableFuture> f = new CompletableFuture<>(); + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + try { f.complete(CustomStructureManager.getPlacements(id)); } + catch (Exception e) { f.completeExceptionally(e); } + }); + return f; + } + + public CompletableFuture exists(UUID placementId) { + CompletableFuture f = new CompletableFuture<>(); + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + try { f.complete(CustomStructureManager.existsPlacement(placementId)); } + catch (Exception e) { f.completeExceptionally(e); } + }); + return f; + } + + private interface Lookup { DBPlacedStructure get() throws Exception; } + + private CompletableFuture removeInternal(Lookup lookup) { + + if (!CustomStructureManager.actionAllow()) return null; + + CompletableFuture future = new CompletableFuture<>(); + + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + // 1) lecture DB + final DBPlacedStructure placed; + try { + placed = lookup.get(); + } catch (Exception e) { future.completeExceptionally(e); return; } + if (placed == null) { future.complete(false); return; } + + Bukkit.getScheduler().runTask(plugin, () -> { + // 2) restauration (main thread) + try { + World world = Bukkit.getWorld(UUID.fromString(placed.getWorld())); + if (world == null) throw new IllegalStateException("Monde introuvable"); + restore(placed, world); + } catch (Exception e) { future.completeExceptionally(e); return; } + + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + // 3) suppression de la ligne + try { + CustomStructureManager.deletePlacement(placed.getId()); + future.complete(true); + } catch (Exception e) { future.completeExceptionally(e); } + }); + }); + }); + return future; + } + + private void restore(DBPlacedStructure placed, World world) throws Exception { + + if (!CustomStructureManager.actionAllow()) return; + + Clipboard backup = deserialize(placed.getBackup()); + backup.setOrigin(backup.getRegion().getMinimumPoint()); + BlockVector3 min = BlockVector3.at(placed.getX(), placed.getY(), placed.getZ()); + + try (EditSession session = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world))) { + Operations.complete(new ClipboardHolder(backup) + .createPaste(session) + .to(min) + .ignoreAirBlocks(false) // remet aussi l'air + .build()); + } + } + + /** Lit un .schem / .schematic via WorldEdit, ou un .nbt vanilla converti en clipboard WorldEdit. */ + private static Clipboard readFile(File file) throws IOException { + + if (!CustomStructureManager.actionAllow()) return null; + + if (file.getName().toLowerCase().endsWith(".nbt")) { + return readNbt(file); + } + ClipboardFormat format = ClipboardFormats.findByFile(file); + if (format == null) throw new IOException("Format de schematic inconnu : " + file.getName()); + try (ClipboardReader reader = format.getReader(new FileInputStream(file))) { + return reader.read(); + } + } + + /** + * Convertit un .nbt (structure block) en clipboard WorldEdit, sans rien placer dans le monde. + * Limites : le contenu des tile entities (coffres, panneaux...) et les entités ne sont pas conservés. + */ + private static Clipboard readNbt(File file) throws IOException { + + if (!CustomStructureManager.actionAllow()) return null; + + Structure structure = Bukkit.getStructureManager().loadStructure(file); + if (structure.getPaletteCount() == 0) throw new IOException("Structure vide : " + file.getName()); + + BlockVector size = structure.getSize(); + CuboidRegion region = new CuboidRegion( + BlockVector3.ZERO, + BlockVector3.at(size.getBlockX() - 1, size.getBlockY() - 1, size.getBlockZ() - 1)); + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(BlockVector3.ZERO); + + try { + for (org.bukkit.block.BlockState state : structure.getPalettes().get(0).getBlocks()) { + BlockVector3 pos = BlockVector3.at(state.getX(), state.getY(), state.getZ()); + clipboard.setBlock(pos, BukkitAdapter.adapt(state.getBlockData())); + } + } catch (Exception e) { + throw new IOException("Conversion du .nbt impossible : " + file.getName(), e); + } + return clipboard; + } + + private static byte[] serialize(Clipboard clipboard) throws IOException { + + if (!CustomStructureManager.actionAllow()) return null; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (ClipboardWriter writer = BACKUP_FORMAT.getWriter(out)) { + writer.write(clipboard); + } + return out.toByteArray(); + } + + private static Clipboard deserialize(byte[] data) throws IOException { + + if (!CustomStructureManager.actionAllow()) return null; + + try (ClipboardReader reader = BACKUP_FORMAT.getReader(new ByteArrayInputStream(data))) { + return reader.read(); + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/structures/CustomStructureRegistry.java b/src/main/java/fr/openmc/core/registry/structures/CustomStructureRegistry.java new file mode 100644 index 000000000..5487afe73 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/structures/CustomStructureRegistry.java @@ -0,0 +1,40 @@ +package fr.openmc.core.registry.structures; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.bootstrap.features.types.LoadIfEnable; +import fr.openmc.core.bootstrap.registries.KeyedRegistry; +import fr.openmc.core.bootstrap.registries.Registry; +import fr.openmc.core.hooks.WorldEditHook; +import fr.openmc.core.utils.FilesUtils; + +import java.io.File; + +public class CustomStructureRegistry extends Registry + implements KeyedRegistry, LoadIfEnable { + + private static final File structureFolder = new File(OMCPlugin.getInstance().getDataFolder(), "data/omc_structure"); + + public static final String structureKey = "omc_structure"; + + // ** REGISTER STRUCTURES ** + public final CustomStructure WITCH_SHOP = register("test","pillar.nbt"); + + public CustomStructureRegistry() { + FilesUtils.createDirectoryIfNotExists(structureFolder); + } + + @Override + public String key(CustomStructure registryObject) { + return registryObject.getId(); + } + + public CustomStructure register(String name, String fileName) { + return register(name, new File(structureFolder, fileName)); + } + + public CustomStructure register(String name, File file) { + if (file == null || name == null) return null; + if (!file.exists()) return null; + return KeyedRegistry.super.register(new CustomStructure((structureKey + name), file)); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/structures/model/CustomStructureManager.java b/src/main/java/fr/openmc/core/registry/structures/model/CustomStructureManager.java new file mode 100644 index 000000000..992a7fb98 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/structures/model/CustomStructureManager.java @@ -0,0 +1,79 @@ +package fr.openmc.core.registry.structures.model; + +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.bootstrap.features.Feature; +import fr.openmc.core.bootstrap.features.types.HasDatabase; +import fr.openmc.core.bootstrap.integration.OMCLogger; +import fr.openmc.core.hooks.WorldEditHook; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; + +/** + * Feature qui porte uniquement la table des undos de structures. + * Les accès DB sont synchrones : à appeler hors du thread principal (voir CustomStructure). + */ +public class CustomStructureManager extends Feature implements HasDatabase { + + private static Dao dao; + + public static boolean WELoaded = false; + + @Override + public void init() { + WELoaded = WorldEditHook.isEnable(); + } + + @Override + public void initDB(ConnectionSource connectionSource) throws SQLException { + TableUtils.createTableIfNotExists(connectionSource, DBPlacedStructure.class); + dao = DaoManager.createDao(connectionSource, DBPlacedStructure.class); + } + +// @Override +// protected void save() { +// +// } + + public static void savePlacement(DBPlacedStructure p) throws SQLException { + dao.create(p); + } + + public static DBPlacedStructure getPlacement(UUID id) throws SQLException { + return dao.queryForId(id); + } + + public static boolean existsPlacement(UUID id) throws SQLException { + return dao.idExists(id); + } + + public static DBPlacedStructure getLatestPlacement(String structureId) throws SQLException { + return dao.queryBuilder() + .orderBy("createdAt", false) + .where().eq("structureId", structureId) + .queryForFirst(); + } + + /** Du plus récent au plus ancien. */ + public static List getPlacements(String structureId) throws SQLException { + return dao.queryBuilder() + .orderBy("createdAt", false) + .where().eq("structureId", structureId) + .query(); + } + + public static void deletePlacement(UUID id) throws SQLException { + dao.deleteById(id); + } + + public static boolean actionAllow() { + if (!WELoaded) + OMCLogger.warn("Action prevent from use because WE or FAWE not enable"); + return WELoaded; + } +} + diff --git a/src/main/java/fr/openmc/core/registry/structures/model/DBPlacedStructure.java b/src/main/java/fr/openmc/core/registry/structures/model/DBPlacedStructure.java new file mode 100644 index 000000000..96c1c21db --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/structures/model/DBPlacedStructure.java @@ -0,0 +1,48 @@ +package fr.openmc.core.registry.structures.model; + + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; +import lombok.Getter; + +import java.util.UUID; + +/** Une instance de structure posée dans le monde + la sauvegarde de ce qu'il y avait avant (undo). */ +@Getter +@DatabaseTable(tableName = "structures_placed") +public class DBPlacedStructure { + + /** Identifiant unique de CE placement (pas de la structure). */ + @DatabaseField(id = true, dataType = DataType.UUID) + private UUID id; + + /** Id de la CustomStructure (ex: "maison"). Plusieurs placements peuvent avoir le même. */ + @DatabaseField(canBeNull = false, index = true) + private String structureId; + + @DatabaseField(canBeNull = false) + private String world; // UUID du monde + + // Coin minimum de la zone sauvegardée + @DatabaseField private int x; + @DatabaseField private int y; + @DatabaseField private int z; + + @DatabaseField(dataType = DataType.BYTE_ARRAY, canBeNull = false) + private byte[] backup; // schematic WorldEdit de la zone AVANT placement + + @DatabaseField private long createdAt; + + public DBPlacedStructure() {} // requis par ORMLite + + public DBPlacedStructure(UUID id, String structureId, String world, int x, int y, int z, byte[] backup) { + this.id = id; + this.structureId = structureId; + this.world = world; + this.x = x; this.y = y; this.z = z; + this.backup = backup; + this.createdAt = System.currentTimeMillis(); + } + +} diff --git a/src/main/java/fr/openmc/core/utils/RandomUtils.java b/src/main/java/fr/openmc/core/utils/RandomUtils.java index 38107091f..d99a311d4 100644 --- a/src/main/java/fr/openmc/core/utils/RandomUtils.java +++ b/src/main/java/fr/openmc/core/utils/RandomUtils.java @@ -1,6 +1,7 @@ package fr.openmc.core.utils; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -48,4 +49,24 @@ public static List generateRandomOrder(List inital) { Collections.shuffle(shuffle); return shuffle; } + + /** + * Prends une liste initial et retourne une entrée de celle-ci. + * @param list la liste initial + * @return l'entrée aléatoirement choisie + */ + public static T getRandomObjectFromList(List list) { + return list.get(ThreadLocalRandom.current().nextInt(list.size())); + } + + /** + * Sélectionne et retourne un objet choisi aléatoirement parmi ceux fournis. + * + * @param objects les objets parmi lesquels effectuer la sélection + * @return un objet choisi aléatoirement + */ + @SafeVarargs + public static T getRandomObject(T... objects) { + return getRandomObjectFromList(Arrays.stream(objects).toList()); + } } \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/utils/text/messages/Prefix.java b/src/main/java/fr/openmc/core/utils/text/messages/Prefix.java index bf240ee3b..ee8651e4e 100644 --- a/src/main/java/fr/openmc/core/utils/text/messages/Prefix.java +++ b/src/main/java/fr/openmc/core/utils/text/messages/Prefix.java @@ -37,7 +37,8 @@ public enum Prefix { HALLOWEEN("halloween"), DIMOPENER("dimopener"), GOLDEN_HARVEST("golden harvest"), - MIRACULOUS_FISHING("miraculous fishing"); + MIRACULOUS_FISHING("miraculous fishing"), + LUCKYBLOCK("luckyblock"); @Getter private final Component prefix; diff --git a/src/main/resources/contents/omc_daily_events/items.yml b/src/main/resources/contents/omc_daily_events/items.yml index 8bd96d2aa..f96c925bf 100644 --- a/src/main/resources/contents/omc_daily_events/items.yml +++ b/src/main/resources/contents/omc_daily_events/items.yml @@ -66,6 +66,71 @@ items: - - + candy_red: + display_name: + resource: + generate: true + textures: + - candy/candy_red + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_yellow: + display_name: + resource: + generate: true + textures: + - candy/candy_yellow + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_green: + display_name: + resource: + generate: true + textures: + - candy/candy_green + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_blue: + display_name: + resource: + generate: true + textures: + - candy/candy_blue + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_brown: + display_name: + resource: + generate: true + textures: + - candy/candy_brown + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + recipes: iaa_crafting_table: fermentum: diff --git a/src/main/resources/contents/omc_halloween/configs/fonts.yml b/src/main/resources/contents/omc_halloween/configs/fonts.yml new file mode 100644 index 000000000..e17c69365 --- /dev/null +++ b/src/main/resources/contents/omc_halloween/configs/fonts.yml @@ -0,0 +1,33 @@ +info: + namespace: omc_halloween +font_images: + candy_red: + permission: candy_red + show_in_gui: true + path: "candy/candy_red.png" + scale_ratio: 12 + y_position: 9 + candy_yellow: + permission: candy_yellow + show_in_gui: true + path: "candy/candy_yellow.png" + scale_ratio: 12 + y_position: 9 + candy_green: + permission: candy_green + show_in_gui: true + path: "candy/candy_green.png" + scale_ratio: 12 + y_position: 9 + candy_blue: + permission: candy_blue + show_in_gui: true + path: "candy/candy_blue.png" + scale_ratio: 12 + y_position: 9 + candy_brown: + permission: candy_brown + show_in_gui: true + path: "candy/candy_brown.png" + scale_ratio: 12 + y_position: 9 \ No newline at end of file diff --git a/src/main/resources/contents/omc_halloween/configs/items.yml b/src/main/resources/contents/omc_halloween/configs/items.yml new file mode 100644 index 000000000..040fa93ed --- /dev/null +++ b/src/main/resources/contents/omc_halloween/configs/items.yml @@ -0,0 +1,110 @@ +info: + namespace: omc_halloween + +items: + candy_red: + display_name: + resource: + generate: true + textures: + - candy/candy_red + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_yellow: + display_name: + resource: + generate: true + textures: + - candy/candy_yellow + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_green: + display_name: + resource: + generate: true + textures: + - candy/candy_green + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_blue: + display_name: + resource: + generate: true + textures: + - candy/candy_blue + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + + candy_brown: + display_name: + resource: + generate: true + textures: + - candy/candy_brown + material: PAPER + lore: + - + consumable: + nutrition: 1 + saturation: 0.6 + +recipes: + iaa_crafting_table: + fermentum: + shapeless: true + ingredients: + - item: omc_daily_events:golden_wheat + amount: 5 + - item: omc_daily_events:golden_beetroot + amount: 5 + - item: omc_daily_events:really_golden_carrot + amount: 5 + - item: omc_daily_events:golden_potato + amount: 5 + - item: omc_daily_events:golden_melon + amount: 5 + - item: omc_daily_events:golden_pumpkin + amount: 5 + result: + item: omc_daily_events:fermentum + + blessed_bread: + enabled: true + pattern: + - FFF + - XXX + - XXX + pattern_2: + - XXX + - FFF + - XXX + pattern_3: + - XXX + - XXX + - FFF + ingredients: + F: + item: omc_daily_events:golden_wheat + amount: 192 + result: + item: omc_daily_events:blessed_bread + amount: 1 \ No newline at end of file diff --git a/src/main/resources/contents/omc_halloween/textures/candy/candy_blue.png b/src/main/resources/contents/omc_halloween/textures/candy/candy_blue.png new file mode 100644 index 000000000..be6fd4e73 Binary files /dev/null and b/src/main/resources/contents/omc_halloween/textures/candy/candy_blue.png differ diff --git a/src/main/resources/contents/omc_halloween/textures/candy/candy_brown.png b/src/main/resources/contents/omc_halloween/textures/candy/candy_brown.png new file mode 100644 index 000000000..fcfae29a3 Binary files /dev/null and b/src/main/resources/contents/omc_halloween/textures/candy/candy_brown.png differ diff --git a/src/main/resources/contents/omc_halloween/textures/candy/candy_green.png b/src/main/resources/contents/omc_halloween/textures/candy/candy_green.png new file mode 100644 index 000000000..acac6625f Binary files /dev/null and b/src/main/resources/contents/omc_halloween/textures/candy/candy_green.png differ diff --git a/src/main/resources/contents/omc_halloween/textures/candy/candy_red.png b/src/main/resources/contents/omc_halloween/textures/candy/candy_red.png new file mode 100644 index 000000000..8e564fb23 Binary files /dev/null and b/src/main/resources/contents/omc_halloween/textures/candy/candy_red.png differ diff --git a/src/main/resources/contents/omc_halloween/textures/candy/candy_yellow.png b/src/main/resources/contents/omc_halloween/textures/candy/candy_yellow.png new file mode 100644 index 000000000..55ef12b3d Binary files /dev/null and b/src/main/resources/contents/omc_halloween/textures/candy/candy_yellow.png differ diff --git a/src/main/resources/contents/omc_icons/configs/fonts.yml b/src/main/resources/contents/omc_icons/configs/fonts.yml index d72d0d785..2a214f727 100644 --- a/src/main/resources/contents/omc_icons/configs/fonts.yml +++ b/src/main/resources/contents/omc_icons/configs/fonts.yml @@ -30,4 +30,4 @@ font_images: show_in_gui: true path: "font/bits.png" scale_ratio: 12 - y_position: 9 \ No newline at end of file + y_position: 9 diff --git a/src/main/resources/contents/omc_menus/configs/menus.yml b/src/main/resources/contents/omc_menus/configs/menus.yml index 6732011ac..411ed932c 100644 --- a/src/main/resources/contents/omc_menus/configs/menus.yml +++ b/src/main/resources/contents/omc_menus/configs/menus.yml @@ -61,6 +61,12 @@ font_images: suggestion_in_command: false path: "city_template6x9.png" y_position: 30 + halloween_event_main_menu: + permission: halloween.event_main_menu + show_in_gui: true + suggestion_in_command: false + path: "halloween_event_main_menu.png" + y_position: 30 home_mailbox: permission: mailbox.home show_in_gui: true diff --git a/src/main/resources/contents/omc_menus/textures/halloween_event_main_menu.png b/src/main/resources/contents/omc_menus/textures/halloween_event_main_menu.png new file mode 100644 index 000000000..dbf83e2ba Binary files /dev/null and b/src/main/resources/contents/omc_menus/textures/halloween_event_main_menu.png differ diff --git a/src/main/resources/data/witch_house_spawn.yml b/src/main/resources/data/witch_house_spawn.yml new file mode 100644 index 000000000..061641011 --- /dev/null +++ b/src/main/resources/data/witch_house_spawn.yml @@ -0,0 +1,5 @@ +spawn: + world: world # Nom du monde + x: 0.0 # Coordonnées X + y: 0.0 # Coordonnées Y + z: 0.0 # Coordonnées Z \ No newline at end of file diff --git a/src/main/resources/datapacks/omc_halloween/data/omc_halloween/dimension/witch_house.json b/src/main/resources/datapacks/omc_halloween/data/omc_halloween/dimension/witch_house.json new file mode 100644 index 000000000..25b5d79f6 --- /dev/null +++ b/src/main/resources/datapacks/omc_halloween/data/omc_halloween/dimension/witch_house.json @@ -0,0 +1,12 @@ +{ + "type": "minecraft:overworld", + "generator": { + "type": "minecraft:flat", + "settings": { + "biome": "draft:draft", + "lakes": false, + "features": false, + "layers": [] + } + } +} \ No newline at end of file diff --git a/src/main/resources/datapacks/omc_halloween/pack.mcmeta b/src/main/resources/datapacks/omc_halloween/pack.mcmeta new file mode 100644 index 000000000..a409a42ca --- /dev/null +++ b/src/main/resources/datapacks/omc_halloween/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "Le Pack de données de la Witch House d'OpenMC", + "pack_format": 107.1, + "min_format": 107.1, + "max_format": 107.1 + } +} \ No newline at end of file