Update from Git Manager GUI
This commit is contained in:
@@ -34,6 +34,9 @@ import de.viper.survivalplus.commands.TradeCommand;
|
|||||||
import de.viper.survivalplus.commands.ReportCommand;
|
import de.viper.survivalplus.commands.ReportCommand;
|
||||||
import de.viper.survivalplus.Manager.ShopManager;
|
import de.viper.survivalplus.Manager.ShopManager;
|
||||||
import de.viper.survivalplus.commands.ShopCommand;
|
import de.viper.survivalplus.commands.ShopCommand;
|
||||||
|
import de.viper.survivalplus.commands.JobCommand;
|
||||||
|
import de.viper.survivalplus.jobs.JobManager;
|
||||||
|
import de.viper.survivalplus.jobs.JobListener;
|
||||||
import de.viper.survivalplus.commands.HealCommand;
|
import de.viper.survivalplus.commands.HealCommand;
|
||||||
import de.viper.survivalplus.Manager.TablistManager;
|
import de.viper.survivalplus.Manager.TablistManager;
|
||||||
import de.viper.survivalplus.Manager.CommandBlocker;
|
import de.viper.survivalplus.Manager.CommandBlocker;
|
||||||
@@ -158,6 +161,12 @@ public class SurvivalPlus extends JavaPlugin {
|
|||||||
private final String VANISH_META_KEY = "vanished";
|
private final String VANISH_META_KEY = "vanished";
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
|
// --- Shop & Economy ---
|
||||||
|
private ShopManager shopManager;
|
||||||
|
private JobManager jobManager;
|
||||||
|
private de.viper.survivalplus.economy.BalanceManager balanceManager;
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
public void reloadTablistConfig() {
|
public void reloadTablistConfig() {
|
||||||
if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml");
|
if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml");
|
||||||
if (!tablistFile.exists()) {
|
if (!tablistFile.exists()) {
|
||||||
@@ -274,17 +283,32 @@ public class SurvivalPlus extends JavaPlugin {
|
|||||||
reloadBlockedCommandsConfig();
|
reloadBlockedCommandsConfig();
|
||||||
loadClaims();
|
loadClaims();
|
||||||
|
|
||||||
// --- NEU: Vault Economy Setup ---
|
// --- Economy Setup ---
|
||||||
|
// BalanceManager sofort initialisieren.
|
||||||
|
balanceManager = new de.viper.survivalplus.economy.BalanceManager(this);
|
||||||
|
|
||||||
if (Bukkit.getPluginManager().getPlugin("Vault") != null) {
|
if (Bukkit.getPluginManager().getPlugin("Vault") != null) {
|
||||||
|
// 1. Prüfen ob bereits ein externes Economy-Plugin registriert ist (z.B. EssentialsX)
|
||||||
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
|
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
|
||||||
if (rsp != null) {
|
if (rsp != null) {
|
||||||
|
// Externes Plugin gefunden – bevorzugen
|
||||||
economy = rsp.getProvider();
|
economy = rsp.getProvider();
|
||||||
getLogger().info("Vault Economy erfolgreich verbunden.");
|
getLogger().info("Externes Economy-Plugin verbunden: " + economy.getName());
|
||||||
} else {
|
} else {
|
||||||
getLogger().warning("Vault Plugin gefunden, aber kein Economy Provider!");
|
// SOFORT eigenen Provider registrieren, damit andere Plugins ihn beim Start finden
|
||||||
|
de.viper.survivalplus.economy.SurvivalPlusEconomy ownEconomy =
|
||||||
|
new de.viper.survivalplus.economy.SurvivalPlusEconomy(this, balanceManager);
|
||||||
|
getServer().getServicesManager().register(
|
||||||
|
Economy.class, ownEconomy, this,
|
||||||
|
org.bukkit.plugin.ServicePriority.Normal
|
||||||
|
);
|
||||||
|
economy = ownEconomy;
|
||||||
|
getLogger().info("SurvivalPlusEconomy als Vault-Provider registriert.");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
getLogger().warning("Vault nicht gefunden! Economy-Funktionen werden deaktiviert.");
|
||||||
}
|
}
|
||||||
// -------------------------------------------------
|
// ---------------------
|
||||||
|
|
||||||
PluginManager pluginManager = getServer().getPluginManager();
|
PluginManager pluginManager = getServer().getPluginManager();
|
||||||
try {
|
try {
|
||||||
@@ -358,7 +382,14 @@ public class SurvivalPlus extends JavaPlugin {
|
|||||||
getCommand("workbench").setExecutor(new WorkbenchCommand());
|
getCommand("workbench").setExecutor(new WorkbenchCommand());
|
||||||
getCommand("anvil").setExecutor(new AnvilCommand());
|
getCommand("anvil").setExecutor(new AnvilCommand());
|
||||||
getCommand("nick").setExecutor(new NickCommand(this));
|
getCommand("nick").setExecutor(new NickCommand(this));
|
||||||
|
// ShopManager zentral initialisieren – wird von ShopCommand und ShopGui gemeinsam genutzt
|
||||||
|
shopManager = new ShopManager(this);
|
||||||
getCommand("shop").setExecutor(new ShopCommand(this));
|
getCommand("shop").setExecutor(new ShopCommand(this));
|
||||||
|
getCommand("money").setExecutor(new MoneyCommand(this));
|
||||||
|
// --- Jobs ---
|
||||||
|
jobManager = new JobManager(this);
|
||||||
|
getCommand("job").setExecutor(new JobCommand(this, jobManager));
|
||||||
|
pluginManager.registerEvents(new JobListener(this, jobManager), this);
|
||||||
getCommand("stats").setExecutor(new StatsCommand(this, statsManager));
|
getCommand("stats").setExecutor(new StatsCommand(this, statsManager));
|
||||||
getCommand("spawn").setExecutor(new SpawnCommand(this));
|
getCommand("spawn").setExecutor(new SpawnCommand(this));
|
||||||
getCommand("setwarp").setExecutor(new SetWarpCommand(this, warpManager));
|
getCommand("setwarp").setExecutor(new SetWarpCommand(this, warpManager));
|
||||||
@@ -1331,7 +1362,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
|
|||||||
"tpaccept", "tpdeny", "block", "unblock", "blocklist", "kit", "nick", "ride",
|
"tpaccept", "tpdeny", "block", "unblock", "blocklist", "kit", "nick", "ride",
|
||||||
"vanish", "freeze", "lootchests", "tploot", "day", "night", "trade", "tradeaccept",
|
"vanish", "freeze", "lootchests", "tploot", "day", "night", "trade", "tradeaccept",
|
||||||
"report", "showreport", "clearreport", "shop", "spawn", "setwarp", "delwarp",
|
"report", "showreport", "clearreport", "shop", "spawn", "setwarp", "delwarp",
|
||||||
"warps", "startchallenge", "heal", "claim", "head"
|
"warps", "startchallenge", "heal", "claim", "head", "money", "job", "jobs"
|
||||||
};
|
};
|
||||||
for (String name : commands) {
|
for (String name : commands) {
|
||||||
if (getCommand(name) != null) {
|
if (getCommand(name) != null) {
|
||||||
@@ -1391,6 +1422,21 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
|
|||||||
return economy;
|
return economy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShopManager
|
||||||
|
public ShopManager getShopManager() {
|
||||||
|
return shopManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceManager (für direkte Admin-Zugriffe, z.B. /money set)
|
||||||
|
public de.viper.survivalplus.economy.BalanceManager getBalanceManager() {
|
||||||
|
return balanceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JobManager
|
||||||
|
public JobManager getJobManager() {
|
||||||
|
return jobManager;
|
||||||
|
}
|
||||||
|
|
||||||
// ProtocolLib Bridge (für Silent Vanish)
|
// ProtocolLib Bridge (für Silent Vanish)
|
||||||
public ProtocolManager getProtocolManager() {
|
public ProtocolManager getProtocolManager() {
|
||||||
return ProtocolLibrary.getProtocolManager();
|
return ProtocolLibrary.getProtocolManager();
|
||||||
|
|||||||
189
src/main/java/de/viper/survivalplus/commands/JobCommand.java
Normal file
189
src/main/java/de/viper/survivalplus/commands/JobCommand.java
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
package de.viper.survivalplus.commands;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import de.viper.survivalplus.jobs.JobGui;
|
||||||
|
import de.viper.survivalplus.jobs.JobManager;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JobCommand implements CommandExecutor {
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
private final JobManager jobManager;
|
||||||
|
|
||||||
|
public JobCommand(SurvivalPlus plugin, JobManager jobManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.jobManager = jobManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Dieser Befehl ist nur für Spieler.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Player player = (Player) sender;
|
||||||
|
|
||||||
|
// Kein Argument oder "gui" / "list" → GUI öffnen
|
||||||
|
if (args.length == 0
|
||||||
|
|| args[0].equalsIgnoreCase("gui")
|
||||||
|
|| args[0].equalsIgnoreCase("list")) {
|
||||||
|
new JobGui(plugin, player, jobManager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[0].toLowerCase()) {
|
||||||
|
|
||||||
|
// /job info <job>
|
||||||
|
case "info": {
|
||||||
|
if (args.length < 2) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Benutzung: /job info <job>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cmdInfo(player, args[1].toLowerCase());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /job join <job>
|
||||||
|
case "join": {
|
||||||
|
if (args.length < 2) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Benutzung: /job join <job>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cmdJoin(player, args[1].toLowerCase());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /job leave <job>
|
||||||
|
case "leave": {
|
||||||
|
if (args.length < 2) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Benutzung: /job leave <job>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cmdLeave(player, args[1].toLowerCase());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /job myjobs
|
||||||
|
case "myjobs": {
|
||||||
|
cmdMyJobs(player);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
sendHelp(player);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Subcommands (Textfallback, GUI ist der bevorzugte Weg)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void cmdInfo(Player player, String jobId) {
|
||||||
|
if (!jobManager.jobExists(jobId)) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Job '" + jobId + "' nicht gefunden. /job list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean hasJob = jobManager.hasJob(player.getUniqueId(), jobId);
|
||||||
|
int level = jobManager.getLevel(player.getUniqueId(), jobId);
|
||||||
|
int xp = jobManager.getXp(player.getUniqueId(), jobId);
|
||||||
|
int xpReq = jobManager.getXpRequired(jobId, level);
|
||||||
|
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " " + jobManager.getJobDisplayName(jobId));
|
||||||
|
player.sendMessage(ChatColor.GRAY + " " + jobManager.getJobDescription(jobId));
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
if (hasJob) {
|
||||||
|
player.sendMessage(ChatColor.WHITE + " Level: " + ChatColor.AQUA + level
|
||||||
|
+ ChatColor.GRAY + " / " + jobManager.getMaxLevel());
|
||||||
|
if (level < jobManager.getMaxLevel()) {
|
||||||
|
player.sendMessage(ChatColor.WHITE + " XP: " + ChatColor.AQUA + xp
|
||||||
|
+ ChatColor.GRAY + " / " + xpReq);
|
||||||
|
player.sendMessage(" " + xpBar(xp, xpReq));
|
||||||
|
} else {
|
||||||
|
player.sendMessage(ChatColor.GOLD + " ⭐ Maximales Level erreicht!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.sendMessage(ChatColor.GRAY + " Nicht aktiv.");
|
||||||
|
player.sendMessage(ChatColor.GREEN + " /job join " + jobId);
|
||||||
|
}
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cmdJoin(Player player, String jobId) {
|
||||||
|
if (!jobManager.jobExists(jobId)) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Job '" + jobId + "' nicht gefunden. /job list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jobManager.hasJob(player.getUniqueId(), jobId)) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Du hast diesen Job bereits.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jobManager.getPlayerJobs(player.getUniqueId()).size() >= jobManager.getMaxJobs()) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Du kannst maximal " + jobManager.getMaxJobs()
|
||||||
|
+ " Jobs gleichzeitig haben. Verlasse zuerst einen Job.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jobManager.joinJob(player.getUniqueId(), jobId);
|
||||||
|
player.sendMessage(ChatColor.GREEN + "Du hast den Job "
|
||||||
|
+ ChatColor.YELLOW + jobManager.getJobDisplayName(jobId)
|
||||||
|
+ ChatColor.GREEN + " angenommen!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cmdLeave(Player player, String jobId) {
|
||||||
|
if (!jobManager.hasJob(player.getUniqueId(), jobId)) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Du hast den Job '" + jobId + "' nicht.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jobManager.leaveJob(player.getUniqueId(), jobId);
|
||||||
|
player.sendMessage(ChatColor.YELLOW + "Du hast den Job "
|
||||||
|
+ ChatColor.WHITE + jobManager.getJobDisplayName(jobId)
|
||||||
|
+ ChatColor.YELLOW + " verlassen. Level bleibt gespeichert.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cmdMyJobs(Player player) {
|
||||||
|
List<String> jobs = jobManager.getPlayerJobs(player.getUniqueId());
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " Deine Jobs ("
|
||||||
|
+ jobs.size() + "/" + jobManager.getMaxJobs() + "):");
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
if (jobs.isEmpty()) {
|
||||||
|
player.sendMessage(ChatColor.GRAY + " Noch kein Job aktiv. /job");
|
||||||
|
} else {
|
||||||
|
for (String jobId : jobs) {
|
||||||
|
int level = jobManager.getLevel(player.getUniqueId(), jobId);
|
||||||
|
int xp = jobManager.getXp(player.getUniqueId(), jobId);
|
||||||
|
int xpReq = jobManager.getXpRequired(jobId, level);
|
||||||
|
player.sendMessage(ChatColor.WHITE + " " + jobManager.getJobDisplayName(jobId)
|
||||||
|
+ ChatColor.GRAY + " Lv." + ChatColor.AQUA + level
|
||||||
|
+ ChatColor.GRAY + " XP: " + xp + "/" + xpReq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendHelp(Player player) {
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " /job " + ChatColor.GRAY + "GUI öffnen");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " /job info <job> " + ChatColor.GRAY + "Job-Details & Level");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " /job join <job> " + ChatColor.GRAY + "Job annehmen");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " /job leave <job> " + ChatColor.GRAY + "Job verlassen");
|
||||||
|
player.sendMessage(ChatColor.YELLOW + " /job myjobs " + ChatColor.GRAY + "Deine aktiven Jobs");
|
||||||
|
player.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String xpBar(int current, int max) {
|
||||||
|
int len = 20;
|
||||||
|
int filled = max == 0 ? 0 : Math.min((int) ((double) current / max * len), len);
|
||||||
|
return ChatColor.GREEN + "█".repeat(filled)
|
||||||
|
+ ChatColor.DARK_GRAY + "█".repeat(len - filled)
|
||||||
|
+ ChatColor.GRAY + " " + current + "/" + max;
|
||||||
|
}
|
||||||
|
}
|
||||||
294
src/main/java/de/viper/survivalplus/commands/MoneyCommand.java
Normal file
294
src/main/java/de/viper/survivalplus/commands/MoneyCommand.java
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
package de.viper.survivalplus.commands;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import de.viper.survivalplus.economy.BalanceManager;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class MoneyCommand implements CommandExecutor {
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
|
||||||
|
public MoneyCommand(SurvivalPlus plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
Economy economy = plugin.getEconomy();
|
||||||
|
if (economy == null) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Economy-System ist nicht verfügbar.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /money oder /money <Spieler> → Guthaben anzeigen
|
||||||
|
if (args.length == 0) {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Als Konsole bitte /money <Spieler> benutzen.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Player player = (Player) sender;
|
||||||
|
double bal = economy.getBalance(player);
|
||||||
|
player.sendMessage(ChatColor.GOLD + "Dein Guthaben: " + ChatColor.GREEN
|
||||||
|
+ String.format("%.2f", bal) + " " + economy.currencyNamePlural());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sub = args[0].toLowerCase();
|
||||||
|
|
||||||
|
// /money <Spieler> → Guthaben eines anderen Spielers anzeigen (nur OP)
|
||||||
|
if (args.length == 1 && !isSubCommand(sub)) {
|
||||||
|
if (!sender.hasPermission("survivalplus.money.admin")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du hast keine Berechtigung, das Guthaben anderer Spieler zu sehen.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
OfflinePlayer target = getOfflinePlayer(args[0]);
|
||||||
|
if (target == null) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Spieler '" + args[0] + "' nicht gefunden.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double bal = economy.getBalance(target);
|
||||||
|
sender.sendMessage(ChatColor.GOLD + "Guthaben von " + ChatColor.YELLOW + target.getName()
|
||||||
|
+ ChatColor.GOLD + ": " + ChatColor.GREEN
|
||||||
|
+ String.format("%.2f", bal) + " " + economy.currencyNamePlural());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sub) {
|
||||||
|
|
||||||
|
// /money pay <Spieler> <Betrag>
|
||||||
|
case "pay": {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Dieser Befehl ist nur für Spieler.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!sender.hasPermission("survivalplus.money.pay")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du hast keine Berechtigung für diesen Befehl.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (args.length < 3) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Benutzung: /money pay <Spieler> <Betrag>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Player payer = (Player) sender;
|
||||||
|
OfflinePlayer target = getOfflinePlayer(args[1]);
|
||||||
|
if (target == null || !target.isOnline()) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Spieler '" + args[1] + "' ist nicht online.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (target.getUniqueId().equals(payer.getUniqueId())) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du kannst dir nicht selbst Geld überweisen.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double amount = parseAmount(sender, args[2]);
|
||||||
|
if (amount < 0) return true;
|
||||||
|
if (amount == 0) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Der Betrag muss größer als 0 sein.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EconomyResponse withdraw = economy.withdrawPlayer(payer, amount);
|
||||||
|
if (!withdraw.transactionSuccess()) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Nicht genug Guthaben! Du hast "
|
||||||
|
+ String.format("%.2f", economy.getBalance(payer)) + " "
|
||||||
|
+ economy.currencyNamePlural() + ".");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
economy.depositPlayer(target, amount);
|
||||||
|
|
||||||
|
payer.sendMessage(ChatColor.GREEN + "Du hast " + ChatColor.YELLOW
|
||||||
|
+ String.format("%.2f", amount) + " " + economy.currencyNamePlural()
|
||||||
|
+ ChatColor.GREEN + " an " + ChatColor.YELLOW + target.getName()
|
||||||
|
+ ChatColor.GREEN + " überwiesen.");
|
||||||
|
if (target.getPlayer() != null) {
|
||||||
|
target.getPlayer().sendMessage(ChatColor.GREEN + ChatColor.BOLD.toString() + "+"
|
||||||
|
+ String.format("%.2f", amount) + " " + economy.currencyNamePlural()
|
||||||
|
+ ChatColor.RESET + ChatColor.GREEN + " von " + ChatColor.YELLOW + payer.getName());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /money set <Spieler> <Betrag>
|
||||||
|
case "set": {
|
||||||
|
if (!sender.hasPermission("survivalplus.money.admin")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du hast keine Berechtigung für diesen Befehl.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (args.length < 3) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Benutzung: /money set <Spieler> <Betrag>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
OfflinePlayer target = getOfflinePlayer(args[1]);
|
||||||
|
if (target == null) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Spieler '" + args[1] + "' nicht gefunden.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double amount = parseAmount(sender, args[2]);
|
||||||
|
if (amount < 0) return true;
|
||||||
|
|
||||||
|
plugin.getBalanceManager().setBalance(target.getUniqueId(), amount);
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "Guthaben von " + ChatColor.YELLOW + target.getName()
|
||||||
|
+ ChatColor.GREEN + " wurde auf " + ChatColor.YELLOW
|
||||||
|
+ String.format("%.2f", amount) + " " + economy.currencyNamePlural()
|
||||||
|
+ ChatColor.GREEN + " gesetzt.");
|
||||||
|
notifyTarget(target, ChatColor.YELLOW + "Dein Guthaben wurde auf "
|
||||||
|
+ String.format("%.2f", amount) + " " + economy.currencyNamePlural() + " gesetzt.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /money add <Spieler> <Betrag>
|
||||||
|
case "add": {
|
||||||
|
if (!sender.hasPermission("survivalplus.money.admin")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du hast keine Berechtigung für diesen Befehl.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (args.length < 3) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Benutzung: /money add <Spieler> <Betrag>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
OfflinePlayer target = getOfflinePlayer(args[1]);
|
||||||
|
if (target == null) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Spieler '" + args[1] + "' nicht gefunden.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double amount = parseAmount(sender, args[2]);
|
||||||
|
if (amount < 0) return true;
|
||||||
|
|
||||||
|
economy.depositPlayer(target, amount);
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "Du hast " + ChatColor.YELLOW
|
||||||
|
+ String.format("%.2f", amount) + " " + economy.currencyNamePlural()
|
||||||
|
+ ChatColor.GREEN + " zu " + ChatColor.YELLOW + target.getName()
|
||||||
|
+ ChatColor.GREEN + " hinzugefügt. Neues Guthaben: " + ChatColor.YELLOW
|
||||||
|
+ String.format("%.2f", economy.getBalance(target)));
|
||||||
|
notifyTarget(target, ChatColor.GREEN + "+" + String.format("%.2f", amount)
|
||||||
|
+ " " + economy.currencyNamePlural() + " von einem Admin gutgeschrieben.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /money remove <Spieler> <Betrag>
|
||||||
|
case "remove": {
|
||||||
|
if (!sender.hasPermission("survivalplus.money.admin")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du hast keine Berechtigung für diesen Befehl.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (args.length < 3) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Benutzung: /money remove <Spieler> <Betrag>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
OfflinePlayer target = getOfflinePlayer(args[1]);
|
||||||
|
if (target == null) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Spieler '" + args[1] + "' nicht gefunden.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double amount = parseAmount(sender, args[2]);
|
||||||
|
if (amount < 0) return true;
|
||||||
|
|
||||||
|
EconomyResponse response = economy.withdrawPlayer(target, amount);
|
||||||
|
if (!response.transactionSuccess()) {
|
||||||
|
sender.sendMessage(ChatColor.RED + target.getName() + " hat nicht genug Guthaben. "
|
||||||
|
+ "Aktuell: " + String.format("%.2f", economy.getBalance(target)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "Du hast " + ChatColor.YELLOW
|
||||||
|
+ String.format("%.2f", amount) + " " + economy.currencyNamePlural()
|
||||||
|
+ ChatColor.GREEN + " von " + ChatColor.YELLOW + target.getName()
|
||||||
|
+ ChatColor.GREEN + " abgezogen. Neues Guthaben: " + ChatColor.YELLOW
|
||||||
|
+ String.format("%.2f", economy.getBalance(target)));
|
||||||
|
notifyTarget(target, ChatColor.RED + "-" + String.format("%.2f", amount)
|
||||||
|
+ " " + economy.currencyNamePlural() + " von einem Admin abgezogen.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /money top → Top 10 reichste Spieler
|
||||||
|
case "top": {
|
||||||
|
if (!sender.hasPermission("survivalplus.baltop")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Du hast keine Berechtigung für diesen Befehl.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BalanceManager bm = plugin.getBalanceManager();
|
||||||
|
// Alle Einträge aus dem BalanceManager holen und sortieren
|
||||||
|
Map<UUID, Double> allBalances = bm.getAllBalances();
|
||||||
|
List<Map.Entry<UUID, Double>> sorted = new ArrayList<>(allBalances.entrySet());
|
||||||
|
sorted.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
|
||||||
|
|
||||||
|
sender.sendMessage(ChatColor.GOLD + "--- " + ChatColor.YELLOW + "Top 10 Reichste Spieler"
|
||||||
|
+ ChatColor.GOLD + " ---");
|
||||||
|
int shown = 0;
|
||||||
|
for (Map.Entry<UUID, Double> entry : sorted) {
|
||||||
|
if (shown >= 10) break;
|
||||||
|
OfflinePlayer p = Bukkit.getOfflinePlayer(entry.getKey());
|
||||||
|
String name = (p.getName() != null) ? p.getName() : entry.getKey().toString();
|
||||||
|
shown++;
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "#" + shown + " " + ChatColor.WHITE + name
|
||||||
|
+ ChatColor.GRAY + " - " + ChatColor.GREEN
|
||||||
|
+ String.format("%.2f", entry.getValue()) + " " + economy.currencyNamePlural());
|
||||||
|
}
|
||||||
|
if (shown == 0) {
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "Noch keine Kontodaten vorhanden.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
sender.sendMessage(ChatColor.RED + "Unbekannter Unterbefehl!");
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "/money [pay <Spieler> <Betrag>]");
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "/money [set|add|remove <Spieler> <Betrag>]");
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "/money top");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Hilfsmethoden ----
|
||||||
|
|
||||||
|
private boolean isSubCommand(String s) {
|
||||||
|
return s.equals("pay") || s.equals("set") || s.equals("add")
|
||||||
|
|| s.equals("remove") || s.equals("top");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parst einen Betrag. Gibt -1 zurück und sendet eine Fehlermeldung bei ungültigem Wert.
|
||||||
|
*/
|
||||||
|
private double parseAmount(CommandSender sender, String raw) {
|
||||||
|
try {
|
||||||
|
double val = Double.parseDouble(raw);
|
||||||
|
if (val < 0) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Der Betrag darf nicht negativ sein.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "'" + raw + "' ist keine gültige Zahl!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht zuerst online, dann offline.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private OfflinePlayer getOfflinePlayer(String name) {
|
||||||
|
Player online = Bukkit.getPlayer(name);
|
||||||
|
if (online != null) return online;
|
||||||
|
OfflinePlayer offline = Bukkit.getOfflinePlayer(name);
|
||||||
|
// hasPlayedBefore() verhindert, dass komplett unbekannte Namen akzeptiert werden
|
||||||
|
return offline.hasPlayedBefore() ? offline : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schickt einer OfflinePlayer-Benachrichtigung, falls sie online sind.
|
||||||
|
*/
|
||||||
|
private void notifyTarget(OfflinePlayer target, String message) {
|
||||||
|
if (target.isOnline() && target.getPlayer() != null) {
|
||||||
|
target.getPlayer().sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package de.viper.survivalplus.commands;
|
|||||||
import de.viper.survivalplus.Manager.ShopManager;
|
import de.viper.survivalplus.Manager.ShopManager;
|
||||||
import de.viper.survivalplus.SurvivalPlus;
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
import de.viper.survivalplus.gui.ShopGui;
|
import de.viper.survivalplus.gui.ShopGui;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@@ -12,32 +14,17 @@ import org.bukkit.command.CommandSender;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.PlayerInventory;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
|
||||||
import net.milkbowl.vault.economy.Economy;
|
|
||||||
import net.milkbowl.vault.economy.EconomyResponse;
|
|
||||||
|
|
||||||
public class ShopCommand implements CommandExecutor {
|
public class ShopCommand implements CommandExecutor {
|
||||||
|
|
||||||
private final ShopManager shopManager;
|
|
||||||
private final SurvivalPlus plugin;
|
private final SurvivalPlus plugin;
|
||||||
private Economy economy;
|
// ShopManager und Economy kommen zentral aus dem Plugin
|
||||||
|
private final ShopManager shopManager;
|
||||||
|
|
||||||
public ShopCommand(SurvivalPlus plugin) {
|
public ShopCommand(SurvivalPlus plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.shopManager = new ShopManager(plugin);
|
// ShopManager zentral aus dem Plugin holen, KEINE eigene Instanz erstellen
|
||||||
|
this.shopManager = plugin.getShopManager();
|
||||||
// Economy laden (für Admin-Verkauf an Server)
|
|
||||||
try {
|
|
||||||
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
|
|
||||||
RegisteredServiceProvider<Economy> rsp = Bukkit.getServicesManager().getRegistration(Economy.class);
|
|
||||||
if (rsp != null) {
|
|
||||||
this.economy = rsp.getProvider();
|
|
||||||
plugin.getLogger().info("Vault Economy für ShopCommand gefunden.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Ignorieren wenn Vault fehlt
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMessage(String key) {
|
private String getMessage(String key) {
|
||||||
@@ -53,17 +40,23 @@ public class ShopCommand implements CommandExecutor {
|
|||||||
Player player = (Player) sender;
|
Player player = (Player) sender;
|
||||||
|
|
||||||
// Shop-GUI öffnen
|
// Shop-GUI öffnen
|
||||||
if (args.length == 0 || (args.length > 0 && args[0].equalsIgnoreCase("gui"))) {
|
if (args.length == 0 || args[0].equalsIgnoreCase("gui")) {
|
||||||
ShopGui shopGui = new ShopGui(plugin, player, shopManager);
|
ShopGui shopGui = new ShopGui(plugin, player, shopManager);
|
||||||
Bukkit.getPluginManager().registerEvents(shopGui, plugin);
|
Bukkit.getPluginManager().registerEvents(shopGui, plugin);
|
||||||
player.openInventory(shopGui.getInventory());
|
player.openInventory(shopGui.getInventory());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SHOP ADD LOGIK ---
|
// --- SHOP ADD ---
|
||||||
if (args.length >= 4 && args[0].equalsIgnoreCase("add")) {
|
if (args[0].equalsIgnoreCase("add")) {
|
||||||
|
if (args.length < 4) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Falsche Benutzung!");
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "/shop add <Material> <Preis> <Bestand>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Economy-Check
|
// Economy zentral aus Plugin holen
|
||||||
|
Economy economy = plugin.getEconomy();
|
||||||
if (economy == null) {
|
if (economy == null) {
|
||||||
sender.sendMessage(ChatColor.RED + "Vault Economy ist nicht verfügbar!");
|
sender.sendMessage(ChatColor.RED + "Vault Economy ist nicht verfügbar!");
|
||||||
return true;
|
return true;
|
||||||
@@ -74,7 +67,6 @@ public class ShopCommand implements CommandExecutor {
|
|||||||
String amountStr = args[3];
|
String amountStr = args[3];
|
||||||
|
|
||||||
Material mat = Material.matchMaterial(materialName);
|
Material mat = Material.matchMaterial(materialName);
|
||||||
|
|
||||||
if (mat == null) {
|
if (mat == null) {
|
||||||
sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!");
|
sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!");
|
||||||
return true;
|
return true;
|
||||||
@@ -82,12 +74,11 @@ public class ShopCommand implements CommandExecutor {
|
|||||||
|
|
||||||
double price;
|
double price;
|
||||||
int amount;
|
int amount;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
price = Double.parseDouble(priceStr);
|
price = Double.parseDouble(priceStr);
|
||||||
amount = Integer.parseInt(amountStr);
|
amount = Integer.parseInt(amountStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
sender.sendMessage(ChatColor.RED + "Ungültige Preis oder Bestandszahl! Beispiel: /shop add Diamond 20 64");
|
sender.sendMessage(ChatColor.RED + "Ungültige Preis- oder Bestandszahl! Beispiel: /shop add Diamond 20 64");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,56 +87,50 @@ public class ShopCommand implements CommandExecutor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Prüfen: Hat der Spieler die Items?
|
// Hat der Spieler die Items?
|
||||||
int hasAmount = countItems(player.getInventory(), mat);
|
int hasAmount = countItems(player.getInventory(), mat);
|
||||||
|
|
||||||
if (hasAmount < amount) {
|
if (hasAmount < amount) {
|
||||||
sender.sendMessage(ChatColor.RED + "Du hast nicht genug Items! (Benötigt: " + amount + ", Vorhanden: " + hasAmount + ")");
|
sender.sendMessage(ChatColor.RED + "Du hast nicht genug Items! (Benötigt: " + amount
|
||||||
|
+ ", Vorhanden: " + hasAmount + ")");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Items aus Inventar entfernen
|
// Items entfernen
|
||||||
player.getInventory().removeItem(new ItemStack(mat, amount));
|
player.getInventory().removeItem(new ItemStack(mat, amount));
|
||||||
player.updateInventory(); // Wichtig für Client-Update
|
player.updateInventory();
|
||||||
|
|
||||||
// 3. Spieler Geld geben (Verkauf an Server)
|
// Geld auszahlen (Verkauf an Server)
|
||||||
double totalCost = price * amount;
|
double totalCost = price * amount;
|
||||||
EconomyResponse response = economy.depositPlayer(player, totalCost);
|
EconomyResponse response = economy.depositPlayer(player, totalCost);
|
||||||
|
|
||||||
if (response.transactionSuccess()) {
|
if (response.transactionSuccess()) {
|
||||||
sender.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + mat.name() + " für " + totalCost + " Coins an den Server verkauft!");
|
sender.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + mat.name()
|
||||||
|
+ " für " + String.format("%.2f", totalCost) + " Coins an den Server verkauft!");
|
||||||
|
|
||||||
// 4. Shop-Config aktualisieren (Lagerbestand erhöhen)
|
// Shop-Config aktualisieren
|
||||||
String itemKey = mat.name().toLowerCase();
|
String itemKey = mat.name().toLowerCase();
|
||||||
shopManager.addOrUpdateItem(itemKey, price, shopManager.getStock(itemKey) + amount);
|
shopManager.addOrUpdateItem(itemKey, price, shopManager.getStock(itemKey) + amount);
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung!");
|
// Rollback: Items zurückgeben
|
||||||
return true;
|
player.getInventory().addItem(new ItemStack(mat, amount));
|
||||||
|
player.updateInventory();
|
||||||
|
sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung: " + response.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback für falsche Argumente
|
|
||||||
if (args.length > 0 && args[0].equalsIgnoreCase("add")) {
|
|
||||||
sender.sendMessage(ChatColor.RED + "Falsche Benutzung!");
|
|
||||||
sender.sendMessage(ChatColor.GRAY + "Mit Item in Hand: /shop add <Preis> <Bestand>");
|
|
||||||
sender.sendMessage(ChatColor.GRAY + "Mit Namen: /shop add <Material> <Preis> <Bestand>");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.sendMessage(ChatColor.RED + "Unbekannter Befehl! Benutze /shop [add|gui]");
|
sender.sendMessage(ChatColor.RED + "Unbekannter Befehl! Benutze /shop [add|gui]");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zählt die Anzahl der Items eines bestimmten Materials im Inventar.
|
* Zählt alle Items eines Materials im Spieler-Inventar (nur Storage-Slots).
|
||||||
* Nutzt StorageContents, um auch Rüstung/Shulker-Slots zu prüfen.
|
|
||||||
*/
|
*/
|
||||||
private int countItems(PlayerInventory inv, Material mat) {
|
private int countItems(PlayerInventory inv, Material mat) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (ItemStack item : inv.getStorageContents()) {
|
for (ItemStack item : inv.getStorageContents()) {
|
||||||
if (item != null && item.getType().equals(mat)) {
|
if (item != null && item.getType() == mat) {
|
||||||
count += item.getAmount();
|
count += item.getAmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class SurvivalPlusTabCompleter implements TabCompleter {
|
|||||||
private static final List<String> CLAIM_SUBCOMMANDS = List.of("mark", "unclaim", "del", "delete", "trust", "untrust", "info", "kick", "ban", "unban");
|
private static final List<String> CLAIM_SUBCOMMANDS = List.of("mark", "unclaim", "del", "delete", "trust", "untrust", "info", "kick", "ban", "unban");
|
||||||
private static final List<String> SPLOCK_SUBCOMMANDS = List.of("lock", "unlock", "friendadd", "friendremove");
|
private static final List<String> SPLOCK_SUBCOMMANDS = List.of("lock", "unlock", "friendadd", "friendremove");
|
||||||
private static final List<String> SHOP_SUBCOMMANDS = List.of("gui", "add");
|
private static final List<String> SHOP_SUBCOMMANDS = List.of("gui", "add");
|
||||||
|
private static final List<String> MONEY_SUBCOMMANDS = List.of("pay", "set", "add", "remove", "top");
|
||||||
|
private static final List<String> JOB_SUBCOMMANDS = List.of("info", "join", "leave", "myjobs");
|
||||||
private static final List<String> NICK_SUBCOMMANDS = List.of("off", "remove", "reset");
|
private static final List<String> NICK_SUBCOMMANDS = List.of("off", "remove", "reset");
|
||||||
private static final List<String> GAMEMODE_SUBCOMMANDS = List.of("0", "1", "2", "3", "survival", "creative", "adventure", "spectator");
|
private static final List<String> GAMEMODE_SUBCOMMANDS = List.of("0", "1", "2", "3", "survival", "creative", "adventure", "spectator");
|
||||||
|
|
||||||
@@ -61,6 +63,14 @@ public class SurvivalPlusTabCompleter implements TabCompleter {
|
|||||||
return completeSplock(sender, args);
|
return completeSplock(sender, args);
|
||||||
case "shop":
|
case "shop":
|
||||||
return completeShop(sender, args);
|
return completeShop(sender, args);
|
||||||
|
case "money":
|
||||||
|
case "bal":
|
||||||
|
case "balance":
|
||||||
|
case "geld":
|
||||||
|
return completeMoney(sender, args);
|
||||||
|
case "job":
|
||||||
|
case "jobs":
|
||||||
|
return completeJob(sender, args);
|
||||||
case "nick":
|
case "nick":
|
||||||
return filterPrefix(NICK_SUBCOMMANDS, args[0]);
|
return filterPrefix(NICK_SUBCOMMANDS, args[0]);
|
||||||
case "gm":
|
case "gm":
|
||||||
@@ -159,6 +169,42 @@ public class SurvivalPlusTabCompleter implements TabCompleter {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> completeMoney(CommandSender sender, String[] args) {
|
||||||
|
if (args.length == 1) {
|
||||||
|
// Spieler sehen nur pay + top, Admins alle Subcommands
|
||||||
|
if (sender.hasPermission("survivalplus.money.admin")) {
|
||||||
|
return filterPrefix(MONEY_SUBCOMMANDS, args[0]);
|
||||||
|
}
|
||||||
|
return filterPrefix(List.of("pay", "top"), args[0]);
|
||||||
|
}
|
||||||
|
if (args.length == 2) {
|
||||||
|
String sub = args[0].toLowerCase(Locale.ROOT);
|
||||||
|
// Spielername-Vorschlag für alle Subcommands die einen Spieler erwarten
|
||||||
|
if (sub.equals("pay") || sub.equals("set") || sub.equals("add") || sub.equals("remove")) {
|
||||||
|
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// args.length == 3: Betrag – keine sinnvollen Vorschläge möglich
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeJob(CommandSender sender, String[] args) {
|
||||||
|
if (args.length == 1) {
|
||||||
|
// Arg 1: Subcommand-Vorschläge
|
||||||
|
return filterPrefix(JOB_SUBCOMMANDS, args[0]);
|
||||||
|
}
|
||||||
|
if (args.length == 2) {
|
||||||
|
String sub = args[0].toLowerCase(Locale.ROOT);
|
||||||
|
// Arg 2: Job-Namen aus der jobs.yml (über JobManager)
|
||||||
|
if (sub.equals("info") || sub.equals("join") || sub.equals("leave")) {
|
||||||
|
de.viper.survivalplus.jobs.JobManager jobManager = plugin.getJobManager();
|
||||||
|
if (jobManager == null) return Collections.emptyList();
|
||||||
|
return filterPrefix(new java.util.ArrayList<>(jobManager.getJobIds()), args[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> completeShop(CommandSender sender, String[] args) {
|
private List<String> completeShop(CommandSender sender, String[] args) {
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
return filterPrefix(SHOP_SUBCOMMANDS, args[0]);
|
return filterPrefix(SHOP_SUBCOMMANDS, args[0]);
|
||||||
|
|||||||
135
src/main/java/de/viper/survivalplus/economy/BalanceManager.java
Normal file
135
src/main/java/de/viper/survivalplus/economy/BalanceManager.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package de.viper.survivalplus.economy;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verwaltet alle Spieler-Kontostände und speichert sie in balances.yml.
|
||||||
|
*/
|
||||||
|
public class BalanceManager {
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
private final File balancesFile;
|
||||||
|
private FileConfiguration balancesConfig;
|
||||||
|
|
||||||
|
// In-Memory Cache für schnellen Zugriff
|
||||||
|
private final Map<UUID, Double> balances = new HashMap<>();
|
||||||
|
|
||||||
|
public BalanceManager(SurvivalPlus plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.balancesFile = new File(plugin.getDataFolder(), "balances.yml");
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Laden & Speichern ----
|
||||||
|
|
||||||
|
private void load() {
|
||||||
|
if (!balancesFile.exists()) {
|
||||||
|
try {
|
||||||
|
balancesFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Fehler beim Erstellen von balances.yml: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
balancesConfig = YamlConfiguration.loadConfiguration(balancesFile);
|
||||||
|
|
||||||
|
// Alle gespeicherten Kontostände in den Cache laden
|
||||||
|
for (String key : balancesConfig.getKeys(false)) {
|
||||||
|
try {
|
||||||
|
UUID uuid = UUID.fromString(key);
|
||||||
|
double balance = balancesConfig.getDouble(key);
|
||||||
|
balances.put(uuid, balance);
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
// Ungültiger UUID-Eintrag, überspringen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin.getLogger().info("BalanceManager: " + balances.size() + " Konten geladen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
for (Map.Entry<UUID, Double> entry : balances.entrySet()) {
|
||||||
|
balancesConfig.set(entry.getKey().toString(), entry.getValue());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
balancesConfig.save(balancesFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Fehler beim Speichern von balances.yml: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einzelnen Eintrag sofort auf Disk persistieren (nach jeder Transaktion)
|
||||||
|
private void saveEntry(UUID uuid, double amount) {
|
||||||
|
balancesConfig.set(uuid.toString(), amount);
|
||||||
|
try {
|
||||||
|
balancesConfig.save(balancesFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Fehler beim Speichern von balances.yml: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Konto-Operationen ----
|
||||||
|
|
||||||
|
public boolean hasAccount(UUID uuid) {
|
||||||
|
return balances.containsKey(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt ein neues Konto mit dem Startsaldo aus der config.yml.
|
||||||
|
*/
|
||||||
|
public void createAccount(UUID uuid) {
|
||||||
|
if (!balances.containsKey(uuid)) {
|
||||||
|
double startBalance = plugin.getConfig().getDouble("economy.start-balance", 500.0);
|
||||||
|
balances.put(uuid, startBalance);
|
||||||
|
saveEntry(uuid, startBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBalance(UUID uuid) {
|
||||||
|
return balances.getOrDefault(uuid, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Kontostand direkt (z.B. für Admin-Befehle).
|
||||||
|
*/
|
||||||
|
public void setBalance(UUID uuid, double amount) {
|
||||||
|
balances.put(uuid, Math.max(0.0, amount));
|
||||||
|
saveEntry(uuid, balances.get(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zieht einen Betrag ab. Gibt false zurück wenn das Guthaben nicht ausreicht.
|
||||||
|
*/
|
||||||
|
public boolean withdraw(UUID uuid, double amount) {
|
||||||
|
double current = getBalance(uuid);
|
||||||
|
if (current < amount) return false;
|
||||||
|
double newBalance = current - amount;
|
||||||
|
balances.put(uuid, newBalance);
|
||||||
|
saveEntry(uuid, newBalance);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt eine Kopie aller Kontostände zurück (für /money top).
|
||||||
|
*/
|
||||||
|
public Map<UUID, Double> getAllBalances() {
|
||||||
|
return Collections.unmodifiableMap(balances);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schreibt einen Betrag gut. Gibt immer true zurück.
|
||||||
|
*/
|
||||||
|
public boolean deposit(UUID uuid, double amount) {
|
||||||
|
double newBalance = getBalance(uuid) + amount;
|
||||||
|
balances.put(uuid, newBalance);
|
||||||
|
saveEntry(uuid, newBalance);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package de.viper.survivalplus.economy;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import net.milkbowl.vault.economy.AbstractEconomy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eigener Vault-Economy-Provider für SurvivalPlus.
|
||||||
|
* Wird bei Vault registriert, sodass kein externes Economy-Plugin nötig ist.
|
||||||
|
*/
|
||||||
|
public class SurvivalPlusEconomy extends AbstractEconomy {
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
private final BalanceManager balanceManager;
|
||||||
|
|
||||||
|
public SurvivalPlusEconomy(SurvivalPlus plugin, BalanceManager balanceManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.balanceManager = balanceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Pflicht-Methoden von AbstractEconomy ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return plugin.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "SurvivalPlusEconomy";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currencyNamePlural() {
|
||||||
|
return plugin.getConfig().getString("economy.currency-plural", "Coins");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currencyNameSingular() {
|
||||||
|
return plugin.getConfig().getString("economy.currency-singular", "Coin");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasBankSupport() {
|
||||||
|
return false; // Kein Bank-System
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fractionalDigits() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(double amount) {
|
||||||
|
return String.format("%.2f %s", amount, currencyNamePlural());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Account-Verwaltung ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccount(String playerName) {
|
||||||
|
OfflinePlayer p = Bukkit.getOfflinePlayer(playerName);
|
||||||
|
return p != null && balanceManager.hasAccount(p.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccount(OfflinePlayer player) {
|
||||||
|
return balanceManager.hasAccount(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccount(String playerName, String worldName) {
|
||||||
|
return hasAccount(playerName); // Keine World-spezifischen Konten
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccount(OfflinePlayer player, String worldName) {
|
||||||
|
return hasAccount(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createPlayerAccount(String playerName) {
|
||||||
|
OfflinePlayer p = Bukkit.getOfflinePlayer(playerName);
|
||||||
|
if (p == null) return false;
|
||||||
|
balanceManager.createAccount(p.getUniqueId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createPlayerAccount(OfflinePlayer player) {
|
||||||
|
balanceManager.createAccount(player.getUniqueId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createPlayerAccount(String playerName, String worldName) {
|
||||||
|
return createPlayerAccount(playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createPlayerAccount(OfflinePlayer player, String worldName) {
|
||||||
|
return createPlayerAccount(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Kontostand ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBalance(String playerName) {
|
||||||
|
OfflinePlayer p = Bukkit.getOfflinePlayer(playerName);
|
||||||
|
if (p == null) return 0;
|
||||||
|
ensureAccount(p.getUniqueId());
|
||||||
|
return balanceManager.getBalance(p.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBalance(OfflinePlayer player) {
|
||||||
|
ensureAccount(player.getUniqueId());
|
||||||
|
return balanceManager.getBalance(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBalance(String playerName, String world) {
|
||||||
|
return getBalance(playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBalance(OfflinePlayer player, String world) {
|
||||||
|
return getBalance(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String playerName, double amount) {
|
||||||
|
return getBalance(playerName) >= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(OfflinePlayer player, double amount) {
|
||||||
|
return getBalance(player) >= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String playerName, String worldName, double amount) {
|
||||||
|
return has(playerName, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(OfflinePlayer player, String worldName, double amount) {
|
||||||
|
return has(player, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Transaktionen ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse withdrawPlayer(String playerName, double amount) {
|
||||||
|
OfflinePlayer p = Bukkit.getOfflinePlayer(playerName);
|
||||||
|
if (p == null) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE, "Spieler nicht gefunden.");
|
||||||
|
}
|
||||||
|
return withdrawPlayer(p, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse withdrawPlayer(OfflinePlayer player, double amount) {
|
||||||
|
if (amount < 0) {
|
||||||
|
return new EconomyResponse(0, getBalance(player),
|
||||||
|
EconomyResponse.ResponseType.FAILURE, "Betrag darf nicht negativ sein.");
|
||||||
|
}
|
||||||
|
ensureAccount(player.getUniqueId());
|
||||||
|
boolean success = balanceManager.withdraw(player.getUniqueId(), amount);
|
||||||
|
if (success) {
|
||||||
|
return new EconomyResponse(amount, getBalance(player), EconomyResponse.ResponseType.SUCCESS, null);
|
||||||
|
} else {
|
||||||
|
return new EconomyResponse(0, getBalance(player),
|
||||||
|
EconomyResponse.ResponseType.FAILURE, "Nicht genug Guthaben.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse withdrawPlayer(String playerName, String worldName, double amount) {
|
||||||
|
return withdrawPlayer(playerName, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse withdrawPlayer(OfflinePlayer player, String worldName, double amount) {
|
||||||
|
return withdrawPlayer(player, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse depositPlayer(String playerName, double amount) {
|
||||||
|
OfflinePlayer p = Bukkit.getOfflinePlayer(playerName);
|
||||||
|
if (p == null) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE, "Spieler nicht gefunden.");
|
||||||
|
}
|
||||||
|
return depositPlayer(p, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse depositPlayer(OfflinePlayer player, double amount) {
|
||||||
|
if (amount < 0) {
|
||||||
|
return new EconomyResponse(0, getBalance(player),
|
||||||
|
EconomyResponse.ResponseType.FAILURE, "Betrag darf nicht negativ sein.");
|
||||||
|
}
|
||||||
|
ensureAccount(player.getUniqueId());
|
||||||
|
balanceManager.deposit(player.getUniqueId(), amount);
|
||||||
|
return new EconomyResponse(amount, getBalance(player), EconomyResponse.ResponseType.SUCCESS, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse depositPlayer(String playerName, String worldName, double amount) {
|
||||||
|
return depositPlayer(playerName, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse depositPlayer(OfflinePlayer player, String worldName, double amount) {
|
||||||
|
return depositPlayer(player, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Bank-Methoden (nicht unterstützt) ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse createBank(String name, String player) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse createBank(String name, OfflinePlayer player) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse deleteBank(String name) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankBalance(String name) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankHas(String name, double amount) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankWithdraw(String name, double amount) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankDeposit(String name, double amount) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse isBankOwner(String name, String playerName) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse isBankOwner(String name, OfflinePlayer player) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse isBankMember(String name, String playerName) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse isBankMember(String name, OfflinePlayer player) {
|
||||||
|
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, "Banken nicht unterstützt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getBanks() {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Hilfsmethode ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt automatisch ein Konto falls noch keines existiert.
|
||||||
|
*/
|
||||||
|
private void ensureAccount(UUID uuid) {
|
||||||
|
if (!balanceManager.hasAccount(uuid)) {
|
||||||
|
balanceManager.createAccount(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package de.viper.survivalplus.gui;
|
|||||||
|
|
||||||
import de.viper.survivalplus.Manager.ShopManager;
|
import de.viper.survivalplus.Manager.ShopManager;
|
||||||
import de.viper.survivalplus.SurvivalPlus;
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@@ -22,11 +24,14 @@ public class ShopGui implements Listener {
|
|||||||
private final Player player;
|
private final Player player;
|
||||||
private Inventory inv;
|
private Inventory inv;
|
||||||
private final SurvivalPlus plugin;
|
private final SurvivalPlus plugin;
|
||||||
|
// Economy wird zentral aus dem Plugin geholt – keine eigene Initialisierung nötig
|
||||||
|
private final Economy economy;
|
||||||
|
|
||||||
public ShopGui(SurvivalPlus plugin, Player player, ShopManager shopManager) {
|
public ShopGui(SurvivalPlus plugin, Player player, ShopManager shopManager) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.shopManager = shopManager;
|
this.shopManager = shopManager;
|
||||||
|
this.economy = plugin.getEconomy(); // <-- zentral aus SurvivalPlus
|
||||||
createInventory();
|
createInventory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +43,9 @@ public class ShopGui implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alle Items aus shop.yml einlesen
|
|
||||||
for (String itemKey : shopManager.getShopConfig().getConfigurationSection("items").getKeys(false)) {
|
for (String itemKey : shopManager.getShopConfig().getConfigurationSection("items").getKeys(false)) {
|
||||||
Material mat = getMaterialFromKey(itemKey);
|
Material mat = getMaterialFromKey(itemKey);
|
||||||
if (mat == null) {
|
if (mat == null) continue;
|
||||||
continue; // Unbekannte Materialien einfach überspringen
|
|
||||||
}
|
|
||||||
|
|
||||||
addShopItem(itemKey, mat, 64);
|
addShopItem(itemKey, mat, 64);
|
||||||
addShopItem(itemKey, mat, 16);
|
addShopItem(itemKey, mat, 16);
|
||||||
@@ -72,8 +74,8 @@ public class ShopGui implements Listener {
|
|||||||
meta.setDisplayName(ChatColor.GREEN.toString() + amount + "x " + material.name());
|
meta.setDisplayName(ChatColor.GREEN.toString() + amount + "x " + material.name());
|
||||||
meta.setLore(Arrays.asList(
|
meta.setLore(Arrays.asList(
|
||||||
ChatColor.GRAY + "Lagerbestand: " + ChatColor.YELLOW + currentStock,
|
ChatColor.GRAY + "Lagerbestand: " + ChatColor.YELLOW + currentStock,
|
||||||
ChatColor.YELLOW + "Preis pro Stück: " + pricePerUnit,
|
ChatColor.YELLOW + "Preis pro Stück: " + String.format("%.2f", pricePerUnit),
|
||||||
ChatColor.YELLOW + "Gesamtpreis: " + totalPrice,
|
ChatColor.YELLOW + "Gesamtpreis: " + String.format("%.2f", totalPrice),
|
||||||
"",
|
"",
|
||||||
ChatColor.GREEN + "Linksklick zum Kaufen",
|
ChatColor.GREEN + "Linksklick zum Kaufen",
|
||||||
ChatColor.RED + "Rechtsklick zum Verkaufen"
|
ChatColor.RED + "Rechtsklick zum Verkaufen"
|
||||||
@@ -100,58 +102,85 @@ public class ShopGui implements Listener {
|
|||||||
ItemStack clicked = e.getCurrentItem();
|
ItemStack clicked = e.getCurrentItem();
|
||||||
if (clicked == null || clicked.getType() == Material.AIR) return;
|
if (clicked == null || clicked.getType() == Material.AIR) return;
|
||||||
|
|
||||||
|
// Economy-Check: Wenn Vault nicht verfügbar ist, Shop sperren
|
||||||
|
if (economy == null) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Das Economy-System ist aktuell nicht verfügbar.");
|
||||||
|
player.closeInventory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int amount = clicked.getAmount();
|
int amount = clicked.getAmount();
|
||||||
Material mat = clicked.getType();
|
Material mat = clicked.getType();
|
||||||
String itemKey = mat.name().toLowerCase();
|
String itemKey = mat.name().toLowerCase();
|
||||||
|
|
||||||
if (e.isLeftClick()) {
|
if (e.isLeftClick()) {
|
||||||
// --- KAUFEN ---
|
// --- KAUFEN ---
|
||||||
|
double totalPrice = shopManager.getCurrentPrice(itemKey) * amount;
|
||||||
|
|
||||||
|
// 1. Prüfen ob Spieler genug Geld hat
|
||||||
|
if (economy.getBalance(player) < totalPrice) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Benötigt: "
|
||||||
|
+ String.format("%.2f", totalPrice) + " Coins.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Shop-Logik (Preis erhöhen, ggf. Bestand prüfen)
|
||||||
if (!shopManager.buyItem(itemKey, amount)) {
|
if (!shopManager.buyItem(itemKey, amount)) {
|
||||||
player.sendMessage(ChatColor.RED + "Nicht genügend Bestand im Shop.");
|
player.sendMessage(ChatColor.RED + "Nicht genügend Bestand im Shop.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double totalPrice = shopManager.getCurrentPrice(itemKey) * amount;
|
// 3. Geld abziehen
|
||||||
|
EconomyResponse response = economy.withdrawPlayer(player, totalPrice);
|
||||||
// TODO: Economy Abzug hier einfügen!
|
if (!response.transactionSuccess()) {
|
||||||
// Beispiel: if (!economy.withdrawPlayer(player, totalPrice)) { ... }
|
player.sendMessage(ChatColor.RED + "Fehler bei der Zahlung: " + response.errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Items geben
|
||||||
player.getInventory().addItem(new ItemStack(mat, amount));
|
player.getInventory().addItem(new ItemStack(mat, amount));
|
||||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + String.format("%.2f", totalPrice) + " gekauft.");
|
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name()
|
||||||
|
+ " für " + String.format("%.2f", totalPrice) + " Coins gekauft.");
|
||||||
|
|
||||||
} else if (e.isRightClick()) {
|
} else if (e.isRightClick()) {
|
||||||
// --- VERKAUFEN ---
|
// --- VERKAUFEN ---
|
||||||
|
|
||||||
// Prüfen ob Spieler genug Items hat
|
// 1. Prüfen ob Spieler genug Items hat
|
||||||
if (!player.getInventory().containsAtLeast(new ItemStack(mat), amount)) {
|
if (!player.getInventory().containsAtLeast(new ItemStack(mat), amount)) {
|
||||||
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items zum Verkaufen.");
|
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items zum Verkaufen.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verkaufslogik
|
// 2. Preis VOR dem Preisverfall festhalten
|
||||||
|
double sellPrice = shopManager.getCurrentPrice(itemKey) * amount;
|
||||||
|
|
||||||
|
// 3. Items entfernen
|
||||||
|
player.getInventory().removeItem(new ItemStack(mat, amount));
|
||||||
|
|
||||||
|
// 4. Preis im Shop anpassen (Lager erhöhen, Preis senken)
|
||||||
shopManager.sellItem(itemKey, amount);
|
shopManager.sellItem(itemKey, amount);
|
||||||
|
|
||||||
double sellPrice = shopManager.getCurrentPrice(itemKey) * amount; // Neuer Preis nach dem Verkauf wird genutzt
|
// 5. Geld gutschreiben
|
||||||
// Hinweis: In echten Systemen bekommt man oft den Preis *vor* dem Preisverfall, hier nehmen wir den neuen für Einfachheit oder den gespeicherten:
|
EconomyResponse response = economy.depositPlayer(player, sellPrice);
|
||||||
// Besser wäre: double oldPrice = price * amount; ... player.giveMoney(oldPrice);
|
if (!response.transactionSuccess()) {
|
||||||
|
// Rollback: Items zurückgeben wenn Zahlung fehlschlägt
|
||||||
// TODO: Economy Gutschrift hier einfügen!
|
player.getInventory().addItem(new ItemStack(mat, amount));
|
||||||
|
player.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung: " + response.errorMessage);
|
||||||
player.getInventory().removeItem(new ItemStack(mat, amount));
|
return;
|
||||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + String.format("%.2f", sellPrice) + " verkauft.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inventory schließen, um Preise neu zu laden und Bugs zu vermeiden
|
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name()
|
||||||
|
+ " für " + String.format("%.2f", sellPrice) + " Coins verkauft.");
|
||||||
|
}
|
||||||
|
|
||||||
|
player.updateInventory();
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
// Optional: direkt neu öffnen mit createInventory() wenn es flüssig sein soll
|
|
||||||
// createInventory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onInventoryClose(InventoryCloseEvent e) {
|
public void onInventoryClose(InventoryCloseEvent e) {
|
||||||
if (e.getView().getTitle().equals("Shop") && e.getPlayer().equals(player)) {
|
if (e.getView().getTitle().equals("Shop") && e.getPlayer().equals(player)) {
|
||||||
// Listener kann entladen werden, wenn nicht mehr gebraucht
|
// Listener-Cleanup hier möglich, wenn ShopGui nicht als Singleton genutzt wird
|
||||||
// HandlerList.unregisterAll(this); // Vorsicht: falls Singleton, sonst fliegt allen der GUI weg
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
394
src/main/java/de/viper/survivalplus/jobs/JobGui.java
Normal file
394
src/main/java/de/viper/survivalplus/jobs/JobGui.java
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
package de.viper.survivalplus.jobs;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dreistufiges Job-GUI:
|
||||||
|
* Screen 1 – Übersicht aller Jobs
|
||||||
|
* Screen 2 – Job-Detail (Info, Level, XP-Balken, Join/Leave-Button)
|
||||||
|
* Screen 3 – Bestätigung (Ja / Nein)
|
||||||
|
*
|
||||||
|
* Öffnen: new JobGui(plugin, player, jobManager);
|
||||||
|
* Der Listener registriert/deregistriert sich selbst automatisch.
|
||||||
|
*/
|
||||||
|
public class JobGui implements Listener {
|
||||||
|
|
||||||
|
// ---- GUI-Titel (werden zur Erkennung im ClickEvent benutzt) ----
|
||||||
|
private static final String TITLE_OVERVIEW = ChatColor.DARK_AQUA + "✦ " + ChatColor.WHITE + "Jobs" + ChatColor.DARK_AQUA + " ✦";
|
||||||
|
private static final String TITLE_DETAIL = ChatColor.DARK_AQUA + "✦ " + ChatColor.WHITE + "Job-Info" + ChatColor.DARK_AQUA + " ✦";
|
||||||
|
private static final String TITLE_CONFIRM_JOIN = ChatColor.DARK_GREEN + "Job annehmen?";
|
||||||
|
private static final String TITLE_CONFIRM_LEAVE = ChatColor.DARK_RED + "Job kündigen?";
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
private final JobManager jobManager;
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
/** Der aktuell angezeigte / ausgewählte Job. */
|
||||||
|
private String currentJobId = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX Bug 1: Flag, das gesetzt wird wenn wir absichtlich zwischen
|
||||||
|
* Screens wechseln. So wird der Listener beim Screen-Wechsel NICHT
|
||||||
|
* abgemeldet – nur wenn der Spieler die GUI wirklich schließt.
|
||||||
|
*/
|
||||||
|
private boolean transitioning = false;
|
||||||
|
|
||||||
|
public JobGui(SurvivalPlus plugin, Player player, JobManager jobManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.player = player;
|
||||||
|
this.jobManager = jobManager;
|
||||||
|
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||||
|
openOverview();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Screen 1 – Übersicht
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void openOverview() {
|
||||||
|
transitioning = true;
|
||||||
|
Inventory inv = Bukkit.createInventory(null, 27, TITLE_OVERVIEW);
|
||||||
|
fillBorder(inv);
|
||||||
|
|
||||||
|
List<String> jobIds = new ArrayList<>(jobManager.getJobIds());
|
||||||
|
int[] slots = centeredSlots(jobIds.size(), 10, 16); // Reihe 2, Slots 10–16
|
||||||
|
|
||||||
|
for (int i = 0; i < jobIds.size() && i < slots.length; i++) {
|
||||||
|
inv.setItem(slots[i], buildOverviewItem(jobIds.get(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statuszeile unten (Slot 22)
|
||||||
|
int active = jobManager.getPlayerJobs(player.getUniqueId()).size();
|
||||||
|
inv.setItem(22, buildSimpleItem(
|
||||||
|
Material.PAPER,
|
||||||
|
ChatColor.GRAY + "Aktive Jobs: " + ChatColor.WHITE + active + ChatColor.GRAY + " / " + jobManager.getMaxJobs(),
|
||||||
|
ChatColor.DARK_GRAY + "Klicke einen Job für Details."
|
||||||
|
));
|
||||||
|
|
||||||
|
player.openInventory(inv);
|
||||||
|
transitioning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack buildOverviewItem(String jobId) {
|
||||||
|
boolean hasJob = jobManager.hasJob(player.getUniqueId(), jobId);
|
||||||
|
int level = jobManager.getLevel(player.getUniqueId(), jobId);
|
||||||
|
|
||||||
|
ItemStack item = new ItemStack(iconFor(jobId));
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
|
||||||
|
meta.setDisplayName(
|
||||||
|
(hasJob ? ChatColor.GREEN : ChatColor.YELLOW) + "" + ChatColor.BOLD
|
||||||
|
+ jobManager.getJobDisplayName(jobId)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<String> lore = new ArrayList<>();
|
||||||
|
lore.add(ChatColor.GRAY + jobManager.getJobDescription(jobId));
|
||||||
|
lore.add("");
|
||||||
|
if (hasJob) {
|
||||||
|
lore.add(ChatColor.GREEN + "✔ Aktiv " + ChatColor.GRAY + "│ " + ChatColor.WHITE + "Level " + ChatColor.AQUA + level);
|
||||||
|
} else {
|
||||||
|
lore.add(ChatColor.GRAY + "Noch nicht aktiv");
|
||||||
|
}
|
||||||
|
lore.add("");
|
||||||
|
lore.add(ChatColor.DARK_GRAY + "» Klicken für Details");
|
||||||
|
|
||||||
|
meta.setLore(lore);
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Screen 2 – Detail
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void openDetail(String jobId) {
|
||||||
|
transitioning = true;
|
||||||
|
currentJobId = jobId;
|
||||||
|
Inventory inv = Bukkit.createInventory(null, 27, TITLE_DETAIL);
|
||||||
|
fillBorder(inv);
|
||||||
|
|
||||||
|
boolean hasJob = jobManager.hasJob(player.getUniqueId(), jobId);
|
||||||
|
int level = jobManager.getLevel(player.getUniqueId(), jobId);
|
||||||
|
int xp = jobManager.getXp(player.getUniqueId(), jobId);
|
||||||
|
int xpReq = jobManager.getXpRequired(jobId, level);
|
||||||
|
|
||||||
|
// ---- Mitte (Slot 13): Job-Info ----
|
||||||
|
ItemStack info = new ItemStack(iconFor(jobId));
|
||||||
|
ItemMeta im = info.getItemMeta();
|
||||||
|
im.setDisplayName(ChatColor.YELLOW + "" + ChatColor.BOLD + jobManager.getJobDisplayName(jobId));
|
||||||
|
|
||||||
|
List<String> lore = new ArrayList<>();
|
||||||
|
lore.add(ChatColor.GRAY + jobManager.getJobDescription(jobId));
|
||||||
|
lore.add("");
|
||||||
|
if (hasJob) {
|
||||||
|
lore.add(ChatColor.WHITE + "Level: " + ChatColor.AQUA + level
|
||||||
|
+ ChatColor.GRAY + " / " + jobManager.getMaxLevel());
|
||||||
|
if (level < jobManager.getMaxLevel()) {
|
||||||
|
lore.add(ChatColor.WHITE + "XP: " + ChatColor.AQUA + xp
|
||||||
|
+ ChatColor.GRAY + " / " + xpReq);
|
||||||
|
lore.add(xpBar(xp, xpReq));
|
||||||
|
} else {
|
||||||
|
lore.add(ChatColor.GOLD + "⭐ Maximales Level erreicht!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lore.add(ChatColor.GRAY + "Du hast diesen Job noch nicht.");
|
||||||
|
}
|
||||||
|
im.setLore(lore);
|
||||||
|
info.setItemMeta(im);
|
||||||
|
inv.setItem(13, info);
|
||||||
|
|
||||||
|
// ---- Zurück (Slot 18) ----
|
||||||
|
inv.setItem(18, buildSimpleItem(Material.ARROW,
|
||||||
|
ChatColor.GRAY + "◀ Zurück", null));
|
||||||
|
|
||||||
|
// ---- Aktions-Button (Slot 16) ----
|
||||||
|
if (hasJob) {
|
||||||
|
inv.setItem(16, buildActionItem(
|
||||||
|
Material.BARRIER,
|
||||||
|
ChatColor.RED + "" + ChatColor.BOLD + "✖ Job kündigen",
|
||||||
|
Arrays.asList(
|
||||||
|
ChatColor.GRAY + "Verlasse den Job",
|
||||||
|
ChatColor.GRAY + jobManager.getJobDisplayName(jobId) + ".",
|
||||||
|
"",
|
||||||
|
ChatColor.DARK_GRAY + "Level & XP bleiben gespeichert."
|
||||||
|
)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
boolean canJoin = jobManager.getPlayerJobs(player.getUniqueId()).size() < jobManager.getMaxJobs();
|
||||||
|
if (canJoin) {
|
||||||
|
inv.setItem(16, buildActionItem(
|
||||||
|
Material.LIME_DYE,
|
||||||
|
ChatColor.GREEN + "" + ChatColor.BOLD + "✔ Job annehmen",
|
||||||
|
Arrays.asList(
|
||||||
|
ChatColor.GRAY + "Nimm den Job",
|
||||||
|
ChatColor.GRAY + jobManager.getJobDisplayName(jobId) + " an."
|
||||||
|
)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
inv.setItem(16, buildActionItem(
|
||||||
|
Material.GRAY_DYE,
|
||||||
|
ChatColor.DARK_GRAY + "" + ChatColor.BOLD + "✖ Keine freien Slots",
|
||||||
|
Arrays.asList(
|
||||||
|
ChatColor.GRAY + "Du hast bereits " + jobManager.getMaxJobs() + " Jobs.",
|
||||||
|
ChatColor.GRAY + "Kündige zuerst einen Job."
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.openInventory(inv);
|
||||||
|
transitioning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Screen 3 – Bestätigung
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void openConfirm(String jobId, boolean joining) {
|
||||||
|
transitioning = true;
|
||||||
|
String title = joining ? TITLE_CONFIRM_JOIN : TITLE_CONFIRM_LEAVE;
|
||||||
|
Inventory inv = Bukkit.createInventory(null, 27, title);
|
||||||
|
fillBorder(inv);
|
||||||
|
|
||||||
|
// Info-Item (Slot 13)
|
||||||
|
inv.setItem(13, buildSimpleItem(
|
||||||
|
iconFor(jobId),
|
||||||
|
(joining ? ChatColor.GREEN : ChatColor.RED) + jobManager.getJobDisplayName(jobId),
|
||||||
|
joining
|
||||||
|
? ChatColor.GRAY + "Dem Job wirklich beitreten?"
|
||||||
|
: ChatColor.GRAY + "Job wirklich kündigen?"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Ja (Slot 11)
|
||||||
|
inv.setItem(11, buildActionItem(
|
||||||
|
Material.LIME_DYE,
|
||||||
|
ChatColor.GREEN + "" + ChatColor.BOLD + "✔ Ja",
|
||||||
|
Collections.singletonList(ChatColor.GRAY + "Bestätigen")
|
||||||
|
));
|
||||||
|
|
||||||
|
// Nein (Slot 15)
|
||||||
|
inv.setItem(15, buildActionItem(
|
||||||
|
Material.RED_DYE,
|
||||||
|
ChatColor.RED + "" + ChatColor.BOLD + "✖ Nein",
|
||||||
|
Collections.singletonList(ChatColor.GRAY + "Abbrechen")
|
||||||
|
));
|
||||||
|
|
||||||
|
player.openInventory(inv);
|
||||||
|
transitioning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Click-Handler
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClick(InventoryClickEvent e) {
|
||||||
|
if (!e.getWhoClicked().equals(player)) return;
|
||||||
|
|
||||||
|
String title = e.getView().getTitle();
|
||||||
|
if (!isOurGui(title)) return;
|
||||||
|
|
||||||
|
// FIX Bug 1: Immer canceln – egal welcher Slot angeklickt wird.
|
||||||
|
// Verhindert das Entnehmen von Items aus der GUI (auch Shift-Click
|
||||||
|
// in den eigenen Inventarbereich während die GUI offen ist).
|
||||||
|
e.setCancelled(true);
|
||||||
|
|
||||||
|
ItemStack clicked = e.getCurrentItem();
|
||||||
|
if (clicked == null || clicked.getType() == Material.AIR) return;
|
||||||
|
if (clicked.getType() == Material.BLACK_STAINED_GLASS_PANE) return;
|
||||||
|
|
||||||
|
String name = clicked.hasItemMeta() && clicked.getItemMeta().hasDisplayName()
|
||||||
|
? ChatColor.stripColor(clicked.getItemMeta().getDisplayName()) : "";
|
||||||
|
|
||||||
|
// ---- Screen 1 ----
|
||||||
|
if (title.equals(TITLE_OVERVIEW)) {
|
||||||
|
for (String jobId : jobManager.getJobIds()) {
|
||||||
|
if (name.contains(ChatColor.stripColor(jobManager.getJobDisplayName(jobId)))) {
|
||||||
|
openDetail(jobId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Screen 2 ----
|
||||||
|
if (title.equals(TITLE_DETAIL)) {
|
||||||
|
if (name.equals("◀ Zurück")) {
|
||||||
|
openOverview();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentJobId == null) return;
|
||||||
|
if (name.equals("✔ Job annehmen")) {
|
||||||
|
openConfirm(currentJobId, true);
|
||||||
|
} else if (name.equals("✖ Job kündigen")) {
|
||||||
|
openConfirm(currentJobId, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Screen 3 ----
|
||||||
|
if (title.equals(TITLE_CONFIRM_JOIN) || title.equals(TITLE_CONFIRM_LEAVE)) {
|
||||||
|
boolean joining = title.equals(TITLE_CONFIRM_JOIN);
|
||||||
|
if (name.equals("✔ Ja")) {
|
||||||
|
if (joining) {
|
||||||
|
if (jobManager.joinJob(player.getUniqueId(), currentJobId)) {
|
||||||
|
player.sendMessage(ChatColor.GREEN + "Du hast den Job "
|
||||||
|
+ ChatColor.YELLOW + jobManager.getJobDisplayName(currentJobId)
|
||||||
|
+ ChatColor.GREEN + " angenommen!");
|
||||||
|
} else {
|
||||||
|
player.sendMessage(ChatColor.RED + "Beitreten nicht möglich (Limit erreicht oder Job aktiv).");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (jobManager.leaveJob(player.getUniqueId(), currentJobId)) {
|
||||||
|
player.sendMessage(ChatColor.YELLOW + "Du hast den Job "
|
||||||
|
+ ChatColor.WHITE + jobManager.getJobDisplayName(currentJobId)
|
||||||
|
+ ChatColor.YELLOW + " gekündigt. Level bleibt gespeichert.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Nach Aktion direkt Übersicht aktualisiert anzeigen.
|
||||||
|
// transitioning wird in openOverview() gesetzt – kein Extra-Flag nötig.
|
||||||
|
Bukkit.getScheduler().runTask(plugin, this::openOverview);
|
||||||
|
|
||||||
|
} else if (name.equals("✖ Nein")) {
|
||||||
|
openDetail(currentJobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClose(InventoryCloseEvent e) {
|
||||||
|
if (!e.getPlayer().equals(player)) return;
|
||||||
|
// FIX Bug 1: Listener NUR abmelden wenn der Spieler die GUI
|
||||||
|
// wirklich schließt – nicht bei internem Screen-Wechsel.
|
||||||
|
if (isOurGui(e.getView().getTitle()) && !transitioning) {
|
||||||
|
HandlerList.unregisterAll(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Builder-Hilfsmethoden
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void fillBorder(Inventory inv) {
|
||||||
|
ItemStack pane = new ItemStack(Material.BLACK_STAINED_GLASS_PANE);
|
||||||
|
ItemMeta m = pane.getItemMeta();
|
||||||
|
m.setDisplayName(" ");
|
||||||
|
pane.setItemMeta(m);
|
||||||
|
int size = inv.getSize();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (i < 9 || i >= size - 9 || i % 9 == 0 || i % 9 == 8) {
|
||||||
|
inv.setItem(i, pane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack buildSimpleItem(Material mat, String name, String loreLine) {
|
||||||
|
ItemStack item = new ItemStack(mat);
|
||||||
|
ItemMeta m = item.getItemMeta();
|
||||||
|
m.setDisplayName(name);
|
||||||
|
if (loreLine != null) m.setLore(Collections.singletonList(loreLine));
|
||||||
|
item.setItemMeta(m);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack buildActionItem(Material mat, String name, List<String> lore) {
|
||||||
|
ItemStack item = new ItemStack(mat);
|
||||||
|
ItemMeta m = item.getItemMeta();
|
||||||
|
m.setDisplayName(name);
|
||||||
|
m.setLore(lore);
|
||||||
|
item.setItemMeta(m);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Passendes Icon-Material pro Job. */
|
||||||
|
private Material iconFor(String jobId) {
|
||||||
|
switch (jobId.toLowerCase()) {
|
||||||
|
case "miner": return Material.DIAMOND_PICKAXE;
|
||||||
|
case "farmer": return Material.WHEAT;
|
||||||
|
case "hunter": return Material.IRON_SWORD;
|
||||||
|
case "woodcutter": return Material.IRON_AXE;
|
||||||
|
case "fischer": return Material.FISHING_ROD;
|
||||||
|
case "builder": return Material.BRICKS;
|
||||||
|
default: return Material.BOOK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Berechnet zentrierte Slot-Positionen für n Items zwischen minSlot und maxSlot.
|
||||||
|
*/
|
||||||
|
private int[] centeredSlots(int count, int minSlot, int maxSlot) {
|
||||||
|
int available = maxSlot - minSlot + 1;
|
||||||
|
int start = minSlot + (available - Math.min(count, available)) / 2;
|
||||||
|
int[] slots = new int[Math.min(count, available)];
|
||||||
|
for (int i = 0; i < slots.length; i++) slots[i] = start + i;
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** XP-Fortschrittsbalken als farbigen String. */
|
||||||
|
private String xpBar(int current, int max) {
|
||||||
|
int len = 16;
|
||||||
|
int filled = max == 0 ? 0 : Math.min((int) ((double) current / max * len), len);
|
||||||
|
return ChatColor.GREEN + "█".repeat(filled)
|
||||||
|
+ ChatColor.DARK_GRAY + "█".repeat(len - filled)
|
||||||
|
+ ChatColor.GRAY + " " + current + "/" + max;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOurGui(String title) {
|
||||||
|
return title.equals(TITLE_OVERVIEW)
|
||||||
|
|| title.equals(TITLE_DETAIL)
|
||||||
|
|| title.equals(TITLE_CONFIRM_JOIN)
|
||||||
|
|| title.equals(TITLE_CONFIRM_LEAVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
170
src/main/java/de/viper/survivalplus/jobs/JobListener.java
Normal file
170
src/main/java/de/viper/survivalplus/jobs/JobListener.java
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package de.viper.survivalplus.jobs;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.Ageable;
|
||||||
|
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.block.BlockPlaceEvent;
|
||||||
|
import org.bukkit.event.entity.EntityDeathEvent;
|
||||||
|
import org.bukkit.event.player.PlayerFishEvent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class JobListener implements Listener {
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
private final JobManager jobManager;
|
||||||
|
|
||||||
|
public JobListener(SurvivalPlus plugin, JobManager jobManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.jobManager = jobManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// MINER + WOODCUTTER + FARMER (alle über BlockBreak)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onBlockBreak(BlockBreakEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Block block = event.getBlock();
|
||||||
|
String key = block.getType().name();
|
||||||
|
|
||||||
|
if (isCrop(block)) {
|
||||||
|
if (!isFullyGrown(block)) return;
|
||||||
|
reward(player, "farmer", key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pumpkin & Melon sind keine Ageable-Crops
|
||||||
|
if (key.equals("PUMPKIN") || key.equals("MELON")) {
|
||||||
|
reward(player, "farmer", key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reward(player, "miner", key);
|
||||||
|
reward(player, "woodcutter", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// BUILDER
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
|
reward(event.getPlayer(), "builder", event.getBlock().getType().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// HUNTER
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onEntityDeath(EntityDeathEvent event) {
|
||||||
|
org.bukkit.entity.LivingEntity entity = event.getEntity();
|
||||||
|
if (!(entity.getKiller() instanceof Player)) return;
|
||||||
|
reward((Player) entity.getKiller(), "hunter", entity.getType().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// FISCHER
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onPlayerFish(PlayerFishEvent event) {
|
||||||
|
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) return;
|
||||||
|
if (!(event.getCaught() instanceof org.bukkit.entity.Item)) return;
|
||||||
|
|
||||||
|
org.bukkit.entity.Item caughtItem = (org.bukkit.entity.Item) event.getCaught();
|
||||||
|
String itemKey = caughtItem.getItemStack().getType().name();
|
||||||
|
reward(event.getPlayer(), "fischer", itemKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Kern-Logik
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void reward(Player player, String jobId, String actionKey) {
|
||||||
|
UUID uuid = player.getUniqueId();
|
||||||
|
|
||||||
|
if (!jobManager.hasJob(uuid, jobId)) return;
|
||||||
|
if (!jobManager.hasAction(jobId, actionKey)) return; // Block einfach nicht konfiguriert → still ignorieren
|
||||||
|
|
||||||
|
int level = jobManager.getLevel(uuid, jobId);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// GELD – unabhängig von XP; Fehler hier bricht XP NICHT ab
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
double money = jobManager.getMoneyForAction(jobId, actionKey, level);
|
||||||
|
if (money > 0) {
|
||||||
|
Economy economy = plugin.getEconomy();
|
||||||
|
if (economy == null) {
|
||||||
|
plugin.getLogger().warning(
|
||||||
|
"[Jobs] Economy = null! Vault installiert? Economy-Plugin aktiv?");
|
||||||
|
} else {
|
||||||
|
EconomyResponse response = economy.depositPlayer(player, money);
|
||||||
|
if (response != null && response.transactionSuccess()) {
|
||||||
|
player.sendMessage(
|
||||||
|
ChatColor.GREEN + "+" + String.format("%.2f", money) + " Coins "
|
||||||
|
+ ChatColor.GRAY + "(" + jobManager.getJobDisplayName(jobId)
|
||||||
|
+ " Lv." + level + ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
String reason = (response != null) ? response.errorMessage : "response=null";
|
||||||
|
plugin.getLogger().warning(
|
||||||
|
"[Jobs] depositPlayer fehlgeschlagen für " + player.getName()
|
||||||
|
+ " | +" + money + " Coins | Grund: " + reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// XP – läuft IMMER, egal ob Economy funktioniert oder nicht
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
int xp = jobManager.getXpForAction(jobId, actionKey);
|
||||||
|
if (xp > 0) {
|
||||||
|
boolean leveledUp = jobManager.addXp(uuid, jobId, xp);
|
||||||
|
if (leveledUp) {
|
||||||
|
int newLevel = jobManager.getLevel(uuid, jobId);
|
||||||
|
player.sendMessage(
|
||||||
|
ChatColor.GOLD + "⭐ " + ChatColor.YELLOW
|
||||||
|
+ jobManager.getJobDisplayName(jobId)
|
||||||
|
+ ChatColor.GOLD + " Level Up! → "
|
||||||
|
+ ChatColor.WHITE + "Level " + newLevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Hilfsmethoden
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private boolean isCrop(Block block) {
|
||||||
|
switch (block.getType()) {
|
||||||
|
case WHEAT:
|
||||||
|
case POTATOES:
|
||||||
|
case CARROTS:
|
||||||
|
case BEETROOTS:
|
||||||
|
case NETHER_WART:
|
||||||
|
case COCOA:
|
||||||
|
case SWEET_BERRY_BUSH:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFullyGrown(Block block) {
|
||||||
|
if (!(block.getBlockData() instanceof Ageable)) return true;
|
||||||
|
Ageable ageable = (Ageable) block.getBlockData();
|
||||||
|
return ageable.getAge() == ageable.getMaximumAge();
|
||||||
|
}
|
||||||
|
}
|
||||||
212
src/main/java/de/viper/survivalplus/jobs/JobManager.java
Normal file
212
src/main/java/de/viper/survivalplus/jobs/JobManager.java
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package de.viper.survivalplus.jobs;
|
||||||
|
|
||||||
|
import de.viper.survivalplus.SurvivalPlus;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verwaltet alle Job-Daten:
|
||||||
|
* jobs.yml → Job-Definitionen (Aktionen, Bezahlung, XP)
|
||||||
|
* playerjobs.yml → Spieler-Fortschritt (aktive Jobs, Level, XP)
|
||||||
|
*/
|
||||||
|
public class JobManager {
|
||||||
|
|
||||||
|
private final SurvivalPlus plugin;
|
||||||
|
|
||||||
|
private final File jobsFile;
|
||||||
|
private FileConfiguration jobsConfig;
|
||||||
|
|
||||||
|
private final File playerJobsFile;
|
||||||
|
private FileConfiguration playerJobsConfig;
|
||||||
|
|
||||||
|
private int maxJobs;
|
||||||
|
private int maxLevel;
|
||||||
|
private double levelMultiplier;
|
||||||
|
|
||||||
|
public JobManager(SurvivalPlus plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.jobsFile = new File(plugin.getDataFolder(), "jobs.yml");
|
||||||
|
this.playerJobsFile = new File(plugin.getDataFolder(), "playerjobs.yml");
|
||||||
|
loadJobsConfig();
|
||||||
|
loadPlayerJobsConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Config-Verwaltung
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void loadJobsConfig() {
|
||||||
|
if (!jobsFile.exists()) {
|
||||||
|
plugin.saveResource("jobs.yml", false);
|
||||||
|
}
|
||||||
|
jobsConfig = YamlConfiguration.loadConfiguration(jobsFile);
|
||||||
|
maxJobs = jobsConfig.getInt("settings.max-jobs", 2);
|
||||||
|
maxLevel = jobsConfig.getInt("settings.max-level", 10);
|
||||||
|
levelMultiplier = jobsConfig.getDouble("settings.level-multiplier", 0.1);
|
||||||
|
plugin.getLogger().info("JobManager: " + getJobIds().size() + " Jobs geladen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlayerJobsConfig() {
|
||||||
|
if (!playerJobsFile.exists()) {
|
||||||
|
try {
|
||||||
|
playerJobsFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Fehler beim Erstellen von playerjobs.yml: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playerJobsConfig = YamlConfiguration.loadConfiguration(playerJobsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePlayerJobs() {
|
||||||
|
try {
|
||||||
|
playerJobsConfig.save(playerJobsFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Fehler beim Speichern von playerjobs.yml: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lädt beide Configs neu (für /sp reload). */
|
||||||
|
public void reload() {
|
||||||
|
loadJobsConfig();
|
||||||
|
loadPlayerJobsConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Job-Definitionen (aus jobs.yml)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Alle konfigurierten Job-IDs in der Reihenfolge der YAML-Datei. */
|
||||||
|
public Set<String> getJobIds() {
|
||||||
|
if (jobsConfig.getConfigurationSection("jobs") == null) return new LinkedHashSet<>();
|
||||||
|
return jobsConfig.getConfigurationSection("jobs").getKeys(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean jobExists(String jobId) {
|
||||||
|
return jobsConfig.contains("jobs." + jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Anzeigename inkl. Emoji, z.B. "⛏ Miner". */
|
||||||
|
public String getJobDisplayName(String jobId) {
|
||||||
|
return jobsConfig.getString("jobs." + jobId + ".display", jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJobDescription(String jobId) {
|
||||||
|
return jobsConfig.getString("jobs." + jobId + ".description", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxJobs() { return maxJobs; }
|
||||||
|
public int getMaxLevel() { return maxLevel; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bezahlung für eine Aktion, skaliert mit dem Level des Spielers.
|
||||||
|
* Formel: baseMoney * (1 + level * levelMultiplier)
|
||||||
|
*/
|
||||||
|
public double getMoneyForAction(String jobId, String actionKey, int level) {
|
||||||
|
double base = jobsConfig.getDouble("jobs." + jobId + ".actions." + actionKey + ".money", 0);
|
||||||
|
if (base <= 0) return 0;
|
||||||
|
return base * (1.0 + level * levelMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXpForAction(String jobId, String actionKey) {
|
||||||
|
return jobsConfig.getInt("jobs." + jobId + ".actions." + actionKey + ".xp", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAction(String jobId, String actionKey) {
|
||||||
|
return jobsConfig.contains("jobs." + jobId + ".actions." + actionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** XP-Anforderung für das nächste Level: xpBase * currentLevel. */
|
||||||
|
public int getXpRequired(String jobId, int level) {
|
||||||
|
int base = jobsConfig.getInt("jobs." + jobId + ".xp-base", 100);
|
||||||
|
return base * level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Spieler-Daten (aus playerjobs.yml)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private String p(UUID uuid) { return uuid.toString(); }
|
||||||
|
|
||||||
|
/** Aktive Job-IDs des Spielers. */
|
||||||
|
public List<String> getPlayerJobs(UUID uuid) {
|
||||||
|
return playerJobsConfig.getStringList(p(uuid) + ".jobs");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasJob(UUID uuid, String jobId) {
|
||||||
|
return getPlayerJobs(uuid).contains(jobId.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevel(UUID uuid, String jobId) {
|
||||||
|
return playerJobsConfig.getInt(p(uuid) + ".data." + jobId + ".level", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXp(UUID uuid, String jobId) {
|
||||||
|
return playerJobsConfig.getInt(p(uuid) + ".data." + jobId + ".xp", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spieler tritt einem Job bei.
|
||||||
|
* @return false wenn bereits aktiv oder Job-Limit erreicht.
|
||||||
|
*/
|
||||||
|
public boolean joinJob(UUID uuid, String jobId) {
|
||||||
|
jobId = jobId.toLowerCase();
|
||||||
|
if (!jobExists(jobId)) return false;
|
||||||
|
List<String> jobs = new ArrayList<>(getPlayerJobs(uuid));
|
||||||
|
if (jobs.contains(jobId)) return false;
|
||||||
|
if (jobs.size() >= maxJobs) return false;
|
||||||
|
|
||||||
|
jobs.add(jobId);
|
||||||
|
playerJobsConfig.set(p(uuid) + ".jobs", jobs);
|
||||||
|
|
||||||
|
if (!playerJobsConfig.contains(p(uuid) + ".data." + jobId + ".level")) {
|
||||||
|
playerJobsConfig.set(p(uuid) + ".data." + jobId + ".level", 1);
|
||||||
|
playerJobsConfig.set(p(uuid) + ".data." + jobId + ".xp", 0);
|
||||||
|
}
|
||||||
|
savePlayerJobs();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spieler verlässt einen Job. Level & XP bleiben erhalten.
|
||||||
|
* @return false wenn er den Job nicht hatte.
|
||||||
|
*/
|
||||||
|
public boolean leaveJob(UUID uuid, String jobId) {
|
||||||
|
jobId = jobId.toLowerCase();
|
||||||
|
List<String> jobs = new ArrayList<>(getPlayerJobs(uuid));
|
||||||
|
if (!jobs.remove(jobId)) return false;
|
||||||
|
playerJobsConfig.set(p(uuid) + ".jobs", jobs);
|
||||||
|
savePlayerJobs();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XP hinzufügen, Level-Aufstiege werden automatisch verarbeitet.
|
||||||
|
* @return true wenn ein Level-Aufstieg stattfand.
|
||||||
|
*/
|
||||||
|
public boolean addXp(UUID uuid, String jobId, int xpGained) {
|
||||||
|
int level = getLevel(uuid, jobId);
|
||||||
|
if (level >= maxLevel) return false;
|
||||||
|
|
||||||
|
int xp = getXp(uuid, jobId) + xpGained;
|
||||||
|
int required = getXpRequired(jobId, level);
|
||||||
|
boolean leveledUp = false;
|
||||||
|
|
||||||
|
while (xp >= required && level < maxLevel) {
|
||||||
|
xp -= required;
|
||||||
|
level++;
|
||||||
|
leveledUp = true;
|
||||||
|
required = getXpRequired(jobId, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerJobsConfig.set(p(uuid) + ".data." + jobId + ".xp", xp);
|
||||||
|
playerJobsConfig.set(p(uuid) + ".data." + jobId + ".level", level);
|
||||||
|
savePlayerJobs();
|
||||||
|
return leveledUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileConfiguration getJobsConfig() { return jobsConfig; }
|
||||||
|
}
|
||||||
@@ -14,29 +14,27 @@ import org.bukkit.event.Listener;
|
|||||||
import org.bukkit.event.block.SignChangeEvent;
|
import org.bukkit.event.block.SignChangeEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
|
||||||
|
|
||||||
public class SignShopListener implements Listener {
|
public class SignShopListener implements Listener {
|
||||||
|
|
||||||
private final SurvivalPlus plugin;
|
private final SurvivalPlus plugin;
|
||||||
private Economy economy;
|
// Economy wird zentral aus dem Plugin geholt – keine eigene Initialisierung nötig
|
||||||
|
|
||||||
public SignShopListener(SurvivalPlus plugin) {
|
public SignShopListener(SurvivalPlus plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
try {
|
// Kein eigener Vault-Setup hier! Economy kommt über plugin.getEconomy()
|
||||||
if (plugin.getServer().getPluginManager().isPluginEnabled("Vault")) {
|
|
||||||
RegisteredServiceProvider<Economy> rsp = plugin.getServer().getServicesManager().getRegistration(Economy.class);
|
|
||||||
if (rsp != null) {
|
|
||||||
this.economy = rsp.getProvider();
|
|
||||||
plugin.getLogger().info("Vault Economy für Sign Shops gefunden.");
|
|
||||||
} else {
|
|
||||||
plugin.getLogger().warning("Vault Economy Service nicht gefunden!");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
plugin.getLogger().warning("Vault Plugin nicht installiert! Sign Shops funktionieren nicht.");
|
/**
|
||||||
}
|
* Hilfsmethode: Economy aus Plugin holen mit Null-Check und Fehlermeldung.
|
||||||
} catch (Exception e) {
|
* Gibt null zurück wenn Economy nicht verfügbar ist.
|
||||||
plugin.getLogger().warning("Fehler beim Laden der Vault API.");
|
*/
|
||||||
|
private Economy getEconomy(Player player) {
|
||||||
|
Economy economy = plugin.getEconomy();
|
||||||
|
if (economy == null) {
|
||||||
|
player.sendMessage(ChatColor.RED + "Shop System offline (Vault Economy fehlt).");
|
||||||
}
|
}
|
||||||
|
return economy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Schilderstellung ---
|
// --- Schilderstellung ---
|
||||||
@@ -50,13 +48,14 @@ public class SignShopListener implements Listener {
|
|||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
if (!player.hasPermission("survivalplus.shop.create")) {
|
if (!player.hasPermission("survivalplus.shop.create")) {
|
||||||
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu erstellen.");
|
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu erstellen.");
|
||||||
|
event.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String line2 = event.getLine(1);
|
String line1 = event.getLine(1); // "64 Diamond"
|
||||||
String line3 = event.getLine(2);
|
String line2 = event.getLine(2); // "500"
|
||||||
|
|
||||||
String[] parts = line2.split(" ");
|
String[] parts = line1.split(" ");
|
||||||
if (parts.length < 2) {
|
if (parts.length < 2) {
|
||||||
player.sendMessage(ChatColor.RED + "Format in Zeile 2 falsch! Beispiel: 64 Diamond");
|
player.sendMessage(ChatColor.RED + "Format in Zeile 2 falsch! Beispiel: 64 Diamond");
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
@@ -70,22 +69,22 @@ public class SignShopListener implements Listener {
|
|||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Double.parseDouble(line3);
|
Double.parseDouble(line2);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
player.sendMessage(ChatColor.RED + "Fehler im Format! Benutze: [Buy] \n 64 Diamond \n 500");
|
player.sendMessage(ChatColor.RED + "Fehler im Format! Benutze: [Buy] / 64 Diamond / 500");
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NEU: Schild farbig machen ---
|
// Schild farbig formatieren
|
||||||
if (line0Raw.equalsIgnoreCase("[buy]")) {
|
if (line0Raw.equalsIgnoreCase("[buy]")) {
|
||||||
event.setLine(0, ChatColor.GREEN + "[BUY]");
|
event.setLine(0, ChatColor.GREEN + "[BUY]");
|
||||||
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
|
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
|
||||||
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
|
event.setLine(2, ChatColor.GOLD + line2 + " Coins");
|
||||||
} else if (line0Raw.equalsIgnoreCase("[sell]")) {
|
} else {
|
||||||
event.setLine(0, ChatColor.RED + "[SELL]");
|
event.setLine(0, ChatColor.RED + "[SELL]");
|
||||||
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
|
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
|
||||||
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
|
event.setLine(2, ChatColor.GOLD + line2 + " Coins");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.getLine(3) != null && !event.getLine(3).isEmpty()) {
|
if (event.getLine(3) != null && !event.getLine(3).isEmpty()) {
|
||||||
@@ -97,23 +96,14 @@ public class SignShopListener implements Listener {
|
|||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||||
Block block = event.getClickedBlock();
|
Block block = event.getClickedBlock();
|
||||||
if (block == null || !(block.getState() instanceof Sign)) {
|
if (block == null || !(block.getState() instanceof Sign)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sign sign = (Sign) block.getState();
|
Sign sign = (Sign) block.getState();
|
||||||
String line0 = ChatColor.stripColor(sign.getLine(0));
|
String line0 = ChatColor.stripColor(sign.getLine(0));
|
||||||
|
|
||||||
if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) {
|
if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (economy == null) {
|
// SHIFT+KLICK: Shop entfernen
|
||||||
event.getPlayer().sendMessage(ChatColor.RED + "Shop System offline (Vault fehlt).");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- FEATURE: SHIFT+KLICK LOGIK (LÖSCHEN) ---
|
|
||||||
if (event.getPlayer().isSneaking()) {
|
if (event.getPlayer().isSneaking()) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
if (!player.hasPermission("survivalplus.shop.create")) {
|
if (!player.hasPermission("survivalplus.shop.create")) {
|
||||||
@@ -127,23 +117,25 @@ public class SignShopListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- ROUTING ---
|
|
||||||
if (line0.equalsIgnoreCase("[buy]")) {
|
if (line0.equalsIgnoreCase("[buy]")) {
|
||||||
handleBuy(event, sign);
|
handleBuy(event, sign);
|
||||||
} else if (line0.equalsIgnoreCase("[sell]")) {
|
} else {
|
||||||
handleSell(event, sign);
|
handleSell(event, sign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- KAUFS LOGIK (ROBUST) ---
|
// --- Kaufs-Logik ---
|
||||||
private void handleBuy(PlayerInteractEvent event, Sign sign) {
|
private void handleBuy(PlayerInteractEvent event, Sign sign) {
|
||||||
event.setCancelled(true); // Interaktion stoppen
|
event.setCancelled(true);
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
String line1Raw = ChatColor.stripColor(sign.getLine(1)); // "64 Diamond"
|
|
||||||
String line2Raw = ChatColor.stripColor(sign.getLine(2)); // "500" oder "500 Coins"
|
|
||||||
|
|
||||||
// 1. Validierung: Zeilen leer?
|
// Economy zentral aus Plugin holen
|
||||||
|
Economy economy = getEconomy(player);
|
||||||
|
if (economy == null) return;
|
||||||
|
|
||||||
|
String line1Raw = ChatColor.stripColor(sign.getLine(1));
|
||||||
|
String line2Raw = ChatColor.stripColor(sign.getLine(2));
|
||||||
|
|
||||||
if (line1Raw == null || line1Raw.isEmpty()) {
|
if (line1Raw == null || line1Raw.isEmpty()) {
|
||||||
player.sendMessage(ChatColor.RED + "Zeile 1 des Schildes ist leer! (Format: 64 Diamond)");
|
player.sendMessage(ChatColor.RED + "Zeile 1 des Schildes ist leer! (Format: 64 Diamond)");
|
||||||
return;
|
return;
|
||||||
@@ -153,92 +145,68 @@ public class SignShopListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Parsing: Zeile 1 "64 Diamond"
|
String[] parts = line1Raw.split(" ");
|
||||||
String[] partsRaw = line1Raw.split(" ");
|
if (parts.length < 2) {
|
||||||
if (partsRaw.length < 2) {
|
player.sendMessage(ChatColor.RED + "Falsches Format! (Format: 64 Diamond)");
|
||||||
player.sendMessage(ChatColor.RED + "Falsches Format! Benutze: [Buy] \n 64 Diamond");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String amountStr = partsRaw[0].trim(); // "64"
|
|
||||||
String itemNameStr = partsRaw[1].trim(); // "Diamond"
|
|
||||||
|
|
||||||
if (amountStr.isEmpty() || itemNameStr.isEmpty()) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Format Fehler!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int amount;
|
int amount;
|
||||||
Material material;
|
Material material;
|
||||||
try {
|
try {
|
||||||
amount = Integer.parseInt(amountStr);
|
amount = Integer.parseInt(parts[0].trim());
|
||||||
material = Material.matchMaterial(itemNameStr);
|
material = Material.matchMaterial(parts[1].trim());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
player.sendMessage(ChatColor.RED + "Angebot '" + amountStr + "' ist keine Zahl! (Beispiel: 64)");
|
player.sendMessage(ChatColor.RED + "Menge '" + parts[0] + "' ist keine Zahl!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (material == null) {
|
if (material == null) {
|
||||||
player.sendMessage(ChatColor.RED + "Item '" + itemNameStr + "' existiert nicht!");
|
player.sendMessage(ChatColor.RED + "Item '" + parts[1] + "' existiert nicht!");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Parsing: Zeile 2 "500 Coins" (Hier passierte der Fehler!)
|
|
||||||
String[] priceParts = line2Raw.split(" ");
|
|
||||||
if (priceParts.length == 0) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Beispiel: 500)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String priceStr = priceParts[0].trim();
|
|
||||||
if (priceStr.isEmpty()) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Kein Preis gefunden! (Beispiel: 500)");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preis parsen ("500 Coins" → "500")
|
||||||
double price;
|
double price;
|
||||||
try {
|
try {
|
||||||
price = Double.parseDouble(priceStr);
|
price = Double.parseDouble(line2Raw.split(" ")[0].trim());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl! (Beispiel: 500)");
|
player.sendMessage(ChatColor.RED + "Preis '" + line2Raw + "' ist keine gültige Zahl!");
|
||||||
return;
|
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Fehlerhafter Preis! (Beispiel: 500)");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount <= 0 || price < 0) {
|
if (amount <= 0 || price < 0) {
|
||||||
player.sendMessage(ChatColor.RED + "Negative Werte sind nicht erlaubt.");
|
player.sendMessage(ChatColor.RED + "Ungültige Werte auf dem Schild.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Transaktion
|
// Genug Geld?
|
||||||
try {
|
if (economy.getBalance(player) < price) {
|
||||||
ItemStack item = new ItemStack(material, amount);
|
player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Du brauchst "
|
||||||
|
+ String.format("%.2f", price) + " Coins.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (economy.getBalance(player) >= price) {
|
// Transaktion
|
||||||
EconomyResponse response = economy.withdrawPlayer(player, price);
|
EconomyResponse response = economy.withdrawPlayer(player, price);
|
||||||
if (response.transactionSuccess()) {
|
if (response.transactionSuccess()) {
|
||||||
player.getInventory().addItem(item);
|
player.getInventory().addItem(new ItemStack(material, amount));
|
||||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " gekauft!");
|
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x "
|
||||||
|
+ material.name().toLowerCase() + " für " + String.format("%.2f", price) + " Coins gekauft!");
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion.");
|
player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion: " + response.errorMessage);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Du brauchst " + price + ".");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Ein Fehler ist beim Lesen des Shops aufgetreten.");
|
|
||||||
// Loggen für Debugging, falls Vault spinnt
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VERKAUFS LOGIK ---
|
// --- Verkaufs-Logik ---
|
||||||
private void handleSell(PlayerInteractEvent event, Sign sign) {
|
private void handleSell(PlayerInteractEvent event, Sign sign) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
// Economy zentral aus Plugin holen
|
||||||
|
Economy economy = getEconomy(player);
|
||||||
|
if (economy == null) return;
|
||||||
|
|
||||||
String line1Raw = ChatColor.stripColor(sign.getLine(1));
|
String line1Raw = ChatColor.stripColor(sign.getLine(1));
|
||||||
String line2Raw = ChatColor.stripColor(sign.getLine(2));
|
String line2Raw = ChatColor.stripColor(sign.getLine(2));
|
||||||
|
|
||||||
@@ -247,24 +215,21 @@ public class SignShopListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (line2Raw == null || line2Raw.isEmpty()) {
|
if (line2Raw == null || line2Raw.isEmpty()) {
|
||||||
player.sendMessage(ChatColor.RED + "Zeile 2 ist leer!");
|
player.sendMessage(ChatColor.RED + "Preiszeile ist leer!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] partsRaw = line1Raw.split(" ");
|
String[] parts = line1Raw.split(" ");
|
||||||
if (partsRaw.length < 2) {
|
if (parts.length < 2) {
|
||||||
player.sendMessage(ChatColor.RED + "Format falsch! Benutze: [Sell] \n 64 Diamond");
|
player.sendMessage(ChatColor.RED + "Format falsch! (Format: 64 Diamond)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String amountStr = partsRaw[0].trim();
|
|
||||||
String itemNameStr = partsRaw[1].trim();
|
|
||||||
|
|
||||||
int amount;
|
int amount;
|
||||||
Material material;
|
Material material;
|
||||||
try {
|
try {
|
||||||
amount = Integer.parseInt(amountStr);
|
amount = Integer.parseInt(parts[0].trim());
|
||||||
material = Material.matchMaterial(itemNameStr);
|
material = Material.matchMaterial(parts[1].trim());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
player.sendMessage(ChatColor.RED + "Menge ist keine Zahl!");
|
player.sendMessage(ChatColor.RED + "Menge ist keine Zahl!");
|
||||||
return;
|
return;
|
||||||
@@ -275,50 +240,46 @@ public class SignShopListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preis Parsing (wie im Kauf, nur sicherer)
|
|
||||||
String[] priceParts = line2Raw.split(" ");
|
|
||||||
if (priceParts.length == 0) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Preiszeile ist leer!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String priceStr = priceParts[0].trim();
|
|
||||||
if (priceStr.isEmpty()) {
|
|
||||||
player.sendMessage(ChatColor.RED + "Kein Preis!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double price;
|
double price;
|
||||||
try {
|
try {
|
||||||
price = Double.parseDouble(priceStr);
|
price = Double.parseDouble(line2Raw.split(" ")[0].trim());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl!");
|
player.sendMessage(ChatColor.RED + "Preis '" + line2Raw + "' ist keine gültige Zahl!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount <= 0 || price < 0) {
|
if (amount <= 0 || price < 0) {
|
||||||
player.sendMessage(ChatColor.RED + "Negativer Wert!");
|
player.sendMessage(ChatColor.RED + "Ungültige Werte auf dem Schild.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemStack tempItem = new ItemStack(material, amount);
|
// Hat der Spieler genug Items?
|
||||||
int hasAmount = 0;
|
int hasAmount = 0;
|
||||||
|
|
||||||
for (ItemStack item : player.getInventory().getStorageContents()) {
|
for (ItemStack item : player.getInventory().getStorageContents()) {
|
||||||
if (item != null && item.isSimilar(tempItem)) {
|
if (item != null && item.isSimilar(new ItemStack(material))) {
|
||||||
hasAmount += item.getAmount();
|
hasAmount += item.getAmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAmount < amount) {
|
if (hasAmount < amount) {
|
||||||
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items!");
|
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items! (Benötigt: " + amount
|
||||||
|
+ ", Vorhanden: " + hasAmount + ")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verkauf durchführen
|
// Items entfernen
|
||||||
player.getInventory().removeItem(new ItemStack(material, amount));
|
player.getInventory().removeItem(new ItemStack(material, amount));
|
||||||
economy.depositPlayer(player, price);
|
|
||||||
player.updateInventory();
|
|
||||||
|
|
||||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " Coins verkauft!");
|
// Geld gutschreiben
|
||||||
|
EconomyResponse response = economy.depositPlayer(player, price);
|
||||||
|
if (response.transactionSuccess()) {
|
||||||
|
player.updateInventory();
|
||||||
|
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x "
|
||||||
|
+ material.name().toLowerCase() + " für " + String.format("%.2f", price) + " Coins verkauft!");
|
||||||
|
} else {
|
||||||
|
// Rollback: Items zurückgeben
|
||||||
|
player.getInventory().addItem(new ItemStack(material, amount));
|
||||||
|
player.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung: " + response.errorMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,11 @@ blocks:
|
|||||||
# Sollen alle Spieler beim Joinen automatisch in Survival gesetzt werden?
|
# Sollen alle Spieler beim Joinen automatisch in Survival gesetzt werden?
|
||||||
force-survival: true
|
force-survival: true
|
||||||
|
|
||||||
|
economy:
|
||||||
|
start-balance: 500.0
|
||||||
|
currency-singular: "$"
|
||||||
|
currency-plural: "$"
|
||||||
|
|
||||||
# Warp Default Item
|
# Warp Default Item
|
||||||
defaultWarpItem: OAK_SIGN
|
defaultWarpItem: OAK_SIGN
|
||||||
# Anzahl der erlaubten Warps für Member
|
# Anzahl der erlaubten Warps für Member
|
||||||
|
|||||||
575
src/main/resources/jobs.yml
Normal file
575
src/main/resources/jobs.yml
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
# =============================================
|
||||||
|
# SurvivalPlus - Jobs Konfiguration
|
||||||
|
# =============================================
|
||||||
|
|
||||||
|
settings:
|
||||||
|
max-jobs: 2
|
||||||
|
max-level: 10
|
||||||
|
level-multiplier: 0.1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
miner:
|
||||||
|
name: "Miner"
|
||||||
|
display: "⛏ Miner"
|
||||||
|
description: "Baue Erze und Steine ab und verdiene Coins."
|
||||||
|
xp-base: 100
|
||||||
|
actions:
|
||||||
|
# Kohle
|
||||||
|
COAL_ORE: { money: 1.0, xp: 3 }
|
||||||
|
DEEPSLATE_COAL_ORE: { money: 1.0, xp: 3 }
|
||||||
|
# Eisen
|
||||||
|
IRON_ORE: { money: 2.5, xp: 6 }
|
||||||
|
DEEPSLATE_IRON_ORE: { money: 2.5, xp: 6 }
|
||||||
|
RAW_IRON_BLOCK: { money: 5.0, xp: 10 }
|
||||||
|
# Kupfer
|
||||||
|
COPPER_ORE: { money: 2.0, xp: 5 }
|
||||||
|
DEEPSLATE_COPPER_ORE: { money: 2.0, xp: 5 }
|
||||||
|
RAW_COPPER_BLOCK: { money: 4.0, xp: 8 }
|
||||||
|
# Gold
|
||||||
|
GOLD_ORE: { money: 4.0, xp: 10 }
|
||||||
|
DEEPSLATE_GOLD_ORE: { money: 4.0, xp: 10 }
|
||||||
|
NETHER_GOLD_ORE: { money: 3.5, xp: 9 }
|
||||||
|
RAW_GOLD_BLOCK: { money: 8.0, xp: 18 }
|
||||||
|
# Lapis
|
||||||
|
LAPIS_ORE: { money: 3.0, xp: 8 }
|
||||||
|
DEEPSLATE_LAPIS_ORE: { money: 3.0, xp: 8 }
|
||||||
|
# Redstone
|
||||||
|
REDSTONE_ORE: { money: 3.0, xp: 8 }
|
||||||
|
DEEPSLATE_REDSTONE_ORE: { money: 3.0, xp: 8 }
|
||||||
|
# Smaragd
|
||||||
|
EMERALD_ORE: { money: 8.0, xp: 18 }
|
||||||
|
DEEPSLATE_EMERALD_ORE: { money: 8.0, xp: 18 }
|
||||||
|
# Diamant
|
||||||
|
DIAMOND_ORE: { money: 12.0, xp: 25 }
|
||||||
|
DEEPSLATE_DIAMOND_ORE: { money: 12.0, xp: 25 }
|
||||||
|
# Nether
|
||||||
|
NETHER_QUARTZ_ORE: { money: 2.0, xp: 5 }
|
||||||
|
ANCIENT_DEBRIS: { money: 25.0, xp: 50 }
|
||||||
|
# Gewöhnliche Steine / Deepslate
|
||||||
|
STONE: { money: 0.1, xp: 1 }
|
||||||
|
COBBLESTONE: { money: 0.1, xp: 1 }
|
||||||
|
DEEPSLATE: { money: 0.15, xp: 1 }
|
||||||
|
COBBLED_DEEPSLATE: { money: 0.15, xp: 1 }
|
||||||
|
GRANITE: { money: 0.1, xp: 1 }
|
||||||
|
DIORITE: { money: 0.1, xp: 1 }
|
||||||
|
ANDESITE: { money: 0.1, xp: 1 }
|
||||||
|
TUFF: { money: 0.1, xp: 1 }
|
||||||
|
CALCITE: { money: 0.1, xp: 1 }
|
||||||
|
DRIPSTONE_BLOCK: { money: 0.1, xp: 1 }
|
||||||
|
POINTED_DRIPSTONE: { money: 0.1, xp: 1 }
|
||||||
|
# Erde / Sand
|
||||||
|
GRAVEL: { money: 0.1, xp: 1 }
|
||||||
|
SAND: { money: 0.1, xp: 1 }
|
||||||
|
RED_SAND: { money: 0.1, xp: 1 }
|
||||||
|
CLAY: { money: 0.2, xp: 2 }
|
||||||
|
# Nether-Steine
|
||||||
|
NETHERRACK: { money: 0.1, xp: 1 }
|
||||||
|
NETHER_BRICKS: { money: 0.2, xp: 1 }
|
||||||
|
BASALT: { money: 0.1, xp: 1 }
|
||||||
|
BLACKSTONE: { money: 0.2, xp: 2 }
|
||||||
|
MAGMA_BLOCK: { money: 0.3, xp: 2 }
|
||||||
|
SOUL_SAND: { money: 0.2, xp: 2 }
|
||||||
|
SOUL_SOIL: { money: 0.2, xp: 2 }
|
||||||
|
# End
|
||||||
|
END_STONE: { money: 0.3, xp: 2 }
|
||||||
|
OBSIDIAN: { money: 1.0, xp: 5 }
|
||||||
|
CRYING_OBSIDIAN: { money: 1.5, xp: 6 }
|
||||||
|
# Eis
|
||||||
|
ICE: { money: 0.2, xp: 1 }
|
||||||
|
PACKED_ICE: { money: 0.3, xp: 2 }
|
||||||
|
BLUE_ICE: { money: 0.5, xp: 3 }
|
||||||
|
# Sonstiges
|
||||||
|
AMETHYST_CLUSTER: { money: 2.0, xp: 5 }
|
||||||
|
BUDDING_AMETHYST: { money: 3.0, xp: 7 }
|
||||||
|
MOSS_BLOCK: { money: 0.3, xp: 2 }
|
||||||
|
SCULK: { money: 0.3, xp: 2 }
|
||||||
|
SCULK_VEIN: { money: 0.2, xp: 1 }
|
||||||
|
SCULK_CATALYST: { money: 2.0, xp: 8 }
|
||||||
|
SCULK_SHRIEKER: { money: 3.0, xp: 10 }
|
||||||
|
SCULK_SENSOR: { money: 2.5, xp: 8 }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
farmer:
|
||||||
|
name: "Farmer"
|
||||||
|
display: "🌾 Farmer"
|
||||||
|
description: "Ernte Crops und sammle Naturmaterialien."
|
||||||
|
xp-base: 80
|
||||||
|
actions:
|
||||||
|
# Anbaubare Crops (nur vollgewachsen)
|
||||||
|
WHEAT: { money: 0.8, xp: 3 }
|
||||||
|
POTATOES: { money: 0.8, xp: 3 }
|
||||||
|
CARROTS: { money: 0.8, xp: 3 }
|
||||||
|
BEETROOTS: { money: 0.8, xp: 3 }
|
||||||
|
NETHER_WART: { money: 1.5, xp: 5 }
|
||||||
|
COCOA: { money: 1.0, xp: 3 }
|
||||||
|
SWEET_BERRY_BUSH: { money: 1.0, xp: 3 }
|
||||||
|
# Abbrechen / Ernten
|
||||||
|
PUMPKIN: { money: 2.0, xp: 6 }
|
||||||
|
MELON: { money: 1.5, xp: 5 }
|
||||||
|
SUGAR_CANE: { money: 0.5, xp: 2 }
|
||||||
|
CACTUS: { money: 0.4, xp: 2 }
|
||||||
|
BAMBOO: { money: 0.3, xp: 1 }
|
||||||
|
KELP: { money: 0.3, xp: 1 }
|
||||||
|
KELP_PLANT: { money: 0.2, xp: 1 }
|
||||||
|
SEA_PICKLE: { money: 0.5, xp: 2 }
|
||||||
|
CHORUS_FLOWER: { money: 1.5, xp: 5 }
|
||||||
|
CHORUS_PLANT: { money: 1.0, xp: 3 }
|
||||||
|
# Pilze
|
||||||
|
BROWN_MUSHROOM: { money: 0.5, xp: 2 }
|
||||||
|
RED_MUSHROOM: { money: 0.5, xp: 2 }
|
||||||
|
BROWN_MUSHROOM_BLOCK: { money: 0.8, xp: 3 }
|
||||||
|
RED_MUSHROOM_BLOCK: { money: 0.8, xp: 3 }
|
||||||
|
MUSHROOM_STEM: { money: 0.6, xp: 2 }
|
||||||
|
CRIMSON_FUNGUS: { money: 0.8, xp: 3 }
|
||||||
|
WARPED_FUNGUS: { money: 0.8, xp: 3 }
|
||||||
|
# Blumen & Dekor-Pflanzen
|
||||||
|
DANDELION: { money: 0.2, xp: 1 }
|
||||||
|
POPPY: { money: 0.2, xp: 1 }
|
||||||
|
BLUE_ORCHID: { money: 0.3, xp: 1 }
|
||||||
|
ALLIUM: { money: 0.3, xp: 1 }
|
||||||
|
AZURE_BLUET: { money: 0.3, xp: 1 }
|
||||||
|
RED_TULIP: { money: 0.3, xp: 1 }
|
||||||
|
ORANGE_TULIP: { money: 0.3, xp: 1 }
|
||||||
|
WHITE_TULIP: { money: 0.3, xp: 1 }
|
||||||
|
PINK_TULIP: { money: 0.3, xp: 1 }
|
||||||
|
OXEYE_DAISY: { money: 0.3, xp: 1 }
|
||||||
|
CORNFLOWER: { money: 0.3, xp: 1 }
|
||||||
|
LILY_OF_THE_VALLEY: { money: 0.3, xp: 1 }
|
||||||
|
WITHER_ROSE: { money: 1.0, xp: 4 }
|
||||||
|
SUNFLOWER: { money: 0.4, xp: 2 }
|
||||||
|
LILAC: { money: 0.4, xp: 2 }
|
||||||
|
ROSE_BUSH: { money: 0.4, xp: 2 }
|
||||||
|
PEONY: { money: 0.4, xp: 2 }
|
||||||
|
PINK_PETALS: { money: 0.3, xp: 1 }
|
||||||
|
TORCHFLOWER: { money: 1.5, xp: 6 }
|
||||||
|
PITCHER_PLANT: { money: 1.5, xp: 6 }
|
||||||
|
SPORE_BLOSSOM: { money: 0.8, xp: 3 }
|
||||||
|
# Gras & Farne
|
||||||
|
GRASS: { money: 0.1, xp: 1 }
|
||||||
|
TALL_GRASS: { money: 0.1, xp: 1 }
|
||||||
|
FERN: { money: 0.1, xp: 1 }
|
||||||
|
LARGE_FERN: { money: 0.1, xp: 1 }
|
||||||
|
DEAD_BUSH: { money: 0.1, xp: 1 }
|
||||||
|
HANGING_ROOTS: { money: 0.2, xp: 1 }
|
||||||
|
GLOW_BERRIES: { money: 0.8, xp: 3 }
|
||||||
|
CAVE_VINES: { money: 0.5, xp: 2 }
|
||||||
|
CAVE_VINES_PLANT: { money: 0.3, xp: 1 }
|
||||||
|
VINE: { money: 0.2, xp: 1 }
|
||||||
|
LILY_PAD: { money: 0.2, xp: 1 }
|
||||||
|
MOSS_CARPET: { money: 0.2, xp: 1 }
|
||||||
|
# Nether-Pflanzen
|
||||||
|
CRIMSON_ROOTS: { money: 0.3, xp: 1 }
|
||||||
|
WARPED_ROOTS: { money: 0.3, xp: 1 }
|
||||||
|
NETHER_SPROUTS: { money: 0.2, xp: 1 }
|
||||||
|
WEEPING_VINES: { money: 0.3, xp: 1 }
|
||||||
|
TWISTING_VINES: { money: 0.3, xp: 1 }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
hunter:
|
||||||
|
name: "Hunter"
|
||||||
|
display: "⚔ Hunter"
|
||||||
|
description: "Töte Mobs und verdiene Coins."
|
||||||
|
xp-base: 120
|
||||||
|
actions:
|
||||||
|
# Overworld – Standard
|
||||||
|
ZOMBIE: { money: 1.5, xp: 4 }
|
||||||
|
ZOMBIE_VILLAGER: { money: 2.0, xp: 5 }
|
||||||
|
ZOMBIFIED_PIGLIN: { money: 2.0, xp: 5 }
|
||||||
|
HUSK: { money: 1.5, xp: 4 }
|
||||||
|
DROWNED: { money: 1.5, xp: 4 }
|
||||||
|
SKELETON: { money: 1.5, xp: 4 }
|
||||||
|
STRAY: { money: 2.0, xp: 5 }
|
||||||
|
CREEPER: { money: 2.0, xp: 6 }
|
||||||
|
SPIDER: { money: 1.0, xp: 3 }
|
||||||
|
CAVE_SPIDER: { money: 1.5, xp: 4 }
|
||||||
|
WITCH: { money: 3.0, xp: 8 }
|
||||||
|
SLIME: { money: 1.0, xp: 3 }
|
||||||
|
ENDERMAN: { money: 3.0, xp: 8 }
|
||||||
|
ENDERMITE: { money: 0.8, xp: 2 }
|
||||||
|
SILVERFISH: { money: 0.5, xp: 2 }
|
||||||
|
PHANTOM: { money: 2.5, xp: 7 }
|
||||||
|
# Overworld – Raider
|
||||||
|
PILLAGER: { money: 3.0, xp: 8 }
|
||||||
|
VINDICATOR: { money: 3.5, xp: 9 }
|
||||||
|
EVOKER: { money: 5.0, xp: 12 }
|
||||||
|
RAVAGER: { money: 8.0, xp: 20 }
|
||||||
|
VEX: { money: 1.5, xp: 4 }
|
||||||
|
# Overworld – Wasser
|
||||||
|
GUARDIAN: { money: 4.0, xp: 10 }
|
||||||
|
ELDER_GUARDIAN: { money: 10.0, xp: 25 }
|
||||||
|
# Nether
|
||||||
|
BLAZE: { money: 3.5, xp: 9 }
|
||||||
|
WITHER_SKELETON: { money: 4.0, xp: 10 }
|
||||||
|
GHAST: { money: 4.0, xp: 10 }
|
||||||
|
MAGMA_CUBE: { money: 1.5, xp: 4 }
|
||||||
|
PIGLIN: { money: 2.0, xp: 5 }
|
||||||
|
PIGLIN_BRUTE: { money: 4.0, xp: 10 }
|
||||||
|
HOGLIN: { money: 3.0, xp: 8 }
|
||||||
|
ZOGLIN: { money: 3.5, xp: 9 }
|
||||||
|
STRIDER: { money: 2.0, xp: 5 }
|
||||||
|
# End
|
||||||
|
SHULKER: { money: 4.0, xp: 10 }
|
||||||
|
# Bosses
|
||||||
|
WITHER: { money: 50.0, xp: 100 }
|
||||||
|
# Neutrale / Passive die angegriffen werden können
|
||||||
|
WOLF: { money: 0.5, xp: 2 }
|
||||||
|
POLAR_BEAR: { money: 2.0, xp: 5 }
|
||||||
|
PANDA: { money: 1.5, xp: 4 }
|
||||||
|
BEE: { money: 0.5, xp: 2 }
|
||||||
|
LLAMA: { money: 1.0, xp: 3 }
|
||||||
|
TRADER_LLAMA: { money: 1.0, xp: 3 }
|
||||||
|
DOLPHIN: { money: 1.5, xp: 4 }
|
||||||
|
IRON_GOLEM: { money: 5.0, xp: 12 }
|
||||||
|
SNOW_GOLEM: { money: 1.0, xp: 3 }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
woodcutter:
|
||||||
|
name: "Woodcutter"
|
||||||
|
display: "🪓 Woodcutter"
|
||||||
|
description: "Fälle Bäume und verdiene Coins."
|
||||||
|
xp-base: 80
|
||||||
|
actions:
|
||||||
|
# Normale Stämme
|
||||||
|
OAK_LOG: { money: 0.5, xp: 2 }
|
||||||
|
SPRUCE_LOG: { money: 0.5, xp: 2 }
|
||||||
|
BIRCH_LOG: { money: 0.5, xp: 2 }
|
||||||
|
JUNGLE_LOG: { money: 0.6, xp: 2 }
|
||||||
|
ACACIA_LOG: { money: 0.5, xp: 2 }
|
||||||
|
DARK_OAK_LOG: { money: 0.6, xp: 2 }
|
||||||
|
MANGROVE_LOG: { money: 0.7, xp: 3 }
|
||||||
|
CHERRY_LOG: { money: 0.8, xp: 3 }
|
||||||
|
CRIMSON_STEM: { money: 0.8, xp: 3 }
|
||||||
|
WARPED_STEM: { money: 0.8, xp: 3 }
|
||||||
|
# Entrindete Stämme
|
||||||
|
STRIPPED_OAK_LOG: { money: 0.6, xp: 2 }
|
||||||
|
STRIPPED_SPRUCE_LOG: { money: 0.6, xp: 2 }
|
||||||
|
STRIPPED_BIRCH_LOG: { money: 0.6, xp: 2 }
|
||||||
|
STRIPPED_JUNGLE_LOG: { money: 0.7, xp: 2 }
|
||||||
|
STRIPPED_ACACIA_LOG: { money: 0.6, xp: 2 }
|
||||||
|
STRIPPED_DARK_OAK_LOG: { money: 0.7, xp: 2 }
|
||||||
|
STRIPPED_MANGROVE_LOG: { money: 0.8, xp: 3 }
|
||||||
|
STRIPPED_CHERRY_LOG: { money: 0.9, xp: 3 }
|
||||||
|
STRIPPED_CRIMSON_STEM: { money: 0.9, xp: 3 }
|
||||||
|
STRIPPED_WARPED_STEM: { money: 0.9, xp: 3 }
|
||||||
|
# Holz-Blöcke (2 Stämme kombiniert)
|
||||||
|
OAK_WOOD: { money: 0.5, xp: 2 }
|
||||||
|
SPRUCE_WOOD: { money: 0.5, xp: 2 }
|
||||||
|
BIRCH_WOOD: { money: 0.5, xp: 2 }
|
||||||
|
JUNGLE_WOOD: { money: 0.6, xp: 2 }
|
||||||
|
ACACIA_WOOD: { money: 0.5, xp: 2 }
|
||||||
|
DARK_OAK_WOOD: { money: 0.6, xp: 2 }
|
||||||
|
MANGROVE_WOOD: { money: 0.7, xp: 3 }
|
||||||
|
CHERRY_WOOD: { money: 0.8, xp: 3 }
|
||||||
|
CRIMSON_HYPHAE: { money: 0.8, xp: 3 }
|
||||||
|
WARPED_HYPHAE: { money: 0.8, xp: 3 }
|
||||||
|
# Blätter
|
||||||
|
OAK_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
SPRUCE_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
BIRCH_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
JUNGLE_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
ACACIA_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
DARK_OAK_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
MANGROVE_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
CHERRY_LEAVES: { money: 0.1, xp: 1 }
|
||||||
|
AZALEA_LEAVES: { money: 0.2, xp: 1 }
|
||||||
|
FLOWERING_AZALEA_LEAVES: { money: 0.3, xp: 1 }
|
||||||
|
# Sonstige Holz-artige Blöcke
|
||||||
|
AZALEA: { money: 0.3, xp: 2 }
|
||||||
|
FLOWERING_AZALEA: { money: 0.4, xp: 2 }
|
||||||
|
MANGROVE_ROOTS: { money: 0.3, xp: 1 }
|
||||||
|
MUDDY_MANGROVE_ROOTS: { money: 0.3, xp: 1 }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
fischer:
|
||||||
|
name: "Fischer"
|
||||||
|
display: "🎣 Fischer"
|
||||||
|
description: "Angle Fische und verdiene Coins."
|
||||||
|
xp-base: 90
|
||||||
|
actions:
|
||||||
|
# Fische
|
||||||
|
COD: { money: 1.5, xp: 4 }
|
||||||
|
SALMON: { money: 2.0, xp: 5 }
|
||||||
|
TROPICAL_FISH: { money: 2.5, xp: 6 }
|
||||||
|
PUFFERFISH: { money: 3.0, xp: 7 }
|
||||||
|
# Schätze
|
||||||
|
BOW: { money: 5.0, xp: 12 }
|
||||||
|
FISHING_ROD: { money: 4.0, xp: 10 }
|
||||||
|
NAME_TAG: { money: 8.0, xp: 20 }
|
||||||
|
SADDLE: { money: 6.0, xp: 15 }
|
||||||
|
NAUTILUS_SHELL: { money: 5.0, xp: 12 }
|
||||||
|
ENCHANTED_BOOK: { money: 7.0, xp: 18 }
|
||||||
|
LILY_PAD: { money: 0.5, xp: 2 }
|
||||||
|
LEATHER: { money: 1.0, xp: 3 }
|
||||||
|
INK_SAC: { money: 0.5, xp: 2 }
|
||||||
|
TRIPWIRE_HOOK: { money: 1.0, xp: 3 }
|
||||||
|
ROTTEN_FLESH: { money: 0.3, xp: 1 }
|
||||||
|
STICK: { money: 0.2, xp: 1 }
|
||||||
|
STRING: { money: 0.3, xp: 1 }
|
||||||
|
BONE: { money: 0.5, xp: 2 }
|
||||||
|
BOWL: { money: 0.2, xp: 1 }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
builder:
|
||||||
|
name: "Builder"
|
||||||
|
display: "🏗 Builder"
|
||||||
|
description: "Platziere Blöcke und verdiene Coins."
|
||||||
|
xp-base: 60
|
||||||
|
actions:
|
||||||
|
# Stein-Varianten
|
||||||
|
STONE: { money: 0.2, xp: 1 }
|
||||||
|
COBBLESTONE: { money: 0.2, xp: 1 }
|
||||||
|
MOSSY_COBBLESTONE: { money: 0.3, xp: 1 }
|
||||||
|
STONE_BRICKS: { money: 0.5, xp: 2 }
|
||||||
|
MOSSY_STONE_BRICKS: { money: 0.5, xp: 2 }
|
||||||
|
CHISELED_STONE_BRICKS: { money: 0.6, xp: 2 }
|
||||||
|
CRACKED_STONE_BRICKS: { money: 0.4, xp: 1 }
|
||||||
|
SMOOTH_STONE: { money: 0.3, xp: 1 }
|
||||||
|
STONE_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
STONE_BRICK_SLAB: { money: 0.3, xp: 1 }
|
||||||
|
STONE_STAIRS: { money: 0.3, xp: 1 }
|
||||||
|
STONE_BRICK_STAIRS: { money: 0.4, xp: 1 }
|
||||||
|
STONE_BRICK_WALL: { money: 0.4, xp: 1 }
|
||||||
|
# Granit / Diorit / Andesit
|
||||||
|
GRANITE: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_GRANITE: { money: 0.4, xp: 1 }
|
||||||
|
POLISHED_GRANITE_SLAB: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_GRANITE_STAIRS: { money: 0.3, xp: 1 }
|
||||||
|
DIORITE: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_DIORITE: { money: 0.4, xp: 1 }
|
||||||
|
POLISHED_DIORITE_SLAB: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_DIORITE_STAIRS: { money: 0.3, xp: 1 }
|
||||||
|
ANDESITE: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_ANDESITE: { money: 0.4, xp: 1 }
|
||||||
|
POLISHED_ANDESITE_SLAB: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_ANDESITE_STAIRS: { money: 0.3, xp: 1 }
|
||||||
|
# Deepslate
|
||||||
|
DEEPSLATE: { money: 0.2, xp: 1 }
|
||||||
|
COBBLED_DEEPSLATE: { money: 0.2, xp: 1 }
|
||||||
|
COBBLED_DEEPSLATE_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
COBBLED_DEEPSLATE_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
COBBLED_DEEPSLATE_WALL: { money: 0.2, xp: 1 }
|
||||||
|
POLISHED_DEEPSLATE: { money: 0.4, xp: 1 }
|
||||||
|
POLISHED_DEEPSLATE_SLAB: { money: 0.25, xp: 1 }
|
||||||
|
POLISHED_DEEPSLATE_STAIRS: { money: 0.35, xp: 1 }
|
||||||
|
POLISHED_DEEPSLATE_WALL: { money: 0.3, xp: 1 }
|
||||||
|
DEEPSLATE_BRICKS: { money: 0.6, xp: 2 }
|
||||||
|
DEEPSLATE_BRICK_SLAB: { money: 0.35, xp: 1 }
|
||||||
|
DEEPSLATE_BRICK_STAIRS: { money: 0.45, xp: 1 }
|
||||||
|
DEEPSLATE_BRICK_WALL: { money: 0.4, xp: 1 }
|
||||||
|
DEEPSLATE_TILES: { money: 0.6, xp: 2 }
|
||||||
|
DEEPSLATE_TILE_SLAB: { money: 0.35, xp: 1 }
|
||||||
|
DEEPSLATE_TILE_STAIRS: { money: 0.45, xp: 1 }
|
||||||
|
DEEPSLATE_TILE_WALL: { money: 0.4, xp: 1 }
|
||||||
|
CHISELED_DEEPSLATE: { money: 0.7, xp: 2 }
|
||||||
|
# Ziegel
|
||||||
|
BRICKS: { money: 0.5, xp: 2 }
|
||||||
|
BRICK_SLAB: { money: 0.3, xp: 1 }
|
||||||
|
BRICK_STAIRS: { money: 0.4, xp: 1 }
|
||||||
|
BRICK_WALL: { money: 0.4, xp: 1 }
|
||||||
|
MUD_BRICKS: { money: 0.4, xp: 1 }
|
||||||
|
MUD_BRICK_SLAB: { money: 0.25, xp: 1 }
|
||||||
|
MUD_BRICK_STAIRS: { money: 0.3, xp: 1 }
|
||||||
|
MUD_BRICK_WALL: { money: 0.3, xp: 1 }
|
||||||
|
# Holz-Planken & Stufen
|
||||||
|
OAK_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
SPRUCE_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
BIRCH_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
JUNGLE_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
ACACIA_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
DARK_OAK_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
MANGROVE_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
CHERRY_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
BAMBOO_PLANKS: { money: 0.3, xp: 1 }
|
||||||
|
BAMBOO_MOSAIC: { money: 0.4, xp: 1 }
|
||||||
|
CRIMSON_PLANKS: { money: 0.4, xp: 1 }
|
||||||
|
WARPED_PLANKS: { money: 0.4, xp: 1 }
|
||||||
|
OAK_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
SPRUCE_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
BIRCH_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
JUNGLE_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
ACACIA_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
DARK_OAK_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
MANGROVE_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
CHERRY_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
BAMBOO_SLAB: { money: 0.15, xp: 1 }
|
||||||
|
CRIMSON_SLAB: { money: 0.2, xp: 1 }
|
||||||
|
WARPED_SLAB: { money: 0.2, xp: 1 }
|
||||||
|
OAK_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
SPRUCE_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
BIRCH_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
JUNGLE_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
ACACIA_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
DARK_OAK_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
MANGROVE_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
CHERRY_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
BAMBOO_STAIRS: { money: 0.2, xp: 1 }
|
||||||
|
CRIMSON_STAIRS: { money: 0.25, xp: 1 }
|
||||||
|
WARPED_STAIRS: { money: 0.25, xp: 1 }
|
||||||
|
# Quarz
|
||||||
|
QUARTZ_BLOCK: { money: 0.8, xp: 2 }
|
||||||
|
SMOOTH_QUARTZ: { money: 0.8, xp: 2 }
|
||||||
|
CHISELED_QUARTZ_BLOCK: { money: 1.0, xp: 3 }
|
||||||
|
QUARTZ_PILLAR: { money: 0.9, xp: 2 }
|
||||||
|
QUARTZ_BRICKS: { money: 0.9, xp: 2 }
|
||||||
|
QUARTZ_SLAB: { money: 0.4, xp: 1 }
|
||||||
|
QUARTZ_STAIRS: { money: 0.6, xp: 2 }
|
||||||
|
# Sandstein
|
||||||
|
SANDSTONE: { money: 0.4, xp: 1 }
|
||||||
|
SMOOTH_SANDSTONE: { money: 0.5, xp: 2 }
|
||||||
|
CHISELED_SANDSTONE: { money: 0.6, xp: 2 }
|
||||||
|
CUT_SANDSTONE: { money: 0.5, xp: 2 }
|
||||||
|
SANDSTONE_SLAB: { money: 0.25, xp: 1 }
|
||||||
|
SANDSTONE_STAIRS: { money: 0.35, xp: 1 }
|
||||||
|
SANDSTONE_WALL: { money: 0.3, xp: 1 }
|
||||||
|
RED_SANDSTONE: { money: 0.4, xp: 1 }
|
||||||
|
SMOOTH_RED_SANDSTONE: { money: 0.5, xp: 2 }
|
||||||
|
CHISELED_RED_SANDSTONE: { money: 0.6, xp: 2 }
|
||||||
|
CUT_RED_SANDSTONE: { money: 0.5, xp: 2 }
|
||||||
|
RED_SANDSTONE_SLAB: { money: 0.25, xp: 1 }
|
||||||
|
RED_SANDSTONE_STAIRS: { money: 0.35, xp: 1 }
|
||||||
|
RED_SANDSTONE_WALL: { money: 0.3, xp: 1 }
|
||||||
|
# Glas
|
||||||
|
GLASS: { money: 0.4, xp: 1 }
|
||||||
|
GLASS_PANE: { money: 0.2, xp: 1 }
|
||||||
|
WHITE_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
ORANGE_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
MAGENTA_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
LIGHT_BLUE_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
YELLOW_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
LIME_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
PINK_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
GRAY_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
LIGHT_GRAY_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
CYAN_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
PURPLE_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
BLUE_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
BROWN_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
GREEN_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
RED_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
BLACK_STAINED_GLASS: { money: 0.4, xp: 1 }
|
||||||
|
# Terrakotta
|
||||||
|
TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
WHITE_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
ORANGE_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
MAGENTA_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
LIGHT_BLUE_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
YELLOW_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
LIME_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
PINK_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
GRAY_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
LIGHT_GRAY_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
CYAN_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
PURPLE_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
BLUE_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
BROWN_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
GREEN_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
RED_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
BLACK_TERRACOTTA: { money: 0.3, xp: 1 }
|
||||||
|
WHITE_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
ORANGE_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
MAGENTA_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
LIGHT_BLUE_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
YELLOW_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
LIME_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
PINK_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
GRAY_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
LIGHT_GRAY_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
CYAN_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
PURPLE_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
BLUE_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
BROWN_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
GREEN_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
RED_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
BLACK_GLAZED_TERRACOTTA: { money: 0.5, xp: 2 }
|
||||||
|
# Beton
|
||||||
|
WHITE_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
ORANGE_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
MAGENTA_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
LIGHT_BLUE_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
YELLOW_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
LIME_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
PINK_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
GRAY_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
LIGHT_GRAY_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
CYAN_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
PURPLE_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
BLUE_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
BROWN_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
GREEN_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
RED_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
BLACK_CONCRETE: { money: 0.4, xp: 1 }
|
||||||
|
# Wolle
|
||||||
|
WHITE_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
ORANGE_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
MAGENTA_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
LIGHT_BLUE_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
YELLOW_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
LIME_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
PINK_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
GRAY_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
LIGHT_GRAY_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
CYAN_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
PURPLE_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
BLUE_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
BROWN_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
GREEN_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
RED_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
BLACK_WOOL: { money: 0.3, xp: 1 }
|
||||||
|
# Nether-Blöcke
|
||||||
|
NETHER_BRICKS: { money: 0.4, xp: 1 }
|
||||||
|
NETHER_BRICK_SLAB: { money: 0.25, xp: 1 }
|
||||||
|
NETHER_BRICK_STAIRS: { money: 0.35, xp: 1 }
|
||||||
|
NETHER_BRICK_WALL: { money: 0.3, xp: 1 }
|
||||||
|
RED_NETHER_BRICKS: { money: 0.5, xp: 2 }
|
||||||
|
RED_NETHER_BRICK_SLAB: { money: 0.3, xp: 1 }
|
||||||
|
RED_NETHER_BRICK_STAIRS: { money: 0.4, xp: 1 }
|
||||||
|
RED_NETHER_BRICK_WALL: { money: 0.35, xp: 1 }
|
||||||
|
CHISELED_NETHER_BRICKS: { money: 0.6, xp: 2 }
|
||||||
|
CRACKED_NETHER_BRICKS: { money: 0.4, xp: 1 }
|
||||||
|
BLACKSTONE: { money: 0.3, xp: 1 }
|
||||||
|
POLISHED_BLACKSTONE: { money: 0.5, xp: 2 }
|
||||||
|
POLISHED_BLACKSTONE_BRICKS: { money: 0.6, xp: 2 }
|
||||||
|
CHISELED_POLISHED_BLACKSTONE: { money: 0.7, xp: 2 }
|
||||||
|
POLISHED_BLACKSTONE_SLAB: { money: 0.3, xp: 1 }
|
||||||
|
POLISHED_BLACKSTONE_STAIRS: { money: 0.4, xp: 1 }
|
||||||
|
POLISHED_BLACKSTONE_WALL: { money: 0.35, xp: 1 }
|
||||||
|
POLISHED_BLACKSTONE_BRICK_SLAB: { money: 0.35, xp: 1 }
|
||||||
|
POLISHED_BLACKSTONE_BRICK_STAIRS: { money: 0.45, xp: 1 }
|
||||||
|
POLISHED_BLACKSTONE_BRICK_WALL: { money: 0.4, xp: 1 }
|
||||||
|
BASALT: { money: 0.2, xp: 1 }
|
||||||
|
SMOOTH_BASALT: { money: 0.3, xp: 1 }
|
||||||
|
POLISHED_BASALT: { money: 0.4, xp: 1 }
|
||||||
|
# End
|
||||||
|
END_STONE_BRICKS: { money: 0.6, xp: 2 }
|
||||||
|
END_STONE_BRICK_SLAB: { money: 0.35, xp: 1 }
|
||||||
|
END_STONE_BRICK_STAIRS: { money: 0.45, xp: 1 }
|
||||||
|
END_STONE_BRICK_WALL: { money: 0.4, xp: 1 }
|
||||||
|
PURPUR_BLOCK: { money: 0.6, xp: 2 }
|
||||||
|
PURPUR_PILLAR: { money: 0.7, xp: 2 }
|
||||||
|
PURPUR_SLAB: { money: 0.35, xp: 1 }
|
||||||
|
PURPUR_STAIRS: { money: 0.45, xp: 1 }
|
||||||
|
# Spezial-Blöcke
|
||||||
|
OBSIDIAN: { money: 1.5, xp: 5 }
|
||||||
|
CRYING_OBSIDIAN: { money: 2.0, xp: 6 }
|
||||||
|
TUFF: { money: 0.2, xp: 1 }
|
||||||
|
CALCITE: { money: 0.2, xp: 1 }
|
||||||
|
DRIPSTONE_BLOCK: { money: 0.2, xp: 1 }
|
||||||
|
MUD: { money: 0.1, xp: 1 }
|
||||||
|
PACKED_MUD: { money: 0.2, xp: 1 }
|
||||||
|
DIRT: { money: 0.1, xp: 1 }
|
||||||
|
COARSE_DIRT: { money: 0.1, xp: 1 }
|
||||||
|
ROOTED_DIRT: { money: 0.1, xp: 1 }
|
||||||
|
GRASS_BLOCK: { money: 0.1, xp: 1 }
|
||||||
|
MYCELIUM: { money: 0.2, xp: 1 }
|
||||||
|
PODZOL: { money: 0.2, xp: 1 }
|
||||||
|
GRAVEL: { money: 0.1, xp: 1 }
|
||||||
|
SAND: { money: 0.1, xp: 1 }
|
||||||
|
RED_SAND: { money: 0.1, xp: 1 }
|
||||||
|
SOUL_SAND: { money: 0.2, xp: 1 }
|
||||||
|
SOUL_SOIL: { money: 0.2, xp: 1 }
|
||||||
|
NETHERRACK: { money: 0.1, xp: 1 }
|
||||||
|
AMETHYST_BLOCK: { money: 1.0, xp: 3 }
|
||||||
|
BUDDING_AMETHYST: { money: 2.0, xp: 5 }
|
||||||
|
TINTED_GLASS: { money: 0.8, xp: 2 }
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
name: SurvivalPlus
|
name: SurvivalPlus
|
||||||
version: 1.1.3
|
version: 1.1.4
|
||||||
|
|
||||||
main: de.viper.survivalplus.SurvivalPlus
|
main: de.viper.survivalplus.SurvivalPlus
|
||||||
api-version: 1.21
|
api-version: 1.21
|
||||||
softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib, Vault]
|
softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib, Vault]
|
||||||
author: Viper
|
loadbefore: [IngameShopSpigot]
|
||||||
|
author: M_Viper
|
||||||
description: A plugin for enhancing survival gameplay in Minecraft.
|
description: A plugin for enhancing survival gameplay in Minecraft.
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
@@ -292,6 +293,22 @@ commands:
|
|||||||
permission: survivalplus.head
|
permission: survivalplus.head
|
||||||
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
|
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
|
||||||
|
|
||||||
|
# --- NEU: MONEY COMMAND ---
|
||||||
|
money:
|
||||||
|
description: Verwaltet das Guthaben von Spielern.
|
||||||
|
usage: /<command> [pay <Spieler> <Betrag>|set <Spieler> <Betrag>|add <Spieler> <Betrag>|remove <Spieler> <Betrag>|top]
|
||||||
|
aliases: [bal, balance, geld]
|
||||||
|
permission: survivalplus.money
|
||||||
|
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
|
||||||
|
|
||||||
|
# --- NEU: JOB COMMAND ---
|
||||||
|
job:
|
||||||
|
description: Öffnet das Job-System (GUI oder Textbefehle).
|
||||||
|
usage: /<command> [gui|info|join|leave|myjobs] [job]
|
||||||
|
aliases: [jobs]
|
||||||
|
permission: survivalplus.job
|
||||||
|
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
survivalplus.*:
|
survivalplus.*:
|
||||||
description: Gibt Zugriff auf alle SurvivalPlus-Befehle
|
description: Gibt Zugriff auf alle SurvivalPlus-Befehle
|
||||||
@@ -369,6 +386,11 @@ permissions:
|
|||||||
survivalplus.claim.kick: true
|
survivalplus.claim.kick: true
|
||||||
survivalplus.claim.ban: true
|
survivalplus.claim.ban: true
|
||||||
survivalplus.head: true
|
survivalplus.head: true
|
||||||
|
survivalplus.money: true
|
||||||
|
survivalplus.money.admin: true
|
||||||
|
survivalplus.money.pay: true
|
||||||
|
survivalplus.baltop: true
|
||||||
|
survivalplus.job: true
|
||||||
survivalplus.claim.admin: true
|
survivalplus.claim.admin: true
|
||||||
survivalplus.vanish.silent: true
|
survivalplus.vanish.silent: true
|
||||||
survivalplus.vanish.no-pickup: true
|
survivalplus.vanish.no-pickup: true
|
||||||
@@ -597,3 +619,18 @@ permissions:
|
|||||||
survivalplus.head:
|
survivalplus.head:
|
||||||
description: Erlaubt das Holen von Spielerköpfen
|
description: Erlaubt das Holen von Spielerköpfen
|
||||||
default: true
|
default: true
|
||||||
|
survivalplus.job:
|
||||||
|
description: Erlaubt die Nutzung des Job-Systems (/job)
|
||||||
|
default: true
|
||||||
|
survivalplus.money:
|
||||||
|
description: Erlaubt das Anzeigen des eigenen Guthabens mit /money
|
||||||
|
default: true
|
||||||
|
survivalplus.money.pay:
|
||||||
|
description: Erlaubt das Überweisen von Geld an andere Spieler mit /money pay
|
||||||
|
default: true
|
||||||
|
survivalplus.money.admin:
|
||||||
|
description: Erlaubt Admin-Befehle wie /money set, /money add, /money remove
|
||||||
|
default: op
|
||||||
|
survivalplus.baltop:
|
||||||
|
description: Erlaubt das Anzeigen der reichsten Spieler mit /money top
|
||||||
|
default: true
|
||||||
Reference in New Issue
Block a user