Moves code around to split the huge QuestHandler class

This commit is contained in:
2022-11-01 19:18:00 +01:00
parent 3e93b4a733
commit 219abf9ed9
19 changed files with 812 additions and 627 deletions

View File

@ -0,0 +1,90 @@
package net.knarcraft.dynmapcitizens.handler.trait;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.dynmapcitizens.DynmapCitizens;
import org.bukkit.Location;
import org.bukkit.World;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.Marker;
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
*/
public abstract class AbstractTraitHandler implements CitizensTraitHandler {
protected boolean isEnabled = false;
@Override
public boolean isEnabled() {
return isEnabled;
}
/**
* Gets the given marker set (and creates it if necessary)
*
* @param dynmapAPI <p>A reference to dynmap's API</p>
* @param setId <p>The id of the marker set to get</p>
* @param label <p>The label of the marker set if creation is necessary</p>
* @return <p>The marker set, or null if something went wrong</p>
*/
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
*
* @param npcId <p>The if of the NPC</p>
* @param markerName <p>The name of the NPC marker</p>
* @param markerDescription <p>The description of the NPC marker</p>
* @param icon <p>The icon used for the marker</p>
* @param markerSet <p>The marker set to add the marker to</p>
*/
protected void addNPCMarker(UUID npcId, String markerName, String markerDescription, MarkerIcon icon, MarkerSet markerSet) {
NPC npc = CitizensAPI.getNPCRegistry().getByUniqueId(npcId);
//If the NPC has been removed, abort
if (npc == null) {
return;
}
//Skip if not a proper location
Location npcLocation = npc.getStoredLocation();
World world = npcLocation.getWorld();
if (world == null) {
return;
}
Marker marker = markerSet.createMarker(null, markerName + npc.getName(), npcLocation.getWorld().getName(),
npcLocation.getX(), npcLocation.getY(), npcLocation.getZ(), icon, false);
if (marker != null) {
marker.setDescription(markerDescription);
}
}
/**
* Gets whether the given NPC is currently moving
*
* @param npc <p>The NPC to check</p>
* @return <p>True if the NPC is currently moving about</p>
*/
protected boolean isMoving(NPC npc) {
return npc.getNavigator().getTargetAsLocation() != null;
}
}

View File

@ -0,0 +1,106 @@
package net.knarcraft.dynmapcitizens.handler.trait;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.NPCSettings;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.dynmapcitizens.DynmapCitizens;
import net.knarcraft.dynmapcitizens.property.Icon;
import net.knarcraft.dynmapcitizens.property.UpdateRate;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.GenericMarker;
import org.dynmap.markers.MarkerSet;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
/**
* A handler class for the blacksmith trait
*/
public class BlacksmithHandler extends AbstractTraitHandler {
private MarkerSet blacksmithSet;
@Override
public void initialize() {
BlacksmithPlugin blacksmithPlugin = (BlacksmithPlugin) Bukkit.getServer().getPluginManager().getPlugin("Blacksmith");
DynmapAPI dynmapAPI = DynmapCitizens.getInstance().getDynmapAPI();
if (blacksmithPlugin != null) {
blacksmithSet = getMarkerSet(dynmapAPI, "blacksmiths", "Blacksmiths");
if (blacksmithSet != null) {
blacksmithSet.setHideByDefault(false);
blacksmithSet.setLayerPriority(3);
isEnabled = true;
return;
}
}
isEnabled = false;
}
@Override
public UpdateRate getUpdateRate() {
return UpdateRate.VERY_SLOW;
}
@Override
public void updateMarkers() {
//Remove existing markers
blacksmithSet.getMarkers().forEach(GenericMarker::deleteMarker);
Class<? extends Trait> blacksmithTrait = CitizensAPI.getTraitFactory().getTraitClass("blacksmith");
for (NPC npc : CitizensAPI.getNPCRegistry()) {
if (npc.hasTrait(blacksmithTrait)) {
BlacksmithTrait trait = npc.getTraitNullable(BlacksmithTrait.class);
String description = null;
if (trait == null) {
DynmapCitizens.getInstance().getLogger().log(Level.WARNING, "Unable to get blacksmith trait");
} else {
//TODO: Make a setting which allows disabling extra information (except title and reforge-able items?)
description = getDetailedBlacksmithInfo(npc, trait.getSettings());
}
addNPCMarker(npc.getUniqueId(), "Blacksmith NPC: ", description,
DynmapCitizens.getInstance().getMarkerIcons().get(Icon.BLACKSMITH), blacksmithSet);
}
}
}
/**
* Gets detailed information about a blacksmith NPC
*
* @param npc <p>The NPC the settings belong to</p>
* @param npcSettings <p>The settings to search for information</p>
* @return <p>A string describing the blacksmith</p>
*/
private String getDetailedBlacksmithInfo(NPC npc, NPCSettings npcSettings) {
String info = "<h2>" + npc.getName() + " the " +
npcSettings.getBlacksmithTitle() + "</h2><b>Fail chance:</b> " + npcSettings.getFailChance() +
"<br><b>Enchantment chance:</b> " + npcSettings.getExtraEnchantmentChance() + "<br><b>Delay:</b> " +
npcSettings.getMinReforgeDelay() + " to " + npcSettings.getMaxReforgeDelay() +
" seconds<br><b>Cool-down:</b> " + npcSettings.getReforgeCoolDown() + " seconds<br><b>Drop item:</b> " +
npcSettings.getDropItem() + "<br><b>Max enchantments:</b> " + npcSettings.getMaxEnchantments();
if (!npcSettings.getReforgeAbleItems().isEmpty()) {
info += "<br><b>Reforge-able items:</b> " + getReforgeAbleItemsString(npcSettings.getReforgeAbleItems());
}
return info;
}
/**
* Gets reforge-able items as a string
*
* @param materials <p>The materials specified as reforge-able items</p>
* @return <p>The reforge-able items</p>
*/
private static String getReforgeAbleItemsString(List<Material> materials) {
List<String> materialNames = new ArrayList<>();
for (Material material : materials) {
materialNames.add(material.name());
}
return String.join(", ", materialNames);
}
}

