modified: plugin.yml

modified:   src/main/java/com/simolzimol/levelcraft/LevelCraft.java
	modified:   src/main/java/com/simolzimol/levelcraft/command/TestSmithChestCommand.java
	modified:   src/main/java/com/simolzimol/levelcraft/item/ItemUtil.java
	modified:   src/main/java/com/simolzimol/levelcraft/item/Rarity.java
	modified:   src/main/java/com/simolzimol/levelcraft/listener/CustomStatsListener.java
	modified:   src/main/java/com/simolzimol/levelcraft/system/PlayerStatsManager.java
This commit is contained in:
SimolZimol
2025-10-09 18:56:48 +02:00
parent 9cbd605b3d
commit 43b4ab60e4
7 changed files with 81 additions and 65 deletions

View File

@@ -3,7 +3,17 @@ main: com.simolzimol.levelcraft.LevelCraft
version: 1.0
api-version: 1.21
author: SimolZimol
description: TEXT
description: RPG-Style Item Enhancement Plugin with custom stats, rarities and bonuses
commands:
testsmithchest:
description: Gibt dir eine Kiste
description: Spawns a weaponsmith chest for testing
usage: /testsmithchest
permissions:
levelcraft.admin:
description: Administrative commands for LevelCraft
default: op
levelcraft.use:
description: Basic usage of LevelCraft features
default: true

View File

@@ -3,9 +3,16 @@ package com.simolzimol.levelcraft;
import org.bukkit.plugin.java.JavaPlugin;
public class LevelCraft extends JavaPlugin {
private static LevelCraft instance;
public static LevelCraft getInstance() {
return instance;
}
@Override
public void onEnable() {
getLogger().info("LevelCraft aktiviert!");
instance = this;
getLogger().info("LevelCraft enabled!");
com.simolzimol.levelcraft.item.ItemUtil.init(this);
getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.CraftListener(), this);
getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.LootListener(this), this);
@@ -15,13 +22,13 @@ public class LevelCraft extends JavaPlugin {
getServer().getPluginManager().registerEvents(new com.simolzimol.levelcraft.listener.CustomStatsListener(), this);
getCommand("testsmithchest").setExecutor(new com.simolzimol.levelcraft.command.TestSmithChestCommand());
// ActionBar-Task für alle Spieler starten
// Start ActionBar task for all players
com.simolzimol.levelcraft.system.PlayerStatsManager.startActionBarTask(this);
com.simolzimol.levelcraft.system.PlayerStatsManager.startRegenTask(this); // <--- hinzufügen
com.simolzimol.levelcraft.system.PlayerStatsManager.startRegenTask(this);
}
@Override
public void onDisable() {
getLogger().info("LevelCraft deaktiviert!");
getLogger().info("LevelCraft disabled!");
}
}

View File

@@ -15,7 +15,7 @@ public class TestSmithChestCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("Nur Spieler können diesen Befehl nutzen.");
sender.sendMessage("Only players can use this command.");
return true;
}
@@ -23,15 +23,15 @@ public class TestSmithChestCommand implements CommandExecutor {
World world = loc.getWorld();
Block block = world.getBlockAt(loc);
// Setze Block zu Kiste
// Set block to chest
block.setType(Material.CHEST);
Chest chest = (Chest) block.getState();
// Setze den Schmied-Lootpool
// Set weaponsmith loot table
chest.setLootTable(LootTables.VILLAGE_WEAPONSMITH.getLootTable());
chest.update();
player.sendMessage("Kiste mit Schmied-Loot an deiner Position gespawnt!");
player.sendMessage("Weaponsmith chest spawned at your location!");
return true;
}
}

View File

