diff --git a/plugin.yml b/plugin.yml index ea334eb..bb9eccf 100644 --- a/plugin.yml +++ b/plugin.yml @@ -9,6 +9,22 @@ commands: testsmithchest: description: Spawns a weaponsmith chest for testing usage: /testsmithchest + augment: + description: Manage item augmentations + usage: /augment + aliases: [aug, enhance] + playerlevel: + description: View and manage player levels + usage: /playerlevel [player] [info|add|set] [amount] + aliases: [plevel, level] + demo: + description: Get demo items with pre-configured augmentations + usage: /demo [1-10|all|random|list] + aliases: [demoitems, examples] + convert: + description: Convert regular items to Enhanced Items + usage: /convert [hand|all|help] + aliases: [enhance, upgrade] permissions: levelcraft.admin: diff --git a/src/main/java/com/simolzimol/levelcraft/LevelCraft.java b/src/main/java/com/simolzimol/levelcraft/LevelCraft.java index a6cb17c..3f2b55b 100644 --- a/src/main/java/com/simolzimol/levelcraft/LevelCraft.java +++ b/src/main/java/com/simolzimol/levelcraft/LevelCraft.java @@ -13,16 +13,35 @@ public class LevelCraft extends JavaPlugin { public void onEnable() { instance = this; getLogger().info("LevelCraft enabled!"); - com.simolzimol.levelcraft.item.ItemUtil.init(this); + + // Initialize core systems + // com.simolzimol.levelcraft.item.ItemUtil.init(this); // DISABLED: Legacy system replaced by augmentations + com.simolzimol.levelcraft.augmentation.EnhancedItem.init(this); + com.simolzimol.levelcraft.player.PlayerLevelManager.init(this); + + // Register event listeners getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.CraftListener(), this); getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.LootListener(this), this); getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.VillagerTradeListener(), this); getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.MobDropListener(), this); getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.MobSpawnListener(), this); getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.CustomStatsListener(), this); + getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.PlayerLevelListener(), this); + + // NEW: Auto-Enhancement System - converts all items to Enhanced Items + getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.AutoEnhancementListener(), this); + + // NEW: Item Leveling System - items gain XP from usage + getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.ItemLevelingListener(), this); + + // Register commands getCommand("testsmithchest").setExecutor(new com.simolzimol.levelcraft.command.TestSmithChestCommand()); + getCommand("augment").setExecutor(new com.simolzimol.levelcraft.command.AugmentCommand()); + getCommand("playerlevel").setExecutor(new com.simolzimol.levelcraft.command.PlayerLevelCommand()); + getCommand("demo").setExecutor(new com.simolzimol.levelcraft.command.DemoCommand()); + getCommand("convert").setExecutor(new com.simolzimol.levelcraft.command.ConvertCommand()); - // Start ActionBar task for all players + // Start background tasks com.simolzimol.levelcraft.system.PlayerStatsManager.startActionBarTask(this); com.simolzimol.levelcraft.system.PlayerStatsManager.startRegenTask(this); } diff --git a/src/main/java/com/simolzimol/levelcraft/augmentation/Augmentation.java b/src/main/java/com/simolzimol/levelcraft/augmentation/Augmentation.java new file mode 100644 index 0000000..6b01ffe --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/augmentation/Augmentation.java @@ -0,0 +1,127 @@ +package com.simolzimol.levelcraft.augmentation; + +import java.util.Map; +import java.util.HashMap; + +public class Augmentation { + private final AugmentationType type; + private final int level; + private final Map statModifiers; + + public Augmentation(AugmentationType type, int level) { + this.type = type; + this.level = Math.min(level, type.getMaxLevel()); + this.statModifiers = calculateStatModifiers(); + } + + private Map calculateStatModifiers() { + Map modifiers = new HashMap<>(); + + switch (type) { + case SHARPNESS -> modifiers.put("attack_damage", (double) level); + case PROTECTION -> modifiers.put("damage_reduction", level * 2.0); // 2% per level + case FIRE_ASPECT -> modifiers.put("fire_duration", (double) level); + case KNOCKBACK -> modifiers.put("knockback", level * 0.5); + + case FORTUNE -> modifiers.put("drop_multiplier", 1.0 + (level * 0.2)); // 20% per level + case EFFICIENCY -> modifiers.put("mining_speed", level * 10.0); // 10% per level + case UNBREAKING -> modifiers.put("durability_save", level * 20.0); // 20% per level + case MENDING -> modifiers.put("xp_repair", 1.0); + + case VAMPIRIC -> modifiers.put("lifesteal", level * 5.0); // 5% per level + case CRITICAL_STRIKE -> modifiers.put("crit_chance", level * 2.0); // 2% per level + case CHAIN_LIGHTNING -> modifiers.put("chain_range", level * 3.0); // 3 block range per level + case THORNS -> modifiers.put("reflect_damage", level * 15.0); // 15% damage reflection per level + + case RESISTANCE -> modifiers.put("all_damage_reduction", level * 3.0); // 3% per level + case REGENERATION -> modifiers.put("health_regen", level * 0.1); // 0.1 HP/sec per level + case SPEED -> modifiers.put("movement_speed", level * 10.0); // 10% per level + + case SILK_TOUCH -> modifiers.put("silk_touch", 1.0); + case SMITE -> modifiers.put("undead_damage", level * 2.0); // +2 damage vs undead per level + case BANE_OF_ARTHROPODS -> modifiers.put("arthropod_damage", level * 2.0); // +2 damage vs arthropods per level + } + + return modifiers; + } + + public AugmentationType getType() { + return type; + } + + public int getLevel() { + return level; + } + + public Map getStatModifiers() { + return new HashMap<>(statModifiers); + } + + public String getDisplayName() { + return type.getFormattedName(level); + } + + public String getDescription() { + StringBuilder desc = new StringBuilder(type.getDescription()); + + // Add specific values to description + switch (type) { + case SHARPNESS -> desc.append(" (+").append(level).append(" damage)"); + case PROTECTION -> desc.append(" (-").append(level * 2).append("% damage)"); + case FIRE_ASPECT -> desc.append(" (").append(level).append("s burn)"); + case KNOCKBACK -> desc.append(" (+").append(level * 50).append("% knockback)"); + case FORTUNE -> desc.append(" (+").append((int)(level * 20)).append("% drops)"); + case EFFICIENCY -> desc.append(" (+").append(level * 10).append("% speed)"); + case UNBREAKING -> desc.append(" (+").append(level * 20).append("% durability)"); + case MENDING -> desc.append(" (XP repairs item)"); + case VAMPIRIC -> desc.append(" (").append(level * 5).append("% lifesteal)"); + case CRITICAL_STRIKE -> desc.append(" (").append(level * 2).append("% chance)"); + case CHAIN_LIGHTNING -> desc.append(" (").append(level * 3).append(" block range)"); + case THORNS -> desc.append(" (").append(level * 15).append("% reflect)"); + case RESISTANCE -> desc.append(" (-").append(level * 3).append("% all damage)"); + case REGENERATION -> desc.append(" (+").append(level * 0.1).append(" HP/sec)"); + case SPEED -> desc.append(" (+").append(level * 10).append("% speed)"); + case SILK_TOUCH -> desc.append(" (Harvests blocks intact)"); + case SMITE -> desc.append(" (+").append(level * 2).append(" vs undead)"); + case BANE_OF_ARTHROPODS -> desc.append(" (+").append(level * 2).append(" vs arthropods)"); + } + + return desc.toString(); + } + + // Serialization for NBT storage + public String serialize() { + return type.name() + ":" + level; + } + + public static Augmentation deserialize(String data) { + String[] parts = data.split(":"); + if (parts.length != 2) return null; + + try { + AugmentationType type = AugmentationType.valueOf(parts[0]); + int level = Integer.parseInt(parts[1]); + return new Augmentation(type, level); + } catch (Exception e) { + return null; + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Augmentation that = (Augmentation) obj; + return level == that.level && type == that.type; + } + + @Override + public int hashCode() { + return type.hashCode() * 31 + level; + } + + @Override + public String toString() { + return getDisplayName(); + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/augmentation/AugmentationSlot.java b/src/main/java/com/simolzimol/levelcraft/augmentation/AugmentationSlot.java new file mode 100644 index 0000000..c4702b3 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/augmentation/AugmentationSlot.java @@ -0,0 +1,104 @@ +package com.simolzimol.levelcraft.augmentation; + +public class AugmentationSlot { + private final int slotId; + private Augmentation augmentation; + private boolean locked; + + public AugmentationSlot(int slotId) { + this.slotId = slotId; + this.augmentation = null; + this.locked = false; + } + + public AugmentationSlot(int slotId, boolean locked) { + this.slotId = slotId; + this.augmentation = null; + this.locked = locked; + } + + public int getSlotId() { + return slotId; + } + + public Augmentation getAugmentation() { + return augmentation; + } + + public void setAugmentation(Augmentation augmentation) { + if (!locked) { + this.augmentation = augmentation; + } + } + + public void removeAugmentation() { + if (!locked) { + this.augmentation = null; + } + } + + public boolean isEmpty() { + return augmentation == null; + } + + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public void unlock() { + this.locked = false; + } + + public String getDisplayName() { + if (locked) { + return "§8[LOCKED]"; + } + if (isEmpty()) { + return "§7[EMPTY]"; + } + return augmentation.getDisplayName(); + } + + // Serialization for NBT storage + public String serialize() { + StringBuilder sb = new StringBuilder(); + sb.append(slotId).append(";"); + sb.append(locked).append(";"); + if (augmentation != null) { + sb.append(augmentation.serialize()); + } else { + sb.append("EMPTY"); + } + return sb.toString(); + } + + public static AugmentationSlot deserialize(String data) { + String[] parts = data.split(";"); + if (parts.length != 3) return null; + + try { + int slotId = Integer.parseInt(parts[0]); + boolean locked = Boolean.parseBoolean(parts[1]); + + AugmentationSlot slot = new AugmentationSlot(slotId, locked); + + if (!parts[2].equals("EMPTY")) { + Augmentation aug = Augmentation.deserialize(parts[2]); + slot.setAugmentation(aug); + } + + return slot; + } catch (Exception e) { + return null; + } + } + + @Override + public String toString() { + return "Slot " + slotId + ": " + getDisplayName(); + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/augmentation/AugmentationType.java b/src/main/java/com/simolzimol/levelcraft/augmentation/AugmentationType.java new file mode 100644 index 0000000..60d2b87 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/augmentation/AugmentationType.java @@ -0,0 +1,100 @@ +package com.simolzimol.levelcraft.augmentation; + +public enum AugmentationType { + // Combat Augmentations + SHARPNESS("Sharpness", "§c", "Increases attack damage", 10), + PROTECTION("Protection", "§9", "Reduces incoming damage", 5), + FIRE_ASPECT("Fire Aspect", "§6", "Sets enemies on fire", 3), + KNOCKBACK("Knockback", "§7", "Increases knockback", 2), + + // Utility Augmentations + FORTUNE("Fortune", "§e", "Increases drop rates", 5), + EFFICIENCY("Efficiency", "§a", "Increases mining speed", 10), + UNBREAKING("Unbreaking", "§b", "Reduces durability loss", 5), + MENDING("Mending", "§d", "Repairs item with XP", 1), + + // Special Augmentations + VAMPIRIC("Vampiric", "§4", "Heals on damage dealt", 3), + CRITICAL_STRIKE("Critical Strike", "§c", "Chance for critical hits", 5), + CHAIN_LIGHTNING("Chain Lightning", "§e", "Damages nearby enemies", 2), + THORNS("Thorns", "§8", "Reflects damage to attackers", 3), + + // Defensive Augmentations + RESISTANCE("Resistance", "§5", "Reduces all damage types", 3), + REGENERATION("Regeneration", "§c", "Slowly regenerates health", 3), + SPEED("Speed", "§f", "Increases movement speed", 2), + + // Tool Augmentations + SILK_TOUCH("Silk Touch", "§f", "Harvests blocks intact", 1), + SMITE("Smite", "§e", "Extra damage vs undead", 5), + BANE_OF_ARTHROPODS("Bane of Arthropods", "§2", "Extra damage vs arthropods", 5); + + private final String displayName; + private final String colorCode; + private final String description; + private final int maxLevel; + + AugmentationType(String displayName, String colorCode, String description, int maxLevel) { + this.displayName = displayName; + this.colorCode = colorCode; + this.description = description; + this.maxLevel = maxLevel; + } + + public String getDisplayName() { + return displayName; + } + + public String getColorCode() { + return colorCode; + } + + public String getDescription() { + return description; + } + + public int getMaxLevel() { + return maxLevel; + } + + public String getFormattedName(int level) { + return colorCode + displayName + " " + toRoman(level); + } + + private String toRoman(int number) { + if (number <= 0) return ""; + String[] romanNumerals = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"}; + return number <= 10 ? romanNumerals[number] : String.valueOf(number); + } + + // Check if augmentation is applicable to item type + public boolean isApplicableTo(org.bukkit.Material material) { + return switch (this) { + case SHARPNESS, FIRE_ASPECT, KNOCKBACK, SMITE, BANE_OF_ARTHROPODS -> isWeapon(material); + case PROTECTION, THORNS, RESISTANCE -> isArmor(material); + case FORTUNE, EFFICIENCY, SILK_TOUCH -> isTool(material); + case UNBREAKING, MENDING -> material.getMaxDurability() > 0; + case VAMPIRIC, CRITICAL_STRIKE, CHAIN_LIGHTNING -> isWeapon(material); + case REGENERATION, SPEED -> isArmor(material); + default -> true; // Universal augmentations + }; + } + + private boolean isWeapon(org.bukkit.Material material) { + String name = material.name(); + return name.endsWith("_SWORD") || name.endsWith("_AXE") || + name.equals("TRIDENT") || name.equals("BOW") || name.equals("CROSSBOW"); + } + + private boolean isArmor(org.bukkit.Material material) { + String name = material.name(); + return name.endsWith("_HELMET") || name.endsWith("_CHESTPLATE") || + name.endsWith("_LEGGINGS") || name.endsWith("_BOOTS") || name.equals("SHIELD"); + } + + private boolean isTool(org.bukkit.Material material) { + String name = material.name(); + return name.endsWith("_PICKAXE") || name.endsWith("_SHOVEL") || + name.endsWith("_HOE") || name.endsWith("_AXE"); + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/augmentation/EnhancedItem.java b/src/main/java/com/simolzimol/levelcraft/augmentation/EnhancedItem.java new file mode 100644 index 0000000..203ed9c --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/augmentation/EnhancedItem.java @@ -0,0 +1,393 @@ +package com.simolzimol.levelcraft.augmentation; + +import com.simolzimol.levelcraft.item.Rarity; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; + +import java.util.*; + +public class EnhancedItem { + private static NamespacedKey enhancedKey; + private static NamespacedKey levelKey; + private static NamespacedKey slotsKey; + private static NamespacedKey rarityKey; + private static NamespacedKey xpKey; + + private ItemStack itemStack; + private Rarity rarity; + private int itemLevel; + private long itemXP; + private List slots; + private Map calculatedStats; + + public EnhancedItem(ItemStack itemStack, Rarity rarity) { + this(itemStack, rarity, 1); + } + + public EnhancedItem(ItemStack itemStack, Rarity rarity, int level) { + this.itemStack = itemStack.clone(); + this.rarity = rarity; + this.itemLevel = Math.max(1, level); + this.itemXP = 0; // Start with 0 XP + this.slots = new ArrayList<>(); + this.calculatedStats = new HashMap<>(); + + initializeSlots(); + updateItemMeta(); + } + + // Create from existing ItemStack (load from NBT) + public static EnhancedItem fromItemStack(ItemStack itemStack) { + ItemMeta meta = itemStack.getItemMeta(); + if (meta == null || !isEnhanced(itemStack)) { + return null; + } + + // Load rarity + Integer rarityValue = meta.getPersistentDataContainer().get(rarityKey, PersistentDataType.INTEGER); + Rarity rarity = rarityValue != null ? Rarity.fromValue(rarityValue) : Rarity.COMMON; + + EnhancedItem enhanced = new EnhancedItem(itemStack, rarity); + + // Load level + Integer level = meta.getPersistentDataContainer().get(levelKey, PersistentDataType.INTEGER); + enhanced.itemLevel = level != null ? level : 1; + + // Load XP + Long xp = meta.getPersistentDataContainer().get(xpKey, PersistentDataType.LONG); + enhanced.itemXP = xp != null ? xp : 0; + + // Load slots + String slotsData = meta.getPersistentDataContainer().get(slotsKey, PersistentDataType.STRING); + if (slotsData != null && !slotsData.isEmpty()) { + enhanced.loadSlotsFromData(slotsData); + } + + enhanced.recalculateStats(); + return enhanced; + } + + public static void init(org.bukkit.plugin.Plugin plugin) { + enhancedKey = new NamespacedKey(plugin, "enhanced"); + slotsKey = new NamespacedKey(plugin, "augmentation_slots"); + levelKey = new NamespacedKey(plugin, "item_level"); + rarityKey = new NamespacedKey(plugin, "rarity"); + xpKey = new NamespacedKey(plugin, "item_xp"); + } + + public static boolean isEnhanced(ItemStack itemStack) { + if (itemStack == null || !itemStack.hasItemMeta()) return false; + ItemMeta meta = itemStack.getItemMeta(); + return meta.getPersistentDataContainer().has(enhancedKey, PersistentDataType.BYTE); + } + + private void initializeSlots() { + int maxSlots = getMaxSlotsForRarity(rarity); + int unlockedSlots = com.simolzimol.levelcraft.system.ItemLevelingSystem.calculateSlotsForLevel(itemLevel); + + // Ensure we don't exceed the rarity maximum + unlockedSlots = Math.min(unlockedSlots, maxSlots); + + slots.clear(); + + // Add unlocked slots + for (int i = 0; i < unlockedSlots; i++) { + slots.add(new AugmentationSlot(i)); + } + + // Add locked slots (locked by level or rarity) + for (int i = unlockedSlots; i < maxSlots; i++) { + slots.add(new AugmentationSlot(i, true)); + } + } + + private int getMaxSlotsForRarity(Rarity rarity) { + return switch (rarity) { + case CURSED -> 1; + case COMMON -> 1; + case UNCOMMON -> 2; + case RARE -> 3; + case EPIC -> 4; + case LEGENDARY -> 5; + case MYTHIC -> 6; + case ANCIENT -> 7; + case DIVINE -> 8; + }; + } + + private int getAvailableSlotsForRarity(Rarity rarity) { + return switch (rarity) { + case CURSED -> 0; // Cursed items have no available slots initially + case COMMON -> 1; + case UNCOMMON -> 1; + case RARE -> 2; + case EPIC -> 2; + case LEGENDARY -> 3; + case MYTHIC -> 4; + case ANCIENT -> 5; + case DIVINE -> 6; + }; + } + + public boolean addAugmentation(Augmentation augmentation, org.bukkit.entity.Player player) { + // Check if augmentation is applicable to this item type + if (!augmentation.getType().isApplicableTo(itemStack.getType())) { + return false; + } + + // Check if player/item level allows this augmentation level + int maxAllowedLevel = com.simolzimol.levelcraft.player.PlayerLevelManager.getMaxAugmentationLevel( + player, itemLevel, augmentation.getType().getMaxLevel()); + + if (augmentation.getLevel() > maxAllowedLevel) { + return false; + } + + // Find first empty, unlocked slot + for (AugmentationSlot slot : slots) { + if (slot.isEmpty() && !slot.isLocked()) { + slot.setAugmentation(augmentation); + recalculateStats(); + updateItemMeta(); + return true; + } + } + return false; + } + + // Backward compatibility method (assumes max allowed level) + public boolean addAugmentation(Augmentation augmentation) { + // Find first empty, unlocked slot + for (AugmentationSlot slot : slots) { + if (slot.isEmpty() && !slot.isLocked()) { + slot.setAugmentation(augmentation); + recalculateStats(); + updateItemMeta(); + return true; + } + } + return false; + } + + public boolean removeAugmentation(int slotId) { + if (slotId >= 0 && slotId < slots.size()) { + AugmentationSlot slot = slots.get(slotId); + if (!slot.isEmpty() && !slot.isLocked()) { + slot.removeAugmentation(); + recalculateStats(); + updateItemMeta(); + return true; + } + } + return false; + } + + public void levelUp() { + itemLevel++; + // Every 5 levels, unlock a random locked slot + if (itemLevel % 5 == 0) { + unlockRandomSlot(); + } + updateItemMeta(); + } + + private void unlockRandomSlot() { + List lockedSlots = slots.stream() + .filter(AugmentationSlot::isLocked) + .toList(); + + if (!lockedSlots.isEmpty()) { + Random random = new Random(); + AugmentationSlot slotToUnlock = lockedSlots.get(random.nextInt(lockedSlots.size())); + slotToUnlock.unlock(); + } + } + + private void recalculateStats() { + calculatedStats.clear(); + + for (AugmentationSlot slot : slots) { + if (!slot.isEmpty()) { + Augmentation aug = slot.getAugmentation(); + for (Map.Entry entry : aug.getStatModifiers().entrySet()) { + calculatedStats.merge(entry.getKey(), entry.getValue(), Double::sum); + } + } + } + } + + private void updateItemMeta() { + ItemMeta meta = itemStack.getItemMeta(); + if (meta == null) return; + + // Mark as enhanced + meta.getPersistentDataContainer().set(enhancedKey, PersistentDataType.BYTE, (byte) 1); + meta.getPersistentDataContainer().set(rarityKey, PersistentDataType.INTEGER, rarity.getValue()); + meta.getPersistentDataContainer().set(levelKey, PersistentDataType.INTEGER, itemLevel); + meta.getPersistentDataContainer().set(xpKey, PersistentDataType.LONG, itemXP); + meta.getPersistentDataContainer().set(slotsKey, PersistentDataType.STRING, serializeSlots()); + + // Update display name and lore + updateDisplayNameAndLore(meta); + + itemStack.setItemMeta(meta); + } + + private void updateDisplayNameAndLore(ItemMeta meta) { + // Update display name with level and rarity + String baseName = itemStack.getType().name().replace("_", " ").toLowerCase(); + baseName = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); + String displayName = rarity.getDisplayName() + " " + baseName + " §7[Lv." + itemLevel + "]"; + meta.displayName(net.kyori.adventure.text.Component.text(displayName)); + + // Create lore + List lore = new ArrayList<>(); + + // Rarity line + lore.add(net.kyori.adventure.text.Component.text(rarity.getDisplayName() + " §7Item")); + lore.add(net.kyori.adventure.text.Component.text("§7Level: §f" + itemLevel)); + + // XP Progress line + if (itemLevel < com.simolzimol.levelcraft.system.ItemLevelingSystem.getMaxItemLevel(rarity)) { + long xpForNext = com.simolzimol.levelcraft.system.ItemLevelingSystem.getXPRequiredForLevel(itemLevel + 1); + long xpForCurrent = itemLevel > 1 ? com.simolzimol.levelcraft.system.ItemLevelingSystem.getXPRequiredForLevel(itemLevel) : 0; + long xpInLevel = itemXP - xpForCurrent; + long xpNeeded = xpForNext - xpForCurrent; + + double progress = (double) xpInLevel / xpNeeded; + int bars = 10; + int filledBars = (int) (progress * bars); + StringBuilder progressBar = new StringBuilder("§a"); + for (int i = 0; i < bars; i++) { + if (i < filledBars) { + progressBar.append("█"); + } else { + progressBar.append("§7█"); + } + } + lore.add(net.kyori.adventure.text.Component.text("§7XP: " + progressBar + " §e" + xpInLevel + "§7/§e" + xpNeeded)); + } else { + lore.add(net.kyori.adventure.text.Component.text("§7XP: §6MAX LEVEL")); + } + + lore.add(net.kyori.adventure.text.Component.text("")); + + // Augmentation slots + lore.add(net.kyori.adventure.text.Component.text("§6⬟ Augmentation Slots:")); + for (AugmentationSlot slot : slots) { + String slotDisplay; + if (slot.isEmpty()) { + if (slot.isLocked()) { + slotDisplay = "§8▣ §7[" + (slot.getSlotId() + 1) + "] §8Locked Slot"; + } else { + slotDisplay = "§7▢ §7[" + (slot.getSlotId() + 1) + "] §7Empty Slot"; + } + } else { + Augmentation aug = slot.getAugmentation(); + String typeColor = getAugmentationTypeColor(aug.getType()); + slotDisplay = "§a▣ §7[" + (slot.getSlotId() + 1) + "] " + typeColor + aug.getDisplayName(); + lore.add(net.kyori.adventure.text.Component.text(" " + slotDisplay)); + lore.add(net.kyori.adventure.text.Component.text(" §8↳ " + aug.getDescription())); + continue; + } + lore.add(net.kyori.adventure.text.Component.text(" " + slotDisplay)); + } + + // Stats summary if any augmentations + if (!calculatedStats.isEmpty()) { + lore.add(net.kyori.adventure.text.Component.text("")); + lore.add(net.kyori.adventure.text.Component.text("§aTotal Bonuses:")); + for (Map.Entry entry : calculatedStats.entrySet()) { + String statName = entry.getKey().replace("_", " "); + statName = Character.toUpperCase(statName.charAt(0)) + statName.substring(1); + lore.add(net.kyori.adventure.text.Component.text("§7+ " + entry.getValue() + " " + statName)); + } + } + + meta.lore(lore); + } + + private String serializeSlots() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < slots.size(); i++) { + if (i > 0) sb.append("|"); + sb.append(slots.get(i).serialize()); + } + return sb.toString(); + } + + private void loadSlotsFromData(String data) { + slots.clear(); + String[] slotData = data.split("\\|"); + + for (String slotString : slotData) { + AugmentationSlot slot = AugmentationSlot.deserialize(slotString); + if (slot != null) { + slots.add(slot); + } + } + } + + // Getters + public ItemStack getItemStack() { + return itemStack.clone(); + } + + public Rarity getRarity() { + return rarity; + } + + public int getItemLevel() { + return itemLevel; + } + + public long getItemXP() { + return itemXP; + } + + public void setItemLevel(int level) { + this.itemLevel = Math.max(1, level); + // Reinitialize slots when level changes (may unlock new slots) + initializeSlots(); + updateItemMeta(); + } + + public void setItemXP(long xp) { + this.itemXP = Math.max(0, xp); + updateItemMeta(); + } + + public List getSlots() { + return new ArrayList<>(slots); + } + + public Map getCalculatedStats() { + return new HashMap<>(calculatedStats); + } + + public int getUsedSlots() { + return (int) slots.stream().filter(slot -> !slot.isEmpty()).count(); + } + + public int getAvailableSlots() { + return (int) slots.stream().filter(slot -> slot.isEmpty() && !slot.isLocked()).count(); + } + + public int getTotalSlots() { + return slots.size(); + } + + private String getAugmentationTypeColor(AugmentationType type) { + return switch (type) { + case SHARPNESS, SMITE, BANE_OF_ARTHROPODS, CRITICAL_STRIKE -> "§c"; // Red for damage + case PROTECTION, RESISTANCE, THORNS -> "§9"; // Blue for protection + case EFFICIENCY, UNBREAKING, MENDING, SILK_TOUCH -> "§e"; // Yellow for utility + case FORTUNE, VAMPIRIC, REGENERATION -> "§a"; // Green for beneficial + case FIRE_ASPECT, CHAIN_LIGHTNING, SPEED -> "§d"; // Magenta for special + case KNOCKBACK -> "§7"; // Gray for knockback + default -> "§f"; // White fallback + }; + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/command/AugmentCommand.java b/src/main/java/com/simolzimol/levelcraft/command/AugmentCommand.java new file mode 100644 index 0000000..86a100c --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/command/AugmentCommand.java @@ -0,0 +1,226 @@ +package com.simolzimol.levelcraft.command; + +import com.simolzimol.levelcraft.augmentation.*; +import com.simolzimol.levelcraft.item.Rarity; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Random; + +public class AugmentCommand implements CommandExecutor { + private final Random random = new Random(); + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Only players can use this command."); + return true; + } + + if (args.length == 0) { + player.sendMessage("§6=== LevelCraft Augmentation Commands ==="); + player.sendMessage("§e/augment create [rarity] §7- Create enhanced item"); + player.sendMessage("§e/augment add [level] §7- Add augmentation to held item"); + player.sendMessage("§e/augment remove §7- Remove augmentation from slot"); + player.sendMessage("§e/augment info §7- Show item information"); + player.sendMessage("§e/augment levelup §7- Level up held item"); + return true; + } + + String subCommand = args[0].toLowerCase(); + + switch (subCommand) { + case "create" -> handleCreate(player, args); + case "add" -> handleAdd(player, args); + case "remove" -> handleRemove(player, args); + case "info" -> handleInfo(player); + case "levelup" -> handleLevelUp(player); + default -> player.sendMessage("§cUnknown subcommand. Use /augment for help."); + } + + return true; + } + + private void handleCreate(Player player, String[] args) { + Rarity rarity = Rarity.COMMON; + + if (args.length > 1) { + try { + rarity = Rarity.valueOf(args[1].toUpperCase()); + } catch (IllegalArgumentException e) { + player.sendMessage("§cInvalid rarity. Available: " + java.util.Arrays.toString(Rarity.values())); + return; + } + } + + // Create a random weapon/tool/armor + Material[] testMaterials = { + Material.DIAMOND_SWORD, Material.DIAMOND_PICKAXE, Material.DIAMOND_HELMET, + Material.IRON_SWORD, Material.IRON_AXE, Material.IRON_CHESTPLATE, + Material.NETHERITE_SWORD, Material.NETHERITE_BOOTS + }; + + Material material = testMaterials[random.nextInt(testMaterials.length)]; + ItemStack item = new ItemStack(material); + + EnhancedItem enhanced = new EnhancedItem(item, rarity); + player.getInventory().addItem(enhanced.getItemStack()); + + player.sendMessage("§aCreated " + rarity.getDisplayName() + " §aitem with " + + enhanced.getAvailableSlots() + "/" + enhanced.getTotalSlots() + " slots available!"); + } + + private void handleAdd(Player player, String[] args) { + if (args.length < 2) { + player.sendMessage("§cUsage: /augment add [level]"); + player.sendMessage("§cAvailable types: " + java.util.Arrays.toString(AugmentationType.values())); + return; + } + + ItemStack heldItem = player.getInventory().getItemInMainHand(); + if (heldItem.getType() == Material.AIR) { + player.sendMessage("§cYou must hold an item!"); + return; + } + + // Convert to enhanced item if not already + EnhancedItem enhanced; + if (EnhancedItem.isEnhanced(heldItem)) { + enhanced = EnhancedItem.fromItemStack(heldItem); + } else { + enhanced = new EnhancedItem(heldItem, Rarity.COMMON); + } + + if (enhanced == null) { + player.sendMessage("§cFailed to create enhanced item!"); + return; + } + + try { + AugmentationType type = AugmentationType.valueOf(args[1].toUpperCase()); + int level = args.length > 2 ? Integer.parseInt(args[2]) : 1; + level = Math.min(level, type.getMaxLevel()); + level = Math.max(level, 1); + + // Check level restrictions + int maxAllowedLevel = com.simolzimol.levelcraft.player.PlayerLevelManager.getMaxAugmentationLevel( + player, enhanced.getItemLevel(), type.getMaxLevel()); + + if (level > maxAllowedLevel) { + level = maxAllowedLevel; + player.sendMessage("§eLevel reduced to " + level + " (max allowed for your player/item level)"); + } + + if (level < 1) { + player.sendMessage("§cYour player level is too low for this augmentation!"); + return; + } + + Augmentation augmentation = new Augmentation(type, level); + + if (enhanced.addAugmentation(augmentation, player)) { + player.getInventory().setItemInMainHand(enhanced.getItemStack()); + player.sendMessage("§aAdded " + augmentation.getDisplayName() + " §ato your item!"); + } else { + player.sendMessage("§cCould not add augmentation! (No free slots, incompatible type, or level too high)"); + } + + } catch (IllegalArgumentException e) { + player.sendMessage("§cInvalid augmentation type or level!"); + } + } + + private void handleRemove(Player player, String[] args) { + if (args.length < 2) { + player.sendMessage("§cUsage: /augment remove "); + return; + } + + ItemStack heldItem = player.getInventory().getItemInMainHand(); + if (!EnhancedItem.isEnhanced(heldItem)) { + player.sendMessage("§cYou must hold an enhanced item!"); + return; + } + + EnhancedItem enhanced = EnhancedItem.fromItemStack(heldItem); + if (enhanced == null) { + player.sendMessage("§cFailed to load enhanced item!"); + return; + } + + try { + int slotId = Integer.parseInt(args[1]) - 1; // Convert to 0-based + + if (enhanced.removeAugmentation(slotId)) { + player.getInventory().setItemInMainHand(enhanced.getItemStack()); + player.sendMessage("§aRemoved augmentation from slot " + (slotId + 1) + "!"); + } else { + player.sendMessage("§cCould not remove augmentation from slot " + (slotId + 1) + "!"); + } + + } catch (NumberFormatException e) { + player.sendMessage("§cInvalid slot number!"); + } + } + + private void handleInfo(Player player) { + ItemStack heldItem = player.getInventory().getItemInMainHand(); + if (!EnhancedItem.isEnhanced(heldItem)) { + player.sendMessage("§cYou must hold an enhanced item!"); + return; + } + + EnhancedItem enhanced = EnhancedItem.fromItemStack(heldItem); + if (enhanced == null) { + player.sendMessage("§cFailed to load enhanced item!"); + return; + } + + player.sendMessage("§6=== Enhanced Item Info ==="); + player.sendMessage("§eRarity: " + enhanced.getRarity().getDisplayName()); + player.sendMessage("§eLevel: §f" + enhanced.getItemLevel()); + player.sendMessage("§eSlots: §f" + enhanced.getUsedSlots() + "/" + enhanced.getAvailableSlots() + + " available, " + enhanced.getTotalSlots() + " total"); + + player.sendMessage("§6Augmentations:"); + for (AugmentationSlot slot : enhanced.getSlots()) { + player.sendMessage("§7[" + (slot.getSlotId() + 1) + "] " + slot.getDisplayName()); + } + + if (!enhanced.getCalculatedStats().isEmpty()) { + player.sendMessage("§6Total Stats:"); + for (var entry : enhanced.getCalculatedStats().entrySet()) { + player.sendMessage("§a+ " + entry.getValue() + " " + entry.getKey()); + } + } + } + + private void handleLevelUp(Player player) { + ItemStack heldItem = player.getInventory().getItemInMainHand(); + if (!EnhancedItem.isEnhanced(heldItem)) { + player.sendMessage("§cYou must hold an enhanced item!"); + return; + } + + EnhancedItem enhanced = EnhancedItem.fromItemStack(heldItem); + if (enhanced == null) { + player.sendMessage("§cFailed to load enhanced item!"); + return; + } + + int oldLevel = enhanced.getItemLevel(); + enhanced.levelUp(); + int newLevel = enhanced.getItemLevel(); + + player.getInventory().setItemInMainHand(enhanced.getItemStack()); + player.sendMessage("§aItem leveled up! §7(" + oldLevel + " → " + newLevel + ")"); + + if (newLevel % 5 == 0) { + player.sendMessage("§6Milestone reached! A slot may have been unlocked!"); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/command/ConvertCommand.java b/src/main/java/com/simolzimol/levelcraft/command/ConvertCommand.java new file mode 100644 index 0000000..cda3b05 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/command/ConvertCommand.java @@ -0,0 +1,164 @@ +package com.simolzimol.levelcraft.command; + +import com.simolzimol.levelcraft.augmentation.EnhancedItem; +import com.simolzimol.levelcraft.item.Rarity; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Random; + +/** + * Command to convert regular items to Enhanced Items + * Usage: /convert [hand|all] + */ +public class ConvertCommand implements CommandExecutor { + + private final Random random = new Random(); + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("§cThis command can only be used by players!"); + return true; + } + + Player player = (Player) sender; + + if (args.length == 0) { + // Default: convert item in hand + convertItemInHand(player); + return true; + } + + String subcommand = args[0].toLowerCase(); + + switch (subcommand) { + case "hand": + convertItemInHand(player); + break; + + case "all": + convertAllItems(player); + break; + + case "help": + showHelp(player); + break; + + default: + player.sendMessage("§cInvalid argument! Use: /convert [hand|all|help]"); + break; + } + + return true; + } + + private void convertItemInHand(Player player) { + ItemStack item = player.getInventory().getItemInMainHand(); + + if (item == null || item.getType() == Material.AIR) { + player.sendMessage("§cYou must hold an item!"); + return; + } + + if (EnhancedItem.isEnhanced(item)) { + player.sendMessage("§eThis item is already enhanced!"); + return; + } + + if (!isEnhanceable(item)) { + player.sendMessage("§cThis item cannot be enhanced! (Only tools, weapons, and armor)"); + return; + } + + // Convert to Enhanced Item + Rarity rarity = getRandomRarity(); + EnhancedItem enhanced = new EnhancedItem(item, rarity, 1); + player.getInventory().setItemInMainHand(enhanced.getItemStack()); + + player.sendMessage("§a✦ Item converted to Enhanced Item!"); + player.sendMessage("§7Rarity: " + rarity.getDisplayName()); + player.sendMessage("§7Level: §f1 §8(no augmentations yet)"); + } + + private void convertAllItems(Player player) { + if (!player.hasPermission("levelcraft.admin")) { + player.sendMessage("§cYou don't have permission to convert all items!"); + return; + } + + int converted = 0; + + for (int slot = 0; slot < player.getInventory().getSize(); slot++) { + ItemStack item = player.getInventory().getItem(slot); + + if (item == null || item.getType() == Material.AIR) continue; + if (EnhancedItem.isEnhanced(item)) continue; + if (!isEnhanceable(item)) continue; + + // Convert to Enhanced Item + Rarity rarity = getRandomRarity(); + EnhancedItem enhanced = new EnhancedItem(item, rarity, 1); + player.getInventory().setItem(slot, enhanced.getItemStack()); + converted++; + } + + if (converted > 0) { + player.sendMessage("§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§a§l BULK CONVERSION COMPLETE!"); + player.sendMessage("§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§eConverted " + converted + " items to Enhanced Items!"); + player.sendMessage("§7All items are now Level 1 with random rarities."); + player.sendMessage("§7Use /augment add to add augmentations to them!"); + } else { + player.sendMessage("§eNo items to convert found!"); + } + } + + private void showHelp(Player player) { + player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§6§l CONVERT COMMAND HELP"); + player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage(""); + player.sendMessage("§e/convert §7- Convert item in hand to Enhanced Item"); + player.sendMessage("§e/convert hand §7- Convert item in hand to Enhanced Item"); + player.sendMessage("§e/convert all §7- Convert all items in inventory §c(Admin)"); + player.sendMessage("§e/convert help §7- Show this help"); + player.sendMessage(""); + player.sendMessage("§7Enhanced Items start at Level 1 with random rarity"); + player.sendMessage("§7but no augmentations. Use §e/augment add §7to enhance them!"); + player.sendMessage("§6▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + } + + /** + * Determines if an item can be enhanced + */ + private boolean isEnhanceable(ItemStack item) { + if (item == null || item.getType().isAir()) return false; + + // Only enhance items with durability (tools, weapons, armor) + return item.getType().getMaxDurability() > 0; + } + + /** + * Gets a random rarity with weighted distribution + */ + private Rarity getRandomRarity() { + int roll = random.nextInt(1000); + + // Weighted rarity distribution using correct Rarity enum values + if (roll < 3) return Rarity.DIVINE; // 0.3% + if (roll < 15) return Rarity.ANCIENT; // 1.2% + if (roll < 40) return Rarity.MYTHIC; // 2.5% + if (roll < 90) return Rarity.LEGENDARY; // 5.0% + if (roll < 170) return Rarity.EPIC; // 8.0% + if (roll < 290) return Rarity.RARE; // 12.0% + if (roll < 490) return Rarity.UNCOMMON; // 20.0% + if (roll < 980) return Rarity.COMMON; // 49.0% + return Rarity.CURSED; // 2.0% + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/command/DemoCommand.java b/src/main/java/com/simolzimol/levelcraft/command/DemoCommand.java new file mode 100644 index 0000000..1d5c0ea --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/command/DemoCommand.java @@ -0,0 +1,162 @@ +package com.simolzimol.levelcraft.command; + +import com.simolzimol.levelcraft.demo.DemoAugmentationSystem; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +/** + * Command for creating demo items with augmentations + * Usage: /demo [item_number] [all] [random] + */ +public class DemoCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("§cThis command can only be used by players!"); + return true; + } + + Player player = (Player) sender; + + if (args.length == 0) { + showHelp(player); + return true; + } + + String subcommand = args[0].toLowerCase(); + + switch (subcommand) { + case "all": + giveAllDemoItems(player); + break; + + case "random": + giveRandomDemoItem(player); + break; + + case "list": + listDemoItems(player); + break; + + default: + try { + int itemIndex = Integer.parseInt(subcommand) - 1; // Convert to 0-based index + if (itemIndex >= 0 && itemIndex <= 9) { + giveSpecificDemoItem(player, itemIndex); + } else { + player.sendMessage("§cInvalid item number! Use 1-10."); + } + } catch (NumberFormatException e) { + player.sendMessage("§cInvalid argument! Use: /demo [1-10|all|random|list]"); + } + break; + } + + return true; + } + + private void showHelp(Player player) { + player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§6§l DEMO AUGMENTATION SYSTEM"); + player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage(""); + player.sendMessage("§e/demo [1-10] §7- Get a specific demo item"); + player.sendMessage("§e/demo all §7- Get all 10 demo items"); + player.sendMessage("§e/demo random §7- Get a random demo item"); + player.sendMessage("§e/demo list §7- List all available demo items"); + player.sendMessage(""); + player.sendMessage("§7These items showcase the augmentation system with"); + player.sendMessage("§7pre-configured augmentations for different playstyles!"); + player.sendMessage("§6▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + } + + private void giveAllDemoItems(Player player) { + try { + for (int i = 0; i < 10; i++) { + ItemStack demoItem = DemoAugmentationSystem.getDemoItem(i); + player.getInventory().addItem(demoItem); + } + + player.sendMessage("§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§a§l ALL DEMO ITEMS RECEIVED!"); + player.sendMessage("§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§eYou received all 10 demo items with pre-configured augmentations!"); + player.sendMessage("§7Use these items to explore the augmentation system capabilities."); + + } catch (Exception e) { + player.sendMessage("§cError creating demo items: " + e.getMessage()); + } + } + + private void giveRandomDemoItem(Player player) { + try { + ItemStack randomItem = DemoAugmentationSystem.createRandomDemoItem(); + player.getInventory().addItem(randomItem); + + player.sendMessage("§a✦ §eYou received a random demo item!"); + player.sendMessage("§7Check your inventory to see what you got!"); + + } catch (Exception e) { + player.sendMessage("§cError creating random demo item: " + e.getMessage()); + } + } + + private void giveSpecificDemoItem(Player player, int index) { + try { + ItemStack demoItem = DemoAugmentationSystem.getDemoItem(index); + String itemName = DemoAugmentationSystem.getDemoItemName(index); + + player.getInventory().addItem(demoItem); + + player.sendMessage("§a▣ §eYou received: §6" + itemName); + player.sendMessage("§7This item showcases specific augmentation combinations!"); + + } catch (Exception e) { + player.sendMessage("§cError creating demo item: " + e.getMessage()); + } + } + + private void listDemoItems(Player player) { + player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§6§l AVAILABLE DEMO ITEMS"); + player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage(""); + + for (int i = 0; i < 10; i++) { + String itemName = DemoAugmentationSystem.getDemoItemName(i); + String themeColor = getDemoItemThemeColor(i); + player.sendMessage("§e" + (i + 1) + ". " + themeColor + itemName + " §8- " + getDemoItemDescription(i)); + } + + player.sendMessage(""); + player.sendMessage("§7Use §e/demo [number] §7to get a specific item!"); + player.sendMessage("§6▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + } + + private String getDemoItemThemeColor(int index) { + String[] colors = { + "§c", "§9", "§e", "§8", "§6", "§a", "§d", "§7", "§2", "§5" + }; + return colors[index % colors.length]; + } + + private String getDemoItemDescription(int index) { + String[] descriptions = { + "High damage combat sword", + "Ultimate protection armor", + "Efficient mining tool", + "Fast critical strikes", + "Balanced combat/defense", + "Ranged combat bow", + "Magical lightning staff", + "Basic survival gear", + "Treasure finding tool", + "Ultimate legendary item" + }; + return descriptions[index % descriptions.length]; + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/command/PlayerLevelCommand.java b/src/main/java/com/simolzimol/levelcraft/command/PlayerLevelCommand.java new file mode 100644 index 0000000..f917508 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/command/PlayerLevelCommand.java @@ -0,0 +1,160 @@ +package com.simolzimol.levelcraft.command; + +import com.simolzimol.levelcraft.player.PlayerLevelManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/** + * Command for managing player levels and XP + * Usage: /playerlevel [player] [add/set/info] [amount] + */ +public class PlayerLevelCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) { + // Show own level if player + if (sender instanceof Player) { + showPlayerInfo((Player) sender, (Player) sender); + } else { + sender.sendMessage("§cUsage: /playerlevel [add/set/info] [amount]"); + } + return true; + } + + if (args.length == 1) { + // Show target player's level + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendMessage("§cPlayer not found!"); + return true; + } + + if (sender instanceof Player) { + showPlayerInfo((Player) sender, target); + } else { + showPlayerInfoConsole(sender, target); + } + return true; + } + + if (args.length >= 2) { + if (!sender.hasPermission("levelcraft.admin")) { + sender.sendMessage("§cYou don't have permission to modify player levels!"); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendMessage("§cPlayer not found!"); + return true; + } + + String action = args[1].toLowerCase(); + + switch (action) { + case "info": + if (sender instanceof Player) { + showPlayerInfo((Player) sender, target); + } else { + showPlayerInfoConsole(sender, target); + } + break; + + case "add": + if (args.length < 3) { + sender.sendMessage("§cUsage: /playerlevel add "); + return true; + } + + try { + int amount = Integer.parseInt(args[2]); + PlayerLevelManager.addXP(target, amount, "Admin Command"); + sender.sendMessage("§aAdded " + amount + " XP to " + target.getName()); + } catch (NumberFormatException e) { + sender.sendMessage("§cInvalid amount!"); + } + break; + + case "set": + if (args.length < 3) { + sender.sendMessage("§cUsage: /playerlevel set "); + return true; + } + + try { + int level = Integer.parseInt(args[2]); + if (level < 1) { + sender.sendMessage("§cLevel must be at least 1!"); + return true; + } + + PlayerLevelManager.setPlayerLevel(target, level); + sender.sendMessage("§aSet " + target.getName() + "'s level to " + level); + target.sendMessage("§eYour level has been set to " + level + " by an admin!"); + } catch (NumberFormatException e) { + sender.sendMessage("§cInvalid level!"); + } + break; + + default: + sender.sendMessage("§cInvalid action! Use: info, add, set"); + break; + } + } + + return true; + } + + private void showPlayerInfo(Player viewer, Player target) { + int level = PlayerLevelManager.getPlayerLevel(target); + long xp = PlayerLevelManager.getPlayerXP(target); + long xpForNext = PlayerLevelManager.getXPRequiredForLevel(level + 1); + long xpForCurrent = level > 1 ? PlayerLevelManager.getXPRequiredForLevel(level) : 0; + long xpInLevel = xp - xpForCurrent; + long xpNeededForNext = xpForNext - xpForCurrent; + + String targetName = target.equals(viewer) ? "Your" : target.getName() + "'s"; + + viewer.sendMessage("§6▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + viewer.sendMessage("§6§l " + targetName.toUpperCase() + " LEVEL STATUS"); + viewer.sendMessage(""); + viewer.sendMessage("§e Current Level: §6§l" + level); + viewer.sendMessage("§e Total XP: §6" + xp); + viewer.sendMessage("§e XP in Level: §6" + xpInLevel + "§7/§6" + xpNeededForNext); + viewer.sendMessage("§e XP to Next: §6" + (xpNeededForNext - xpInLevel)); + viewer.sendMessage(""); + + // Show progress bar + double progress = (double) xpInLevel / xpNeededForNext; + int barLength = 30; + int filledBars = (int) (progress * barLength); + + StringBuilder progressBar = new StringBuilder("§a"); + for (int i = 0; i < barLength; i++) { + if (i < filledBars) { + progressBar.append("█"); + } else if (i == filledBars && progress > 0) { + progressBar.append("§e▌§7"); + } else { + progressBar.append("§7█"); + } + } + + viewer.sendMessage(" Progress: " + progressBar + " §e" + String.format("%.1f%%", progress * 100)); + viewer.sendMessage("§6▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + } + + private void showPlayerInfoConsole(CommandSender sender, Player target) { + int level = PlayerLevelManager.getPlayerLevel(target); + long xp = PlayerLevelManager.getPlayerXP(target); + long xpForNext = PlayerLevelManager.getXPRequiredForLevel(level + 1); + + sender.sendMessage("§6" + target.getName() + "'s Level Status:"); + sender.sendMessage("§e Level: " + level); + sender.sendMessage("§e XP: " + xp + "/" + xpForNext); + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/demo/DemoAugmentationSystem.java b/src/main/java/com/simolzimol/levelcraft/demo/DemoAugmentationSystem.java new file mode 100644 index 0000000..38729b4 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/demo/DemoAugmentationSystem.java @@ -0,0 +1,213 @@ +package com.simolzimol.levelcraft.demo; + +import com.simolzimol.levelcraft.augmentation.*; +import com.simolzimol.levelcraft.item.Rarity; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Demo system for creating items with pre-configured augmentations + * This showcases the augmentation system with 10 different demo setups + */ +public class DemoAugmentationSystem { + + private static final Random random = new Random(); + + /** + * Creates a list of 10 demo items with different augmentation setups + */ + public static List createDemoItems() { + List demoItems = new ArrayList<>(); + + // 1. Warrior's Sword - Combat focused + demoItems.add(createWarriorSword()); + + // 2. Guardian's Shield - Protection focused + demoItems.add(createGuardianShield()); + + // 3. Miner's Pickaxe - Utility focused + demoItems.add(createMinerPickaxe()); + + // 4. Assassin's Dagger - Critical strike focused + demoItems.add(createAssassinDagger()); + + // 5. Paladin's Hammer - Balanced combat/defense + demoItems.add(createPaladinHammer()); + + // 6. Hunter's Bow - Ranged combat + demoItems.add(createHunterBow()); + + // 7. Wizard's Staff - Magic focused + demoItems.add(createWizardStaff()); + + // 8. Survivor's Armor - Survival focused + demoItems.add(createSurvivorArmor()); + + // 9. Treasure Hunter's Tool - Luck focused + demoItems.add(createTreasureHunterTool()); + + // 10. Legendary All-Purpose Item - Mixed augmentations + demoItems.add(createLegendaryItem()); + + return demoItems; + } + + private static ItemStack createWarriorSword() { + ItemStack sword = new ItemStack(Material.NETHERITE_SWORD); + EnhancedItem enhanced = new EnhancedItem(sword, Rarity.EPIC, 15); + + enhanced.addAugmentation(new Augmentation(AugmentationType.SHARPNESS, 8)); + enhanced.addAugmentation(new Augmentation(AugmentationType.CRITICAL_STRIKE, 4)); + enhanced.addAugmentation(new Augmentation(AugmentationType.FIRE_ASPECT, 2)); + + return enhanced.getItemStack(); + } + + private static ItemStack createGuardianShield() { + ItemStack chestplate = new ItemStack(Material.DIAMOND_CHESTPLATE); + EnhancedItem enhanced = new EnhancedItem(chestplate, Rarity.RARE, 12); + + enhanced.addAugmentation(new Augmentation(AugmentationType.PROTECTION, 5)); + enhanced.addAugmentation(new Augmentation(AugmentationType.RESISTANCE, 3)); + enhanced.addAugmentation(new Augmentation(AugmentationType.THORNS, 2)); + + return enhanced.getItemStack(); + } + + private static ItemStack createMinerPickaxe() { + ItemStack pickaxe = new ItemStack(Material.DIAMOND_PICKAXE); + EnhancedItem enhanced = new EnhancedItem(pickaxe, Rarity.RARE, 10); + + enhanced.addAugmentation(new Augmentation(AugmentationType.EFFICIENCY, 7)); + enhanced.addAugmentation(new Augmentation(AugmentationType.FORTUNE, 4)); + enhanced.addAugmentation(new Augmentation(AugmentationType.UNBREAKING, 4)); + + return enhanced.getItemStack(); + } + + private static ItemStack createAssassinDagger() { + ItemStack dagger = new ItemStack(Material.IRON_SWORD); + EnhancedItem enhanced = new EnhancedItem(dagger, Rarity.UNCOMMON, 8); + + enhanced.addAugmentation(new Augmentation(AugmentationType.CRITICAL_STRIKE, 5)); + enhanced.addAugmentation(new Augmentation(AugmentationType.VAMPIRIC, 2)); + enhanced.addAugmentation(new Augmentation(AugmentationType.SPEED, 1)); + + return enhanced.getItemStack(); + } + + private static ItemStack createPaladinHammer() { + ItemStack hammer = new ItemStack(Material.NETHERITE_AXE); + EnhancedItem enhanced = new EnhancedItem(hammer, Rarity.EPIC, 16); + + enhanced.addAugmentation(new Augmentation(AugmentationType.SMITE, 6)); + enhanced.addAugmentation(new Augmentation(AugmentationType.PROTECTION, 3)); + enhanced.addAugmentation(new Augmentation(AugmentationType.REGENERATION, 2)); + enhanced.addAugmentation(new Augmentation(AugmentationType.UNBREAKING, 3)); + + return enhanced.getItemStack(); + } + + private static ItemStack createHunterBow() { + ItemStack bow = new ItemStack(Material.BOW); + EnhancedItem enhanced = new EnhancedItem(bow, Rarity.RARE, 11); + + enhanced.addAugmentation(new Augmentation(AugmentationType.SHARPNESS, 5)); // Represents Power + enhanced.addAugmentation(new Augmentation(AugmentationType.CRITICAL_STRIKE, 3)); + enhanced.addAugmentation(new Augmentation(AugmentationType.MENDING, 1)); + + return enhanced.getItemStack(); + } + + private static ItemStack createWizardStaff() { + ItemStack staff = new ItemStack(Material.BLAZE_ROD); + EnhancedItem enhanced = new EnhancedItem(staff, Rarity.LEGENDARY, 18); + + enhanced.addAugmentation(new Augmentation(AugmentationType.CHAIN_LIGHTNING, 2)); + enhanced.addAugmentation(new Augmentation(AugmentationType.FIRE_ASPECT, 3)); + enhanced.addAugmentation(new Augmentation(AugmentationType.REGENERATION, 2)); + enhanced.addAugmentation(new Augmentation(AugmentationType.MENDING, 1)); + + return enhanced.getItemStack(); + } + + private static ItemStack createSurvivorArmor() { + ItemStack armor = new ItemStack(Material.CHAINMAIL_HELMET); + EnhancedItem enhanced = new EnhancedItem(armor, Rarity.UNCOMMON, 7); + + enhanced.addAugmentation(new Augmentation(AugmentationType.PROTECTION, 4)); + enhanced.addAugmentation(new Augmentation(AugmentationType.REGENERATION, 1)); + enhanced.addAugmentation(new Augmentation(AugmentationType.UNBREAKING, 3)); + + return enhanced.getItemStack(); + } + + private static ItemStack createTreasureHunterTool() { + ItemStack shovel = new ItemStack(Material.GOLDEN_SHOVEL); + EnhancedItem enhanced = new EnhancedItem(shovel, Rarity.RARE, 9); + + enhanced.addAugmentation(new Augmentation(AugmentationType.FORTUNE, 5)); + enhanced.addAugmentation(new Augmentation(AugmentationType.EFFICIENCY, 6)); + enhanced.addAugmentation(new Augmentation(AugmentationType.SILK_TOUCH, 1)); + + return enhanced.getItemStack(); + } + + private static ItemStack createLegendaryItem() { + ItemStack item = new ItemStack(Material.NETHERITE_HELMET); + EnhancedItem enhanced = new EnhancedItem(item, Rarity.LEGENDARY, 20); + + // Fill all slots with different augmentations + enhanced.addAugmentation(new Augmentation(AugmentationType.PROTECTION, 5)); + enhanced.addAugmentation(new Augmentation(AugmentationType.RESISTANCE, 3)); + enhanced.addAugmentation(new Augmentation(AugmentationType.REGENERATION, 3)); + enhanced.addAugmentation(new Augmentation(AugmentationType.SPEED, 2)); + enhanced.addAugmentation(new Augmentation(AugmentationType.UNBREAKING, 5)); + enhanced.addAugmentation(new Augmentation(AugmentationType.MENDING, 1)); + + return enhanced.getItemStack(); + } + + /** + * Creates a random demo item from the available templates + */ + public static ItemStack createRandomDemoItem() { + List demoItems = createDemoItems(); + return demoItems.get(random.nextInt(demoItems.size())).clone(); + } + + /** + * Gets a specific demo item by index (0-9) + */ + public static ItemStack getDemoItem(int index) { + if (index < 0 || index >= 10) { + throw new IllegalArgumentException("Demo item index must be between 0 and 9"); + } + + List demoItems = createDemoItems(); + return demoItems.get(index).clone(); + } + + /** + * Gets the name of a demo item by index + */ + public static String getDemoItemName(int index) { + String[] names = { + "Warrior's Sword", "Guardian's Shield", "Miner's Pickaxe", + "Assassin's Dagger", "Paladin's Hammer", "Hunter's Bow", + "Wizard's Staff", "Survivor's Armor", "Treasure Hunter's Tool", + "Legendary All-Purpose Item" + }; + + if (index < 0 || index >= names.length) { + return "Unknown Demo Item"; + } + + return names[index]; + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/item/ItemEffects.java b/src/main/java/com/simolzimol/levelcraft/item/ItemEffects.java index 7923a56..7b26ece 100644 --- a/src/main/java/com/simolzimol/levelcraft/item/ItemEffects.java +++ b/src/main/java/com/simolzimol/levelcraft/item/ItemEffects.java @@ -2,20 +2,38 @@ package com.simolzimol.levelcraft.item; import java.util.List; +/** + * @deprecated This class is deprecated in favor of the new Augmentation System. + * Use com.simolzimol.levelcraft.augmentation.AugmentationType instead. + * This class is kept for backward compatibility but should not be used in new code. + */ +@Deprecated public class ItemEffects { + + // These are deprecated - use AugmentationType enum instead + @Deprecated public static final List EFFECTS = List.of( - "§bEffekt: Feuerresistenz", - "§bEffekt: Schnelligkeit", - "§bEffekt: Regeneration", - "§bEffekt: Unsichtbarkeit", - "§bEffekt: Stärke" + "§8§m§bEffekt: Feuerresistenz§r §7(Use FIRE_ASPECT augmentation)", + "§8§m§bEffekt: Schnelligkeit§r §7(Use SPEED augmentation)", + "§8§m§bEffekt: Regeneration§r §7(Use REGENERATION augmentation)", + "§8§m§bEffekt: Unsichtbarkeit§r §7(Removed - not available)", + "§8§m§bEffekt: Stärke§r §7(Use SHARPNESS augmentation)" ); + @Deprecated public static final List SKILLS = List.of( - "§dSkill: Doppelschlag", - "§dSkill: Lebensraub", - "§dSkill: Flächenschaden", - "§dSkill: Rückstoß", - "§dSkill: Kritischer Treffer" + "§8§m§dSkill: Doppelschlag§r §7(Use CRITICAL_STRIKE augmentation)", + "§8§m§dSkill: Lebensraub§r §7(Use VAMPIRIC augmentation)", + "§8§m§dSkill: Flächenschaden§r §7(Use CHAIN_LIGHTNING augmentation)", + "§8§m§dSkill: Rückstoß§r §7(Use KNOCKBACK augmentation)", + "§8§m§dSkill: Kritischer Treffer§r §7(Use CRITICAL_STRIKE augmentation)" ); + + /** + * @deprecated Use the new Augmentation system instead + */ + @Deprecated + public static void printDeprecationWarning() { + System.out.println("[LevelCraft] WARNING: ItemEffects is deprecated. Use AugmentationType instead!"); + } } diff --git a/src/main/java/com/simolzimol/levelcraft/item/ItemUtil.java b/src/main/java/com/simolzimol/levelcraft/item/ItemUtil.java index 475f7c6..929e96f 100644 --- a/src/main/java/com/simolzimol/levelcraft/item/ItemUtil.java +++ b/src/main/java/com/simolzimol/levelcraft/item/ItemUtil.java @@ -167,20 +167,19 @@ public class ItemUtil { } } - // Effekte und Skills NUR wenn NICHT CURSED + // Legacy Effects and Skills - DISABLED (Use new Augmentation System) + // NOTE: ItemEffects system has been replaced by the Augmentation System + // Use EnhancedItem with AugmentationType instead for new items + /* if (rarity != Rarity.CURSED) { - java.util.List effectsList = new java.util.ArrayList<>(ItemEffects.EFFECTS); - java.util.Collections.shuffle(effectsList, random); - int effects = 1; - for (int i = 0; i < effects && i < effectsList.size(); i++) { - lore.add(net.kyori.adventure.text.Component.text(effectsList.get(i))); - } - java.util.List skillsList = new java.util.ArrayList<>(ItemEffects.SKILLS); - java.util.Collections.shuffle(skillsList, random); - int skills = 1; - for (int i = 0; i < skills && i < skillsList.size(); i++) { - lore.add(net.kyori.adventure.text.Component.text(skillsList.get(i))); - } + // Old system disabled - use Augmentation system instead + ItemEffects.printDeprecationWarning(); + } + */ + + // Add note about new augmentation system + if (rarity != Rarity.CURSED) { + lore.add(net.kyori.adventure.text.Component.text("§8Use /augment add to add augmentations")); } meta.lore(lore); diff --git a/src/main/java/com/simolzimol/levelcraft/listener/AutoEnhancementListener.java b/src/main/java/com/simolzimol/levelcraft/listener/AutoEnhancementListener.java new file mode 100644 index 0000000..59ae16f --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/listener/AutoEnhancementListener.java @@ -0,0 +1,111 @@ +package com.simolzimol.levelcraft.listener; + +import com.simolzimol.levelcraft.augmentation.EnhancedItem; +import com.simolzimol.levelcraft.item.Rarity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDropItemEvent; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.world.LootGenerateEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Automatically converts all items to Enhanced Items (Level 1, no augmentations) + * This replaces the old legacy system and ensures all items use the new framework + */ +public class AutoEnhancementListener implements Listener { + + private final Random random = new Random(); + + @EventHandler(priority = EventPriority.HIGH) + public void onItemCraft(CraftItemEvent event) { + ItemStack result = event.getCurrentItem(); + if (result == null) return; + + // Only enhance items that can have durability (tools, weapons, armor) + if (!isEnhanceable(result)) return; + + // Skip if already enhanced + if (EnhancedItem.isEnhanced(result)) return; + + // Convert to Enhanced Item with random rarity but Level 1 and no augmentations + Rarity rarity = getRandomRarity(); + EnhancedItem enhanced = new EnhancedItem(result, rarity, 1); + event.setCurrentItem(enhanced.getItemStack()); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onMobDrop(EntityDropItemEvent event) { + ItemStack item = event.getItemDrop().getItemStack(); + if (item == null) return; + + if (!isEnhanceable(item)) return; + if (EnhancedItem.isEnhanced(item)) return; + + // Convert to Enhanced Item + Rarity rarity = getRandomRarity(); + EnhancedItem enhanced = new EnhancedItem(item, rarity, 1); + event.getItemDrop().setItemStack(enhanced.getItemStack()); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onLootGenerate(LootGenerateEvent event) { + List newLoot = new ArrayList<>(); + boolean changed = false; + + for (ItemStack item : event.getLoot()) { + if (item == null) { + newLoot.add(item); + continue; + } + + if (!isEnhanceable(item) || EnhancedItem.isEnhanced(item)) { + newLoot.add(item); + continue; + } + + // Convert to Enhanced Item + Rarity rarity = getRandomRarity(); + EnhancedItem enhanced = new EnhancedItem(item, rarity, 1); + newLoot.add(enhanced.getItemStack()); + changed = true; + } + + if (changed) { + event.setLoot(newLoot); + } + } + + /** + * Determines if an item should be enhanced + */ + private boolean isEnhanceable(ItemStack item) { + if (item == null || item.getType().isAir()) return false; + + // Only enhance items with durability (tools, weapons, armor) + return item.getType().getMaxDurability() > 0; + } + + /** + * Gets a random rarity with weighted distribution + */ + private Rarity getRandomRarity() { + int roll = random.nextInt(1000); + + // Weighted rarity distribution using correct Rarity enum values + if (roll < 3) return Rarity.DIVINE; // 0.3% + if (roll < 15) return Rarity.ANCIENT; // 1.2% + if (roll < 40) return Rarity.MYTHIC; // 2.5% + if (roll < 90) return Rarity.LEGENDARY; // 5.0% + if (roll < 170) return Rarity.EPIC; // 8.0% + if (roll < 290) return Rarity.RARE; // 12.0% + if (roll < 490) return Rarity.UNCOMMON; // 20.0% + if (roll < 980) return Rarity.COMMON; // 49.0% + return Rarity.CURSED; // 2.0% + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/listener/CraftListener.java b/src/main/java/com/simolzimol/levelcraft/listener/CraftListener.java index 408c64b..f7b0ed8 100644 --- a/src/main/java/com/simolzimol/levelcraft/listener/CraftListener.java +++ b/src/main/java/com/simolzimol/levelcraft/listener/CraftListener.java @@ -14,6 +14,11 @@ public class CraftListener implements Listener { @EventHandler public void onCraft(CraftItemEvent event) { + // LEGACY SYSTEM DISABLED - Use new Augmentation System instead + // This old system conflicts with the new EnhancedItem augmentation system + // Players can now use /demo or /augment commands to get enhanced items + + /* OLD CODE DISABLED: ItemStack result = event.getCurrentItem(); if (result == null) return; if (result.getType().getMaxDurability() <= 0) return; @@ -23,5 +28,6 @@ public class CraftListener implements Listener { ItemUtil.assignUniqueId(result); ItemUtil.addRandomBonuses(result, rarity, random); event.setCurrentItem(result); + */ } } diff --git a/src/main/java/com/simolzimol/levelcraft/listener/ItemLevelingListener.java b/src/main/java/com/simolzimol/levelcraft/listener/ItemLevelingListener.java new file mode 100644 index 0000000..1585cad --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/listener/ItemLevelingListener.java @@ -0,0 +1,90 @@ +package com.simolzimol.levelcraft.listener; + +import com.simolzimol.levelcraft.system.ItemLevelingSystem; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.enchantment.EnchantItemEvent; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +/** + * Handles item XP gain from various activities + */ +public class ItemLevelingListener implements Listener { + + @EventHandler(priority = EventPriority.MONITOR) + public void onBlockBreak(BlockBreakEvent event) { + if (event.isCancelled()) return; + + Player player = event.getPlayer(); + ItemStack tool = player.getInventory().getItemInMainHand(); + + if (tool == null || tool.getType().isAir()) return; + + // Give XP to the tool used + ItemLevelingSystem.addItemXP(tool, ItemLevelingSystem.XP_PER_BLOCK_BROKEN, + player, "Mining"); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onEntityDeath(EntityDeathEvent event) { + Player killer = event.getEntity().getKiller(); + if (killer == null) return; + + ItemStack weapon = killer.getInventory().getItemInMainHand(); + if (weapon == null || weapon.getType().isAir()) return; + + // Give XP to the weapon used for killing + ItemLevelingSystem.addItemXP(weapon, ItemLevelingSystem.XP_PER_MOB_KILL, + killer, "Combat"); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onEntityDamage(EntityDamageByEntityEvent event) { + if (event.isCancelled()) return; + + if (event.getDamager() instanceof Player player) { + ItemStack weapon = player.getInventory().getItemInMainHand(); + if (weapon == null || weapon.getType().isAir()) return; + + // Give XP based on damage dealt (small amounts) + double damage = event.getFinalDamage(); + int xp = (int) Math.ceil(damage * ItemLevelingSystem.XP_PER_DAMAGE_DEALT); + + if (xp > 0) { + ItemLevelingSystem.addItemXP(weapon, xp, player, "Combat"); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onCraftItem(CraftItemEvent event) { + if (event.isCancelled()) return; + + if (event.getWhoClicked() instanceof Player player) { + ItemStack result = event.getCurrentItem(); + if (result == null) return; + + // Give XP to crafted items (if they are enhanced) + ItemLevelingSystem.addItemXP(result, ItemLevelingSystem.XP_PER_CRAFT, + player, "Crafting"); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onEnchantItem(EnchantItemEvent event) { + if (event.isCancelled()) return; + + Player player = event.getEnchanter(); + ItemStack item = event.getItem(); + + // Give significant XP for enchanting + ItemLevelingSystem.addItemXP(item, ItemLevelingSystem.XP_PER_ENCHANT, + player, "Enchanting"); + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/listener/LootListener.java b/src/main/java/com/simolzimol/levelcraft/listener/LootListener.java index cb85c09..f288aa4 100644 --- a/src/main/java/com/simolzimol/levelcraft/listener/LootListener.java +++ b/src/main/java/com/simolzimol/levelcraft/listener/LootListener.java @@ -57,7 +57,11 @@ public class LootListener implements Listener { String type = inv.getType().name(); if (!(type.contains("CHEST") || type.contains("BARREL") || type.contains("SHULKER_BOX"))) return; - // Verzögere die Verarbeitung um 1 Tick, damit der Loot generiert wurde + // LEGACY SYSTEM DISABLED - Use new Augmentation System instead + // This old system conflicts with the new EnhancedItem augmentation system + // Use /demo commands to get enhanced items instead + + /* OLD CODE DISABLED: Bukkit.getScheduler().runTaskLater(plugin, () -> { for (ItemStack item : inv.getContents()) { if (item == null) continue; @@ -67,8 +71,7 @@ public class LootListener implements Listener { if (meta == null) continue; if (meta.getPersistentDataContainer().has(ItemUtil.getRarityKey(), PersistentDataType.INTEGER)) continue; - // Rarity für Kisten-Loot: -1 bis 5 - int rarityValue = random.nextInt(7) - 1; // -1 bis 5 + int rarityValue = random.nextInt(7) - 1; if (rarityValue > 5) rarityValue = 5; if (rarityValue < -1) rarityValue = -1; @@ -78,6 +81,7 @@ public class LootListener implements Listener { ItemUtil.addRandomBonuses(item, rarity, random); item.setItemMeta(item.getItemMeta()); } - }, 1L); // 1 Tick Verzögerung + }, 1L); + */ } } diff --git a/src/main/java/com/simolzimol/levelcraft/listener/MobDropListener.java b/src/main/java/com/simolzimol/levelcraft/listener/MobDropListener.java index 781057b..d8349e8 100644 --- a/src/main/java/com/simolzimol/levelcraft/listener/MobDropListener.java +++ b/src/main/java/com/simolzimol/levelcraft/listener/MobDropListener.java @@ -14,13 +14,15 @@ public class MobDropListener implements Listener { @EventHandler public void onMobDrop(EntityDropItemEvent event) { + // LEGACY SYSTEM DISABLED - Use new Augmentation System instead + // This old system conflicts with the new EnhancedItem augmentation system + // Use DemoAugmentationSystem or manual augmentation commands instead + + /* OLD CODE DISABLED: ItemStack item = event.getItemDrop().getItemStack(); if (item == null) return; - // Optional: Nur für bestimmte Itemtypen, z.B. Waffen, Rüstung, Tools, Bögen, Schilde - // if (!ItemUtil.isWeapon(item) && !ItemUtil.isArmor(item) && !ItemUtil.isTool(item) && !item.getType().name().equals("SHIELD") && !item.getType().name().equals("BOW") && !item.getType().name().equals("CROSSBOW")) return; - - int rarityValue = random.nextInt(9) - 1; // -1 bis 7 + int rarityValue = random.nextInt(9) - 1; if (rarityValue > 7) rarityValue = 7; if (rarityValue < -1) rarityValue = -1; @@ -29,5 +31,6 @@ public class MobDropListener implements Listener { ItemUtil.assignUniqueId(item); ItemUtil.addRandomBonuses(item, rarity, random); event.getItemDrop().setItemStack(item); + */ } } diff --git a/src/main/java/com/simolzimol/levelcraft/listener/MobSpawnListener.java b/src/main/java/com/simolzimol/levelcraft/listener/MobSpawnListener.java index e246e92..b49be1a 100644 --- a/src/main/java/com/simolzimol/levelcraft/listener/MobSpawnListener.java +++ b/src/main/java/com/simolzimol/levelcraft/listener/MobSpawnListener.java @@ -31,10 +31,13 @@ public class MobSpawnListener implements Listener { eq.getLeggings(), eq.getBoots() }; + // LEGACY SYSTEM DISABLED - Use new Augmentation System instead + // This old system conflicts with the new EnhancedItem augmentation system + + /* OLD CODE DISABLED: for (int i = 0; i < items.length; i++) { ItemStack item = items[i]; if (item == null || item.getType().isAir()) continue; - // Optional: Filter für Waffen, Bögen, etc. Rarity rarity = Rarity.getRandomRarity(random, true, 5); ItemUtil.setRarity(item, rarity); ItemUtil.assignUniqueId(item); @@ -48,6 +51,7 @@ public class MobSpawnListener implements Listener { case 5 -> eq.setBoots(item); } } + */ }, 1L // 1 Tick Verzögerung ); } diff --git a/src/main/java/com/simolzimol/levelcraft/listener/PlayerLevelListener.java b/src/main/java/com/simolzimol/levelcraft/listener/PlayerLevelListener.java new file mode 100644 index 0000000..e54f4fb --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/listener/PlayerLevelListener.java @@ -0,0 +1,141 @@ +package com.simolzimol.levelcraft.listener; + +import com.simolzimol.levelcraft.player.PlayerLevelManager; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.player.PlayerJoinEvent; + +/** + * Listener for player level and XP events + */ +public class PlayerLevelListener implements Listener { + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + // Initialize player data if needed + PlayerLevelManager.getPlayerLevel(player); + PlayerLevelManager.getPlayerXP(player); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onCraftItem(CraftItemEvent event) { + if (event.isCancelled()) return; + + if (event.getWhoClicked() instanceof Player) { + Player player = (Player) event.getWhoClicked(); + + // Give crafting XP based on item complexity + int xpGain = calculateCraftingXP(event); + if (xpGain > 0) { + PlayerLevelManager.addXP(player, xpGain, "Crafting"); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onBlockBreak(BlockBreakEvent event) { + if (event.isCancelled()) return; + + Player player = event.getPlayer(); + + // Give mining XP for certain blocks + int xpGain = calculateMiningXP(event); + if (xpGain > 0) { + PlayerLevelManager.addXP(player, xpGain, "Mining"); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onEntityDeath(EntityDeathEvent event) { + Player killer = event.getEntity().getKiller(); + if (killer == null) return; + + // Give combat XP based on mob type + int xpGain = calculateCombatXP(event); + if (xpGain > 0) { + PlayerLevelManager.addXP(killer, xpGain, "Combat"); + } + } + + private int calculateCraftingXP(CraftItemEvent event) { + // Base XP for crafting + int baseXP = 5; + int amount = event.getRecipe().getResult().getAmount(); + + // More complex recipes give more XP + int ingredientCount = event.getRecipe().getResult().getType().toString().length() / 10; // Simple complexity metric + return Math.max(1, baseXP + ingredientCount) * amount; + } + + private int calculateMiningXP(BlockBreakEvent event) { + switch (event.getBlock().getType()) { + case COAL_ORE: + case DEEPSLATE_COAL_ORE: + return 8; + case IRON_ORE: + case DEEPSLATE_IRON_ORE: + return 12; + case GOLD_ORE: + case DEEPSLATE_GOLD_ORE: + return 15; + case REDSTONE_ORE: + case DEEPSLATE_REDSTONE_ORE: + return 10; + case LAPIS_ORE: + case DEEPSLATE_LAPIS_ORE: + return 12; + case DIAMOND_ORE: + case DEEPSLATE_DIAMOND_ORE: + return 25; + case EMERALD_ORE: + case DEEPSLATE_EMERALD_ORE: + return 30; + case ANCIENT_DEBRIS: + return 50; + case STONE: + case DEEPSLATE: + return 1; + case COBBLESTONE: + case COBBLED_DEEPSLATE: + return 1; + default: + return 0; + } + } + + private int calculateCombatXP(EntityDeathEvent event) { + switch (event.getEntity().getType()) { + case ZOMBIE: + case SKELETON: + case SPIDER: + return 15; + case CREEPER: + return 20; + case ENDERMAN: + return 25; + case WITCH: + return 18; + case BLAZE: + return 30; + case WITHER_SKELETON: + return 35; + case GHAST: + return 40; + case ENDER_DRAGON: + return 500; + case WITHER: + return 300; + case VILLAGER: + case IRON_GOLEM: + return 0; // No XP for killing friendly mobs + default: + return 10; // Default for other hostile mobs + } + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/listener/VillagerTradeListener.java b/src/main/java/com/simolzimol/levelcraft/listener/VillagerTradeListener.java index dbf85bf..0eb269f 100644 --- a/src/main/java/com/simolzimol/levelcraft/listener/VillagerTradeListener.java +++ b/src/main/java/com/simolzimol/levelcraft/listener/VillagerTradeListener.java @@ -24,14 +24,18 @@ public class VillagerTradeListener implements Listener { Inventory inv = event.getInventory(); if (!(inv instanceof MerchantInventory merchantInventory)) return; - // Neue Rezeptliste + // LEGACY SYSTEM DISABLED - Use new Augmentation System instead + // This old system conflicts with the new EnhancedItem augmentation system + // Use /demo commands or manual augmentation instead + + /* OLD CODE DISABLED: List newRecipes = new ArrayList<>(); for (MerchantRecipe recipe : merchantInventory.getMerchant().getRecipes()) { ItemStack result = recipe.getResult().clone(); ItemMeta meta = result.getItemMeta(); if (meta != null && !meta.getPersistentDataContainer().has(ItemUtil.getRarityKey(), PersistentDataType.INTEGER)) { if (result.getType().getMaxDurability() > 0) { - int rarityValue = random.nextInt(7) - 1; // -1 bis 5 + int rarityValue = random.nextInt(7) - 1; if (rarityValue > 5) rarityValue = 5; if (rarityValue < -1) rarityValue = -1; Rarity rarity = Rarity.fromValue(rarityValue); @@ -40,7 +44,6 @@ public class VillagerTradeListener implements Listener { ItemUtil.addRandomBonuses(result, rarity, random); } } - // Neues Rezept mit gleichem Input, aber modifiziertem Output MerchantRecipe newRecipe = new MerchantRecipe(result, recipe.getMaxUses()); newRecipe.setIngredients(recipe.getIngredients()); newRecipe.setExperienceReward(recipe.hasExperienceReward()); @@ -49,5 +52,6 @@ public class VillagerTradeListener implements Listener { newRecipes.add(newRecipe); } merchantInventory.getMerchant().setRecipes(newRecipes); + */ } } diff --git a/src/main/java/com/simolzimol/levelcraft/player/PlayerLevelManager.java b/src/main/java/com/simolzimol/levelcraft/player/PlayerLevelManager.java new file mode 100644 index 0000000..9aac6c0 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/player/PlayerLevelManager.java @@ -0,0 +1,220 @@ +package com.simolzimol.levelcraft.player; + +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class PlayerLevelManager { + private static NamespacedKey playerLevelKey; + private static NamespacedKey playerXpKey; + private static final Map levelCache = new HashMap<>(); + private static final Map xpCache = new HashMap<>(); + + public static void init(org.bukkit.plugin.Plugin plugin) { + playerLevelKey = new NamespacedKey(plugin, "player_level"); + playerXpKey = new NamespacedKey(plugin, "player_xp"); + } + + public static int getPlayerLevel(Player player) { + UUID uuid = player.getUniqueId(); + + // Check cache first + if (levelCache.containsKey(uuid)) { + return levelCache.get(uuid); + } + + // Load from persistent data + Integer level = player.getPersistentDataContainer().get(playerLevelKey, PersistentDataType.INTEGER); + if (level == null) { + level = 1; // Starting level + setPlayerLevel(player, level); + } + + levelCache.put(uuid, level); + return level; + } + + public static void setPlayerLevel(Player player, int level) { + UUID uuid = player.getUniqueId(); + level = Math.max(1, level); // Minimum level 1 + + player.getPersistentDataContainer().set(playerLevelKey, PersistentDataType.INTEGER, level); + levelCache.put(uuid, level); + + // Update action bar or send message + player.sendMessage("§6Level up! You are now level " + level + "!"); + } + + public static long getPlayerXP(Player player) { + UUID uuid = player.getUniqueId(); + + // Check cache first + if (xpCache.containsKey(uuid)) { + return xpCache.get(uuid); + } + + // Load from persistent data + Long xp = player.getPersistentDataContainer().get(playerXpKey, PersistentDataType.LONG); + if (xp == null) { + xp = 0L; + setPlayerXP(player, xp); + } + + xpCache.put(uuid, xp); + return xp; + } + + public static void setPlayerXP(Player player, long xp) { + UUID uuid = player.getUniqueId(); + xp = Math.max(0, xp); + + player.getPersistentDataContainer().set(playerXpKey, PersistentDataType.LONG, xp); + xpCache.put(uuid, xp); + } + + public static void addPlayerXP(Player player, long xpAmount) { + long currentXp = getPlayerXP(player); + int currentLevel = getPlayerLevel(player); + + long newXp = currentXp + xpAmount; + setPlayerXP(player, newXp); + + // Check for level up + int newLevel = calculateLevelFromXP(newXp); + if (newLevel > currentLevel) { + setPlayerLevel(player, newLevel); + + // Level up effects + player.sendMessage("§a+" + xpAmount + " XP!"); + player.playSound(player.getLocation(), org.bukkit.Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f); + } else if (xpAmount > 0) { + player.sendMessage("§a+" + xpAmount + " XP!"); + } + } + + public static long getXPRequiredForLevel(int level) { + // Exponential XP curve: Level^2.5 * 100 + return (long) (Math.pow(level, 2.5) * 100); + } + + public static int calculateLevelFromXP(long totalXP) { + int level = 1; + while (getXPRequiredForLevel(level + 1) <= totalXP) { + level++; + if (level >= 100) break; // Max level cap + } + return level; + } + + public static long getXPToNextLevel(Player player) { + int currentLevel = getPlayerLevel(player); + long currentXP = getPlayerXP(player); + long xpForNextLevel = getXPRequiredForLevel(currentLevel + 1); + + return Math.max(0, xpForNextLevel - currentXP); + } + + public static double getXPProgressToNextLevel(Player player) { + int currentLevel = getPlayerLevel(player); + long currentXP = getPlayerXP(player); + long xpForCurrentLevel = getXPRequiredForLevel(currentLevel); + long xpForNextLevel = getXPRequiredForLevel(currentLevel + 1); + + if (currentLevel >= 100) return 1.0; // Max level + + long xpInCurrentLevel = currentXP - xpForCurrentLevel; + long xpNeededForNextLevel = xpForNextLevel - xpForCurrentLevel; + + return Math.min(1.0, (double) xpInCurrentLevel / xpNeededForNextLevel); + } + + // Calculate max augmentation level based on player and item level + public static int getMaxAugmentationLevel(Player player, int itemLevel, int augmentationMaxLevel) { + int playerLevel = getPlayerLevel(player); + + // Formula: (Player Level + Item Level) / 4, capped by augmentation's natural max + int calculatedMax = (playerLevel + itemLevel) / 4; + calculatedMax = Math.max(1, calculatedMax); // At least level 1 + + return Math.min(calculatedMax, augmentationMaxLevel); + } + + /** + * Adds XP to a player with a custom message source + */ + public static void addXP(Player player, int xp, String source) { + if (xp <= 0) return; + + long currentXP = getPlayerXP(player); + int currentLevel = getPlayerLevel(player); + + // Add the XP + addPlayerXP(player, xp); + + // Check if we leveled up + int newLevel = getPlayerLevel(player); + if (newLevel > currentLevel) { + // Level up notification is handled in addPlayerXP + player.sendMessage("§7Source: " + source); + } else { + // Show XP gain message + long newXP = currentXP + xp; + long xpForNext = getXPRequiredForLevel(newLevel + 1); + player.sendMessage("§a+" + xp + " XP §7(" + source + ") §8[" + + newXP + "/" + xpForNext + "]"); + } + } + + // Grant XP for various activities + public static void giveXPForCrafting(Player player) { + addPlayerXP(player, 10 + (getPlayerLevel(player) * 2)); + } + + public static void giveXPForKill(Player player, org.bukkit.entity.EntityType entityType) { + long baseXP = switch (entityType) { + case ZOMBIE, SKELETON, SPIDER -> 5; + case CREEPER, ENDERMAN -> 10; + case BLAZE, WITHER_SKELETON -> 15; + case ENDER_DRAGON -> 1000; + case WITHER -> 500; + default -> 3; + }; + + addPlayerXP(player, baseXP + (getPlayerLevel(player) / 2)); + } + + public static void giveXPForMining(Player player, org.bukkit.Material material) { + long baseXP = switch (material) { + case COAL_ORE, DEEPSLATE_COAL_ORE -> 2; + case IRON_ORE, DEEPSLATE_IRON_ORE -> 3; + case GOLD_ORE, DEEPSLATE_GOLD_ORE -> 4; + case DIAMOND_ORE, DEEPSLATE_DIAMOND_ORE -> 8; + case EMERALD_ORE, DEEPSLATE_EMERALD_ORE -> 10; + case ANCIENT_DEBRIS -> 15; + default -> 1; + }; + + addPlayerXP(player, baseXP); + } + + // Clear cache when player leaves + public static void clearCache(Player player) { + UUID uuid = player.getUniqueId(); + levelCache.remove(uuid); + xpCache.remove(uuid); + } + + // Get level info for display + public static String getLevelInfo(Player player) { + int level = getPlayerLevel(player); + long xpToNext = getXPToNextLevel(player); + double progress = getXPProgressToNextLevel(player) * 100; + + return String.format("§6Level %d §7(%.1f%% to next) §8- %d XP needed", + level, progress, xpToNext); + } +} \ No newline at end of file diff --git a/src/main/java/com/simolzimol/levelcraft/system/ItemLevelingSystem.java b/src/main/java/com/simolzimol/levelcraft/system/ItemLevelingSystem.java new file mode 100644 index 0000000..5fa6e77 --- /dev/null +++ b/src/main/java/com/simolzimol/levelcraft/system/ItemLevelingSystem.java @@ -0,0 +1,196 @@ +package com.simolzimol.levelcraft.system; + +import com.simolzimol.levelcraft.augmentation.EnhancedItem; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +/** + * System for item leveling and XP management + * Items gain XP through usage and level up to unlock more augmentation slots + */ +public class ItemLevelingSystem { + + // XP constants for different activities + public static final int XP_PER_BLOCK_BROKEN = 2; + public static final int XP_PER_MOB_KILL = 5; + public static final int XP_PER_DAMAGE_DEALT = 1; + public static final int XP_PER_CRAFT = 3; + public static final int XP_PER_ENCHANT = 10; + + // Level up thresholds (exponential growth) + private static final int BASE_XP_REQUIREMENT = 100; + private static final double XP_MULTIPLIER = 1.5; + + /** + * Adds XP to an item and handles level ups + * @param item The item to add XP to + * @param xp Amount of XP to add + * @param player The player using the item (for notifications) + * @param activity Description of the activity + * @return True if the item leveled up + */ + public static boolean addItemXP(ItemStack item, int xp, Player player, String activity) { + if (item == null || !EnhancedItem.isEnhanced(item)) { + return false; + } + + EnhancedItem enhanced = EnhancedItem.fromItemStack(item); + if (enhanced == null) return false; + + // Get current XP and level + long currentXP = enhanced.getItemXP(); + int currentLevel = enhanced.getItemLevel(); + + // Add XP + long newXP = currentXP + xp; + enhanced.setItemXP(newXP); + + // Check for level up + int newLevel = calculateLevelFromXP(newXP); + boolean leveledUp = false; + + if (newLevel > currentLevel) { + enhanced.setItemLevel(newLevel); + leveledUp = true; + + // Notify player of level up + notifyItemLevelUp(player, enhanced, currentLevel, newLevel, activity); + + // Update the item in player's inventory + updateItemInInventory(player, item, enhanced.getItemStack()); + } else if (xp > 0) { + // Just show XP gain + long xpForNext = getXPRequiredForLevel(newLevel + 1); + player.sendMessage("§7+" + xp + " Item XP §8(" + activity + ") §7[" + + newXP + "/" + xpForNext + "]"); + + // Update the item in player's inventory + updateItemInInventory(player, item, enhanced.getItemStack()); + } + + return leveledUp; + } + + /** + * Calculates the required XP for a specific level + */ + public static long getXPRequiredForLevel(int level) { + if (level <= 1) return 0; + return (long) (BASE_XP_REQUIREMENT * Math.pow(XP_MULTIPLIER, level - 2)); + } + + /** + * Calculates the level from total XP + */ + public static int calculateLevelFromXP(long totalXP) { + int level = 1; + long requiredXP = 0; + + while (requiredXP <= totalXP) { + level++; + requiredXP = getXPRequiredForLevel(level); + } + + return level - 1; // Return the highest level we can afford + } + + /** + * Gets the maximum level an item can reach (based on rarity) + */ + public static int getMaxItemLevel(com.simolzimol.levelcraft.item.Rarity rarity) { + return switch (rarity) { + case CURSED -> 5; // Limited growth + case COMMON -> 10; // Basic cap + case UNCOMMON -> 15; // Moderate cap + case RARE -> 25; // Good cap + case EPIC -> 40; // High cap + case LEGENDARY -> 60; // Very high cap + case MYTHIC -> 80; // Extreme cap + case ANCIENT -> 100; // Maximum for Ancient + case DIVINE -> 120; // Ultimate cap for Divine + }; + } + + /** + * Notifies the player of an item level up + */ + private static void notifyItemLevelUp(Player player, EnhancedItem enhanced, + int oldLevel, int newLevel, String activity) { + + // Title notification + player.sendTitle( + "§6§lITEM LEVEL UP!", + enhanced.getRarity().getDisplayName() + " §7→ §6Level " + newLevel, + 10, 30, 10 + ); + + // Chat notification + player.sendMessage("§a▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + player.sendMessage("§a§l ITEM LEVEL UP!"); + player.sendMessage("§e " + enhanced.getRarity().getDisplayName() + " §eitem leveled up!"); + player.sendMessage("§7 Level " + oldLevel + " → §6Level " + newLevel); + player.sendMessage("§7 Activity: §f" + activity); + + // Check if new augmentation slots unlocked + int oldSlots = calculateSlotsForLevel(oldLevel); + int newSlots = calculateSlotsForLevel(newLevel); + if (newSlots > oldSlots) { + int unlockedSlots = newSlots - oldSlots; + player.sendMessage("§a ✦ Unlocked " + unlockedSlots + " new augmentation slot" + + (unlockedSlots > 1 ? "s" : "") + "!"); + } + + player.sendMessage("§a▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + + // Sound effect + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.2f); + } + + /** + * Calculates how many augmentation slots should be available at a given level + */ + public static int calculateSlotsForLevel(int level) { + // Start with 2 slots, gain 1 slot every 5 levels + int baseSlots = 2; + int bonusSlots = Math.max(0, (level - 1) / 5); + return Math.min(baseSlots + bonusSlots, 8); // Cap at 8 slots max + } + + /** + * Updates an item in the player's inventory + */ + private static void updateItemInInventory(Player player, ItemStack oldItem, ItemStack newItem) { + // Find the item in inventory and replace it + for (int slot = 0; slot < player.getInventory().getSize(); slot++) { + ItemStack slotItem = player.getInventory().getItem(slot); + if (slotItem != null && slotItem.equals(oldItem)) { + player.getInventory().setItem(slot, newItem); + break; + } + } + + // Also check main hand + if (player.getInventory().getItemInMainHand().equals(oldItem)) { + player.getInventory().setItemInMainHand(newItem); + } + + // And off hand + if (player.getInventory().getItemInOffHand().equals(oldItem)) { + player.getInventory().setItemInOffHand(newItem); + } + } + + /** + * Gets the XP progress towards the next level as a percentage + */ + public static double getXPProgressPercentage(long currentXP, int currentLevel) { + long xpForCurrentLevel = currentLevel > 1 ? getXPRequiredForLevel(currentLevel) : 0; + long xpForNextLevel = getXPRequiredForLevel(currentLevel + 1); + + long xpInCurrentLevel = currentXP - xpForCurrentLevel; + long xpNeededForNext = xpForNextLevel - xpForCurrentLevel; + + return (double) xpInCurrentLevel / xpNeededForNext; + } +} \ No newline at end of file