View File

@ -0,0 +1,34 @@
package net.knarcraft.dynmapcitizens.handler.trait;
import net.knarcraft.dynmapcitizens.property.UpdateRate;
/**
* A handler which takes care of everything for one citizen trait
*/
public interface CitizensTraitHandler {
/**
* Initializes this trait handler
*/
void initialize();
/**
* Gets the update rate used for this handler
*
* @return <p>The update rate for this handler</p>
*/
UpdateRate getUpdateRate();
/**
* Gets whether this handler was properly initialized and enabled
*
* @return <p>True if this handler was enabled</p>
*/
boolean isEnabled();
/**
* Updates all markers used for this handler
*/
void updateMarkers();
}

View File

@ -0,0 +1,85 @@
package net.knarcraft.dynmapcitizens.handler.trait;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.knarcraft.dynmapcitizens.DynmapCitizens;
import net.knarcraft.dynmapcitizens.property.Icon;
import net.knarcraft.dynmapcitizens.property.UpdateRate;
import net.knarcraft.minstrel.MinstrelPlugin;
import net.knarcraft.minstrel.music.Song;
import net.knarcraft.minstrel.trait.MinstrelTrait;
import org.bukkit.Bukkit;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.GenericMarker;
import org.dynmap.markers.MarkerSet;
import java.util.logging.Level;
/**
* A handler class for the minstrel trait
*/
public class MinstrelHandler extends AbstractTraitHandler {
private MarkerSet minstrelSet;
@Override
public void initialize() {
MinstrelPlugin minstrelPlugin = (MinstrelPlugin) Bukkit.getServer().getPluginManager().getPlugin("Minstrel");
DynmapAPI dynmapAPI = DynmapCitizens.getInstance().getDynmapAPI();
if (minstrelPlugin != null) {
minstrelSet = getMarkerSet(dynmapAPI, "minstrels", "Minstrels");
if (minstrelSet != null) {
minstrelSet.setHideByDefault(false);
minstrelSet.setLayerPriority(3);
isEnabled = true;
return;
}
}
isEnabled = false;
}
@Override
public UpdateRate getUpdateRate() {
return UpdateRate.VERY_SLOW;
}
@Override
public void updateMarkers() {
//Remove existing markers
minstrelSet.getMarkers().forEach(GenericMarker::deleteMarker);
Class<? extends Trait> minstrelTrait = CitizensAPI.getTraitFactory().getTraitClass("minstrel");
for (NPC npc : CitizensAPI.getNPCRegistry()) {
if (npc.hasTrait(minstrelTrait)) {
MinstrelTrait trait = npc.getTraitNullable(MinstrelTrait.class);
String description = null;
if (trait == null) {
DynmapCitizens.getInstance().getLogger().log(Level.WARNING, "Unable to get minstrel trait");
} else {
description = getDetailedMinstrelInfo(npc, trait);
}
addNPCMarker(npc.getUniqueId(), "Minstrel NPC: ", description,
DynmapCitizens.getInstance().getMarkerIcons().get(Icon.MINSTREL), minstrelSet);
}
}
}
/**
* Gets detailed information about a minstrel NPC
*
* @param npc <p>The NPC the settings belong to</p>
* @param trait <p>The minstrel trait to get information from</p>
* @return <p>A string describing the minstrel</p>
*/
private String getDetailedMinstrelInfo(NPC npc, MinstrelTrait trait) {
StringBuilder info = new StringBuilder("<h2>" + npc.getName() + "</h2>");
info.append("<b>Songs:</b><ul>");
for (Song song : trait.getPlaylist().getSongs()) {
info.append("<li>Category: ").append(song.getCategory()).append("<br>Sound: ").append(song.getSound()).append("</li>");
}
info.append("</ul>");
return info.toString();
}
}

