Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f5499653a | |||
| f371a9d1d8 |
3
pom.xml
3
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>de.viper</groupId>
|
||||
<artifactId>SurvivalPlus</artifactId>
|
||||
<version>1.1.3</version>
|
||||
<version>1.1.4</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>SurvivalPlus</name>
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
<include>nicknames.yml</include>
|
||||
<include>mobadapt.yml</include>
|
||||
<include>heads.yml</include>
|
||||
<include>jobs.yml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
@@ -34,6 +34,9 @@ import de.viper.survivalplus.commands.TradeCommand;
|
||||
import de.viper.survivalplus.commands.ReportCommand;
|
||||
import de.viper.survivalplus.Manager.ShopManager;
|
||||
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.Manager.TablistManager;
|
||||
import de.viper.survivalplus.Manager.CommandBlocker;
|
||||
@@ -158,6 +161,12 @@ public class SurvivalPlus extends JavaPlugin {
|
||||
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() {
|
||||
if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml");
|
||||
if (!tablistFile.exists()) {
|
||||
@@ -274,17 +283,32 @@ public class SurvivalPlus extends JavaPlugin {
|
||||
reloadBlockedCommandsConfig();
|
||||
loadClaims();
|
||||
|
||||
// --- NEU: Vault Economy Setup ---
|
||||
// --- Economy Setup ---
|
||||
// BalanceManager sofort initialisieren.
|
||||
balanceManager = new de.viper.survivalplus.economy.BalanceManager(this);
|
||||
|
||||
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);
|
||||
if (rsp != null) {
|
||||
// Externes Plugin gefunden – bevorzugen
|
||||
economy = rsp.getProvider();
|
||||
getLogger().info("Vault Economy erfolgreich verbunden.");
|
||||
getLogger().info("Externes Economy-Plugin verbunden: " + economy.getName());
|
||||
} 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();
|
||||
try {
|
||||
@@ -358,7 +382,14 @@ public class SurvivalPlus extends JavaPlugin {
|
||||
getCommand("workbench").setExecutor(new WorkbenchCommand());
|
||||
getCommand("anvil").setExecutor(new AnvilCommand());
|
||||
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("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("spawn").setExecutor(new SpawnCommand(this));
|
||||
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",
|
||||
"vanish", "freeze", "lootchests", "tploot", "day", "night", "trade", "tradeaccept",
|
||||
"report", "showreport", "clearreport", "shop", "spawn", "setwarp", "delwarp",
|
||||
"warps", "startchallenge", "heal", "claim", "head"
|
||||
"warps", "startchallenge", "heal", "claim", "head", "money", "job", "jobs"
|
||||
};
|
||||
for (String name : commands) {
|
||||
if (getCommand(name) != null) {
|
||||
@@ -1391,6 +1422,21 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
|
||||
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)
|
||||
public ProtocolManager 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.SurvivalPlus;
|
||||
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.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
@@ -12,32 +14,17 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
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 {
|
||||
|
||||
private final ShopManager shopManager;
|
||||
private final SurvivalPlus plugin;
|
||||
private Economy economy;
|
||||
// ShopManager und Economy kommen zentral aus dem Plugin
|
||||
private final ShopManager shopManager;
|
||||
|
||||
public ShopCommand(SurvivalPlus plugin) {
|
||||
this.plugin = plugin;
|
||||
this.shopManager = new ShopManager(plugin);
|
||||
|
||||
// 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
|
||||
}
|
||||
// ShopManager zentral aus dem Plugin holen, KEINE eigene Instanz erstellen
|
||||
this.shopManager = plugin.getShopManager();
|
||||
}
|
||||
|
||||
private String getMessage(String key) {
|
||||
@@ -53,28 +40,33 @@ public class ShopCommand implements CommandExecutor {
|
||||
Player player = (Player) sender;
|
||||
|
||||
// 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);
|
||||
Bukkit.getPluginManager().registerEvents(shopGui, plugin);
|
||||
player.openInventory(shopGui.getInventory());
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- SHOP ADD LOGIK ---
|
||||
if (args.length >= 4 && args[0].equalsIgnoreCase("add")) {
|
||||
// --- SHOP 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) {
|
||||
sender.sendMessage(ChatColor.RED + "Vault Economy ist nicht verfügbar!");
|
||||
return true;
|
||||
}
|
||||
|
||||
String materialName = args[1];
|
||||
String priceStr = args[2];
|
||||
String amountStr = args[3];
|
||||
String priceStr = args[2];
|
||||
String amountStr = args[3];
|
||||
|
||||
Material mat = Material.matchMaterial(materialName);
|
||||
|
||||
if (mat == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!");
|
||||
return true;
|
||||
@@ -82,12 +74,11 @@ public class ShopCommand implements CommandExecutor {
|
||||
|
||||
double price;
|
||||
int amount;
|
||||
|
||||
try {
|
||||
price = Double.parseDouble(priceStr);
|
||||
price = Double.parseDouble(priceStr);
|
||||
amount = Integer.parseInt(amountStr);
|
||||
} 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;
|
||||
}
|
||||
|
||||
@@ -96,56 +87,50 @@ public class ShopCommand implements CommandExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 1. Prüfen: Hat der Spieler die Items?
|
||||
// Hat der Spieler die Items?
|
||||
int hasAmount = countItems(player.getInventory(), mat);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 2. Items aus Inventar entfernen
|
||||
// Items entfernen
|
||||
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;
|
||||
EconomyResponse response = economy.depositPlayer(player, totalCost);
|
||||
|
||||
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();
|
||||
shopManager.addOrUpdateItem(itemKey, price, shopManager.getStock(itemKey) + amount);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung!");
|
||||
return true;
|
||||
// Rollback: Items zurückgeben
|
||||
player.getInventory().addItem(new ItemStack(mat, amount));
|
||||
player.updateInventory();
|
||||
sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung: " + response.errorMessage);
|
||||
}
|
||||
|
||||
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]");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zählt die Anzahl der Items eines bestimmten Materials im Inventar.
|
||||
* Nutzt StorageContents, um auch Rüstung/Shulker-Slots zu prüfen.
|
||||
* Zählt alle Items eines Materials im Spieler-Inventar (nur Storage-Slots).
|
||||
*/
|
||||
private int countItems(PlayerInventory inv, Material mat) {
|
||||
int count = 0;
|
||||
for (ItemStack item : inv.getStorageContents()) {
|
||||
if (item != null && item.getType().equals(mat)) {
|
||||
if (item != null && item.getType() == mat) {
|
||||
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> SPLOCK_SUBCOMMANDS = List.of("lock", "unlock", "friendadd", "friendremove");
|
||||
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> 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);
|
||||
case "shop":
|
||||
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":
|
||||
return filterPrefix(NICK_SUBCOMMANDS, args[0]);
|
||||
case "gm":
|
||||
@@ -159,6 +169,42 @@ public class SurvivalPlusTabCompleter implements TabCompleter {
|
||||
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) {
|
||||
if (args.length == 1) {
|
||||
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.SurvivalPlus;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.economy.EconomyResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
@@ -22,11 +24,14 @@ public class ShopGui implements Listener {
|
||||
private final Player player;
|
||||
private Inventory inv;
|
||||
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) {
|
||||
this.plugin = plugin;
|
||||
this.player = player;
|
||||
this.shopManager = shopManager;
|
||||
this.economy = plugin.getEconomy(); // <-- zentral aus SurvivalPlus
|
||||
createInventory();
|
||||
}
|
||||
|
||||
@@ -38,12 +43,9 @@ public class ShopGui implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// Alle Items aus shop.yml einlesen
|
||||
for (String itemKey : shopManager.getShopConfig().getConfigurationSection("items").getKeys(false)) {
|
||||
Material mat = getMaterialFromKey(itemKey);
|
||||
if (mat == null) {
|
||||
continue; // Unbekannte Materialien einfach überspringen
|
||||
}
|
||||
if (mat == null) continue;
|
||||
|
||||
addShopItem(itemKey, mat, 64);
|
||||
addShopItem(itemKey, mat, 16);
|
||||
@@ -72,8 +74,8 @@ public class ShopGui implements Listener {
|
||||
meta.setDisplayName(ChatColor.GREEN.toString() + amount + "x " + material.name());
|
||||
meta.setLore(Arrays.asList(
|
||||
ChatColor.GRAY + "Lagerbestand: " + ChatColor.YELLOW + currentStock,
|
||||
ChatColor.YELLOW + "Preis pro Stück: " + pricePerUnit,
|
||||
ChatColor.YELLOW + "Gesamtpreis: " + totalPrice,
|
||||
ChatColor.YELLOW + "Preis pro Stück: " + String.format("%.2f", pricePerUnit),
|
||||
ChatColor.YELLOW + "Gesamtpreis: " + String.format("%.2f", totalPrice),
|
||||
"",
|
||||
ChatColor.GREEN + "Linksklick zum Kaufen",
|
||||
ChatColor.RED + "Rechtsklick zum Verkaufen"
|
||||
@@ -100,58 +102,85 @@ public class ShopGui implements Listener {
|
||||
ItemStack clicked = e.getCurrentItem();
|
||||
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();
|
||||
Material mat = clicked.getType();
|
||||
String itemKey = mat.name().toLowerCase();
|
||||
|
||||
if (e.isLeftClick()) {
|
||||
// --- 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)) {
|
||||
player.sendMessage(ChatColor.RED + "Nicht genügend Bestand im Shop.");
|
||||
return;
|
||||
}
|
||||
|
||||
double totalPrice = shopManager.getCurrentPrice(itemKey) * amount;
|
||||
|
||||
// TODO: Economy Abzug hier einfügen!
|
||||
// Beispiel: if (!economy.withdrawPlayer(player, totalPrice)) { ... }
|
||||
// 3. Geld abziehen
|
||||
EconomyResponse response = economy.withdrawPlayer(player, totalPrice);
|
||||
if (!response.transactionSuccess()) {
|
||||
player.sendMessage(ChatColor.RED + "Fehler bei der Zahlung: " + response.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Items geben
|
||||
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()) {
|
||||
// --- VERKAUFEN ---
|
||||
|
||||
// Prüfen ob Spieler genug Items hat
|
||||
// 1. Prüfen ob Spieler genug Items hat
|
||||
if (!player.getInventory().containsAtLeast(new ItemStack(mat), amount)) {
|
||||
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items zum Verkaufen.");
|
||||
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);
|
||||
|
||||
double sellPrice = shopManager.getCurrentPrice(itemKey) * amount; // Neuer Preis nach dem Verkauf wird genutzt
|
||||
// Hinweis: In echten Systemen bekommt man oft den Preis *vor* dem Preisverfall, hier nehmen wir den neuen für Einfachheit oder den gespeicherten:
|
||||
// Besser wäre: double oldPrice = price * amount; ... player.giveMoney(oldPrice);
|
||||
// 5. Geld gutschreiben
|
||||
EconomyResponse response = economy.depositPlayer(player, sellPrice);
|
||||
if (!response.transactionSuccess()) {
|
||||
// Rollback: Items zurückgeben wenn Zahlung fehlschlägt
|
||||
player.getInventory().addItem(new ItemStack(mat, amount));
|
||||
player.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung: " + response.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Economy Gutschrift hier einfügen!
|
||||
|
||||
player.getInventory().removeItem(new ItemStack(mat, amount));
|
||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + String.format("%.2f", sellPrice) + " verkauft.");
|
||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name()
|
||||
+ " für " + String.format("%.2f", sellPrice) + " Coins verkauft.");
|
||||
}
|
||||
|
||||
// Inventory schließen, um Preise neu zu laden und Bugs zu vermeiden
|
||||
player.updateInventory();
|
||||
player.closeInventory();
|
||||
// Optional: direkt neu öffnen mit createInventory() wenn es flüssig sein soll
|
||||
// createInventory();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent e) {
|
||||
if (e.getView().getTitle().equals("Shop") && e.getPlayer().equals(player)) {
|
||||
// Listener kann entladen werden, wenn nicht mehr gebraucht
|
||||
// HandlerList.unregisterAll(this); // Vorsicht: falls Singleton, sonst fliegt allen der GUI weg
|
||||
// Listener-Cleanup hier möglich, wenn ShopGui nicht als Singleton genutzt wird
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,32 +14,30 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
public class SignShopListener implements Listener {
|
||||
|
||||
private final SurvivalPlus plugin;
|
||||
private Economy economy;
|
||||
// Economy wird zentral aus dem Plugin geholt – keine eigene Initialisierung nötig
|
||||
|
||||
public SignShopListener(SurvivalPlus plugin) {
|
||||
this.plugin = plugin;
|
||||
try {
|
||||
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.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Fehler beim Laden der Vault API.");
|
||||
}
|
||||
// Kein eigener Vault-Setup hier! Economy kommt über plugin.getEconomy()
|
||||
}
|
||||
|
||||
// --- Schild erstellung ---
|
||||
/**
|
||||
* Hilfsmethode: Economy aus Plugin holen mit Null-Check und Fehlermeldung.
|
||||
* Gibt null zurück wenn Economy nicht verfügbar ist.
|
||||
*/
|
||||
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 ---
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onSignChange(SignChangeEvent event) {
|
||||
String line0Raw = event.getLine(0);
|
||||
@@ -50,13 +48,14 @@ public class SignShopListener implements Listener {
|
||||
Player player = event.getPlayer();
|
||||
if (!player.hasPermission("survivalplus.shop.create")) {
|
||||
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu erstellen.");
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
String line2 = event.getLine(1);
|
||||
String line3 = event.getLine(2);
|
||||
String line1 = event.getLine(1); // "64 Diamond"
|
||||
String line2 = event.getLine(2); // "500"
|
||||
|
||||
String[] parts = line2.split(" ");
|
||||
String[] parts = line1.split(" ");
|
||||
if (parts.length < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Format in Zeile 2 falsch! Beispiel: 64 Diamond");
|
||||
event.setCancelled(true);
|
||||
@@ -66,30 +65,30 @@ public class SignShopListener implements Listener {
|
||||
try {
|
||||
Integer.parseInt(parts[0]);
|
||||
if (Material.matchMaterial(parts[1]) == null) {
|
||||
player.sendMessage(ChatColor.RED + "Das Item '" + parts[1] + "' existiert nicht.");
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
player.sendMessage(ChatColor.RED + "Das Item '" + parts[1] + "' existiert nicht.");
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
Double.parseDouble(line3);
|
||||
Double.parseDouble(line2);
|
||||
} 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);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- NEU: Schild farbig machen ---
|
||||
// Schild farbig formatieren
|
||||
if (line0Raw.equalsIgnoreCase("[buy]")) {
|
||||
event.setLine(0, ChatColor.GREEN + "[BUY]");
|
||||
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
|
||||
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
|
||||
} else if (line0Raw.equalsIgnoreCase("[sell]")) {
|
||||
event.setLine(2, ChatColor.GOLD + line2 + " Coins");
|
||||
} else {
|
||||
event.setLine(0, ChatColor.RED + "[SELL]");
|
||||
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()) {
|
||||
event.setLine(3, ChatColor.GRAY + event.getLine(3));
|
||||
event.setLine(3, ChatColor.GRAY + event.getLine(3));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,23 +96,14 @@ public class SignShopListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
Block block = event.getClickedBlock();
|
||||
if (block == null || !(block.getState() instanceof Sign)) {
|
||||
return;
|
||||
}
|
||||
if (block == null || !(block.getState() instanceof Sign)) return;
|
||||
|
||||
Sign sign = (Sign) block.getState();
|
||||
String line0 = ChatColor.stripColor(sign.getLine(0));
|
||||
|
||||
if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) {
|
||||
return;
|
||||
}
|
||||
if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) return;
|
||||
|
||||
if (economy == null) {
|
||||
event.getPlayer().sendMessage(ChatColor.RED + "Shop System offline (Vault fehlt).");
|
||||
return;
|
||||
}
|
||||
|
||||
// --- FEATURE: SHIFT+KLICK LOGIK (LÖSCHEN) ---
|
||||
// SHIFT+KLICK: Shop entfernen
|
||||
if (event.getPlayer().isSneaking()) {
|
||||
Player player = event.getPlayer();
|
||||
if (!player.hasPermission("survivalplus.shop.create")) {
|
||||
@@ -127,23 +117,25 @@ public class SignShopListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// --- ROUTING ---
|
||||
if (line0.equalsIgnoreCase("[buy]")) {
|
||||
handleBuy(event, sign);
|
||||
} else if (line0.equalsIgnoreCase("[sell]")) {
|
||||
} else {
|
||||
handleSell(event, sign);
|
||||
}
|
||||
}
|
||||
|
||||
// --- KAUFS LOGIK (ROBUST) ---
|
||||
// --- Kaufs-Logik ---
|
||||
private void handleBuy(PlayerInteractEvent event, Sign sign) {
|
||||
event.setCancelled(true); // Interaktion stoppen
|
||||
|
||||
event.setCancelled(true);
|
||||
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()) {
|
||||
player.sendMessage(ChatColor.RED + "Zeile 1 des Schildes ist leer! (Format: 64 Diamond)");
|
||||
return;
|
||||
@@ -153,92 +145,68 @@ public class SignShopListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Parsing: Zeile 1 "64 Diamond"
|
||||
String[] partsRaw = line1Raw.split(" ");
|
||||
if (partsRaw.length < 2) {
|
||||
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!");
|
||||
String[] parts = line1Raw.split(" ");
|
||||
if (parts.length < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Falsches Format! (Format: 64 Diamond)");
|
||||
return;
|
||||
}
|
||||
|
||||
int amount;
|
||||
Material material;
|
||||
try {
|
||||
amount = Integer.parseInt(amountStr);
|
||||
material = Material.matchMaterial(itemNameStr);
|
||||
amount = Integer.parseInt(parts[0].trim());
|
||||
material = Material.matchMaterial(parts[1].trim());
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (material == null) {
|
||||
player.sendMessage(ChatColor.RED + "Item '" + itemNameStr + "' 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)");
|
||||
player.sendMessage(ChatColor.RED + "Item '" + parts[1] + "' existiert nicht!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Preis parsen ("500 Coins" → "500")
|
||||
double price;
|
||||
try {
|
||||
price = Double.parseDouble(priceStr);
|
||||
price = Double.parseDouble(line2Raw.split(" ")[0].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl! (Beispiel: 500)");
|
||||
return;
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
player.sendMessage(ChatColor.RED + "Fehlerhafter Preis! (Beispiel: 500)");
|
||||
player.sendMessage(ChatColor.RED + "Preis '" + line2Raw + "' ist keine gültige Zahl!");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 4. Transaktion
|
||||
try {
|
||||
ItemStack item = new ItemStack(material, amount);
|
||||
// Genug Geld?
|
||||
if (economy.getBalance(player) < price) {
|
||||
player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Du brauchst "
|
||||
+ String.format("%.2f", price) + " Coins.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (economy.getBalance(player) >= price) {
|
||||
EconomyResponse response = economy.withdrawPlayer(player, price);
|
||||
if (response.transactionSuccess()) {
|
||||
player.getInventory().addItem(item);
|
||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " gekauft!");
|
||||
} else {
|
||||
player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion.");
|
||||
}
|
||||
} 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();
|
||||
// Transaktion
|
||||
EconomyResponse response = economy.withdrawPlayer(player, price);
|
||||
if (response.transactionSuccess()) {
|
||||
player.getInventory().addItem(new ItemStack(material, amount));
|
||||
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x "
|
||||
+ material.name().toLowerCase() + " für " + String.format("%.2f", price) + " Coins gekauft!");
|
||||
} else {
|
||||
player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion: " + response.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// --- VERKAUFS LOGIK ---
|
||||
// --- Verkaufs-Logik ---
|
||||
private void handleSell(PlayerInteractEvent event, Sign sign) {
|
||||
event.setCancelled(true);
|
||||
|
||||
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 line2Raw = ChatColor.stripColor(sign.getLine(2));
|
||||
|
||||
@@ -247,24 +215,21 @@ public class SignShopListener implements Listener {
|
||||
return;
|
||||
}
|
||||
if (line2Raw == null || line2Raw.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "Zeile 2 ist leer!");
|
||||
player.sendMessage(ChatColor.RED + "Preiszeile ist leer!");
|
||||
return;
|
||||
}
|
||||
|
||||
String[] partsRaw = line1Raw.split(" ");
|
||||
if (partsRaw.length < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Format falsch! Benutze: [Sell] \n 64 Diamond");
|
||||
String[] parts = line1Raw.split(" ");
|
||||
if (parts.length < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Format falsch! (Format: 64 Diamond)");
|
||||
return;
|
||||
}
|
||||
|
||||
String amountStr = partsRaw[0].trim();
|
||||
String itemNameStr = partsRaw[1].trim();
|
||||
|
||||
int amount;
|
||||
Material material;
|
||||
try {
|
||||
amount = Integer.parseInt(amountStr);
|
||||
material = Material.matchMaterial(itemNameStr);
|
||||
amount = Integer.parseInt(parts[0].trim());
|
||||
material = Material.matchMaterial(parts[1].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage(ChatColor.RED + "Menge ist keine Zahl!");
|
||||
return;
|
||||
@@ -275,50 +240,46 @@ public class SignShopListener implements Listener {
|
||||
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;
|
||||
try {
|
||||
price = Double.parseDouble(priceStr);
|
||||
price = Double.parseDouble(line2Raw.split(" ")[0].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl!");
|
||||
player.sendMessage(ChatColor.RED + "Preis '" + line2Raw + "' ist keine gültige Zahl!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (amount <= 0 || price < 0) {
|
||||
player.sendMessage(ChatColor.RED + "Negativer Wert!");
|
||||
player.sendMessage(ChatColor.RED + "Ungültige Werte auf dem Schild.");
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack tempItem = new ItemStack(material, amount);
|
||||
// Hat der Spieler genug Items?
|
||||
int hasAmount = 0;
|
||||
|
||||
for (ItemStack item : player.getInventory().getStorageContents()) {
|
||||
if (item != null && item.isSimilar(tempItem)) {
|
||||
if (item != null && item.isSimilar(new ItemStack(material))) {
|
||||
hasAmount += item.getAmount();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Verkauf durchführen
|
||||
// Items entfernen
|
||||
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?
|
||||
force-survival: true
|
||||
|
||||
economy:
|
||||
start-balance: 500.0
|
||||
currency-singular: "$"
|
||||
currency-plural: "$"
|
||||
|
||||
# Warp Default Item
|
||||
defaultWarpItem: OAK_SIGN
|
||||
# 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
|
||||
version: 1.1.3
|
||||
version: 1.1.4
|
||||
|
||||
main: de.viper.survivalplus.SurvivalPlus
|
||||
api-version: 1.21
|
||||
softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib, Vault]
|
||||
author: Viper
|
||||
loadbefore: [IngameShopSpigot]
|
||||
author: M_Viper
|
||||
description: A plugin for enhancing survival gameplay in Minecraft.
|
||||
|
||||
commands:
|
||||
@@ -292,6 +293,22 @@ commands:
|
||||
permission: survivalplus.head
|
||||
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:
|
||||
survivalplus.*:
|
||||
description: Gibt Zugriff auf alle SurvivalPlus-Befehle
|
||||
@@ -369,6 +386,11 @@ permissions:
|
||||
survivalplus.claim.kick: true
|
||||
survivalplus.claim.ban: 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.vanish.silent: true
|
||||
survivalplus.vanish.no-pickup: true
|
||||
@@ -597,3 +619,18 @@ permissions:
|
||||
survivalplus.head:
|
||||
description: Erlaubt das Holen von Spielerköpfen
|
||||
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