diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/AbstractTraitHandler.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/AbstractTraitHandler.java index e5c22c4..c869b65 100644 --- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/AbstractTraitHandler.java +++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/AbstractTraitHandler.java @@ -4,6 +4,7 @@ import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.npc.NPC; import net.knarcraft.dynmapcitizens.DynmapCitizens; import net.knarcraft.dynmapcitizens.settings.TraitSettings; +import net.knarcraft.dynmapcitizens.util.DynmapHelper; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -13,7 +14,6 @@ import org.dynmap.markers.MarkerIcon; import org.dynmap.markers.MarkerSet; import java.util.UUID; -import java.util.logging.Level; /** * An abstract class with useful code for all citizens-trait-handlers @@ -34,7 +34,7 @@ public abstract class AbstractTraitHandler implements CitizensTraitHandler { protected void initializeMarkerSet() { TraitSettings settings = getSettings(); DynmapAPI dynmapAPI = DynmapCitizens.getInstance().getDynmapAPI(); - markerSet = getMarkerSet(dynmapAPI, settings.getMarkerSetId(), settings.getMarkerSetName()); + markerSet = DynmapHelper.getMarkerSet(dynmapAPI, settings.getMarkerSetId(), settings.getMarkerSetName()); if (markerSet != null) { markerSet.setHideByDefault(settings.markersHiddenByDefault()); markerSet.setLayerPriority(settings.getMarkerSetPriority()); @@ -42,28 +42,6 @@ public abstract class AbstractTraitHandler implements CitizensTraitHandler { } } - /** - * Gets the given marker set (and creates it if necessary) - * - * @param dynmapAPI

A reference to dynmap's API

- * @param setId

The id of the marker set to get

- * @param label

The label of the marker set if creation is necessary

- * @return

The marker set, or null if something went wrong

- */ - protected MarkerSet getMarkerSet(DynmapAPI dynmapAPI, String setId, String label) { - MarkerSet markerSet = dynmapAPI.getMarkerAPI().getMarkerSet(setId); - if (markerSet == null) { - markerSet = dynmapAPI.getMarkerAPI().createMarkerSet(setId, label, null, false); - if (markerSet == null) { - DynmapCitizens.getInstance().getLogger().log(Level.SEVERE, "Unable to get or create " + setId + - " marker set"); - DynmapCitizens.getInstance().onDisable(); - return null; - } - } - return markerSet; - } - /** * Adds a marker for an NPC * diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestAreaHandler.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestAreaHandler.java new file mode 100644 index 0000000..25cf000 --- /dev/null +++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestAreaHandler.java @@ -0,0 +1,114 @@ +package net.knarcraft.dynmapcitizens.handler.trait.quests; + +import me.blackvein.quests.QuestsAPI; +import me.blackvein.quests.quests.IQuest; +import me.blackvein.quests.quests.IStage; +import net.knarcraft.dynmapcitizens.settings.QuestsSettings; +import net.knarcraft.dynmapcitizens.util.DynmapHelper; +import net.knarcraft.dynmapcitizens.util.QuestsHelper; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.dynmap.DynmapAPI; +import org.dynmap.markers.GenericMarker; +import org.dynmap.markers.MarkerSet; + +import java.util.List; + +/** + * A handler class for quest areas + */ +public class QuestAreaHandler { + + private final QuestsAPI questsAPI; + private final MarkerSet killAreaMarkerSet; + private final MarkerSet reachAreaMarkerSet; + private final QuestsSettings settings; + private final List unavailableQuests; + + /** + * Instantiates a new quest area handler + * + * @param questsAPI

A reference to the quests API

+ * @param dynmapAPI

A reference to the dynmap API

+ * @param settings

The quests settings to use

+ * @param unavailableQuests

The list of currently unavailable quests to possibly skip

+ */ + public QuestAreaHandler(QuestsAPI questsAPI, DynmapAPI dynmapAPI, QuestsSettings settings, + List unavailableQuests) { + this.questsAPI = questsAPI; + this.settings = settings; + this.unavailableQuests = unavailableQuests; + killAreaMarkerSet = DynmapHelper.initializeMarkerSet(dynmapAPI, settings.getKillAreaSettings()); + reachAreaMarkerSet = DynmapHelper.initializeMarkerSet(dynmapAPI, settings.getReachAreaSettings()); + } + + /** + * Updates all quest area markers + */ + public void updateQuestAreas() { + this.killAreaMarkerSet.getCircleMarkers().forEach(GenericMarker::deleteMarker); + this.reachAreaMarkerSet.getCircleMarkers().forEach(GenericMarker::deleteMarker); + for (IQuest quest : questsAPI.getLoadedQuests()) { + //Skip marker if quest is unavailable, and unavailable quests are hidden + if (settings.hideUnavailableQuests() && this.unavailableQuests.contains(quest)) { + continue; + } + for (IStage stage : quest.getStages()) { + markKillLocations(quest, stage); + markReachLocations(quest, stage); + } + } + //TODO: Mark WorldGuard areas part of quests. Requires WorldGuard integration + } + + /** + * Marks any reach locations found in the given stage + * + * @param quest

The quest the stage belongs to

+ * @param stage

The stage to search for reach locations

+ */ + private void markReachLocations(IQuest quest, IStage stage) { + if (settings.getReachAreaSettings().isDisabled()) { + return; + } + for (int i = 0; i < stage.getLocationsToReach().size(); i++) { + Location location = stage.getLocationsToReach().get(i); + int radius = stage.getRadiiToReachWithin().get(i); + String areaName = stage.getLocationNames().get(i); + String description = ""; + if (areaName != null) { + description += "" + areaName + "
"; + } + description += "Target location for " + quest.getName(); + DynmapHelper.markLocation(location, radius, description, reachAreaMarkerSet, settings.getReachAreaSettings()); + } + } + + /** + * Marks any kill locations found in the given stage + * + * @param quest

The quest the stage belongs to

+ * @param stage

The stage to search for kill locations

+ */ + private void markKillLocations(IQuest quest, IStage stage) { + if (settings.getKillAreaSettings().isDisabled()) { + return; + } + for (int i = 0; i < stage.getLocationsToKillWithin().size(); i++) { + Location location = stage.getLocationsToKillWithin().get(i); + int radius = stage.getRadiiToKillWithin().get(i); + EntityType mob = stage.getMobsToKill().get(i); + int mobAmount = stage.getMobNumToKill().get(i); + String areaName = stage.getKillNames().get(i); + + String description = ""; + if (areaName != null) { + description += "" + areaName + "
"; + } + description += "Kill location for " + quest.getName() + + "
Kill " + QuestsHelper.normalizeName(mob.name()) + " x " + mobAmount; + DynmapHelper.markLocation(location, radius, description, killAreaMarkerSet, settings.getKillAreaSettings()); + } + } + +} diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestsHandler.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestsHandler.java index a711d83..3b928a0 100644 --- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestsHandler.java +++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestsHandler.java @@ -3,26 +3,19 @@ package net.knarcraft.dynmapcitizens.handler.trait.quests; import me.blackvein.quests.QuestsAPI; import me.blackvein.quests.quests.IQuest; import me.blackvein.quests.quests.IStage; -import me.blackvein.quests.quests.Planner; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.npc.NPCRegistry; import net.knarcraft.dynmapcitizens.DynmapCitizens; import net.knarcraft.dynmapcitizens.handler.trait.AbstractTraitHandler; import net.knarcraft.dynmapcitizens.property.Icon; -import net.knarcraft.dynmapcitizens.settings.AreaMarkerSettings; import net.knarcraft.dynmapcitizens.settings.QuestsSettings; import net.knarcraft.dynmapcitizens.settings.TraitSettings; import net.knarcraft.dynmapcitizens.util.QuestsHelper; import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.EntityType; import org.dynmap.DynmapAPI; -import org.dynmap.markers.CircleMarker; import org.dynmap.markers.GenericMarker; import org.dynmap.markers.Marker; import org.dynmap.markers.MarkerIcon; -import org.dynmap.markers.MarkerSet; import java.util.ArrayList; import java.util.HashMap; @@ -30,7 +23,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.logging.Level; /** * A handler class for the quests trait @@ -38,12 +30,11 @@ import java.util.logging.Level; public class QuestsHandler extends AbstractTraitHandler { private QuestsAPI questsAPI; - private MarkerSet killAreaMarkerSet; - private MarkerSet reachAreaMarkerSet; private Map markerIcons; - private Map questGiverInfo; + private final Map questGiverInfo = new HashMap<>(); private final QuestsSettings settings = new QuestsSettings(); - private List unavailableQuests; + private final List unavailableQuests = new ArrayList<>(); + private QuestAreaHandler questAreaHandler; @Override public void initialize() { @@ -54,8 +45,7 @@ public class QuestsHandler extends AbstractTraitHandler { super.initializeMarkerSet(); super.markerSet.getMarkers().forEach(GenericMarker::deleteMarker); - killAreaMarkerSet = initializeMarkerSet(dynmapAPI, settings.getKillAreaSettings()); - reachAreaMarkerSet = initializeMarkerSet(dynmapAPI, settings.getReachAreaSettings()); + questAreaHandler = new QuestAreaHandler(questsAPI, dynmapAPI, settings, unavailableQuests); } else { isEnabled = false; } @@ -72,12 +62,12 @@ public class QuestsHandler extends AbstractTraitHandler { return; } - //Update all area markers for quests - updateQuestAreas(); - //Generate information about all NPCs involved in quests generateQuestNPCInfo(); + //Update all area markers for quests + questAreaHandler.updateQuestAreas(); + //Remove any markers for deleted quests for (Marker marker : super.markerSet.getMarkers()) { if (!questGiverInfo.containsKey(UUID.fromString(marker.getMarkerID()))) { @@ -89,32 +79,16 @@ public class QuestsHandler extends AbstractTraitHandler { generateAllMarkers(); } - /** - * Initializes a marker set from the given settings - * - * @param dynmapAPI

A reference to the Dynmap API

- * @param markerSettings

The settings to use for initialization

- * @return

The initialized marker

- */ - private MarkerSet initializeMarkerSet(DynmapAPI dynmapAPI, AreaMarkerSettings markerSettings) { - MarkerSet markerSet = getMarkerSet(dynmapAPI, markerSettings.getMarkerSetId(), markerSettings.getMarkerSetName()); - if (markerSet != null) { - markerSet.setHideByDefault(markerSettings.markersHiddenByDefault()); - markerSet.setLayerPriority(markerSettings.getMarkerSetPriority()); - } - return markerSet; - } - /** * Generates information about all NPCs involved in quests */ private void generateQuestNPCInfo() { //Clear any previous information - questGiverInfo = new HashMap<>(); - unavailableQuests = new ArrayList<>(); + questGiverInfo.clear(); + unavailableQuests.clear(); //Generation information about NPC's parts in each quest for (IQuest quest : questsAPI.getLoadedQuests()) { - if (isQuestUnavailable(quest)) { + if (QuestsHelper.isQuestUnavailable(quest)) { unavailableQuests.add(quest); //If unavailable quests aren't displayed, there is no point in continuing if (settings.hideUnavailableQuests()) { @@ -138,49 +112,6 @@ public class QuestsHandler extends AbstractTraitHandler { } } - /** - * Checks whether the given quest is unavailable, according to its planner information - * - * @param quest

The quest to check for availability

- * @return

True if the quest is unavailable

- */ - private boolean isQuestUnavailable(IQuest quest) { - Planner planner = quest.getPlanner(); - long currentTime = System.currentTimeMillis(); - - if (!planner.hasEnd() && !planner.hasStart()) { - return false; - } - - if (!planner.hasStart()) { - //If past the end datetime, the quest is no longer available - return currentTime > planner.getEndInMillis(); - } - - if (!planner.hasEnd()) { - //If before the start datetime, the quest is not yet available - return currentTime < planner.getStartInMillis(); - } - - if (!planner.hasRepeat()) { - //If outside the start and end dates, the quest is unavailable - return currentTime < planner.getStartInMillis() || currentTime > planner.getEndInMillis(); - } - - long startTime = planner.getStartInMillis(); - long endTime = planner.getEndInMillis(); - - do { - if (currentTime > startTime && currentTime < endTime) { - return false; - } - startTime += planner.getRepeat(); - endTime += planner.getRepeat(); - //If startTime > currentTime, current time cannot be within the interval - } while (startTime < currentTime); - return true; - } - /** * Generates all quest markers based on the previously generated quest NPC info */ @@ -292,116 +223,4 @@ public class QuestsHandler extends AbstractTraitHandler { return questGiverInfo.get(npcId); } - /** - * Updates all quest area markers - */ - private void updateQuestAreas() { - this.killAreaMarkerSet.getCircleMarkers().forEach(GenericMarker::deleteMarker); - this.reachAreaMarkerSet.getCircleMarkers().forEach(GenericMarker::deleteMarker); - for (IQuest quest : questsAPI.getLoadedQuests()) { - //Skip marker if quest is unavailable, and unavailable quests are hidden - if (settings.hideUnavailableQuests() && this.unavailableQuests.contains(quest)) { - continue; - } - for (IStage stage : quest.getStages()) { - markKillLocations(quest, stage); - markReachLocations(quest, stage); - } - } - //TODO: Mark WorldGuard areas part of quests. Requires WorldGuard integration - } - - /** - * Marks any reach locations found in the given stage - * - * @param quest

The quest the stage belongs to

- * @param stage

The stage to search for reach locations

- */ - private void markReachLocations(IQuest quest, IStage stage) { - if (settings.getReachAreaSettings().isDisabled()) { - return; - } - for (int i = 0; i < stage.getLocationsToReach().size(); i++) { - Location location = stage.getLocationsToReach().get(i); - int radius = stage.getRadiiToReachWithin().get(i); - String areaName = stage.getLocationNames().get(i); - String description = ""; - if (areaName != null) { - description += "" + areaName + "
"; - } - description += "Target location for " + quest.getName(); - markLocation(location, radius, description, reachAreaMarkerSet, settings.getReachAreaSettings()); - } - } - - /** - * Marks any kill locations found in the given stage - * - * @param quest

The quest the stage belongs to

- * @param stage

The stage to search for kill locations

- */ - private void markKillLocations(IQuest quest, IStage stage) { - if (settings.getKillAreaSettings().isDisabled()) { - return; - } - for (int i = 0; i < stage.getLocationsToKillWithin().size(); i++) { - Location location = stage.getLocationsToKillWithin().get(i); - int radius = stage.getRadiiToKillWithin().get(i); - EntityType mob = stage.getMobsToKill().get(i); - int mobAmount = stage.getMobNumToKill().get(i); - String areaName = stage.getKillNames().get(i); - - String description = ""; - if (areaName != null) { - description += "" + areaName + "
"; - } - description += "Kill location for " + quest.getName() + - "
Kill " + QuestsHelper.normalizeName(mob.name()) + " x " + mobAmount; - markLocation(location, radius, description, killAreaMarkerSet, settings.getKillAreaSettings()); - } - } - - /** - * Marks a location on the dynamic map - * - * @param location

The location to mark

- * @param radius

The radius of the circular area to mark

- * @param description

The description to put on the marker

- * @param markerSet

The marker set to use when marking the location

- * @param markerSettings

The settings to use for the marker

- */ - private void markLocation(Location location, Integer radius, String description, MarkerSet markerSet, - AreaMarkerSettings markerSettings) { - //Skip if location is invalid - World world = location.getWorld(); - if (world == null) { - return; - } - - CircleMarker circleMarker = markerSet.createCircleMarker(null, description, true, - world.getName(), location.getX(), location.getY(), location.getZ(), radius, radius, false); - if (circleMarker == null) { - DynmapCitizens.getInstance().getLogger().log(Level.WARNING, "Unable to create circle marker at " + - location + " with radius " + radius); - } else { - circleMarker.setFillStyle(markerSettings.getFillOpacity(), getColor(markerSettings.getFillColor())); - circleMarker.setLineStyle(markerSettings.getLineThickness(), markerSettings.getLineOpacity(), - getColor(markerSettings.getLineColor())); - } - } - - /** - * Gets an integer representation from a hexadecimal color code - * - * @param color

A hexadecimal color

- * @return

An integer representation of the color

- */ - private int getColor(String color) { - try { - return Integer.parseInt(color, 16); - } catch (NumberFormatException exception) { - return 0; - } - } - } diff --git a/src/main/java/net/knarcraft/dynmapcitizens/util/DynmapHelper.java b/src/main/java/net/knarcraft/dynmapcitizens/util/DynmapHelper.java new file mode 100644 index 0000000..fbe9990 --- /dev/null +++ b/src/main/java/net/knarcraft/dynmapcitizens/util/DynmapHelper.java @@ -0,0 +1,97 @@ +package net.knarcraft.dynmapcitizens.util; + +import net.knarcraft.dynmapcitizens.DynmapCitizens; +import net.knarcraft.dynmapcitizens.settings.AreaMarkerSettings; +import org.bukkit.Location; +import org.bukkit.World; +import org.dynmap.DynmapAPI; +import org.dynmap.markers.CircleMarker; +import org.dynmap.markers.MarkerSet; + +import java.util.logging.Level; + +public class DynmapHelper { + + /** + * Gets the given marker set (and creates it if necessary) + * + * @param dynmapAPI

A reference to dynmap's API

+ * @param setId

The id of the marker set to get

+ * @param label

The label of the marker set if creation is necessary

+ * @return

The marker set, or null if something went wrong

+ */ + public static MarkerSet getMarkerSet(DynmapAPI dynmapAPI, String setId, String label) { + MarkerSet markerSet = dynmapAPI.getMarkerAPI().getMarkerSet(setId); + if (markerSet == null) { + markerSet = dynmapAPI.getMarkerAPI().createMarkerSet(setId, label, null, false); + if (markerSet == null) { + DynmapCitizens.getInstance().getLogger().log(Level.SEVERE, "Unable to get or create " + setId + + " marker set"); + DynmapCitizens.getInstance().onDisable(); + return null; + } + } + return markerSet; + } + + /** + * Initializes a marker set from the given settings + * + * @param dynmapAPI

A reference to the Dynmap API

+ * @param markerSettings

The settings to use for initialization

+ * @return

The initialized marker

+ */ + public static MarkerSet initializeMarkerSet(DynmapAPI dynmapAPI, AreaMarkerSettings markerSettings) { + MarkerSet markerSet = DynmapHelper.getMarkerSet(dynmapAPI, markerSettings.getMarkerSetId(), + markerSettings.getMarkerSetName()); + if (markerSet != null) { + markerSet.setHideByDefault(markerSettings.markersHiddenByDefault()); + markerSet.setLayerPriority(markerSettings.getMarkerSetPriority()); + } + return markerSet; + } + + /** + * Marks a location on the dynamic map + * + * @param location

The location to mark

+ * @param radius

The radius of the circular area to mark

+ * @param description

The description to put on the marker

+ * @param markerSet

The marker set to use when marking the location

+ * @param markerSettings

The settings to use for the marker

+ */ + public static void markLocation(Location location, Integer radius, String description, MarkerSet markerSet, + AreaMarkerSettings markerSettings) { + //Skip if location is invalid + World world = location.getWorld(); + if (world == null) { + return; + } + + CircleMarker circleMarker = markerSet.createCircleMarker(null, description, true, + world.getName(), location.getX(), location.getY(), location.getZ(), radius, radius, false); + if (circleMarker == null) { + DynmapCitizens.getInstance().getLogger().log(Level.WARNING, "Unable to create circle marker at " + + location + " with radius " + radius); + } else { + circleMarker.setFillStyle(markerSettings.getFillOpacity(), getColor(markerSettings.getFillColor())); + circleMarker.setLineStyle(markerSettings.getLineThickness(), markerSettings.getLineOpacity(), + getColor(markerSettings.getLineColor())); + } + } + + /** + * Gets an integer representation from a hexadecimal color code + * + * @param color

A hexadecimal color

+ * @return

An integer representation of the color

+ */ + private static int getColor(String color) { + try { + return Integer.parseInt(color, 16); + } catch (NumberFormatException exception) { + return 0; + } + } + +} diff --git a/src/main/java/net/knarcraft/dynmapcitizens/util/QuestsHelper.java b/src/main/java/net/knarcraft/dynmapcitizens/util/QuestsHelper.java index 21b06e3..a29f5d8 100644 --- a/src/main/java/net/knarcraft/dynmapcitizens/util/QuestsHelper.java +++ b/src/main/java/net/knarcraft/dynmapcitizens/util/QuestsHelper.java @@ -1,5 +1,7 @@ package net.knarcraft.dynmapcitizens.util; +import me.blackvein.quests.quests.IQuest; +import me.blackvein.quests.quests.Planner; import net.knarcraft.dynmapcitizens.DynmapCitizens; import net.knarcraft.dynmapcitizens.handler.VaultHandler; import net.knarcraft.dynmapcitizens.handler.trait.quests.QuestNPCType; @@ -81,4 +83,47 @@ public class QuestsHelper { }; } + /** + * Checks whether the given quest is unavailable, according to its planner information + * + * @param quest

The quest to check for availability

+ * @return

True if the quest is unavailable

+ */ + public static boolean isQuestUnavailable(IQuest quest) { + Planner planner = quest.getPlanner(); + long currentTime = System.currentTimeMillis(); + + if (!planner.hasEnd() && !planner.hasStart()) { + return false; + } + + if (!planner.hasStart()) { + //If past the end datetime, the quest is no longer available + return currentTime > planner.getEndInMillis(); + } + + if (!planner.hasEnd()) { + //If before the start datetime, the quest is not yet available + return currentTime < planner.getStartInMillis(); + } + + if (!planner.hasRepeat()) { + //If outside the start and end dates, the quest is unavailable + return currentTime < planner.getStartInMillis() || currentTime > planner.getEndInMillis(); + } + + long startTime = planner.getStartInMillis(); + long endTime = planner.getEndInMillis(); + + do { + if (currentTime > startTime && currentTime < endTime) { + return false; + } + startTime += planner.getRepeat(); + endTime += planner.getRepeat(); + //If startTime > currentTime, current time cannot be within the interval + } while (startTime < currentTime); + return true; + } + } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 08a4b88..9dd6bdc 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -74,7 +74,7 @@ traits: reachMarker: enabled: true # The priority of quest reach area markers. Higher priority markers will display on top of lower priority ones - markerSetPriority: 1 + markerSetPriority: 2 # The id of the quest reach area marker set. Change if it overlaps with an existing set id markerSetId: "quests_reach" # The name of the reach areas marker set. Change it if you want a cooler name