View File

@ -0,0 +1,60 @@
package net.knarcraft.dynmapcitizens.handler.trait;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.knarcraft.dynmapcitizens.DynmapCitizens;
import net.knarcraft.dynmapcitizens.property.Icon;
import net.knarcraft.dynmapcitizens.property.UpdateRate;
import org.bukkit.Bukkit;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.GenericMarker;
import org.dynmap.markers.MarkerSet;
import org.mcmonkey.sentinel.SentinelPlugin;
import org.mcmonkey.sentinel.SentinelTrait;
/**
* A handler class for the sentinel trait
*/
public class SentinelHandler extends AbstractTraitHandler {
private MarkerSet sentinelSet;
@Override
public void initialize() {
SentinelPlugin sentinelPlugin = (SentinelPlugin) Bukkit.getServer().getPluginManager().getPlugin("Sentinel");
DynmapAPI dynmapAPI = DynmapCitizens.getInstance().getDynmapAPI();
if (sentinelPlugin != null) {
sentinelSet = getMarkerSet(dynmapAPI, "sentinels", "Sentinels");
if (sentinelSet != null) {
sentinelSet.setHideByDefault(false);
sentinelSet.setLayerPriority(1);
isEnabled = true;
return;
}
}
isEnabled = false;
}
@Override
public UpdateRate getUpdateRate() {
return UpdateRate.FAST;
}
@Override
public void updateMarkers() {
sentinelSet.getMarkers().forEach(GenericMarker::deleteMarker);
Class<? extends Trait> sentinelTrait = CitizensAPI.getTraitFactory().getTraitClass("sentinel");
for (NPC npc : CitizensAPI.getNPCRegistry()) {
if (npc.hasTrait(sentinelTrait)) {
SentinelTrait trait = CitizensAPI.getTraitFactory().getTrait("sentinel");
String description = "<h2>" + npc.getName() + "</h2><b>Armor:</b> " +
trait.armor + "<br><b>Health:</b> " + trait.health;
addNPCMarker(npc.getUniqueId(), "Sentinel NPC: ", description,
DynmapCitizens.getInstance().getMarkerIcons().get(Icon.SENTINEL), sentinelSet);
}
}
}
}

View File

