traitHandlers;
@@ -30,6 +41,7 @@ public final class DynmapCitizens extends JavaPlugin {
@Override
public void onEnable() {
DynmapCitizens.instance = this;
+
//Initialize quest and dynmap APIs
PluginManager pluginManager = Bukkit.getPluginManager();
Plugin dynmapPlugin = pluginManager.getPlugin("dynmap");
@@ -38,7 +50,7 @@ public final class DynmapCitizens extends JavaPlugin {
this.onDisable();
return;
}
- this.dynmapAPI = dynmapAPI;
+ this.dynmapAPIInstance = dynmapAPI;
this.globalSettings = new GlobalSettings();
FileConfiguration configuration = this.getConfig();
@@ -49,6 +61,14 @@ public final class DynmapCitizens extends JavaPlugin {
configuration = this.getConfig();
this.globalSettings.load(configuration);
+ //Load all messages
+ translator = new Translator();
+ translator.registerMessageCategory(TranslatableTimeUnit.UNIT_SECOND);
+ translator.registerMessageCategory(QuestsTranslatableMessage.QUESTS_REQUIREMENTS_FORMAT);
+ translator.registerMessageCategory(SentinelTranslatableMessage.SENTINEL_DESCRIPTION);
+ translator.loadLanguages(this.getDataFolder(), "en", "en");
+ stringFormatter = new StringFormatter(this.getDescription().getName(), translator);
+
//Initialize all enabled traits
initializeTraitHandlers(configuration);
@@ -69,6 +89,24 @@ public final class DynmapCitizens extends JavaPlugin {
//TODO: Perhaps remove icons, just in case?
}
+ /**
+ * Gets the translator to use for translation
+ *
+ * @return The translator to use
+ */
+ public static Translator getTranslator() {
+ return translator;
+ }
+
+ /**
+ * Gets the string formatter to use for formatting
+ *
+ * @return The string formatter to use
+ */
+ public static StringFormatter getFormatter() {
+ return stringFormatter;
+ }
+
/**
* Gets the global settings for this plugin
*
@@ -93,7 +131,7 @@ public final class DynmapCitizens extends JavaPlugin {
* @return A reference to the Dynmap API
*/
public DynmapAPI getDynmapAPI() {
- return this.dynmapAPI;
+ return this.dynmapAPIInstance;
}
/**
@@ -117,6 +155,7 @@ public final class DynmapCitizens extends JavaPlugin {
this.traitHandlers.add(new QuestsHandler());
this.traitHandlers.add(new SentinelHandler());
this.traitHandlers.add(new MinstrelHandler());
+ this.traitHandlers.add(new DTLTradersHandler());
//Load and initialize all enabled trait handlers
for (CitizensTraitHandler handler : this.traitHandlers) {
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/formatting/QuestsTranslatableMessage.java b/src/main/java/net/knarcraft/dynmapcitizens/formatting/QuestsTranslatableMessage.java
new file mode 100644
index 0000000..1b56b61
--- /dev/null
+++ b/src/main/java/net/knarcraft/dynmapcitizens/formatting/QuestsTranslatableMessage.java
@@ -0,0 +1,169 @@
+package net.knarcraft.dynmapcitizens.formatting;
+
+import net.knarcraft.knarlib.formatting.TranslatableMessage;
+
+/**
+ * An enum describing all of DynmapCitizens' translatable messages
+ */
+public enum QuestsTranslatableMessage implements TranslatableMessage {
+
+ /**
+ * The format for a quest's planner description
+ *
+ * Placeholders: {questCoolDown}, {questFrom}, {questUntil}, {questRepeat}
+ */
+ QUESTS_PLANNER_DESCRIPTION,
+
+ /**
+ * The format for a quest's cool-down
+ *
+ * Placeholders: {coolDown}
+ */
+ QUESTS_PLANNER_COOL_DOWN,
+
+ /**
+ * The text to display if a quest cannot be repeated
+ */
+ QUESTS_PLANNER_UNREPEATABLE,
+
+ /**
+ * The format for a quest's first availability date
+ *
+ * Placeholders: {startDate}
+ */
+ QUESTS_PLANNER_FROM,
+
+ /**
+ * The format for a quest's last availability date
+ *
+ * Placeholders: {endDate}
+ */
+ QUESTS_PLANNER_UNTIL,
+
+ /**
+ * The format for a quest's repeat delay
+ *
+ * Placeholders: {repeatDelay}
+ */
+ QUEST_PLANNER_REPEAT,
+
+ /**
+ * The format for a quest's requirements
+ *
+ * Placeholders: {requirementQuestPoints}, {requirementExp}, {requirementBlockedByQuests},
+ * {requirementRequiredQuests}, {requirementRequiredItems}, {requirementMCMMOSkills}, {requirementPermissions},
+ * {requirementCustom}
+ */
+ QUESTS_REQUIREMENTS_FORMAT,
+
+ /**
+ * The format for a quest's quest point requirement
+ *
+ * Placeholders: {questPoints}
+ */
+ QUESTS_REQUIREMENTS_QUEST_POINTS,
+
+ /**
+ * The format for a quest's exp requirement
+ *
+ * Placeholders: {exp}
+ */
+ QUESTS_REQUIREMENTS_EXP,
+
+ /**
+ * The format for a quest's blocking quests
+ *
+ * Placeholders: {blockingQuests}
+ */
+ QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_FORMAT,
+
+ /**
+ * The format for one of a quest's blocking quests
+ *
+ * Placeholders: {questName}
+ */
+ QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_ITEM,
+
+ /**
+ * The format for a quest's required quests
+ *
+ * Placeholders: {requiredQuests}
+ */
+ QUESTS_REQUIREMENTS_REQUIRED_QUEST_FORMAT,
+
+ /**
+ * The format for one of a quest's required quests
+ *
+ * Placeholders: {questName}
+ */
+ QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM,
+
+ /**
+ * The format for a quest's required items
+ *
+ * Placeholders: {requiredItems}
+ */
+ QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT,
+
+ /**
+ * The format for one of a quest's required items
+ *
+ * Placeholders: {itemName}
+ */
+ QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM,
+
+ /**
+ * The format for a quest's mcMMO skill requirement
+ *
+ * Placeholders: {skill}, {level}
+ */
+ QUESTS_REQUIREMENTS_MC_MMO_SKILL,
+
+ /**
+ * The format for a quest's required permissions
+ *
+ * Placeholders: {permissions}
+ */
+ QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT,
+
+ /**
+ * The format for one of a quest's required permissions
+ *
+ * Placeholders: {permission}
+ */
+ QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM,
+
+ /**
+ * The format for a quest's reach area name
+ *
+ * Placeholders: {name}
+ */
+ QUESTS_REACH_AREA_NAME_FORMAT,
+
+ /**
+ * The format for a quest's reach area's description
+ *
+ * Placeholders: {areaName}, {questName}
+ */
+ QUESTS_REACH_AREA_DESCRIPTION_FORMAT,
+
+ /**
+ * The format for a quest's kill area name
+ *
+ * Placeholders: {name}
+ */
+ QUESTS_KILL_AREA_NAME_FORMAT,
+
+ /**
+ * The format for a quest's kill area description
+ *
+ * Placeholders: {areaName}, {questName}, {mobName}, {mobAmount}
+ */
+ QUESTS_KILL_AREA_DESCRIPTION_FORMAT,
+ ;
+
+ @Override
+ public TranslatableMessage[] getAllMessages() {
+ return QuestsTranslatableMessage.values();
+ }
+}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/formatting/SentinelTranslatableMessage.java b/src/main/java/net/knarcraft/dynmapcitizens/formatting/SentinelTranslatableMessage.java
new file mode 100644
index 0000000..27898cc
--- /dev/null
+++ b/src/main/java/net/knarcraft/dynmapcitizens/formatting/SentinelTranslatableMessage.java
@@ -0,0 +1,31 @@
+package net.knarcraft.dynmapcitizens.formatting;
+
+import net.knarcraft.knarlib.formatting.TranslatableMessage;
+
+/**
+ * An enum describing all translatable messages for sentinels
+ */
+public enum SentinelTranslatableMessage implements TranslatableMessage {
+
+ /**
+ * The format for the basic description of any sentinel
+ *
+ * Placeholders: {name}, {squad}, {sentinelDetails}
+ */
+ SENTINEL_DESCRIPTION,
+
+ /**
+ * The format for the detailed description of any sentinel
+ *
+ * Placeholders: {invincible}, {armor}, {health}, {accuracy}, {damage}, {speed}, {allowKnockback}, {range},
+ * {reach}, {targets}, {avoids}, {ignores}
+ */
+ SENTINEL_DETAILS,
+ ;
+
+ @Override
+ public TranslatableMessage[] getAllMessages() {
+ return SentinelTranslatableMessage.values();
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/DTLTradersHandler.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/DTLTradersHandler.java
new file mode 100644
index 0000000..9657fda
--- /dev/null
+++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/DTLTradersHandler.java
@@ -0,0 +1,53 @@
+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.settings.DTLTradersSettings;
+import net.knarcraft.dynmapcitizens.settings.TraitSettings;
+import org.dynmap.markers.GenericMarker;
+
+/**
+ * A handler class for the minstrel trait
+ */
+public class DTLTradersHandler extends AbstractTraitHandler {
+
+ private final DTLTradersSettings settings = new DTLTradersSettings();
+
+ @Override
+ public void initialize() {
+ super.isEnabled = false;
+ CitizensAPI.getTraitFactory().getRegisteredTraits().forEach(traitInfo -> {
+ if (traitInfo.getTraitName().equals("trader")) {
+ super.isEnabled = true;
+ }
+ });
+
+ if (this.isEnabled) {
+ super.initializeMarkerSet();
+ }
+ }
+
+ @Override
+ public TraitSettings getSettings() {
+ return this.settings;
+ }
+
+ @Override
+ public void updateMarkers() {
+ //Remove existing markers
+ super.markerSet.getMarkers().forEach(GenericMarker::deleteMarker);
+
+ Class extends Trait> traderTrait = CitizensAPI.getTraitFactory().getTraitClass("trader");
+ for (NPC npc : CitizensAPI.getNPCRegistry()) {
+ if (npc.hasTrait(traderTrait)) {
+ String description = "" + npc.getName() + "
";
+ addNPCMarker(npc.getUniqueId(), "Trader NPC: ", description,
+ DynmapCitizens.getInstance().getGlobalSettings().getMarkerIcons().get(Icon.TRADER), super.markerSet);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java
index 28a7993..dc0938e 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java
@@ -47,12 +47,14 @@ public class SentinelHandler extends AbstractTraitHandler {
description += "
Squad: " + trait.squad;
}
if (settings.displaySentinelStats()) {
- description += "
Invincible: " + trait.invincible + "
Armor: " +
- trait.armor + "
Health: " + trait.health + "
Accuracy: " + trait.accuracy +
- "
Damage: " + trait.damage + "
Allow knockback: " + trait.allowKnockback;
+ description += "
Invincible: " + trait.invincible + "
Armor: " + trait.armor;
+ description += "
Health: " + trait.health + "
Accuracy: " + trait.accuracy;
+ description += "
Damage: " + trait.damage + "
Speed: " + trait.speed;
+ description += "
Allow knockback: " + trait.allowKnockback;
description += "
Range: " + trait.range + "
Reach: " + trait.reach;
- description += "
Targets: " + trait.allTargets.toAllInOneString() + "
Avoids: " +
- trait.allAvoids.toAllInOneString() + "
Ignores: " + trait.allIgnores.toAllInOneString();
+ description += "
Targets: " + trait.allTargets.toAllInOneString();
+ description += "
Avoids: " + trait.allAvoids.toAllInOneString();
+ description += "
Ignores: " + trait.allIgnores.toAllInOneString();
}
addNPCMarker(npc.getUniqueId(), "Sentinel NPC: ", description,
DynmapCitizens.getInstance().getGlobalSettings().getMarkerIcons().get(Icon.SENTINEL), super.markerSet);
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
index 25cf000..adf80b8 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestAreaHandler.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestAreaHandler.java
@@ -3,9 +3,13 @@ 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.DynmapCitizens;
import net.knarcraft.dynmapcitizens.settings.QuestsSettings;
import net.knarcraft.dynmapcitizens.util.DynmapHelper;
import net.knarcraft.dynmapcitizens.util.QuestsHelper;
+import net.knarcraft.knarlib.formatting.StringFormatter;
+import net.knarcraft.knarlib.formatting.StringReplacer;
+import net.knarcraft.knarlib.formatting.Translator;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.dynmap.DynmapAPI;
@@ -14,6 +18,11 @@ import org.dynmap.markers.MarkerSet;
import java.util.List;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_KILL_AREA_DESCRIPTION_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_KILL_AREA_NAME_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REACH_AREA_DESCRIPTION_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REACH_AREA_NAME_FORMAT;
+
/**
* A handler class for quest areas
*/
@@ -24,6 +33,7 @@ public class QuestAreaHandler {
private final MarkerSet reachAreaMarkerSet;
private final QuestsSettings settings;
private final List unavailableQuests;
+ private final StringFormatter formatter;
/**
* Instantiates a new quest area handler
@@ -38,6 +48,7 @@ public class QuestAreaHandler {
this.questsAPI = questsAPI;
this.settings = settings;
this.unavailableQuests = unavailableQuests;
+ this.formatter = DynmapCitizens.getFormatter();
killAreaMarkerSet = DynmapHelper.initializeMarkerSet(dynmapAPI, settings.getKillAreaSettings());
reachAreaMarkerSet = DynmapHelper.initializeMarkerSet(dynmapAPI, settings.getReachAreaSettings());
}
@@ -75,11 +86,16 @@ public class QuestAreaHandler {
Location location = stage.getLocationsToReach().get(i);
int radius = stage.getRadiiToReachWithin().get(i);
String areaName = stage.getLocationNames().get(i);
- String description = "";
+
+ String formattedAreaName;
if (areaName != null) {
- description += "" + areaName + "
";
+ formattedAreaName = formatter.replacePlaceholder(QUESTS_REACH_AREA_NAME_FORMAT, "{name}", areaName);
+ } else {
+ formattedAreaName = "";
}
- description += "Target location for " + quest.getName();
+
+ String description = formatter.replacePlaceholders(QUESTS_REACH_AREA_DESCRIPTION_FORMAT,
+ new String[]{"{areaName}", "{questName}"}, new String[]{formattedAreaName, quest.getName()});
DynmapHelper.markLocation(location, radius, description, reachAreaMarkerSet, settings.getReachAreaSettings());
}
}
@@ -101,13 +117,20 @@ public class QuestAreaHandler {
int mobAmount = stage.getMobNumToKill().get(i);
String areaName = stage.getKillNames().get(i);
- String description = "";
+ String formattedAreaName;
if (areaName != null) {
- description += "" + areaName + "
";
+ formattedAreaName = formatter.replacePlaceholder(QUESTS_KILL_AREA_NAME_FORMAT, "{name}", areaName);
+ } else {
+ formattedAreaName = "";
}
- description += "Kill location for " + quest.getName() +
- "
Kill " + QuestsHelper.normalizeName(mob.name()) + " x " + mobAmount;
- DynmapHelper.markLocation(location, radius, description, killAreaMarkerSet, settings.getKillAreaSettings());
+
+ Translator translator = DynmapCitizens.getTranslator();
+ StringReplacer replacer = new StringReplacer(translator.getTranslatedMessage(QUESTS_KILL_AREA_DESCRIPTION_FORMAT));
+ replacer.add("{areaName}", formattedAreaName);
+ replacer.add("{questName}", quest.getName());
+ replacer.add("{mobName}", QuestsHelper.normalizeName(mob.name()));
+ replacer.add("{mobAmount}", String.valueOf(mobAmount));
+ DynmapHelper.markLocation(location, radius, replacer.replace(), killAreaMarkerSet, settings.getKillAreaSettings());
}
}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestPlannerInfoGenerator.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestPlannerInfoGenerator.java
index 3d6d6e5..d3bce69 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestPlannerInfoGenerator.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestPlannerInfoGenerator.java
@@ -2,7 +2,12 @@ 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;
+import net.knarcraft.dynmapcitizens.DynmapCitizens;
+import net.knarcraft.knarlib.formatting.TimeFormatter;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
/**
* A class to generate a string containing all information about a quest's planner info
@@ -33,7 +38,8 @@ public class QuestPlannerInfoGenerator {
//Quest can be repeated after a cool-down
if (planner.hasCooldown()) {
plannerInfo.append("Quest repeatable after: ");
- plannerInfo.append(TimeFormatter.getDurationString(planner.getCooldown() / 1000));
+ plannerInfo.append(TimeFormatter.getDurationString(DynmapCitizens.getTranslator(),
+ planner.getCooldown() / 1000));
plannerInfo.append("");
} else {
plannerInfo.append("Quest cannot be repeated!");
@@ -42,23 +48,36 @@ public class QuestPlannerInfoGenerator {
//Quest only becomes available after the start date
if (planner.hasStart()) {
plannerInfo.append("Quest available from ");
- plannerInfo.append(TimeFormatter.formatTimestamp(planner.getStartInMillis())).append("");
+ plannerInfo.append(formatTimestamp(planner.getStartInMillis())).append("");
}
//Quest is only available until the end date
if (planner.hasEnd()) {
plannerInfo.append("Quest available until ");
- plannerInfo.append(TimeFormatter.formatTimestamp(planner.getEndInMillis())).append("");
+ plannerInfo.append(formatTimestamp(planner.getEndInMillis())).append("");
}
//Quest availability repeats
if (planner.hasRepeat()) {
plannerInfo.append("Quest will become available again after ");
- plannerInfo.append(TimeFormatter.getDurationString(planner.getRepeat() / 1000)).append("");
+ plannerInfo.append(TimeFormatter.getDurationString(DynmapCitizens.getTranslator(),
+ planner.getRepeat() / 1000)).append("");
}
plannerInfo.append("");
return plannerInfo.toString();
}
+ /**
+ * Gets a datetime string for the given timestamp
+ *
+ * @param timestamp A timestamp in milliseconds
+ * @return A datetime string
+ */
+ private String formatTimestamp(long timestamp) {
+ DateFormat format = new SimpleDateFormat("dd MM yyyy HH:mm:ss");
+ Date date = new Date(timestamp);
+ return format.format(date);
+ }
+
}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestRequirementsInfoGenerator.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestRequirementsInfoGenerator.java
index 426b2ff..03f2dc0 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestRequirementsInfoGenerator.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestRequirementsInfoGenerator.java
@@ -1,27 +1,51 @@
package net.knarcraft.dynmapcitizens.handler.trait.quests;
+import me.blackvein.quests.QuestsAPI;
import me.blackvein.quests.quests.IQuest;
import me.blackvein.quests.quests.Requirements;
+import net.knarcraft.dynmapcitizens.DynmapCitizens;
import net.knarcraft.dynmapcitizens.util.QuestsHelper;
+import net.knarcraft.knarlib.formatting.StringFormatter;
+import net.knarcraft.knarlib.formatting.StringReplacer;
+import net.knarcraft.knarlib.formatting.TranslatableMessage;
import org.bukkit.inventory.ItemStack;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_ITEM;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_EXP;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_MC_MMO_SKILL;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_QUEST_POINTS;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_REQUIRED_QUEST_FORMAT;
+import static net.knarcraft.dynmapcitizens.formatting.QuestsTranslatableMessage.QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM;
+
/**
* A class to generate a string containing all information about a quest's requirements
*/
public class QuestRequirementsInfoGenerator {
+ private final QuestsAPI questsAPI;
private final IQuest quest;
+ private final StringFormatter formatter;
/**
* Instantiates a new quest requirement info generator
*
- * @param quest The quest to generate information about
+ * @param questsAPI The API to use for getting quest information
+ * @param quest The quest to generate information about
*/
- public QuestRequirementsInfoGenerator(IQuest quest) {
+ public QuestRequirementsInfoGenerator(QuestsAPI questsAPI, IQuest quest) {
+ this.questsAPI = questsAPI;
this.quest = quest;
+ formatter = DynmapCitizens.getFormatter();
}
/**
@@ -31,78 +55,140 @@ public class QuestRequirementsInfoGenerator {
*/
public String getQuestRequirementsInfo() {
Requirements requirements = quest.getRequirements();
- StringBuilder requirementInfo = new StringBuilder();
if (!requirements.hasRequirement()) {
- return requirementInfo.toString();
+ return "";
}
- requirementInfo.append("Requirements: ");
+ StringReplacer replacer = new StringReplacer(DynmapCitizens.getTranslator().getTranslatedMessage(
+ QUESTS_REQUIREMENTS_FORMAT));
- if (requirements.getQuestPoints() > 0) {
- requirementInfo.append("- ").append(requirements.getQuestPoints()).append(" quest points
");
- }
+ //Add info about quest point requirement
+ replacer.add("{requirementQuestPoints}", requirements.getQuestPoints() > 0 ?
+ formatter.replacePlaceholder(QUESTS_REQUIREMENTS_QUEST_POINTS,
+ "{questPoints}", String.valueOf(requirements.getQuestPoints())) : "");
- if (requirements.getExp() > 0) {
- requirementInfo.append("- ").append(requirements.getExp()).append(" exp
");
- }
+ //Add info about exp requirement
+ replacer.add("{requirementExp}", requirements.getExp() > 0 ? formatter.replacePlaceholder(
+ QUESTS_REQUIREMENTS_EXP, "{exp}", String.valueOf(requirements.getExp())) : "");
- if (!requirements.getBlockQuests().isEmpty()) {
- requirementInfo.append("- Blocked by quests:
");
- for (IQuest blockQuest : requirements.getBlockQuests()) {
- requirementInfo.append("- ").append(blockQuest.getName()).append("
");
- }
- requirementInfo.append("
");
- }
+ //Add info about blocking quests
+ replacer.add("{requirementBlockedByQuests}", !requirements.getBlockQuestIds().isEmpty() ?
+ getRequirementList(getQuestNames(requirements.getBlockQuestIds()),
+ QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_FORMAT, "{blockingQuests}",
+ QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_ITEM, "{questName}") : "");
- if (!requirements.getNeededQuests().isEmpty()) {
- requirementInfo.append("- Required quests:
");
- for (IQuest neededQuest : requirements.getNeededQuests()) {
- requirementInfo.append("- ").append(neededQuest.getName()).append("
");
- }
- requirementInfo.append("
");
- }
+ //Add info about required quests
+ replacer.add("{requirementRequiredQuests}", !requirements.getBlockQuestIds().isEmpty() ?
+ getRequirementList(getQuestNames(requirements.getBlockQuestIds()),
+ QUESTS_REQUIREMENTS_REQUIRED_QUEST_FORMAT, "{requiredQuests}",
+ QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM, "{questName}") : "");
- if (!requirements.getItems().isEmpty()) {
- requirementInfo.append("- Required items:
");
- for (ItemStack item : requirements.getItems()) {
- requirementInfo.append("- ").append(QuestsHelper.getUpperCasedItemStackString(item)).append("
");
- }
- requirementInfo.append("
");
- }
+ //Add info about required items
+ replacer.add("{requirementRequiredItems}", !requirements.getItems().isEmpty() ?
+ getRequirementList(getItemNames(requirements.getItems()), QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT,
+ "{requiredItems}", QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM, "{itemName}") : "");
+ //Add info about required mcMMO skills
if (!requirements.getMcmmoSkills().isEmpty()) {
List skills = requirements.getMcmmoSkills();
List amounts = requirements.getMcmmoAmounts();
+ StringBuilder mcMMOSkillsBuilder = new StringBuilder();
for (int i = 0; i < skills.size(); i++) {
- requirementInfo.append("- Requires mcMMO skill ").append(skills.get(i)).append(" at level ");
- requirementInfo.append(amounts.get(i)).append("
");
+ mcMMOSkillsBuilder.append(formatter.replacePlaceholders(QUESTS_REQUIREMENTS_MC_MMO_SKILL, new String[]{
+ "{skill}", "{level}"}, new String[]{skills.get(i), String.valueOf(amounts.get(i))}));
}
+ replacer.add("{requirementMCMMOSkills}", mcMMOSkillsBuilder.toString());
+ } else {
+ replacer.add("{requirementMCMMOSkills}", "");
}
- if (!requirements.getPermissions().isEmpty()) {
- requirementInfo.append("- Required permissions:
");
- for (String permission : requirements.getPermissions()) {
- requirementInfo.append("- ").append(permission).append("
");
- }
- requirementInfo.append("
");
- }
+ //Add info about required permissions
+ replacer.add("{requirementPermissions}", !requirements.getPermissions().isEmpty() ?
+ getRequirementList(requirements.getPermissions(), QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT,
+ "{permissions}", QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM, "{permission}") : "");
Map> customRequirementPlugins = requirements.getCustomRequirements();
+ StringBuilder customRequirementsBuilder = new StringBuilder();
for (String plugin : customRequirementPlugins.keySet()) {
- requirementInfo.append("- ").append(plugin).append(":
");
+ customRequirementsBuilder.append("- ").append(plugin).append(":
");
//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 customRequirementEntry = customRequirementPlugins.get(plugin);
for (String requirementDescription : customRequirementEntry.keySet()) {
- requirementInfo.append("- ").append(requirementDescription).append(" ");
- requirementInfo.append(customRequirementEntry.get(requirementDescription)).append("
");
+ customRequirementsBuilder.append("- ").append(requirementDescription).append(" ");
+ customRequirementsBuilder.append(customRequirementEntry.get(requirementDescription)).append("
");
}
- requirementInfo.append("
");
+ customRequirementsBuilder.append("
");
}
+ replacer.add("{requirementCustom}", customRequirementsBuilder.toString());
+ return replacer.replace();
+ }
- requirementInfo.append("
");
- return requirementInfo.toString();
+ /**
+ * Gets a list of item names from the given list of items
+ *
+ * @param items The items to get the names of
+ * @return The names of the given items
+ */
+ private List getItemNames(List items) {
+ List itemNames = new ArrayList<>();
+ for (ItemStack itemStack : items) {
+ itemNames.add(QuestsHelper.getUpperCasedItemStackString(itemStack));
+ }
+ return itemNames;
+ }
+
+ /**
+ * Gets a list of the quest names for the given quests
+ *
+ * @param questIds The quests to get names for
+ * @return A list of quest names
+ */
+ private List getQuestNames(List questIds) {
+ List questNames = new ArrayList<>(questIds.size());
+ for (String questId : questIds) {
+ IQuest quest = getQuest(questId);
+ if (quest != null) {
+ questNames.add(quest.getName());
+ }
+ }
+ return questNames;
+ }
+
+ /**
+ * Gets the quest with the given id
+ *
+ * @param questId The id of the quest to get
+ * @return The quest, or null if not found
+ */
+ private IQuest getQuest(String questId) {
+ for (IQuest quest : questsAPI.getLoadedQuests()) {
+ if (quest.getId().equals(questId)) {
+ return quest;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a string for the given list of requirements
+ *
+ * @param itemList The items to display in the list of requirements
+ * @param formatMessage The translatable message describing the list format
+ * @param formatPlaceholder The placeholder to replace with the list items
+ * @param itemMessage The translatable message describing each item's format
+ * @param itemPlaceholder The placeholder to replace with each item in the list
+ * @return The string corresponding to the given requirement list
+ */
+ private String getRequirementList(List itemList, TranslatableMessage formatMessage, String formatPlaceholder,
+ TranslatableMessage itemMessage, String itemPlaceholder) {
+ StringBuilder blockedBuilder = new StringBuilder();
+ for (Object requirements : itemList) {
+ blockedBuilder.append(formatter.replacePlaceholder(itemMessage, itemPlaceholder,
+ String.valueOf(requirements)));
+ }
+ return formatter.replacePlaceholder(formatMessage, formatPlaceholder, blockedBuilder.toString());
}
}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestStagesInfoGenerator.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestStagesInfoGenerator.java
index 3b9d25a..f91575d 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestStagesInfoGenerator.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/quests/QuestStagesInfoGenerator.java
@@ -94,6 +94,9 @@ public class QuestStagesInfoGenerator {
for (UUID npcId : stage.getNpcsToKill()) {
questInfo.append("Kill NPC ").append(registry.getByUniqueId(npcId).getName()).append("");
}
+ for (UUID npcId : stage.getNpcsToInteract()) {
+ questInfo.append("Talk to ").append(registry.getByUniqueId(npcId).getName()).append("");
+ }
questInfo.append(getQuestItemsTaskString(stage.getBlocksToBreak(), "Break ")).append("");
questInfo.append(getQuestItemsTaskString(stage.getBlocksToCut(), "Cut ")).append("");
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 3b928a0..b68cf7c 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
@@ -163,7 +163,7 @@ public class QuestsHandler extends AbstractTraitHandler {
stringBuilder.append(new QuestRewardsInfoGenerator(quest).getQuestRewardsInfo());
}
if (settings.displayRequirementInfo()) {
- stringBuilder.append(new QuestRequirementsInfoGenerator(quest).getQuestRequirementsInfo());
+ stringBuilder.append(new QuestRequirementsInfoGenerator(questsAPI, quest).getQuestRequirementsInfo());
}
if (settings.displayPlannerInfo()) {
stringBuilder.append(new QuestPlannerInfoGenerator(quest).getQuestPlannerInfo());
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/property/Icon.java b/src/main/java/net/knarcraft/dynmapcitizens/property/Icon.java
index b0b5ee0..5f2f949 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/property/Icon.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/property/Icon.java
@@ -43,6 +43,11 @@ public enum Icon {
/**
* An icon representing a minstrel NPC
*/
- MINSTREL
+ MINSTREL,
+
+ /**
+ * An icon representing a trader NPC
+ */
+ TRADER,
}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/settings/DTLTradersSettings.java b/src/main/java/net/knarcraft/dynmapcitizens/settings/DTLTradersSettings.java
new file mode 100644
index 0000000..80b3593
--- /dev/null
+++ b/src/main/java/net/knarcraft/dynmapcitizens/settings/DTLTradersSettings.java
@@ -0,0 +1,20 @@
+package net.knarcraft.dynmapcitizens.settings;
+
+import org.bukkit.configuration.file.FileConfiguration;
+
+/**
+ * All settings for the minstrel trait
+ */
+public class DTLTradersSettings extends AbstractTraitSettings {
+
+ @Override
+ public void load(FileConfiguration configuration) {
+ super.load(configuration);
+ }
+
+ @Override
+ protected String getTraitConfigRoot() {
+ return "traits.trader";
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/settings/GlobalSettings.java b/src/main/java/net/knarcraft/dynmapcitizens/settings/GlobalSettings.java
index a7fe753..47f70bd 100644
--- a/src/main/java/net/knarcraft/dynmapcitizens/settings/GlobalSettings.java
+++ b/src/main/java/net/knarcraft/dynmapcitizens/settings/GlobalSettings.java
@@ -82,6 +82,7 @@ public class GlobalSettings {
case BLACKSMITH -> "hammer";
case SENTINEL -> "shield";
case MINSTREL -> "theater";
+ case TRADER -> "coins";
};
}
diff --git a/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java b/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java
deleted file mode 100644
index 5ebc8ba..0000000
--- a/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package net.knarcraft.dynmapcitizens.util;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder;
-
-/**
- * A helper class for time formatting
- */
-public class TimeFormatter {
-
- /**
- * Gets a datetime string for the given timestamp
- *
- * @param timestamp A timestamp in milliseconds
- * @return A datetime string
- */
- public static String formatTimestamp(long timestamp) {
- DateFormat format = new SimpleDateFormat("dd MM yyyy HH:mm:ss");
- Date date = new Date(timestamp);
- return format.format(date);
- }
-
- /**
- * Gets the string used for displaying this sign's duration
- *
- * @return The string used for displaying this sign's duration
- */
- public static String getDurationString(long duration) {
- if (duration == 0) {
- return "immediately";
- } else {
- double minute = 60;
- double hour = minute * 60;
- double day = hour * 24;
- double week = day * 7;
- double month = day * 30;
- double year = day * 365;
- double decade = year * 10;
-
- Map timeUnits = new HashMap<>();
- timeUnits.put(decade, new String[]{"decade", "decades"});
- timeUnits.put(year, new String[]{"year", "years"});
- timeUnits.put(month, new String[]{"month", "months"});
- timeUnits.put(week, new String[]{"week", "weeks"});
- timeUnits.put(day, new String[]{"day", "days"});
- timeUnits.put(hour, new String[]{"hour", "hours"});
- timeUnits.put(minute, new String[]{"minute", "minutes"});
- timeUnits.put(1D, new String[]{"second", "seconds"});
-
- List sortedUnits = new ArrayList<>(timeUnits.keySet());
- Collections.sort(sortedUnits);
- Collections.reverse(sortedUnits);
-
- for (Double unit : sortedUnits) {
- if (duration / unit >= 1) {
- double units = round(duration / unit);
- return formatDurationString(units, timeUnits.get(unit)[units == 1 ? 0 : 1],
- (units * 10) % 10 == 0);
- }
- }
- return formatDurationString(duration, "seconds", false);
- }
- }
-
- /**
- * Rounds a number to its last two digits
- *
- * @param number The number to round
- * @return The rounded number
- */
- private static double round(double number) {
- return Math.round(number * 100.0) / 100.0;
- }
-
- /**
- * Formats a duration string
- *
- * @param duration The duration to display
- * @param translatableMessage The time unit to display
- * @param castToInt Whether to cast the duration to an int
- * @return The formatted duration string
- */
- private static String formatDurationString(double duration, String translatableMessage, boolean castToInt) {
- String durationFormat = "{duration} {unit}";
- durationFormat = replacePlaceholder(durationFormat, "{unit}", translatableMessage);
- return replacePlaceholder(durationFormat, "{duration}", castToInt ? String.valueOf((int) duration) :
- String.valueOf(duration));
- }
-
-}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 9dd6bdc..95ef02b 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -16,6 +16,8 @@ icon:
SENTINEL: "shield"
# The marker used for minstrels
MINSTREL: "theater"
+ # The marker used for traders
+ TRADER: "coins"
# Settings for how often markers will be updated
timer:
@@ -129,4 +131,15 @@ traits:
# Whether to hide the minstrel icon layer by default
markersHiddenByDefault: false
# Whether to display the list of songs a minstrel is playing
- displayMinstrelSongs: true
\ No newline at end of file
+ displayMinstrelSongs: true
+ # Settings for the trader trait
+ trader:
+ enabled: true
+ # The priority of trader markers. Higher priority markers will display on top of lower priority ones
+ markerSetPriority: 1
+ # The id of the trader marker set. Change if it overlaps with an existing set id
+ markerSetId: "traders"
+ # The name of the trader marker set. Change it if you want a cooler name
+ markerSetName: "Traders"
+ # Whether to hide the trader icon layer by default
+ markersHiddenByDefault: false
\ No newline at end of file
diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml
new file mode 100644
index 0000000..21803ac
--- /dev/null
+++ b/src/main/resources/strings.yml
@@ -0,0 +1,76 @@
+en:
+ SENTINEL_DESCRIPTION: |
+ {name}
+
Squad: {squad}
+ {sentinelDetails}
+ SENTINEL_DETAILS: |
+
+ - Invincible: {invincible}
+ - Armor: {armor}
+ - Health: {health}
+ - Accuracy: {accuracy}
+ - Damage: {damage}
+ - Speed: {speed}
+ - Allow knockback: {allowKnockback}
+ - Range: {range}
+ - Reach: {reach}
+ - Targets: {targets}
+ - Avoids: {avoids}
+ - Ignores: {ignores}
+
+ QUESTS_PLANNER_DESCRIPTION: |
+ Planner:
+ {questCoolDown}
+ {questFrom}
+ {questUntil}
+ {questRepeat}
+
+ QUESTS_PLANNER_COOL_DOWN: "Quest repeatable after: {coolDown}"
+ QUESTS_PLANNER_UNREPEATABLE: "Quest cannot be repeated!"
+ QUESTS_PLANNER_FROM: "Quest available from {startDate}"
+ QUESTS_PLANNER_UNTIL: "Quest available until {endDate}"
+ QUEST_PLANNER_REPEAT: "Quest will become available again after {repeatDelay}"
+ QUESTS_REQUIREMENTS_FORMAT: |
+ Requirements:
+ {requirementQuestPoints}
+ {requirementExp}
+ {requirementBlockedByQuests}
+ {requirementRequiredQuests}
+ {requirementRequiredItems}
+ {requirementMCMMOSkills}
+ {requirementPermissions}
+ {requirementCustom}
+
+ QUESTS_REQUIREMENTS_QUEST_POINTS: "{questPoints} quest points"
+ QUESTS_REQUIREMENTS_EXP: "{exp} exp"
+ QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_FORMAT: "Blocked by quests:"
+ QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_ITEM: "{questName}"
+ QUESTS_REQUIREMENTS_REQUIRED_QUEST_FORMAT: "Required quests:"
+ QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM: "{questName}"
+ QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT: "Required items:"
+ QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM: "{itemName}"
+ QUESTS_REQUIREMENTS_MC_MMO_SKILL: "Requires mcMMO skill {skill} at level {level}"
+ QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT: "Required permissions:"
+ QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM: "{permission}"
+ QUESTS_REACH_AREA_NAME_FORMAT: "{name}
"
+ QUESTS_REACH_AREA_DESCRIPTION_FORMAT: "{areaName}Target location for {questName}"
+ QUESTS_KILL_AREA_NAME_FORMAT: "{name}
"
+ QUESTS_KILL_AREA_DESCRIPTION_FORMAT: "{areaName}Kill location for {questName}
Kill {mobName} x {mobAmount}"
+ DURATION_FORMAT: "in {time} {unit}"
+ UNIT_NOW: "imminently"
+ UNIT_SECOND: "second"
+ UNIT_SECONDS: "seconds"
+ UNIT_MINUTE: "minute"
+ UNIT_MINUTES: "minutes"
+ UNIT_HOUR: "hour"
+ UNIT_HOURS: "hours"
+ UNIT_DAY: "day"
+ UNIT_DAYS: "days"
+ UNIT_WEEK: "week"
+ UNIT_WEEKS: "weeks"
+ UNIT_MONTH: "month"
+ UNIT_MONTHS: "months"
+ UNIT_YEAR: "year"
+ UNIT_YEARS: "years"
+ UNIT_DECADE: "decade"
+ UNIT_DECADES: "decades"
\ No newline at end of file