diff --git a/README.md b/README.md index b832ebe..7a7a440 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,24 @@ -## **KillQuest - A Nukkit Quest System** v1.0.2 +## **KillQuest - A Nukkit Quest System** v1.0.3 **KillQuest** is a **feature-rich quest system** for **Nukkit** servers, allowing players to complete **kill and gather quests** for rewards, and challenge themselves with procedurally generated jumping puzzles. It includes **multilingual support**, **scoreboard tracking**, and **EconomyAPI integration**. --- +## Changes in 1.0.3 +- Added quest tracking for distance traveled (in height, depth or any direction) + - see example quests.yml +- Added better Forms handling of quest selection +- Added /queststatus command to show the detailed view of the active quest +- Added the ability to cancel the active quest in the /queststatus command +- Added the configuration possibility for generated kumpign puzzles + - /jumpconfig + - resetOnCompletion - Boolean - To regenerate the puzzle when a player finishes the puzzle + - greenBlockResetTimeout - Int - How many seconds a player have to sit on the green block to triger jumping puzzle regeneration +- Added translation keys for Quest Menus + - menu.questselector + - menu.questdetails + - menu.queststatus + - menu.questselector.description + ## Changes in 1.0.2 - Added regenaration of puzzle when a user finishes it - Added regeneration of puzzle, using a reset block. You must stay at least 5 seconds on it @@ -37,8 +53,9 @@ --- ## **πŸ“œ Features** -- **Quest System:** Kill entities and gather items to complete quests. -- **Dynamic Quest Selection:** Players can select quests using a UI (`/quests`). +- **Quest System:** Kill entities, gather items or travel distances to complete quests. +- **Dynamic Quest Selection:** Players can select quests using a UI (`/quests`). +- **Active Quest Status:** Players can view active quests using a UI (`/questsstatus`). - **Scoreboard Tracking:** Displays active quest progress in the top-right. - **Auto-Saving:** Quest progress is saved to files per player. - **EconomyAPI Integration:** Players earn credits upon completion. @@ -112,9 +129,25 @@ quests: diamond: 20 iron_ore: 50 reward: 200 + + - name: "Long Journey" + description: "Travel over 500 blocks from your starting position" + distance: 500 + reward: 50 + + - name: "High Climb" + description: "Climb 200 blocks in height" + height: 200 + reward: 30 + + - name: "Deep Dive" + description: "Descend 100 blocks in depth" + depth: 100 + reward: 40 ``` - **Kill Targets:** Define entities to kill. - **Gather Items:** Define items to collect. +- **Distance Traveled:** Define distance to travel (in range, height or depth). - **Reward:** Credits given via EconomyAPI. ### **`translations.yml`** @@ -137,10 +170,12 @@ translations: | Command | Description | |---------|------------| | `/quests` | Opens the quest selection UI | +| `/questsstatus` | Opens the active quest status UI | | `/killquest reload` | Reloads quests and translations | | `/jumpgen ` | Generates a jumping puzzle with a unique name. | | `/clearpuzzle ` | Clears a specific puzzle. | | `/listpuzzles` | Lists all active puzzles. | +| `/jumpconfig ` | Adjust values of generated jumping puzzle | --- @@ -155,7 +190,7 @@ translations: [INFO ] [KillQuestPlugin] β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ [INFO ] [KillQuestPlugin] β•šβ•β• β•šβ•β•β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•šβ•β•β–€β–€β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•šβ•β• [INFO ] [KillQuestPlugin] - [INFO ] [KillQuestPlugin] Version: 1.0.2 + [INFO ] [KillQuestPlugin] Version: 1.0.3 [INFO ] [KillQuestPlugin] Developed by digitalwm [INFO ] [KillQuestPlugin] [INFO ] [KillQuestPlugin] Loaded language: en_US with 41 keys. @@ -172,7 +207,6 @@ translations: ## **πŸ’‘ Future Improvements** - βœ… **More Quest Types:** Crafting, Shooting -- βœ… **Permissions Support** - βœ… **More Customization Options** - βœ… **SQL Database support** diff --git a/pom.xml b/pom.xml index 53d0503..7c50b62 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.digitalwm.killquestplugin KillQuestPlugin - 1.0.2 + 1.0.3 package diff --git a/src/main/java/com/digitalwm/killquest/ActiveQuest.java b/src/main/java/com/digitalwm/killquest/ActiveQuest.java index 37d13f6..c0c3f27 100644 --- a/src/main/java/com/digitalwm/killquest/ActiveQuest.java +++ b/src/main/java/com/digitalwm/killquest/ActiveQuest.java @@ -5,18 +5,22 @@ import java.util.Map; import java.util.HashMap; import java.util.ArrayList; +import cn.nukkit.math.Vector3; public class ActiveQuest { private Quest quest; private QuestProgress progress; + private Vector3 startPos; - public ActiveQuest(Quest quest, QuestProgress progress) { + public ActiveQuest(Quest quest, QuestProgress progress, Vector3 startPos) { this.quest = quest; this.progress = progress; + this.startPos = startPos; } public Quest getQuest() { return quest; } public QuestProgress getProgress() { return progress; } public void setQuest(Quest quest) { this.quest = quest; } public void setProgress(QuestProgress progress) { this.progress = progress; } + public Vector3 getStartPos() { return startPos; } } \ No newline at end of file diff --git a/src/main/java/com/digitalwm/killquest/JumpPuzzleGenerator.java b/src/main/java/com/digitalwm/killquest/JumpPuzzleGenerator.java index 316ac3b..2d83414 100644 --- a/src/main/java/com/digitalwm/killquest/JumpPuzzleGenerator.java +++ b/src/main/java/com/digitalwm/killquest/JumpPuzzleGenerator.java @@ -36,6 +36,9 @@ public class JumpPuzzleGenerator { private final String puzzleName; // βœ… Declare puzzle name + private boolean resetOnCompletion = true; + private int greenBlockResetTimeout = 5; // Default to 5 seconds + public JumpPuzzleGenerator(KillQuestPlugin plugin, Level level, Vector3 startPos, String puzzleName, int length, int width, int maxHeight) { this.level = level; this.startPos = startPos; @@ -51,12 +54,27 @@ public void setPuzzleBoundaries(Vector3 startPos, int width, int length, int max maxHeight++; puzzleMin = new Vector3(startPos.x - width / 2, startPos.y, startPos.z - length / 2); puzzleMax = new Vector3(startPos.x + width / 2, startPos.y + maxHeight, startPos.z + length / 2); - plugin.getLogger().info("Puzzle boundaries set: " + puzzleMin + " to " + puzzleMax); } public String getPuzzleName() { return puzzleName; } + + public boolean isResetOnCompletion() { + return resetOnCompletion; + } + + public void setResetOnCompletion(boolean resetOnCompletion) { + this.resetOnCompletion = resetOnCompletion; + } + + public int getGreenBlockResetTimeout() { + return greenBlockResetTimeout; + } + + public void setGreenBlockResetTimeout(int greenBlockResetTimeout) { + this.greenBlockResetTimeout = greenBlockResetTimeout; + } public void generate() { clearArea(); @@ -326,7 +344,7 @@ public void handlePlayerMovement(PlayerMoveEvent event) { playerGreenBlockTimes.put(player, currentTime); } else { long timeOnGreenBlock = currentTime - playerGreenBlockTimes.get(player); - if (timeOnGreenBlock > 5000) { // 5 seconds + if (timeOnGreenBlock > greenBlockResetTimeout * 1000) { // 5 seconds player.sendMessage("Β§cYou stayed on the green block for too long! The puzzle has been reset."); resetPuzzleForPlayer(player); } @@ -349,7 +367,10 @@ public void handlePlayerMovement(PlayerMoveEvent event) { EconomyAPI.getInstance().addMoney(player, 100); plugin.onJumpPuzzleEnd(player, puzzleName); // Trigger end event playerStartTimes.remove(player); - resetPuzzleForPlayer(player); + + if (resetOnCompletion) { + resetPuzzleForPlayer(player); + } } } } diff --git a/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java b/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java index 94ca9d2..3653dd1 100644 --- a/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java +++ b/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java @@ -15,6 +15,7 @@ import cn.nukkit.event.entity.EntityDamageByEntityEvent; import cn.nukkit.event.inventory.InventoryPickupItemEvent; import cn.nukkit.event.player.PlayerFormRespondedEvent; +import cn.nukkit.event.player.PlayerMoveEvent; import cn.nukkit.block.Block; import cn.nukkit.level.Level; @@ -62,6 +63,7 @@ public class KillQuestPlugin extends PluginBase implements Listener, CommandExec // Mapping of player's name to their active quest (only one active quest at a time) public Map activeQuests = new HashMap<>(); + private final Map selectedQuests = new HashMap<>(); public Map scoreboards = new HashMap<>(); public static final String SCOREBOARD_TITLE = "Β§6Active Quest"; @@ -331,6 +333,24 @@ public void createScoreboard(Player player) { scoreboard.setScore(" " + current + "/" + required, line++); } + // Add distance progress + if (quest.getDistance() > 0) { + int distanceTraveled = progress.getDistanceTraveled(); + scoreboard.setScore("Β§bDi: " + distanceTraveled + "/" + quest.getDistance(), line++); + } + + // Add height progress + if (quest.getHeight() > 0) { + int heightClimbed = progress.getHeightClimbed(); + scoreboard.setScore("Β§bH: " + heightClimbed + "/" + quest.getHeight(), line++); + } + + // Add depth progress + if (quest.getDepth() > 0) { + int depthDescended = progress.getDepthDescended(); + scoreboard.setScore("Β§bDe: " + depthDescended + "/" + quest.getDepth(), line++); + } + // Store and show the scoreboard scoreboards.put(player, scoreboard); scoreboard.showTo(player); @@ -386,7 +406,11 @@ private void loadQuests() { } } } - Quest quest = new Quest(name, description, killTargets, gatherItems, reward); + int distance = questMap.containsKey("distance") ? Integer.parseInt(questMap.get("distance").toString()) : 0; + int height = questMap.containsKey("height") ? Integer.parseInt(questMap.get("height").toString()) : 0; + int depth = questMap.containsKey("depth") ? Integer.parseInt(questMap.get("depth").toString()) : 0; + + Quest quest = new Quest(name, description, killTargets, gatherItems, reward, distance, height, depth); questList.add(quest); } } @@ -426,7 +450,23 @@ private ActiveQuest loadActiveQuestProgress(String playerName) { } } } - return new ActiveQuest(quest, progress); + if (activeData.containsKey("distanceTraveled")) { + progress.setDistanceTraveled(Integer.parseInt(activeData.get("distanceTraveled").toString())); + } + if (activeData.containsKey("heightClimbed")) { + progress.setHeightClimbed(Integer.parseInt(activeData.get("heightClimbed").toString())); + } + if (activeData.containsKey("depthDescended")) { + progress.setDepthDescended(Integer.parseInt(activeData.get("depthDescended").toString())); + } + String startPosString = (String) activeData.get("startPos"); + String[] components = startPosString.substring(1, startPosString.length() - 1).split(","); + Vector3 startPos = new Vector3( + Double.parseDouble(components[0]), + Double.parseDouble(components[1]), + Double.parseDouble(components[2]) + ); // Load the starting position + return new ActiveQuest(quest, progress, startPos); } } } @@ -444,6 +484,10 @@ private void saveActiveQuestProgress(String playerName) { activeData.put("questName", aq.getQuest().getName()); activeData.put("kills", aq.getProgress().getKills()); activeData.put("gather", aq.getProgress().getGather()); + activeData.put("distanceTraveled", aq.getProgress().getDistanceTraveled()); + activeData.put("heightClimbed", aq.getProgress().getHeightClimbed()); + activeData.put("depthDescended", aq.getProgress().getDepthDescended()); + activeData.put("startPos", aq.getStartPos().toString()); // Save the starting position File file = new File(playersFolder, playerName + ".yml"); Config config = new Config(file, Config.YAML); @@ -486,6 +530,17 @@ private void updateQuestProgressForPlayer(Player player) { "': " + inventoryCount + "/" + entry.getValue()); } + // Update distance progress + Vector3 startPos = aq.getStartPos(); + Vector3 currentPos = player.getPosition().floor(); + int distanceTraveled = (int) startPos.distance(currentPos); + int heightClimbed = (int) (currentPos.getY() - startPos.getY()); + int depthDescended = (int) (startPos.getY() - currentPos.getY()); + + progress.setDistanceTraveled(distanceTraveled); + progress.setHeightClimbed(heightClimbed); + progress.setDepthDescended(depthDescended); + // Check for quest completion. if (checkQuestCompletion(quest, progress)) { // Remove required items from inventory. @@ -507,6 +562,7 @@ private void updateQuestProgressForPlayer(Player player) { activeQuests.remove(playerName); clearActiveQuestProgress(playerName); + destroyScoreboard(player); } else { saveActiveQuestProgress(playerName); } @@ -569,6 +625,21 @@ private boolean checkQuestCompletion(Quest quest, QuestProgress progress) { return false; } } + + // Check distance traveled + if (quest.getDistance() > 0 && progress.getDistanceTraveled() < quest.getDistance()) { + return false; + } + + // Check height climbed + if (quest.getHeight() > 0 && progress.getHeightClimbed() < quest.getHeight()) { + return false; + } + + // Check depth descended + if (quest.getDepth() > 0 && progress.getDepthDescended() < quest.getDepth()) { + return false; + } return true; } @@ -587,14 +658,30 @@ public boolean onCommand(CommandSender sender, Command command, String label, St // Handle Quest Selection if (command.getName().equalsIgnoreCase("quests")) { List quests = getRandomQuestsForPlayer(player.getName()); - FormWindowSimple form = new FormWindowSimple("Quest Selector", "Select one quest from the list below. Only one active quest is allowed at a time."); + FormWindowSimple form = new FormWindowSimple(translate(player, "menu.questselector"), translate(player, "menu.questselector.description")); for (Quest quest : quests) { - form.addButton(new ElementButton(quest.getName() + "\n" + quest.getDescription())); + form.addButton(new ElementButton(quest.getName())); } player.showFormWindow(form); return true; } + // Handle Quest Status Command + if (command.getName().equalsIgnoreCase("queststatus")) { + ActiveQuest activeQuest = activeQuests.get(player.getName()); + if (activeQuest == null) { + player.sendMessage("Β§cYou do not have an active quest."); + return true; + } + + Quest quest = activeQuest.getQuest(); + FormWindowSimple form = new FormWindowSimple(translate(player, "menu.queststatus"), quest.getDescription()); + form.addButton(new ElementButton("OK")); + form.addButton(new ElementButton("Cancel Quest")); + player.showFormWindow(form); + return true; + } + // Handle Jump Puzzle Generation if (command.getName().equalsIgnoreCase("jumpgen")) { if (args.length < 4) { @@ -692,10 +779,46 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } + // Handle Puzzle Configuration + if (command.getName().equalsIgnoreCase("jumpconfig")) { + if (args.length < 2) { + sender.sendMessage("Β§cUsage: /jumpconfig "); + return false; + } + + String puzzleName = args[0]; + JumpPuzzleGenerator puzzle = activeJumpPuzzles.get(puzzleName); + + if (puzzle == null) { + sender.sendMessage("Β§cNo puzzle found with that name."); + return true; + } + + boolean resetOnCompletion = Boolean.parseBoolean(args[1]); + int greenBlockResetTimeout = Integer.parseInt(args[2]); + + puzzle.setResetOnCompletion(resetOnCompletion); + puzzle.setGreenBlockResetTimeout(greenBlockResetTimeout); + saveJumpPuzzles(); // Save changes + + sender.sendMessage("Β§aJump puzzle '" + puzzleName + "' configuration updated!"); + return true; + } + return false; } - + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + String playerName = player.getName(); + ActiveQuest aq = activeQuests.get(playerName); + if (aq == null) { + return; + } else { + updateQuestProgressForPlayer(player); + } + } /** * Handles the player's form response for quest selection. @@ -707,47 +830,87 @@ public void onPlayerFormResponse(PlayerFormRespondedEvent event) { } FormWindowSimple form = (FormWindowSimple) event.getWindow(); - if (!form.getTitle().equals("Quest Selector")) { - return; - } - FormResponseSimple response = (FormResponseSimple) event.getResponse(); if (response == null) { return; } - List quests = playerQuestSelections.get(event.getPlayer().getName()); - if (quests == null) { - return; - } + Player player = event.getPlayer(); - int selected = response.getClickedButtonId(); - if (selected < 0 || selected >= quests.size()) { - return; + // Handle Quest Selector Form + if (form.getTitle().equals(translate(player, "menu.questselector"))) { + List quests = playerQuestSelections.get(player.getName()); + if (quests == null) { + return; + } + + int selected = response.getClickedButtonId(); + if (selected < 0 || selected >= quests.size()) { + return; + } + + Quest selectedQuest = quests.get(selected); + selectedQuests.put(player.getName(), selectedQuest); // Store the selected quest + + FormWindowSimple questDetailsForm = new FormWindowSimple(translate(player, "menu.questdetails"), selectedQuest.getName() + "\n" + selectedQuest.getDescription()); + questDetailsForm.addButton(new ElementButton("Accept")); + questDetailsForm.addButton(new ElementButton("Cancel")); + player.showFormWindow(questDetailsForm); } + // Handle Quest Details Form + else if (form.getTitle().equals(translate(player, "menu.questdetails"))) { + Quest selectedQuest = selectedQuests.get(player.getName()); + if (selectedQuest == null) { + return; + } - Player player = event.getPlayer(); - Quest selectedQuest = quests.get(selected); + int clickedButtonId = response.getClickedButtonId(); + + if (clickedButtonId == 0) { // Accept button + // Check if the player already has an active quest + boolean hasActiveQuest = activeQuests.containsKey(player.getName()); - // Check if the player already has an active quest - boolean hasActiveQuest = activeQuests.containsKey(player.getName()); + // Update active quest + Vector3 startPos = player.getPosition().floor(); // Store the starting position + activeQuests.put(player.getName(), new ActiveQuest(selectedQuest, new QuestProgress(), startPos)); + saveActiveQuestProgress(player.getName()); + player.sendMessage("Β§aActive quest set to: " + selectedQuest.getName()); - // Update active quest - activeQuests.put(player.getName(), new ActiveQuest(selectedQuest, new QuestProgress())); - saveActiveQuestProgress(player.getName()); - player.sendMessage("Β§aActive quest set to: " + selectedQuest.getName()); + // Trigger Quest Start Event + onQuestStart(player, selectedQuest.getName()); - // Trigger Quest Start Event - onQuestStart(player, selectedQuest.getName()); + // Update the Scoreboard Immediately + if (hasActiveQuest) { + destroyScoreboard(player); // Remove old scoreboard + } + createScoreboard(player); // Create the updated one + } else if (clickedButtonId == 1) { // Cancel button + // Show the quest selector form again + List quests = getRandomQuestsForPlayer(player.getName()); + FormWindowSimple questListForm = new FormWindowSimple(translate(player, "menu.questselector"), "Select a quest from the list below."); + for (Quest quest : quests) { + questListForm.addButton(new ElementButton(quest.getName())); + } + player.showFormWindow(questListForm); + } - // βœ… **Update the Scoreboard Immediately** - if (hasActiveQuest) { - destroyScoreboard(player); // Remove old scoreboard + selectedQuests.remove(player.getName()); // Remove the selected quest from the map + } + // Handle Quest Status Form + else if (form.getTitle().equals(translate(player, "menu.queststatus"))) { + int clickedButtonId = response.getClickedButtonId(); + + if (clickedButtonId == 1) { // Cancel Quest button + ActiveQuest activeQuest = activeQuests.remove(player.getName()); + if (activeQuest != null) { + clearActiveQuestProgress(player.getName()); + player.sendMessage("Β§cQuest cancelled: " + activeQuest.getQuest().getName()); + destroyScoreboard(player); // Remove the scoreboard + } + } } - createScoreboard(player); // Create the updated one } - /** * Event handler for inventory pickup events. * Schedules a 1-tick delayed task to update the active quest progress based on the player's current inventory. @@ -835,6 +998,7 @@ public void onEntityDeath(EntityDeathEvent event) { getLogger().info("Active quest '" + quest.getName() + "' completed for player " + playerName); activeQuests.remove(playerName); clearActiveQuestProgress(playerName); + destroyScoreboard(player); } else { saveActiveQuestProgress(playerName); } diff --git a/src/main/java/com/digitalwm/killquest/Quest.java b/src/main/java/com/digitalwm/killquest/Quest.java index 34fe5ab..e3b7561 100644 --- a/src/main/java/com/digitalwm/killquest/Quest.java +++ b/src/main/java/com/digitalwm/killquest/Quest.java @@ -12,14 +12,20 @@ public class Quest { private Map killTargets; private Map gatherItems; private int reward; + private int distance; + private int height; + private int depth; public Quest(String name, String description, Map killTargets, - Map gatherItems, int reward) { + Map gatherItems, int reward, int distance, int height, int depth) { this.name = name; this.description = description; this.killTargets = killTargets; this.gatherItems = gatherItems; this.reward = reward; + this.distance = distance; + this.height = height; + this.depth = depth; } public String getName() { return name; } @@ -27,4 +33,7 @@ public Quest(String name, String description, Map killTargets, public Map getKillTargets() { return killTargets; } public Map getGatherItems() { return gatherItems; } public int getReward() { return reward; } + public int getDistance() { return distance; } + public int getHeight() { return height; } + public int getDepth() { return depth; } } \ No newline at end of file diff --git a/src/main/java/com/digitalwm/killquest/QuestProgress.java b/src/main/java/com/digitalwm/killquest/QuestProgress.java index 3d631a6..ca09688 100644 --- a/src/main/java/com/digitalwm/killquest/QuestProgress.java +++ b/src/main/java/com/digitalwm/killquest/QuestProgress.java @@ -10,9 +10,18 @@ public class QuestProgress { private Map kills = new HashMap<>(); private Map gather = new HashMap<>(); private boolean completed = false; + private int distanceTraveled; + private int heightClimbed; + private int depthDescended; public Map getKills() { return kills; } public Map getGather() { return gather; } public boolean isCompleted() { return completed; } public void setCompleted(boolean completed) { this.completed = completed; } + public int getDistanceTraveled() { return distanceTraveled; } + public void setDistanceTraveled(int distanceTraveled) { this.distanceTraveled = distanceTraveled; } + public int getHeightClimbed() { return heightClimbed; } + public void setHeightClimbed(int heightClimbed) { this.heightClimbed = heightClimbed; } + public int getDepthDescended() { return depthDescended; } + public void setDepthDescended(int depthDescended) { this.depthDescended = depthDescended; } } \ No newline at end of file diff --git a/src/main/java/com/digitalwm/killquest/QuestScoreboardUpdater.java b/src/main/java/com/digitalwm/killquest/QuestScoreboardUpdater.java index 03fffc0..5f3b92b 100644 --- a/src/main/java/com/digitalwm/killquest/QuestScoreboardUpdater.java +++ b/src/main/java/com/digitalwm/killquest/QuestScoreboardUpdater.java @@ -40,13 +40,26 @@ public void run() { Quest quest = aq.getQuest(); QuestProgress progress = aq.getProgress(); + // Check if there are any elements to track + boolean hasElementsToTrack = !quest.getKillTargets().isEmpty() || !quest.getGatherItems().isEmpty() || + quest.getDistance() > 0 || quest.getHeight() > 0 || quest.getDepth() > 0; + + if (!hasElementsToTrack) { + // Remove scoreboard if no elements to track + if (plugin.scoreboards.containsKey(player)) { + plugin.scoreboards.get(player).clear(); + plugin.scoreboards.remove(player); + } + continue; + } + Scoreboard scoreboard = plugin.scoreboards.getOrDefault(player, new Scoreboard("QuestTracker", SortOrder.ASCENDING, DisplaySlot.SIDEBAR)); scoreboard.holdUpdates(); boolean needsUpdate = false; List lines = new ArrayList<>(); - lines.add("Β§6U:" + quest.getName()); + lines.add("Β§6" + quest.getName()); lines.add("Β§9" + quest.getReward() + " credits"); // Add kill progress @@ -81,6 +94,39 @@ public void run() { } } + // Add distance progress + if (quest.getDistance() > 0) { + int distanceTraveled = progress.getDistanceTraveled(); + String line = "Β§bDi: " + distanceTraveled + "/" + quest.getDistance(); + lines.add(line); + + if (!scoreboard.getScores().containsKey(line)) { + needsUpdate = true; + } + } + + // Add height progress + if (quest.getHeight() > 0) { + int heightClimbed = progress.getHeightClimbed(); + String line = "Β§bH: " + heightClimbed + "/" + quest.getHeight(); + lines.add(line); + + if (!scoreboard.getScores().containsKey(line)) { + needsUpdate = true; + } + } + + // Add depth progress + if (quest.getDepth() > 0) { + int depthDescended = progress.getDepthDescended(); + String line = "Β§bDe: " + depthDescended + "/" + quest.getDepth(); + lines.add(line); + + if (!scoreboard.getScores().containsKey(line)) { + needsUpdate = true; + } + } + if (needsUpdate) { scoreboard.clear(); int lineNumber = 0; @@ -93,4 +139,4 @@ public void run() { plugin.scoreboards.put(player, scoreboard); } } -} \ No newline at end of file +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index dedf5d1..2ce001e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: KillQuestPlugin -version: 1.0.2 +version: 1.0.3 author: digitalwm main: com.digitalwm.killquest.KillQuestPlugin api: [1.1.0] @@ -7,6 +7,11 @@ commands: quests: description: "Opens the quest selection form." usage: "/quests" + aliases: [kq] + queststatus: + description: "Opens the quest status form." + usage: "/queststatus" + aliases: [kqs] jumpgen: description: "Generates a random jumping puzzle." usage: "/jumpgen " @@ -26,6 +31,10 @@ commands: description: "List all generated puzzles" usage: "/listpuzzle" permission: killquest:listpuzzle + jumpconfig: + description: "Change jumping puzzle values" + usage: "/jumpconfig " + permission: killquest:jumpconfig permissions: killquest.jumpgen: @@ -39,4 +48,7 @@ permissions: default: op killquest.listpuzzle: description: "List all generated puzzles." + default: op + killquest.jumpconfig: + description: "Change jumping puzzle values" default: op \ No newline at end of file diff --git a/src/main/resources/quests.yml b/src/main/resources/quests.yml index e56a17c..e7467bd 100644 --- a/src/main/resources/quests.yml +++ b/src/main/resources/quests.yml @@ -1,4 +1,19 @@ quests: + - name: "Long Journey" + description: "Travel over 500 blocks from your starting position" + distance: 500 + reward: 50 + + - name: "High Climb" + description: "Climb 200 blocks in height" + height: 200 + reward: 30 + + - name: "Deep Dive" + description: "Descend 100 blocks in depth" + depth: 100 + reward: 40 + - name: "Monster Slayer" description: "Kill 10 Zombies and 5 Skeletons" killTargets: diff --git a/src/main/resources/translations.yml b/src/main/resources/translations.yml index afe630e..808d2d4 100644 --- a/src/main/resources/translations.yml +++ b/src/main/resources/translations.yml @@ -41,6 +41,10 @@ translations: item.minecraft.glowstone: "Glowstone" item.minecraft.fish: "Fish" item.minecraft.fishing_rod: "Fishing Rod" + menu.questselector: "Quest Selection" + menu.questdetails: "Quest Details" + menu.queststatus: "Active Quest" + menu.questselector.description: "Select one quest from the list below. Only one active quest is allowed at a time." de_DE: entity.minecraft.zombie: "Zombie" entity.minecraft.skeleton: "Skelett" @@ -82,4 +86,8 @@ translations: item.minecraft.honeycomb: "Honigwabe" item.minecraft.glowstone: "Glowstone" item.minecraft.fish: "Fisch" - item.minecraft.fishing_rod: "Angel" \ No newline at end of file + item.minecraft.fishing_rod: "Angel" + menu.questselector: "Questauswahl" + menu.questdetails: "Questdetails" + menu.queststatus: "Aktive Quest" + menu.questselector.description: "WΓ€hlen Sie eine Quest aus der folgenden Liste aus. Es ist nur eine aktive Quest gleichzeitig erlaubt." \ No newline at end of file