@ -0,0 +1,126 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
import me.blackvein.quests.quests.IQuest;
import net.knarcraft.dynmapcitizens.property.Icon;
import java.util.ArrayList;
import java.util.List;
/**
* Keeps track of quest information for an NPC
*/
public class NPCQuestInfo {
private final List<IQuest> questStart = new ArrayList<>();
private final List<IQuest> questKill = new ArrayList<>();
private final List<IQuest> questDeliver = new ArrayList<>();
private final List<IQuest> questInteract = new ArrayList<>();
/**
* Adds the given quest to the list of quests this NPC provides
*
* @param quest <p>The quest to add</p>
*/
public void addQuestStart(IQuest quest) {
this.questStart.add(quest);
}
/**
* Adds the given quest to the list of quests in which this NPC is killed
*
* @param quest <p>The quest to add</p>
*/
public void addQuestKill(IQuest quest) {
this.questKill.add(quest);
}
/**
* Adds the given quest to the list of quests in which this NPC takes deliveries
*
* @param quest <p>The quest to add</p>
*/
public void addQuestDeliver(IQuest quest) {
this.questDeliver.add(quest);
}
/**
* Adds the given quest to the list of quests in which this NPC has to be interacted with
*
* @param quest <p>The quest to add</p>
*/
public void addQuestInteract(IQuest quest) {
this.questInteract.add(quest);
}
/**
* Gets all quests given by this NPC
*
* @return <p>All quests this NPC is used to start</p>
*/
public List<IQuest> getQuestStarts() {
return new ArrayList<>(this.questStart);
}
/**
* Gets all quests in which this NPC has to be killed
*
* @return <p>All kill quests involving this NPC</p>
*/
public List<IQuest> getQuestKills() {
return new ArrayList<>(this.questKill);
}
/**
* Gets all quests requiring something be delivered to this NPC
*
* @return <p>All quests delivering to this NPC</p>
*/
public List<IQuest> getQuestDeliveries() {
return new ArrayList<>(this.questDeliver);
}
/**
* Gets all quests requiring interaction with this NPC
*
* @return <p>All quests requiring interaction with this NPC</p>
*/
public List<IQuest> getQuestInteractions() {
return new ArrayList<>(this.questInteract);
}
/**
* Gets the main type of the quest the NPC
*
* @return <p>The main type of the quest NPC</p>
*/
public QuestNPCType getQuestNPCType() {
if (!questStart.isEmpty()) {
return QuestNPCType.GIVER;
} else if (!questKill.isEmpty() && questInteract.isEmpty() && questDeliver.isEmpty()) {
return QuestNPCType.KILL;
} else if (!questInteract.isEmpty() && questKill.isEmpty() && questDeliver.isEmpty()) {
return QuestNPCType.INTERACT;
} else if (!questDeliver.isEmpty() && questKill.isEmpty() && questInteract.isEmpty()) {
return QuestNPCType.DELIVER;
} else {
return QuestNPCType.CHAIN;
}
}
/**
* Gets the Icon to use when marking this NPC
*
* @return <p>The icon used to mark this NPC</p>
*/
public Icon getNPCIcon() {
QuestNPCType type = getQuestNPCType();
return switch (type) {
case KILL -> Icon.QUEST_KILL;
case GIVER -> Icon.QUEST_GIVER;
case DELIVER -> Icon.QUEST_DELIVER;
case INTERACT -> Icon.QUEST_INTERACT;
case CHAIN -> Icon.QUEST_CHAIN;
};
}
}

View File

@ -0,0 +1,33 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
/**
* A specifier for a quest NPC's main type
*/
public enum QuestNPCType {
/**
* An NPC responsible for giving quests
*/
GIVER,
/**
* An NPC killed in a quest
*/
KILL,
/**
* An NPC set as the delivery target in a quest
*/
DELIVER,
/**
* An NPC to be interacted with in a quest
*/
INTERACT,
/**
* An NPC which is not a quest giver, but has several tasks as part of other quests
*/
CHAIN
}

View File

@ -0,0 +1,64 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
import me.blackvein.quests.quests.IQuest;
import me.blackvein.quests.quests.Planner;
import net.knarcraft.dynmapcitizens.util.TimeFormatter;
/**
* A class to generate a string containing all information about a quest's planner info
*/
public class QuestPlannerInfoGenerator {
private final IQuest quest;
/**
* Instantiates a new quest planner info generator
*
* @param quest <p>The quest to generate information about</p>
*/
public QuestPlannerInfoGenerator(IQuest quest) {
this.quest = quest;
}
/**
* Gets information about the time-availability of a quest
*
* @return <p>Information about when the quest is available</p>
*/
public String getQuestPlannerInfo() {
Planner planner = quest.getPlanner();
StringBuilder plannerInfo = new StringBuilder();
plannerInfo.append("<b>Planner:</b><ul>");
//Quest can be repeated after a cool-down
if (planner.hasCooldown()) {
plannerInfo.append("<li>Quest repeatable after: ");
plannerInfo.append(TimeFormatter.getDurationString(planner.getCooldown() / 1000));
plannerInfo.append("</li>");
} else {
plannerInfo.append("<li>Quest cannot be repeated!</li>");
}
//Quest only becomes available after the start date
if (planner.hasStart()) {
plannerInfo.append("<li>Quest available from ");
plannerInfo.append(TimeFormatter.formatTimestamp(planner.getStartInMillis())).append("</li>");
}
//Quest is only available until the end date
if (planner.hasEnd()) {
plannerInfo.append("<li>Quest available until ");
plannerInfo.append(TimeFormatter.formatTimestamp(planner.getEndInMillis())).append("</li>");
}
//Quest availability repeats
if (planner.hasRepeat()) {
plannerInfo.append("<li>Quest will become available again after ");
plannerInfo.append(TimeFormatter.getDurationString(planner.getRepeat() / 1000)).append("</li>");
}
plannerInfo.append("</ul>");
return plannerInfo.toString();
}
}

