diff --git a/src/main/java/de/mviper/adventskalender/AdventCalendarExpansion.java b/src/main/java/de/mviper/adventskalender/AdventCalendarExpansion.java new file mode 100644 index 0000000..0a54fd3 --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/AdventCalendarExpansion.java @@ -0,0 +1,69 @@ +package de.mviper.adventskalender; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.time.LocalDate; +import java.time.Month; + +public class AdventCalendarExpansion extends PlaceholderExpansion { + + @Override + public @NotNull String getIdentifier() { + return "ak"; + } + + @Override + public @NotNull String getAuthor() { + return "M_Viper"; + } + + @Override + public @NotNull String getVersion() { + return "2.0.0"; + } + + @Override + public boolean persist() { + return true; // Expansion bleibt nach PAPI-Reload registriert + } + + @Override + public String onPlaceholderRequest(Player player, String identifier) { + if (player == null) return ""; + + // Aktuellen Tag ermitteln (Berücksichtigt den test_day aus der Config) + int testDay = Adventskalender.getInstance().getConfig().getInt("general.test_day", 0); + LocalDate date = LocalDate.now(); + int currentDay = (testDay > 0) ? testDay : (date.getMonth() == Month.DECEMBER ? date.getDayOfMonth() : 0); + + switch (identifier.toLowerCase()) { + + // %ak_claimed% - Anzahl der bereits geöffneten Türchen + case "claimed": + return String.valueOf(DataHandler.getClaimedDays(player).size()); + + // %ak_day% - Der aktuelle Tag des Adventskalenders (1-24) + case "day": + return String.valueOf(currentDay); + + // %ak_has_claimed_today% - Gibt "Ja" oder "Nein" zurück, ob heute schon geöffnet wurde + case "has_claimed_today": + if (currentDay == 0) return "Keine Adventszeit"; + return DataHandler.hasClaimed(player, currentDay) ? "Ja" : "Nein"; + + // %ak_remaining% - Wie viele Türchen sind noch offen (für diesen Spieler)? + case "remaining": + int claimedCount = DataHandler.getClaimedDays(player).size(); + return String.valueOf(24 - claimedCount); + + // %ak_is_global% - Zeigt an, ob der globale Modus aktiv ist + case "is_global": + boolean isGlobal = Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar", false); + return isGlobal ? "Global" : "Spieler-basiert"; + } + + return null; // Placeholder unbekannt + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/AdventCommand.java b/src/main/java/de/mviper/adventskalender/AdventCommand.java new file mode 100644 index 0000000..07e2e8a --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/AdventCommand.java @@ -0,0 +1,160 @@ +package de.mviper.adventskalender; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import java.util.*; +import java.util.stream.Collectors; + +public class AdventCommand implements CommandExecutor, TabCompleter { + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (args.length > 0 && args[0].equalsIgnoreCase("admin")) { + if (!sender.hasPermission("adventskalender.admin")) { + sender.sendMessage(LanguageManager.getMessage("messages.no_permission")); + return true; + } + + // /ak admin reload + if (args.length == 2 && args[1].equalsIgnoreCase("reload")) { + Adventskalender.getInstance().reloadConfig(); + LanguageManager.setup(); + DataHandler.setup(); + sender.sendMessage(LanguageManager.getMessage("messages.admin.reload_success")); + return true; + } + + // /ak admin test + if (args.length == 3 && args[1].equalsIgnoreCase("test")) { + if (args[2].equalsIgnoreCase("reset")) { + Adventskalender.getInstance().getConfig().set("general.test_day", 0); + Adventskalender.getInstance().saveConfig(); + sender.sendMessage("§e[Admin] §7Test-Modus §cbeendet§7. Das echte Datum wird nun verwendet."); + return true; + } + + try { + int day = Integer.parseInt(args[2]); + if (day < 1 || day > 24) { + sender.sendMessage("§cBitte gib einen Tag zwischen 1 und 24 an (oder 'reset')."); + return true; + } + Adventskalender.getInstance().getConfig().set("general.test_day", day); + Adventskalender.getInstance().saveConfig(); + sender.sendMessage("§e[Admin] §7Test-Tag auf §6" + day + " §7gesetzt."); + } catch (NumberFormatException e) { + sender.sendMessage("§cBitte gib eine gültige Zahl (1-24) oder 'reset' an."); + } + return true; + } + + // /ak admin resetday + if (args.length == 4 && args[1].equalsIgnoreCase("resetday")) { + String target = args[2]; + try { + int day = Integer.parseInt(args[3]); + if (day < 1 || day > 24) throw new NumberFormatException(); + + if (target.equalsIgnoreCase("global")) { + DataHandler.GlobalDataManager.resetGlobalDay(day); + sender.sendMessage("§a[Admin] §7Tag §6" + day + " §7global zurückgesetzt."); + } else { + DataHandler.resetPlayerDay(target, day); + sender.sendMessage("§a[Admin] §7Tag §6" + day + " §7für §e" + target + " §7zurückgesetzt."); + } + } catch (NumberFormatException e) { + sender.sendMessage("§cTag muss zwischen 1 und 24 liegen."); + } + return true; + } + + // /ak admin reset + if (args.length >= 3 && args[1].equalsIgnoreCase("reset")) { + String baseTitle = LanguageManager.getString("gui.title"); + if (args[2].equalsIgnoreCase("global")) { + DataHandler.resetAll(); + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.getOpenInventory().getTitle().contains(baseTitle)) online.closeInventory(); + } + sender.sendMessage("§a[Admin] §7Kompletter Reset durchgeführt."); + } else { + String targetName = args[2]; + Player target = Bukkit.getPlayer(targetName); + if (target != null) { + DataHandler.resetPlayer(target); + if (target.getOpenInventory().getTitle().contains(baseTitle)) target.closeInventory(); + sender.sendMessage("§a[Admin] §7Daten für §e" + target.getName() + " §7gelöscht."); + } else { + DataHandler.resetOfflinePlayer(targetName); + sender.sendMessage("§a[Admin] §7Offline-Daten für §e" + targetName + " §7gelöscht."); + } + } + return true; + } + + sender.sendMessage("§8§m---§r §6Advent Admin §8§m---"); + sender.sendMessage("§e/ak admin reload §7- Config neu laden"); + sender.sendMessage("§e/ak admin test <1-24/reset> §7- Datum simulieren"); + sender.sendMessage("§e/ak admin resetday §7- Tag löschen"); + sender.sendMessage("§e/ak admin reset §7- Alles löschen"); + return true; + } + + if (!(sender instanceof Player player)) return true; + if (!player.hasPermission("adventskalender.use")) { + player.sendMessage(LanguageManager.getMessage("messages.no_permission")); + return true; + } + + AdventInventory.open(player); + return true; + } + + @Override + public List onTabComplete(CommandSender s, Command c, String a, String[] args) { + if (args.length == 1) return Collections.singletonList("admin"); + + if (args.length == 2 && args[0].equalsIgnoreCase("admin")) + return Arrays.asList("reload", "test", "reset", "resetday"); + + // Tab-Completion für das Ziel (global oder Spielername) + if (args.length == 3 && (args[1].equalsIgnoreCase("reset") || args[1].equalsIgnoreCase("resetday"))) { + List options = new ArrayList<>(); + options.add("global"); + Bukkit.getOnlinePlayers().forEach(p -> options.add(p.getName())); + return options.stream() + .filter(opt -> opt.toLowerCase().startsWith(args[2].toLowerCase())) + .collect(Collectors.toList()); + } + + // Tab-Completion für die TAGE (Numerisch sortiert) + if ((args.length == 3 && args[1].equalsIgnoreCase("test")) || (args.length == 4 && args[1].equalsIgnoreCase("resetday"))) { + List options = new ArrayList<>(); + + // "reset" Option nur für den test command hinzufügen + if (args[1].equalsIgnoreCase("test")) { + options.add("reset"); + } + + for (int i = 1; i <= 24; i++) { + options.add(String.valueOf(i)); + } + + String currentInput = (args.length == 3) ? args[2] : args[3]; + + return options.stream() + .filter(opt -> opt.toLowerCase().startsWith(currentInput.toLowerCase())) + .sorted((s1, s2) -> { + // "reset" immer nach oben sortieren + if (s1.equalsIgnoreCase("reset")) return -1; + if (s2.equalsIgnoreCase("reset")) return 1; + // Restliche Zahlen numerisch vergleichen + return Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2)); + }) + .collect(Collectors.toList()); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/AdventInventory.java b/src/main/java/de/mviper/adventskalender/AdventInventory.java new file mode 100644 index 0000000..b28f617 --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/AdventInventory.java @@ -0,0 +1,154 @@ +package de.mviper.adventskalender; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerProfile; +import org.bukkit.profile.PlayerTextures; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.Month; +import java.util.*; + +public class AdventInventory { + + // Textur für das geöffnete Geschenk (Wreath) + private static final String OPEN_WREATH_URL = "http://textures.minecraft.net/texture/5e39248b341c87ce3e4294ac214c6f74468889cb1273ae7412906fd28db097e8"; + + // Texturen für die geschlossenen Tage (1-24) + private static final Map CLOSED_TEXTURES = new HashMap<>(); + private static final Map HEAD_CACHE = new HashMap<>(); + + static { + // Hier sind die URLs für die "Christmas Calendar #01-24 (red)" Reihe + CLOSED_TEXTURES.put(1, "http://textures.minecraft.net/texture/105b356be68ac56cfe0611b95027adf1ee67050e4cd66a0a99678fc2e25b6b0f"); + CLOSED_TEXTURES.put(2, "http://textures.minecraft.net/texture/40ec45e8ffb9cb714d1412bb9ee54fd1e7a3140c02e35ae0d3f308f1666ba126"); + CLOSED_TEXTURES.put(3, "http://textures.minecraft.net/texture/2466629a31aae22dba15a7b710a4517308656c19ba39a89ac2de2915a46e2823"); + CLOSED_TEXTURES.put(4, "http://textures.minecraft.net/texture/97a1956ed6f32e18b139f2d709355ac6945d0ab8fecd04d4c159d08006bfbe58"); + CLOSED_TEXTURES.put(5, "http://textures.minecraft.net/texture/855fad012c8844fc313510436b4919e002d93d6a0761f7bdaf78069683eee5e6"); + CLOSED_TEXTURES.put(6, "http://textures.minecraft.net/texture/c0400111ae026101077b59d8482c84a832c74f4f2105fc13b7c94f5b8000ea3"); + CLOSED_TEXTURES.put(7, "http://textures.minecraft.net/texture/a34505ed2567ea2efbab49977f7da8f1178db9ae53a0e99737286dc56e63636a"); + CLOSED_TEXTURES.put(8, "http://textures.minecraft.net/texture/a17ddf12485fb54cab9c9ffb55feea62a2d90b825772c6ea38561ce750cb6f83"); + CLOSED_TEXTURES.put(9, "http://textures.minecraft.net/texture/f75022feb11b736e50bc7d19d26d4ee00cecd6734a2138d2579f799b119b6734"); + CLOSED_TEXTURES.put(10, "http://textures.minecraft.net/texture/6b84208e4be2233fcd96e7f02d9613d5437f1d006236277c57d40652c5be2f23"); + + CLOSED_TEXTURES.put(11, "http://textures.minecraft.net/texture/19aa8a65ba29b4b7cbb9c66cf59fb537ba2c3013a958620b976b9f06248e8dda"); + CLOSED_TEXTURES.put(12, "http://textures.minecraft.net/texture/7dc1330b302e800c31c8dfefbd1b996016c34649f3a396749d3e048637364aad"); + CLOSED_TEXTURES.put(13, "http://textures.minecraft.net/texture/7e98a3884e0eac967c73a2bcbff7df62ab2fb327b4ceb9e636c74254866b5c7c"); + CLOSED_TEXTURES.put(14, "http://textures.minecraft.net/texture/3866643decb2d148999f9768dae89fbdb196b30d55f8846b1fbbe736bb7e5042"); + CLOSED_TEXTURES.put(15, "http://textures.minecraft.net/texture/744a95611950cc7f154c9f56ad472c1941376ec171efb8ba476e6cec4f866526"); + CLOSED_TEXTURES.put(16, "http://textures.minecraft.net/texture/1546959556eff4b8827ca50d8df686e8f20d5c843da689dddc48bff0a217efbe"); + CLOSED_TEXTURES.put(17, "http://textures.minecraft.net/texture/1f4fb00e824c97d9cfba4c44def27a79513978979515048f2e69eb5b7123c159"); + CLOSED_TEXTURES.put(18, "http://textures.minecraft.net/texture/9216b42018004a86081b5c1ee87dc8f12770f1859266e2cd359b75b44b5e7680"); + CLOSED_TEXTURES.put(19, "http://textures.minecraft.net/texture/93c7097291042c394267892c56feee9ba3936826ea5b2232f46cb5474f9cd937"); + CLOSED_TEXTURES.put(20, "http://textures.minecraft.net/texture/4f85a13977d01865a9d95d3279528285872551c36fd5231c7e3ab9897a7560fd"); + + CLOSED_TEXTURES.put(21, "http://textures.minecraft.net/texture/cdb512403f3610966d10bd3ca9fc61fdc5021268382f69719ac5679f3efd4c14"); + CLOSED_TEXTURES.put(22, "http://textures.minecraft.net/texture/cadaef824771250374fc3e82efe020ab765e665340cc2393f3b4ae8d6d18529c"); + CLOSED_TEXTURES.put(23, "http://textures.minecraft.net/texture/266f896c37a9d7fb857ff8c9f6f5e3540d17e1b893419f523eb199b71e71dde3"); + CLOSED_TEXTURES.put(24, "http://textures.minecraft.net/texture/f081f9274e4ef802453ac95381c00426e462440d3bb085fb3afefa6ded6a8d01"); + // Hinweis: In der Praxis füllst du hier alle 1-24 mit den URLs von Minecraft-Heads. + } + + public static void initCache() { + // Cache den Wreath-Kopf + HEAD_CACHE.put("OPENED", createBaseSkull(OPEN_WREATH_URL)); + + // Cache alle nummerierten geschlossenen Köpfe + for (int i = 1; i <= 24; i++) { + if (CLOSED_TEXTURES.containsKey(i)) { + HEAD_CACHE.put("CLOSED_" + i, createBaseSkull(CLOSED_TEXTURES.get(i))); + } + } + } + + public static void open(Player player) { + String title = " " + LanguageManager.getString("gui.title"); + Inventory inv = Bukkit.createInventory(null, 54, title); + + int testDay = Adventskalender.getInstance().getConfig().getInt("general.test_day", 0); + LocalDate date = LocalDate.now(); + int currentDay = (testDay > 0) ? testDay : (date.getMonth() == Month.DECEMBER ? date.getDayOfMonth() : 0); + + for (int i = 0; i < 54; i++) { + if (i < 9 || i >= 45 || i % 9 == 0 || (i + 1) % 9 == 0) { + Material m = (i % 2 == 0) ? Material.RED_STAINED_GLASS_PANE : Material.GREEN_STAINED_GLASS_PANE; + inv.setItem(i, createFiller(m)); + } else { + inv.setItem(i, createFiller(Material.WHITE_STAINED_GLASS_PANE)); + } + } + + int[] slots = {10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 39, 40, 41}; + + for (int day = 1; day <= 24; day++) { + boolean claimed = DataHandler.hasClaimed(player, day); + boolean isAvailable = currentDay >= day; + inv.setItem(slots[day - 1], createGiftItem(day, claimed, isAvailable)); + } + + player.openInventory(inv); + } + + private static ItemStack createGiftItem(int day, boolean claimed, boolean isAvailable) { + ItemStack item; + if (claimed) { + item = HEAD_CACHE.getOrDefault("OPENED", new ItemStack(Material.PLAYER_HEAD)).clone(); + } else { + item = HEAD_CACHE.getOrDefault("CLOSED_" + day, new ItemStack(Material.PLAYER_HEAD)).clone(); + } + + ItemMeta meta = item.getItemMeta(); + List lore = new ArrayList<>(); + lore.add(" "); + + if (claimed) { + meta.setDisplayName(LanguageManager.getString("rewards.day_" + day + ".name")); + meta.addEnchant(Enchantment.LUCK, 1, true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + lore.add("§8» §6Bereits abgeholt"); + lore.add("§aDu hast dieses Geschenk bereits geöffnet! ✔"); + } else { + if (!isAvailable) { + meta.setDisplayName("§c§lTag #" + day); + lore.add("§8» §7Noch verschlossen"); + lore.add("§7Komme am " + day + ". Dezember wieder!"); + } else { + meta.setDisplayName(LanguageManager.getString("rewards.day_" + day + ".name")); + lore.add("§8» §eKlicke zum Öffnen!"); + lore.addAll(LanguageManager.getStringList("rewards.day_" + day + ".lore")); + } + } + + meta.setLore(lore); + item.setItemMeta(meta); + return item; + } + + private static ItemStack createBaseSkull(String url) { + ItemStack item = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta) item.getItemMeta(); + try { + UUID uuid = UUID.nameUUIDFromBytes(url.getBytes()); + PlayerProfile profile = Bukkit.createPlayerProfile(uuid, "AdventHead"); + profile.getTextures().setSkin(new URL(url)); + meta.setOwnerProfile(profile); + item.setItemMeta(meta); + } catch (MalformedURLException e) { e.printStackTrace(); } + return item; + } + + private static ItemStack createFiller(Material material) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { meta.setDisplayName(" "); item.setItemMeta(meta); } + return item; + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/AdventListener.java b/src/main/java/de/mviper/adventskalender/AdventListener.java new file mode 100644 index 0000000..fee86a4 --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/AdventListener.java @@ -0,0 +1,135 @@ +package de.mviper.adventskalender; + +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.hover.content.Text; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import java.time.LocalDate; +import java.time.Month; +import java.util.List; +import java.util.Map; + +public class AdventListener implements Listener { + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + // Nachricht an Admins senden, wenn ein Update da ist + if (player.hasPermission("adventskalender.admin")) { + // Delay von 2 Sekunden (40 Ticks), damit die Nachricht nicht im Join-Spam untergeht + Bukkit.getScheduler().runTaskLater(Adventskalender.getInstance(), () -> { + if (Adventskalender.getInstance().isUpdateAvailable()) { + String latest = Adventskalender.getInstance().getLatestVersion(); + String current = Adventskalender.getInstance().getDescription().getVersion(); + + player.sendMessage("§7 "); + player.sendMessage("§8§m+-------------------------------------------+"); + player.sendMessage("§6§l ADVENTSKALENDER UPDATE"); + player.sendMessage("§7 "); + player.sendMessage("§7 Eine neue Version ist verfügbar: §a§l" + latest); + player.sendMessage("§7 Installierte Version: §c" + current); + player.sendMessage("§7 "); + + // Erstellung der klickbaren Nachricht + TextComponent message = new TextComponent("§e §nKLICKE HIER ZUM DOWNLOAD"); + message.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.spigotmc.org/resources/130974/")); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("§7Öffnet die Spigot-Seite"))); + + player.spigot().sendMessage(message); + + player.sendMessage("§7 "); + player.sendMessage("§8§m+-------------------------------------------+"); + player.sendMessage("§7 "); + } + }, 40L); + } + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + String baseTitle = LanguageManager.getString("gui.title"); + if (!event.getView().getTitle().contains(baseTitle)) return; + + event.setCancelled(true); + + if (!(event.getWhoClicked() instanceof Player player)) return; + + ItemStack clicked = event.getCurrentItem(); + if (clicked == null || clicked.getType() != Material.PLAYER_HEAD) return; + if (!clicked.hasItemMeta() || !clicked.getItemMeta().hasDisplayName()) return; + + String displayName = ChatColor.stripColor(clicked.getItemMeta().getDisplayName()); + int day; + try { + day = Integer.parseInt(displayName.replaceAll("[^0-9]", "")); + } catch (NumberFormatException e) { + return; + } + + int testDay = Adventskalender.getInstance().getConfig().getInt("general.test_day", 0); + LocalDate date = LocalDate.now(); + int currentDay = (testDay > 0) ? testDay : (date.getMonth() == Month.DECEMBER ? date.getDayOfMonth() : 0); + + if (currentDay >= day) { + if (DataHandler.hasClaimed(player, day)) { + player.sendMessage(LanguageManager.getMessage("messages.day_already_claimed")); + player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f); + return; + } + + giveReward(player, day); + DataHandler.setClaimed(player, day); + player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1.5f); + + AdventInventory.open(player); + } else { + player.sendMessage(LanguageManager.getMessage("messages.day_not_available")); + player.playSound(player.getLocation(), Sound.BLOCK_CHEST_LOCKED, 1f, 1f); + } + } + + private void giveReward(Player player, int day) { + String path = "rewards." + day; + String matName = Adventskalender.getInstance().getConfig().getString(path + ".material", "COOKIE"); + int amount = Adventskalender.getInstance().getConfig().getInt(path + ".amount", 1); + + ItemStack reward = new ItemStack(Material.valueOf(matName.toUpperCase()), amount); + ItemMeta meta = reward.getItemMeta(); + + if (meta != null) { + List> enchants = Adventskalender.getInstance().getConfig().getMapList(path + ".enchantments"); + for (Map entry : enchants) { + String type = (String) entry.get("type"); + Object levelObj = entry.get("level"); + int level = (levelObj instanceof Integer) ? (int) levelObj : 1; + + Enchantment enc = Enchantment.getByName(type.toUpperCase()); + if (enc != null) { + meta.addEnchant(enc, level, true); + } + } + reward.setItemMeta(meta); + } + + if (player.getInventory().firstEmpty() == -1) { + player.getWorld().dropItemNaturally(player.getLocation(), reward); + player.sendMessage(LanguageManager.getMessage("messages.inventory_full")); + } else { + player.getInventory().addItem(reward); + } + player.sendMessage(LanguageManager.getMessage("messages.reward_received").replace("%day%", String.valueOf(day))); + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/Adventskalender.java b/src/main/java/de/mviper/adventskalender/Adventskalender.java new file mode 100644 index 0000000..35898a9 --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/Adventskalender.java @@ -0,0 +1,62 @@ +package de.mviper.adventskalender; + +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.plugin.java.JavaPlugin; + +public final class Adventskalender extends JavaPlugin { + private static Adventskalender instance; + private NamespacedKey dataKey; + + // Update-Status Variablen + private boolean updateAvailable = false; + private String latestVersion = ""; + + @Override + public void onEnable() { + instance = this; + this.dataKey = new NamespacedKey(this, "claimed_days_v2"); + + saveDefaultConfig(); + LanguageManager.setup(); + + // Initialisierung der Daten (MySQL wird hier bevorzugt behandelt) + DataHandler.setup(); + + // Textur-Puffer füllen, um Steve-Köpfe zu vermeiden + AdventInventory.initCache(); + + getCommand("adventskalender").setExecutor(new AdventCommand()); + getServer().getPluginManager().registerEvents(new AdventListener(), this); + + // Update Checker Integration (ID: 130974) + new UpdateChecker(this, 130974).getVersion(version -> { + if (!this.getDescription().getVersion().equals(version)) { + this.updateAvailable = true; + this.latestVersion = version; + getLogger().warning("Eine neue Version (" + version + ") ist verfügbar!"); + getLogger().warning("Download: https://www.spigotmc.org/resources/130974/"); + } else { + getLogger().info("Das Plugin ist auf dem neuesten Stand."); + } + }); + + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + new AdventCalendarExpansion().register(); + } + + getLogger().info("Adventskalender erfolgreich gestartet!"); + } + + @Override + public void onDisable() { + MySQLManager.close(); + } + + public static Adventskalender getInstance() { return instance; } + public NamespacedKey getDataKey() { return dataKey; } + + // Getter für den Update-Status + public boolean isUpdateAvailable() { return updateAvailable; } + public String getLatestVersion() { return latestVersion; } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/DataHandler.java b/src/main/java/de/mviper/adventskalender/DataHandler.java new file mode 100644 index 0000000..6020e1c --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/DataHandler.java @@ -0,0 +1,241 @@ +package de.mviper.adventskalender; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import java.io.File; +import java.io.IOException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.HashSet; + +public class DataHandler { + + private static File file; + private static FileConfiguration dataConfig; + private static boolean mysqlActive = false; + + public static void setup() { + if (Adventskalender.getInstance().getConfig().getBoolean("mysql.enable")) { + MySQLManager.connect(); + if (MySQLManager.isConnected()) { + mysqlActive = true; + Adventskalender.getInstance().getLogger().info("MySQL-Verbindung steht. data.yml wird deaktiviert."); + return; + } else { + Adventskalender.getInstance().getLogger().warning("MySQL aktiviert, aber Verbindung fehlgeschlagen! Nutze data.yml als Backup."); + } + } + + if (!Adventskalender.getInstance().getDataFolder().exists()) { + Adventskalender.getInstance().getDataFolder().mkdirs(); + } + + file = new File(Adventskalender.getInstance().getDataFolder(), "data.yml"); + + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + dataConfig = YamlConfiguration.loadConfiguration(file); + } + + public static boolean hasClaimed(Player player, int day) { + if (isGlobal()) { + return GlobalDataManager.isDayClaimedGlobally(day); + } + + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("SELECT 1 FROM advent_players WHERE uuid = ? AND day = ?")) { + ps.setString(1, player.getUniqueId().toString()); + ps.setInt(2, day); + return ps.executeQuery().next(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + if (dataConfig != null) { + List claimed = dataConfig.getIntegerList("players." + player.getUniqueId() + ".claimed"); + return claimed.contains(day); + } + return false; + } + + public static void setClaimed(Player player, int day) { + if (isGlobal()) { + GlobalDataManager.setDayClaimedGlobally(day, player.getName()); + return; + } + + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("INSERT IGNORE INTO advent_players (uuid, day) VALUES (?, ?)")) { + ps.setString(1, player.getUniqueId().toString()); + ps.setInt(2, day); + ps.executeUpdate(); + return; + } catch (SQLException e) { + e.printStackTrace(); + } + } + + if (dataConfig != null) { + List claimed = dataConfig.getIntegerList("players." + player.getUniqueId() + ".claimed"); + if (!claimed.contains(day)) { + claimed.add(day); + dataConfig.set("players." + player.getUniqueId() + ".claimed", claimed); + dataConfig.set("players." + player.getUniqueId() + ".name", player.getName()); + save(); + } + } + } + + public static void resetPlayerDay(String playerName, int day) { + if (mysqlActive && MySQLManager.isConnected()) { + @SuppressWarnings("deprecation") + OfflinePlayer target = Bukkit.getOfflinePlayer(playerName); + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_players WHERE uuid = ? AND day = ?")) { + ps.setString(1, target.getUniqueId().toString()); + ps.setInt(2, day); + ps.executeUpdate(); + } catch (SQLException e) { e.printStackTrace(); } + } + if (dataConfig != null && dataConfig.getConfigurationSection("players") != null) { + for (String uuid : dataConfig.getConfigurationSection("players").getKeys(false)) { + if (playerName.equalsIgnoreCase(dataConfig.getString("players." + uuid + ".name"))) { + List claimed = dataConfig.getIntegerList("players." + uuid + ".claimed"); + claimed.remove(Integer.valueOf(day)); + dataConfig.set("players." + uuid + ".claimed", claimed); + save(); + break; + } + } + } + } + + public static Set getClaimedDays(Player player) { + if (mysqlActive && MySQLManager.isConnected()) { + Set days = new HashSet<>(); + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("SELECT day FROM advent_players WHERE uuid = ?")) { + ps.setString(1, player.getUniqueId().toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) days.add(rs.getInt("day")); + return days; + } catch (SQLException e) { + e.printStackTrace(); + } + } + + if (dataConfig != null) { + return new HashSet<>(dataConfig.getIntegerList("players." + player.getUniqueId() + ".claimed")); + } + return new HashSet<>(); + } + + public static void resetPlayer(Player player) { + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_players WHERE uuid = ?")) { + ps.setString(1, player.getUniqueId().toString()); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (dataConfig != null) { + dataConfig.set("players." + player.getUniqueId(), null); + save(); + } + } + + public static void resetOfflinePlayer(String name) { + if (dataConfig == null || dataConfig.getConfigurationSection("players") == null) return; + for (String uuid : dataConfig.getConfigurationSection("players").getKeys(false)) { + String savedName = dataConfig.getString("players." + uuid + ".name"); + if (name.equalsIgnoreCase(savedName)) { + dataConfig.set("players." + uuid, null); + save(); + break; + } + } + } + + public static void resetAll() { + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps1 = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_players"); + PreparedStatement ps2 = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_global")) { + ps1.executeUpdate(); + ps2.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (dataConfig != null) { + dataConfig.set("players", null); + dataConfig.set("global", null); + save(); + } + } + + private static boolean isGlobal() { + return Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar", false); + } + + private static void save() { + if (dataConfig == null || file == null) return; + try { + dataConfig.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static class GlobalDataManager { + public static boolean isDayClaimedGlobally(int day) { + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("SELECT 1 FROM advent_global WHERE day = ?")) { + ps.setInt(1, day); + return ps.executeQuery().next(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + return dataConfig != null && dataConfig.contains("global.day_" + day); + } + + public static void setDayClaimedGlobally(int day, String playerName) { + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("REPLACE INTO advent_global (day, player_name) VALUES (?, ?)")) { + ps.setInt(1, day); + ps.setString(2, playerName); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } else if (dataConfig != null) { + dataConfig.set("global.day_" + day, playerName); + save(); + } + } + + public static void resetGlobalDay(int day) { + if (mysqlActive && MySQLManager.isConnected()) { + try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_global WHERE day = ?")) { + ps.setInt(1, day); + ps.executeUpdate(); + } catch (SQLException e) { e.printStackTrace(); } + } else if (dataConfig != null) { + dataConfig.set("global.day_" + day, null); + DataHandler.save(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/LanguageManager.java b/src/main/java/de/mviper/adventskalender/LanguageManager.java new file mode 100644 index 0000000..de8ce20 --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/LanguageManager.java @@ -0,0 +1,35 @@ +package de.mviper.adventskalender; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.YamlConfiguration; +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; + +public class LanguageManager { + private static YamlConfiguration cfg; + private static String prefix; + + public static void setup() { + String lang = Adventskalender.getInstance().getConfig().getString("general.language", "de"); + File f = new File(Adventskalender.getInstance().getDataFolder(), "messages_" + lang + ".yml"); + if (!f.exists()) Adventskalender.getInstance().saveResource("messages_" + lang + ".yml", false); + cfg = YamlConfiguration.loadConfiguration(f); + prefix = ChatColor.translateAlternateColorCodes('&', cfg.getString("prefix", "&6[Advent] ")); + } + + public static String getString(String path) { + String s = cfg.getString(path); + return s == null ? "§cKey error: " + path : ChatColor.translateAlternateColorCodes('&', s); + } + + public static String getMessage(String path) { + return prefix + getString(path); + } + + public static List getStringList(String path) { + return cfg.getStringList(path).stream() + .map(s -> ChatColor.translateAlternateColorCodes('&', s)) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/MySQLManager.java b/src/main/java/de/mviper/adventskalender/MySQLManager.java new file mode 100644 index 0000000..3252a8a --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/MySQLManager.java @@ -0,0 +1,66 @@ +package de.mviper.adventskalender; + +import org.bukkit.configuration.file.FileConfiguration; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class MySQLManager { + + private static Connection connection; + + public static void connect() { + FileConfiguration config = Adventskalender.getInstance().getConfig(); + String host = config.getString("mysql.host"); + int port = config.getInt("mysql.port"); + String database = config.getString("mysql.database"); + String user = config.getString("mysql.user"); + String password = config.getString("mysql.password"); + + try { + if (connection != null && !connection.isClosed()) return; + + synchronized (MySQLManager.class) { + Class.forName("com.mysql.jdbc.Driver"); + connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true&useSSL=false", user, password); + } + + createTables(); + Adventskalender.getInstance().getLogger().info("MySQL-Verbindung erfolgreich hergestellt!"); + } catch (Exception e) { + Adventskalender.getInstance().getLogger().severe("MySQL-Verbindung fehlgeschlagen: " + e.getMessage()); + } + } + + private static void createTables() { + try (Statement s = connection.createStatement()) { + // Tabelle für Einzelspieler-Daten + s.executeUpdate("CREATE TABLE IF NOT EXISTS advent_players (uuid VARCHAR(36), day INT, PRIMARY KEY(uuid, day))"); + // Tabelle für globalen Modus + s.executeUpdate("CREATE TABLE IF NOT EXISTS advent_global (day INT PRIMARY KEY, player_name VARCHAR(16))"); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static boolean isConnected() { + try { + return connection != null && !connection.isClosed(); + } catch (SQLException e) { + return false; + } + } + + public static void close() { + try { + if (isConnected()) connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static Connection getConnection() { + return connection; + } +} \ No newline at end of file diff --git a/src/main/java/de/mviper/adventskalender/UpdateChecker.java b/src/main/java/de/mviper/adventskalender/UpdateChecker.java new file mode 100644 index 0000000..8bd9e39 --- /dev/null +++ b/src/main/java/de/mviper/adventskalender/UpdateChecker.java @@ -0,0 +1,33 @@ +package de.mviper.adventskalender; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Scanner; +import java.util.function.Consumer; + +public class UpdateChecker { + + private final JavaPlugin plugin; + private final int resourceId; + + public UpdateChecker(JavaPlugin plugin, int resourceId) { + this.plugin = plugin; + this.resourceId = resourceId; + } + + public void getVersion(final Consumer consumer) { + Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { + try (InputStream is = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId).openStream(); + Scanner scann = new Scanner(is)) { + if (scann.hasNext()) { + consumer.accept(scann.next()); + } + } catch (IOException e) { + plugin.getLogger().info("Update-Check fehlgeschlagen: " + e.getMessage()); + } + }); + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 4f190e3..e58782d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -8,6 +8,9 @@ general: # Verfügbare Optionen: 'de', 'en' language: "de" +# Nur für Testzwecke: Simuliert einen Tag (0 = echtes Datum verwenden) + test_day: 0 + # --- Kalender-Einstellungen --- calendar: # Soll jeder Spieler seinen eigenen, individuellen Kalender haben? @@ -19,6 +22,15 @@ calendar: # Dieser Text ist ein Schlüssel, der in der messages.yml übersetzt wird. gui_title_key: "adventskalender.gui_title" +# Datenbank für Bungeecord Nutzung +mysql: + enable: false + host: "localhost" + port: 3306 + database: "adventskalender" + user: "root" + password: "" + # --- Belohnungen für die Tage 1 bis 24 --- # Hier definierst du die Items für jeden Tag. # 'material': Der Materialname des Items (z.B. DIAMOND, ELYTRA). diff --git a/src/main/resources/messages_de.yml b/src/main/resources/messages_de.yml index 34856cc..8d9b0fd 100644 --- a/src/main/resources/messages_de.yml +++ b/src/main/resources/messages_de.yml @@ -1,15 +1,14 @@ # ============================================================================= -# Adventskalender Plugin - Deutsche Sprachdatei +# Adventskalender Plugin - Deutsche Sprachdatei (v2.0 - 2026) # ============================================================================= # --- Prefix --- -# Dieser Prefix wird vor fast jeder Nachricht im Chat angezeigt. prefix: "&6[Adventskalender] &r" # --- GUI-Texte --- gui: # Der Titel des Adventskalender-Inventars. - title: "&6✦ Adventskalender 2024 ✦" + title: "&6✦ Adventskalender 2026 ✦" # --- Nachrichten an Spieler --- # Verfügbare Platzhalter: @@ -18,90 +17,92 @@ gui: messages: no_permission: "&cDafür hast du keine Berechtigung." only_player: "&cDieser Befehl kann nur von einem Spieler ausgeführt werden." - inventory_full: "&cDein Inventar war voll! Das Item wurde auf den Boden geworfen." - reward_received: "&aDu hast dein Geschenk für Tag %day% erhalten!" - day_not_available: "&cDieses Geschenk kannst du (noch) nicht öffnen." - day_already_claimed: "&7Du hast dein Geschenk für diesen Tag bereits geholt." + inventory_full: "&cDein Inventar ist voll! Dein Geschenk wurde vor deine Füße geworfen." + reward_received: "&aGlückwunsch! Du hast dein Geschenk für Tag &e%day% &aerhalten!" + day_not_available: "&cDieses Türchen ist noch fest verschlossen. Gedulde dich noch etwas!" + day_already_claimed: "&7Du hast dieses Geschenk bereits abgeholt." + global_already_claimed: "&cDieses Geschenk wurde heute bereits von jemand anderem abgeholt!" # --- Admin-Nachrichten --- admin: - reload_success: "&aKonfiguration und Sprachdateien wurden neu geladen." + reload_success: "&aKonfiguration und Sprachdateien wurden erfolgreich neu geladen." + test_mode_on: "&e[Admin] &7Der Test-Modus wurde auf Tag &6%day% &7gesetzt." + test_mode_off: "&e[Admin] &7Test-Modus deaktiviert. Es wird wieder das reale Datum genutzt." player_not_found: "&cDer Spieler '%player%' wurde nicht gefunden." invalid_day: "&cUngültiger Tag. Bitte wähle eine Zahl zwischen 1 und 24." - open_success: "&aDu hast für %player% das Türchen für Tag %day% geöffnet." -# --- Namen und Beschreibungen für die Belohnungs-Items --- -# Die Schlüssel hier (z.B. 'day_1') müssen mit den 'keys' in der config.yml übereinstimmen. +# --- Belohnungs-Texte im GUI --- +# Diese Schlüssel werden vom AdventInventory genutzt, um die Items im Menü zu benennen. rewards: day_1: - name: "&bErste Diamanten!" + name: "&bTag 1: Erste Diamanten!" lore: ["&7Ein kleiner Vorgeschmack auf den Reichtum...", "&7Viel Spaß damit!"] day_2: - name: "&6Goldene Äpfel" + name: "&6Tag 2: Goldene Äpfel" lore: ["&7Für den Fall, dass es mal eng wird..."] day_3: - name: "&cSchwert des Helden" + name: "&cTag 3: Schwert des Helden" lore: ["&7Ein mächtiges Schwert, um Monster zu jagen."] day_4: - name: "&aFlügel der Freiheit" + name: "&aTag 4: Flügel der Freiheit" lore: ["&7Sei frei wie ein Adler!"] day_5: - name: "&8Ein Hauch von Netherit" + name: "&8Tag 5: Ein Hauch von Netherit" lore: ["&7Sehr wertvoll und selten."] day_6: - name: "&6Göttlicher Apfel" + name: "&6Tag 6: Göttlicher Apfel" lore: ["&7Ein Geschenk der Götter... oder des Admins."] day_7: - name: "&eBergbau-Meister" + name: "&eTag 7: Bergbau-Meister" lore: ["&7Für die größten Schätze der Welt."] day_8: - name: "&dTotem des Unvergänglichen" + name: "&dTag 8: Totem des Unvergänglichen" lore: ["&7Ein kleines Extra-Leben."] day_9: - name: "&5Stern des Nethers" + name: "&5Tag 9: Stern des Nethers" lore: ["&7Der Kern eines mächtigen Wesens."] day_10: - name: "&9Magische Truhe" + name: "&9Tag 10: Magische Truhen" lore: ["&7Hält all deine Schätze sicher."] day_11: - name: "&3Stachel des Ozeans" + name: "&3Tag 11: Stachel des Ozeans" lore: ["&7Die Waffe der Wächter."] day_12: - name: "&7Helm der Weisheit" + name: "&7Tag 12: Helm der Weisheit" lore: ["&7Schützt deinen Kopf."] day_13: - name: "&fSattel für ein treues Tier" + name: "&fTag 13: Sattel" lore: ["&7Für dein nächstes Abenteuer zu Pferd."] day_14: - name: "&eNamensschilder" + name: "&eTag 14: Namensschilder" lore: ["&7Gib deinem Lieblings-Wolf einen Namen!"] day_15: - name: "&aSchallplatte - cat" + name: "&aTag 15: Schallplatte - cat" lore: ["&7Relaxende Musik für eine gemütliche Zeit."] day_16: - name: "&5Endkristall" + name: "&5Tag 16: Endkristalle" lore: ["&7Mächtig und gefährlich. Mit Vorsicht genießen!"] day_17: - name: "&7Brustpanzer der Unzerstörbarkeit" + name: "&7Tag 17: Brustpanzer der Unzerstörbarkeit" lore: ["&7Bietet maximalen Schutz."] day_18: - name: "&bHerz des Ozeans" + name: "&bTag 18: Herz des Ozeans" lore: ["&7Das Zentrum einer Conduit-Struktur."] day_19: - name: "&fNautilusmuschel" + name: "&fTag 19: Nautilusmuscheln" lore: ["&7Ein seltenes Gut aus den Tiefen."] day_20: - name: "&8Kopf des Enderdrachen" + name: "&8Tag 20: Kopf des Enderdrachen" lore: ["&7Eine beeindruckende Trophäe."] day_21: - name: "&8Stiefel der Tiefe" + name: "&8Tag 21: Stiefel der Tiefe" lore: ["&7Lass dich nicht im Feuer verbrennen."] day_22: - name: "&dVerzauberungstisch" + name: "&dTag 22: Verzauberungstisch" lore: ["&7Kreiere deine eigenen magischen Items."] day_23: - name: "&bDie ultimative Hacke" + name: "&bTag 23: Die ultimative Hacke" lore: ["&7Nicht nur für den Ackerbau..."] day_24: - name: "&cDas große Finale! - Feuerwerk" + name: "&cTag 24: Das große Finale!" lore: ["&7Frohe Weihnachten!", "&7Feiere mit einem riesigen Feuerwerk!"] \ No newline at end of file diff --git a/src/main/resources/messages_en.yml b/src/main/resources/messages_en.yml index 5e7f292..10c9154 100644 --- a/src/main/resources/messages_en.yml +++ b/src/main/resources/messages_en.yml @@ -1,15 +1,14 @@ # ============================================================================= -# Adventskalender Plugin - English Language File +# Adventskalender Plugin - English Language File (v2.0 - 2026) # ============================================================================= # --- Prefix --- -# This prefix will be shown before most chat messages. -prefix: "&6[Adventskalender] &r" +prefix: "&6[Advent] &r" # --- GUI Texts --- gui: # The title of the Advent Calendar inventory. - title: "&6✦ Advent Calendar 2024 ✦" + title: "&6✦ Advent Calendar 2026 ✦" # --- Messages to Players --- # Available placeholders: @@ -18,90 +17,91 @@ gui: messages: no_permission: "&cYou don't have permission for that." only_player: "&cThis command can only be executed by a player." - inventory_full: "&cYour inventory was full! The item was dropped on the ground." - reward_received: "&aYou have received your gift for day %day%!" - day_not_available: "&cYou cannot open this gift (yet)." - day_already_claimed: "&7You have already claimed your gift for this day." + inventory_full: "&cYour inventory is full! Your gift was dropped at your feet." + reward_received: "&aCongratulations! You have received your gift for day &e%day%&a!" + day_not_available: "&cThis door is still locked. You'll have to wait a bit longer!" + day_already_claimed: "&7You have already claimed this gift." + global_already_claimed: "&cThis gift has already been claimed by someone else today!" # --- Admin Messages --- admin: reload_success: "&aConfiguration and language files have been reloaded." + test_mode_on: "&e[Admin] &7Test mode set to day &6%day%&7." + test_mode_off: "&e[Admin] &7Test mode disabled. Using real-time date again." player_not_found: "&cThe player '%player%' was not found." invalid_day: "&cInvalid day. Please choose a number between 1 and 24." - open_success: "&aYou have opened day %day% for %player%." -# --- Names and Descriptions for Reward Items --- -# The keys here (e.g., 'day_1') must match the 'keys' in the config.yml. +# --- Reward Texts in GUI --- rewards: day_1: - name: "&bFirst Diamonds!" + name: "&bDay 1: First Diamonds!" lore: ["&7A little taste of wealth...", "&7Have fun with it!"] day_2: - name: "&6Golden Apples" + name: "&6Day 2: Golden Apples" lore: ["&7In case things get tight..."] day_3: - name: "&cSword of the Hero" + name: "&cDay 3: Sword of the Hero" lore: ["&7A mighty sword to hunt monsters."] day_4: - name: "&aWings of Freedom" + name: "&aDay 4: Wings of Freedom" lore: ["&7Be free as an eagle!"] day_5: - name: "&8A Touch of Netherite" + name: "&8Day 5: A Touch of Netherite" lore: ["&7Very valuable and rare."] day_6: - name: "&6Godly Apple" + name: "&6Day 6: Godly Apple" lore: ["&7A gift from the gods... or the admin."] day_7: - name: "&eMaster Pickaxe" + name: "&eDay 7: Master Pickaxe" lore: ["&7For the greatest treasures in the world."] day_8: - name: "&dTotem of Undying" + name: "&dDay 8: Totem of Undying" lore: ["&7An extra life."] day_9: - name: "&5Nether Star" + name: "&5Day 9: Nether Star" lore: ["&7The core of a powerful being."] day_10: - name: "&9Magic Box" + name: "&9Day 10: Magic Chests" lore: ["&7Keeps all your treasures safe."] day_11: - name: "&3Spear of the Ocean" + name: "&3Day 11: Spear of the Ocean" lore: ["&7The weapon of the guardians."] day_12: - name: "&7Helmet of Wisdom" + name: "&7Day 12: Helmet of Wisdom" lore: ["&7Protects your head."] day_13: - name: "&fSaddle for a Loyal Steed" + name: "&fDay 13: Saddle" lore: ["&7For your next equestrian adventure."] day_14: - name: "&eName Tags" + name: "&eDay 14: Name Tags" lore: ["&7Give your favorite wolf a name!"] day_15: - name: "&aMusic Disc - cat" + name: "&aDay 15: Music Disc - cat" lore: ["&7Relaxing music for a cozy time."] day_16: - name: "&5End Crystal" + name: "&5Day 16: End Crystals" lore: ["&7Powerful and dangerous. Handle with care!"] day_17: - name: "&7Chestplate of Durability" + name: "&7Day 17: Chestplate of Durability" lore: ["&7Offers maximum protection."] day_18: - name: "&bHeart of the Sea" + name: "&bDay 18: Heart of the Sea" lore: ["&7The center of a Conduit structure."] day_19: - name: "&fNautilus Shell" + name: "&fDay 19: Nautilus Shells" lore: ["&7A rare goodie from the depths."] day_20: - name: "&8Head of the Ender Dragon" + name: "&8Day 20: Head of the Ender Dragon" lore: ["&7An impressive trophy."] day_21: - name: "&8Boots of the Deep" + name: "&8Day 21: Boots of the Deep" lore: ["&7Don't let yourself get burned in fire."] day_22: - name: "&dEnchanting Table" + name: "&dDay 22: Enchanting Table" lore: ["&7Create your own magical items."] day_23: - name: "&bThe Ultimate Hoe" + name: "&bDay 23: The Ultimate Hoe" lore: ["&7Not just for farming..."] day_24: - name: "&cThe Grand Finale! - Fireworks" + name: "&cDay 24: The Grand Finale!" lore: ["&7Merry Christmas!", "&7Celebrate with a huge firework!"] \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 9cd587c..d9c02e2 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,9 +1,9 @@ name: Adventskalender -version: '1.0.0' +version: '1.0.1' main: de.mviper.adventskalender.Adventskalender api-version: 1.20 author: M_Viper -description: Ein konfigurierbarer Adventskalender für Minecraft. +description: Ein moderner, performanter Adventskalender (2026 Edition). commands: adventskalender: