diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7786e09..82d6223 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,45 +1,12 @@ -name: Build and Release +name: Release on: push: branches: [main] - pull_request: - branches: [main] jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: "17" - distribution: "temurin" - - - name: Cache Maven packages - uses: actions/cache@v3 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - - name: Build with Maven - run: mvn clean compile test package - - - name: Upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: SimpleEco-jar - path: target/*.jar - release: - needs: build runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout code @@ -51,8 +18,8 @@ jobs: java-version: "17" distribution: "temurin" - - name: Cache Maven packages - uses: actions/cache@v3 + - name: Cache Maven dependencies + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -61,57 +28,36 @@ jobs: - name: Build with Maven run: mvn clean package - - name: Get version from pom.xml - id: get_version - run: | - VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Version: $VERSION" - - - name: Check if release exists - id: check_release - run: | - if gh release view "v${{ steps.get_version.outputs.version }}" >/dev/null 2>&1; then - echo "exists=true" >> $GITHUB_OUTPUT - else - echo "exists=false" >> $GITHUB_OUTPUT - fi - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Get version + id: version + run: echo "VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT - name: Create Release - if: steps.check_release.outputs.exists == 'false' id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: v${{ steps.get_version.outputs.version }} - release_name: SimpleEco v${{ steps.get_version.outputs.version }} + tag_name: v${{ steps.version.outputs.VERSION }} + release_name: SimpleEco v${{ steps.version.outputs.VERSION }} body: | - ## SimpleEco v${{ steps.get_version.outputs.version }} + ## SimpleEco v${{ steps.version.outputs.VERSION }} - ### Changes - - Automatic release from main branch + Automatisch erstelltes Release. ### Installation - 1. Download the `SimpleEco-${{ steps.get_version.outputs.version }}.jar` file - 2. Place it in your server's `plugins/` directory - 3. Restart your server - - ### Requirements - - Java 17 or higher - - Paper/Spigot 1.20.4 or compatible + 1. Download der `SimpleEco.jar` + 2. In den `plugins/` Ordner kopieren + 3. Server neustarten draft: false prerelease: false - name: Upload Release Asset - if: steps.check_release.outputs.exists == 'false' uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./target/SimpleEco-${{ steps.get_version.outputs.version }}.jar - asset_name: SimpleEco-${{ steps.get_version.outputs.version }}.jar + asset_path: ./target/SimpleEco-${{ steps.version.outputs.VERSION }}.jar + asset_name: SimpleEco.jar asset_content_type: application/java-archive diff --git a/.gitignore b/.gitignore index dbf52e7..ba653f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,31 @@ +# Created by https://www.toptal.com/developers/gitignore/api/maven +# Edit at https://www.toptal.com/developers/gitignore?templates=maven + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# End of https://www.toptal.com/developers/gitignore/api/maven + +# Created by https://www.toptal.com/developers/gitignore/api/java +# Edit at https://www.toptal.com/developers/gitignore?templates=java + +### Java ### # Compiled class file *.class @@ -19,101 +47,137 @@ *.tar.gz *.rar -# Virtual machine crash logs +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* -# Maven -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar +# End of https://www.toptal.com/developers/gitignore/api/java + +# Created by https://www.toptal.com/developers/gitignore/api/intellij +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml # Gradle -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -# IntelliJ IDEA -.idea/ +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format *.iws -*.iml -*.ipr + +# IntelliJ out/ -!**/src/main/**/out/ -!**/src/test/**/out/ -# Eclipse -.apt_generated +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +.idea/* + .classpath -.factorypath .project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -# NetBeans -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -# VS Code -.vscode/ +.settings/ +target/ -# Mac -.DS_Store - -# Windows -Thumbs.db -ehthumbs.db -Desktop.ini - -# Linux -*~ - -# Temporary files -*.tmp -*.temp -*.swp -*.swo - -# Application specific -*.db -*.sqlite -*.sqlite3 - -# Server files (for testing) -server/ -plugins/ -world/ -world_nether/ -world_the_end/ -logs/ -crash-reports/ -debug/ - -# Test server configuration -server.properties -bukkit.yml -spigot.yml -paper.yml -eula.txt -banned-ips.json -banned-players.json -ops.json -whitelist.json -usercache.json -usernamecache.json \ No newline at end of file + +# End of https://www.toptal.com/developers/gitignore/api/intellij + +.vscode/ +.vscode/* \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index a0ccf77..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Environment-dependent path to Maven home directory -/mavenHomeManager.xml diff --git a/.idea/SimpleEco.iml b/.idea/SimpleEco.iml deleted file mode 100644 index 3cf00db..0000000 --- a/.idea/SimpleEco.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - PAPER - - 1 - - - - \ No newline at end of file diff --git a/.idea/artifacts/SimpleEco_jar.xml b/.idea/artifacts/SimpleEco_jar.xml deleted file mode 100644 index 551fc4c..0000000 --- a/.idea/artifacts/SimpleEco_jar.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - $PROJECT_DIR$/out/artifacts/SimpleEco_jar - - - - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index bfbece6..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index aa00ffa..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 8c89be5..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index c990afb..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 4978308..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 849f79e..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.compile.nullAnalysis.mode": "automatic" -} diff --git a/README.md b/README.md index dfa0f24..3c686b8 100644 --- a/README.md +++ b/README.md @@ -1,271 +1,31 @@ -# SimpleEco - Dynamisches Wirtschaftssystem Plugin +# SimpleEco -Ein vollständiges Paper Spigot Plugin für Minecraft, das ein dynamisches Wirtschaftssystem mit intelligenter Preisbildung und Villager-Trading implementiert. +Ein dynamisches Wirtschaftssystem Plugin für Minecraft Paper/Spigot. -## 🌟 Features +## Features -### Grundlegende Währung -- **BasicCurrency-System**: Vollständige Verwaltung von Spielerkonten -- **Konfigurierbare Währung**: Name, Symbol und Startguthaben anpassbar -- **Asynchrone Operationen**: Alle Datenbankzugriffe erfolgen asynchron für optimale Performance +- **Dynamische Preise**: Preise ändern sich basierend auf Angebot und Nachfrage +- **Villager Trading**: Interaktive Händler mit Live-Preisen +- **SQLite Datenbank**: Persistente Speicherung aller Daten +- **Vollständig konfigurierbar**: Alle Parameter anpassbar -### Dynamische Preisbildung -- **Intelligente Preisformel**: `Preis = clamp(basisPreis * (1 + preisFaktor * nettoVerkäufe * regressionFaktor / referenzMenge), minPreis, maxPreis)` -- **Preis-Regression**: Preise kehren über konfigurierbare Zeit zum Basispreis zurück -- **Item-spezifische Parameter**: Jedes Item kann eigene `priceFactor` und `referenceAmount` haben -- **Echzeit-Updates**: Preise ändern sich sofort basierend auf Handelstätigkeiten -- **Item-Kontrolle**: Konfigurierbare Kaufbarkeit/Verkaufbarkeit pro Item -- **Vollständig konfigurierbar**: Alle Preisparameter in der `config.yml` anpassbar +## Installation -### Villager-Trading-Interface -- **Interaktives Menü**: Rechtsklick auf Villager öffnet Trading-Interface -- **Live-Preisinformationen**: Aktuelle Kauf-/Verkaufspreise, Trends und Volatilität -- **Multi-Handels-Support**: Einzel- oder 64er-Handel per Klick +1. [Latest Release](../../releases/latest) herunterladen +2. `SimpleEco.jar` in den `plugins/` Ordner kopieren +3. Server neustarten -### SQLite-Persistierung -- **Robuste Datenbank**: SQLite mit WAL-Modus für bessere Concurrency -- **Zwei Haupttabellen**: - - `player_balance`: Spielerkontostände - - `item_stats`: Item-Handelsstatistiken -- **Performance-Optimiert**: Caching und asynchrone Operationen +## Commands -## 🚀 Installation +| Command | Beschreibung | +| ---------------------------- | ------------------- | +| `/eco balance [player]` | Kontostand anzeigen | +| `/eco pay ` | Geld überweisen | -1. **Download**: Lade die neueste `SimpleEco.jar` Datei herunter -2. **Installation**: Platziere die JAR-Datei in deinem `plugins/` Ordner -3. **Server-Neustart**: Starte deinen Paper Spigot Server neu -4. **Konfiguration**: Passe die `plugins/SimpleEco/config.yml` nach deinen Wünschen an +## Konfiguration -## ⚙️ Konfiguration +Die `config.yml` wird automatisch erstellt und enthält alle verfügbaren Optionen mit Erklärungen. -### Grundeinstellungen -```yaml -# Währungseinstellungen -currency: - name: "Gold" - startBalance: 1000.0 - symbol: "G" +## Support -# Datenbankeinstellungen -database: - path: "plugins/SimpleEco/economy.db" - -# Preiseinstellungen -pricing: - priceFactor: 0.05 # 5% Elastizität (global, überschreibbar pro Item) - referenceAmount: 1000 # Referenzmenge (global, überschreibbar pro Item) - regressionTimeMinutes: 60 # Zeit bis Preise zum Default zurückkehren - regressionUpdateInterval: 5 # Update-Intervall in Minuten -``` - -### Item-Preise konfigurieren -```yaml -pricing: - priceFactor: 0.05 # Globaler Standard-Preisfaktor - referenceAmount: 1000 # Globale Standard-Referenzmenge - regressionTimeMinutes: 60 # Zeit bis Preise zum Basispreis zurückkehren - regressionUpdateInterval: 5 # Update-Intervall in Minuten - - items: - WHEAT: - basePrice: 10.0 - minPrice: 5.0 - maxPrice: 50.0 - buyable: true # Kann gekauft werden - sellable: true # Kann verkauft werden - # Verwendet globale priceFactor und referenceAmount - - DIAMOND: - basePrice: 500.0 - minPrice: 250.0 - maxPrice: 2500.0 - buyable: true - sellable: false # Nur kaufbar, nicht verkaufbar - priceFactor: 0.02 # Item-spezifisch: Weniger volatil - referenceAmount: 100 # Item-spezifisch: Reagiert schneller - - COAL: - basePrice: 5.0 - minPrice: 2.0 - maxPrice: 25.0 - buyable: false # Nur verkaufbar (Rohstoff) - sellable: true - priceFactor: 0.1 # Item-spezifisch: Sehr volatil - referenceAmount: 2000 # Item-spezifisch: Massengut -``` - -## 🎮 Commands - -| Command | Beschreibung | Permission | -|---------|-------------|------------| -| `/eco balance [Spieler]` | Zeigt Kontostand an | `simpleeco.use` | -| `/eco pay ` | Überweist Geld | `simpleeco.use` | -| `/eco help` | Zeigt Hilfe an | `simpleeco.use` | - -## 🔑 Permissions - -| Permission | Beschreibung | Standard | -|------------|-------------|----------| -| `simpleeco.use` | Grundlegende Plugin-Nutzung | `true` | -| `simpleeco.admin` | Admin-Funktionen | `op` | -| `simpleeco.balance.other` | Fremde Kontostände einsehen | `op` | - -## 🛠️ Villager-Trading - -### Wie es funktioniert -1. **Rechtsklick auf Villager**: Öffnet das Trading-Menü -2. **Linksklick auf Item**: Kauft 1x Item -3. **Rechtsklick auf Item**: Verkauft 1x Item -4. **Shift+Klick**: Handelt mit 64x Items -5. **Shift+Rechtsklick auf Villager**: Normales Villager-Trading - -### Preisinformationen -Das Interface zeigt für jedes Item: -- Aktueller Kauf-/Verkaufspreis -- Preistoleranz-Trends (steigend/fallend/stabil) -- Volatilität (niedrig/mittel/hoch) -- Handelsstatistiken (verkauft/gekauft/netto) -- Effektive Preisparameter (Faktor und Referenzmenge) - -## 📊 Dynamische Preisbildung - -### Preisformel -``` -Preis = clamp( - basisPreis * (1 + preisFaktor * (verkauft - gekauft) * regressionFaktor / referenzMenge), - minPreis, - maxPreis -) - -RegressionFaktor = 1.0 - (zeitSeitLetztemHandel / regressionZeit) -``` - -### Beispiel -- **Basispreis**: 10 Gold -- **Preis-Faktor**: 0.05 (5%) -- **Referenzmenge**: 1000 -- **Verkauft**: 1500, **Gekauft**: 500 -- **Netto**: +1000 -- **Zeit seit letztem Handel**: 30 Minuten -- **Regressions-Zeit**: 60 Minuten -- **Regressions-Faktor**: 0.5 (1.0 - 30/60) - -**Berechneter Preis**: `10 * (1 + 0.05 * 1000 * 0.5 / 1000) = 10 * 1.025 = 10.25 Gold` - -### Beispiel: Item-spezifische Parameter - -**Diamant** (wenig volatil, reagiert schnell): -- **Basispreis**: 500 Gold -- **Item-spezifischer Preis-Faktor**: 0.02 (vs. global 0.05) -- **Item-spezifische Referenzmenge**: 100 (vs. global 1000) -- **Verkauft**: 50, **Gekauft**: 30, **Netto**: +20 -- **Berechneter Preis**: `500 * (1 + 0.02 * 20 * 1.0 / 100) = 500 * 1.004 = 502 Gold` - -**Kohle** (sehr volatil, Massengut): -- **Basispreis**: 5 Gold -- **Item-spezifischer Preis-Faktor**: 0.1 (vs. global 0.05) -- **Item-spezifische Referenzmenge**: 2000 (vs. global 1000) -- **Verkauft**: 3000, **Gekauft**: 1000, **Netto**: +2000 -- **Berechneter Preis**: `5 * (1 + 0.1 * 2000 * 1.0 / 2000) = 5 * 1.1 = 5.5 Gold` - -## 🔧 Technische Details - -### Architektur -- **Thread-Safe**: Alle Operationen sind thread-sicher implementiert -- **Asynchron**: Datenbankzugriffe blockieren nie den Haupt-Thread -- **Modularer Aufbau**: Saubere Trennung der Komponenten -- **Performance-Optimiert**: Caching und effiziente Datenbankabfragen - -### Systemanforderungen -- **Minecraft**: 1.20.4+ -- **Server**: Paper Spigot (empfohlen) -- **Java**: 17+ -- **RAM**: Minimal 512MB für Plugin-Daten - -### Datenbank-Schema -```sql --- Spieler-Kontostände -CREATE TABLE player_balance ( - uuid TEXT PRIMARY KEY, - balance REAL NOT NULL DEFAULT 0.0, - last_updated INTEGER NOT NULL -); - --- Item-Handelsstatistiken -CREATE TABLE item_stats ( - item TEXT PRIMARY KEY, - sold BIGINT NOT NULL DEFAULT 0, - bought BIGINT NOT NULL DEFAULT 0, - last_updated INTEGER NOT NULL -); -``` - -## 📈 Performance - -### Optimierungen -- **SQLite WAL-Modus**: Bessere Concurrency bei Datenbankzugriffen -- **In-Memory-Caching**: Häufig abgerufene Daten werden gecacht -- **Asynchrone Verarbeitung**: Keine Blockierung des Haupt-Threads -- **Batch-Operationen**: Effiziente Datenbank-Updates - -### Benchmarks -- **Spieler-Balance-Abfrage**: < 1ms (gecacht), < 10ms (Datenbank) -- **Preis-Berechnung**: < 0.1ms -- **Trading-Transaktion**: < 50ms (komplett) - -## 🐛 Troubleshooting - -### Häufige Probleme - -**Plugin startet nicht** -- Überprüfe Java-Version (benötigt Java 17+) -- Kontrolliere Konsolen-Logs auf Fehlermeldungen -- Stelle sicher, dass Paper Spigot 1.20.4+ verwendet wird - -**Datenbank-Fehler** -- Überprüfe Schreibrechte im Plugin-Verzeichnis -- Kontrolliere verfügbaren Speicherplatz -- Prüfe auf SQLite-Korruption - -**Preise aktualisieren nicht** -- Überprüfe `priceFactor` in der Konfiguration -- Kontrolliere Item-Konfiguration -- Prüfe Konsolen-Logs auf Fehler - -## 📝 Changelog - -### Version 1.0.0 -- Erste vollständige Version -- Dynamisches Preissystem implementiert -- Villager-Trading-Interface -- Vollständiges Command-System -- SQLite-Persistierung -- Umfassende Konfigurationsmöglichkeiten - -### Version 1.1.0 -- **Preis-Regression**: Preise kehren über Zeit zum Basispreis zurück -- **Item-Kontrolle**: Konfigurierbare Kaufbarkeit/Verkaufbarkeit pro Item -- **Automatische Updates**: Scheduler-Task für regelmäßige Preisanpassungen -- **Verbesserte Performance**: Optimierte Datenbankzugriffe mit Zeitstempel-Tracking -- **Enhanced UI**: Bessere Anzeige von handelbaren Optionen im Villager-Menü - -### Version 1.2.0 (Aktuelle Version) -- **Item-spezifische Parameter**: Jedes Item kann eigene `priceFactor` und `referenceAmount` haben -- **Granulare Kontrolle**: Verschiedene Volatilitäten und Reaktionsgeschwindigkeiten pro Item -- **Erweiterte Anzeige**: UI zeigt effektive Preisparameter für jedes Item -- **Flexible Konfiguration**: Globale Standards mit optionalen item-spezifischen Überschreibungen - -## 👥 Support - -Bei Fragen oder Problemen: -1. Überprüfe die Dokumentation -2. Kontrolliere die Konsolen-Logs -3. Erstelle ein Issue auf GitHub mit detaillierter Fehlerbeschreibung - -## 📄 Lizenz - -Dieses Plugin ist unter der MIT-Lizenz veröffentlicht. Siehe `LICENSE` Datei für Details. - ---- - -**Entwickelt mit ❤️ für die Minecraft-Community** \ No newline at end of file +Bei Problemen erstelle ein [Issue](../../issues/new). diff --git a/out/artifacts/SimpleEco_jar/SimpleEco.jar b/out/artifacts/SimpleEco_jar/SimpleEco.jar deleted file mode 100644 index b955a01..0000000 Binary files a/out/artifacts/SimpleEco_jar/SimpleEco.jar and /dev/null differ diff --git a/src/main/java/de/simpleeco/SimpleEcoPlugin.java b/src/main/java/de/simpleeco/SimpleEcoPlugin.java index 8273c57..8793034 100644 --- a/src/main/java/de/simpleeco/SimpleEcoPlugin.java +++ b/src/main/java/de/simpleeco/SimpleEcoPlugin.java @@ -1,15 +1,16 @@ package de.simpleeco; import de.simpleeco.commands.EcoCommand; -import de.simpleeco.commands.SpawnCommand; import de.simpleeco.config.ConfigManager; import de.simpleeco.currency.BasicCurrency; import de.simpleeco.database.DatabaseManager; import de.simpleeco.listeners.PlayerJoinListener; +import de.simpleeco.listeners.PlayerDeathListener; import de.simpleeco.listeners.VillagerInteractListener; import de.simpleeco.pricing.PriceManager; import de.simpleeco.scoreboard.ScoreboardManager; import de.simpleeco.tasks.PriceRegressionTask; +import de.simpleeco.tasks.VillagerLookTask; import de.simpleeco.trading.CustomVillagerTrader; import de.simpleeco.villager.ShopVillagerManager; import de.simpleeco.bank.BankManager; @@ -42,6 +43,7 @@ public class SimpleEcoPlugin extends JavaPlugin { private AtmTrader atmTrader; private ScoreboardManager scoreboardManager; private PriceRegressionTask regressionTask; + private VillagerLookTask villagerLookTask; @Override public void onEnable() { @@ -90,7 +92,7 @@ public void onEnable() { getLogger().info("ATM-Trading-System initialisiert"); // 9. Scoreboard-Manager initialisieren - this.scoreboardManager = new ScoreboardManager(this, configManager, currency); + this.scoreboardManager = new ScoreboardManager(this, configManager, currency, bankManager); getLogger().info("Scoreboard-Manager initialisiert"); // 10. Villager-Trading initialisieren @@ -109,8 +111,16 @@ public void onEnable() { this.regressionTask = PriceRegressionTask.start(this); getLogger().info("Preis-Regression-Task gestartet"); - getLogger().info("SimpleEco Plugin erfolgreich aktiviert!"); - + // 14. Villager-Look-Task starten (falls aktiviert) + if (configManager.getConfig().getBoolean("villagerBehavior.lookAtPlayers", true)) { + double lookDistance = configManager.getConfig().getDouble("villagerBehavior.lookDistance", 8.0); + long updateInterval = configManager.getConfig().getLong("villagerBehavior.lookUpdateInterval", 20); + + this.villagerLookTask = VillagerLookTask.start(this, shopVillagerManager, atmVillagerManager, + lookDistance, updateInterval); + getLogger().info("Villager-Look-Task gestartet"); + } + } catch (Exception e) { getLogger().log(Level.SEVERE, "Fehler beim Starten des Plugins:", e); getServer().getPluginManager().disablePlugin(this); @@ -128,6 +138,12 @@ public void onDisable() { getLogger().info("Preis-Regression-Task gestoppt"); } + // Villager-Look-Task stoppen + if (villagerLookTask != null && !villagerLookTask.isCancelled()) { + villagerLookTask.cancel(); + getLogger().info("Villager-Look-Task gestoppt"); + } + // Scoreboard-Manager herunterfahren if (scoreboardManager != null) { scoreboardManager.shutdown(); @@ -150,15 +166,10 @@ public void onDisable() { * Registriert alle Plugin-Commands */ private void registerCommands() { - // Eco Command - EcoCommand ecoCommand = new EcoCommand(this, currency, configManager); + // SimpleEco Command (vereinigt alle Subcommands) + EcoCommand ecoCommand = new EcoCommand(this, currency, configManager, shopVillagerManager, atmVillagerManager); getCommand("eco").setExecutor(ecoCommand); getCommand("eco").setTabCompleter(ecoCommand); - - // Spawn Command - SpawnCommand spawnCommand = new SpawnCommand(this, configManager, shopVillagerManager, atmVillagerManager); - getCommand("spawn").setExecutor(spawnCommand); - getCommand("spawn").setTabCompleter(spawnCommand); } /** @@ -167,6 +178,8 @@ private void registerCommands() { private void registerListeners() { getServer().getPluginManager().registerEvents( new PlayerJoinListener(currency, configManager, scoreboardManager), this); + getServer().getPluginManager().registerEvents( + new PlayerDeathListener(this, configManager, bankManager, scoreboardManager), this); getServer().getPluginManager().registerEvents( new VillagerInteractListener(villagerTrader, shopVillagerManager, atmTrader, atmVillagerManager, scoreboardManager), this); } diff --git a/src/main/java/de/simpleeco/bank/AtmTrader.java b/src/main/java/de/simpleeco/bank/AtmTrader.java index aeaf337..25f35a9 100644 --- a/src/main/java/de/simpleeco/bank/AtmTrader.java +++ b/src/main/java/de/simpleeco/bank/AtmTrader.java @@ -250,7 +250,11 @@ private ItemStack createGlassPane() { */ public void openDepositMenu(Player player) { AtmSession session = activeSessions.get(player); - if (session == null) return; + if (session == null) { + player.sendMessage(configManager.getMessage("prefix") + + "§cATM-Session ungültig. Bitte öffnen Sie das ATM erneut."); + return; + } Inventory depositInventory = Bukkit.createInventory(null, 27, "§2§lGeld einzahlen"); @@ -271,9 +275,16 @@ public void openDepositMenu(Player player) { // Zurück-Button depositInventory.setItem(18, createBackButton()); - player.openInventory(depositInventory); + // Session-Update BEFORE opening inventory session.currentInventory = depositInventory; session.menuType = AtmSession.MenuType.DEPOSIT; + + // Session nochmals in Map speichern (sicherheitshalber) + activeSessions.put(player, session); + + player.openInventory(depositInventory); + + plugin.getLogger().info("Einzahl-Menü für " + player.getName() + " geöffnet. Session-Typ: " + session.menuType); } /** @@ -283,7 +294,11 @@ public void openDepositMenu(Player player) { */ public void openWithdrawMenu(Player player) { AtmSession session = activeSessions.get(player); - if (session == null) return; + if (session == null) { + player.sendMessage(configManager.getMessage("prefix") + + "§cATM-Session ungültig. Bitte öffnen Sie das ATM erneut."); + return; + } Inventory withdrawInventory = Bukkit.createInventory(null, 27, "§c§lGeld abheben"); @@ -304,9 +319,16 @@ public void openWithdrawMenu(Player player) { // Zurück-Button withdrawInventory.setItem(18, createBackButton()); - player.openInventory(withdrawInventory); + // Session-Update BEFORE opening inventory session.currentInventory = withdrawInventory; session.menuType = AtmSession.MenuType.WITHDRAW; + + // Session nochmals in Map speichern (sicherheitshalber) + activeSessions.put(player, session); + + player.openInventory(withdrawInventory); + + plugin.getLogger().info("Abheb-Menü für " + player.getName() + " geöffnet. Session-Typ: " + session.menuType); } /** @@ -317,17 +339,26 @@ public void openWithdrawMenu(Player player) { * @return ItemStack für den Button */ private ItemStack createAmountButton(double amount, String displayName) { - ItemStack item = new ItemStack(Material.GOLD_NUGGET); + Material material = amount > 0 ? Material.GOLD_NUGGET : Material.GRAY_STAINED_GLASS_PANE; + ItemStack item = new ItemStack(material); ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(displayName); - List lore = new ArrayList<>(); - lore.add("§7Betrag: §e" + bankManager.formatAmount(amount)); - lore.add(""); - lore.add("§eLinksklick: Ausführen"); - meta.setLore(lore); - item.setItemMeta(meta); + if (amount > 0) { + meta.setDisplayName(displayName); + List lore = new ArrayList<>(); + lore.add("§7Betrag: §e" + bankManager.formatAmount(amount)); + lore.add(""); + lore.add("§eLinksklick: Ausführen"); + meta.setLore(lore); + } else { + meta.setDisplayName("§8" + displayName.replace("§a", "").replace("§c", "") + " §7(Nicht verfügbar)"); + List lore = new ArrayList<>(); + lore.add("§7Nicht genügend Guthaben"); + lore.add("§7für diese Aktion verfügbar."); + meta.setLore(lore); + } + item.setItemMeta(meta); return item; } @@ -359,7 +390,18 @@ private ItemStack createBackButton() { */ public void handleInventoryClick(Player player, int slot, ClickType clickType, ItemStack item) { AtmSession session = activeSessions.get(player); - if (session == null || item == null || !item.hasItemMeta()) return; + if (session == null) { + plugin.getLogger().warning("Keine aktive ATM-Session für Spieler " + player.getName()); + player.sendMessage(configManager.getMessage("prefix") + + "§cATM-Session ungültig. Bitte öffnen Sie das ATM erneut."); + player.closeInventory(); + return; + } + + // Null-Check für Item und ItemMeta + if (item == null || !item.hasItemMeta() || item.getItemMeta().getDisplayName() == null) { + return; + } String itemName = item.getItemMeta().getDisplayName(); @@ -367,6 +409,7 @@ public void handleInventoryClick(Player player, int slot, ClickType clickType, I case MAIN -> handleMainMenuClick(player, session, slot, itemName); case DEPOSIT -> handleDepositMenuClick(player, session, slot, itemName); case WITHDRAW -> handleWithdrawMenuClick(player, session, slot, itemName); + default -> { /* Unbekannter Menü-Typ */ } } } @@ -375,8 +418,12 @@ public void handleInventoryClick(Player player, int slot, ClickType clickType, I */ private void handleMainMenuClick(Player player, AtmSession session, int slot, String itemName) { switch (slot) { - case 14 -> openDepositMenu(player); // Einzahlen - case 16 -> openWithdrawMenu(player); // Abheben + case 14 -> { // Einzahlen + openDepositMenu(player); + } + case 16 -> { // Abheben + openWithdrawMenu(player); + } case 22 -> { // Schließen player.closeInventory(); activeSessions.remove(player); @@ -388,7 +435,7 @@ private void handleMainMenuClick(Player player, AtmSession session, int slot, St * Behandelt Klicks im Einzahl-Menü */ private void handleDepositMenuClick(Player player, AtmSession session, int slot, String itemName) { - if (slot == 18) { // Zurück + if (slot == 18 && itemName != null && itemName.contains("Zurück")) { // Zurück-Button openAtmMenu(player); return; } @@ -403,7 +450,7 @@ private void handleDepositMenuClick(Player player, AtmSession session, int slot, * Behandelt Klicks im Abheb-Menü */ private void handleWithdrawMenuClick(Player player, AtmSession session, int slot, String itemName) { - if (slot == 18) { // Zurück + if (slot == 18 && itemName != null && itemName.contains("Zurück")) { // Zurück-Button openAtmMenu(player); return; } @@ -418,32 +465,73 @@ private void handleWithdrawMenuClick(Player player, AtmSession session, int slot * Ermittelt den Betrag basierend auf dem Slot */ private double getAmountFromSlot(int slot, AtmSession session, boolean isDeposit) { - return switch (slot) { + double amount = switch (slot) { case 10 -> 10.0; case 11 -> 100.0; case 12 -> 1000.0; case 13 -> isDeposit ? session.cashBalance : session.bankBalance; default -> 0.0; }; + + // Zusätzliche Validierung für "Alles" einzahlen/abheben + if (slot == 13) { + if (isDeposit && session.cashBalance <= 0) { + return 0.0; // Kein Bargeld verfügbar + } + if (!isDeposit && session.bankBalance <= 0) { + return 0.0; // Kein Bank-Guthaben verfügbar + } + } + + return amount; } /** * Führt eine Einzahlung durch */ private void performDeposit(Player player, double amount) { - if (amount <= 0) return; + if (amount <= 0) { + player.sendMessage(configManager.getMessage("prefix") + + "§cUngültiger Betrag für die Einzahlung!"); + return; + } + AtmSession session = activeSessions.get(player); + if (session == null) { + player.sendMessage(configManager.getMessage("prefix") + + "§cATM-Session ungültig. Bitte öffnen Sie das ATM erneut."); + return; + } + + // Prüfe ob genügend Bargeld vorhanden ist + if (amount > session.cashBalance) { + player.sendMessage(configManager.getMessage("prefix") + + "§cNicht genügend Bargeld verfügbar! Sie haben nur " + + bankManager.formatAmount(session.cashBalance)); + return; + } + bankManager.depositToBank(player, amount).thenAccept(success -> { Bukkit.getScheduler().runTask(plugin, () -> { if (success) { player.sendMessage(configManager.getMessage("prefix") + "§a" + bankManager.formatAmount(amount) + " erfolgreich eingezahlt!"); + // Session aktualisieren + session.cashBalance -= amount; + session.bankBalance += amount; openAtmMenu(player); // Menü aktualisieren } else { player.sendMessage(configManager.getMessage("prefix") + - "§cNicht genügend Bargeld verfügbar!"); + "§cFehler bei der Einzahlung! Bitte versuchen Sie es erneut."); } }); + }).exceptionally(throwable -> { + plugin.getLogger().severe("Unerwarteter Fehler bei ATM-Einzahlung: " + throwable.getMessage()); + Bukkit.getScheduler().runTask(plugin, () -> { + player.sendMessage(configManager.getMessage("prefix") + + "§cEin unerwarteter Fehler ist aufgetreten. Bitte wenden Sie sich an einen Administrator."); + }); + return null; }); } @@ -451,19 +539,48 @@ private void performDeposit(Player player, double amount) { * Führt eine Abhebung durch */ private void performWithdraw(Player player, double amount) { - if (amount <= 0) return; + if (amount <= 0) { + player.sendMessage(configManager.getMessage("prefix") + + "§cUngültiger Betrag für die Abhebung!"); + return; + } + + AtmSession session = activeSessions.get(player); + if (session == null) { + player.sendMessage(configManager.getMessage("prefix") + + "§cATM-Session ungültig. Bitte öffnen Sie das ATM erneut."); + return; + } + // Prüfe ob genügend Bank-Guthaben vorhanden ist + if (amount > session.bankBalance) { + player.sendMessage(configManager.getMessage("prefix") + + "§cNicht genügend Bank-Guthaben verfügbar! Sie haben nur " + + bankManager.formatAmount(session.bankBalance)); + return; + } + bankManager.withdrawFromBank(player, amount).thenAccept(success -> { Bukkit.getScheduler().runTask(plugin, () -> { if (success) { player.sendMessage(configManager.getMessage("prefix") + "§a" + bankManager.formatAmount(amount) + " erfolgreich abgehoben!"); + // Session aktualisieren + session.bankBalance -= amount; + session.cashBalance += amount; openAtmMenu(player); // Menü aktualisieren } else { player.sendMessage(configManager.getMessage("prefix") + - "§cNicht genügend Bank-Guthaben verfügbar!"); + "§cFehler bei der Abhebung! Bitte versuchen Sie es erneut."); } }); + }).exceptionally(throwable -> { + plugin.getLogger().severe("Unerwarteter Fehler bei ATM-Abhebung: " + throwable.getMessage()); + Bukkit.getScheduler().runTask(plugin, () -> { + player.sendMessage(configManager.getMessage("prefix") + + "§cEin unerwarteter Fehler ist aufgetreten. Bitte wenden Sie sich an einen Administrator."); + }); + return null; }); } diff --git a/src/main/java/de/simpleeco/bank/AtmVillagerManager.java b/src/main/java/de/simpleeco/bank/AtmVillagerManager.java index 94db07b..0a82cfb 100644 --- a/src/main/java/de/simpleeco/bank/AtmVillagerManager.java +++ b/src/main/java/de/simpleeco/bank/AtmVillagerManager.java @@ -80,8 +80,17 @@ private void configureAtmVillager(Villager villager) { villager.setCustomName("§6§l" + villagerName); villager.setCustomNameVisible(true); - // Bewegung verhindern - villager.setAI(false); + // Bewegung konfigurieren - AI nur teilweise deaktivieren für Look-Funktionalität + boolean lookAtPlayers = configManager.getConfig().getBoolean("villagerBehavior.lookAtPlayers", true); + if (lookAtPlayers) { + // AI aktiviert lassen, aber Bewegung einschränken + villager.setAI(true); + villager.setCollidable(false); // Keine Kollision mit anderen Entities + } else { + // Vollständig deaktivieren + villager.setAI(false); + } + villager.setSilent(true); villager.setInvulnerable(true); villager.setRemoveWhenFarAway(false); diff --git a/src/main/java/de/simpleeco/bank/BankManager.java b/src/main/java/de/simpleeco/bank/BankManager.java index ba586a5..e12d5df 100644 --- a/src/main/java/de/simpleeco/bank/BankManager.java +++ b/src/main/java/de/simpleeco/bank/BankManager.java @@ -67,7 +67,12 @@ public CompletableFuture setCashBalance(UUID playerId, double amount) { * @return CompletableFuture das abgeschlossen wird wenn die Operation fertig ist */ public CompletableFuture setCashBalance(Player player, double amount) { - return setCashBalance(player.getUniqueId(), amount); + return setCashBalance(player.getUniqueId(), amount).thenRun(() -> { + // Scoreboard benachrichtigen + if (plugin.getScoreboardManager() != null) { + plugin.getScoreboardManager().onBalanceChanged(player); + } + }); } /** @@ -89,7 +94,13 @@ public CompletableFuture addCashBalance(UUID playerId, double amount) { * @return CompletableFuture mit dem neuen Bargeld-Betrag */ public CompletableFuture addCashBalance(Player player, double amount) { - return addCashBalance(player.getUniqueId(), amount); + return addCashBalance(player.getUniqueId(), amount).thenApply(newBalance -> { + // Scoreboard benachrichtigen + if (plugin.getScoreboardManager() != null) { + plugin.getScoreboardManager().onBalanceChanged(player); + } + return newBalance; + }); } /** @@ -183,7 +194,12 @@ public CompletableFuture setBankBalance(UUID playerId, double amount) { * @return CompletableFuture das abgeschlossen wird wenn die Operation fertig ist */ public CompletableFuture setBankBalance(Player player, double amount) { - return setBankBalance(player.getUniqueId(), amount); + return setBankBalance(player.getUniqueId(), amount).thenRun(() -> { + // Scoreboard benachrichtigen + if (plugin.getScoreboardManager() != null) { + plugin.getScoreboardManager().onBankBalanceChanged(player); + } + }); } /** @@ -205,7 +221,13 @@ public CompletableFuture addBankBalance(UUID playerId, double amount) { * @return CompletableFuture mit dem neuen Bank-Guthaben */ public CompletableFuture addBankBalance(Player player, double amount) { - return addBankBalance(player.getUniqueId(), amount); + return addBankBalance(player.getUniqueId(), amount).thenApply(newBalance -> { + // Scoreboard benachrichtigen + if (plugin.getScoreboardManager() != null) { + plugin.getScoreboardManager().onBankBalanceChanged(player); + } + return newBalance; + }); } /** diff --git a/src/main/java/de/simpleeco/commands/EcoCommand.java b/src/main/java/de/simpleeco/commands/EcoCommand.java index e97287c..4a3a35f 100644 --- a/src/main/java/de/simpleeco/commands/EcoCommand.java +++ b/src/main/java/de/simpleeco/commands/EcoCommand.java @@ -4,6 +4,9 @@ import de.simpleeco.config.ConfigManager; import de.simpleeco.currency.BasicCurrency; import de.simpleeco.scoreboard.ScoreboardManager; +import de.simpleeco.villager.ShopVillagerManager; +import de.simpleeco.bank.AtmVillagerManager; +import de.simpleeco.bank.BankManager; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -15,26 +18,35 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import java.util.concurrent.CompletableFuture; /** - * Command-Handler für alle Economy-Commands + * Haupt-Command-Handler für alle SimpleEco-Commands * * Behandelt: - * - /eco balance [Spieler] - Zeigt Kontostand an + * - /eco balance [Spieler] - Zeigt Kontostand an (Bargeld und Bank) * - /eco pay - Überweist Geld + * - /eco spawn - Spawnt Entities */ public class EcoCommand implements CommandExecutor, TabCompleter { private final SimpleEcoPlugin plugin; private final BasicCurrency currency; + private final BankManager bankManager; private final ConfigManager configManager; private final ScoreboardManager scoreboardManager; + private final SpawnCommand spawnCommand; - public EcoCommand(SimpleEcoPlugin plugin, BasicCurrency currency, ConfigManager configManager) { + public EcoCommand(SimpleEcoPlugin plugin, BasicCurrency currency, ConfigManager configManager, + ShopVillagerManager shopVillagerManager, AtmVillagerManager atmVillagerManager) { this.plugin = plugin; this.currency = currency; + this.bankManager = plugin.getBankManager(); this.configManager = configManager; this.scoreboardManager = plugin.getScoreboardManager(); + + // SpawnCommand als Subcommand-Handler erstellen + this.spawnCommand = new SpawnCommand(plugin, configManager, shopVillagerManager, atmVillagerManager, currency); } @Override @@ -53,8 +65,14 @@ public boolean onCommand(CommandSender sender, Command command, String label, St String subCommand = args[0].toLowerCase(); switch (subCommand) { - case "balance", "bal", "b" -> handleBalanceCommand(sender, args); - case "pay", "transfer", "send" -> handlePayCommand(sender, args); + case "balance", "bal" -> handleBalance(sender, args); + case "pay" -> handlePay(sender, args); + case "spawn" -> { + // Spawn-Argumente weiterleiten (ohne das "spawn" Argument) + String[] spawnArgs = Arrays.copyOfRange(args, 1, args.length); + return spawnCommand.handleSpawnCommand(sender, spawnArgs); + } + case "reload" -> handleReload(sender); case "help", "?" -> sendUsage(sender); default -> sendUsage(sender); } @@ -68,7 +86,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St * @param sender Der Command-Sender * @param args Command-Argumente */ - private void handleBalanceCommand(CommandSender sender, String[] args) { + private void handleBalance(CommandSender sender, String[] args) { if (args.length == 1) { // Eigenen Kontostand anzeigen if (!(sender instanceof Player player)) { @@ -77,17 +95,7 @@ private void handleBalanceCommand(CommandSender sender, String[] args) { return; } - currency.getBalance(player).thenAccept(balance -> { - String message = configManager.getMessage("balanceYour", - "balance", currency.formatAmount(balance), - "currency", configManager.getCurrencyName()); - player.sendMessage(configManager.getMessage("prefix") + message); - }).exceptionally(throwable -> { - player.sendMessage(configManager.getMessage("prefix") + - "§cFehler beim Laden des Kontostands!"); - plugin.getLogger().severe("Fehler beim Laden des Kontostands: " + throwable.getMessage()); - return null; - }); + showPlayerBalance(sender, player); } else if (args.length == 2) { // Kontostand eines anderen Spielers anzeigen @@ -106,32 +114,166 @@ private void handleBalanceCommand(CommandSender sender, String[] args) { return; } - currency.getBalance(targetPlayer).thenAccept(balance -> { - String message = configManager.getMessage("balanceOther", - "player", targetPlayer.getName(), - "balance", currency.formatAmount(balance), - "currency", configManager.getCurrencyName()); - sender.sendMessage(configManager.getMessage("prefix") + message); - }).exceptionally(throwable -> { + showPlayerBalance(sender, targetPlayer); + + } else if (args.length == 4) { + // Balance add/remove: /eco balance + if (!sender.hasPermission("simpleeco.balance.admin")) { sender.sendMessage(configManager.getMessage("prefix") + - "§cFehler beim Laden des Kontostands!"); - plugin.getLogger().severe("Fehler beim Laden des Kontostands: " + throwable.getMessage()); - return null; - }); + configManager.getMessage("noPermission")); + return; + } + + String targetName = args[1]; + String operation = args[2].toLowerCase(); + String amountString = args[3]; + + // Ziel-Spieler finden + Player targetPlayer = Bukkit.getPlayer(targetName); + if (targetPlayer == null) { + sender.sendMessage(configManager.getMessage("prefix") + + configManager.getMessage("playerNotFound")); + return; + } + + // Operation validieren + if (!operation.equals("add") && !operation.equals("remove")) { + sender.sendMessage(configManager.getMessage("prefix") + + "§cVerwendung: /eco balance "); + return; + } + + // Betrag parsen + double amount; + try { + amount = Double.parseDouble(amountString); + } catch (NumberFormatException e) { + sender.sendMessage(configManager.getMessage("prefix") + + configManager.getMessage("invalidAmount")); + return; + } + + // Betrag muss positiv sein + if (amount <= 0) { + sender.sendMessage(configManager.getMessage("prefix") + + configManager.getMessage("invalidAmount")); + return; + } + + // Operation ausführen + if (operation.equals("add")) { + handleBalanceAdd(sender, targetPlayer, amount); + } else { + handleBalanceRemove(sender, targetPlayer, amount); + } } else { sender.sendMessage(configManager.getMessage("prefix") + - "§cVerwendung: /eco balance [Spieler]"); + "§cVerwendung: /eco balance [Spieler] [add|remove] [Betrag]"); + } + } + + /** + * Behandelt den Reload-Command + * + * @param sender Der Command-Sender + */ + private void handleReload(CommandSender sender) { + if (!sender.hasPermission("simpleeco.admin")) { + sender.sendMessage(configManager.getMessage("prefix") + + configManager.getMessage("noPermission")); + return; + } + + try { + // Konfiguration neu laden + configManager.reload(); + + // Scoreboard-Manager neu laden + if (scoreboardManager != null) { + scoreboardManager.reload(); + } + + sender.sendMessage(configManager.getMessage("prefix") + + "§a§l✓ §7Konfiguration erfolgreich neu geladen!"); + sender.sendMessage(configManager.getMessage("prefix") + + "§7Scoreboards wurden aktualisiert."); + + } catch (Exception e) { + sender.sendMessage(configManager.getMessage("prefix") + + "§c§l✗ §7Fehler beim Neuladen der Konfiguration!"); + plugin.getLogger().severe("Fehler beim Neuladen der Konfiguration: " + e.getMessage()); } } + /** + * Zeigt die Balance eines Spielers an (Bargeld und Bank) + * + * @param sender Der Command-Sender + * @param targetPlayer Der Spieler dessen Balance angezeigt wird + */ + private void showPlayerBalance(CommandSender sender, Player targetPlayer) { + // Beide Guthaben parallel laden + CompletableFuture cashFuture = bankManager.getCashBalance(targetPlayer); + CompletableFuture bankFuture = bankManager.getBankBalance(targetPlayer); + + CompletableFuture.allOf(cashFuture, bankFuture).thenRun(() -> { + try { + double cashBalance = cashFuture.get(); + double bankBalance = bankFuture.get(); + double totalBalance = cashBalance + bankBalance; + + String currencySymbol = configManager.getConfig().getString("currency.symbol", "G"); + boolean isOwnBalance = sender.equals(targetPlayer); + + // Header + sender.sendMessage("§8§m §r §6§lKontostand" + + (isOwnBalance ? "" : " von §e" + targetPlayer.getName()) + " §8§m "); + + // Bargeld + sender.sendMessage("§a💵 Bargeld: §f" + formatAmount(cashBalance) + " " + currencySymbol); + + // Bank + sender.sendMessage("§6🏦 Bank: §f" + formatAmount(bankBalance) + " " + currencySymbol); + + // Trennlinie + sender.sendMessage("§8§m "); + + // Gesamt + sender.sendMessage("§e💰 Gesamt: §f" + formatAmount(totalBalance) + " " + currencySymbol); + + sender.sendMessage("§8§m "); + + } catch (Exception e) { + sender.sendMessage(configManager.getMessage("prefix") + + "§cFehler beim Laden der Kontostände!"); + plugin.getLogger().severe("Fehler beim Laden der Kontostände: " + e.getMessage()); + } + }).exceptionally(throwable -> { + sender.sendMessage(configManager.getMessage("prefix") + + "§cFehler beim Laden der Kontostände!"); + plugin.getLogger().severe("Fehler beim Laden der Kontostände: " + throwable.getMessage()); + return null; + }); + } + + /** + * Formatiert einen Betrag + * + * @param amount Der Betrag + * @return Formatierter String + */ + private String formatAmount(double amount) { + return String.format("%.2f", amount); + } + /** * Behandelt den Pay-Command * * @param sender Der Command-Sender * @param args Command-Argumente */ - private void handlePayCommand(CommandSender sender, String[] args) { + private void handlePay(CommandSender sender, String[] args) { if (!(sender instanceof Player player)) { sender.sendMessage(configManager.getMessage("prefix") + "§cDieser Befehl kann nur von Spielern ausgeführt werden!"); @@ -215,6 +357,104 @@ private void handlePayCommand(CommandSender sender, String[] args) { }); } + /** + * Fügt Geld zum Bargeld-Kontostand eines Spielers hinzu + * + * @param sender Der Command-Sender + * @param targetPlayer Der Ziel-Spieler + * @param amount Der hinzuzufügende Betrag + */ + private void handleBalanceAdd(CommandSender sender, Player targetPlayer, double amount) { + currency.addBalance(targetPlayer, amount).thenAccept(newBalance -> { + // Erfolgsnachrichten senden + String currencySymbol = configManager.getConfig().getString("currency.symbol", "G"); + + sender.sendMessage(configManager.getMessage("prefix") + + "§a§l✓ §7Dem Spieler §e" + targetPlayer.getName() + + " §7wurden §a" + formatAmount(amount) + " " + currencySymbol + + " §7hinzugefügt!"); + + sender.sendMessage(configManager.getMessage("prefix") + + "§7Neuer Bargeld-Kontostand: §f" + formatAmount(newBalance) + " " + currencySymbol); + + // Dem Ziel-Spieler eine Benachrichtigung senden + targetPlayer.sendMessage(configManager.getMessage("prefix") + + "§a§l✓ §7Dir wurden §a" + formatAmount(amount) + " " + currencySymbol + + " §7zu deinem Bargeld hinzugefügt!"); + + // Scoreboard des Ziel-Spielers aktualisieren + if (scoreboardManager != null) { + Bukkit.getScheduler().runTaskLater(plugin, () -> { + scoreboardManager.updatePlayerScoreboard(targetPlayer); + }, 2L); // 0.1 Sekunden Verzögerung + } + + }).exceptionally(throwable -> { + sender.sendMessage(configManager.getMessage("prefix") + + "§cFehler beim Hinzufügen des Betrags!"); + return null; + }); + } + + /** + * Entfernt Geld vom Bargeld-Kontostand eines Spielers + * + * @param sender Der Command-Sender + * @param targetPlayer Der Ziel-Spieler + * @param amount Der zu entfernende Betrag + */ + private void handleBalanceRemove(CommandSender sender, Player targetPlayer, double amount) { + // Erst prüfen, ob genügend Bargeld vorhanden ist + currency.getBalance(targetPlayer).thenAccept(currentBalance -> { + if (currentBalance < amount) { + String currencySymbol = configManager.getConfig().getString("currency.symbol", "G"); + sender.sendMessage(configManager.getMessage("prefix") + + "§c§l✗ §7Der Spieler §e" + targetPlayer.getName() + + " §7hat nicht genügend Bargeld!"); + sender.sendMessage(configManager.getMessage("prefix") + + "§7Verfügbares Bargeld: §f" + formatAmount(currentBalance) + " " + currencySymbol + + " §8| §7Benötigt: §f" + formatAmount(amount) + " " + currencySymbol); + return; + } + + // Betrag entfernen + currency.removeBalance(targetPlayer, amount).thenAccept(newBalance -> { + // Erfolgsnachrichten senden + String currencySymbol = configManager.getConfig().getString("currency.symbol", "G"); + + sender.sendMessage(configManager.getMessage("prefix") + + "§c§l✓ §7Dem Spieler §e" + targetPlayer.getName() + + " §7wurden §c" + formatAmount(amount) + " " + currencySymbol + + " §7vom Bargeld entfernt!"); + + sender.sendMessage(configManager.getMessage("prefix") + + "§7Neuer Bargeld-Kontostand: §f" + formatAmount(newBalance) + " " + currencySymbol); + + // Dem Ziel-Spieler eine Benachrichtigung senden + targetPlayer.sendMessage(configManager.getMessage("prefix") + + "§c§l✗ §7Dir wurden §c" + formatAmount(amount) + " " + currencySymbol + + " §7von deinem Bargeld entfernt!"); + + // Scoreboard des Ziel-Spielers aktualisieren + if (scoreboardManager != null) { + Bukkit.getScheduler().runTaskLater(plugin, () -> { + scoreboardManager.updatePlayerScoreboard(targetPlayer); + }, 2L); // 0.1 Sekunden Verzögerung + } + + }).exceptionally(throwable -> { + sender.sendMessage(configManager.getMessage("prefix") + + "§cFehler beim Entfernen des Betrags!"); + return null; + }); + + }).exceptionally(throwable -> { + sender.sendMessage(configManager.getMessage("prefix") + + "§cFehler beim Prüfen des Kontostands!"); + return null; + }); + } + /** * Sendet die Verwendungshinweise * @@ -224,8 +464,25 @@ private void sendUsage(CommandSender sender) { sender.sendMessage("§8§m §r §6§lSimpleEco Commands §8§m "); sender.sendMessage("§e/eco balance [Spieler] §8- §7Zeigt Kontostand an"); sender.sendMessage("§e/eco pay §8- §7Überweist Geld"); + + // Admin-Commands nur anzeigen wenn Permission vorhanden + if (sender.hasPermission("simpleeco.balance.admin")) { + sender.sendMessage("§c/eco balance add §8- §7Fügt Geld hinzu"); + sender.sendMessage("§c/eco balance remove §8- §7Entfernt Geld"); + } + + // Spawn-Commands nur anzeigen wenn Permission vorhanden + if (sender.hasPermission("simpleeco.spawn")) { + sender.sendMessage("§e/eco spawn §8- §7Spawnt Entities"); + } + + // Reload-Command nur anzeigen wenn Permission vorhanden + if (sender.hasPermission("simpleeco.admin")) { + sender.sendMessage("§e/eco reload §8- §7Lädt die Konfiguration neu"); + } + sender.sendMessage("§e/eco help §8- §7Zeigt diese Hilfe an"); - sender.sendMessage("§8§m "); + sender.sendMessage("§8§m "); } @Override @@ -238,7 +495,18 @@ public List onTabComplete(CommandSender sender, Command command, String if (args.length == 1) { // Erste Ebene: Subcommands - List subCommands = Arrays.asList("balance", "pay", "help"); + List subCommands = new ArrayList<>(Arrays.asList("balance", "pay", "help")); + + // Spawn hinzufügen wenn Permission vorhanden + if (sender.hasPermission("simpleeco.spawn")) { + subCommands.add("spawn"); + } + + // Reload hinzufügen wenn Permission vorhanden + if (sender.hasPermission("simpleeco.admin")) { + subCommands.add("reload"); + } + String input = args[0].toLowerCase(); completions = subCommands.stream() @@ -258,10 +526,30 @@ public List onTabComplete(CommandSender sender, Command command, String if (sender instanceof Player player) { completions.remove(player.getName()); } + + } else if (subCommand.equals("spawn") && sender.hasPermission("simpleeco.spawn")) { + // Spawn-Subcommands + String[] spawnArgs = {args[1]}; + completions = spawnCommand.getSpawnTabComplete(sender, spawnArgs); + } + + } else if (args.length == 3) { + String subCommand = args[0].toLowerCase(); + + if (subCommand.equals("balance") && sender.hasPermission("simpleeco.balance.admin")) { + // Add/Remove subcommands für Balance-Command + String input = args[2].toLowerCase(); + completions = Arrays.asList("add", "remove").stream() + .filter(op -> op.startsWith(input)) + .collect(Collectors.toList()); + + } else if (subCommand.equals("pay")) { + // Betrag-Vorschläge für Pay-Command + completions = Arrays.asList("10", "50", "100", "500", "1000"); } - } else if (args.length == 3 && args[0].equalsIgnoreCase("pay")) { - // Betrag-Vorschläge für Pay-Command + } else if (args.length == 4 && args[0].equalsIgnoreCase("balance") && sender.hasPermission("simpleeco.balance.admin")) { + // Betrag-Vorschläge für Balance-Add/Remove completions = Arrays.asList("10", "50", "100", "500", "1000"); } diff --git a/src/main/java/de/simpleeco/commands/SpawnCommand.java b/src/main/java/de/simpleeco/commands/SpawnCommand.java index 0420683..7276afc 100644 --- a/src/main/java/de/simpleeco/commands/SpawnCommand.java +++ b/src/main/java/de/simpleeco/commands/SpawnCommand.java @@ -4,6 +4,7 @@ import de.simpleeco.config.ConfigManager; import de.simpleeco.villager.ShopVillagerManager; import de.simpleeco.bank.AtmVillagerManager; +import de.simpleeco.currency.BasicCurrency; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.command.Command; @@ -22,8 +23,8 @@ * Command-Handler für Spawn-Commands * * Behandelt: - * - /spawn villager - Spawnt einen Shop-Villager auf dem angeschauten Block - * - /spawn atm - Spawnt einen ATM-Villager auf dem angeschauten Block + * - /eco spawn shop - Spawnt einen Shop-Villager auf dem angeschauten Block + * - /eco spawn atm - Spawnt einen ATM-Villager auf dem angeschauten Block */ public class SpawnCommand implements CommandExecutor, TabCompleter { @@ -31,46 +32,16 @@ public class SpawnCommand implements CommandExecutor, TabCompleter { private final ConfigManager configManager; private final ShopVillagerManager shopVillagerManager; private final AtmVillagerManager atmVillagerManager; + private final BasicCurrency currency; public SpawnCommand(SimpleEcoPlugin plugin, ConfigManager configManager, - ShopVillagerManager shopVillagerManager, AtmVillagerManager atmVillagerManager) { + ShopVillagerManager shopVillagerManager, AtmVillagerManager atmVillagerManager, + BasicCurrency currency) { this.plugin = plugin; this.configManager = configManager; this.shopVillagerManager = shopVillagerManager; this.atmVillagerManager = atmVillagerManager; - } - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - // Permissions prüfen - if (!sender.hasPermission("simpleeco.spawn")) { - sender.sendMessage(configManager.getMessage("prefix") + - configManager.getMessage("noPermission")); - return true; - } - - // Nur Spieler können diesen Command ausführen - if (!(sender instanceof Player player)) { - sender.sendMessage(configManager.getMessage("prefix") + - "§cDieser Befehl kann nur von Spielern ausgeführt werden!"); - return true; - } - - if (args.length == 0) { - sendUsage(sender); - return true; - } - - String subCommand = args[0].toLowerCase(); - - switch (subCommand) { - case "villager" -> handleVillagerSpawn(player); - case "atm" -> handleAtmSpawn(player); - case "help", "?" -> sendUsage(sender); - default -> sendUsage(sender); - } - - return true; + this.currency = currency; } /** @@ -78,14 +49,19 @@ public boolean onCommand(CommandSender sender, Command command, String label, St * * @param player Der Spieler der den Command ausführt */ - private void handleVillagerSpawn(Player player) { - // Permissions für Villager-Spawn prüfen - if (!player.hasPermission("simpleeco.spawn.villager")) { + private void handleShopSpawn(Player player) { + // Permissions für Shop-Spawn prüfen + if (!player.hasPermission("simpleeco.spawn.shop")) { player.sendMessage(configManager.getMessage("prefix") + configManager.getMessage("noPermission")); return; } + // Kosten prüfen und abziehen + if (!checkAndChargeSpawnCost(player, "shopCost")) { + return; + } + // Block finden, auf den der Spieler schaut RayTraceResult rayTrace = player.rayTraceBlocks(5.0); if (rayTrace == null || rayTrace.getHitBlock() == null) { @@ -104,18 +80,20 @@ private void handleVillagerSpawn(Player player) { if (success) { player.sendMessage(configManager.getMessage("prefix") + configManager.getMessage("villagerSpawned")); - plugin.getLogger().info("Shop-Villager gespawnt von " + player.getName() + - " bei " + spawnLocation.getBlockX() + ", " + - spawnLocation.getBlockY() + ", " + spawnLocation.getBlockZ()); + // Shop-Villager erfolgreich gespawnt } else { player.sendMessage(configManager.getMessage("prefix") + configManager.getMessage("villagerSpawnFailed")); + // Kosten zurückerstatten bei Fehler + refundSpawnCost(player, "shopCost"); } } catch (Exception e) { player.sendMessage(configManager.getMessage("prefix") + configManager.getMessage("villagerSpawnFailed")); plugin.getLogger().severe("Fehler beim Spawnen des Shop-Villagers: " + e.getMessage()); + // Kosten zurückerstatten bei Fehler + refundSpawnCost(player, "shopCost"); } } @@ -132,6 +110,11 @@ private void handleAtmSpawn(Player player) { return; } + // Kosten prüfen und abziehen + if (!checkAndChargeSpawnCost(player, "atmCost")) { + return; + } + // Block finden, auf den der Spieler schaut RayTraceResult rayTrace = player.rayTraceBlocks(5.0); if (rayTrace == null || rayTrace.getHitBlock() == null) { @@ -149,19 +132,116 @@ private void handleAtmSpawn(Player player) { if (success) { player.sendMessage(configManager.getMessage("prefix") + - "§a§lATM-Villager erfolgreich gespawnt!"); - plugin.getLogger().info("ATM-Villager gespawnt von " + player.getName() + - " bei " + spawnLocation.getBlockX() + ", " + - spawnLocation.getBlockY() + ", " + spawnLocation.getBlockZ()); + configManager.getMessage("atmSpawned")); + // ATM-Villager erfolgreich gespawnt } else { player.sendMessage(configManager.getMessage("prefix") + - "§c§lFehler beim Spawnen des ATM-Villagers!"); + configManager.getMessage("atmSpawnFailed")); + // Kosten zurückerstatten bei Fehler + refundSpawnCost(player, "atmCost"); } } catch (Exception e) { player.sendMessage(configManager.getMessage("prefix") + - "§c§lFehler beim Spawnen des ATM-Villagers!"); + configManager.getMessage("atmSpawnFailed")); plugin.getLogger().severe("Fehler beim Spawnen des ATM-Villagers: " + e.getMessage()); + // Kosten zurückerstatten bei Fehler + refundSpawnCost(player, "atmCost"); + } + } + + /** + * Prüft und berechnet die Spawn-Kosten + * + * @param player Der Spieler + * @param costConfigKey Der Konfigurationsschlüssel für die Kosten + * @return true wenn Kosten erfolgreich abgezogen oder nicht erforderlich + */ + private boolean checkAndChargeSpawnCost(Player player, String costConfigKey) { + // Prüfen ob Kosten aktiviert sind + if (!configManager.getConfig().getBoolean("spawnCosts.enabled", true)) { + return true; + } + + // Kosten aus Konfiguration laden + double cost = configManager.getConfig().getDouble("spawnCosts." + costConfigKey, 0.0); + + if (cost <= 0) { + return true; // Keine Kosten konfiguriert + } + + // Prüfen ob Kosten für alle Spieler erzwungen werden sollen + boolean enforceForAll = configManager.getConfig().getBoolean("spawnCosts.enforceForAll", true); + + if (!enforceForAll) { + // Nur normale Spieler zahlen - Admins sind befreit + boolean freeForAdmins = configManager.getConfig().getBoolean("spawnCosts.freeForAdmins", false); + if (freeForAdmins && player.hasPermission("simpleeco.spawn.free")) { + player.sendMessage(configManager.getMessage("prefix") + + "§a§lKostenloses Spawning §7(Admin-Berechtigung)"); + return true; + } + } + + // Wenn enforceForAll = true ist, zahlen alle Spieler (auch OPs/Admins) + + try { + // Guthaben prüfen (synchron für bessere UX) + double balance = currency.getBalance(player.getUniqueId()).get(); + if (balance < cost) { + String message = configManager.getMessage("insufficientFundsForSpawn", + "amount", String.valueOf(cost), + "currency", configManager.getConfig().getString("currency.symbol", "G")); + player.sendMessage(configManager.getMessage("prefix") + message); + return false; + } + + // Geld abziehen + currency.removeBalance(player.getUniqueId(), cost).get(); + String message = configManager.getMessage("spawnCostCharged", + "amount", String.valueOf(cost), + "currency", configManager.getConfig().getString("currency.symbol", "G")); + player.sendMessage(configManager.getMessage("prefix") + message); + + return true; + } catch (Exception e) { + player.sendMessage(configManager.getMessage("prefix") + + "§cFehler beim Verarbeiten der Zahlung!"); + plugin.getLogger().severe("Fehler beim Spawn-Kosten abziehen: " + e.getMessage()); + return false; + } + } + + /** + * Erstattet Spawn-Kosten zurück bei Fehlern + * + * @param player Der Spieler + * @param costConfigKey Der Konfigurationsschlüssel für die Kosten + */ + private void refundSpawnCost(Player player, String costConfigKey) { + // Prüfen ob Kosten aktiviert sind + if (!configManager.getConfig().getBoolean("spawnCosts.enabled", true)) { + return; + } + + // Prüfen ob Spieler kostenlose Berechtigung hat + if (player.hasPermission("simpleeco.spawn.free") && + configManager.getConfig().getBoolean("spawnCosts.freeForAdmins", true)) { + return; + } + + double cost = configManager.getConfig().getDouble("spawnCosts." + costConfigKey, 0.0); + + if (cost > 0) { + try { + currency.addBalance(player.getUniqueId(), cost).get(); + String message = configManager.getMessage("spawnCostRefunded", + "amount", String.valueOf(cost), + "currency", configManager.getConfig().getString("currency.symbol", "G")); + player.sendMessage(configManager.getMessage("prefix") + message); + } catch (Exception e) { + plugin.getLogger().severe("Fehler beim Zurückerstatten der Spawn-Kosten: " + e.getMessage()); + } } } @@ -172,12 +252,77 @@ private void handleAtmSpawn(Player player) { */ private void sendUsage(CommandSender sender) { sender.sendMessage("§8§m §r §6§lSimpleEco Spawn Commands §8§m "); - sender.sendMessage("§e/spawn villager §8- §7Spawnt einen Shop-Villager"); - sender.sendMessage("§e/spawn atm §8- §7Spawnt einen ATM-Villager"); - sender.sendMessage("§e/spawn help §8- §7Zeigt diese Hilfe an"); + sender.sendMessage("§e/eco spawn shop §8- §7Spawnt einen Shop-Villager"); + sender.sendMessage("§e/eco spawn atm §8- §7Spawnt einen ATM-Villager"); + sender.sendMessage("§e/eco spawn help §8- §7Zeigt diese Hilfe an"); + + // Kosteneninformationen anzeigen + if (configManager.getConfig().getBoolean("spawnCosts.enabled", true)) { + sender.sendMessage("§8§m "); + sender.sendMessage("§c§lKosten:"); + double shopCost = configManager.getConfig().getDouble("spawnCosts.shopCost", 0.0); + double atmCost = configManager.getConfig().getDouble("spawnCosts.atmCost", 0.0); + String symbol = configManager.getConfig().getString("currency.symbol", "G"); + + if (shopCost > 0) { + sender.sendMessage("§7Shop: §e" + shopCost + " " + symbol); + } + if (atmCost > 0) { + sender.sendMessage("§7ATM: §e" + atmCost + " " + symbol); + } + + // Informationen über kostenlose Berechtigung + boolean enforceForAll = configManager.getConfig().getBoolean("spawnCosts.enforceForAll", true); + boolean freeForAdmins = configManager.getConfig().getBoolean("spawnCosts.freeForAdmins", false); + + if (enforceForAll) { + sender.sendMessage("§7§o(Alle Spieler zahlen Kosten)"); + } else if (freeForAdmins && sender.hasPermission("simpleeco.spawn.free")) { + sender.sendMessage("§a§o(Du hast kostenlose Berechtigung)"); + } + } + sender.sendMessage("§8§m "); } + // Diese Methode wird jetzt von der übergeordneten EcoCommand aufgerufen + public boolean handleSpawnCommand(CommandSender sender, String[] args) { + // Permissions prüfen + if (!sender.hasPermission("simpleeco.spawn")) { + sender.sendMessage(configManager.getMessage("prefix") + + configManager.getMessage("noPermission")); + return true; + } + + // Nur Spieler können diesen Command ausführen + if (!(sender instanceof Player player)) { + sender.sendMessage(configManager.getMessage("prefix") + + "§cDieser Befehl kann nur von Spielern ausgeführt werden!"); + return true; + } + + if (args.length == 0) { + sendUsage(sender); + return true; + } + + String subCommand = args[0].toLowerCase(); + + switch (subCommand) { + case "shop" -> handleShopSpawn(player); + case "atm" -> handleAtmSpawn(player); + case "help", "?" -> sendUsage(sender); + default -> sendUsage(sender); + } + + return true; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + return handleSpawnCommand(sender, args); + } + @Override public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { if (!sender.hasPermission("simpleeco.spawn")) { @@ -188,15 +333,15 @@ public List onTabComplete(CommandSender sender, Command command, String if (args.length == 1) { // Erste Ebene: Subcommands - List subCommands = Arrays.asList("villager", "atm", "help"); + List subCommands = Arrays.asList("shop", "atm", "help"); String input = args[0].toLowerCase(); completions = subCommands.stream() .filter(sub -> sub.startsWith(input)) .filter(sub -> { // Permissions für spezifische Subcommands prüfen - if (sub.equals("villager")) { - return sender.hasPermission("simpleeco.spawn.villager"); + if (sub.equals("shop")) { + return sender.hasPermission("simpleeco.spawn.shop"); } else if (sub.equals("atm")) { return sender.hasPermission("simpleeco.spawn.atm"); } @@ -207,4 +352,8 @@ public List onTabComplete(CommandSender sender, Command command, String return completions; } + + public List getSpawnTabComplete(CommandSender sender, String[] args) { + return onTabComplete(sender, null, "spawn", args); + } } \ No newline at end of file diff --git a/src/main/java/de/simpleeco/config/ConfigManager.java b/src/main/java/de/simpleeco/config/ConfigManager.java index 00bc8ca..659cede 100644 --- a/src/main/java/de/simpleeco/config/ConfigManager.java +++ b/src/main/java/de/simpleeco/config/ConfigManager.java @@ -217,6 +217,13 @@ public String getInfoButtonName() { return config.getString("trading.infoButtonName", "§e§lInformation"); } + /** + * Lädt die Konfiguration neu + */ + public void reload() { + loadConfig(); + } + /** * Datenklasse für Item-Preiskonfiguration */ diff --git a/src/main/java/de/simpleeco/listeners/PlayerDeathListener.java b/src/main/java/de/simpleeco/listeners/PlayerDeathListener.java new file mode 100644 index 0000000..817a28e --- /dev/null +++ b/src/main/java/de/simpleeco/listeners/PlayerDeathListener.java @@ -0,0 +1,121 @@ +package de.simpleeco.listeners; + +import de.simpleeco.SimpleEcoPlugin; +import de.simpleeco.config.ConfigManager; +import de.simpleeco.bank.BankManager; +import de.simpleeco.scoreboard.ScoreboardManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; + +/** + * Listener für Spieler-Tod Events + * + * Behandelt den konfigurierbaren Bargeldverlust beim Tod. + * Nur Bargeld geht verloren, Bank-Guthaben bleibt sicher. + */ +public class PlayerDeathListener implements Listener { + + private final SimpleEcoPlugin plugin; + private final ConfigManager configManager; + private final BankManager bankManager; + private final ScoreboardManager scoreboardManager; + + public PlayerDeathListener(SimpleEcoPlugin plugin, ConfigManager configManager, + BankManager bankManager, ScoreboardManager scoreboardManager) { + this.plugin = plugin; + this.configManager = configManager; + this.bankManager = bankManager; + this.scoreboardManager = scoreboardManager; + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + + // Prüfen ob Todesstrafe aktiviert ist + if (!configManager.getConfig().getBoolean("deathPenalty.enabled", true)) { + return; + } + + // Prüfen ob Spieler Berechtigung zum Umgehen hat + String exemptPermission = configManager.getConfig().getString("deathPenalty.exemptPermission", "simpleeco.death.exempt"); + if (player.hasPermission(exemptPermission)) { + // Optional: Nachricht senden dass Spieler geschützt ist + String exemptMessage = configManager.getMessage("deathPenaltyExempt"); + if (exemptMessage != null && !exemptMessage.isEmpty()) { + player.sendMessage(configManager.getMessage("prefix") + exemptMessage); + } + return; + } + + // Prüfen ob nur PvP-Tode berücksichtigt werden sollen + boolean onlyPvPDeath = configManager.getConfig().getBoolean("deathPenalty.onlyPvPDeath", false); + if (onlyPvPDeath && player.getKiller() == null) { + return; // Kein PvP-Tod, keine Strafe + } + + // Bargeld-Verlust berechnen und anwenden + applyCashLossPenalty(player); + } + + /** + * Wendet die Bargeld-Verlust-Strafe auf einen Spieler an + * + * @param player Der Spieler der die Strafe erhält + */ + private void applyCashLossPenalty(Player player) { + bankManager.getCashBalance(player).thenAccept(currentCash -> { + if (currentCash <= 0) { + return; // Kein Bargeld vorhanden + } + + // Verlust-Prozentsatz aus Konfiguration + double lossPercentage = configManager.getConfig().getDouble("deathPenalty.cashLossPercentage", 0.25); + double minLoss = configManager.getConfig().getDouble("deathPenalty.minLossAmount", 1.0); + double maxLoss = configManager.getConfig().getDouble("deathPenalty.maxLossAmount", 10000.0); + + // Verlust berechnen + double calculatedLoss = currentCash * lossPercentage; + + // Min/Max-Grenzen anwenden + calculatedLoss = Math.max(minLoss, Math.min(maxLoss, calculatedLoss)); + + // Nicht mehr verlieren als vorhanden ist + final double lossAmount = Math.min(calculatedLoss, currentCash); + + // Verlust anwenden + if (lossAmount > 0) { + bankManager.removeCashBalance(player, lossAmount).thenAccept(newBalance -> { + // Nachricht an Spieler senden + String currencySymbol = configManager.getConfig().getString("currency.symbol", "G"); + String message = configManager.getMessage("deathPenaltyCash", + "amount", String.format("%.2f", lossAmount), + "currency", currencySymbol); + + // Nachricht mit Verzögerung senden (nach Respawn) + Bukkit.getScheduler().runTaskLater(plugin, () -> { + player.sendMessage(configManager.getMessage("prefix") + message); + + // Scoreboard aktualisieren + if (scoreboardManager != null) { + scoreboardManager.updatePlayerScoreboard(player); + } + }, 20L); // 1 Sekunde Verzögerung + }).exceptionally(throwable -> { + plugin.getLogger().severe("Fehler beim Anwenden der Strafe für " + + player.getName() + ": " + throwable.getMessage()); + return null; + }); + } + + }).exceptionally(throwable -> { + plugin.getLogger().severe("Fehler beim Laden des Bargeldes für Strafe von " + + player.getName() + ": " + throwable.getMessage()); + return null; + }); + } +} \ No newline at end of file diff --git a/src/main/java/de/simpleeco/listeners/VillagerInteractListener.java b/src/main/java/de/simpleeco/listeners/VillagerInteractListener.java index e048eb4..e5f99e3 100644 --- a/src/main/java/de/simpleeco/listeners/VillagerInteractListener.java +++ b/src/main/java/de/simpleeco/listeners/VillagerInteractListener.java @@ -173,12 +173,24 @@ public void onInventoryClose(InventoryCloseEvent event) { return; } - // Prüfen ob es sich um ein ATM-Menü handelt - if (inventoryTitle.contains("Bank-Automat") || inventoryTitle.contains("Geld einzahlen") || inventoryTitle.contains("Geld abheben")) { - // ATM-Session schließen - atmTrader.removeSession(player); - return; - } + // Prüfen ob es sich um ein ATM-Menü handelt + if (inventoryTitle.contains("Bank-Automat") || inventoryTitle.contains("Geld einzahlen") || inventoryTitle.contains("Geld abheben")) { + // Verzögerte Session-Entfernung - nur wenn kein neues ATM-Menü innerhalb von 1 Tick geöffnet wird + org.bukkit.Bukkit.getScheduler().runTaskLater(scoreboardManager.getPlugin(), () -> { + // Prüfen ob der Spieler noch ein ATM-Menü offen hat + String currentTitle = player.getOpenInventory().getTitle(); + if (!currentTitle.contains("Bank-Automat") && + !currentTitle.contains("Geld einzahlen") && + !currentTitle.contains("Geld abheben")) { + // Spieler hat kein ATM-Menü mehr offen - Session entfernen + atmTrader.removeSession(player); + scoreboardManager.getPlugin().getLogger().info("ATM-Session für " + player.getName() + " entfernt (Menü geschlossen)"); + } else { + scoreboardManager.getPlugin().getLogger().info("ATM-Session für " + player.getName() + " beibehalten (Menü-Wechsel)"); + } + }, 1L); + return; + } } /** diff --git a/src/main/java/de/simpleeco/scoreboard/ScoreboardManager.java b/src/main/java/de/simpleeco/scoreboard/ScoreboardManager.java index 93e7a53..3fa7cff 100644 --- a/src/main/java/de/simpleeco/scoreboard/ScoreboardManager.java +++ b/src/main/java/de/simpleeco/scoreboard/ScoreboardManager.java @@ -3,6 +3,7 @@ import de.simpleeco.SimpleEcoPlugin; import de.simpleeco.config.ConfigManager; import de.simpleeco.currency.BasicCurrency; +import de.simpleeco.bank.BankManager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -10,35 +11,45 @@ import org.bukkit.scoreboard.*; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; /** * Manager für Scoreboards zur Anzeige der Spieler-Balance * * Verwaltet individuelle Scoreboards für jeden Spieler und aktualisiert - * die Balance-Anzeige in regelmäßigen Intervallen. + * die Balance-Anzeige (Bargeld und Bank-Guthaben) in regelmäßigen Intervallen. */ public class ScoreboardManager { private final SimpleEcoPlugin plugin; private final ConfigManager configManager; private final BasicCurrency currency; + private final BankManager bankManager; - // Map zum Tracking der Player-Scoreboards - private final Map playerScoreboards; - private final Map playerObjectives; + // Map zum Tracking der Player-Scoreboards (Thread-safe) + private final ConcurrentHashMap playerScoreboards; + private final ConcurrentHashMap playerObjectives; + + // Rate-Limiting für Scoreboard Updates (Thread-safe) + private final ConcurrentHashMap lastScoreboardUpdate; // Update-Task private BukkitTask updateTask; - public ScoreboardManager(SimpleEcoPlugin plugin, ConfigManager configManager, BasicCurrency currency) { + public ScoreboardManager(SimpleEcoPlugin plugin, ConfigManager configManager, BasicCurrency currency, BankManager bankManager) { this.plugin = plugin; this.configManager = configManager; this.currency = currency; - this.playerScoreboards = new HashMap<>(); - this.playerObjectives = new HashMap<>(); + this.bankManager = bankManager; + this.playerScoreboards = new ConcurrentHashMap<>(); + this.playerObjectives = new ConcurrentHashMap<>(); + this.lastScoreboardUpdate = new ConcurrentHashMap<>(); // Update-Task starten wenn Scoreboard aktiviert ist if (isScoreboardEnabled()) { @@ -119,6 +130,9 @@ public void removeScoreboard(Player player) { // Scoreboard entfernen playerScoreboards.remove(playerUUID); + // Update-Tracking entfernen + lastScoreboardUpdate.remove(playerUUID); + // Standard-Scoreboard zuweisen try { org.bukkit.scoreboard.ScoreboardManager bukkitScoreboardManager = Bukkit.getScoreboardManager(); @@ -136,6 +150,16 @@ public void removeScoreboard(Player player) { * @param player Der Spieler */ public void updatePlayerScoreboard(Player player) { + updatePlayerScoreboard(player, false); + } + + /** + * Aktualisiert das Scoreboard eines einzelnen Spielers + * + * @param player Der Spieler + * @param forceUpdate Wenn true, wird das Update erzwungen auch bei Rate-Limiting + */ + public void updatePlayerScoreboard(Player player, boolean forceUpdate) { if (!isScoreboardEnabled()) { return; } @@ -149,45 +173,217 @@ public void updatePlayerScoreboard(Player player) { return; } - try { - // Alle bestehenden Scores löschen - for (String entry : objective.getScoreboard().getEntries()) { - objective.getScoreboard().resetScores(entry); + // Rate-Limiting um zu häufige Updates zu vermeiden (außer bei forceUpdate) + if (!forceUpdate) { + long currentTime = System.currentTimeMillis(); + Long lastUpdate = lastScoreboardUpdate.get(playerUUID); + if (lastUpdate != null && (currentTime - lastUpdate) < 500) { // Max alle 0.5 Sekunden + return; } + lastScoreboardUpdate.put(playerUUID, currentTime); + } + + try { + // Beide Balances asynchron laden + CompletableFuture cashFuture = bankManager.getCashBalance(player); + CompletableFuture bankFuture = bankManager.getBankBalance(player); - // Balance asynchron laden und Scoreboard aktualisieren - currency.getBalance(player).thenAccept(balance -> { + CompletableFuture.allOf(cashFuture, bankFuture).thenAccept(ignored -> { // Sicherstellen dass der Spieler noch online ist if (!player.isOnline()) { return; } - // Scoreboard-Zeilen aus Config laden - List lines = configManager.getConfig().getStringList("scoreboard.lines"); - - // Zeilen durchgehen und anzeigen (von unten nach oben) - int score = lines.size(); - for (String line : lines) { - // Platzhalter ersetzen - String processedLine = line - .replace("{balance}", currency.formatAmount(balance)) - .replace("{currency}", configManager.getCurrencyName()) - .replace("{player}", player.getName()); + try { + double cashBalance = cashFuture.get(); + double bankBalance = bankFuture.get(); + double totalBalance = cashBalance + bankBalance; - // Score setzen - Score scoreEntry = objective.getScore(processedLine); - scoreEntry.setScore(score--); + // Hauptthread für Scoreboard-Updates verwenden + Bukkit.getScheduler().runTask(plugin, () -> { + try { + // Prüfen ob Objective noch gültig ist (Player könnte disconnect gewesen sein) + Objective currentObjective = playerObjectives.get(playerUUID); + if (currentObjective != null && currentObjective.getScoreboard() != null) { + updateScoreboardDisplay(player, currentObjective, cashBalance, bankBalance, totalBalance); + } + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Aktualisieren der Scoreboard-Anzeige für " + player.getName() + ": " + e.getMessage()); + } + }); + + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Abrufen der Kontostände für " + player.getName() + ": " + e.getMessage()); } }).exceptionally(throwable -> { - plugin.getLogger().severe("Fehler beim Laden der Balance für Scoreboard: " + throwable.getMessage()); + plugin.getLogger().warning("Fehler beim Laden der Balances für Scoreboard von " + player.getName() + ": " + throwable.getMessage()); return null; }); } catch (Exception e) { - plugin.getLogger().severe("Fehler beim Aktualisieren des Scoreboards für " + player.getName() + ": " + e.getMessage()); + plugin.getLogger().warning("Fehler beim Aktualisieren des Scoreboards für " + player.getName() + ": " + e.getMessage()); + } + } + + /** + * Aktualisiert die Scoreboard-Anzeige mit den Kontodaten + * + * @param player Der Spieler + * @param objective Das Scoreboard-Objective + * @param cashBalance Bargeld-Betrag + * @param bankBalance Bank-Guthaben + * @param totalBalance Gesamt-Guthaben + */ + private void updateScoreboardDisplay(Player player, Objective objective, double cashBalance, double bankBalance, double totalBalance) { + try { + // Alle bestehenden Scores löschen (sichere Methode) + Set entries = new HashSet<>(objective.getScoreboard().getEntries()); + for (String entry : entries) { + try { + objective.getScoreboard().resetScores(entry); + } catch (Exception e) { + // Ignoriere Fehler beim Entfernen einzelner Einträge + } + } + + // Scoreboard-Zeilen aus Config laden oder Standard verwenden + List lines = configManager.getConfig().getStringList("scoreboard.lines"); + + // Falls keine Zeilen konfiguriert sind, Standard-Design verwenden + if (lines.isEmpty()) { + lines = getDefaultScoreboardLines(); + } + + // Zeilen durchgehen und anzeigen (von unten nach oben) + int score = lines.size(); + int emptyLineCounter = 0; // Zähler für leere Zeilen um Duplikate zu vermeiden + + for (String line : lines) { + // Platzhalter ersetzen + String processedLine = line + .replace("{cash}", formatAmountForScoreboard(cashBalance)) + .replace("{bank}", formatAmountForScoreboard(bankBalance)) + .replace("{total}", formatAmountForScoreboard(totalBalance)) + .replace("{balance}", formatAmountForScoreboard(cashBalance)) // Für Rückwärtskompatibilität + .replace("{currency}", configManager.getCurrencyName()) + .replace("{symbol}", configManager.getCurrencySymbol()) + .replace("{player}", player.getName()); + + // Leere Zeilen behandeln (für bessere Formatierung) + if (processedLine.trim().isEmpty()) { + // Jede leere Zeile muss einzigartig sein, sonst wird sie nicht angezeigt + processedLine = " ".repeat(++emptyLineCounter); + } + + // Zeile kürzen falls zu lang (Scoreboard max 40 Zeichen) + processedLine = truncateScoreboardLine(processedLine); + + // Sicherstellen dass die Zeile einzigartig ist (Minecraft Scoreboard Requirement) + processedLine = ensureUniqueScoreboardEntry(processedLine, objective, score); + + // Score setzen + Score scoreEntry = objective.getScore(processedLine); + scoreEntry.setScore(score--); + } + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Anzeigen des Scoreboards für " + player.getName() + ": " + e.getMessage()); } } + /** + * Formatiert einen Betrag speziell für Scoreboard-Anzeige (kürzere Darstellung) + * + * @param amount Der Betrag + * @return Formatierter String + */ + private String formatAmountForScoreboard(double amount) { + if (amount >= 1000000) { + return String.format("%.1fM", amount / 1000000); + } else if (amount >= 1000) { + return String.format("%.1fK", amount / 1000); + } else { + return String.format("%.0f", amount); + } + } + + /** + * Kürzt eine Scoreboard-Zeile auf die maximale Länge + * + * @param line Die ursprüngliche Zeile + * @return Gekürzte Zeile + */ + private String truncateScoreboardLine(String line) { + if (line.length() <= 40) { + return line; + } + + // Intelligentes Kürzen: Versuche an Leerzeichen zu trennen + if (line.length() > 37) { + String truncated = line.substring(0, 37); + int lastSpace = truncated.lastIndexOf(' '); + if (lastSpace > 20) { // Nur wenn genug Text übrig bleibt + return truncated.substring(0, lastSpace) + "..."; + } else { + return truncated + "..."; + } + } + + return line.substring(0, 40); + } + + /** + * Stellt sicher, dass ein Scoreboard-Eintrag einzigartig ist + * (Minecraft zeigt doppelte Einträge nicht an) + * + * @param line Die ursprüngliche Zeile + * @param objective Das Scoreboard-Objective + * @param score Der Score-Wert + * @return Einzigartige Zeile + */ + private String ensureUniqueScoreboardEntry(String line, Objective objective, int score) { + String originalLine = line; + int attempts = 0; + + // Prüfen ob die Zeile bereits existiert + while (objective.getScoreboard().getEntries().contains(line) && attempts < 10) { + attempts++; + // Füge unsichtbare Zeichen hinzu um Einzigartigkeit zu gewährleisten + if (line.trim().isEmpty()) { + // Für leere Zeilen: zusätzliche Leerzeichen + line = " ".repeat(attempts + line.length()); + } else { + // Für normale Zeilen: unsichtbare Farbcodes am Ende + String[] colors = {"§0", "§1", "§2", "§3", "§4", "§5", "§6", "§7", "§8", "§9"}; + line = originalLine + colors[attempts % colors.length] + "§r"; + } + } + + return line; + } + + /** + * Gibt die Standard-Scoreboard-Zeilen zurück + * + * @return Liste der Standard-Zeilen + */ + private List getDefaultScoreboardLines() { + return List.of( + "§7§m────────────────────", + "§e§l💰 Finanzen", + "", + "§a💵 Bargeld:", + "§f {cash}", + "", + "§6🏦 Bank:", + "§f {bank}", + "", + "§e📊 Gesamt:", + "§f {total}", + "", + "§7§m────────────────────" + ); + } + /** * Aktualisiert alle Scoreboards */ @@ -242,6 +438,7 @@ public void shutdown() { // Maps leeren playerScoreboards.clear(); playerObjectives.clear(); + lastScoreboardUpdate.clear(); plugin.getLogger().info("ScoreboardManager heruntergefahren"); } @@ -258,14 +455,16 @@ public void reload() { // Update-Task stoppen stopUpdateTask(); - // Neu starten wenn aktiviert + // Neu starten wenn aktiviert (mit kleiner Verzögerung) if (isScoreboardEnabled()) { - startUpdateTask(); - - // Scoreboards für alle Online-Spieler erstellen - for (Player player : Bukkit.getOnlinePlayers()) { - createScoreboard(player); - } + Bukkit.getScheduler().runTaskLater(plugin, () -> { + startUpdateTask(); + + // Scoreboards für alle Online-Spieler erstellen + for (Player player : Bukkit.getOnlinePlayers()) { + createScoreboard(player); + } + }, 5L); // 0.25 Sekunden Verzögerung } plugin.getLogger().info("ScoreboardManager neu geladen"); @@ -280,6 +479,42 @@ public int getActiveScoreboardCount() { return playerScoreboards.size(); } + /** + * Wird aufgerufen wenn sich die Balance eines Spielers ändert + * Aktualisiert das Scoreboard sofort + * + * @param player Der Spieler dessen Balance sich geändert hat + */ + public void onBalanceChanged(Player player) { + if (player != null && player.isOnline()) { + updatePlayerScoreboard(player, true); // Force Update + } + } + + /** + * Wird aufgerufen wenn sich die Bank-Balance eines Spielers ändert + * Aktualisiert das Scoreboard sofort + * + * @param player Der Spieler dessen Bank-Balance sich geändert hat + */ + public void onBankBalanceChanged(Player player) { + if (player != null && player.isOnline()) { + updatePlayerScoreboard(player, true); // Force Update + } + } + + /** + * Aktualisiert das Scoreboard für einen Spieler basierend auf seiner UUID + * + * @param playerUuid Die UUID des Spielers + */ + public void onBalanceChanged(UUID playerUuid) { + Player player = Bukkit.getPlayer(playerUuid); + if (player != null && player.isOnline()) { + updatePlayerScoreboard(player, true); // Force Update + } + } + /** * Gibt die Plugin-Instanz zurück * diff --git a/src/main/java/de/simpleeco/tasks/VillagerLookTask.java b/src/main/java/de/simpleeco/tasks/VillagerLookTask.java new file mode 100644 index 0000000..4291af4 --- /dev/null +++ b/src/main/java/de/simpleeco/tasks/VillagerLookTask.java @@ -0,0 +1,172 @@ +package de.simpleeco.tasks; + +import de.simpleeco.SimpleEcoPlugin; +import de.simpleeco.villager.ShopVillagerManager; +import de.simpleeco.bank.AtmVillagerManager; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Villager; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; + +/** + * Task für Villager-Blickrichtung + * + * Diese Task sorgt dafür, dass Shop- und ATM-Villager zum nächsten Spieler + * in der Nähe schauen und sich entsprechend drehen. + */ +public class VillagerLookTask extends BukkitRunnable { + + private final SimpleEcoPlugin plugin; + private final ShopVillagerManager shopVillagerManager; + private final AtmVillagerManager atmVillagerManager; + private final double maxLookDistance; + + // Cache für Original-Positionen der Villager + private final ConcurrentHashMap villagerPositions = new ConcurrentHashMap<>(); + + public VillagerLookTask(SimpleEcoPlugin plugin, ShopVillagerManager shopVillagerManager, + AtmVillagerManager atmVillagerManager, double maxLookDistance) { + this.plugin = plugin; + this.shopVillagerManager = shopVillagerManager; + this.atmVillagerManager = atmVillagerManager; + this.maxLookDistance = maxLookDistance; + } + + @Override + public void run() { + // Alle geladenen Welten durchgehen + plugin.getServer().getWorlds().forEach(world -> { + // Alle Villager in der Welt finden + List entities = world.getEntities(); + + for (Entity entity : entities) { + if (!(entity instanceof Villager villager)) { + continue; + } + + // Prüfen ob es sich um einen speziellen Villager handelt + if (!shopVillagerManager.isShopVillager(villager) && + !atmVillagerManager.isAtmVillager(villager)) { + continue; + } + + // Original-Position des Villagers speichern/überprüfen + UUID villagerId = villager.getUniqueId(); + Location currentLocation = villager.getLocation(); + Location originalPosition = villagerPositions.get(villagerId); + + if (originalPosition == null) { + // Erste Begegnung mit diesem Villager - Position speichern + villagerPositions.put(villagerId, currentLocation.clone()); + originalPosition = currentLocation; + } else { + // Prüfen ob Villager sich bewegt hat (mehr als 0.5 Blöcke) + double distance = originalPosition.distance(currentLocation); + if (distance > 0.5) { + // Villager zurück zur Original-Position teleportieren + Location resetLoc = originalPosition.clone(); + resetLoc.setYaw(currentLocation.getYaw()); // Blickrichtung beibehalten + resetLoc.setPitch(currentLocation.getPitch()); + villager.teleport(resetLoc); + currentLocation = resetLoc; + } + } + + // Nächsten Spieler in der Nähe finden + Player nearestPlayer = findNearestPlayer(villager); + + if (nearestPlayer != null) { + // Villager zum Spieler drehen lassen (ohne Position zu ändern) + lookAtPlayer(villager, nearestPlayer, originalPosition); + } + } + }); + } + + /** + * Findet den nächsten Spieler in der Nähe des Villagers + * + * @param villager Der Villager + * @return Der nächste Spieler oder null wenn keiner in der Nähe ist + */ + private Player findNearestPlayer(Villager villager) { + Location villagerLocation = villager.getLocation(); + Player nearestPlayer = null; + double nearestDistance = maxLookDistance; + + // Alle Spieler in der Welt durchgehen + for (Player player : villager.getWorld().getPlayers()) { + // Entfernung berechnen + double distance = player.getLocation().distance(villagerLocation); + + // Prüfen ob Spieler näher ist als der bisherige nächste + if (distance < nearestDistance) { + nearestPlayer = player; + nearestDistance = distance; + } + } + + return nearestPlayer; + } + + /** + * Lässt den Villager zum Spieler schauen + * + * @param villager Der Villager + * @param player Der Spieler + * @param fixedPosition Die feste Position des Villagers (darf nicht verändert werden) + */ + private void lookAtPlayer(Villager villager, Player player, Location fixedPosition) { + Location playerLoc = player.getLocation(); + + // Vektor vom Villager zum Spieler berechnen + Vector direction = playerLoc.toVector().subtract(fixedPosition.toVector()); + + // Y-Komponente ignorieren (nur horizontale Drehung) + direction.setY(0); + direction.normalize(); + + // Yaw berechnen (horizontale Drehung) + double yaw = Math.toDegrees(Math.atan2(-direction.getX(), direction.getZ())); + + // Pitch berechnen (vertikale Drehung) - leicht nach unten schauen da Spieler meist höher sind + double heightDiff = playerLoc.getY() - fixedPosition.getY(); + double horizontalDistance = Math.sqrt(direction.getX() * direction.getX() + direction.getZ() * direction.getZ()); + double pitch = Math.toDegrees(Math.atan2(-heightDiff, horizontalDistance)); + + // Pitch begrenzen für realistisches Aussehen + pitch = Math.max(-30, Math.min(30, pitch)); + + // Neue Location mit angepasster Blickrichtung erstellen (Position bleibt gleich) + Location newLoc = fixedPosition.clone(); + newLoc.setYaw((float) yaw); + newLoc.setPitch((float) pitch); + + // Villager teleportieren (mit neuer Blickrichtung, aber fester Position) + villager.teleport(newLoc); + } + + /** + * Startet die VillagerLookTask + * + * @param plugin Das Plugin + * @param shopVillagerManager Der Shop-Villager-Manager + * @param atmVillagerManager Der ATM-Villager-Manager + * @param maxLookDistance Maximale Entfernung zum Spieler schauen + * @param updateInterval Update-Intervall in Ticks + * @return Die gestartete Task + */ + public static VillagerLookTask start(SimpleEcoPlugin plugin, ShopVillagerManager shopVillagerManager, + AtmVillagerManager atmVillagerManager, double maxLookDistance, + long updateInterval) { + VillagerLookTask task = new VillagerLookTask(plugin, shopVillagerManager, atmVillagerManager, maxLookDistance); + task.runTaskTimer(plugin, 20L, updateInterval); // Start nach 1 Sekunde, dann alle updateInterval Ticks + return task; + } +} \ No newline at end of file diff --git a/src/main/java/de/simpleeco/trading/CustomVillagerTrader.java b/src/main/java/de/simpleeco/trading/CustomVillagerTrader.java index abfb6c9..46ac9c6 100644 --- a/src/main/java/de/simpleeco/trading/CustomVillagerTrader.java +++ b/src/main/java/de/simpleeco/trading/CustomVillagerTrader.java @@ -73,20 +73,35 @@ public void openTradingMenu(Player player) { private CompletableFuture populateMenu(TradingSession session) { Inventory inventory = session.getInventory(); + // Inventar leeren + inventory.clear(); + // Liste aller handelbaren Items (nur Items die kaufbar oder verkaufbar sind) - List tradeableItems = configManager.getItemPrices().entrySet() + List allTradeableItems = configManager.getItemPrices().entrySet() .stream() .filter(entry -> entry.getValue().isBuyable() || entry.getValue().isSellable()) .map(Map.Entry::getKey) .sorted((a, b) -> a.name().compareTo(b.name())) .toList(); + // Items in Session speichern und Seitenzahl berechnen + session.setAllTradeableItems(allTradeableItems); + + // Aktuelle Seite validieren + if (session.getCurrentPage() >= session.getTotalPages()) { + session.setCurrentPage(0); + } + + // Items für aktuelle Seite berechnen + int itemsPerPage = getItemsPerPage(); + int startIndex = session.getCurrentPage() * itemsPerPage; + int endIndex = Math.min(startIndex + itemsPerPage, allTradeableItems.size()); + + List pageItems = allTradeableItems.subList(startIndex, endIndex); List> itemFutures = new ArrayList<>(); int slot = 0; - for (Material material : tradeableItems) { - if (slot >= 45) break; // Platz für Navigationselemente lassen - + for (Material material : pageItems) { final int itemSlot = slot; CompletableFuture itemFuture = createTradeItem(material) .thenAccept(itemStack -> { @@ -100,7 +115,7 @@ private CompletableFuture populateMenu(TradingSession session) { } // Navigation und Info-Items hinzufügen - addNavigationItems(inventory); + addNavigationItems(inventory, session); return CompletableFuture.allOf(itemFutures.toArray(new CompletableFuture[0])); } @@ -184,8 +199,51 @@ private CompletableFuture createTradeItem(Material material) { * Fügt Navigations- und Info-Items zum Inventar hinzu * * @param inventory Das Inventar + * @param session Die Trading-Session */ - private void addNavigationItems(Inventory inventory) { + private void addNavigationItems(Inventory inventory, TradingSession session) { + // Vorherige Seite Button (nur anzeigen wenn verfügbar) + if (session.hasPreviousPage()) { + ItemStack prevItem = new ItemStack(Material.ARROW); + ItemMeta prevMeta = prevItem.getItemMeta(); + if (prevMeta != null) { + prevMeta.setDisplayName("§a§l← Vorherige Seite"); + List prevLore = new ArrayList<>(); + prevLore.add("§7Gehe zur Seite " + session.getCurrentPage()); + prevMeta.setLore(prevLore); + prevItem.setItemMeta(prevMeta); + } + inventory.setItem(45, prevItem); + } + + // Nächste Seite Button (nur anzeigen wenn verfügbar) + if (session.hasNextPage()) { + ItemStack nextItem = new ItemStack(Material.ARROW); + ItemMeta nextMeta = nextItem.getItemMeta(); + if (nextMeta != null) { + nextMeta.setDisplayName("§a§lNächste Seite →"); + List nextLore = new ArrayList<>(); + nextLore.add("§7Gehe zur Seite " + (session.getCurrentPage() + 2)); + nextMeta.setLore(nextLore); + nextItem.setItemMeta(nextMeta); + } + inventory.setItem(53, nextItem); + } + + // Seiten-Info in der Mitte + ItemStack pageInfo = new ItemStack(Material.PAPER); + ItemMeta pageInfoMeta = pageInfo.getItemMeta(); + if (pageInfoMeta != null) { + pageInfoMeta.setDisplayName("§6§lSeite " + (session.getCurrentPage() + 1) + " von " + session.getTotalPages()); + List pageInfoLore = new ArrayList<>(); + pageInfoLore.add("§7"); + pageInfoLore.add("§7Zeigt " + session.getAllTradeableItems().size() + " handelbare Items"); + pageInfoLore.add("§7auf " + session.getTotalPages() + " Seiten"); + pageInfoMeta.setLore(pageInfoLore); + pageInfo.setItemMeta(pageInfoMeta); + } + inventory.setItem(49, pageInfo); + // Info-Item ItemStack infoItem = new ItemStack(Material.BOOK); ItemMeta infoMeta = infoItem.getItemMeta(); @@ -205,16 +263,27 @@ private void addNavigationItems(Inventory inventory) { infoMeta.setLore(infoLore); infoItem.setItemMeta(infoMeta); } - inventory.setItem(49, infoItem); + inventory.setItem(47, infoItem); - // Schließen-Button - ItemStack closeItem = new ItemStack(Material.BARRIER); - ItemMeta closeMeta = closeItem.getItemMeta(); - if (closeMeta != null) { - closeMeta.setDisplayName("§c§lMenü schließen"); - closeItem.setItemMeta(closeMeta); + // Schließen-Button (nur wenn keine nächste Seite verfügbar ist, sonst wird der Slot verwendet) + if (!session.hasNextPage()) { + ItemStack closeItem = new ItemStack(Material.BARRIER); + ItemMeta closeMeta = closeItem.getItemMeta(); + if (closeMeta != null) { + closeMeta.setDisplayName("§c§lMenü schließen"); + closeItem.setItemMeta(closeMeta); + } + inventory.setItem(53, closeItem); + } else { + // Schließen-Button auf anderen Slot verschieben + ItemStack closeItem = new ItemStack(Material.BARRIER); + ItemMeta closeMeta = closeItem.getItemMeta(); + if (closeMeta != null) { + closeMeta.setDisplayName("§c§lMenü schließen"); + closeItem.setItemMeta(closeMeta); + } + inventory.setItem(51, closeItem); } - inventory.setItem(53, closeItem); } /** @@ -231,13 +300,35 @@ public void handleMenuClick(Player player, ItemStack clickedItem, ClickType clic return; } + // Pagination Navigation behandeln + if (slot == 45 && session.hasPreviousPage()) { // Vorherige Seite + session.setCurrentPage(session.getCurrentPage() - 1); + populateMenu(session); + return; + } + + if (slot == 53 && session.hasNextPage()) { // Nächste Seite + session.setCurrentPage(session.getCurrentPage() + 1); + populateMenu(session); + return; + } + // Spezielle Slots behandeln - if (slot == 53) { // Schließen-Button + if (slot == 53 && !session.hasNextPage()) { // Schließen-Button (wenn keine nächste Seite) player.closeInventory(); return; } - if (slot == 49) { // Info-Button + if (slot == 51) { // Schließen-Button (alternativer Slot) + player.closeInventory(); + return; + } + + if (slot == 49) { // Seiten-Info + return; // Nur anzeigen, keine Aktion + } + + if (slot == 47) { // Info-Button return; // Nur anzeigen, keine Aktion } @@ -253,6 +344,11 @@ public void handleMenuClick(Player player, ItemStack clickedItem, ClickType clic return; } + // Prüfen ob der Slot tatsächlich ein handelbares Item enthält (0-44 sind handelbare Items) + if (slot >= 45) { + return; // Navigation-Bereich, kein handelbares Item + } + // Handelsaktion bestimmen boolean isBuying = clickType == ClickType.LEFT || clickType == ClickType.SHIFT_LEFT; boolean isSelling = clickType == ClickType.RIGHT || clickType == ClickType.SHIFT_RIGHT; @@ -398,6 +494,39 @@ private void refreshMenu(Player player) { } } + /** + * Springt zu einer bestimmten Seite im Shop-Menü + * + * @param player Der Spieler + * @param page Die Seitennummer (0-basiert) + */ + public void goToPage(Player player, int page) { + TradingSession session = activeSessions.get(player); + if (session != null) { + session.setCurrentPage(page); + populateMenu(session); + } + } + + /** + * Berechnet die maximale Anzahl von Items pro Seite + * + * @return Anzahl Items pro Seite + */ + private int getItemsPerPage() { + return 45; // 9 * 5 Zeilen für Items, letzte Zeile für Navigation + } + + /** + * Berechnet die Gesamtzahl der Seiten basierend auf der Anzahl der Items + * + * @param totalItems Gesamtanzahl der Items + * @return Anzahl der Seiten + */ + private int calculateTotalPages(int totalItems) { + return Math.max(1, (int) Math.ceil((double) totalItems / getItemsPerPage())); + } + /** * Prüft ob ein Spieler genügend Inventar-Platz hat * @@ -498,8 +627,9 @@ public void closeSession(Player player) { * @return Der deutsche Name */ private String getGermanItemName(Material material) { - // Einfache deutsche Übersetzungen für die wichtigsten Items + // Deutsche Übersetzungen für alle handelbaren Items return switch (material) { + // Traditionelle Lebensmittel case WHEAT -> "Weizen"; case CARROT -> "Karotte"; case POTATO -> "Kartoffel"; @@ -509,11 +639,225 @@ private String getGermanItemName(Material material) { case COOKED_BEEF -> "Gebratenes Rindfleisch"; case COOKED_PORKCHOP -> "Gebratenes Schweinefleisch"; case COOKED_CHICKEN -> "Gebratenes Hühnchen"; - case DIAMOND -> "Diamant"; + + // Holz und Holzprodukte + case OAK_LOG -> "Eichenstamm"; + case OAK_LEAVES -> "Eichenlaub"; + case OAK_SAPLING -> "Eichensetzling"; + case SPRUCE_LOG -> "Fichtenstamm"; + case SPRUCE_LEAVES -> "Fichtenlaub"; + case SPRUCE_SAPLING -> "Fichtensetzling"; + case BIRCH_LOG -> "Birkenstamm"; + case BIRCH_LEAVES -> "Birkenlaub"; + case BIRCH_SAPLING -> "Birkensetzling"; + case JUNGLE_LOG -> "Tropenstamm"; + case JUNGLE_LEAVES -> "Tropenlaub"; + case JUNGLE_SAPLING -> "Tropensetzling"; + case ACACIA_LOG -> "Akazienstamm"; + case ACACIA_LEAVES -> "Akazienlaub"; + case ACACIA_SAPLING -> "Akaziensetzling"; + case DARK_OAK_LOG -> "Schwarzeichenstamm"; + case DARK_OAK_LEAVES -> "Schwarzeichenlaub"; + case DARK_OAK_SAPLING -> "Schwarzeichensetzling"; + case MANGROVE_LOG -> "Mangrovenstamm"; + case MANGROVE_LEAVES -> "Mangrovenlaub"; + case MANGROVE_PROPAGULE -> "Mangrovenkeim"; + case CHERRY_LOG -> "Kirschstamm"; + case CHERRY_LEAVES -> "Kirschlaub"; + case CHERRY_SAPLING -> "Kirschsetzling"; + case BAMBOO_BLOCK -> "Bambusblock"; + case BAMBOO -> "Bambus"; + case CRIMSON_STEM -> "Karmesinroter Stamm"; + case CRIMSON_FUNGUS -> "Karmesinroter Pilz"; + case CRIMSON_ROOTS -> "Karmesinrote Wurzeln"; + case WARPED_STEM -> "Wirriger Stamm"; + case WARPED_FUNGUS -> "Wirriger Pilz"; + case WARPED_ROOTS -> "Wirrige Wurzeln"; + + // Mob-Drops + case BEEHIVE -> "Bienenstock"; + case BEE_NEST -> "Bienennest"; + case HONEY_BLOCK -> "Honigblock"; + case HONEYCOMB_BLOCK -> "Wabenblock"; + case HONEY_BOTTLE -> "Honigflasche"; + case EGG -> "Ei"; + case FEATHER -> "Feder"; + case LEATHER -> "Leder"; + case RABBIT_HIDE -> "Kaninchenfell"; + case TURTLE_EGG -> "Schildkrötenei"; + case SCUTE -> "Schildkrötenpanzer"; + case PUFFERFISH -> "Kugelfisch"; + case INK_SAC -> "Tintenbeutel"; + case GLOW_INK_SAC -> "Leucht-Tintenbeutel"; + case BONE -> "Knochen"; + case ARROW -> "Pfeil"; + case BONE_MEAL -> "Knochenmehl"; + case BONE_BLOCK -> "Knochenblock"; + case STRING -> "Faden"; + case SPIDER_EYE -> "Spinnenauge"; + case SLIME_BALL -> "Schleimball"; + case SLIME_BLOCK -> "Schleimblock"; + case GUNPOWDER -> "Schwarzpulver"; + case PHANTOM_MEMBRANE -> "Phantomhaut"; + case ROTTEN_FLESH -> "Verrottetes Fleisch"; + case BLAZE_ROD -> "Lohenrute"; + case BLAZE_POWDER -> "Lohenpulver"; + case MAGMA_CREAM -> "Magmacreme"; + case GHAST_TEAR -> "Ghastträne"; + case ENDER_PEARL -> "Enderperle"; + case ENDER_EYE -> "Enderauge"; + case SHULKER_SHELL -> "Shulkerschale"; + case DRAGON_BREATH -> "Drachenatem"; + + // Blumen und Pflanzen + case ALLIUM -> "Zierlauch"; + case AZURE_BLUET -> "Porzellansternchen"; + case BLUE_ORCHID -> "Blaue Orchidee"; + case CORNFLOWER -> "Kornblume"; + case DANDELION -> "Löwenzahn"; + case LILAC -> "Flieder"; + case LILY_OF_THE_VALLEY -> "Maiglöckchen"; + case PEONY -> "Pfingstrose"; + case POPPY -> "Mohn"; + case ROSE_BUSH -> "Rosenstrauch"; + case SUNFLOWER -> "Sonnenblume"; + case RED_TULIP -> "Rote Tulpe"; + case ORANGE_TULIP -> "Orange Tulpe"; + case WHITE_TULIP -> "Weiße Tulpe"; + case PINK_TULIP -> "Rosa Tulpe"; + case OXEYE_DAISY -> "Margerite"; + case DEAD_BUSH -> "Toter Busch"; + case CACTUS -> "Kaktus"; + case FERN -> "Farn"; + case LARGE_FERN -> "Großer Farn"; + case SHORT_GRASS -> "Gras"; + case TALL_GRASS -> "Hohes Gras"; + case LILY_PAD -> "Seerosenblatt"; + + // Spezielle Pflanzen + case AZALEA -> "Azalee"; + case FLOWERING_AZALEA -> "Blühende Azalee"; + case HANGING_ROOTS -> "Hängewurzeln"; + case MOSS_BLOCK -> "Moosblock"; + case MOSS_CARPET -> "Moosteppich"; + case CHORUS_FLOWER -> "Chorusblüte"; + case CHORUS_PLANT -> "Choruspflanze"; + case BIG_DRIPLEAF -> "Großes Tropfblatt"; + case SMALL_DRIPLEAF -> "Kleines Tropfblatt"; + case BROWN_MUSHROOM -> "Brauner Pilz"; + case BROWN_MUSHROOM_BLOCK -> "Brauner Pilzblock"; + case RED_MUSHROOM -> "Roter Pilz"; + case RED_MUSHROOM_BLOCK -> "Roter Pilzblock"; + case MUSHROOM_STEM -> "Pilzstiel"; + case NETHER_SPROUTS -> "Nether-Sprossen"; + case TWISTING_VINES -> "Gedrehte Ranken"; + case WEEPING_VINES -> "Weinende Ranken"; + case VINE -> "Ranken"; + case SHROOMLIGHT -> "Pilzlicht"; + case GLOW_BERRIES -> "Leuchtbeeren"; + case GLOW_LICHEN -> "Leuchtflechte"; + case SPORE_BLOSSOM -> "Sporenblüte"; + case SWEET_BERRY_BUSH -> "Süßbeerenstrauch"; + + // Blöcke und Baumaterialien + case ANDESITE -> "Andesit"; + case DIORITE -> "Diorit"; + case GRANITE -> "Granit"; + case TUFF -> "Tuffstein"; + case CALCITE -> "Kalzit"; + case BLACKSTONE -> "Schwarzstein"; + case POLISHED_BLACKSTONE -> "Polierter Schwarzstein"; + case POLISHED_BLACKSTONE_BRICKS -> "Polierte Schwarzstein-Ziegel"; + case DRIPSTONE_BLOCK -> "Tropfstein"; + case POINTED_DRIPSTONE -> "Spitzer Tropfstein"; + case MAGMA_BLOCK -> "Magmablock"; + case SAND -> "Sand"; + case RED_SAND -> "Roter Sand"; + case SANDSTONE -> "Sandstein"; + case RED_SANDSTONE -> "Roter Sandstein"; + case TERRACOTTA -> "Terrakotta"; + case CLAY -> "Ton"; + case BRICK -> "Ziegel"; + case GRAVEL -> "Kies"; + case DIRT -> "Erde"; + case COARSE_DIRT -> "Grobe Erde"; + case GRASS_BLOCK -> "Grasblock"; + case SOUL_SAND -> "Seelensand"; + case SOUL_SOIL -> "Seelenerde"; + case BLUE_ICE -> "Blaues Eis"; + case ICE -> "Eis"; + case PACKED_ICE -> "Packeis"; + case SNOW_BLOCK -> "Schneeblock"; + + // Seltene Blöcke + case NETHER_BRICKS -> "Netherziegel"; + case QUARTZ_BLOCK -> "Quarzblock"; + case GLOWSTONE -> "Glowstone"; + case OBSIDIAN -> "Obsidian"; + case CRYING_OBSIDIAN -> "Weinender Obsidian"; + case REDSTONE_BLOCK -> "Redstone-Block"; + case CHAIN -> "Kette"; + case IRON_BARS -> "Eisengitter"; + case LANTERN -> "Laterne"; + case SOUL_LANTERN -> "Seelenlaterne"; + case TORCH -> "Fackel"; + case SOUL_TORCH -> "Seelenfackel"; + case TNT -> "TNT"; + case SCAFFOLDING -> "Gerüst"; + case LEAD -> "Leine"; + case NAME_TAG -> "Namensschild"; + + // Funktionale Blöcke + case ENDER_CHEST -> "Endertruhe"; + case BARREL -> "Fass"; + case TRAPPED_CHEST -> "Redstone-Truhe"; + case BLAST_FURNACE -> "Schmelzofen"; + case SMOKER -> "Räucherofen"; + case CAMPFIRE -> "Lagerfeuer"; + case COMPOSTER -> "Komposter"; + case GRINDSTONE -> "Schleifstein"; + case CARTOGRAPHY_TABLE -> "Kartentisch"; + case SMITHING_TABLE -> "Schmiedetisch"; + case FLETCHING_TABLE -> "Bognerisch"; + case LOOM -> "Webstuhl"; + case LECTERN -> "Lesepult"; + case ANVIL -> "Amboss"; + + // Erze + case COAL_ORE -> "Kohleerz"; + case IRON_ORE -> "Eisenerz"; + case COPPER_ORE -> "Kupfererz"; + case GOLD_ORE -> "Golderz"; + case DIAMOND_ORE -> "Diamanterz"; + case EMERALD_ORE -> "Smaragderz"; + case NETHER_QUARTZ_ORE -> "Netherquarzerz"; + case NETHER_GOLD_ORE -> "Nethergolderz"; + case LAPIS_ORE -> "Lapislazulierz"; + case ANCIENT_DEBRIS -> "Antike Trümmer"; + case REDSTONE_ORE -> "Redstone-Erz"; + + // Ingots und raffinierte Materialien + case COAL -> "Kohle"; case IRON_INGOT -> "Eisenbarren"; + case COPPER_INGOT -> "Kupferbarren"; case GOLD_INGOT -> "Goldbarren"; + case DIAMOND -> "Diamant"; case EMERALD -> "Smaragd"; - case COAL -> "Kohle"; + case QUARTZ -> "Netherquarz"; + case LAPIS_LAZULI -> "Lapislazuli"; + case REDSTONE -> "Redstone-Staub"; + + // Spawn Eggs + case ALLAY_SPAWN_EGG -> "Allay-Spawn-Ei"; + // case ARMADILLO_SPAWN_EGG -> "Gürteltier-Spawn-Ei"; // Not available in this version + case ENDERMITE_SPAWN_EGG -> "Endermilbe-Spawn-Ei"; + case FROG_SPAWN_EGG -> "Frosch-Spawn-Ei"; + case GOAT_SPAWN_EGG -> "Ziegen-Spawn-Ei"; + case ZOMBIE_SPAWN_EGG -> "Zombie-Spawn-Ei"; + case SHULKER_SPAWN_EGG -> "Shulker-Spawn-Ei"; + case STRIDER_SPAWN_EGG -> "Schreiter-Spawn-Ei"; + case VILLAGER_SPAWN_EGG -> "Dorfbewohner-Spawn-Ei"; + default -> material.name().replace("_", " ").toLowerCase(); }; } @@ -524,10 +868,16 @@ private String getGermanItemName(Material material) { private static class TradingSession { private final Player player; private final Inventory inventory; + private int currentPage; + private int totalPages; + private List allTradeableItems; public TradingSession(Player player, Inventory inventory) { this.player = player; this.inventory = inventory; + this.currentPage = 0; + this.totalPages = 1; + this.allTradeableItems = new ArrayList<>(); } public Player getPlayer() { @@ -537,5 +887,39 @@ public Player getPlayer() { public Inventory getInventory() { return inventory; } + + public int getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(int currentPage) { + this.currentPage = Math.max(0, Math.min(currentPage, totalPages - 1)); + } + + public int getTotalPages() { + return totalPages; + } + + public void setTotalPages(int totalPages) { + this.totalPages = Math.max(1, totalPages); + } + + public List getAllTradeableItems() { + return allTradeableItems; + } + + public void setAllTradeableItems(List allTradeableItems) { + this.allTradeableItems = allTradeableItems != null ? allTradeableItems : new ArrayList<>(); + // Berechne die Gesamtseitenzahl basierend auf Items pro Seite + this.totalPages = Math.max(1, (int) Math.ceil((double) this.allTradeableItems.size() / 45.0)); + } + + public boolean hasNextPage() { + return currentPage < totalPages - 1; + } + + public boolean hasPreviousPage() { + return currentPage > 0; + } } } \ No newline at end of file diff --git a/src/main/java/de/simpleeco/villager/ShopVillagerManager.java b/src/main/java/de/simpleeco/villager/ShopVillagerManager.java index a386888..1ba498d 100644 --- a/src/main/java/de/simpleeco/villager/ShopVillagerManager.java +++ b/src/main/java/de/simpleeco/villager/ShopVillagerManager.java @@ -80,8 +80,17 @@ private void configureShopVillager(Villager villager) { villager.setCustomName("§e§l" + villagerName); villager.setCustomNameVisible(true); - // Bewegung verhindern - villager.setAI(false); + // Bewegung konfigurieren - AI nur teilweise deaktivieren für Look-Funktionalität + boolean lookAtPlayers = configManager.getConfig().getBoolean("villagerBehavior.lookAtPlayers", true); + if (lookAtPlayers) { + // AI aktiviert lassen, aber Bewegung einschränken + villager.setAI(true); + villager.setCollidable(false); // Keine Kollision mit anderen Entities + } else { + // Vollständig deaktivieren + villager.setAI(false); + } + villager.setSilent(true); villager.setInvulnerable(true); villager.setRemoveWhenFarAway(false); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0c9deb9..a12a37a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -22,17 +22,45 @@ atmVillager: profession: "LIBRARIAN" # Beruf des ATM-Villagers (LIBRARIAN für Banker-Look) villagerType: "PLAINS" # Typ des Villagers (PLAINS, DESERT, JUNGLE, etc.) +# Villager-Verhalten Einstellungen +villagerBehavior: + lookAtPlayers: true # Sollen Villager zu Spielern schauen? + lookDistance: 8.0 # Maximale Entfernung für das Anschauen von Spielern + lookUpdateInterval: 20 # Update-Intervall in Ticks (20 = 1 Sekunde) + +# Spawn-Kosteneinstellungen +spawnCosts: + shopCost: 500.0 # Kosten für das Spawnen eines Shops + atmCost: 750.0 # Kosten für das Spawnen eines ATMs + enabled: true # Kosten aktivieren/deaktivieren + freeForAdmins: false # Admins mit simpleeco.spawn.free spawnen kostenlos + enforceForAll: true # Wenn true, zahlen ALLE Spieler (auch OPs), wenn false nur normale Spieler + +# Todesstrafe-Einstellungen +deathPenalty: + enabled: true # Todesstrafe aktivieren/deaktivieren + cashLossPercentage: 0.25 # Anteil des Bargeldes der verloren geht (0.25 = 25%) + minLossAmount: 1.0 # Minimaler Verlust-Betrag + maxLossAmount: 10000.0 # Maximaler Verlust-Betrag + exemptPermission: "simpleeco.death.exempt" # Berechtigung um Todesstrafe zu umgehen + onlyPvPDeath: false # Nur bei PvP-Tod Geld verlieren (false = bei jedem Tod) + # Scoreboard Einstellungen scoreboard: enabled: true # Scoreboard aktivieren/deaktivieren - title: "§6§lSimpleEco" # Titel des Scoreboards + title: "§6§l✦ SimpleEco ✦" # Titel des Scoreboards updateInterval: 20 # Update-Intervall in Ticks (20 = 1 Sekunde) lines: - - "§7§m " - - "§e§lKontostand:" - - "§a{balance} {currency}" + - "§7§m━━━━━━━━━━━━━━━━" + - "§e§lFinanzen" + - "" + - "§aBargeld:" + - "§f {cash}" - "" - - "§7§m " + - "§6Bank:" + - "§f {bank}" + - "" + - "§7§m━━━━━━━━━━━━━━━━" # Preiseinstellungen pricing: @@ -55,100 +83,1270 @@ pricing: # Höherer priceFactor = volatiler (stärkere Preisschwankungen) # Niedrigere referenceAmount = reaktionsschneller (weniger Handel nötig für Preisänderung) items: - WHEAT: + # Holz und Holzprodukte + OAK_LOG: basePrice: 10.0 minPrice: 5.0 - maxPrice: 50.0 + maxPrice: 20.0 + buyable: true + sellable: false + OAK_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + OAK_SAPLING: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 buyable: true sellable: true - # referenceAmount: 1000 # Optional: Überschreibt globalen Wert - # priceFactor: 0.05 # Optional: Überschreibt globalen Wert - CARROT: - basePrice: 8.0 - minPrice: 4.0 + SPRUCE_LOG: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 20.0 + buyable: true + sellable: false + SPRUCE_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + SPRUCE_SAPLING: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 30.0 + buyable: true + sellable: false + BIRCH_LOG: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + BIRCH_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + BIRCH_SAPLING: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + JUNGLE_LOG: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + JUNGLE_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + JUNGLE_SAPLING: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + ACACIA_LOG: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + ACACIA_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + ACACIA_SAPLING: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + DARK_OAK_LOG: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + DARK_OAK_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + DARK_OAK_SAPLING: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 30.0 + buyable: true + sellable: false + MANGROVE_LOG: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + MANGROVE_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + MANGROVE_PROPAGULE: + basePrice: 18.0 + minPrice: 10.0 + maxPrice: 35.0 + buyable: true + sellable: false + CHERRY_LOG: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + CHERRY_LEAVES: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + CHERRY_SAPLING: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 30.0 + buyable: true + sellable: false + BAMBOO_BLOCK: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 35.0 + buyable: true + sellable: false + BAMBOO: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 22.0 + buyable: true + sellable: false + CRIMSON_STEM: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + CRIMSON_FUNGUS: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 25.0 + buyable: true + sellable: false + CRIMSON_ROOTS: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 28.0 + buyable: true + sellable: false + WARPED_STEM: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: false + WARPED_FUNGUS: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 25.0 + buyable: true + sellable: false + WARPED_ROOTS: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 28.0 + buyable: true + sellable: false + + # Mob-Drops + BEEHIVE: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 60.0 + buyable: true + sellable: true + BEE_NEST: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 60.0 + buyable: true + sellable: true + HONEY_BLOCK: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 60.0 + buyable: true + sellable: true + HONEYCOMB_BLOCK: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 60.0 + buyable: true + sellable: true + HONEY_BOTTLE: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 60.0 + buyable: true + sellable: true + EGG: + basePrice: 2.0 + minPrice: 1.0 maxPrice: 40.0 buyable: true + sellable: false + FEATHER: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 18.0 + buyable: true sellable: true - POTATO: - basePrice: 8.0 - minPrice: 4.0 + LEATHER: + basePrice: 2.0 + minPrice: 1.0 maxPrice: 40.0 buyable: true sellable: true - BEETROOT: - basePrice: 12.0 - minPrice: 6.0 - maxPrice: 60.0 + RABBIT_HIDE: + basePrice: 25.0 + minPrice: 15.0 + maxPrice: 40.0 buyable: true sellable: true - APPLE: - basePrice: 15.0 - minPrice: 8.0 - maxPrice: 75.0 + TURTLE_EGG: + basePrice: 150.0 + minPrice: 105.0 + maxPrice: 180.0 buyable: true sellable: true - BREAD: + SCUTE: basePrice: 25.0 - minPrice: 12.0 - maxPrice: 125.0 + minPrice: 15.0 + maxPrice: 40.0 buyable: true - sellable: false # Nur kaufbar, nicht verkaufbar - COOKED_BEEF: + sellable: true + PUFFERFISH: + basePrice: 30.0 + minPrice: 18.0 + maxPrice: 45.0 + buyable: true + sellable: true + INK_SAC: basePrice: 20.0 minPrice: 10.0 - maxPrice: 100.0 + maxPrice: 30.0 buyable: true sellable: true - COOKED_PORKCHOP: - basePrice: 18.0 - minPrice: 9.0 - maxPrice: 90.0 + GLOW_INK_SAC: + basePrice: 22.0 + minPrice: 12.0 + maxPrice: 35.0 buyable: true sellable: true - COOKED_CHICKEN: - basePrice: 16.0 - minPrice: 8.0 - maxPrice: 80.0 + BONE: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 18.0 + buyable: true + sellable: false + ARROW: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 20.0 + buyable: true + sellable: false + BONE_MEAL: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 18.0 + buyable: true + sellable: false + BONE_BLOCK: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 35.0 + buyable: true + sellable: false + STRING: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 28.0 + buyable: true + sellable: false + SPIDER_EYE: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 32.0 + buyable: true + sellable: false + SLIME_BALL: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 50.0 + buyable: true + sellable: false + SLIME_BLOCK: + basePrice: 32.0 + minPrice: 22.0 + maxPrice: 52.0 buyable: true sellable: true - DIAMOND: - basePrice: 500.0 - minPrice: 250.0 - maxPrice: 2500.0 + GUNPOWDER: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 35.0 buyable: true - sellable: false # Nur kaufbar (zu wertvoll für Verkauf) - priceFactor: 0.02 # Niedriger Faktor - weniger volatil - referenceAmount: 100 # Kleine Referenzmenge - reagiert schneller - IRON_INGOT: - basePrice: 50.0 - minPrice: 25.0 - maxPrice: 250.0 + sellable: false + PHANTOM_MEMBRANE: + basePrice: 80.0 + minPrice: 50.0 + maxPrice: 120.0 buyable: true sellable: true - priceFactor: 0.08 # Höherer Faktor - volatiler - GOLD_INGOT: - basePrice: 100.0 + ROTTEN_FLESH: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 15.0 + buyable: true + sellable: false + BLAZE_ROD: + basePrice: 80.0 minPrice: 50.0 - maxPrice: 500.0 + maxPrice: 120.0 buyable: true sellable: true - priceFactor: 0.06 # Mittel-volatil - EMERALD: + BLAZE_POWDER: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 65.0 + buyable: true + sellable: true + MAGMA_CREAM: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 65.0 + buyable: true + sellable: true + GHAST_TEAR: basePrice: 200.0 - minPrice: 100.0 - maxPrice: 1000.0 + minPrice: 160.0 + maxPrice: 350.0 buyable: true - sellable: false # Nur kaufbar - priceFactor: 0.03 # Sehr stabil - referenceAmount: 50 # Kleine Menge - exklusiv - COAL: - basePrice: 5.0 - minPrice: 2.0 + sellable: true + ENDER_PEARL: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 150.0 + buyable: true + sellable: false + ENDER_EYE: + basePrice: 160.0 + minPrice: 80.0 + maxPrice: 280.0 + buyable: true + sellable: false + SHULKER_SHELL: + basePrice: 330.0 + minPrice: 190.0 + maxPrice: 2000.0 + buyable: true + sellable: true + DRAGON_BREATH: + basePrice: 200.0 + minPrice: 150.0 + maxPrice: 300.0 + buyable: true + sellable: false + + # Pflanzen + ALLIUM: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 25.0 + buyable: true + sellable: true + AZURE_BLUET: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 25.0 + buyable: true + sellable: true + BLUE_ORCHID: + basePrice: 12.0 + minPrice: 6.0 maxPrice: 25.0 - buyable: false # Nur verkaufbar (Rohstoff) + buyable: true + sellable: true + CORNFLOWER: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 25.0 + buyable: true + sellable: true + DANDELION: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: true + LILAC: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 28.0 + buyable: true + sellable: true + LILY_OF_THE_VALLEY: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 28.0 + buyable: true + sellable: true + PEONY: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 28.0 + buyable: true + sellable: true + POPPY: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: true + ROSE_BUSH: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 28.0 + buyable: true + sellable: true + SUNFLOWER: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 28.0 + buyable: true + sellable: true + RED_TULIP: + basePrice: 11.0 + minPrice: 6.0 + maxPrice: 22.0 + buyable: true + sellable: true + ORANGE_TULIP: + basePrice: 11.0 + minPrice: 6.0 + maxPrice: 22.0 + buyable: true + sellable: true + WHITE_TULIP: + basePrice: 11.0 + minPrice: 6.0 + maxPrice: 22.0 + buyable: true + sellable: true + PINK_TULIP: + basePrice: 11.0 + minPrice: 6.0 + maxPrice: 22.0 + buyable: true + sellable: true + OXEYE_DAISY: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 25.0 + buyable: true + sellable: true + DEAD_BUSH: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + CACTUS: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 20.0 + buyable: true + sellable: true + FERN: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: true + LARGE_FERN: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 24.0 + buyable: true + sellable: true + SHORT_GRASS: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 15.0 + buyable: true + sellable: true + TALL_GRASS: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + LILY_PAD: + basePrice: 10.0 + minPrice: 3.0 + maxPrice: 23.0 + buyable: true + sellable: true + + # Spezielle Pflanzen und Deko + AZALEA: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 28.0 + buyable: true + sellable: true + FLOWERING_AZALEA: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 30.0 + buyable: true + sellable: true + HANGING_ROOTS: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 24.0 + buyable: true + sellable: true + MOSS_BLOCK: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 28.0 + buyable: true + sellable: false + MOSS_CARPET: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 22.0 + buyable: true + sellable: false + CHORUS_FLOWER: + basePrice: 20.0 + minPrice: 10.0 + maxPrice: 35.0 + buyable: true + sellable: true + CHORUS_PLANT: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 32.0 + buyable: true + sellable: true + BIG_DRIPLEAF: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 30.0 + buyable: true + sellable: true + SMALL_DRIPLEAF: + basePrice: 15.0 + minPrice: 7.0 + maxPrice: 26.0 + buyable: true + sellable: true + BROWN_MUSHROOM: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + BROWN_MUSHROOM_BLOCK: + basePrice: 18.0 + minPrice: 10.0 + maxPrice: 30.0 + buyable: true + sellable: true + RED_MUSHROOM: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + RED_MUSHROOM_BLOCK: + basePrice: 18.0 + minPrice: 10.0 + maxPrice: 30.0 + buyable: true + sellable: true + MUSHROOM_STEM: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 26.0 + buyable: true + sellable: true + NETHER_SPROUTS: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 26.0 + buyable: true + sellable: true + TWISTING_VINES: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 30.0 + buyable: true + sellable: true + WEEPING_VINES: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 30.0 + buyable: true + sellable: true + VINE: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 24.0 + buyable: true + sellable: false + SHROOMLIGHT: + basePrice: 22.0 + minPrice: 12.0 + maxPrice: 35.0 + buyable: true + sellable: true + GLOW_BERRIES: + basePrice: 20.0 + minPrice: 10.0 + maxPrice: 30.0 + buyable: true + sellable: true + GLOW_LICHEN: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 28.0 + buyable: true + sellable: true + SPORE_BLOSSOM: + basePrice: 28.0 + minPrice: 15.0 + maxPrice: 45.0 + buyable: true + sellable: true + SWEET_BERRY_BUSH: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 28.0 + buyable: true + sellable: true + + # Blöcke und Baumaterialien + ANDESITE: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 14.0 + buyable: true + sellable: true + DIORITE: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 14.0 + buyable: true + sellable: true + GRANITE: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 14.0 + buyable: true + sellable: true + TUFF: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: false + CALCITE: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 20.0 + buyable: true + sellable: true + BLACKSTONE: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: false + POLISHED_BLACKSTONE: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 20.0 + buyable: true + sellable: true + POLISHED_BLACKSTONE_BRICKS: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 24.0 + buyable: true + sellable: true + DRIPSTONE_BLOCK: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + POINTED_DRIPSTONE: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 22.0 + buyable: true + sellable: true + MAGMA_BLOCK: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 24.0 + buyable: true + sellable: true + SAND: + basePrice: 6.0 + minPrice: 3.0 + maxPrice: 10.0 + buyable: true + sellable: false + RED_SAND: + basePrice: 7.0 + minPrice: 4.0 + maxPrice: 12.0 + buyable: true + sellable: false + SANDSTONE: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 16.0 + buyable: true + sellable: true + RED_SANDSTONE: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 18.0 + buyable: true + sellable: true + TERRACOTTA: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 20.0 + buyable: true + sellable: true + CLAY: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 18.0 + buyable: true + sellable: true + BRICK: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 28.0 + buyable: true + sellable: true + GRAVEL: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 10.0 + buyable: true + sellable: true + DIRT: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 8.0 + buyable: true + sellable: false + COARSE_DIRT: + basePrice: 5.0 + minPrice: 3.0 + maxPrice: 9.0 + buyable: true + sellable: true + GRASS_BLOCK: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 5.0 + buyable: true + sellable: true + SOUL_SAND: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + SOUL_SOIL: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 18.0 + buyable: true + sellable: true + BLUE_ICE: + basePrice: 10.0 + minPrice: 2.0 + maxPrice: 45.0 + buyable: true + sellable: true + ICE: + basePrice: 10.0 + minPrice: 2.0 + maxPrice: 22.0 + buyable: true + sellable: true + PACKED_ICE: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 20.0 + buyable: true + sellable: true + SNOW_BLOCK: + basePrice: 10.0 + minPrice: 2.0 + maxPrice: 18.0 + buyable: true + sellable: true + + # Seltene Blöcke + NETHER_BRICKS: + basePrice: 14.0 + minPrice: 7.0 + maxPrice: 25.0 + buyable: true + sellable: true + QUARTZ_BLOCK: + basePrice: 20.0 + minPrice: 12.0 + maxPrice: 35.0 + buyable: true + sellable: true + GLOWSTONE: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 30.0 + buyable: true + sellable: true + OBSIDIAN: + basePrice: 25.0 + minPrice: 15.0 + maxPrice: 40.0 + buyable: true + sellable: true + CRYING_OBSIDIAN: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 50.0 + buyable: true + sellable: true + REDSTONE_BLOCK: + basePrice: 6.0 + minPrice: 4.0 + maxPrice: 15.0 + buyable: true + sellable: true + CHAIN: + basePrice: 10.0 + minPrice: 6.0 + maxPrice: 18.0 + buyable: true + sellable: false + IRON_BARS: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 14.0 + buyable: true + sellable: false + LANTERN: + basePrice: 10.0 + minPrice: 2.0 + maxPrice: 20.0 + buyable: true + sellable: true + SOUL_LANTERN: + basePrice: 16.0 + minPrice: 9.0 + maxPrice: 26.0 + buyable: true + sellable: true + TORCH: + basePrice: 4.0 + minPrice: 2.0 + maxPrice: 6.0 + buyable: true + sellable: true + SOUL_TORCH: + basePrice: 6.0 + minPrice: 3.0 + maxPrice: 10.0 + buyable: true + sellable: true + TNT: + basePrice: 130.0 + minPrice: 120.0 + maxPrice: 180.0 + buyable: false + sellable: true + SCAFFOLDING: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 14.0 + buyable: true + sellable: true + LEAD: + basePrice: 10.0 + minPrice: 2.0 + maxPrice: 28.0 + buyable: true + sellable: true + NAME_TAG: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: true + + # Funktionale Blöcke + ENDER_CHEST: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + BARREL: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + TRAPPED_CHEST: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + BLAST_FURNACE: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + SMOKER: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + CAMPFIRE: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + COMPOSTER: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + GRINDSTONE: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + CARTOGRAPHY_TABLE: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + SMITHING_TABLE: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + FLETCHING_TABLE: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + LOOM: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + LECTERN: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + ANVIL: + basePrice: 40.0 + minPrice: 25.0 + maxPrice: 70.0 + buyable: true + sellable: false + + # Erze + COAL_ORE: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 20.0 + buyable: true + sellable: true + IRON_ORE: + basePrice: 12.0 + minPrice: 8.0 + maxPrice: 20.0 + buyable: true + sellable: true + COPPER_ORE: + basePrice: 18.0 + minPrice: 10.0 + maxPrice: 30.0 + buyable: true + sellable: true + GOLD_ORE: + basePrice: 30.0 + minPrice: 18.0 + maxPrice: 50.0 + buyable: true + sellable: true + DIAMOND_ORE: + basePrice: 190.0 + minPrice: 100.0 + maxPrice: 400.0 + buyable: true + sellable: true + EMERALD_ORE: + basePrice: 110.0 + minPrice: 85.0 + maxPrice: 150.0 + buyable: true + sellable: true + NETHER_QUARTZ_ORE: + basePrice: 25.0 + minPrice: 15.0 + maxPrice: 40.0 + buyable: true + sellable: true + NETHER_GOLD_ORE: + basePrice: 28.0 + minPrice: 17.0 + maxPrice: 45.0 + buyable: true + sellable: true + LAPIS_ORE: + basePrice: 22.0 + minPrice: 13.0 + maxPrice: 35.0 + buyable: true + sellable: true + ANCIENT_DEBRIS: + basePrice: 650.0 + minPrice: 520.0 + maxPrice: 800.0 + buyable: true + sellable: true + REDSTONE_ORE: + basePrice: 18.0 + minPrice: 10.0 + maxPrice: 30.0 + buyable: true + sellable: true + + # Ingots und raffinierte Materialien + COAL: + basePrice: 12.0 + minPrice: 7.0 + maxPrice: 25.0 + buyable: true + sellable: true + IRON_INGOT: + basePrice: 1.0 + minPrice: 1.0 + maxPrice: 30.0 + buyable: true + sellable: false + COPPER_INGOT: + basePrice: 35.0 + minPrice: 20.0 + maxPrice: 55.0 + buyable: true + sellable: true + GOLD_INGOT: + basePrice: 50.0 + minPrice: 30.0 + maxPrice: 75.0 + buyable: true + sellable: true + DIAMOND: + basePrice: 280.0 + minPrice: 130.0 + maxPrice: 600.0 + buyable: true + sellable: true + EMERALD: + basePrice: 2.0 + minPrice: 1.0 + maxPrice: 20.0 + buyable: true + sellable: true + QUARTZ: + basePrice: 30.0 + minPrice: 18.0 + maxPrice: 45.0 + buyable: true + sellable: true + LAPIS_LAZULI: + basePrice: 28.0 + minPrice: 15.0 + maxPrice: 40.0 + buyable: true + sellable: true + REDSTONE: + basePrice: 20.0 + minPrice: 12.0 + maxPrice: 35.0 + buyable: true + sellable: false + + # Spawn Eggs + ALLAY_SPAWN_EGG: + basePrice: 1200.0 + minPrice: 950.0 + maxPrice: 1500.0 + buyable: true + sellable: true + # ARMADILLO_SPAWN_EGG not available in this version + # ARMADILLO_SPAWN_EGG: + # basePrice: 1100.0 + # minPrice: 900.0 + # maxPrice: 1400.0 + # buyable: true + # sellable: true + ENDERMITE_SPAWN_EGG: + basePrice: 850.0 + minPrice: 700.0 + maxPrice: 1000.0 + buyable: true + sellable: true + FROG_SPAWN_EGG: + basePrice: 200.0 + minPrice: 100.0 + maxPrice: 450.0 + buyable: true + sellable: true + GOAT_SPAWN_EGG: + basePrice: 350.0 + minPrice: 250.0 + maxPrice: 700.0 + buyable: true + sellable: true + ZOMBIE_SPAWN_EGG: + basePrice: 300.0 + minPrice: 200.0 + maxPrice: 400.0 + buyable: true + sellable: true + SHULKER_SPAWN_EGG: + basePrice: 2500.0 + minPrice: 2000.0 + maxPrice: 3000.0 + buyable: true + sellable: true + STRIDER_SPAWN_EGG: + basePrice: 500.0 + minPrice: 400.0 + maxPrice: 650.0 + buyable: true + sellable: true + VILLAGER_SPAWN_EGG: + basePrice: 180.0 + minPrice: 150.0 + maxPrice: 220.0 + buyable: true + sellable: true + + # Traditionelle Lebensmittel (aus dem Original behalten) + WHEAT: + basePrice: 10.0 + minPrice: 5.0 + maxPrice: 50.0 + buyable: true + sellable: true + CARROT: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 40.0 + buyable: true + sellable: true + POTATO: + basePrice: 8.0 + minPrice: 4.0 + maxPrice: 40.0 + buyable: true + sellable: true + BEETROOT: + basePrice: 12.0 + minPrice: 6.0 + maxPrice: 60.0 + buyable: true + sellable: true + APPLE: + basePrice: 15.0 + minPrice: 8.0 + maxPrice: 75.0 + buyable: true + sellable: true + BREAD: + basePrice: 25.0 + minPrice: 12.0 + maxPrice: 125.0 + buyable: true + sellable: false + COOKED_BEEF: + basePrice: 20.0 + minPrice: 10.0 + maxPrice: 100.0 + buyable: true + sellable: true + COOKED_PORKCHOP: + basePrice: 18.0 + minPrice: 9.0 + maxPrice: 90.0 + buyable: true + sellable: true + COOKED_CHICKEN: + basePrice: 16.0 + minPrice: 8.0 + maxPrice: 80.0 + buyable: true sellable: true - priceFactor: 0.1 # Sehr volatil - referenceAmount: 2000 # Große Menge - Massengut # Villager-Handelseinstellungen trading: @@ -175,3 +1373,10 @@ messages: villagerSpawned: "§aShop-Villager erfolgreich gespawnt!" lookingAtNoBlock: "§cDu musst auf einen Block schauen!" villagerSpawnFailed: "§cFehler beim Spawnen des Villagers!" + atmSpawned: "§aATM-Villager erfolgreich gespawnt!" + atmSpawnFailed: "§cFehler beim Spawnen des ATM-Villagers!" + spawnCostCharged: "§7Kosten abgezogen: §c-{amount} {currency}" + spawnCostRefunded: "§aKosten zurückerstattet: §e+{amount} {currency}" + insufficientFundsForSpawn: "§cNicht genügend Guthaben! Benötigt: §e{amount} {currency}" + deathPenaltyCash: "§c💀 Du hast beim Tod §e{amount} {currency} §cBargeld verloren!" + deathPenaltyExempt: "§a🛡️ Du bist vor Geldverlust beim Tod geschützt!" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ea4a301..84bf16e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,20 +1,16 @@ name: SimpleEco version: 1.0.0 main: de.simpleeco.SimpleEcoPlugin -api-version: 1.20 +api-version: "1.20" description: Dynamisches Wirtschaftssystem Plugin mit Villager-Trading -author: SimpleEco Team +author: Nichtmetall website: https://github.com/simpleeco commands: eco: - description: Hauptkommando für das Wirtschaftssystem - usage: /eco [args...] + description: Hauptkommando für das SimpleEco Plugin + usage: /eco [args...] permission: simpleeco.use - spawn: - description: Spawnt spezielle Entities für das Plugin - usage: /spawn - permission: simpleeco.spawn permissions: simpleeco.use: @@ -25,10 +21,22 @@ permissions: default: op simpleeco.balance.other: description: Erlaubt das Einsehen fremder Kontostände + default: true + simpleeco.balance.admin: + description: Erlaubt das Hinzufügen und Entfernen von Geld bei Spielern default: op simpleeco.spawn: description: Erlaubt das Spawnen von Shop-Entities - default: op - simpleeco.spawn.villager: + default: true + simpleeco.spawn.shop: description: Erlaubt das Spawnen von Shop-Villagern + default: true + simpleeco.spawn.atm: + description: Erlaubt das Spawnen von ATM-Villagern + default: true + simpleeco.spawn.free: + description: Erlaubt kostenloses Spawnen (ohne Kosten) + default: op + simpleeco.death.exempt: + description: Befreit von Geldverlust beim Tod default: op diff --git a/target/classes/config.yml b/target/classes/config.yml deleted file mode 100644 index 0c9deb9..0000000 --- a/target/classes/config.yml +++ /dev/null @@ -1,177 +0,0 @@ -# SimpleEco Plugin Konfiguration - -# Währungseinstellungen -currency: - name: "Gold" # Name der Währung - startBalance: 1000.0 # Startguthaben für neue Spieler - symbol: "G" # Symbol der Währung - -# Datenbankeinstellungen -database: - path: "plugins/SimpleEco/economy.db" # Pfad zur SQLite-Datenbankdatei - -# Shop-Villager Einstellungen -shopVillager: - name: "Shop" # Name der Shop-Villager - profession: "TOOLSMITH" # Beruf des Villagers (TOOLSMITH, LIBRARIAN, etc.) - villagerType: "PLAINS" # Typ des Villagers (PLAINS, DESERT, JUNGLE, etc.) - -# ATM-Villager Einstellungen -atmVillager: - name: "Bank-Automat" # Name der ATM-Villager - profession: "LIBRARIAN" # Beruf des ATM-Villagers (LIBRARIAN für Banker-Look) - villagerType: "PLAINS" # Typ des Villagers (PLAINS, DESERT, JUNGLE, etc.) - -# Scoreboard Einstellungen -scoreboard: - enabled: true # Scoreboard aktivieren/deaktivieren - title: "§6§lSimpleEco" # Titel des Scoreboards - updateInterval: 20 # Update-Intervall in Ticks (20 = 1 Sekunde) - lines: - - "§7§m " - - "§e§lKontostand:" - - "§a{balance} {currency}" - - "" - - "§7§m " - -# Preiseinstellungen -pricing: - priceFactor: 0.05 # Elastizitätsfaktor (5% = 0.05) - Globaler Standard - referenceAmount: 1000 # Referenzmenge für Preisberechnung - Globaler Standard - regressionTimeMinutes: 60 # Zeit in Minuten bis Preise sich zum Default zurückbewegen - regressionUpdateInterval: 5 # Intervall in Minuten für Preis-Updates - - # Standard-Items mit Preiseinstellungen - # - # Jedes Item kann folgende Parameter haben: - # - basePrice: Basispreis des Items - # - minPrice: Minimaler Preis (Untergrenze) - # - maxPrice: Maximaler Preis (Obergrenze) - # - buyable: true/false - Kann das Item gekauft werden? - # - sellable: true/false - Kann das Item verkauft werden? - # - priceFactor: Optional - Überschreibt globalen priceFactor (Volatilität) - # - referenceAmount: Optional - Überschreibt globale referenceAmount (Reaktionsgeschwindigkeit) - # - # Höherer priceFactor = volatiler (stärkere Preisschwankungen) - # Niedrigere referenceAmount = reaktionsschneller (weniger Handel nötig für Preisänderung) - items: - WHEAT: - basePrice: 10.0 - minPrice: 5.0 - maxPrice: 50.0 - buyable: true - sellable: true - # referenceAmount: 1000 # Optional: Überschreibt globalen Wert - # priceFactor: 0.05 # Optional: Überschreibt globalen Wert - CARROT: - basePrice: 8.0 - minPrice: 4.0 - maxPrice: 40.0 - buyable: true - sellable: true - POTATO: - basePrice: 8.0 - minPrice: 4.0 - maxPrice: 40.0 - buyable: true - sellable: true - BEETROOT: - basePrice: 12.0 - minPrice: 6.0 - maxPrice: 60.0 - buyable: true - sellable: true - APPLE: - basePrice: 15.0 - minPrice: 8.0 - maxPrice: 75.0 - buyable: true - sellable: true - BREAD: - basePrice: 25.0 - minPrice: 12.0 - maxPrice: 125.0 - buyable: true - sellable: false # Nur kaufbar, nicht verkaufbar - COOKED_BEEF: - basePrice: 20.0 - minPrice: 10.0 - maxPrice: 100.0 - buyable: true - sellable: true - COOKED_PORKCHOP: - basePrice: 18.0 - minPrice: 9.0 - maxPrice: 90.0 - buyable: true - sellable: true - COOKED_CHICKEN: - basePrice: 16.0 - minPrice: 8.0 - maxPrice: 80.0 - buyable: true - sellable: true - DIAMOND: - basePrice: 500.0 - minPrice: 250.0 - maxPrice: 2500.0 - buyable: true - sellable: false # Nur kaufbar (zu wertvoll für Verkauf) - priceFactor: 0.02 # Niedriger Faktor - weniger volatil - referenceAmount: 100 # Kleine Referenzmenge - reagiert schneller - IRON_INGOT: - basePrice: 50.0 - minPrice: 25.0 - maxPrice: 250.0 - buyable: true - sellable: true - priceFactor: 0.08 # Höherer Faktor - volatiler - GOLD_INGOT: - basePrice: 100.0 - minPrice: 50.0 - maxPrice: 500.0 - buyable: true - sellable: true - priceFactor: 0.06 # Mittel-volatil - EMERALD: - basePrice: 200.0 - minPrice: 100.0 - maxPrice: 1000.0 - buyable: true - sellable: false # Nur kaufbar - priceFactor: 0.03 # Sehr stabil - referenceAmount: 50 # Kleine Menge - exklusiv - COAL: - basePrice: 5.0 - minPrice: 2.0 - maxPrice: 25.0 - buyable: false # Nur verkaufbar (Rohstoff) - sellable: true - priceFactor: 0.1 # Sehr volatil - referenceAmount: 2000 # Große Menge - Massengut - -# Villager-Handelseinstellungen -trading: - menuTitle: "§6§lWirtschaftshandel" - buyButtonName: "§a§lKaufen" - sellButtonName: "§c§lVerkaufen" - infoButtonName: "§e§lInformation" - -# Nachrichten -messages: - prefix: "§8[§6SimpleEco§8] §7" - noPermission: "§cDu hast keine Berechtigung für diesen Befehl!" - playerNotFound: "§cSpieler nicht gefunden!" - invalidAmount: "§cUngültiger Betrag!" - insufficientFunds: "§cNicht genügend Guthaben!" - paymentSent: "§aDu hast §e{amount} {currency} §aan §e{player} §aüberwiesen!" - paymentReceived: "§aDu hast §e{amount} {currency} §avon §e{player} §aerhalten!" - balanceYour: "§aDein Kontostand: §e{balance} {currency}" - balanceOther: "§aKontostand von §e{player}§a: §e{balance} {currency}" - tradeSuccess: "§aHandel erfolgreich abgeschlossen!" - tradeFailed: "§cHandel fehlgeschlagen!" - insufficientItems: "§cNicht genügend Items im Inventar!" - inventoryFull: "§cDein Inventar ist voll!" - villagerSpawned: "§aShop-Villager erfolgreich gespawnt!" - lookingAtNoBlock: "§cDu musst auf einen Block schauen!" - villagerSpawnFailed: "§cFehler beim Spawnen des Villagers!" diff --git a/target/classes/de/simpleeco/SimpleEcoPlugin.class b/target/classes/de/simpleeco/SimpleEcoPlugin.class deleted file mode 100644 index 8a591b7..0000000 Binary files a/target/classes/de/simpleeco/SimpleEcoPlugin.class and /dev/null differ diff --git a/target/classes/de/simpleeco/bank/AtmTrader$AtmSession$MenuType.class b/target/classes/de/simpleeco/bank/AtmTrader$AtmSession$MenuType.class deleted file mode 100644 index f2c9d45..0000000 Binary files a/target/classes/de/simpleeco/bank/AtmTrader$AtmSession$MenuType.class and /dev/null differ diff --git a/target/classes/de/simpleeco/bank/AtmTrader$AtmSession.class b/target/classes/de/simpleeco/bank/AtmTrader$AtmSession.class deleted file mode 100644 index e73102b..0000000 Binary files a/target/classes/de/simpleeco/bank/AtmTrader$AtmSession.class and /dev/null differ diff --git a/target/classes/de/simpleeco/bank/AtmTrader.class b/target/classes/de/simpleeco/bank/AtmTrader.class deleted file mode 100644 index 317149a..0000000 Binary files a/target/classes/de/simpleeco/bank/AtmTrader.class and /dev/null differ diff --git a/target/classes/de/simpleeco/bank/AtmVillagerManager.class b/target/classes/de/simpleeco/bank/AtmVillagerManager.class deleted file mode 100644 index 211a5a6..0000000 Binary files a/target/classes/de/simpleeco/bank/AtmVillagerManager.class and /dev/null differ diff --git a/target/classes/de/simpleeco/bank/BankManager.class b/target/classes/de/simpleeco/bank/BankManager.class deleted file mode 100644 index 9a51e38..0000000 Binary files a/target/classes/de/simpleeco/bank/BankManager.class and /dev/null differ diff --git a/target/classes/de/simpleeco/commands/EcoCommand.class b/target/classes/de/simpleeco/commands/EcoCommand.class deleted file mode 100644 index 5a08685..0000000 Binary files a/target/classes/de/simpleeco/commands/EcoCommand.class and /dev/null differ diff --git a/target/classes/de/simpleeco/commands/SpawnCommand.class b/target/classes/de/simpleeco/commands/SpawnCommand.class deleted file mode 100644 index df4226c..0000000 Binary files a/target/classes/de/simpleeco/commands/SpawnCommand.class and /dev/null differ diff --git a/target/classes/de/simpleeco/config/ConfigManager$ItemPriceConfig.class b/target/classes/de/simpleeco/config/ConfigManager$ItemPriceConfig.class deleted file mode 100644 index 2437d3e..0000000 Binary files a/target/classes/de/simpleeco/config/ConfigManager$ItemPriceConfig.class and /dev/null differ diff --git a/target/classes/de/simpleeco/config/ConfigManager.class b/target/classes/de/simpleeco/config/ConfigManager.class deleted file mode 100644 index a82f02f..0000000 Binary files a/target/classes/de/simpleeco/config/ConfigManager.class and /dev/null differ diff --git a/target/classes/de/simpleeco/currency/BasicCurrency.class b/target/classes/de/simpleeco/currency/BasicCurrency.class deleted file mode 100644 index 57a248e..0000000 Binary files a/target/classes/de/simpleeco/currency/BasicCurrency.class and /dev/null differ diff --git a/target/classes/de/simpleeco/database/DatabaseManager$ItemStats.class b/target/classes/de/simpleeco/database/DatabaseManager$ItemStats.class deleted file mode 100644 index 80d02fd..0000000 Binary files a/target/classes/de/simpleeco/database/DatabaseManager$ItemStats.class and /dev/null differ diff --git a/target/classes/de/simpleeco/database/DatabaseManager.class b/target/classes/de/simpleeco/database/DatabaseManager.class deleted file mode 100644 index 91585d6..0000000 Binary files a/target/classes/de/simpleeco/database/DatabaseManager.class and /dev/null differ diff --git a/target/classes/de/simpleeco/listeners/PlayerJoinListener.class b/target/classes/de/simpleeco/listeners/PlayerJoinListener.class deleted file mode 100644 index 4b970f5..0000000 Binary files a/target/classes/de/simpleeco/listeners/PlayerJoinListener.class and /dev/null differ diff --git a/target/classes/de/simpleeco/listeners/VillagerInteractListener.class b/target/classes/de/simpleeco/listeners/VillagerInteractListener.class deleted file mode 100644 index cebae4c..0000000 Binary files a/target/classes/de/simpleeco/listeners/VillagerInteractListener.class and /dev/null differ diff --git a/target/classes/de/simpleeco/pricing/PriceManager$PriceInfo.class b/target/classes/de/simpleeco/pricing/PriceManager$PriceInfo.class deleted file mode 100644 index bbb4159..0000000 Binary files a/target/classes/de/simpleeco/pricing/PriceManager$PriceInfo.class and /dev/null differ diff --git a/target/classes/de/simpleeco/pricing/PriceManager.class b/target/classes/de/simpleeco/pricing/PriceManager.class deleted file mode 100644 index 435b771..0000000 Binary files a/target/classes/de/simpleeco/pricing/PriceManager.class and /dev/null differ diff --git a/target/classes/de/simpleeco/scoreboard/ScoreboardManager$1.class b/target/classes/de/simpleeco/scoreboard/ScoreboardManager$1.class deleted file mode 100644 index e9e071d..0000000 Binary files a/target/classes/de/simpleeco/scoreboard/ScoreboardManager$1.class and /dev/null differ diff --git a/target/classes/de/simpleeco/scoreboard/ScoreboardManager.class b/target/classes/de/simpleeco/scoreboard/ScoreboardManager.class deleted file mode 100644 index 73ea8c2..0000000 Binary files a/target/classes/de/simpleeco/scoreboard/ScoreboardManager.class and /dev/null differ diff --git a/target/classes/de/simpleeco/tasks/PriceRegressionTask.class b/target/classes/de/simpleeco/tasks/PriceRegressionTask.class deleted file mode 100644 index c182dee..0000000 Binary files a/target/classes/de/simpleeco/tasks/PriceRegressionTask.class and /dev/null differ diff --git a/target/classes/de/simpleeco/trading/CustomVillagerTrader$1.class b/target/classes/de/simpleeco/trading/CustomVillagerTrader$1.class deleted file mode 100644 index 3e692a0..0000000 Binary files a/target/classes/de/simpleeco/trading/CustomVillagerTrader$1.class and /dev/null differ diff --git a/target/classes/de/simpleeco/trading/CustomVillagerTrader$TradingSession.class b/target/classes/de/simpleeco/trading/CustomVillagerTrader$TradingSession.class deleted file mode 100644 index 0d3f0bd..0000000 Binary files a/target/classes/de/simpleeco/trading/CustomVillagerTrader$TradingSession.class and /dev/null differ diff --git a/target/classes/de/simpleeco/trading/CustomVillagerTrader.class b/target/classes/de/simpleeco/trading/CustomVillagerTrader.class deleted file mode 100644 index 7c3702f..0000000 Binary files a/target/classes/de/simpleeco/trading/CustomVillagerTrader.class and /dev/null differ diff --git a/target/classes/de/simpleeco/utils/EconomyUtils$1.class b/target/classes/de/simpleeco/utils/EconomyUtils$1.class deleted file mode 100644 index 285d4cc..0000000 Binary files a/target/classes/de/simpleeco/utils/EconomyUtils$1.class and /dev/null differ diff --git a/target/classes/de/simpleeco/utils/EconomyUtils.class b/target/classes/de/simpleeco/utils/EconomyUtils.class deleted file mode 100644 index adb2343..0000000 Binary files a/target/classes/de/simpleeco/utils/EconomyUtils.class and /dev/null differ diff --git a/target/classes/de/simpleeco/villager/ShopVillagerManager.class b/target/classes/de/simpleeco/villager/ShopVillagerManager.class deleted file mode 100644 index 47bd725..0000000 Binary files a/target/classes/de/simpleeco/villager/ShopVillagerManager.class and /dev/null differ diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml deleted file mode 100644 index ea4a301..0000000 --- a/target/classes/plugin.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: SimpleEco -version: 1.0.0 -main: de.simpleeco.SimpleEcoPlugin -api-version: 1.20 -description: Dynamisches Wirtschaftssystem Plugin mit Villager-Trading -author: SimpleEco Team -website: https://github.com/simpleeco - -commands: - eco: - description: Hauptkommando für das Wirtschaftssystem - usage: /eco [args...] - permission: simpleeco.use - spawn: - description: Spawnt spezielle Entities für das Plugin - usage: /spawn - permission: simpleeco.spawn - -permissions: - simpleeco.use: - description: Erlaubt die Nutzung der Eco-Commands - default: true - simpleeco.admin: - description: Erlaubt Admin-Funktionen - default: op - simpleeco.balance.other: - description: Erlaubt das Einsehen fremder Kontostände - default: op - simpleeco.spawn: - description: Erlaubt das Spawnen von Shop-Entities - default: op - simpleeco.spawn.villager: - description: Erlaubt das Spawnen von Shop-Villagern - default: op