View File

@ -0,0 +1,108 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
import me.blackvein.quests.quests.IQuest;
import me.blackvein.quests.quests.Requirements;
import net.knarcraft.dynmapcitizens.util.QuestsHelper;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Map;
/**
* A class to generate a string containing all information about a quest's requirements
*/
public class QuestRequirementsInfoGenerator {
private final IQuest quest;
/**
* Instantiates a new quest requirement info generator
*
* @param quest <p>The quest to generate information about</p>
*/
public QuestRequirementsInfoGenerator(IQuest quest) {
this.quest = quest;
}
/**
* Gets information about all requirements for the given quest
*
* @return <p>Information about the quest's requirements</p>
*/
public String getQuestRequirementsInfo() {
Requirements requirements = quest.getRequirements();
StringBuilder requirementInfo = new StringBuilder();
if (!requirements.hasRequirement()) {
return requirementInfo.toString();
}
requirementInfo.append("<b>Requirements: </b><ul>");
if (requirements.getQuestPoints() > 0) {
requirementInfo.append("<li>").append(requirements.getQuestPoints()).append(" quest points</li>");
}
if (requirements.getExp() > 0) {
requirementInfo.append("<li>").append(requirements.getExp()).append(" exp</li>");
}
if (!requirements.getBlockQuests().isEmpty()) {
requirementInfo.append("<li>Blocked by quests:<ul>");
for (IQuest blockQuest : requirements.getBlockQuests()) {
requirementInfo.append("<li>").append(blockQuest.getName()).append("</li>");
}
requirementInfo.append("</ul></li>");
}
if (!requirements.getNeededQuests().isEmpty()) {
requirementInfo.append("<li>Required quests:<ul>");
for (IQuest neededQuest : requirements.getNeededQuests()) {
requirementInfo.append("<li>").append(neededQuest.getName()).append("</li>");
}
requirementInfo.append("</ul></li>");
}
if (!requirements.getItems().isEmpty()) {
requirementInfo.append("<li>Required items:<ul>");
for (ItemStack item : requirements.getItems()) {
requirementInfo.append("<li>").append(QuestsHelper.getUpperCasedItemStackString(item)).append("</li>");
}
requirementInfo.append("</ul></li>");
}
if (!requirements.getMcmmoSkills().isEmpty()) {
List<String> skills = requirements.getMcmmoSkills();
List<Integer> amounts = requirements.getMcmmoAmounts();
for (int i = 0; i < skills.size(); i++) {
requirementInfo.append("<li>Requires mcMMO skill ").append(skills.get(i)).append(" at level ");
requirementInfo.append(amounts.get(i)).append("</li>");
}
}
if (!requirements.getPermissions().isEmpty()) {
requirementInfo.append("<li>Required permissions:<ul>");
for (String permission : requirements.getPermissions()) {
requirementInfo.append("<li>").append(permission).append("</li>");
}
requirementInfo.append("</ul></li>");
}
Map<String, Map<String, Object>> customRequirementPlugins = requirements.getCustomRequirements();
for (String plugin : customRequirementPlugins.keySet()) {
requirementInfo.append("<li>").append(plugin).append(":<ul>");
//Note: The format of custom requirements is kind of weird. First, you have the key for which plugin the
// requirement belongs to. Getting the value of the key gives another map. The map contains as key, the type
// of value, like "Skill Amount" or "Skill Type". The value is the actual value of whatever it is.
Map<String, Object> customRequirementEntry = customRequirementPlugins.get(plugin);
for (String requirementDescription : customRequirementEntry.keySet()) {
requirementInfo.append("<li>").append(requirementDescription).append(" ");
requirementInfo.append(customRequirementEntry.get(requirementDescription)).append("</li>");
}
requirementInfo.append("</ul></li>");
}
requirementInfo.append("</ul>");
return requirementInfo.toString();
}
}

View File