@@ -1,16 +1,12 @@
package com.simolzimol.levelcraft.item;
import org.bukkit.NamespacedKey;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.inventory.EquipmentSlot;
import java.util.List;
import java.util.UUID;
public class ItemUtil {
@@ -25,10 +21,10 @@ public class ItemUtil {
public static void setRarity(ItemStack item, Rarity rarity) {
ItemMeta meta = item.getItemMeta();
meta.getPersistentDataContainer().set(rarityKey, PersistentDataType.INTEGER, rarity.getValue());
// Lore aktualisieren (alte Zeile ersetzen)
java.util.List<String> lore = meta.hasLore() ? meta.getLore() : new java.util.ArrayList<>();
lore.add(rarity.getDisplayName() + "§7 Seltenheit");
meta.setLore(lore);
// Update lore (replace old line)
java.util.List<net.kyori.adventure.text.Component> lore = meta.hasLore() ? meta.lore() : new java.util.ArrayList<>();
lore.add(net.kyori.adventure.text.Component.text(rarity.getDisplayName() + "§7 Rarity"));
meta.lore(lore);
item.setItemMeta(meta);
}
@@ -61,7 +57,7 @@ public class ItemUtil {
public static void addRandomBonuses(ItemStack item, Rarity rarity, java.util.Random random) {
ItemMeta meta = item.getItemMeta();
java.util.List<String> lore = meta.hasLore() ? meta.getLore() : new java.util.ArrayList<>();
java.util.List<net.kyori.adventure.text.Component> lore = meta.hasLore() ? meta.lore() : new java.util.ArrayList<>();
int stats = switch (rarity) {
case COMMON -> 1;
@@ -95,8 +91,10 @@ public class ItemUtil {
case "DURABILITY" -> {
double percent = tier.getMinDurability() + (tier.getMaxDurability() - tier.getMinDurability()) * random.nextDouble();
int bonus = (int) Math.round(item.getType().getMaxDurability() * percent);
lore.add(tier.getDisplay() + ": +" + (int)(percent*100) + "% Haltbarkeit (" + bonus + " Punkte)");
item.setDurability((short) 0); // Item ist voll repariert
lore.add(net.kyori.adventure.text.Component.text(tier.getDisplay() + ": +" + (int)(percent*100) + "% Durability (" + bonus + " points)"));
if (meta instanceof org.bukkit.inventory.meta.Damageable damageable) {
damageable.setDamage(0); // Item is fully repaired
}
}
case "DAMAGE" -> {
double percent = tier.getMinDamage() + (tier.getMaxDamage() - tier.getMinDamage()) * random.nextDouble();
@@ -105,11 +103,11 @@ public class ItemUtil {
double newDamage = base + bonus;
meta.addAttributeModifier(
org.bukkit.attribute.Attribute.GENERIC_ATTACK_DAMAGE,
new AttributeModifier(UUID.nameUUIDFromBytes("levelcraft_base_damage".getBytes()), "levelcraft_base_damage", base, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.HAND));
new AttributeModifier(new NamespacedKey("levelcraft", "base_damage"), base, AttributeModifier.Operation.ADD_NUMBER));
meta.addAttributeModifier(
org.bukkit.attribute.Attribute.GENERIC_ATTACK_DAMAGE,
new AttributeModifier(UUID.randomUUID(), "levelcraft_bonus_damage", bonus, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.HAND));
lore.add(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Angriffsschaden (gesamt: " + String.format("%.2f", newDamage) + ")");
new AttributeModifier(new NamespacedKey("levelcraft", "bonus_damage"), bonus, AttributeModifier.Operation.ADD_NUMBER));
lore.add(net.kyori.adventure.text.Component.text(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Attack Damage (total: " + String.format("%.2f", newDamage) + ")"));
}
case "ARMOR" -> {
double base = getBaseArmor(item);
@@ -127,11 +125,11 @@ public class ItemUtil {
double bonus = base * percent * 10;
double newArmor = base + bonus;
meta.getPersistentDataContainer().set(new NamespacedKey("levelcraft", "custom_armor"), PersistentDataType.DOUBLE, newArmor);
lore.add(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Rüstung (Basis: " + String.format("%.2f", base) + ", gesamt: " + String.format("%.2f", newArmor) + " /100)");
lore.add(net.kyori.adventure.text.Component.text(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Armor (base: " + String.format("%.2f", base) + ", total: " + String.format("%.2f", newArmor) + " /100)"));
} else {
// CURSED: Nur Basiswert, keine Boni, keine Effekte, keine Skills
meta.getPersistentDataContainer().set(new NamespacedKey("levelcraft", "custom_armor"), PersistentDataType.DOUBLE, base);
lore.add("§8Verflucht: Nur Basis-Rüstung (" + String.format("%.2f", base) + " /100)");
lore.add(net.kyori.adventure.text.Component.text("§8Cursed: Base armor only (" + String.format("%.2f", base) + " /100)"));
}
}
case "MINING_SPEED" -> {
@@ -139,16 +137,16 @@ public class ItemUtil {
double bonus = 4.0 * percent * 10; // Bonus x10
meta.addAttributeModifier(
org.bukkit.attribute.Attribute.GENERIC_ATTACK_SPEED,
new AttributeModifier(UUID.randomUUID(), "levelcraft_bonus_mining_speed", bonus, AttributeModifier.Operation.ADD_NUMBER));
lore.add(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Abbausgeschwindigkeit");
new AttributeModifier(new NamespacedKey("levelcraft", "mining_speed"), bonus, AttributeModifier.Operation.ADD_NUMBER));
lore.add(net.kyori.adventure.text.Component.text(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Mining Speed"));
}
case "LUCK" -> {
double percent = tier.getMinLuck() + (tier.getMaxLuck() - tier.getMinLuck()) * random.nextDouble();
double bonus = 1.0 * percent * 10; // Bonus x10
meta.addAttributeModifier(
org.bukkit.attribute.Attribute.GENERIC_LUCK,
new AttributeModifier(UUID.randomUUID(), "levelcraft_bonus_luck", bonus, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.HAND));
lore.add(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Glück");
new AttributeModifier(new NamespacedKey("levelcraft", "bonus_luck"), bonus, AttributeModifier.Operation.ADD_NUMBER));
lore.add(net.kyori.adventure.text.Component.text(tier.getDisplay() + ": +" + String.format("%.2f", bonus) + " Luck"));
}
case "ATTACK_SPEED" -> {
double percent = tier.getMinAttackSpeed() + (tier.getMaxAttackSpeed() - tier.getMinAttackSpeed()) * random.nextDouble();
@@ -156,15 +154,15 @@ public class ItemUtil {
double malus = base * percent;
double newSpeed = base - malus;
if (newSpeed < 0.1) newSpeed = 0.1;
// Basiswert als Modifier setzen
// Set base value as modifier
meta.addAttributeModifier(
org.bukkit.attribute.Attribute.GENERIC_ATTACK_SPEED,
new AttributeModifier(UUID.nameUUIDFromBytes("levelcraft_base_attackspeed".getBytes()), "levelcraft_base_attackspeed", base, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.HAND));
// Bonus als Modifier setzen (negativ, weil niedriger besser)
new AttributeModifier(new NamespacedKey("levelcraft", "base_attack_speed"), base, AttributeModifier.Operation.ADD_NUMBER));
// Set bonus as modifier (negative because lower is better)
meta.addAttributeModifier(
org.bukkit.attribute.Attribute.GENERIC_ATTACK_SPEED,
new AttributeModifier(UUID.randomUUID(), "levelcraft_bonus_attackspeed", -malus, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.HAND));
lore.add(tier.getDisplay() + ": -" + String.format("%.2f", malus) + " Angriffsgeschwindigkeit (neu: " + String.format("%.2f", newSpeed) + ")");
new AttributeModifier(new NamespacedKey("levelcraft", "bonus_attack_speed"), -malus, AttributeModifier.Operation.ADD_NUMBER));
lore.add(net.kyori.adventure.text.Component.text(tier.getDisplay() + ": -" + String.format("%.2f", malus) + " Attack Speed (new: " + String.format("%.2f", newSpeed) + ")"));
}
}
}
@@ -175,17 +173,17 @@ public class ItemUtil {
java.util.Collections.shuffle(effectsList, random);
int effects = 1;
for (int i = 0; i < effects && i < effectsList.size(); i++) {
lore.add(effectsList.get(i));
lore.add(net.kyori.adventure.text.Component.text(effectsList.get(i)));
}
java.util.List<String> 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(skillsList.get(i));
lore.add(net.kyori.adventure.text.Component.text(skillsList.get(i)));
}
}
meta.setLore(lore);
meta.lore(lore);
item.setItemMeta(meta);
// --- Basiswert für Armor IMMER setzen, auch für CURSED ---
@@ -195,9 +193,10 @@ public class ItemUtil {
if (current == null) {
meta.getPersistentDataContainer().set(new NamespacedKey("levelcraft", "custom_armor"), PersistentDataType.DOUBLE, base);
// Basiswert auch in der Lore, falls noch nicht vorhanden
boolean found = lore.stream().anyMatch(s -> s.contains("Basis-Rüstung") || s.contains("Basis:"));
if (!found) lore.add("§7Basis-Rüstung: " + String.format("%.2f", base) + " /100");
meta.setLore(lore);
boolean found = lore.stream().anyMatch(s -> net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(s).contains("Base armor") ||
net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(s).contains("base:"));
if (!found) lore.add(net.kyori.adventure.text.Component.text("§7Base armor: " + String.format("%.2f", base) + " /100"));
meta.lore(lore);
item.setItemMeta(meta);
}
}
@@ -224,7 +223,7 @@ public class ItemUtil {
if (armor > 0) stats += "§b" + String.format("%.2f", armor) + "🛡 ";
if (speed > 0) stats += "§a" + String.format("%.2f", speed) + "";
if (luck > 0) stats += "§d" + String.format("%.2f", luck) + "";
player.sendActionBar(stats.trim());
player.sendActionBar(net.kyori.adventure.text.Component.text(stats.trim()));
}
// Hilfsmethoden für Item-Typen:

View File

@@ -1,15 +1,15 @@
package com.simolzimol.levelcraft.item;
public enum Rarity {
CURSED(-1, "§8Verflucht", 0.02), // Sehr selten, negativ
COMMON(0, "§7Gewöhnlich", 0.50), // Am häufigsten (50%)
UNCOMMON(1, "§aUngewöhnlich", 0.20), // 20%
RARE(2, "§9Selten", 0.12), // 12%
EPIC(3, "§5Episch", 0.08), // 8%
LEGENDARY(4, "§6Legendär", 0.05), // 5%
MYTHIC(5, "§dMythisch", 0.025), // 2.5%
ANCIENT(6, "§eUralte", 0.012), // 1.2%
DIVINE(7, "§bGöttlich", 0.003); // 0.3%
CURSED(-1, "§8Cursed", 0.02), // Very rare, negative
COMMON(0, "§7Common", 0.50), // Most frequent (50%)
UNCOMMON(1, "§aUncommon", 0.20), // 20%
RARE(2, "§9Rare", 0.12), // 12%
EPIC(3, "§5Epic", 0.08), // 8%
LEGENDARY(4, "§6Legendary", 0.05), // 5%
MYTHIC(5, "§dMythic", 0.025), // 2.5%
ANCIENT(6, "§eAncient", 0.012), // 1.2%
DIVINE(7, "§bDivine", 0.003); // 0.3%
private final int value;
private final String displayName;
@@ -40,7 +40,7 @@ public enum Rarity {
return COMMON;
}
// Zufällige Rarity nach Gewichtung (ohne CURSED)
// Random rarity by weight (without CURSED)
public static Rarity getRandomRarity(java.util.Random random, boolean allowCursed, int maxRarity) {
double roll = random.nextDouble();
double cumulative = 0.0;

View File

@@ -23,14 +23,14 @@ public class CustomStatsListener implements Listener {
hp -= damage;
PlayerStatsManager.setHP(player, hp);
// Nach 1 Sekunde wieder die Stats anzeigen
// Show stats again after 1 second
org.bukkit.Bukkit.getScheduler().runTaskLater(
org.bukkit.Bukkit.getPluginManager().getPlugin("LevelCraft"),
com.simolzimol.levelcraft.LevelCraft.getInstance(),
() -> PlayerStatsManager.showStats(player),
20L // 1 Sekunde
20L // 1 second
);
if (hp <= 0) player.setHealth(0); // Spieler töten
if (hp <= 0) player.setHealth(0); // Kill player
}
public static void updatePlayerArmor(Player player) {
@@ -43,16 +43,16 @@ public class CustomStatsListener implements Listener {
if (armor != null) totalArmor += armor;
}
PlayerStatsManager.setArmor(player, totalArmor);
// ActionBar sofort updaten
// Update ActionBar immediately
PlayerStatsManager.showStats(player);
}
@EventHandler
public void onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
// Verzögert ausführen, damit das Inventar schon aktualisiert ist
// Execute delayed so inventory is already updated
org.bukkit.Bukkit.getScheduler().runTaskLater(
org.bukkit.Bukkit.getPluginManager().getPlugin("LevelCraft"),
com.simolzimol.levelcraft.LevelCraft.getInstance(),
() -> updatePlayerArmor(player),
1L
);
@@ -66,7 +66,7 @@ public class CustomStatsListener implements Listener {
@EventHandler
public void onRespawn(org.bukkit.event.player.PlayerRespawnEvent event) {
org.bukkit.Bukkit.getScheduler().runTaskLater(
org.bukkit.Bukkit.getPluginManager().getPlugin("LevelCraft"),
com.simolzimol.levelcraft.LevelCraft.getInstance(),
() -> updatePlayerArmor(event.getPlayer()),
1L
);

View File

@@ -39,10 +39,10 @@ public class PlayerStatsManager {
double hpVal = getHP(p);
double armorVal = getArmor(p);
double manaVal = getMana(p);
p.sendActionBar("§cHP: " + Math.round(hpVal*10)/10.0 + " §bRüstung: " + Math.round(armorVal*10)/10.0 + " §9Mana: " + Math.round(manaVal*10)/10.0);
p.sendActionBar(net.kyori.adventure.text.Component.text("§cHP: " + Math.round(hpVal*10)/10.0 + " §bArmor: " + Math.round(armorVal*10)/10.0 + " §9Mana: " + Math.round(manaVal*10)/10.0));
}
}
}.runTaskTimer(plugin, 0L, 20L); // alle 20 Ticks = 1 Sekunde
}.runTaskTimer(plugin, 0L, 20L); // every 20 ticks = 1 second
}
public static void startRegenTask(org.bukkit.plugin.Plugin plugin) {
@@ -52,17 +52,17 @@ public class PlayerStatsManager {
for (Player p : Bukkit.getOnlinePlayers()) {
double hp = getHP(p);
if (hp < 20.0) {
setHP(p, Math.min(20.0, hp + 0.2)); // 0.2 HP pro Sekunde
setHP(p, Math.min(20.0, hp + 0.2)); // 0.2 HP per second
}
}
}
}.runTaskTimer(plugin, 0L, 20L); // alle 20 Ticks = 1 Sekunde
}.runTaskTimer(plugin, 0L, 20L); // every 20 ticks = 1 second
}
public static void showStats(Player p) {
double hpVal = getHP(p);
double armorVal = getArmor(p);
double manaVal = getMana(p);
p.sendActionBar("§cHP: " + Math.round(hpVal*10)/10.0 + " §bRüstung: " + Math.round(armorVal*10)/10.0 + " §9Mana: " + Math.round(manaVal*10)/10.0);
p.sendActionBar(net.kyori.adventure.text.Component.text("§cHP: " + Math.round(hpVal*10)/10.0 + " §bArmor: " + Math.round(armorVal*10)/10.0 + " §9Mana: " + Math.round(manaVal*10)/10.0));
}
}