From e88a538b1423623beee2c5e35f62f203a6d4f79a Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Sun, 16 Feb 2025 12:08:59 +0100 Subject: [PATCH 01/11] Increase Plugin Version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0f292d5f5c8a56854f708ab6f7f9be6cda66b7f9 Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Sun, 16 Feb 2025 12:09:28 +0100 Subject: [PATCH 02/11] Increase Plugin Version and added the new command to config the jumping puzzle --- src/main/resources/plugin.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index dedf5d1..a880179 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] @@ -26,6 +26,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 +43,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 From f600caa1050a27ba3c4675dec3d0b05800b1e0c4 Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Sun, 16 Feb 2025 12:09:44 +0100 Subject: [PATCH 03/11] Implemented Jumping Puzzle config logic --- .../killquest/JumpPuzzleGenerator.java | 27 ++++++++++++++++--- .../digitalwm/killquest/KillQuestPlugin.java | 26 ++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) 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..3c808b4 100644 --- a/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java +++ b/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java @@ -692,6 +692,32 @@ 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; } From 6b28a4182f1ef8c46842749f243e852a9eba77c1 Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 08:32:34 +0100 Subject: [PATCH 04/11] Added translation keys for Quest Title windowss --- src/main/resources/translations.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/resources/translations.yml b/src/main/resources/translations.yml index afe630e..f13a6c7 100644 --- a/src/main/resources/translations.yml +++ b/src/main/resources/translations.yml @@ -41,6 +41,9 @@ 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" de_DE: entity.minecraft.zombie: "Zombie" entity.minecraft.skeleton: "Skelett" @@ -82,4 +85,7 @@ 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" \ No newline at end of file From 0f94cc67bb4bc6f8143e3e998d3f494ace38e5da Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 08:33:49 +0100 Subject: [PATCH 05/11] Added translation keys for Quest Title Description --- src/main/resources/translations.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/translations.yml b/src/main/resources/translations.yml index f13a6c7..b71c7cd 100644 --- a/src/main/resources/translations.yml +++ b/src/main/resources/translations.yml @@ -44,6 +44,7 @@ translations: menu.questselector: "Quest Selection" menu.questdetails: "Quest Details" menu.queststatus: "Active Quest" + menu.questselecto.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" @@ -88,4 +89,5 @@ translations: item.minecraft.fishing_rod: "Angel" menu.questselector: "Questauswahl" menu.questdetails: "Questdetails" - menu.queststatus: "Aktive Quest" \ No newline at end of file + menu.queststatus: "Aktive Quest" + menu.questselecto.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 From 656fcabfcdbb9ca30bb367b14e0ef4e3e3be93b3 Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 08:34:39 +0100 Subject: [PATCH 06/11] fixed a typo --- src/main/resources/translations.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/translations.yml b/src/main/resources/translations.yml index b71c7cd..808d2d4 100644 --- a/src/main/resources/translations.yml +++ b/src/main/resources/translations.yml @@ -44,7 +44,7 @@ translations: menu.questselector: "Quest Selection" menu.questdetails: "Quest Details" menu.queststatus: "Active Quest" - menu.questselecto.description: "Select one quest from the list below. Only one active quest is allowed at a time." + 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" @@ -90,4 +90,4 @@ translations: menu.questselector: "Questauswahl" menu.questdetails: "Questdetails" menu.queststatus: "Aktive Quest" - menu.questselecto.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 + 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 From 0382719327bf357358b484128a3aaaf3efd6f9fa Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 08:36:54 +0100 Subject: [PATCH 07/11] Added command for showing active quest --- src/main/resources/plugin.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a880179..2ce001e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -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 " From c0d8733e426a5add341095232e9f63baa15f1f9e Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 08:37:40 +0100 Subject: [PATCH 08/11] Added logic to show active quest and cancel it. Changed logic to display the quests titles first and on selection the details are shown where user can accept or reject the quest --- .../digitalwm/killquest/KillQuestPlugin.java | 112 +++++++++++++----- 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java b/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java index 3c808b4..ab92548 100644 --- a/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java +++ b/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java @@ -62,6 +62,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"; @@ -587,14 +588,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) { @@ -733,46 +750,85 @@ 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(); - // Check if the player already has an active quest - boolean hasActiveQuest = activeQuests.containsKey(player.getName()); + if (clickedButtonId == 0) { // Accept button + // Check if the player already has an active quest + boolean hasActiveQuest = activeQuests.containsKey(player.getName()); - // Update active quest - activeQuests.put(player.getName(), new ActiveQuest(selectedQuest, new QuestProgress())); - 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 + // 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); + } + + selectedQuests.remove(player.getName()); // Remove the selected quest from the map } - createScoreboard(player); // Create the updated one - } + // 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 + } + } + } + } /** * Event handler for inventory pickup events. From 340bc32015289529d81b8fee49638692bdcc1ece Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 09:46:13 +0100 Subject: [PATCH 09/11] Added example of Distance, Height and Depth Quests --- src/main/resources/quests.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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: From 36e3fb88a3b86a50a16a458a3dc6343dadf73748 Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 09:46:44 +0100 Subject: [PATCH 10/11] Added logic for tracking Distance, Height or Depth distance from the quest starting point --- .../com/digitalwm/killquest/ActiveQuest.java | 6 +- .../digitalwm/killquest/KillQuestPlugin.java | 90 ++++++++++++++++++- .../java/com/digitalwm/killquest/Quest.java | 11 ++- .../digitalwm/killquest/QuestProgress.java | 9 ++ .../killquest/QuestScoreboardUpdater.java | 50 ++++++++++- 5 files changed, 158 insertions(+), 8 deletions(-) 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/KillQuestPlugin.java b/src/main/java/com/digitalwm/killquest/KillQuestPlugin.java index ab92548..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; @@ -332,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); @@ -387,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); } } @@ -427,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); } } } @@ -445,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); @@ -487,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. @@ -508,6 +562,7 @@ private void updateQuestProgressForPlayer(Player player) { activeQuests.remove(playerName); clearActiveQuestProgress(playerName); + destroyScoreboard(player); } else { saveActiveQuestProgress(playerName); } @@ -570,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; } @@ -738,7 +808,17 @@ public boolean onCommand(CommandSender sender, Command command, String label, St 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. @@ -791,7 +871,8 @@ else if (form.getTitle().equals(translate(player, "menu.questdetails"))) { boolean hasActiveQuest = activeQuests.containsKey(player.getName()); // Update active quest - activeQuests.put(player.getName(), new ActiveQuest(selectedQuest, new QuestProgress())); + 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()); @@ -917,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 +} From 433b069ce96eff362e42fe4d2f4462e0a759c71e Mon Sep 17 00:00:00 2001 From: Dan Harabagiu Date: Mon, 17 Feb 2025 09:58:28 +0100 Subject: [PATCH 11/11] Updated Readme, with the changes in the version 1.0.3 --- README.md | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) 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**