@ -0,0 +1,58 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
import me.blackvein.quests.quests.IQuest;
import me.blackvein.quests.quests.Rewards;
import net.knarcraft.dynmapcitizens.util.QuestsHelper;
import org.bukkit.inventory.ItemStack;
/**
* A class to generate a string containing all information about a quest's rewards
*/
public class QuestRewardsInfoGenerator {
private final IQuest quest;
/**
* Instantiates a new quest reward info generator
*
* @param quest <p>The quest to generate information about</p>
*/
public QuestRewardsInfoGenerator(IQuest quest) {
this.quest = quest;
}
/**
* Gets information about all rewards for the given quest
*
* @return <p>Information about the quest's rewards</p>
*/
public String getQuestRewardsInfo() {
Rewards reward = quest.getRewards();
StringBuilder rewardInfo = new StringBuilder();
rewardInfo.append("<b>Rewards:</b><ul>");
if (reward.getMoney() > 0) {
rewardInfo.append("<li>").append(reward.getMoney()).append(" ").append(QuestsHelper.getCurrency(reward.getMoney())).append("</li>");
}
if (reward.getExp() > 0) {
rewardInfo.append("<li>").append(reward.getExp()).append(" exp").append("</li>");
}
for (String permission : reward.getPermissions()) {
rewardInfo.append("<li>").append("Permission: ").append(permission).append("</li>");
}
for (ItemStack item : reward.getItems()) {
rewardInfo.append("<li>").append(QuestsHelper.getUpperCasedItemStackString(item)).append("</li>");
}
for (String command : reward.getCommands()) {
rewardInfo.append("<li>Command: ").append(command).append("</li>");
}
rewardInfo.append("</ul>");
return rewardInfo.toString();
}
}

View File

@ -0,0 +1,109 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
import me.blackvein.quests.quests.IQuest;
import me.blackvein.quests.quests.IStage;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.knarcraft.dynmapcitizens.util.QuestsHelper;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.UUID;
/**
* A class to generate a string containing all information about a quest's stages
*/
public class QuestStagesInfoGenerator {
private final IQuest quest;
/**
* Instantiates a new quest stages info generator
*
* @param quest <p>The quest to generate information about</p>
*/
public QuestStagesInfoGenerator(IQuest quest) {
this.quest = quest;
}
/**
* Gets information about a quest's stages
*
* @return <p>A string with information about the quest's stages</p>
*/
public String getQuestStagesInfo() {
StringBuilder questInfo = new StringBuilder();
NPCRegistry registry = CitizensAPI.getNPCRegistry();
int stageCounter = 1;
questInfo.append("<b>Stages:</b><ul>");
for (IStage stage : quest.getStages()) {
questInfo.append("<li><b>Stage ").append(stageCounter).append(" tasks:</b><ul>");
int mobTypes = stage.getMobsToKill().size();
for (int i = 0; i < mobTypes; i++) {
questInfo.append("<li>Kill ").append(QuestsHelper.normalizeName(stage.getMobsToKill().get(i).name())).append(
" X ").append(stage.getMobNumToKill().get(i)).append("</li>");
}
int deliveries = stage.getItemDeliveryTargets().size();
for (int i = 0; i < deliveries; i++) {
NPC npc = registry.getByUniqueId(stage.getItemDeliveryTargets().get(i));
questInfo.append("<li>Deliver ").append(QuestsHelper.getItemStackString(stage.getItemsToDeliver().get(i))).append(
" to ").append(npc.getName()).append("</li>");
}
if (stage.getFishToCatch() != null) {
questInfo.append("<li>Catch ").append(stage.getFishToCatch()).append(" fish").append("</li>");
}
for (UUID npcId : stage.getNpcsToKill()) {
questInfo.append("<li>Kill NPC ").append(registry.getByUniqueId(npcId).getName()).append("</li>");
}
questInfo.append(getQuestItemsTaskString(stage.getBlocksToBreak(), "<li>Break ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getBlocksToCut(), "<li>Cut ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getBlocksToDamage(), "<li>Damage ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getBlocksToUse(), "<li>Use ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getBlocksToPlace(), "<li>Place ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getItemsToBrew(), "<li>Brew ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getItemsToConsume(), "<li>Consume ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getItemsToCraft(), "<li>Craft ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getItemsToEnchant(), "<li>Enchant ")).append("</li>");
questInfo.append(getQuestItemsTaskString(stage.getItemsToSmelt(), "<li>Smelt ")).append("</li>");
int sheepTypes = stage.getSheepToShear().size();
for (int i = 0; i < sheepTypes; i++) {
questInfo.append("<li>Shear ").append(stage.getSheepNumToShear().get(i)).append(" ").append(
QuestsHelper.normalizeName(stage.getSheepToShear().get(i).name())).append(" sheep").append("</li>");
}
if (stage.getCowsToMilk() != null) {
questInfo.append("<li>Milk ").append(stage.getCowsToMilk()).append(" cows").append("</li>");
}
int mobTamingEntries = stage.getMobsToTame().size();
for (int i = 0; i < mobTamingEntries; i++) {
questInfo.append("<li>Tame ").append(stage.getMobNumToTame().get(i)).append(" ").append(
QuestsHelper.normalizeName(stage.getMobsToTame().get(i).name())).append("</li>");
}
questInfo.append("</ul></li>");
stageCounter++;
}
questInfo.append("</ul>");
return questInfo.toString();
}
/**
* Gets a string to display a quest task involving some action on an item
*
* @param items <p>The items that are part of the task</p>
* @param explanation <p>The explanation of what the player needs to do with the items</p>
* @return <p>A string describing the necessary tasks</p>
*/
private String getQuestItemsTaskString(List<ItemStack> items, String explanation) {
StringBuilder questInfo = new StringBuilder();
for (ItemStack itemStack : items) {
questInfo.append(explanation).append(QuestsHelper.getItemStackString(itemStack));
}
return questInfo.toString();
}
}

View File

@ -0,0 +1,266 @@
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.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.property.UpdateRate;
import net.knarcraft.dynmapcitizens.util.QuestsHelper;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.CircleMarker;
import org.dynmap.markers.GenericMarker;
import org.dynmap.markers.MarkerIcon;
import org.dynmap.markers.MarkerSet;
import java.util.Collection;
import java.util.HashMap;
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
*/
public class QuestsHandler extends AbstractTraitHandler {
private QuestsAPI questsAPI;
private MarkerSet questMarkerSet;
private MarkerSet questAreaMarkerSet;
private Map<Icon, MarkerIcon> markerIcons;
private Collection<IQuest> loadedQuests;
private Map<UUID, NPCQuestInfo> questGiverInfo;
@Override
public void initialize() {
questsAPI = (QuestsAPI) Bukkit.getServer().getPluginManager().getPlugin("Quests");
DynmapAPI dynmapAPI = DynmapCitizens.getInstance().getDynmapAPI();
markerIcons = DynmapCitizens.getInstance().getMarkerIcons();
if (questsAPI != null) {
questMarkerSet = getMarkerSet(dynmapAPI, "quests", "Quests");
questAreaMarkerSet = getMarkerSet(dynmapAPI, "quest_areas", "Quest areas");
if (questMarkerSet != null && questAreaMarkerSet != null) {
questMarkerSet.setHideByDefault(false);
questAreaMarkerSet.setHideByDefault(true);
questMarkerSet.setLayerPriority(3);
questAreaMarkerSet.setLayerPriority(2);
isEnabled = true;
return;
}
}
isEnabled = false;
}
@Override
public UpdateRate getUpdateRate() {
return UpdateRate.VERY_SLOW;
}
@Override
public void updateMarkers() {
if (questsAPI.isLoading()) {
return;
}
//There is no point in updating if there has been no changes in quests
boolean questsChanged = loadedQuests == null || !loadedQuests.equals(questsAPI.getLoadedQuests());
loadedQuests = questsAPI.getLoadedQuests();
//Remove old quest markers
questMarkerSet.getMarkers().forEach(GenericMarker::deleteMarker);
//Updates all quest area markers
if (questsChanged) {
updateQuestAreas();
}
//Generate information about all NPCs involved in quests
generateQuestNPCInfo();
//Generate markers based on the generated info
generateAllMarkers();
}
/**
* Generates information about all NPCs involved in quests
*/
private void generateQuestNPCInfo() {
//Clear any previous information
questGiverInfo = new HashMap<>();
//Generation information about NPC's parts in each quest
for (IQuest quest : questsAPI.getLoadedQuests()) {
if (quest.getNpcStart() != null) {
getInfo(quest.getNpcStart()).addQuestStart(quest);
}
for (IStage stage : quest.getStages()) {
for (UUID npcId : stage.getNpcsToKill()) {
getInfo(npcId).addQuestKill(quest);
}
for (UUID npcId : stage.getItemDeliveryTargets()) {
getInfo(npcId).addQuestDeliver(quest);
}
for (UUID npcId : stage.getNpcsToInteract()) {
getInfo(npcId).addQuestInteract(quest);
}
}
}
}
/**
* Generates all quest markers based on the previously generated quest NPC info
*/
private void generateAllMarkers() {
NPCRegistry registry = CitizensAPI.getNPCRegistry();
//Add markers for each NPC detected as part of a quest
for (UUID npcId : questGiverInfo.keySet()) {
NPCQuestInfo info = questGiverInfo.get(npcId);
MarkerIcon icon = markerIcons.get(info.getNPCIcon());
List<IQuest> questStarts = info.getQuestStarts();
StringBuilder markerDescription = new StringBuilder();
markerDescription.append("<h2>").append(registry.getByUniqueId(npcId).getName()).append("</h2>");
if (!questStarts.isEmpty()) {
markerDescription.append("<h3>Quests offered:</h3><ul>");
for (IQuest quest : questStarts) {
markerDescription.append("<li><h4><b>").append(quest.getName()).append("</b></h4><h5><b>- ");
markerDescription.append(quest.getDescription()).append("</b></h5>").append(
new QuestStagesInfoGenerator(quest).getQuestStagesInfo());
markerDescription.append(new QuestRewardsInfoGenerator(quest).getQuestRewardsInfo());
markerDescription.append(new QuestRequirementsInfoGenerator(quest).getQuestRequirementsInfo());
markerDescription.append(new QuestPlannerInfoGenerator(quest).getQuestPlannerInfo()).append("</li>");
}
markerDescription.append("</ul>");
}
markerDescription.append(getInvolvedInQuestsString(info));
addNPCMarker(npcId, QuestsHelper.getMarkerTitle(info.getQuestNPCType()), markerDescription.toString(), icon, questMarkerSet);
}
}
/**
* Gets information about the quests an NPC is involved in
*
* @param info <p>The NPC info to look through</p>
* @return <p>Information about an NPC's involvement in different quests</p>
*/
private String getInvolvedInQuestsString(NPCQuestInfo info) {
List<IQuest> questKills = info.getQuestKills();
List<IQuest> questInteractions = info.getQuestInteractions();
List<IQuest> questDeliveries = info.getQuestDeliveries();
if (questKills.isEmpty() && questInteractions.isEmpty() && questDeliveries.isEmpty()) {
return "";
}
StringBuilder markerDescription = new StringBuilder();
markerDescription.append("<h3>Involved in quests:</h3><ul>");
addInvolvedInString("Killed in", questKills, markerDescription);
addInvolvedInString("Delivery target in", questDeliveries, markerDescription);
addInvolvedInString("Interacted with in quest", questInteractions, markerDescription);
markerDescription.append("</ul>");
return markerDescription.toString();
}
/**
* Adds string explaining how an NPC is involved with the given quests
*
* @param prefix <p>The string explaining how the NPC is involved</p>
* @param quests <p>The quests the NPC is involved in</p>
* @param builder <p>The string builder to append to</p>
*/
private void addInvolvedInString(String prefix, List<IQuest> quests, StringBuilder builder) {
for (IQuest quest : new HashSet<>(quests)) {
builder.append("<li>").append(prefix).append(": ").append(quest.getName()).append("</li>");
}
}
/**
* Gets the info object for the given NPC
*
* @param npcId <p>The id of the NPC to get information about</p>
* @return <p>The NPC's info object</p>
*/
private NPCQuestInfo getInfo(UUID npcId) {
if (questGiverInfo.get(npcId) == null) {
questGiverInfo.put(npcId, new NPCQuestInfo());
}
return questGiverInfo.get(npcId);
}
/**
* Updates all quest area markers
*/
private void updateQuestAreas() {
questAreaMarkerSet.getCircleMarkers().forEach(GenericMarker::deleteMarker);
for (IQuest quest : questsAPI.getLoadedQuests()) {
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 <p>The quest the stage belongs to</p>
* @param stage <p>The stage to search for reach locations</p>
*/
private void markReachLocations(IQuest quest, IStage stage) {
markLocations(stage.getLocationsToReach(), stage.getRadiiToReachWithin(),
"<b>Target location for:</b> " + quest.getName());
}
/**
* Marks any kill locations found in the given stage
*
* @param quest <p>The quest the stage belongs to</p>
* @param stage <p>The stage to search for kill locations</p>
*/
private void markKillLocations(IQuest quest, IStage stage) {
markLocations(stage.getLocationsToKillWithin(), stage.getRadiiToKillWithin(),
"<b>Kill location for:</b> " + quest.getName());
}
/**
* Marks the given locations on the dynamic map
*
* @param locations <p>The locations to mark</p>
* @param radii <p>The radius of each location's circle</p>
* @param description <p>The description for what the location means</p>
*/
private void markLocations(List<Location> locations, List<Integer> radii, String description) {
for (int i = 0; i < locations.size(); i++) {
Location location = locations.get(i);
int radius = radii.get(i);
//Skip if location is invalid
World world = location.getWorld();
if (world == null) {
continue;
}
CircleMarker circleMarker = questAreaMarkerSet.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(0.3, 0x75AFD2);
circleMarker.setLineStyle(1, 1.0, 0x36c90e);
}
}
}
}