set) {
- this.regenStat = StatType.valueOf(name() + "_REGENERATION");
- this.maxRegenStat = StatType.valueOf("MAX_" + name() + "_REGENERATION");
+ this.regenStat = name() + "_REGENERATION";
+ this.maxRegenStat = "MAX_" + name() + "_REGENERATION";
this.offCombatRegen = ClassOption.valueOf("OFF_COMBAT_" + name() + "_REGEN");
this.current = current;
this.max = max;
@@ -68,14 +67,14 @@ public enum PlayerResource {
/**
* @return Stat which corresponds to flat resource regeneration
*/
- public StatType getRegenStat() {
+ public String getRegenStat() {
return regenStat;
}
/**
* @return Stat which corresponds to resource regeneration scaling with the player's max health
*/
- public StatType getMaxRegenStat() {
+ public String getMaxRegenStat() {
return maxRegenStat;
}
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java b/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java
index be0912c7..0682683c 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java
@@ -8,17 +8,14 @@ import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.modifier.ModifierType;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.Profession;
+import net.Indyuce.mmocore.player.stats.StatInfo;
import net.Indyuce.mmocore.skill.ClassSkill;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Locale;
public class PlayerStats {
private final PlayerData data;
/**
- * Utilclass to easily manipulate the MMOLib stat map
+ * Util class to easily manipulate the MMOLib stat map
*
* @param data Playerdata
*/
@@ -34,6 +31,7 @@ public class PlayerStats {
return data.getMMOPlayerData().getStatMap();
}
+ @Deprecated
public StatInstance getInstance(StatType stat) {
return getMap().getInstance(stat.name());
}
@@ -42,35 +40,26 @@ public class PlayerStats {
return getMap().getInstance(stat);
}
- /**
- * Allows for stat type enum to have dynamic professions.
- * ID FORMAT: STAT_TYPE_HERE_PROFESSION_HERE
- *
- * @param type the type of stat
- * @param profession the stat's specific permission
- * @return instance of found stat
- * @author Ehhthan
- */
- @NotNull
- public StatInstance getInstance(StatType type, @Nullable Profession profession) {
- if (profession == null)
- return getInstance(type);
- else {
- String id = (type.name() + '_' + profession.getId()).replace('-', '_').replace(' ', '_').toUpperCase(Locale.ROOT);
- return getInstance(id);
- }
- }
-
- /*
- * applies relative attributes on the base stat too
- */
- public double getStat(StatType stat) {
+ public double getStat(String stat) {
return getInstance(stat).getTotal();
}
- public double getBase(StatType stat) {
- return data.getProfess().calculateStat(stat,
- stat.hasProfession() ? data.getCollectionSkills().getLevel(stat.getProfession()) : data.getLevel());
+ /**
+ * MMOCore base stat value differs from the on in MythicLib.
+ *
+ * MythicLib: the base stat value is only defined for stats
+ * which are based on vanilla player attributes. It corresponds
+ * to the stat amount any player has with NO attribute modifier whatsoever.
+ *
+ * MMOCore: the base stat value corresponds to the stat amount
+ * the player CLASS grants. It can be similar or equal to the one
+ * in MMOCore but it really is completely different.
+ *
+ * @return MMOCore base stat value
+ */
+ public double getBase(String stat) {
+ Profession profession = StatInfo.valueOf(stat).profession;
+ return data.getProfess().calculateStat(stat, profession == null ? data.getLevel() : data.getCollectionSkills().getLevel(profession));
}
/**
@@ -81,7 +70,7 @@ public class PlayerStats {
* see {@link PlayerData#update()} for more info
*/
public synchronized void updateStats() {
- for (StatType stat : StatType.values()) {
+ for (StatType stat : StatType.values()) { // TODO fix
StatInstance instance = getMap().getInstance(stat.name());
StatInstance.ModifierPacket packet = instance.newPacket();
@@ -89,7 +78,7 @@ public class PlayerStats {
packet.removeIf(str -> str.equals("mmocoreClass"));
// Add newest one
- double total = getBase(stat) - instance.getBase();
+ double total = getBase(stat.name()) - instance.getBase();
if (total != 0)
packet.addModifier(new StatModifier("mmocoreClass", stat.name(), total, ModifierType.FLAT, EquipmentSlot.OTHER, ModifierSource.OTHER));
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java b/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java
index cfc69000..6adbde42 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java
@@ -1,14 +1,14 @@
package net.Indyuce.mmocore.api.player.stats;
-import io.lumine.mythic.lib.MythicLib;
-import net.Indyuce.mmocore.MMOCore;
-import net.Indyuce.mmocore.api.ConfigFile;
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
import net.Indyuce.mmocore.experience.Profession;
-import org.bukkit.configuration.file.FileConfiguration;
+import net.Indyuce.mmocore.player.stats.StatInfo;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
-import java.text.DecimalFormat;
+import java.util.Objects;
+@Deprecated
public enum StatType {
// Vanilla stats
@@ -61,6 +61,7 @@ public enum StatType {
// Utility
ADDITIONAL_EXPERIENCE,
COOLDOWN_REDUCTION,
+ CHANCE,
// Damage-type based stats
MAGIC_DAMAGE,
@@ -86,17 +87,17 @@ public enum StatType {
/**
* Reduces amount of tugs needed to fish
*/
- FISHING_STRENGTH("fishing"),
+ FISHING_STRENGTH,
/**
* Chance of instant success when fishing
*/
- CRITICAL_FISHING_CHANCE("fishing"),
+ CRITICAL_FISHING_CHANCE,
/**
* Chance of crit fishing failure
*/
- CRITICAL_FISHING_FAILURE_CHANCE("fishing"),
+ CRITICAL_FISHING_FAILURE_CHANCE,
/**
* Chance of dropping more minerals when mining.
@@ -113,49 +114,35 @@ public enum StatType {
*/
LUCK_OF_THE_FIELD;
- private String profession;
-
- private LinearValue defaultInfo;
- private DecimalFormat format;
-
- StatType() {
- // Completely custom stat
- }
-
- @SuppressWarnings("SameParameterValue")
- StatType(String profession) {
- this.profession = profession;
- }
-
+ @Deprecated
public String getProfession() {
- return profession;
+ return findProfession().getId();
}
+ @Deprecated
+ @Nullable
public Profession findProfession() {
- return MMOCore.plugin.professionManager.get(profession);
+ return StatInfo.valueOf(name()).profession;
}
+ @Deprecated
public boolean hasProfession() {
- return profession != null;
+ return findProfession() != null;
}
+ @Deprecated
+ @NotNull
public LinearValue getDefault() {
- return defaultInfo;
+ return StatInfo.valueOf(name()).getDefaultFormula();
}
+ @Deprecated
public boolean matches(Profession profession) {
- return this.profession != null && this.profession.equals(profession.getId());
+ return Objects.equals(findProfession(), profession);
}
+ @Deprecated
public String format(double value) {
- return format.format(value);
- }
-
- public static void load() {
- FileConfiguration config = new ConfigFile("stats").getConfig();
- for (StatType stat : values()) {
- stat.defaultInfo = config.contains("default." + stat.name()) ? new LinearValue(config.getConfigurationSection("default." + stat.name())) : new LinearValue(0, 0);
- stat.format = MythicLib.plugin.getMMOConfig().newDecimalFormat(config.contains("decimal-format." + stat.name()) ? config.getString("decimal-format." + stat.name()) : "0.#");
- }
+ return StatInfo.valueOf(name()).format(value);
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/api/quest/trigger/ExperienceTrigger.java b/src/main/java/net/Indyuce/mmocore/api/quest/trigger/ExperienceTrigger.java
index 4f84729d..a7181940 100644
--- a/src/main/java/net/Indyuce/mmocore/api/quest/trigger/ExperienceTrigger.java
+++ b/src/main/java/net/Indyuce/mmocore/api/quest/trigger/ExperienceTrigger.java
@@ -1,6 +1,7 @@
package net.Indyuce.mmocore.api.quest.trigger;
-import net.Indyuce.mmocore.experience.ExperienceObject;
+import net.Indyuce.mmocore.experience.SimpleExperienceObject;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import org.apache.commons.lang.Validate;
import net.Indyuce.mmocore.MMOCore;
@@ -9,17 +10,14 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.math.formula.RandomAmount;
import io.lumine.mythic.lib.api.MMOLineConfig;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Objects;
public class ExperienceTrigger extends Trigger {
@NotNull
private final RandomAmount amount;
@NotNull
private final EXPSource source;
- @Nullable
- private final ExperienceObject expObject;
+ @NotNull
+ private final ExperienceDispenser dispenser;
public ExperienceTrigger(MMOLineConfig config) {
super(config);
@@ -29,15 +27,15 @@ public class ExperienceTrigger extends Trigger {
if (config.contains("profession")) {
String id = config.getString("profession").toLowerCase().replace("_", "-");
Validate.isTrue(MMOCore.plugin.professionManager.has(id), "Could not find profession");
- expObject = MMOCore.plugin.professionManager.get(id);
+ dispenser = MMOCore.plugin.professionManager.get(id);
} else
- expObject = null;
+ dispenser = new SimpleExperienceObject();
amount = new RandomAmount(config.getString("amount"));
source = config.contains("source") ? EXPSource.valueOf(config.getString("source").toUpperCase()) : EXPSource.QUEST;
}
@Override
public void apply(PlayerData player) {
- Objects.requireNonNullElse(expObject, player.getProfess()).giveExperience(player, amount.calculateInt(), null, source);
+ dispenser.giveExperience(player, amount.calculateInt(), null, source);
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java b/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java
index b15bb60a..64a5744a 100644
--- a/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java
+++ b/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java
@@ -196,7 +196,7 @@ public class MMOCoreUtils {
*/
public static void decreaseDurability(Player player, EquipmentSlot slot, int damage) {
ItemStack item = player.getInventory().getItem(slot);
- if (!item.hasItemMeta() || !(item.getItemMeta() instanceof Damageable) || item.getItemMeta().isUnbreakable())
+ if (item == null || item.getType().getMaxDurability() == 0 || !item.hasItemMeta() || !(item.getItemMeta() instanceof Damageable) || item.getItemMeta().isUnbreakable())
return;
PlayerItemDamageEvent event = new PlayerItemDamageEvent(player, item, damage);
diff --git a/src/main/java/net/Indyuce/mmocore/api/util/math/formula/LinearValue.java b/src/main/java/net/Indyuce/mmocore/api/util/math/formula/LinearValue.java
index 5bd0d5ef..972bf5b2 100644
--- a/src/main/java/net/Indyuce/mmocore/api/util/math/formula/LinearValue.java
+++ b/src/main/java/net/Indyuce/mmocore/api/util/math/formula/LinearValue.java
@@ -110,4 +110,16 @@ public class LinearValue {
return value;
}
+
+ @Override
+ public String toString() {
+ return "LinearValue{" +
+ "base=" + base +
+ ", perLevel=" + perLevel +
+ ", min=" + min +
+ ", max=" + max +
+ ", hasmin=" + hasmin +
+ ", hasmax=" + hasmax +
+ '}';
+ }
}
diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/admin/ExperienceCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/admin/ExperienceCommandTreeNode.java
index b556d3d7..5c4b7664 100644
--- a/src/main/java/net/Indyuce/mmocore/command/rpg/admin/ExperienceCommandTreeNode.java
+++ b/src/main/java/net/Indyuce/mmocore/command/rpg/admin/ExperienceCommandTreeNode.java
@@ -58,8 +58,8 @@ public class ExperienceCommandTreeNode extends CommandTreeNode {
int amount;
try {
amount = Integer.parseInt(args[5]);
- Validate.isTrue(amount > 0);
- } catch (NumberFormatException exception) {
+ Validate.isTrue(amount >= 0);
+ } catch (RuntimeException exception) {
sender.sendMessage(ChatColor.RED + args[5] + " is not a valid number.");
return CommandResult.FAILURE;
}
diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatModifiersCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatModifiersCommandTreeNode.java
index 38de283c..d440e4ee 100644
--- a/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatModifiersCommandTreeNode.java
+++ b/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatModifiersCommandTreeNode.java
@@ -1,11 +1,11 @@
package net.Indyuce.mmocore.command.rpg.debug;
+import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.stat.StatInstance;
import io.lumine.mythic.lib.api.stat.modifier.StatModifier;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.player.stats.StatType;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -15,10 +15,7 @@ public class StatModifiersCommandTreeNode extends CommandTreeNode {
public StatModifiersCommandTreeNode(CommandTreeNode parent) {
super(parent, "statmods");
- addParameter(new Parameter("", (explorer, list) -> {
- for (StatType stat : StatType.values())
- list.add(stat.name());
- }));
+ addParameter(new Parameter("", (explorer, list) -> list.add("STAT_ID")));
}
@Override
@@ -32,15 +29,7 @@ public class StatModifiersCommandTreeNode extends CommandTreeNode {
}
PlayerData data = PlayerData.get((Player) sender);
- StatType stat;
- try {
- stat = StatType.valueOf(args[2].toUpperCase().replace("-", "_").replace(" ", "_"));
- } catch (IllegalArgumentException exception) {
- sender.sendMessage(ChatColor.RED + "Could not find stat: " + args[2] + ".");
- return CommandResult.FAILURE;
- }
-
- StatInstance instance = data.getStats().getInstance(stat);
+ StatInstance instance = data.getStats().getInstance(UtilityMethods.enumName(args[2]));
sender.sendMessage("Stat Modifiers (" + instance.getKeys().size() + "):");
for (String key : instance.getKeys()) {
StatModifier mod = instance.getModifier(key);
diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatValueCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatValueCommandTreeNode.java
index f13d42a4..f582ab31 100644
--- a/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatValueCommandTreeNode.java
+++ b/src/main/java/net/Indyuce/mmocore/command/rpg/debug/StatValueCommandTreeNode.java
@@ -1,51 +1,36 @@
package net.Indyuce.mmocore.command.rpg.debug;
+import io.lumine.mythic.lib.UtilityMethods;
+import io.lumine.mythic.lib.command.api.CommandTreeNode;
+import io.lumine.mythic.lib.command.api.Parameter;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.player.stats.StatInfo;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.player.stats.StatType;
-import io.lumine.mythic.lib.command.api.CommandTreeNode;
-import io.lumine.mythic.lib.command.api.Parameter;
-
public class StatValueCommandTreeNode extends CommandTreeNode {
- public StatValueCommandTreeNode(CommandTreeNode parent) {
- super(parent, "statvalue");
+ public StatValueCommandTreeNode(CommandTreeNode parent) {
+ super(parent, "statvalue");
- addParameter(new Parameter("", (explorer, list) -> {
- for (StatType stat : StatType.values())
- list.add(stat.name());
- }));
- addParameter(new Parameter("(formatted)", (explorer, list) -> list.add("true")));
- }
+ addParameter(new Parameter("", (explorer, list) -> list.add("STAT_ID")));
+ }
- @Override
- public CommandResult execute(CommandSender sender, String[] args) {
- if (args.length < 3)
- return CommandResult.THROW_USAGE;
+ @Override
+ public CommandResult execute(CommandSender sender, String[] args) {
+ if (args.length < 3)
+ return CommandResult.THROW_USAGE;
- if (!(sender instanceof Player)) {
- sender.sendMessage(ChatColor.RED + "This command can only be used by a player.");
- return CommandResult.FAILURE;
- }
- PlayerData data = PlayerData.get((Player) sender);
+ if (!(sender instanceof Player)) {
+ sender.sendMessage(ChatColor.RED + "This command can only be used by a player.");
+ return CommandResult.FAILURE;
+ }
+ PlayerData data = PlayerData.get((Player) sender);
- StatType stat;
- try {
- stat = StatType.valueOf(args[2].toUpperCase().replace("-", "_").replace(" ", "_"));
- } catch (IllegalArgumentException exception) {
- sender.sendMessage(ChatColor.RED + "Could not find stat: " + args[2] + ".");
- return CommandResult.FAILURE;
- }
+ StatInfo stat = StatInfo.valueOf(UtilityMethods.enumName(args[2]));
+ sender.sendMessage(DebugCommandTreeNode.commandPrefix + "Stat Value (" + ChatColor.BLUE + stat.name + ChatColor.WHITE + "): "
+ + ChatColor.GREEN + data.getStats().getStat(stat.name));
- if (args.length > 3 && args[3].equals("true"))
- sender.sendMessage(DebugCommandTreeNode.commandPrefix + "Stat Value (" + ChatColor.BLUE + stat.name() + ChatColor.WHITE + "): "
- + ChatColor.GREEN + stat.format(data.getStats().getStat(stat)) + ChatColor.WHITE + " *");
- else
- sender.sendMessage(DebugCommandTreeNode.commandPrefix + "Stat Value (" + ChatColor.BLUE + stat.name() + ChatColor.WHITE + "): "
- + ChatColor.GREEN + data.getStats().getStat(stat));
-
- return CommandResult.SUCCESS;
- }
+ return CommandResult.SUCCESS;
+ }
}
diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/ItemCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/ItemCommandTreeNode.java
new file mode 100644
index 00000000..d66f5eac
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/ItemCommandTreeNode.java
@@ -0,0 +1,44 @@
+package net.Indyuce.mmocore.command.rpg.waypoint;
+
+import io.lumine.mythic.lib.api.util.SmartGive;
+import io.lumine.mythic.lib.command.api.CommandTreeNode;
+import io.lumine.mythic.lib.command.api.Parameter;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.util.item.WaypointBookBuilder;
+import net.Indyuce.mmocore.waypoint.Waypoint;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class ItemCommandTreeNode extends CommandTreeNode {
+ public ItemCommandTreeNode(CommandTreeNode parent) {
+ super(parent, "item");
+
+ addParameter(new Parameter("", (explorer, list) -> MMOCore.plugin.waypointManager.getAll().forEach(way -> list.add(way.getId()))));
+ addParameter(Parameter.PLAYER);
+ }
+
+ @Override
+ public CommandResult execute(CommandSender sender, String[] args) {
+ if (args.length < 4)
+ return CommandResult.THROW_USAGE;
+
+ if (!MMOCore.plugin.waypointManager.has(args[2])) {
+ sender.sendMessage(ChatColor.RED + "Could not find waypoint " + args[2]);
+ return CommandResult.FAILURE;
+ }
+
+ Player player = Bukkit.getPlayer(args[3]);
+ if (player == null) {
+ sender.sendMessage(ChatColor.RED + "Could not find player " + args[3]);
+ return CommandResult.FAILURE;
+ }
+
+ Waypoint waypoint = MMOCore.plugin.waypointManager.get(args[2]);
+ new SmartGive(player).give(new WaypointBookBuilder(waypoint).build());
+ sender.sendMessage(ChatColor.GOLD + "Gave " + player.getName() + ChatColor.YELLOW + " a waypoint book of " + ChatColor.GOLD + waypoint.getId()
+ + ChatColor.YELLOW + ".");
+ return CommandResult.SUCCESS;
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/WaypointsCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/WaypointsCommandTreeNode.java
index 839f2995..48aeba29 100644
--- a/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/WaypointsCommandTreeNode.java
+++ b/src/main/java/net/Indyuce/mmocore/command/rpg/waypoint/WaypointsCommandTreeNode.java
@@ -11,6 +11,7 @@ public class WaypointsCommandTreeNode extends CommandTreeNode {
addChild(new UnlockCommandTreeNode(this));
addChild(new OpenCommandTreeNode(this));
addChild(new TeleportCommandTreeNode(this));
+ addChild(new ItemCommandTreeNode(this));
}
@Override
diff --git a/src/main/java/net/Indyuce/mmocore/comp/MMOCoreTargetRestriction.java b/src/main/java/net/Indyuce/mmocore/comp/MMOCoreTargetRestriction.java
deleted file mode 100644
index 5935b582..00000000
--- a/src/main/java/net/Indyuce/mmocore/comp/MMOCoreTargetRestriction.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.Indyuce.mmocore.comp;
-
-import io.lumine.mythic.lib.comp.target.InteractionType;
-import io.lumine.mythic.lib.comp.target.TargetRestriction;
-import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.party.AbstractParty;
-import org.bukkit.entity.LivingEntity;
-import org.bukkit.entity.Player;
-
-import java.util.Optional;
-
-public class MMOCoreTargetRestriction implements TargetRestriction {
-
- @Override
- public boolean canTarget(Player player, LivingEntity target, InteractionType interaction) {
-
- if (interaction.isOffense() && target instanceof Player && PlayerData.has(target.getUniqueId())) {
- PlayerData targetData = PlayerData.get(target.getUniqueId());
-
- // Check for the same party
- AbstractParty party = targetData.getParty();
- if (party != null && party.hasMember(player))
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java b/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java
index dd9e3433..795ac3a1 100644
--- a/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java
+++ b/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java
@@ -5,7 +5,7 @@ import io.lumine.mythic.lib.api.util.AltChar;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.player.stats.StatType;
+import net.Indyuce.mmocore.player.stats.StatInfo;
import net.Indyuce.mmocore.api.quest.PlayerQuests;
import net.Indyuce.mmocore.experience.PlayerProfessions;
import net.Indyuce.mmocore.experience.Profession;
@@ -72,11 +72,11 @@ public class RPGPlaceholders extends PlaceholderExpansion {
}
else if (identifier.equals("health") && player.isOnline()) {
- return StatType.MAX_HEALTH.format(player.getPlayer().getHealth());
+ return StatInfo.valueOf("MAX_HEALTH").format(player.getPlayer().getHealth());
}
else if (identifier.equals("max_health") && player.isOnline()) {
- return StatType.MAX_HEALTH.format(player.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
+ return StatInfo.valueOf("MAX_HEALTH").format(player.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
}
else if (identifier.equals("health_bar") && player.isOnline()) {
@@ -153,7 +153,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
return MythicLib.plugin.getMMOConfig().decimal.format(playerData.getMana());
else if (identifier.equals("mana_bar")) {
- return playerData.getProfess().getManaDisplay().generateBar(playerData.getMana(), playerData.getStats().getStat(StatType.MAX_MANA));
+ return playerData.getProfess().getManaDisplay().generateBar(playerData.getMana(), playerData.getStats().getStat("MAX_MANA"));
}
else if (identifier.startsWith("exp_multiplier_")) {
@@ -173,7 +173,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
else if (identifier.equals("stamina_bar")) {
StringBuilder format = new StringBuilder();
- double ratio = 20 * playerData.getStamina() / playerData.getStats().getStat(StatType.MAX_STAMINA);
+ double ratio = 20 * playerData.getStamina() / playerData.getStats().getStat("MAX_STAMINA");
for (double j = 1; j < 20; j++)
format.append(ratio >= j ? MMOCore.plugin.configManager.staminaFull
: ratio >= j - .5 ? MMOCore.plugin.configManager.staminaHalf : MMOCore.plugin.configManager.staminaEmpty)
@@ -182,8 +182,8 @@ public class RPGPlaceholders extends PlaceholderExpansion {
}
else if (identifier.startsWith("stat_")) {
- StatType type = StatType.valueOf(identifier.substring(5).toUpperCase());
- return type == null ? "Invalid Stat" : type.format(playerData.getStats().getStat(type));
+ StatInfo info = StatInfo.valueOf(identifier.substring(5).toUpperCase());
+ return info.format(playerData.getStats().getStat(info.name));
}
else if (identifier.equals("stellium"))
@@ -191,7 +191,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
else if (identifier.equals("stellium_bar")) {
StringBuilder format = new StringBuilder();
- double ratio = 20 * playerData.getStellium() / playerData.getStats().getStat(StatType.MAX_STELLIUM);
+ double ratio = 20 * playerData.getStellium() / playerData.getStats().getStat("MAX_STELLIUM");
for (double j = 1; j < 20; j++)
format.append(ratio >= j ? ChatColor.BLUE : ratio >= j - .5 ? ChatColor.AQUA : ChatColor.WHITE).append(AltChar.listSquare);
return format.toString();
diff --git a/src/main/java/net/Indyuce/mmocore/comp/region/RegionCondition.java b/src/main/java/net/Indyuce/mmocore/comp/region/RegionCondition.java
index dbe80a15..c8b8a9d0 100644
--- a/src/main/java/net/Indyuce/mmocore/comp/region/RegionCondition.java
+++ b/src/main/java/net/Indyuce/mmocore/comp/region/RegionCondition.java
@@ -3,8 +3,8 @@ package net.Indyuce.mmocore.comp.region;
import java.util.Arrays;
import java.util.List;
-import net.Indyuce.mmocore.loot.droptable.condition.Condition;
-import net.Indyuce.mmocore.loot.droptable.condition.ConditionInstance;
+import net.Indyuce.mmocore.loot.chest.condition.Condition;
+import net.Indyuce.mmocore.loot.chest.condition.ConditionInstance;
import io.lumine.mythic.lib.api.MMOLineConfig;
public class RegionCondition extends Condition {
diff --git a/src/main/java/net/Indyuce/mmocore/comp/region/WorldGuardMMOLoader.java b/src/main/java/net/Indyuce/mmocore/comp/region/WorldGuardMMOLoader.java
index 0c771cf7..735ebda6 100644
--- a/src/main/java/net/Indyuce/mmocore/comp/region/WorldGuardMMOLoader.java
+++ b/src/main/java/net/Indyuce/mmocore/comp/region/WorldGuardMMOLoader.java
@@ -1,6 +1,6 @@
package net.Indyuce.mmocore.comp.region;
-import net.Indyuce.mmocore.loot.droptable.condition.Condition;
+import net.Indyuce.mmocore.loot.chest.condition.Condition;
import net.Indyuce.mmocore.api.load.MMOLoader;
import io.lumine.mythic.lib.api.MMOLineConfig;
diff --git a/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java b/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java
index a5ebf4f4..c14f0232 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java
@@ -1,16 +1,16 @@
package net.Indyuce.mmocore.experience;
-import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
-import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
/**
- * Either a profession or a class
+ * General implementation for professions, classes and attributes.
+ *
+ * An experience object is a type of object that can level up.
+ * It has an experience curve and table and can receive EXP
*
* @author jules
*/
@@ -26,40 +26,11 @@ public interface ExperienceObject extends ExperienceDispenser {
@Nullable
ExpCurve getExpCurve();
- boolean hasExperienceTable();
-
/**
* @return Table read when leveling up
*/
@NotNull
ExperienceTable getExperienceTable();
- /**
- * Called when experience is gained in main class/profession
- *
- * @param playerData Player gaining the experience
- * @param experience Experience gained. Note that it is a double
- * because it gets converted to an integer at
- * the very last moment in MMOCore
- * @param hologramLocation Location of displayed hologram. When set to null
- * and if exp holograms are enabled it will take the
- * player's location instead.
- * @param source Why the EXP was gained
- */
- void giveExperience(PlayerData playerData, double experience, @org.jetbrains.annotations.Nullable Location hologramLocation, @NotNull EXPSource source);
-
- /**
- * Experience sources handle both CLASS experience sources and PROFESSION
- * experience sources. Professions have no problem because whatever
- * class the player has chosen, he can get exp in that profession.
- *
- * But class experience sources must first make sure that the player has
- * the right class before giving exp to the player
- */
- boolean shouldHandle(PlayerData playerData);
-
- @Nullable
- default Location getPlayerLocation(PlayerData player) {
- return player.isOnline() ? MMOCoreUtils.getCenterLocation(player.getPlayer()) : null;
- }
+ boolean hasExperienceTable();
}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java b/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java
index f7dd9963..44f3552c 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java
@@ -4,13 +4,13 @@ import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.lumine.mythic.lib.MythicLib;
+import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.SoundEvent;
import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent;
import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.player.stats.StatType;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.loot.chest.particle.SmallParticleEffect;
import org.apache.commons.lang.Validate;
@@ -26,7 +26,7 @@ import java.util.Map;
import java.util.Map.Entry;
public class PlayerProfessions {
- private final Map exp = new HashMap<>();
+ private final Map exp = new HashMap<>();
private final Map level = new HashMap<>();
private final PlayerData playerData;
@@ -41,7 +41,7 @@ public class PlayerProfessions {
public PlayerProfessions load(ConfigurationSection config) {
for (String key : config.getKeys(false))
if (MMOCore.plugin.professionManager.has(key)) {
- exp.put(key, config.getInt(key + ".exp"));
+ exp.put(key, config.getDouble(key + ".exp"));
level.put(key, config.getInt(key + ".level"));
}
@@ -89,7 +89,7 @@ public class PlayerProfessions {
for (Entry entry : obj.entrySet())
if (MMOCore.plugin.professionManager.has(entry.getKey())) {
JsonObject value = entry.getValue().getAsJsonObject();
- exp.put(entry.getKey(), value.get("exp").getAsInt());
+ exp.put(entry.getKey(), value.get("exp").getAsDouble());
level.put(entry.getKey(), value.get("level").getAsInt());
}
@@ -111,11 +111,11 @@ public class PlayerProfessions {
return getLevel(profession.getId());
}
- public int getExperience(String id) {
- return exp.getOrDefault(id, 0);
+ public double getExperience(String id) {
+ return exp.getOrDefault(id, 0.);
}
- public int getExperience(Profession profession) {
+ public double getExperience(Profession profession) {
return getExperience(profession.getId());
}
@@ -136,7 +136,7 @@ public class PlayerProfessions {
level.put(profession.getId(), Math.max(1, current - value));
}
- public void setExperience(Profession profession, int value) {
+ public void setExperience(Profession profession, double value) {
exp.put(profession.getId(), value);
}
@@ -147,7 +147,7 @@ public class PlayerProfessions {
giveExperience(profession, total, source);
}
- public void giveExperience(Profession profession, int value, EXPSource source) {
+ public void giveExperience(Profession profession, double value, EXPSource source) {
giveExperience(profession, value, source, null);
}
@@ -157,6 +157,8 @@ public class PlayerProfessions {
public void giveExperience(Profession profession, double value, EXPSource source, @Nullable Location hologramLocation) {
Validate.isTrue(playerData.isOnline(), "Cannot give experience to offline player");
+ if (value <= 0)
+ return;
if (hasReachedMaxLevel(profession)) {
setExperience(profession, 0);
@@ -166,19 +168,20 @@ public class PlayerProfessions {
value = MMOCore.plugin.boosterManager.calculateExp(profession, value);
// Adds functionality for additional experience per profession.
- value *= 1 + playerData.getStats().getInstance(StatType.ADDITIONAL_EXPERIENCE, profession).getTotal() / 100;
+ value *= 1 + playerData.getStats().getInstance("ADDITIONAL_EXPERIENCE_" + UtilityMethods.enumName(profession.getId())).getTotal() / 100;
// Display hologram
if (hologramLocation != null)
MMOCoreUtils.displayIndicator(hologramLocation.add(.5, 1.5, .5), MMOCore.plugin.configManager.getSimpleMessage("exp-hologram", "exp", "" + value).message());
- PlayerExperienceGainEvent event = new PlayerExperienceGainEvent(playerData, profession, (int) value, source);
+ PlayerExperienceGainEvent event = new PlayerExperienceGainEvent(playerData, profession, value, source);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled())
return;
- exp.put(profession.getId(), Math.max(0, exp.getOrDefault(profession.getId(), 0) + event.getExperience()));
- int needed, exp, level, oldLevel = getLevel(profession);
+ exp.put(profession.getId(), Math.max(0, exp.getOrDefault(profession.getId(), 0.) + event.getExperience()));
+ int level, oldLevel = getLevel(profession);
+ double needed,exp;
/*
* Loop for exp overload when leveling up, will continue
@@ -195,7 +198,7 @@ public class PlayerProfessions {
this.exp.put(profession.getId(), exp - needed);
this.level.put(profession.getId(), level + 1);
check = true;
- playerData.giveExperience((int) profession.getExperience().calculate(level), null);
+ playerData.giveExperience(profession.getExperience().calculate(level), null);
}
if (check) {
diff --git a/src/main/java/net/Indyuce/mmocore/experience/Profession.java b/src/main/java/net/Indyuce/mmocore/experience/Profession.java
index 27870d28..42db02b1 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/Profession.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/Profession.java
@@ -64,7 +64,7 @@ public class Profession extends PostLoadObject implements ExperienceObject {
maxLevel = config.getInt("max-level");
- if (config.contains("exp-sources")) {
+ if (config.contains("exp-sources"))
for (String key : config.getStringList("exp-sources"))
try {
MMOCore.plugin.experience.registerSource(MMOCore.plugin.loadManager.loadExperienceSource(new MMOLineConfig(key), this));
@@ -72,7 +72,6 @@ public class Profession extends PostLoadObject implements ExperienceObject {
MMOCore.plugin.getLogger().log(Level.WARNING,
"Could not register exp source '" + key + "' from profession '" + id + "': " + exception.getMessage());
}
- }
}
@Override
@@ -127,7 +126,7 @@ public class Profession extends PostLoadObject implements ExperienceObject {
@Override
public void giveExperience(PlayerData playerData, double experience, @Nullable Location hologramLocation, EXPSource source) {
hologramLocation = !getOption(Profession.ProfessionOption.EXP_HOLOGRAMS) ? null
- : hologramLocation == null ? getPlayerLocation(playerData) : hologramLocation;
+ : hologramLocation;
playerData.getCollectionSkills().giveExperience(this, experience, EXPSource.SOURCE, hologramLocation);
}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/SimpleExperienceObject.java b/src/main/java/net/Indyuce/mmocore/experience/SimpleExperienceObject.java
new file mode 100644
index 00000000..87e013c7
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/SimpleExperienceObject.java
@@ -0,0 +1,22 @@
+package net.Indyuce.mmocore.experience;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import org.bukkit.Location;
+import org.jetbrains.annotations.Nullable;
+
+public class SimpleExperienceObject implements ExperienceDispenser {
+
+ @Override
+ public void giveExperience(PlayerData playerData, double experience, @Nullable Location hologramLocation, EXPSource source) {
+ hologramLocation = !MMOCore.plugin.getConfig().getBoolean("display-main-class-exp-holograms") ? null
+ : hologramLocation == null ? getPlayerLocation(playerData) : hologramLocation;
+ playerData.giveExperience(experience, source, hologramLocation, true);
+ }
+
+ @Override
+ public boolean shouldHandle(PlayerData playerData) {
+ return true;
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/dispenser/ExperienceDispenser.java b/src/main/java/net/Indyuce/mmocore/experience/dispenser/ExperienceDispenser.java
index 2218bace..0d2af5f5 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/dispenser/ExperienceDispenser.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/dispenser/ExperienceDispenser.java
@@ -21,9 +21,7 @@ public interface ExperienceDispenser {
* Called when experience is gained in main class/profession
*
* @param playerData Player gaining the experience
- * @param experience Experience gained. Note that it is a double
- * because it gets converted to an integer at
- * the very last moment in MMOCore
+ * @param experience Experience gained
* @param hologramLocation Location of displayed hologram. When set to null
* and if exp holograms are enabled it will take the
* player's location instead.
diff --git a/src/main/java/net/Indyuce/mmocore/experience/droptable/ExperienceItem.java b/src/main/java/net/Indyuce/mmocore/experience/droptable/ExperienceItem.java
index e676156b..31698af7 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/droptable/ExperienceItem.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/droptable/ExperienceItem.java
@@ -13,7 +13,10 @@ import java.util.Random;
public class ExperienceItem {
private final String id;
+
+ //A period of 0 means the item will only trigger one time.
private final int period;
+ private final int firstTrigger;
private final double claimChance, failReduction;
private final List triggers;
@@ -23,6 +26,7 @@ public class ExperienceItem {
* One item for an experience table
*
* @param period The experience item is claimed every X level ups
+ * @param firstTrigger The experience item if claimed for the first time at X level ups.
* @param claimChance Chance for that item to be claimed every X level ups
* @param failReduction Between 0 and 1, by how much the fail chance is reduced
* every time the item is not claimed when leveling up.
@@ -32,19 +36,22 @@ public class ExperienceItem {
* where n is the amount of successive claiming fails
* @param triggers Actions cast when the exp item is claimed
*/
- public ExperienceItem(String id, int period, double claimChance, double failReduction, List triggers) {
+ public ExperienceItem(String id, int period, int firstTrigger, double claimChance, double failReduction, List triggers) {
this.id = id;
this.period = period;
this.claimChance = claimChance;
this.failReduction = failReduction;
this.triggers = triggers;
+ this.firstTrigger = firstTrigger;
}
public ExperienceItem(ConfigurationSection config) {
Validate.notNull(config, "Config cannot be null");
Validate.isTrue(config.contains("triggers"));
id = config.getName();
- period = config.getInt("period", 1);
+
+ period = config.getInt("period", 0);
+ firstTrigger = config.getInt("first-trigger", period);
claimChance = config.getDouble("chance", 100) / 100;
failReduction = config.getDouble("fail-reduction", 80) / 100;
triggers = new ArrayList<>();
@@ -64,7 +71,7 @@ public class ExperienceItem {
* account the randomness factor from the 'chance' parameter
*/
public boolean roll(int professionLevel, int timesCollected) {
- int claimsRequired = professionLevel - (timesCollected + 1) * period;
+ int claimsRequired = professionLevel + 1 - (firstTrigger - timesCollected * period);
if (claimsRequired < 1)
return false;
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java
index a5c085b7..a0f6b5bf 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java
@@ -168,7 +168,7 @@ public class BrewPotionExperienceSource extends ExperienceSource {
*/
// exp += getTotal(mapEffectDurations());
- getDispenser().giveExperience(PlayerData.get(player), (int) exp * multiplier, null, EXPSource.SOURCE);
+ getDispenser().giveExperience(PlayerData.get(player), exp * multiplier, player.getLocation(), EXPSource.SOURCE);
}
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/ClimbExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/ClimbExperienceSource.java
new file mode 100644
index 00000000..c26ebd0a
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/ClimbExperienceSource.java
@@ -0,0 +1,75 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.util.Vector;
+
+public class ClimbExperienceSource extends SpecificExperienceSource {
+ //Can be Ladder,Vines,Twisting Vines,Weeping Vines.
+ private final Material type;
+
+ /**
+ * Gives Experience when a player climbs on a ladder, a vine, a twisting vine or a weeping vine depending
+ * on the type precised (if no type is precised it will give xp for the 4)
+ * The xp given depends on the vertical distance travelled, the random amount given correspond
+ * to the xp when you climb, one block
+ */
+ public ClimbExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ //If no type precised Ladder and all vines types work
+ if (!config.contains("type"))
+ type = null;
+ else {
+ String str = config.getString("type").toUpperCase().replace("-", "_");
+ Validate.isTrue(str.equals("LADDER") ||
+ str.equals("VINE") || str.equals("TWISTING_VINES_PLANT") || str.equals("WEEPING_VINES"),
+ "ClimbExperienceSource problem: The type must be ladder, vine, twisted-vines or weeping-vines");
+
+ type = Material.valueOf(str);
+ }
+
+
+ }
+
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+ @EventHandler
+ public void onClimb(PlayerMoveEvent e) {
+ double delta=e.getTo().getBlockY()-e.getFrom().getBlockY();
+ if (delta > 0) {
+ if (e.getPlayer().hasMetadata("NPC"))
+ return;
+ PlayerData playerData = PlayerData.get(e.getPlayer());
+ for (ClimbExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, e.getFrom().getBlock().getType()))
+ source.giveExperience(playerData, delta, null);
+ }
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, Material material) {
+ if (type == null)
+ return material.equals(Material.LADDER) || material.equals(Material.VINE) ||
+ material.equals(Material.WEEPING_VINES) || material.equals(Material.TWISTING_VINES) ||
+ material.equals(Material.WEEPING_VINES_PLANT) || material.equals(Material.TWISTING_VINES_PLANT);
+ if (type.equals(Material.WEEPING_VINES))
+ return material.equals(Material.WEEPING_VINES) || material.equals(Material.WEEPING_VINES_PLANT);
+ if (type.equals(Material.TWISTING_VINES))
+ return material.equals(Material.TWISTING_VINES) || material.equals(Material.TWISTING_VINES_PLANT);
+ return material.equals(type);
+
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/DamageDealtExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/DamageDealtExperienceSource.java
new file mode 100644
index 00000000..7e1c91ac
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/DamageDealtExperienceSource.java
@@ -0,0 +1,72 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import io.lumine.mythic.lib.api.event.PlayerAttackEvent;
+import io.lumine.mythic.lib.damage.DamagePacket;
+import io.lumine.mythic.lib.damage.DamageType;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.event.EventHandler;
+import scala.Enumeration;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class DamageDealtExperienceSource extends SpecificExperienceSource {
+ private final DamageType type;
+
+ /**
+ * Gives experience when a player deals damage of a certain type. If no type is given it will give xp for all
+ * the damage type. The random value you give correspond to the xp you get per damage dealt.
+ */
+
+ public DamageDealtExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if (!config.contains("type"))
+ type = null;
+ else {
+ String str = config.getString("type").toUpperCase().replace("-", "_");
+ //Checks if the damage type correspond to a value of the damage type enum
+ Validate.isTrue(Arrays.stream(DamageType.values()).map(Objects::toString).collect(Collectors.toList()).contains(str),
+ "Type value not allowed. Type value allowed: magic, physical, weapon, skill, projectile," +
+ " unarmed, on-hit, minion, dot.");
+ type = DamageType.valueOf(str);
+
+ }
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+ @EventHandler
+ public void onDamageDealt(PlayerAttackEvent e) {
+ PlayerData playerData = PlayerData.get(e.getPlayer());
+ for (DamageDealtExperienceSource source : getSources()) {
+ double value = 0;
+ for (DamagePacket packet : e.getDamage().getPackets()) {
+ for (DamageType damageType : packet.getTypes()) {
+ if (source.matchesParameter(playerData, damageType))
+ value += packet.getFinalValue();
+ }
+
+ }
+ source.giveExperience(playerData, value, null);
+ }
+
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, DamageType damageType) {
+ if (type == null)
+ return true;
+ else
+ return type.equals(damageType);
+ }
+
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/DamageTakenExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/DamageTakenExperienceSource.java
new file mode 100644
index 00000000..d361d3a4
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/DamageTakenExperienceSource.java
@@ -0,0 +1,72 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import io.lumine.mythic.lib.damage.DamageType;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class DamageTakenExperienceSource extends SpecificExperienceSource {
+ private final EntityDamageEvent.DamageCause cause;
+
+ /**
+ * Gives experience when a player takes damage from a certain cause. If no cause is given it will give xp for all
+ * the damage causes. The random value you give correspond to the xp you get per damage taken.
+ */
+ public DamageTakenExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if (!config.contains("cause"))
+ cause = null;
+ else {
+ String str = config.getString("cause").toUpperCase().replace("-", "_");
+ //Checks if the damage type correspond to a value of the damage type enum
+ Validate.isTrue(Arrays.stream(EntityDamageEvent.DamageCause.values()).map(Objects::toString).collect(Collectors.toList()).contains(str),
+ "Cause not allowed. Go check at all the Damage Causes in EntityDamageEvent.DamageCause enum.");
+ cause = EntityDamageEvent.DamageCause.valueOf(str);
+ }
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+ @EventHandler
+ public void onDamageTaken(EntityDamageEvent e) {
+ if (e.getEntity() instanceof Player && !e.getEntity().hasMetadata("NPC")) {
+ double amount = e.getDamage();
+ PlayerData playerData = PlayerData.get((OfflinePlayer) e.getEntity());
+ //We wait 2 tick to check if the player is Dead
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ for (DamageTakenExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, e.getCause()))
+ source.giveExperience(playerData, amount, null);
+ }
+ }
+ }.runTaskLater(MMOCore.plugin, 2);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, EntityDamageEvent.DamageCause damageCause) {
+ if (player.getPlayer().isDead())
+ return false;
+ if (cause == null)
+ return true;
+ return damageCause.equals(cause);
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/EatExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/EatExperienceSource.java
new file mode 100644
index 00000000..b6db5451
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/EatExperienceSource.java
@@ -0,0 +1,58 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang3.Validate;
+import org.bukkit.Material;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.entity.FoodLevelChangeEvent;
+import org.bukkit.inventory.ItemStack;
+
+public class EatExperienceSource extends SpecificExperienceSource {
+ private final Material type;
+
+ /**
+ * Gives xp when you eat a certain type of food. If not type is given it will give xp from all the food sources.
+ * The amount of xp given is the xp per food regenerated.
+ */
+ public EatExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if(!config.contains("type"))
+ type=null;
+ else {
+ Material material=Material.valueOf(config.getString("type").toUpperCase().replace("-","_"));
+ Validate.isTrue(material!=null,"You must precise a valid material!");
+ type=material;
+ }
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+
+ @EventHandler
+ public void a(FoodLevelChangeEvent e) {
+ if (!(e.getEntity() instanceof Player) || e.getEntity().hasMetadata("NPC"))
+ return;
+ PlayerData playerData = PlayerData.get((OfflinePlayer) e.getEntity());
+ for (EatExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, e.getItem()))
+ source.giveExperience(playerData, e.getFoodLevel(), null);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, ItemStack obj) {
+ if(type==null)
+ return true;
+ return type.equals(obj.getType());
+ }
+
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/EnchantItemExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/EnchantItemExperienceSource.java
index aae363b1..3a9c334d 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/source/EnchantItemExperienceSource.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/EnchantItemExperienceSource.java
@@ -60,7 +60,7 @@ public class EnchantItemExperienceSource extends ExperienceSource {
double exp = 0;
for (Entry entry : applicableEnchants.entrySet())
exp += MMOCore.plugin.enchantManager.getBaseExperience(entry.getKey()) * entry.getValue();
- getDispenser().giveExperience(player, (int) exp, event.getEnchantBlock().getLocation(), EXPSource.SOURCE);
+ getDispenser().giveExperience(player, exp, event.getEnchantBlock().getLocation(), EXPSource.SOURCE);
}
}
};
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/MineBlockExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/MineBlockExperienceSource.java
index 4eef178f..fcc457a4 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/source/MineBlockExperienceSource.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/MineBlockExperienceSource.java
@@ -15,7 +15,7 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
public class MineBlockExperienceSource extends SpecificExperienceSource {
- public final Material material;
+ private final Material material;
private final boolean silkTouch;
private final boolean crop;
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/MoveExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/MoveExperienceSource.java
new file mode 100644
index 00000000..66ae8959
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/MoveExperienceSource.java
@@ -0,0 +1,81 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerMoveEvent;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class MoveExperienceSource extends SpecificExperienceSource {
+ private final MovingType type;
+
+ public MoveExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if (!config.contains("type"))
+ type = null;
+ else {
+ String str = config.getString("type").toUpperCase().replace("-", "_");
+ //Checks if the damage type correspond to a value of the damage type enum
+ Validate.isTrue(Arrays.stream(MoveExperienceSource.MovingType.values()).map(Objects::toString).collect(Collectors.toList()).contains(str),
+ "moving-type value not allowed. Moving type values allowed: sneak, fly, swim, sprint, walk.");
+ type = MovingType.valueOf(str);
+ }
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+ @EventHandler
+ public void onMove(PlayerMoveEvent e) {
+ double deltax = e.getTo().getBlockX() - e.getFrom().getBlockX();
+ double deltay = e.getTo().getBlockY() - e.getFrom().getBlockY();
+ double deltaz = e.getTo().getBlockZ() - e.getFrom().getBlockZ();
+ if (deltax != 0 && deltay != 0 && deltaz != 0) {
+ double delta = Math.sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);
+ if (e.getPlayer().hasMetadata("NPC"))
+ return;
+ Player player = e.getPlayer();
+ PlayerData playerData = PlayerData.get(player);
+ for (MoveExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, null)) {
+ giveExperience(playerData, delta, null);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, Object obj) {
+ return type == null || type.matches(player.getPlayer());
+ }
+
+ public enum MovingType {
+ SNEAK(Player::isSneaking),
+ FLY((p) -> p.isFlying() || p.isGliding()),
+ SWIM((p) -> p.getLocation().getBlock().isLiquid()),
+ SPRINT(Player::isSprinting),
+ WALK((p) -> !p.isSneaking() && !p.isSprinting() && !p.isFlying() && !p.getLocation().getBlock().isLiquid());
+
+ private final Function matching;
+
+ MovingType(Function matching) {
+ this.matching = matching;
+ }
+
+ public boolean matches(Player player) {
+ return !player.isInsideVehicle() && matching.apply(player);
+ }
+
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/PlayExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/PlayExperienceSource.java
new file mode 100644
index 00000000..b2f49d62
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/PlayExperienceSource.java
@@ -0,0 +1,88 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Objects;
+
+public class PlayExperienceSource extends SpecificExperienceSource {
+
+ private final World world;
+ private final double x1, x2, z1, z2;
+ private final boolean inCombat;
+
+ /**
+ * Experience source giving the specified amount of xp to all the players online each second in certain world bounds.
+ * If no bounds are given, it will give the xp to every player online. You can also specifiy if the player
+ * has to be inCombat or not to get the xp.
+ */
+ public PlayExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+
+ inCombat = config.getBoolean("in-combat", false);
+ world = config.contains("world") ? Objects.requireNonNull(Bukkit.getWorld(config.getString("world")), "Could not find world " + config.getString("world")) : null;
+ if (!config.contains("x1") || !config.contains("x2")) {
+ x1 = Double.NEGATIVE_INFINITY;
+ x2 = Double.POSITIVE_INFINITY;
+ } else {
+ x1 = Math.min(config.getInt("x1"), config.getInt("x2"));
+ x2 = Math.max(config.getInt("x1"), config.getInt("x2"));
+ }
+ if (!config.contains("z1") || !config.contains("z2")) {
+ z1 = Double.NEGATIVE_INFINITY;
+ z2 = Double.POSITIVE_INFINITY;
+ } else {
+ z1 = Math.min(config.getInt("z1"), config.getInt("z2"));
+ z2 = Math.max(config.getInt("z1"), config.getInt("z2"));
+ }
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new PlayingExperienceSourceManager();
+
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, Object obj) {
+ if (inCombat && !player.isInCombat())
+ return false;
+
+ if (world == null)
+ return true;
+ Location location = player.getPlayer().getLocation();
+ return location.getWorld().equals(world) && location.getX() > x1 && location.getX() < x2
+ && location.getZ() > z1 && location.getZ() < z2;
+ }
+
+
+ private class PlayingExperienceSourceManager extends ExperienceSourceManager {
+
+ public PlayingExperienceSourceManager() {
+ new BukkitRunnable() {
+
+ @Override
+ public void run() {
+ Bukkit.getOnlinePlayers().forEach((player) -> {
+ if (!player.hasMetadata("NPC")) {
+ PlayerData playerData = PlayerData.get(player);
+ for (PlayExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, null))
+ giveExperience(playerData, 1, null);
+ }
+ }
+ });
+ }
+
+ }.runTaskTimer(MMOCore.plugin, 0, 20);
+ }
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/ProjectileExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/ProjectileExperienceSource.java
new file mode 100644
index 00000000..c3ac3578
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/ProjectileExperienceSource.java
@@ -0,0 +1,125 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Location;
+import org.bukkit.entity.Arrow;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.entity.Trident;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.entity.ProjectileHitEvent;
+import org.bukkit.event.entity.ProjectileLaunchEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class ProjectileExperienceSource extends SpecificExperienceSource {
+ private final ProjectileType projectileType;
+
+ public ProjectileExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if (!config.contains("type"))
+ projectileType = null;
+ else {
+ String str = config.getString("type").toUpperCase().replace("-", "_");
+ Validate.isTrue(Arrays.stream(ProjectileType.values()).map(ProjectileType::toString).collect(Collectors.toList()).contains(str));
+ projectileType = ProjectileType.valueOf(str);
+ }
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+
+ return new ExperienceSourceManager() {
+ HashMap projectiles = new HashMap<>();
+
+ @EventHandler
+ public void onHit(ProjectileHitEvent e) {
+ if (e.getHitBlock() != null && projectiles.containsKey(e.getEntity()))
+ projectiles.remove(e.getEntity());
+
+ }
+
+ @EventHandler
+ public void onDamage(EntityDamageByEntityEvent e) {
+
+ if (e.getEntity() instanceof Projectile) {
+ Projectile projectile = (Projectile) e.getEntity();
+ if (!projectiles.containsKey(projectile))
+ return;
+ if (projectile.getShooter() instanceof Player && !((Player) projectile.getShooter()).hasMetadata("NPC")) {
+ Player player = (Player) projectile.getShooter();
+ PlayerData playerData = PlayerData.get(player);
+ double distance = projectiles.get(projectile).distance(e.getEntity().getLocation());
+ for (ProjectileExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, projectile))
+ source.giveExperience(playerData, e.getFinalDamage() * distance, null);
+ }
+
+
+ }
+ projectiles.remove(projectile);
+ }
+
+ }
+
+ //Mark every arrow with the the location at which it was shot to calculate the distance
+ @EventHandler
+ public void onLaunch(ProjectileLaunchEvent e) {
+ if (e.getEntity().getShooter() instanceof Player) {
+ Player player = (Player) e.getEntity().getShooter();
+ if (player.hasMetadata("NPC"))
+ return;
+
+
+ projectiles.put(e.getEntity(), e.getLocation());
+ //Remove the projectile 15 s after it was launched
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ projectiles.remove(e.getEntity());
+ }
+ }.runTaskLater(MMOCore.plugin, 60 * 20L);
+
+
+ }
+ }
+
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, Projectile projectile) {
+ if (projectileType == null)
+ return true;
+ return projectileType.matches(projectile);
+ }
+
+
+ public enum ProjectileType {
+ ARROW((p) -> p instanceof Arrow),
+ TRIDENT((p) -> p instanceof Trident);
+
+ private final Function matching;
+
+ ProjectileType(Function matching) {
+ this.matching = matching;
+ }
+
+
+ public boolean matches(Projectile projectile) {
+ return matching.apply(projectile);
+ }
+ }
+
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/RepairItemExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/RepairItemExperienceSource.java
index e7942846..070e5e24 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/source/RepairItemExperienceSource.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/RepairItemExperienceSource.java
@@ -75,7 +75,7 @@ public class RepairItemExperienceSource extends ExperienceSource {
*/
double exp = MMOCore.plugin.smithingManager.getBaseExperience(item.getType())
* Math.max(0, ((Damageable) old.getItemMeta()).getDamage() - ((Damageable) item.getItemMeta()).getDamage()) / 100;
- getDispenser().giveExperience(data, (int) exp, null, EXPSource.SOURCE);
+ getDispenser().giveExperience(data, exp, data.getPlayer().getLocation(), EXPSource.SOURCE);
}
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/ResourceExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/ResourceExperienceSource.java
new file mode 100644
index 00000000..d1e40e84
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/ResourceExperienceSource.java
@@ -0,0 +1,61 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.event.EventHandler;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class ResourceExperienceSource extends SpecificExperienceSource {
+ private final PlayerResource resource;
+
+ /**
+ * Gives experience when the player uses a specific resoure. If no resource is precised it will trigger for
+ * mana, stamina and stellium. The amount specified si the xp given per resource consummed.
+ */
+ public ResourceExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if (!config.contains("resource"))
+ resource = null;
+ else {
+ String str = config.getString("resource").toUpperCase().replace("-", "_");
+ Validate.isTrue(str.equals("MANA") || str.equals("STELLIUM") || str.equals("STAMINA"),
+ "ResourceExperienceSource problem: The resource can only be mana, stamina or STELLIUM");
+ resource = PlayerResource.valueOf(str);
+ }
+
+
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+ @EventHandler
+ public void onResource(PlayerResourceUpdateEvent e) {
+ if (e.getPlayer().hasMetadata("NPC"))
+ return;
+ PlayerData playerData = PlayerData.get(e.getPlayer());
+ if(e.getAmount()<0)
+ for (ResourceExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, e.getResource()))
+ source.giveExperience(playerData, -e.getAmount(), null);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, PlayerResource obj) {
+ if (resource == null)
+ return !obj.equals(PlayerResource.HEALTH);
+ return resource.equals(obj);
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/RideExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/RideExperienceSource.java
new file mode 100644
index 00000000..9c9578ce
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/RideExperienceSource.java
@@ -0,0 +1,74 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerMoveEvent;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class RideExperienceSource extends SpecificExperienceSource {
+ private final EntityType type;
+
+ /**
+ * Gives experience when a player moves riding a certain entity. If no entity type is given it will give xp if you move
+ * while riding an entity whatever it is.
+ * The random value you give correspond to the xp you get per block travelled while riding.
+ */
+ public RideExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ if (!config.contains("type"))
+ type = null;
+ else {
+ String str = config.getString("type").toUpperCase().replace("-", "_");
+ Validate.isTrue(Arrays.stream(EntityType.values()).map(Objects::toString).collect(Collectors.toList()).contains(str),
+ "The type must correspond to an entity that exist in the game.");
+ type = EntityType.valueOf(str);
+ }
+
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+ @EventHandler
+ public void onRide(PlayerMoveEvent e) {
+
+ if (e.getPlayer().isInsideVehicle()) {
+ double deltax = e.getTo().getBlockX() - e.getFrom().getBlockX();
+ double deltay = e.getTo().getBlockY() - e.getFrom().getBlockY();
+ double deltaz = e.getTo().getBlockZ() - e.getFrom().getBlockZ();
+ if (deltax != 0 && deltay != 0 && deltaz != 0) {
+ double delta = Math.sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);
+ if (e.getPlayer().hasMetadata("NPC"))
+ return;
+ PlayerData playerData = PlayerData.get(e.getPlayer());
+ Entity vehicle = e.getPlayer().getVehicle();
+ for (RideExperienceSource source : getSources()) {
+ if (source.matchesParameter(playerData, vehicle.getType()))
+ giveExperience(playerData, e.getFrom().distance(e.getTo()), null);
+ }
+ }
+ }
+ }
+
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, EntityType obj) {
+ if (type == null)
+ return true;
+ return type.equals(obj);
+ }
+
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/TameExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/TameExperienceSource.java
new file mode 100644
index 00000000..83a455c6
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/TameExperienceSource.java
@@ -0,0 +1,43 @@
+package net.Indyuce.mmocore.experience.source;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.api.util.MMOCoreUtils;
+import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
+import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
+import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Wolf;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+
+public class TameExperienceSource extends SpecificExperienceSource {
+ public TameExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
+ super(dispenser, config);
+ }
+
+ @Override
+ public ExperienceSourceManager newManager() {
+ return new ExperienceSourceManager() {
+
+ @EventHandler
+ public void onWolfHit(EntityDamageByEntityEvent e) {
+ if(e.getDamager() instanceof Wolf) {
+ Wolf wolf= (Wolf) e.getDamager();
+ if(wolf.getOwner() instanceof Player &&!((Player) wolf.getOwner()).hasMetadata("NPC")) {
+ PlayerData playerData=PlayerData.get((OfflinePlayer) wolf.getOwner());
+ for(TameExperienceSource source:getSources()) {
+ source.giveExperience(playerData,e.getDamage(), MMOCoreUtils.getCenterLocation(e.getEntity()));
+ }
+ }
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean matchesParameter(PlayerData player, Object obj) {
+ return false;
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/type/SpecificExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/type/SpecificExperienceSource.java
index 6e62560c..05cf0b3d 100644
--- a/src/main/java/net/Indyuce/mmocore/experience/source/type/SpecificExperienceSource.java
+++ b/src/main/java/net/Indyuce/mmocore/experience/source/type/SpecificExperienceSource.java
@@ -5,11 +5,13 @@ import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.math.formula.RandomAmount;
+import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.jetbrains.annotations.Nullable;
public abstract class SpecificExperienceSource extends ExperienceSource {
private final RandomAmount amount;
+ double counter = 0;
/**
* Used to register experience sources with SPECIFIC experience outputs.
@@ -27,8 +29,8 @@ public abstract class SpecificExperienceSource extends ExperienceSource {
return amount;
}
- public int rollAmount() {
- return amount.calculateInt();
+ public double rollAmount() {
+ return amount.calculate();
}
/**
diff --git a/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java b/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java
index 49e74717..058694a8 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java
@@ -13,6 +13,7 @@ import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
import net.Indyuce.mmocore.manager.SoundManager;
+import net.Indyuce.mmocore.player.stats.StatInfo;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.inventory.InventoryClickEvent;
@@ -66,8 +67,9 @@ public class AttributeView extends EditableInventory {
holders.register("current", total);
holders.register("attribute_points", inv.getPlayerData().getAttributePoints());
attribute.getBuffs().forEach(buff -> {
- holders.register("buff_" + buff.getStat().toLowerCase(), buff.getValue());
- holders.register("total_" + buff.getStat().toLowerCase(), buff.multiply(total).getValue());
+ StatInfo info = StatInfo.valueOf(buff.getStat());
+ holders.register("buff_" + buff.getStat().toLowerCase(), info.format(buff.getValue()));
+ holders.register("total_" + buff.getStat().toLowerCase(), info.format(buff.multiply(total).getValue()));
});
return holders;
}
diff --git a/src/main/java/net/Indyuce/mmocore/gui/PlayerStats.java b/src/main/java/net/Indyuce/mmocore/gui/PlayerStats.java
index 0a870c26..63759744 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/PlayerStats.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/PlayerStats.java
@@ -1,14 +1,15 @@
package net.Indyuce.mmocore.gui;
+import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.stat.modifier.StatModifier;
import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmocore.MMOCore;
-import net.Indyuce.mmocore.experience.Booster;
-import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
-import net.Indyuce.mmocore.api.player.stats.StatType;
+import net.Indyuce.mmocore.player.stats.StatInfo;
import net.Indyuce.mmocore.api.util.math.format.DelayFormat;
+import net.Indyuce.mmocore.experience.Booster;
+import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
@@ -18,10 +19,13 @@ import net.Indyuce.mmocore.party.AbstractParty;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import java.util.Objects;
+
public class PlayerStats extends EditableInventory {
public PlayerStats() {
super("player-stats");
@@ -78,7 +82,7 @@ public class PlayerStats extends EditableInventory {
Placeholders holders = new Placeholders();
net.Indyuce.mmocore.api.player.stats.PlayerStats stats = inv.getPlayerData().getStats();
- double ratio = (double) inv.getPlayerData().getCollectionSkills().getExperience(profession)
+ double ratio = inv.getPlayerData().getCollectionSkills().getExperience(profession)
/ (double) inv.getPlayerData().getCollectionSkills().getLevelUpExperience(profession);
String bar = "" + ChatColor.BOLD;
@@ -91,9 +95,9 @@ public class PlayerStats extends EditableInventory {
holders.register("level", "" + inv.getPlayerData().getCollectionSkills().getLevel(profession));
holders.register("xp", inv.getPlayerData().getCollectionSkills().getExperience(profession));
holders.register("percent", decimal.format(ratio * 100));
- for (StatType stat : StatType.values())
- if (stat.matches(profession))
- holders.register(stat.name().toLowerCase(), stat.format(stats.getStat(stat)));
+ for (StatInfo stat : MMOCore.plugin.statManager.getLoaded())
+ if (Objects.equals(stat.profession, profession))
+ holders.register(stat.name.toLowerCase(), stat.format(stats.getStat(stat.name)));
return holders;
}
@@ -113,21 +117,35 @@ public class PlayerStats extends EditableInventory {
@Override
public Placeholders getPlaceholders(GeneratedInventory inv, int n) {
+ return new Placeholders() {
+ final net.Indyuce.mmocore.api.player.stats.PlayerStats stats = inv.getPlayerData().getStats();
- net.Indyuce.mmocore.api.player.stats.PlayerStats stats = inv.getPlayerData().getStats();
- Placeholders holders = new Placeholders();
+ public String apply(Player player, String str) {
+ while (str.contains("{") && str.substring(str.indexOf("{")).contains("}")) {
+ String holder = str.substring(str.indexOf("{") + 1, str.indexOf("}"));
+ String replaced;
- for (StatType stat : StatType.values()) {
- double base = stats.getBase(stat), total = stats.getStat(stat), extra = total - base;
- holders.register(stat.name().toLowerCase(), stat.format(total));
- holders.register(stat.name().toLowerCase() + "_base", stat.format(base));
- holders.register(stat.name().toLowerCase() + "_extra", stat.format(extra));
- }
+ if (holder.endsWith("_base")) {
+ StatInfo info = StatInfo.valueOf(UtilityMethods.enumName(holder.substring(0, holder.length() - 5)));
+ replaced = info.format(stats.getBase(info.name));
+ } else if (holder.endsWith("_extra")) {
+ StatInfo info = StatInfo.valueOf(UtilityMethods.enumName(holder.substring(0, holder.length() - 5)));
+ replaced = info.format(stats.getStat(info.name) - stats.getBase(info.name));
+ } else if (holder.startsWith("attribute_")) {
+ PlayerAttribute attr = MMOCore.plugin.attributeManager.get(holder.substring(10).replace("_", "-").toLowerCase());
+ replaced = String.valueOf(inv.getPlayerData().getAttributes().getAttribute(attr));
+ } else {
+ StatInfo info = StatInfo.valueOf(UtilityMethods.enumName(holder));
+ replaced = info.format(stats.getStat(info.name));
+ }
- for (PlayerAttribute attribute : MMOCore.plugin.attributeManager.getAll())
- holders.register("attribute_" + attribute.getId().replace("-", "_"), inv.getPlayerData().getAttributes().getAttribute(attribute));
+ str = str.replace("{" + holder + "}", replaced);
+ }
- return holders;
+ // External placeholders
+ return MMOCore.plugin.placeholderParser.parse(player, str);
+ }
+ };
}
};
diff --git a/src/main/java/net/Indyuce/mmocore/gui/SkillList.java b/src/main/java/net/Indyuce/mmocore/gui/SkillList.java
index a60e027d..1699f0ef 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/SkillList.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/SkillList.java
@@ -13,7 +13,6 @@ import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill;
-import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
@@ -38,9 +37,6 @@ public class SkillList extends EditableInventory {
if (function.equals("skill"))
return new SkillItem(config);
- if (function.equals("switch"))
- return new SwitchItem(config);
-
if (function.equals("level"))
return new LevelItem(config);
@@ -49,7 +45,7 @@ public class SkillList extends EditableInventory {
@Override
public Placeholders getPlaceholders(SkillViewerInventory inv, int n) {
- RegisteredSkill selected = inv.selected.getSkill();
+ RegisteredSkill selected = inv.selected == null ? null : inv.selected.getSkill();
Placeholders holders = new Placeholders();
holders.register("skill_caps", selected.getName().toUpperCase());
@@ -58,11 +54,6 @@ public class SkillList extends EditableInventory {
return holders;
}
-
- @Override
- public boolean canDisplay(SkillViewerInventory inv) {
- return !inv.binding;
- }
};
if (function.equals("slot"))
@@ -74,7 +65,7 @@ public class SkillList extends EditableInventory {
@Override
public Placeholders getPlaceholders(SkillViewerInventory inv, int n) {
- RegisteredSkill selected = inv.selected.getSkill();
+ RegisteredSkill selected = inv.selected == null ? null : inv.selected.getSkill();
RegisteredSkill skill = inv.getPlayerData().hasSkillBound(n) ? inv.getPlayerData().getBoundSkill(n).getSkill() : null;
Placeholders holders = new Placeholders();
@@ -82,7 +73,7 @@ public class SkillList extends EditableInventory {
holders.register("skill", skill == null ? none : skill.getName());
holders.register("index", "" + (n + 1));
holders.register("slot", MMOCoreUtils.intToRoman(n + 1));
- holders.register("selected", selected.getName());
+ holders.register("selected", selected == null ? none : selected.getName());
return holders;
}
@@ -102,16 +93,29 @@ public class SkillList extends EditableInventory {
return item;
}
- @Override
- public boolean canDisplay(SkillViewerInventory inv) {
- return inv.binding;
- }
-
@Override
public boolean hasDifferentDisplay() {
return true;
}
};
+ if (function.equals("previous"))
+ return new SimplePlaceholderItem(config) {
+
+ @Override
+ public boolean canDisplay(SkillViewerInventory inv) {
+ return inv.page > 0;
+ }
+ };
+ if (function.equals("next")) {
+ return new SimplePlaceholderItem(config) {
+
+ @Override
+ public boolean canDisplay(SkillViewerInventory inv) {
+ final int perPage = inv.skillSlots.size();
+ return inv.page < (inv.skills.size() - 1) / perPage;
+ }
+ };
+ }
return new SimplePlaceholderItem(config);
}
@@ -120,30 +124,6 @@ public class SkillList extends EditableInventory {
return new SkillViewerInventory(data, this);
}
- public class SwitchItem extends SimplePlaceholderItem {
- private final SimplePlaceholderItem binding, upgrading;
-
- public SwitchItem(ConfigurationSection config) {
- super(config);
-
- Validate.isTrue(config.contains("binding"), "Config must have 'binding'");
- Validate.isTrue(config.contains("upgrading"), "Config must have 'upgrading'");
-
- binding = new SimplePlaceholderItem(config.getConfigurationSection("binding"));
- upgrading = new SimplePlaceholderItem(config.getConfigurationSection("upgrading"));
- }
-
- @Override
- public ItemStack display(SkillViewerInventory inv, int n) {
- return inv.binding ? upgrading.display(inv) : binding.display(inv);
- }
-
- @Override
- public boolean canDisplay(SkillViewerInventory inv) {
- return true;
- }
- }
-
public class LevelItem extends InventoryItem {
private final int offset;
@@ -193,20 +173,11 @@ public class SkillList extends EditableInventory {
public Placeholders getPlaceholders(SkillViewerInventory inv, int n) {
return new Placeholders();
}
-
- @Override
- public boolean canDisplay(SkillViewerInventory inv) {
- return !inv.binding;
- }
}
public class SkillItem extends InventoryItem {
- private final int selectedSkillSlot;
-
public SkillItem(ConfigurationSection config) {
super(Material.BARRIER, config);
-
- selectedSkillSlot = config.getInt("selected-slot");
}
@Override
@@ -217,15 +188,17 @@ public class SkillList extends EditableInventory {
@Override
public ItemStack display(SkillViewerInventory inv, int n) {
- /*
- * calculate placeholders
- */
- ClassSkill skill = inv.skills.get(mod(n + inv.getPlayerData().skillGuiDisplayOffset, inv.skills.size()));
+ // Calculate placeholders
+ int index = n + inv.skillSlots.size() * inv.page;
+ if (index >= inv.skills.size())
+ return new ItemStack(Material.AIR);
+
+ ClassSkill skill = inv.skills.get(index);
Placeholders holders = getPlaceholders(inv.getPlayerData(), skill);
List lore = new ArrayList<>(getLore());
- int index = lore.indexOf("{lore}");
+ index = lore.indexOf("{lore}");
lore.remove(index);
List skillLore = skill.calculateLore(inv.getPlayerData());
for (int j = 0; j < skillLore.size(); j++)
@@ -238,9 +211,7 @@ public class SkillList extends EditableInventory {
for (int j = 0; j < lore.size(); j++)
lore.set(j, ChatColor.GRAY + holders.apply(inv.getPlayer(), lore.get(j)));
- /*
- * generate item
- */
+ // Generate item
ItemStack item = skill.getSkill().getIcon();
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(holders.apply(inv.getPlayer(), getName()));
@@ -272,8 +243,9 @@ public class SkillList extends EditableInventory {
private final List skillSlots;
private final List slotSlots;
- private boolean binding;
+ //The skill the player Selected
private ClassSkill selected;
+ private int page = 0;
public SkillViewerInventory(PlayerData playerData, EditableInventory editable) {
super(playerData, editable);
@@ -281,24 +253,23 @@ public class SkillList extends EditableInventory {
skills = new ArrayList<>(playerData.getProfess().getSkills());
skillSlots = getEditable().getByFunction("skill").getSlots();
slotSlots = getEditable().getByFunction("slot").getSlots();
+ selected = skills.get(page * skillSlots.size());
}
@Override
public String calculateName() {
- return getName();
+ return getName().replace("{skill}", selected.getSkill().getName());
}
@Override
public void open() {
- int selectedSkillSlot = ((SkillItem) getEditable().getByFunction("skill")).selectedSkillSlot;
- selected = skills.get(mod(selectedSkillSlot + playerData.skillGuiDisplayOffset, skills.size()));
-
super.open();
}
@Override
public void whenClicked(InventoryClickEvent event, InventoryItem item) {
+ /*
if (skillSlots.contains(event.getRawSlot())
&& event.getRawSlot() != ((SkillItem) getEditable().getByFunction("skill")).selectedSkillSlot) {
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 2);
@@ -306,24 +277,26 @@ public class SkillList extends EditableInventory {
open();
return;
}
+ */
+
+ if (item.getFunction().equals("skill")) {
+ int index = skillSlots.size() * page + skillSlots.indexOf(event.getRawSlot());
+ player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 2);
+ selected = skills.get(index);
+ open();
+ return;
+ }
if (item.getFunction().equals("previous")) {
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 2);
- playerData.skillGuiDisplayOffset = (playerData.skillGuiDisplayOffset - 1) % skills.size();
+ page--;
open();
return;
}
if (item.getFunction().equals("next")) {
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 2);
- playerData.skillGuiDisplayOffset = (playerData.skillGuiDisplayOffset + 1) % skills.size();
- open();
- return;
- }
-
- if (item.getFunction().equals("switch")) {
- player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 2);
- binding = !binding;
+ page++;
open();
return;
}
@@ -331,51 +304,50 @@ public class SkillList extends EditableInventory {
/*
* binding or unbinding skills.
*/
- if (binding) {
- for (int index = 0; index < slotSlots.size(); index++) {
- int slot = slotSlots.get(index);
- if (event.getRawSlot() == slot) {
+ if (item.getFunction().equals("slot")) {
+ int index = slotSlots.indexOf(event.getRawSlot());
- // unbind if there is a current spell.
- if (event.getAction() == InventoryAction.PICKUP_HALF) {
- if (!playerData.hasSkillBound(index)) {
- MMOCore.plugin.configManager.getSimpleMessage("no-skill-bound").send(player);
- player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
- return;
- }
- player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2);
- playerData.unbindSkill(index);
- open();
- return;
- }
-
- if (selected == null)
- return;
-
- if (selected.getSkill().getTrigger().isPassive()) {
- MMOCore.plugin.configManager.getSimpleMessage("not-active-skill").send(player);
- player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
- return;
- }
-
- if (!playerData.hasSkillUnlocked(selected)) {
- MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(player);
- player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
- return;
- }
-
- player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2);
- playerData.setBoundSkill(index, selected);
- open();
+ // unbind if there is a current spell.
+ if (event.getAction() == InventoryAction.PICKUP_HALF) {
+ if (!playerData.hasSkillBound(index)) {
+ MMOCore.plugin.configManager.getSimpleMessage("no-skill-bound").send(player);
+ player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
return;
}
+
+ player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2);
+ playerData.unbindSkill(index);
+ open();
+ return;
}
- /*
- * upgrading a player skill
- */
- } else if (item.getFunction().equals("upgrade")) {
+ if (selected == null)
+ return;
+
+ if (selected.getSkill().getTrigger().isPassive()) {
+ MMOCore.plugin.configManager.getSimpleMessage("not-active-skill").send(player);
+ player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
+ return;
+ }
+
+ if (!playerData.hasSkillUnlocked(selected)) {
+ MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(player);
+ player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
+ return;
+ }
+
+ player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2);
+ playerData.setBoundSkill(index, selected);
+ open();
+ return;
+ }
+
+
+ /*
+ * upgrading a player skill
+ */
+ if (item.getFunction().equals("upgrade")) {
if (!playerData.hasSkillUnlocked(selected)) {
MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(player);
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
@@ -402,6 +374,7 @@ public class SkillList extends EditableInventory {
open();
}
}
+
}
private int mod(int x, int n) {
diff --git a/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java b/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java
index 385db1ce..6dfbfb56 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java
@@ -1,7 +1,5 @@
package net.Indyuce.mmocore.gui;
-import io.lumine.mythic.lib.api.item.ItemTag;
-import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerActivity;
import net.Indyuce.mmocore.api.player.PlayerData;
@@ -10,18 +8,24 @@ import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
-import net.Indyuce.mmocore.waypoint.CostType;
import net.Indyuce.mmocore.waypoint.Waypoint;
-import net.Indyuce.mmocore.waypoint.WaypointOption;
+import net.Indyuce.mmocore.waypoint.WaypointPath;
import org.apache.commons.lang.Validate;
+import org.bukkit.ChatColor;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
public class WaypointViewer extends EditableInventory {
public WaypointViewer() {
@@ -65,7 +69,7 @@ public class WaypointViewer extends EditableInventory {
public class WaypointItem extends SimplePlaceholderItem {
private final SimplePlaceholderItem noWaypoint, locked;
- private final WaypointItemHandler availWaypoint, notLinked, notDynamic, noStellium;
+ private final WaypointItemHandler availWaypoint, noStellium, notLinked, notDynamic, currentWayPoint;
public WaypointItem(ConfigurationSection config) {
super(Material.BARRIER, config);
@@ -74,15 +78,17 @@ public class WaypointViewer extends EditableInventory {
Validate.notNull(config.getConfigurationSection("locked"), "Could not load 'locked' config");
Validate.notNull(config.getConfigurationSection("not-a-destination"), "Could not load 'not-a-destination' config");
Validate.notNull(config.getConfigurationSection("not-dynamic"), "Could not load 'not-dynamic' config");
+ Validate.notNull(config.getConfigurationSection("current-waypoint"), "Could not load 'current-waypoint' config");
Validate.notNull(config.getConfigurationSection("not-enough-stellium"), "Could not load 'not-enough-stellium' config");
Validate.notNull(config.getConfigurationSection("display"), "Could not load 'display' config");
noWaypoint = new SimplePlaceholderItem(config.getConfigurationSection("no-waypoint"));
locked = new SimplePlaceholderItem(config.getConfigurationSection("locked"));
- notLinked = new WaypointItemHandler(config.getConfigurationSection("not-a-destination"));
- notDynamic = new WaypointItemHandler(config.getConfigurationSection("not-dynamic"));
- noStellium = new WaypointItemHandler(config.getConfigurationSection("not-enough-stellium"));
- availWaypoint = new WaypointItemHandler(config.getConfigurationSection("display"));
+ notLinked = new WaypointItemHandler(config.getConfigurationSection("not-a-destination"), true);
+ notDynamic = new WaypointItemHandler(config.getConfigurationSection("not-dynamic"), true);
+ currentWayPoint = new WaypointItemHandler(config.getConfigurationSection("current-waypoint"), true);
+ noStellium = new WaypointItemHandler(config.getConfigurationSection("not-enough-stellium"), false);
+ availWaypoint = new WaypointItemHandler(config.getConfigurationSection("display"), false);
}
@Override
@@ -99,19 +105,22 @@ public class WaypointViewer extends EditableInventory {
// Locked waypoint?
Waypoint waypoint = inv.waypoints.get(index);
+ if (inv.current != null && inv.current.equals(waypoint))
+ return currentWayPoint.display(inv, n);
+
if (!inv.getPlayerData().hasWaypoint(waypoint))
return locked.display(inv, n);
// Waypoints are not linked
- if (inv.current != null && !inv.current.hasDestination(waypoint))
+ if (inv.current != null && !inv.paths.containsKey(waypoint))
return notLinked.display(inv, n);
// Not dynamic waypoint
- if (inv.current == null && !waypoint.hasOption(WaypointOption.DYNAMIC))
+ if (inv.current == null && !inv.paths.containsKey(waypoint))
return notDynamic.display(inv, n);
- // Stellium cost
- if (waypoint.getCost(inv.current == null ? CostType.DYNAMIC_USE : CostType.NORMAL_USE) > inv.getPlayerData().getStellium())
+ //Normal cost
+ if (inv.paths.get(waypoint).getCost() > inv.getPlayerData().getStellium())
return noStellium.display(inv, n);
return availWaypoint.display(inv, n);
@@ -119,8 +128,11 @@ public class WaypointViewer extends EditableInventory {
}
public class WaypointItemHandler extends InventoryItem {
- public WaypointItemHandler(ConfigurationSection config) {
+ private final boolean onlyName;
+
+ public WaypointItemHandler(ConfigurationSection config, boolean onlyName) {
super(config);
+ this.onlyName = onlyName;
}
@Override
@@ -129,7 +141,21 @@ public class WaypointViewer extends EditableInventory {
// If a player can teleport to another waypoint given his location
Waypoint waypoint = inv.waypoints.get(inv.page * inv.getEditable().getByFunction("waypoint").getSlots().size() + n);
- return NBTItem.get(disp).addTag(new ItemTag("waypointId", waypoint.getId())).toItem();
+ ItemMeta meta = disp.getItemMeta();
+
+ // Add waypoint lore if not empty
+ if (!waypoint.getLore().isEmpty()) {
+ List lore = meta.getLore();
+ Placeholders placeholders = new Placeholders();
+ for (String str : waypoint.getLore())
+ lore.add(0, ChatColor.GRAY + placeholders.apply(inv.getPlayer(), str));
+ meta.setLore(lore);
+ }
+
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+ container.set(new NamespacedKey(MMOCore.plugin, "waypointId"), PersistentDataType.STRING, waypoint.getId());
+ disp.setItemMeta(meta);
+ return disp;
}
@Override
@@ -138,9 +164,12 @@ public class WaypointViewer extends EditableInventory {
Waypoint waypoint = inv.waypoints.get(inv.page * inv.getByFunction("waypoint").getSlots().size() + n);
holders.register("name", waypoint.getName());
- holders.register("current_cost", decimal.format(waypoint.getCost(inv.waypointCostType)));
- holders.register("normal_cost", decimal.format(waypoint.getCost(CostType.NORMAL_USE)));
- holders.register("dynamic_cost", decimal.format(waypoint.getCost(CostType.DYNAMIC_USE)));
+ if (!onlyName) {
+ holders.register("current_cost", inv.paths.get(waypoint).getCost());
+ holders.register("normal_cost", decimal.format(inv.paths.containsKey(waypoint) ? inv.paths.get(waypoint).getCost() : Double.POSITIVE_INFINITY));
+ holders.register("dynamic_cost", decimal.format(waypoint.getDynamicCost()));
+ holders.register("intermediary_waypoints", inv.paths.containsKey(waypoint) ? inv.paths.get(waypoint).displayIntermediaryWayPoints(inv.isDynamicUse()) : "None");
+ }
return holders;
}
@@ -148,8 +177,9 @@ public class WaypointViewer extends EditableInventory {
public class WaypointViewerInventory extends GeneratedInventory {
private final List waypoints = new ArrayList<>(MMOCore.plugin.waypointManager.getAll());
+ @Nullable
private final Waypoint current;
- private final CostType waypointCostType;
+ private final Map paths = new HashMap<>();
private int page;
@@ -157,7 +187,29 @@ public class WaypointViewer extends EditableInventory {
super(playerData, editable);
this.current = current;
- this.waypointCostType = current == null ? CostType.DYNAMIC_USE : CostType.NORMAL_USE;
+ if (current != null)
+ for (WaypointPath pathInfo : current.getAllPath())
+ paths.put(pathInfo.getFinalWaypoint(), pathInfo);
+
+ if (current == null) {
+
+ //Iterate through all the dynamic points and find all the points it is linked to and the path
+ HashMap dynamicPoints = new HashMap<>();
+ //We first check all the dynamic waypoints
+ for (Waypoint waypoint : waypoints) {
+ if (waypoint.mayBeUsedDynamically(playerData.getPlayer())) {
+ paths.put(waypoint, new WaypointPath(waypoint, waypoint.getDynamicCost()));
+ dynamicPoints.put(waypoint, waypoint.getDynamicCost());
+ }
+ }
+ for (Waypoint source : dynamicPoints.keySet()) {
+ for (WaypointPath target : source.getAllPath()) {
+ if (!paths.containsKey(target.getFinalWaypoint()) || paths.get(target.getFinalWaypoint()).getCost() > target.getCost() + dynamicPoints.get(source)) {
+ paths.put(target.getFinalWaypoint(), target.addCost(dynamicPoints.get(source)));
+ }
+ }
+ }
+ }
}
@Override
@@ -165,6 +217,10 @@ public class WaypointViewer extends EditableInventory {
return getName();
}
+ public boolean isDynamicUse() {
+ return current == null;
+ }
+
@Override
public void whenClicked(InventoryClickEvent event, InventoryItem item) {
if (item.getFunction().equals("next")) {
@@ -180,7 +236,10 @@ public class WaypointViewer extends EditableInventory {
}
if (item.getFunction().equals("waypoint")) {
- String tag = NBTItem.get(event.getCurrentItem()).getString("waypointId");
+ PersistentDataContainer container = event.getCurrentItem().getItemMeta().getPersistentDataContainer();
+ String tag = container.has(new NamespacedKey(MMOCore.plugin, "waypointId"), PersistentDataType.STRING) ?
+ container.get(new NamespacedKey(MMOCore.plugin, "waypointId"), PersistentDataType.STRING) : "";
+
if (tag.equals(""))
return;
@@ -198,20 +257,20 @@ public class WaypointViewer extends EditableInventory {
}
// Waypoint does not have target as destination
- if (current != null && !current.hasDestination(waypoint)) {
+ if (current != null && current.getPath(waypoint) == null) {
MMOCore.plugin.configManager.getSimpleMessage("cannot-teleport-to").send(player);
return;
}
// Not dynamic waypoint
- if (current == null && !waypoint.hasOption(WaypointOption.DYNAMIC)) {
+ if (current == null && !paths.containsKey(waypoint)) {
MMOCore.plugin.configManager.getSimpleMessage("not-dynamic-waypoint").send(player);
return;
}
// Stellium cost
- CostType costType = current == null ? CostType.DYNAMIC_USE : CostType.NORMAL_USE;
- double left = waypoint.getCost(costType) - playerData.getStellium();
+ double withdraw = paths.get(waypoint).getCost();
+ double left = withdraw - playerData.getStellium();
if (left > 0) {
MMOCore.plugin.configManager.getSimpleMessage("not-enough-stellium", "more", decimal.format(left)).send(player);
return;
@@ -221,7 +280,8 @@ public class WaypointViewer extends EditableInventory {
return;
player.closeInventory();
- playerData.warp(waypoint, costType);
+ playerData.warp(waypoint, withdraw);
+
}
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java b/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java
index f01e98e2..85be0d26 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java
@@ -59,7 +59,7 @@ public abstract class GeneratedInventory extends PluginInventory {
@Override
public Inventory getInventory() {
- Inventory inv = Bukkit.createInventory(this, editable.getSlots(), MythicLib.plugin.parseColors(calculateName()));
+ Inventory inv = Bukkit.createInventory(this, editable.getSlots(), MythicLib.plugin.getPlaceholderParser().parse(getPlayer(), calculateName()));
for (InventoryItem item : editable.getItems())
if (item.canDisplay(this))
diff --git a/src/main/java/net/Indyuce/mmocore/listener/BlockListener.java b/src/main/java/net/Indyuce/mmocore/listener/BlockListener.java
index 3553deca..2ff6c505 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/BlockListener.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/BlockListener.java
@@ -11,7 +11,7 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.experience.source.MineBlockExperienceSource;
import net.Indyuce.mmocore.loot.LootBuilder;
-import net.Indyuce.mmocore.loot.droptable.condition.ConditionInstance;
+import net.Indyuce.mmocore.loot.chest.condition.ConditionInstance;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
diff --git a/src/main/java/net/Indyuce/mmocore/listener/ClassTriggers.java b/src/main/java/net/Indyuce/mmocore/listener/ClassTriggers.java
index 44612aea..465b66cb 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/ClassTriggers.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/ClassTriggers.java
@@ -52,7 +52,7 @@ public class ClassTriggers implements Listener {
public void onAttack(PlayerAttackEvent event) {
for (Map.Entry entry : damageTriggers.entrySet())
if (event.getDamage().hasType(entry.getKey()))
- applyTriggers(event.getPlayer(), entry.getValue()); //, () -> new TriggerMetadata(event.getAttack(), event.getEntity())
+ applyTriggers(event.getPlayer(), entry.getValue(), () -> new TriggerMetadata(event.getAttack(), event.getEntity()));
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
@@ -88,6 +88,16 @@ public class ClassTriggers implements Listener {
return applyTriggers(PlayerData.get(player), type, triggerMetaProvider);
}
+ /**
+ * Apply class shortcut skills from a specific class trigger type.
+ *
+ * @param player Player triggering
+ * @param type Trigger type
+ * @param triggerMetaProvider Small optimization: if no shortcut skill is found
+ * with the corresponding trigger type, trigger meta
+ * is not calculated which saves computations
+ * @return Skill result or null if no shortcut skill was cast
+ */
@Nullable
private SkillResult applyTriggers(PlayerData player, ClassTriggerType type, Provider triggerMetaProvider) {
ClassTrigger trigger = player.getProfess().getClassTrigger(type);
diff --git a/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java b/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java
index b7f837d3..06cdf5ad 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java
@@ -1,22 +1,36 @@
package net.Indyuce.mmocore.listener;
-import org.bukkit.block.Chest;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.inventory.InventoryCloseEvent;
-
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.loot.chest.LootChest;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.Chest;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
public class LootableChestsListener implements Listener {
- @EventHandler
- public void a(InventoryCloseEvent event) {
- if (!(event.getInventory().getHolder() instanceof Chest))
- return;
- Chest chest = (Chest) event.getInventory().getHolder();
- LootChest lootChest = MMOCore.plugin.lootChests.getChest(chest.getLocation());
- if (lootChest != null)
- lootChest.expire(true);
- }
+ @EventHandler
+ public void expireOnClose(InventoryCloseEvent event) {
+ if (!(event.getInventory().getHolder() instanceof Chest))
+ return;
+
+ Chest chest = (Chest) event.getInventory().getHolder();
+ LootChest lootChest = MMOCore.plugin.lootChests.getChest(chest.getLocation());
+ if (lootChest != null)
+ lootChest.expire(true);
+ }
+
+ @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
+ public void noBreaking(BlockBreakEvent event) {
+ Block block = event.getBlock();
+ if (block.getType() == Material.CHEST) {
+ LootChest lootChest = MMOCore.plugin.lootChests.getChest(block.getLocation());
+ if (lootChest != null)
+ event.setCancelled(true);
+ }
+ }
}
diff --git a/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java b/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java
index f49dc3ca..c703c607 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java
@@ -52,7 +52,19 @@ public class WaypointsListener implements Listener {
return;
NBTItem nbtItem = NBTItem.get(event.getItem());
- if (Objects.equals(nbtItem.getString("MMOCoreItemId"), "WAYPOINT_BOOK"))
- InventoryManager.WAYPOINTS.newInventory(PlayerData.get(event.getPlayer())).open();
+ if (Objects.equals(nbtItem.getString("MMOCoreItemId"), "WAYPOINT_BOOK")) {
+ String waypointId = nbtItem.getString("WaypointBookId");
+ Waypoint waypoint = MMOCore.plugin.waypointManager.get(waypointId);
+ if (waypoint == null)
+ return;
+
+ PlayerData playerData = PlayerData.get(event.getPlayer());
+ if (playerData.hasWaypoint(waypoint))
+ return;
+
+ playerData.unlockWaypoint(waypoint);
+ event.getItem().setAmount(event.getItem().getAmount() - 1); // Consume item
+ MMOCore.plugin.configManager.getSimpleMessage("new-waypoint-book", "waypoint", waypoint.getName()).send(event.getPlayer());
+ }
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/listener/option/RedirectVanillaExp.java b/src/main/java/net/Indyuce/mmocore/listener/option/RedirectVanillaExp.java
index 7f697c80..49a19c45 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/option/RedirectVanillaExp.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/option/RedirectVanillaExp.java
@@ -16,7 +16,7 @@ public class RedirectVanillaExp implements Listener {
@EventHandler
public void a(PlayerExpChangeEvent event) {
- int a = (int) (event.getAmount() * ratio);
+ double a = (event.getAmount() * ratio);
if (a > 0)
PlayerData.get(event.getPlayer()).giveExperience(a, EXPSource.VANILLA);
}
diff --git a/src/main/java/net/Indyuce/mmocore/listener/profession/FishingListener.java b/src/main/java/net/Indyuce/mmocore/listener/profession/FishingListener.java
index 19ec3c4f..50c1405c 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/profession/FishingListener.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/profession/FishingListener.java
@@ -4,7 +4,6 @@ import io.lumine.mythic.lib.version.VersionSound;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.CustomPlayerFishEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.player.stats.StatType;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.loot.LootBuilder;
@@ -80,11 +79,11 @@ public class FishingListener implements Listener {
public FishingData(Player player, FishHook hook, FishingDropTable table) {
this.location = hook.getLocation();
- this.caught = table.getRandomItem();
+ this.caught = table.getRandomItem(PlayerData.get(player));
this.playerData = PlayerData.get(this.player = player);
this.hook = hook;
- this.fishStrength = (int) Math.floor(caught.rollTugs() * (1 - PlayerData.get(player).getStats().getStat(StatType.FISHING_STRENGTH) / 100));
+ this.fishStrength = (int) Math.floor(caught.rollTugs() * (1 - PlayerData.get(player).getStats().getStat("FISHING_STRENGTH") / 100));
this.experienceDropped = caught.rollExperience();
fishing.add(player.getUniqueId());
@@ -148,7 +147,7 @@ public class FishingListener implements Listener {
return;
}
- if (currentPulls == 0 && RANDOM.nextDouble() < PlayerData.get(player).getStats().getStat(StatType.CRITICAL_FISHING_CHANCE) / 100)
+ if (currentPulls == 0 && RANDOM.nextDouble() < PlayerData.get(player).getStats().getStat("CRITICAL_FISHING_CHANCE") / 100)
setCriticalFish();
// Check if enough pulls; if not, wait till the next fish event
@@ -165,7 +164,7 @@ public class FishingListener implements Listener {
(mainhand != null && mainhand.getType() == Material.FISHING_ROD) ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND, 1);
// Critical fishing failure
- if (!isCriticalFish() && RANDOM.nextDouble() < PlayerData.get(player).getStats().getStat(StatType.CRITICAL_FISHING_FAILURE_CHANCE) / 100) {
+ if (!isCriticalFish() && RANDOM.nextDouble() < PlayerData.get(player).getStats().getStat("CRITICAL_FISHING_FAILURE_CHANCE") / 100) {
player.setVelocity(hook.getLocation().subtract(player.getLocation()).toVector().setY(0).multiply(3).setY(.5));
hook.getWorld().spawnParticle(Particle.SMOKE_NORMAL, location, 24, 0, 0, 0, .08);
return;
diff --git a/src/main/java/net/Indyuce/mmocore/listener/profession/PlayerCollectStats.java b/src/main/java/net/Indyuce/mmocore/listener/profession/PlayerCollectStats.java
index ce81b963..795d6ed7 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/profession/PlayerCollectStats.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/profession/PlayerCollectStats.java
@@ -1,7 +1,8 @@
package net.Indyuce.mmocore.listener.profession;
-import java.util.Random;
-
+import io.lumine.mythic.lib.MythicLib;
+import net.Indyuce.mmocore.api.event.CustomBlockMineEvent;
+import net.Indyuce.mmocore.loot.chest.particle.SmallParticleEffect;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
@@ -11,10 +12,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
-import net.Indyuce.mmocore.api.event.CustomBlockMineEvent;
-import net.Indyuce.mmocore.api.player.stats.StatType;
-import net.Indyuce.mmocore.loot.chest.particle.SmallParticleEffect;
-import io.lumine.mythic.lib.MythicLib;
+import java.util.Random;
public class PlayerCollectStats implements Listener {
private static final Random random = new Random();
@@ -23,26 +21,26 @@ public class PlayerCollectStats implements Listener {
public void a(CustomBlockMineEvent event) {
Player player = event.getPlayer();
- // give haste if right enchant
- double h = event.getData().getStats().getStat(StatType.GATHERING_HASTE);
+ // Give haste if right enchant
+ double h = event.getData().getStats().getStat("GATHERING_HASTE");
if (h > 0 && random.nextDouble() < h * .045) {
new SmallParticleEffect(player, Particle.SPELL_INSTANT);
player.removePotionEffect(PotionEffectType.FAST_DIGGING);
player.addPotionEffect(new PotionEffect(PotionEffectType.FAST_DIGGING, (int) (10 * h), (int) (1 + h / 7)));
}
- // drop more items if fortune enchant
- double f = event.getData().getStats().getStat(StatType.FORTUNE);
+ // Drop more items if fortune enchant
+ double f = event.getData().getStats().getStat("FORTUNE");
if (f > 0 && random.nextDouble() < f * .045) {
int a = (int) (1.5 * Math.sqrt(f / 1.1));
for (ItemStack item : event.getDrops())
item.setAmount(item.getAmount() + a);
}
- if(MythicLib.plugin.getVersion().getWrapper().isCropFullyGrown(event.getBlock()))
- {
- // drop more items if fortune enchant
- double l = event.getData().getStats().getStat(StatType.LUCK_OF_THE_FIELD);
+ if (MythicLib.plugin.getVersion().getWrapper().isCropFullyGrown(event.getBlock())) {
+
+ // Drop more CROP items
+ double l = event.getData().getStats().getStat("LUCK_OF_THE_FIELD");
if (l > 0 && random.nextDouble() < l * .045) {
int a = (int) (1.5 * Math.sqrt(l / 1.1));
Location loc = event.getBlock().getLocation().add(.5, .1, .5);
diff --git a/src/main/java/net/Indyuce/mmocore/loot/chest/ChestTier.java b/src/main/java/net/Indyuce/mmocore/loot/chest/ChestTier.java
index 770c7986..de9ea2a3 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/chest/ChestTier.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/ChestTier.java
@@ -10,8 +10,7 @@ public class ChestTier {
private final TierEffect effect;
private final ScalingFormula capacity;
private final DropTable table;
-
- public final double chance;
+ private final double chance;
public ChestTier(ConfigurationSection config) {
effect = config.isConfigurationSection("effect") ? new TierEffect(config.getConfigurationSection("effect")) : null;
@@ -24,6 +23,10 @@ public class ChestTier {
return capacity.calculate(player.getLevel());
}
+ public double getChance() {
+ return chance;
+ }
+
public DropTable getDropTable() {
return table;
}
diff --git a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java
index 8c6d2ed3..3277d05e 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java
@@ -2,6 +2,7 @@ package net.Indyuce.mmocore.loot.chest;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.SoundEvent;
+import net.Indyuce.mmocore.util.HashableLocation;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -9,7 +10,6 @@ import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.block.data.BlockData;
-import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import javax.annotation.Nullable;
@@ -20,7 +20,7 @@ public class LootChest {
private final ReplacedBlock block;
@Nullable
private final BukkitRunnable effectRunnable;
- private final long date = System.currentTimeMillis();
+ private final BukkitRunnable closeRunnable;
private boolean active = true;
@@ -36,6 +36,13 @@ public class LootChest {
this.region = region;
this.block = new ReplacedBlock(block);
this.effectRunnable = tier.hasEffect() ? tier.getEffect().startNewRunnable(block.getLocation().add(.5, .5, .5)) : null;
+ closeRunnable = new BukkitRunnable() {
+ @Override
+ public void run() {
+ expire(false);
+ }
+ };
+ closeRunnable.runTaskLater(MMOCore.plugin, MMOCore.plugin.configManager.lootChestExpireTime);
}
public ChestTier getTier() {
@@ -50,15 +57,8 @@ public class LootChest {
return region;
}
- public boolean hasPlayerNearby() {
- for (Player player : block.loc.getWorld().getPlayers())
- if (player.getLocation().distanceSquared(block.loc) < 625)
- return true;
- return false;
- }
-
- public boolean shouldExpire() {
- return System.currentTimeMillis() - date > MMOCore.plugin.configManager.lootChestExpireTime;
+ public boolean isActive() {
+ return active;
}
/**
@@ -76,10 +76,14 @@ public class LootChest {
Validate.isTrue(active, "Chest has already expired");
active = false;
+ // Close runnable
+ if (!closeRunnable.isCancelled())
+ closeRunnable.cancel();
+
// If a player is responsible of closing the chest, close it with sound
if (player) {
- MMOCore.plugin.soundManager.getSound(SoundEvent.CLOSE_LOOT_CHEST).playAt(block.loc);
- block.loc.getWorld().spawnParticle(Particle.CRIT, block.loc.clone().add(.5, .5, .5), 16, 0, 0, 0, .5);
+ MMOCore.plugin.soundManager.getSound(SoundEvent.CLOSE_LOOT_CHEST).playAt(block.loc.bukkit());
+ block.loc.getWorld().spawnParticle(Particle.CRIT, block.loc.bukkit().add(.5, .5, .5), 16, 0, 0, 0, .5);
}
/*
@@ -87,7 +91,7 @@ public class LootChest {
* off and accumulate on the ground (+during dev phase)
*/
else
- ((Chest) block.loc.getBlock().getState()).getBlockInventory().clear();
+ ((Chest) block.loc.bukkit().getBlock().getState()).getBlockInventory().clear();
block.restore();
if (effectRunnable != null)
@@ -97,22 +101,28 @@ public class LootChest {
public static class ReplacedBlock {
private final Material material;
private final BlockData data;
- private final Location loc;
+ private final HashableLocation loc;
public ReplacedBlock(Block block) {
this.material = block.getType();
this.data = block.getBlockData();
- this.loc = block.getLocation();
+ this.loc = new HashableLocation(block.getLocation());
}
+ public HashableLocation getLocation() {
+ return loc;
+ }
+
+ @Deprecated
public boolean matches(Location loc) {
- return this.loc.getWorld().equals(loc.getWorld()) && this.loc.getBlockX() == loc.getBlockX() && this.loc.getBlockY() == loc.getBlockY()
- && this.loc.getBlockZ() == loc.getBlockZ();
+ return this.loc.getWorld().equals(loc.getWorld()) && this.loc.getX() == loc.getBlockX() && this.loc.getY() == loc.getBlockY()
+ && this.loc.getZ() == loc.getBlockZ();
}
public void restore() {
- loc.getBlock().setType(material);
- loc.getBlock().setBlockData(data);
+ Block block = loc.bukkit().getBlock();
+ block.setType(material);
+ block.setBlockData(data);
}
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java
index ef7e8217..8b39522e 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java
@@ -13,6 +13,7 @@ import org.bukkit.block.Chest;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
+import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.logging.Level;
@@ -33,7 +34,7 @@ public class LootChestRegion {
}
};
- private static final Random random = new Random();
+ private static final Random RANDOM = new Random();
public LootChestRegion(ConfigurationSection config) {
Validate.notNull(config, "Could not load config");
@@ -85,7 +86,7 @@ public class LootChestRegion {
player.setLastActivity(PlayerActivity.LOOT_CHEST_SPAWN);
// First randomly determine the chest tier
- ChestTier tier = rollTier();
+ ChestTier tier = rollTier(player);
// Find a random location, 20 trials max
Location location = getRandomLocation(player.getPlayer().getLocation());
@@ -108,7 +109,7 @@ public class LootChestRegion {
location.getBlock().setType(Material.CHEST);
Chest chest = (Chest) location.getBlock().getState();
tier.getDropTable().collect(builder).forEach(item -> {
- Integer slot = slots.get(random.nextInt(slots.size()));
+ Integer slot = slots.get(RANDOM.nextInt(slots.size()));
chest.getInventory().setItem(slot, item);
slots.remove(slot);
});
@@ -116,17 +117,39 @@ public class LootChestRegion {
MMOCore.plugin.lootChests.register(lootChest);
}
- // TODO stat to increase chance to get higher tiers?
- public ChestTier rollTier() {
+ /**
+ * @param player Player rolling the tier
+ * @return A randomly picked tiers taking into account tier spawn rates
+ * and the player Chance attribute
+ */
+ @NotNull
+ public ChestTier rollTier(PlayerData player) {
+ double chance = player.getStats().getStat("CHANCE") * MMOCore.plugin.configManager.lootChestsChanceWeight;
- double s = 0;
+ double sum = 0;
+ for (ChestTier tier : tiers)
+ sum += getTierCoefficient(tier.getChance(), chance);
+ Validate.isTrue(sum > 0, "No chest tier was found");
+
+ double cummulated = 0;
for (ChestTier tier : tiers) {
- if (random.nextDouble() < tier.chance / (1 - s))
+ cummulated += getTierCoefficient(tier.getChance(), chance);
+ if (RANDOM.nextDouble() < cummulated / sum)
return tier;
- s += tier.chance;
}
- return tiers.stream().findAny().orElse(null);
+ throw new RuntimeException("Could not roll chest tier");
+ }
+
+ private static final double CHANCE_COEF = 7 / 100;
+
+ /**
+ * - Chance = 0 | tier coefficient is left unchanged.
+ * - Chance -> +oo | all tier coefficients are the same (1)
+ * - Chance = 50 | coefficients become their square roots
+ */
+ private double getTierCoefficient(double initialTierChance, double chance) {
+ return Math.pow(initialTierChance, 1 / Math.pow(1 + CHANCE_COEF * chance, 1 / 3));
}
public Location getRandomLocation(Location center) {
@@ -151,9 +174,9 @@ public class LootChestRegion {
* Chooses a random direction and get the block in
* that direction which has the same height as the player
*/
- double a = random.nextDouble() * 2 * Math.PI;
+ double a = RANDOM.nextDouble() * 2 * Math.PI;
Vector dir = new Vector(Math.cos(a), 0, Math.sin(a))
- .multiply(algOptions.minRange + random.nextDouble() * (algOptions.maxRange - algOptions.minRange));
+ .multiply(algOptions.minRange + RANDOM.nextDouble() * (algOptions.maxRange - algOptions.minRange));
Location random = center.add(dir);
/*
diff --git a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/BiomeCondition.java b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/BiomeCondition.java
similarity index 91%
rename from src/main/java/net/Indyuce/mmocore/loot/droptable/condition/BiomeCondition.java
rename to src/main/java/net/Indyuce/mmocore/loot/chest/condition/BiomeCondition.java
index 7da08703..1c55c5fc 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/BiomeCondition.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/BiomeCondition.java
@@ -1,4 +1,4 @@
-package net.Indyuce.mmocore.loot.droptable.condition;
+package net.Indyuce.mmocore.loot.chest.condition;
import java.util.Arrays;
import java.util.List;
diff --git a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/Condition.java b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/Condition.java
similarity index 84%
rename from src/main/java/net/Indyuce/mmocore/loot/droptable/condition/Condition.java
rename to src/main/java/net/Indyuce/mmocore/loot/chest/condition/Condition.java
index d1445364..b924cea2 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/Condition.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/Condition.java
@@ -1,4 +1,4 @@
-package net.Indyuce.mmocore.loot.droptable.condition;
+package net.Indyuce.mmocore.loot.chest.condition;
import io.lumine.mythic.lib.api.MMOLineConfig;
diff --git a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/ConditionInstance.java b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/ConditionInstance.java
similarity index 93%
rename from src/main/java/net/Indyuce/mmocore/loot/droptable/condition/ConditionInstance.java
rename to src/main/java/net/Indyuce/mmocore/loot/chest/condition/ConditionInstance.java
index 937b9475..1666ee8e 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/ConditionInstance.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/ConditionInstance.java
@@ -1,4 +1,4 @@
-package net.Indyuce.mmocore.loot.droptable.condition;
+package net.Indyuce.mmocore.loot.chest.condition;
import java.util.List;
import java.util.stream.Stream;
diff --git a/src/main/java/net/Indyuce/mmocore/loot/chest/condition/DistanceCondition.java b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/DistanceCondition.java
new file mode 100644
index 00000000..21e46f8f
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/loot/chest/condition/DistanceCondition.java
@@ -0,0 +1,34 @@
+package net.Indyuce.mmocore.loot.chest.condition;
+
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.entity.Entity;
+
+public class DistanceCondition extends Condition{
+ private final Location location;
+ private final double distance;
+
+ public DistanceCondition(MMOLineConfig config) {
+ super(config);
+ Validate.isTrue(config.contains("world"));
+ Validate.isTrue(config.contains("x"));
+ Validate.isTrue(config.contains("y"));
+ Validate.isTrue(config.contains("z"));
+ Validate.isTrue(config.contains("distance"));
+ Validate.isTrue(Bukkit.getWorld(config.getString("world"))!=null,"This world doesn't exist");
+ location=new Location(Bukkit.getWorld(config.getString("world")),config.getDouble("x"),
+ config.getDouble("y"),config.getDouble("z"));
+ distance=config.getDouble("distance");
+ }
+
+ @Override
+ public boolean isMet(ConditionInstance entity) {
+ Entity entity1=entity.getEntity();
+ return entity1.getWorld().equals(location.getWorld())&&location.distance(entity1.getLocation()) collect(LootBuilder builder) {
for (DropItem item : drops)
- if (item.rollChance() && builder.getCapacity() >= item.getWeight()) {
+ if (item.rollChance(builder.getEntity()) && builder.getCapacity() >= item.getWeight()) {
item.collect(builder);
builder.reduceCapacity(item.getWeight());
}
diff --git a/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropItem.java b/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropItem.java
index 05d99b44..8febb7bb 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropItem.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropItem.java
@@ -2,41 +2,45 @@ package net.Indyuce.mmocore.loot.droptable.dropitem;
import java.util.Random;
+import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.loot.LootBuilder;
import net.Indyuce.mmocore.api.util.math.formula.RandomAmount;
import io.lumine.mythic.lib.api.MMOLineConfig;
public abstract class DropItem {
- protected static final Random random = new Random();
+ protected static final Random random = new Random();
- private final double chance, weight;
- private final RandomAmount amount;
+ private final double chance, weight;
+ private final RandomAmount amount;
- public DropItem(MMOLineConfig config) {
- chance = config.args().length > 0 ? Double.parseDouble(config.args()[0]) : 1;
- amount = config.args().length > 1 ? new RandomAmount(config.args()[1]) : new RandomAmount(1, 1);
- weight = config.args().length > 2 ? Double.parseDouble(config.args()[2]) : 0;
- }
+ public DropItem(MMOLineConfig config) {
+ chance = config.args().length > 0 ? Double.parseDouble(config.args()[0]) : 1;
+ amount = config.args().length > 1 ? new RandomAmount(config.args()[1]) : new RandomAmount(1, 1);
+ weight = config.args().length > 2 ? Double.parseDouble(config.args()[2]) : 0;
+ }
- public RandomAmount getAmount() {
- return amount;
- }
+ public RandomAmount getAmount() {
+ return amount;
+ }
- public double getChance() {
- return chance;
- }
+ public double getChance() {
+ return chance;
+ }
- public double getWeight() {
- return weight;
- }
+ public double getWeight() {
+ return weight;
+ }
- public int rollAmount() {
- return amount.calculateInt();
- }
+ public int rollAmount() {
+ return amount.calculateInt();
+ }
- public boolean rollChance() {
- return random.nextDouble() < chance;
- }
+ /**
+ * If the player chance is 0 the random value will remain the same. When he get lucks the chance gets closer to one.
+ */
+ public boolean rollChance(PlayerData player) {
+ return Math.pow(random.nextDouble(), 1 / Math.log(1 + player.getStats().getStat("CHANCE"))) < chance;
+ }
- public abstract void collect(LootBuilder builder);
+ public abstract void collect(LootBuilder builder);
}
diff --git a/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropTableDropItem.java b/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropTableDropItem.java
index 02123b38..e6c160f2 100644
--- a/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropTableDropItem.java
+++ b/src/main/java/net/Indyuce/mmocore/loot/droptable/dropitem/DropTableDropItem.java
@@ -3,7 +3,7 @@ package net.Indyuce.mmocore.loot.droptable.dropitem;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.loot.droptable.DropTable;
-import net.Indyuce.mmocore.loot.droptable.condition.ConditionInstance;
+import net.Indyuce.mmocore.loot.chest.condition.ConditionInstance;
import net.Indyuce.mmocore.loot.LootBuilder;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.apache.commons.lang.Validate;
diff --git a/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java b/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java
index 96a41f87..137160c3 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java
@@ -27,6 +27,7 @@ public class ConfigManager {
public String partyChatPrefix, noSkillBoundPlaceholder;
public ChatColor staminaFull, staminaHalf, staminaEmpty;
public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown;
+ public double lootChestsChanceWeight;
private final FileConfiguration messages;
private final boolean chatInput;
@@ -93,10 +94,11 @@ public class ConfigManager {
chatInput = MMOCore.plugin.getConfig().getBoolean("use-chat-input");
partyChatPrefix = MMOCore.plugin.getConfig().getString("party.chat-prefix");
combatLogTimer = MMOCore.plugin.getConfig().getInt("combat-log.timer") * 1000L;
- lootChestExpireTime = Math.max(MMOCore.plugin.getConfig().getInt("loot-chests.chest-expire-time"), 1) * 1000L;
+ lootChestExpireTime = Math.max(MMOCore.plugin.getConfig().getInt("loot-chests.chest-expire-time"), 1) * 20;
lootChestPlayerCooldown = (long) MMOCore.plugin.getConfig().getDouble("player-cooldown") * 1000L;
globalSkillCooldown = MMOCore.plugin.getConfig().getLong("global-skill-cooldown") * 50;
noSkillBoundPlaceholder = getSimpleMessage("no-skill-placeholder").message();
+ lootChestsChanceWeight = MMOCore.plugin.getConfig().getDouble("chance-stat-weight.loot-chests");
staminaFull = getColorOrDefault("stamina-whole", ChatColor.GREEN);
staminaHalf = getColorOrDefault("stamina-half", ChatColor.DARK_GREEN);
diff --git a/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java b/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java
index 4dab3230..96317c16 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java
@@ -1,33 +1,36 @@
package net.Indyuce.mmocore.manager;
-import java.util.*;
-import java.util.logging.Level;
-
-import org.bukkit.Location;
-import org.bukkit.configuration.file.FileConfiguration;
-
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigFile;
import net.Indyuce.mmocore.loot.chest.LootChest;
import net.Indyuce.mmocore.loot.chest.LootChestRegion;
+import net.Indyuce.mmocore.util.HashableLocation;
+import org.bukkit.Location;
+import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.logging.Level;
+
public class LootChestManager implements MMOCoreManager {
- /**
- * Active loot chests in the server
- */
- private final Set active = new HashSet<>();
+ /**
+ * Active loot chests in the server
+ */
+ private final Map active = new HashMap<>();
- /**
- * Registered loot chest regions
- */
- private final Map regions = new HashMap<>();
+ /**
+ * Registered loot chest regions
+ */
+ private final Map regions = new HashMap<>();
- public boolean hasRegion(String id) {
- return regions.containsKey(id);
- }
+ public boolean hasRegion(String id) {
+ return regions.containsKey(id);
+ }
/**
* @return Region with specific identifier
@@ -38,48 +41,42 @@ public class LootChestManager implements MMOCoreManager {
return Objects.requireNonNull(regions.get(id), "Could not find region with ID '" + id + "'");
}
- public Collection getRegions() {
- return regions.values();
- }
+ public Collection getRegions() {
+ return regions.values();
+ }
- public Set getActive() {
- return active;
- }
+ public Collection getActive() {
+ return active.values();
+ }
- public void register(LootChest chest) {
- active.add(chest);
- }
+ public void register(LootChest chest) {
+ active.put(chest.getBlock().getLocation(), chest);
+ }
- public void unregister(LootChest chest) {
- active.remove(chest);
- }
+ public void unregister(LootChest chest) {
+ active.remove(chest.getBlock().getLocation());
+ }
- @Nullable
- public LootChest getChest(Location loc) {
+ @Nullable
+ public LootChest getChest(Location loc) {
+ return active.get(new HashableLocation(loc));
+ }
- for (LootChest chest : active)
- if (chest.getBlock().matches(loc))
- return chest;
+ @Override
+ public void initialize(boolean clearBefore) {
+ if (clearBefore) {
+ regions.values().forEach(region -> region.getRunnable().cancel());
+ regions.clear();
+ }
- return null;
- }
-
- @Override
- public void initialize(boolean clearBefore) {
- if (clearBefore) {
- regions.values().forEach(region -> region.getRunnable().cancel());
- regions.clear();
- }
-
- FileConfiguration config = new ConfigFile("loot-chests").getConfig();
- for (String key : config.getKeys(false))
- try {
- LootChestRegion region = new LootChestRegion(config.getConfigurationSection(key));
- regions.put(region.getId(), region);
- } catch (IllegalArgumentException exception) {
- MMOCore.plugin.getLogger().log(Level.WARNING,
- "An error occured while trying to load loot chest region '" + key + "': " + exception.getMessage());
- }
-
- }
+ FileConfiguration config = new ConfigFile("loot-chests").getConfig();
+ for (String key : config.getKeys(false))
+ try {
+ LootChestRegion region = new LootChestRegion(config.getConfigurationSection(key));
+ regions.put(region.getId(), region);
+ } catch (IllegalArgumentException exception) {
+ MMOCore.plugin.getLogger().log(Level.WARNING,
+ "An error occured while trying to load loot chest region '" + key + "': " + exception.getMessage());
+ }
+ }
}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/MMOLoadManager.java b/src/main/java/net/Indyuce/mmocore/manager/MMOLoadManager.java
index be250b6a..30f506d4 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/MMOLoadManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/MMOLoadManager.java
@@ -11,7 +11,7 @@ import org.bukkit.configuration.ConfigurationSection;
import com.google.gson.JsonParseException;
import net.Indyuce.mmocore.api.block.BlockType;
-import net.Indyuce.mmocore.loot.droptable.condition.Condition;
+import net.Indyuce.mmocore.loot.chest.condition.Condition;
import net.Indyuce.mmocore.loot.droptable.dropitem.DropItem;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.api.load.DefaultMMOLoader;
diff --git a/src/main/java/net/Indyuce/mmocore/manager/StatManager.java b/src/main/java/net/Indyuce/mmocore/manager/StatManager.java
new file mode 100644
index 00000000..ee23b9d6
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/manager/StatManager.java
@@ -0,0 +1,71 @@
+package net.Indyuce.mmocore.manager;
+
+import io.lumine.mythic.lib.MythicLib;
+import net.Indyuce.mmocore.api.ConfigFile;
+import net.Indyuce.mmocore.player.stats.StatInfo;
+import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
+import net.Indyuce.mmocore.experience.Profession;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.text.DecimalFormat;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class StatManager implements MMOCoreManager {
+ private final Map loaded = new HashMap<>();
+
+ @Override
+ public void initialize(boolean clearBefore) {
+ if (clearBefore)
+ loaded.clear();
+
+ FileConfiguration config = new ConfigFile("stats").getConfig();
+
+ // Read decimal formats
+ for (String key : config.getConfigurationSection("decimal-format").getKeys(false))
+ registerDecimalFormat(key, MythicLib.plugin.getMMOConfig().newDecimalFormat(config.getString("decimal-format." + key)));
+
+ // Read default formulas
+ for (String key : config.getConfigurationSection("default").getKeys(false))
+ registerDefaultFormula(key, new LinearValue(config.getConfigurationSection("default." + key)));
+ }
+
+ public Collection getLoaded() {
+ return loaded.values();
+ }
+
+ @Nullable
+ public StatInfo getInfo(String stat) {
+ return loaded.get(stat);
+ }
+
+ public void registerProfession(String stat, Profession profession) {
+ compute(stat).profession = profession;
+ }
+
+ public void registerDefaultFormula(String stat, LinearValue defaultFormula) {
+ compute(stat).defaultInfo = defaultFormula;
+ }
+
+ public void registerDecimalFormat(String stat, DecimalFormat format) {
+ compute(stat).format = format;
+ }
+
+ /**
+ * @return A stat info for the specified stat. If it doesn't
+ * exist when method is called, it is registered into the map
+ */
+ @NotNull
+ private StatInfo compute(String stat) {
+ StatInfo found = loaded.get(stat);
+ if (found != null)
+ return found;
+
+ StatInfo newInfo = new StatInfo(stat);
+ loaded.put(stat, newInfo);
+ return newInfo;
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/WaypointManager.java b/src/main/java/net/Indyuce/mmocore/manager/WaypointManager.java
index 9ff105f0..97bb5a00 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/WaypointManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/WaypointManager.java
@@ -25,6 +25,7 @@ public class WaypointManager implements MMOCoreManager {
return waypoints.containsKey(id);
}
+ @Nullable
public Waypoint get(String id) {
return waypoints.get(id);
}
@@ -55,5 +56,12 @@ public class WaypointManager implements MMOCoreManager {
} catch (RuntimeException exception) {
MMOCore.log(Level.WARNING, "Could not load waypoint '" + key + "': " + exception.getMessage());
}
+
+ for (Waypoint waypoint : waypoints.values())
+ try {
+ waypoint.postLoad();
+ } catch (RuntimeException exception) {
+ MMOCore.log(Level.WARNING, "Could not post-load waypoint '" + waypoint.getId() + "': " + exception.getMessage());
+ }
}
}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java
index df083d7a..66d5ed47 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java
@@ -9,7 +9,6 @@ import net.Indyuce.mmocore.api.player.OfflinePlayerData;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.player.profess.SavedClassInformation;
-import net.Indyuce.mmocore.api.player.stats.StatType;
import net.Indyuce.mmocore.guild.provided.Guild;
import net.Indyuce.mmocore.manager.data.PlayerDataManager;
import net.Indyuce.mmocore.manager.data.mysql.MySQLTableEditor.Table;
@@ -35,6 +34,13 @@ public class MySQLPlayerDataManager extends PlayerDataManager {
try {
MMOCore.sqlDebug("Loading data for: '" + data.getUniqueId() + "'...");
+ // Initialize custom resources
+ if (!data.hasUsedTemporaryData()) {
+ data.setMana(data.getStats().getStat("MAX_MANA"));
+ data.setStamina(data.getStats().getStat("MAX_STAMINA"));
+ data.setStellium(data.getStats().getStat("MAX_STELLIUM"));
+ }
+
if (!result.next()) {
data.setLevel(getDefaultData().getLevel());
data.setClassPoints(getDefaultData().getClassPoints());
@@ -44,12 +50,6 @@ public class MySQLPlayerDataManager extends PlayerDataManager {
data.setExperience(0);
data.getQuestData().updateBossBar();
- if (!data.hasUsedTemporaryData()) {
- data.setMana(data.getStats().getStat(StatType.MAX_MANA));
- data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA));
- data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM));
- }
-
data.setFullyLoaded();
MMOCore.sqlDebug("Loaded DEFAULT data for: '" + data.getUniqueId() + "' as no saved data was found.");
return;
@@ -69,12 +69,6 @@ public class MySQLPlayerDataManager extends PlayerDataManager {
json.entrySet().forEach(entry -> data.getItemClaims().put(entry.getKey(), entry.getValue().getAsInt()));
}
- if (!data.hasUsedTemporaryData()) {
- data.setMana(data.getStats().getStat(StatType.MAX_MANA));
- data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA));
- data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM));
- }
-
if (!isEmpty(result.getString("guild"))) {
Guild guild = provider.getGuildManager().getGuild(result.getString("guild"));
data.setGuild(guild.getMembers().has(data.getUniqueId()) ? guild : null);
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java
index 7efe48fa..e8eaa739 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java
@@ -6,7 +6,6 @@ import net.Indyuce.mmocore.api.player.OfflinePlayerData;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.player.profess.SavedClassInformation;
-import net.Indyuce.mmocore.api.player.stats.StatType;
import net.Indyuce.mmocore.guild.provided.Guild;
import net.Indyuce.mmocore.manager.data.DataProvider;
import net.Indyuce.mmocore.manager.data.PlayerDataManager;
@@ -41,9 +40,9 @@ public class YAMLPlayerDataManager extends PlayerDataManager {
data.setClass(MMOCore.plugin.classManager.get(config.getString("class")));
if (!data.hasUsedTemporaryData()) {
- data.setMana(data.getStats().getStat(StatType.MAX_MANA));
- data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA));
- data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM));
+ data.setMana(data.getStats().getStat("MAX_MANA"));
+ data.setStamina(data.getStats().getStat("MAX_STAMINA"));
+ data.setStellium(data.getStats().getStat("MAX_STELLIUM"));
}
if (config.contains("guild")) {
diff --git a/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java b/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java
index c6e72d9e..5b69846d 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java
@@ -7,11 +7,10 @@ import net.Indyuce.mmocore.api.block.BlockInfo.RegeneratingBlock;
import net.Indyuce.mmocore.api.block.BlockType;
import net.Indyuce.mmocore.api.block.SkullBlockType;
import net.Indyuce.mmocore.api.block.VanillaBlockType;
-import net.Indyuce.mmocore.loot.droptable.condition.Condition;
-import net.Indyuce.mmocore.loot.droptable.condition.ConditionInstance;
+import net.Indyuce.mmocore.loot.chest.condition.Condition;
+import net.Indyuce.mmocore.loot.chest.condition.ConditionInstance;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import io.lumine.mythic.lib.api.MMOLineConfig;
-import net.Indyuce.mmocore.manager.profession.SpecificProfessionManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.Block;
diff --git a/src/main/java/net/Indyuce/mmocore/manager/profession/EnchantManager.java b/src/main/java/net/Indyuce/mmocore/manager/profession/EnchantManager.java
index 592d69f4..25b3605f 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/profession/EnchantManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/profession/EnchantManager.java
@@ -4,17 +4,19 @@ import io.lumine.mythic.lib.MythicLib;
import net.Indyuce.mmocore.MMOCore;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
public class EnchantManager extends SpecificProfessionManager {
- private final Map base = new HashMap<>();
+ private final Map base = new HashMap<>();
- public EnchantManager() {
- super("base-enchant-exp");
- }
+ public EnchantManager() {
+ super("base-enchant-exp");
+ }
@Override
public void loadProfessionConfiguration(ConfigurationSection config) {
@@ -27,17 +29,20 @@ public class EnchantManager extends SpecificProfessionManager {
}
}
- public void registerBaseExperience(Enchantment enchant, double value) {
- base.put(enchant, value);
- }
+ public void registerBaseExperience(Enchantment enchant, double value) {
+ base.put(enchant, value);
+ }
- public double getBaseExperience(Enchantment enchant) {
- return base.get(enchant);
- }
+ @NotNull
+ public double getBaseExperience(Enchantment enchant) {
+ // Can be null if argument passed is an enchant with no config attached to it
+ @Nullable Double found = base.get(enchant);
+ return found == null ? 0 : found;
+ }
- @Override
- public void initialize(boolean clearBefore) {
- if (clearBefore)
- base.clear();
- }
+ @Override
+ public void initialize(boolean clearBefore) {
+ if (clearBefore)
+ base.clear();
+ }
}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/profession/FishingManager.java b/src/main/java/net/Indyuce/mmocore/manager/profession/FishingManager.java
index aa9e1738..e794c9e3 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/profession/FishingManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/profession/FishingManager.java
@@ -2,8 +2,9 @@ package net.Indyuce.mmocore.manager.profession;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
-import net.Indyuce.mmocore.loot.droptable.condition.Condition;
-import net.Indyuce.mmocore.loot.droptable.condition.ConditionInstance;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.loot.chest.condition.Condition;
+import net.Indyuce.mmocore.loot.chest.condition.ConditionInstance;
import net.Indyuce.mmocore.loot.droptable.dropitem.fishing.FishingDropItem;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
@@ -29,6 +30,11 @@ public class FishingManager extends SpecificProfessionManager {
} catch (IllegalArgumentException exception) {
MMOCore.log(Level.WARNING, "Could not load fishing drop table " + key + ": " + exception.getMessage());
}
+
+ // Link fishing stats to this profession
+ MMOCore.plugin.statManager.registerProfession("FISHING_STRENGTH", getLinkedProfession());
+ MMOCore.plugin.statManager.registerProfession("CRITICAL_FISHING_CHANCE", getLinkedProfession());
+ MMOCore.plugin.statManager.registerProfession("CRITICAL_FISHING_FAILURE_CHANCE", getLinkedProfession());
}
public FishingDropTable calculateDropTable(Entity entity) {
@@ -44,7 +50,6 @@ public class FishingManager extends SpecificProfessionManager {
public static class FishingDropTable {
private final Set conditions = new HashSet<>();
private final List items = new ArrayList<>();
- private double maxWeight = 0;
public FishingDropTable(ConfigurationSection section) {
Validate.notNull(section, "Could not load config");
@@ -70,7 +75,6 @@ public class FishingManager extends SpecificProfessionManager {
try {
FishingDropItem dropItem = new FishingDropItem(new MMOLineConfig(str));
items.add(dropItem);
- maxWeight += dropItem.getItem().getWeight();
} catch (RuntimeException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING,
"Could not load item '" + str + "' from fishing drop table '" + id + "': " + exception.getMessage());
@@ -90,12 +94,25 @@ public class FishingManager extends SpecificProfessionManager {
return conditions;
}
- public FishingDropItem getRandomItem() {
- double randomCoefficient = RANDOM.nextDouble() * maxWeight;
+ /**
+ * The chance stat will make low weight items more
+ * likely to be chosen by the algorithm
+ *
+ * @return Randomly computed fishing drop item
+ */
+ public FishingDropItem getRandomItem(PlayerData player) {
+ double chance = player.getStats().getStat("CHANCE");
- for (FishingDropItem item : items)
- if ((randomCoefficient -= item.getItem().getWeight()) <= 0)
+ //chance=0 ->the tier.chance remains the same
+ //chance ->+inf -> the tier.chance becomes the same for everyone, uniform law
+ //chance=8-> tierChance=sqrt(tierChance)
+ double sum = 0;
+ double randomCoefficient=RANDOM.nextDouble();
+ for (FishingDropItem item : items) {
+ sum += Math.pow(item.getItem().getWeight(), 1 / Math.log(1 + chance));
+ if(sum format.send(member.getPlayer()));
});
}
-
- /**
- * Cancel damage of players from the same party
- *
- * @deprecated This should be useful with the {@link io.lumine.mythic.lib.comp.target.TargetRestriction} update
- */
- @Deprecated
- @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
- public void b(PlayerAttackEvent event) {
- LivingEntity entity = event.getEntity();
- if (entity instanceof Player && !entity.hasMetadata("NPC")) {
- PlayerData targetData = PlayerData.get((Player) event.getEntity());
- AbstractParty party = targetData.getParty();
- if (party != null && party.hasMember(event.getData().getPlayer()))
- event.setCancelled(true);
- }
- }
}
diff --git a/src/main/java/net/Indyuce/mmocore/player/Unlockable.java b/src/main/java/net/Indyuce/mmocore/player/Unlockable.java
index 6940d1a1..9349f308 100644
--- a/src/main/java/net/Indyuce/mmocore/player/Unlockable.java
+++ b/src/main/java/net/Indyuce/mmocore/player/Unlockable.java
@@ -3,8 +3,8 @@ package net.Indyuce.mmocore.player;
import net.Indyuce.mmocore.api.player.PlayerData;
/**
- * Some item that can be unlocked. ALl unlockable are saved in the same list in
- * the player data. This useful list can be used for:
+ * Some item that can be unlocked. All unlockables are saved in the
+ * same list in the player data. This useful list can be used for:
* - waypoints
* - skill tree nodes
* - skills using skill books? TODO
@@ -15,8 +15,8 @@ import net.Indyuce.mmocore.api.player.PlayerData;
public interface Unlockable {
/**
- * Format being used is the minecraft's default
- * namespaced key format, e.g "skill_tree:strength_1_5"
+ * Format being used is the minecraft's default namespaced
+ * key format, e.g "skill_tree:strength_1_5" for readability
*/
String getUnlockNamespacedKey();
}
diff --git a/src/main/java/net/Indyuce/mmocore/player/stats/StatInfo.java b/src/main/java/net/Indyuce/mmocore/player/stats/StatInfo.java
new file mode 100644
index 00000000..078b4af7
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/player/stats/StatInfo.java
@@ -0,0 +1,71 @@
+package net.Indyuce.mmocore.player.stats;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
+import net.Indyuce.mmocore.experience.Profession;
+import org.jetbrains.annotations.NotNull;
+
+import java.text.DecimalFormat;
+import java.util.Objects;
+
+/**
+ * @author Jules
+ * @impl MMOCore used to have a giant enum of all the stat types
+ * which is now incompatible with MythicLib because the MMO plugins
+ * now have completely OPEN to edition numeric stat registries
+ */
+public class StatInfo {
+ public final String name;
+
+ /**
+ * Profession linked to that stat. Stats which have a profession linked to
+ * them do NOT scale on the main player level but rather on that specific
+ * profession level
+ */
+ public Profession profession;
+
+ /**
+ * Default formula for the stat
+ */
+ public LinearValue defaultInfo;
+
+ /**
+ * How that stat displays anywhere in GUIs
+ */
+ public DecimalFormat format;
+
+ private static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat("0.#");
+
+ public StatInfo(String name) {
+ this.name = name;
+ }
+
+ @NotNull
+ public String format(double d) {
+ return (format == null ? DEFAULT_DECIMAL_FORMAT : format).format(d);
+ }
+
+ @NotNull
+ public LinearValue getDefaultFormula() {
+ return defaultInfo == null ? LinearValue.ZERO : defaultInfo;
+ }
+
+ @NotNull
+ public static StatInfo valueOf(String str) {
+ StatInfo found = MMOCore.plugin.statManager.getInfo(str);
+ return found == null ? new StatInfo(str) : found;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ StatInfo statInfo = (StatInfo) o;
+ return name.equals(statInfo.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/AbstractQuest.java b/src/main/java/net/Indyuce/mmocore/quest/AbstractQuest.java
index a326f10f..93316ec2 100644
--- a/src/main/java/net/Indyuce/mmocore/quest/AbstractQuest.java
+++ b/src/main/java/net/Indyuce/mmocore/quest/AbstractQuest.java
@@ -3,4 +3,6 @@ package net.Indyuce.mmocore.quest;
public interface AbstractQuest {
public String getName();
+
+ public String getId();
}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java b/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java
new file mode 100644
index 00000000..1af7fdbc
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java
@@ -0,0 +1,40 @@
+package net.Indyuce.mmocore.quest;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.quest.Quest;
+import net.Indyuce.mmocore.quest.compat.QuestModule;
+import org.bukkit.entity.Player;
+
+public class MMOCoreQuestModule implements QuestModule {
+ @Override
+ public AbstractQuest getQuestOrThrow(String id) {
+ Quest quest=MMOCore.plugin.questManager.get(id);
+ if(quest==null)
+ return null;
+ return new MMOCoreQuest(quest);
+ }
+
+ @Override
+ public boolean hasCompletedQuest(String quest, Player player) {
+ return false;
+ }
+
+ public class MMOCoreQuest implements AbstractQuest {
+ Quest quest;
+
+ public MMOCoreQuest(Quest quest) {
+
+ this.quest = quest;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public String getId() {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/QuestModule.java b/src/main/java/net/Indyuce/mmocore/quest/QuestModule.java
deleted file mode 100644
index 1089f1db..00000000
--- a/src/main/java/net/Indyuce/mmocore/quest/QuestModule.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.Indyuce.mmocore.quest;
-
-import net.Indyuce.mmocore.api.player.PlayerData;
-
-public interface QuestModule> {
-
- /**
- * @return Quest with given name
- */
- public T getQuestOrThrow(String id);
-
- /**
- * @return Info about the completed quests from a specific player
- */
- public U getQuestData(PlayerData playerData);
-}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java b/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java
new file mode 100644
index 00000000..4c7e0366
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java
@@ -0,0 +1,32 @@
+package net.Indyuce.mmocore.quest;
+
+import net.Indyuce.mmocore.quest.compat.BeautyQuestsModule;
+import net.Indyuce.mmocore.quest.compat.BlackVeinQuestsModule;
+import net.Indyuce.mmocore.quest.compat.QuestCreatorModule;
+import net.Indyuce.mmocore.quest.compat.QuestModule;
+import org.bukkit.Bukkit;
+
+import javax.inject.Provider;
+
+public enum QuestModuleType {
+ MMOCORE("MMOCore", MMOCoreQuestModule::new),
+ QUESTS("Quests", BlackVeinQuestsModule::new),
+ BEAUTY_QUEST("BeautyQuests", BeautyQuestsModule::new),
+ QUEST_CREATOR("QuestCreator", QuestCreatorModule::new);
+
+ private final String pluginName;
+ private final Provider provider;
+
+ QuestModuleType(String pluginName, Provider provider) {
+ this.pluginName = pluginName;
+ this.provider = provider;
+ }
+
+ public boolean isValid() {
+ return Bukkit.getPluginManager().getPlugin(pluginName) != null;
+ }
+
+ public QuestModule provideModule() {
+ return provider.get();
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestsModule.java b/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestsModule.java
new file mode 100644
index 00000000..b060cad3
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestsModule.java
@@ -0,0 +1,44 @@
+package net.Indyuce.mmocore.quest.compat;
+
+import fr.skytasul.quests.api.QuestsAPI;
+import fr.skytasul.quests.players.PlayerAccount;
+import fr.skytasul.quests.players.PlayerQuestDatas;
+import fr.skytasul.quests.players.PlayersManager;
+import fr.skytasul.quests.structure.Quest;
+import net.Indyuce.mmocore.quest.AbstractQuest;
+import org.bukkit.entity.Player;
+
+public class BeautyQuestsModule implements QuestModule {
+
+ @Override
+ public BeautyQuestQuest getQuestOrThrow(String questId) {
+ Quest quest = QuestsAPI.getQuests().getQuest(Integer.parseInt(questId));
+ return quest == null ? null : new BeautyQuestQuest(quest);
+ }
+
+ @Override
+ public boolean hasCompletedQuest(String questId, Player player) {
+ PlayerAccount account = PlayersManager.getPlayerAccount(player);
+ PlayerQuestDatas quest = account.getQuestDatas(QuestsAPI.getQuests().getQuest(Integer.parseInt(questId)));
+ return quest.isFinished();
+ }
+
+ public class BeautyQuestQuest implements AbstractQuest {
+
+ Quest quest;
+
+ public BeautyQuestQuest(Quest quest) {
+ this.quest = quest;
+ }
+
+ @Override
+ public String getName() {
+ return quest.getName();
+ }
+
+ @Override
+ public String getId() {
+ return String.valueOf(quest.getID());
+ }
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/compat/BlackVeinQuestsModule.java b/src/main/java/net/Indyuce/mmocore/quest/compat/BlackVeinQuestsModule.java
new file mode 100644
index 00000000..b074f97c
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/quest/compat/BlackVeinQuestsModule.java
@@ -0,0 +1,47 @@
+package net.Indyuce.mmocore.quest.compat;
+
+import me.blackvein.quests.Quest;
+import me.blackvein.quests.Quester;
+import me.blackvein.quests.Quests;
+import net.Indyuce.mmocore.quest.AbstractQuest;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+public class BlackVeinQuestsModule implements QuestModule {
+ private final Quests plugin = (Quests) Bukkit.getPluginManager().getPlugin("Quests");
+
+ @Override
+ public BlackVeinQuestQuest getQuestOrThrow(String id) {
+ Quests plugin = (Quests) Bukkit.getPluginManager().getPlugin("Quests");
+ return plugin.getQuestById(id) == null ? null : new BlackVeinQuestQuest(plugin.getQuestById(id));
+ }
+
+ @Override
+ public boolean hasCompletedQuest(String questId, Player player) {
+ Quester quester = plugin.getQuester(player.getUniqueId());
+
+ for (Quest quest : quester.getCompletedQuests())
+ if (quest.getId().equals(questId))
+ return true;
+
+ return false;
+ }
+
+ public class BlackVeinQuestQuest implements AbstractQuest {
+ private final Quest quest;
+
+ public BlackVeinQuestQuest(Quest quest) {
+ this.quest = quest;
+ }
+
+ @Override
+ public String getName() {
+ return quest.getName();
+ }
+
+ @Override
+ public String getId() {
+ return quest.getId();
+ }
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/compat/QuestCreatorModule.java b/src/main/java/net/Indyuce/mmocore/quest/compat/QuestCreatorModule.java
new file mode 100644
index 00000000..d4613bdd
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/quest/compat/QuestCreatorModule.java
@@ -0,0 +1,57 @@
+package net.Indyuce.mmocore.quest.compat;
+
+import com.guillaumevdn.questcreator.ConfigQC;
+import com.guillaumevdn.questcreator.data.user.QuestHistoryElement;
+import com.guillaumevdn.questcreator.data.user.UserQC;
+import com.guillaumevdn.questcreator.lib.model.ElementModel;
+import com.guillaumevdn.questcreator.lib.quest.QuestEndType;
+import net.Indyuce.mmocore.quest.AbstractQuest;
+import org.apache.commons.lang.Validate;
+import org.bukkit.entity.Player;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class QuestCreatorModule implements QuestModule {
+
+ @Override
+ public QuestCreatorQuest getQuestOrThrow(String id) {
+ return new QuestCreatorQuest(id);
+ }
+
+ @Override
+ public boolean hasCompletedQuest(String questId, Player player) {
+ UserQC playerData = UserQC.cachedOrNull(player);
+ Validate.notNull(playerData, "QuestCreator User hasn't been loaded!");
+
+ // Gets all the quests the player has succeeded at
+ List elements = playerData.getQuestHistory().getElements(questId, Arrays.asList(QuestEndType.SUCCESS), 0);
+ for (QuestHistoryElement el : elements)
+ if (el.getModelId().equals(questId))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * QC ElementModel corresponds to our quest and their
+ * quests correspond to our Quest progress class
+ */
+ public class QuestCreatorQuest implements AbstractQuest {
+ ElementModel questModel;
+
+ public QuestCreatorQuest(String modelId) {
+ questModel = ConfigQC.models.getElement(modelId).orNull();
+ }
+
+ @Override
+ public String getName() {
+ return questModel.getDisplayName().getId();
+ }
+
+ @Override
+ public String getId() {
+ return questModel.getId();
+ }
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java b/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java
new file mode 100644
index 00000000..d569db22
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java
@@ -0,0 +1,17 @@
+package net.Indyuce.mmocore.quest.compat;
+
+import net.Indyuce.mmocore.quest.AbstractQuest;
+import org.bukkit.entity.Player;
+
+public interface QuestModule {
+
+ /**
+ * @return Quest with given identifier
+ */
+ public T getQuestOrThrow(String id);
+
+ /**
+ * @return If a specific player has made a certain quest
+ */
+ public boolean hasCompletedQuest(String quest, Player player);
+}
diff --git a/src/main/java/net/Indyuce/mmocore/skill/ClassSkill.java b/src/main/java/net/Indyuce/mmocore/skill/ClassSkill.java
index 1d72842b..841c76a2 100644
--- a/src/main/java/net/Indyuce/mmocore/skill/ClassSkill.java
+++ b/src/main/java/net/Indyuce/mmocore/skill/ClassSkill.java
@@ -4,6 +4,7 @@ import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.player.cooldown.CooldownObject;
import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.skill.PassiveSkill;
+import io.lumine.mythic.lib.skill.custom.condition.Condition;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.math.formula.IntegerLinearValue;
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
@@ -17,6 +18,9 @@ public class ClassSkill implements CooldownObject {
private final int unlockLevel, maxSkillLevel;
private final Map modifiers = new HashMap<>();
+ @Deprecated
+ private final Set unlockConditions = new HashSet<>();
+
/**
* Class used to save information about skills IN A CLASS CONTEXT i.e at
* which level the skill can be unlocked, etc.
diff --git a/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java b/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java
index 8fb2b970..0d5ada32 100644
--- a/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java
+++ b/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java
@@ -5,6 +5,7 @@ import io.lumine.mythic.lib.skill.handler.SkillHandler;
import io.lumine.mythic.lib.skill.trigger.TriggerType;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
+import net.Indyuce.mmocore.player.Unlockable;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -15,7 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
-public class RegisteredSkill {
+public class RegisteredSkill implements Unlockable {
private final SkillHandler> handler;
private final String name;
private final Map defaultModifiers = new HashMap<>();
@@ -45,6 +46,11 @@ public class RegisteredSkill {
this.triggerType = triggerType;
}
+ @Override
+ public String getUnlockNamespacedKey() {
+ return "registered_skill:" + handler.getId().toLowerCase();
+ }
+
public SkillHandler> getHandler() {
return handler;
}
diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java b/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java
index 49c85ade..ebc82f10 100644
--- a/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java
+++ b/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java
@@ -19,17 +19,27 @@ public enum SkillCastingMode {
SKILL_BAR(config -> new SkillBar(config)),
/**
- *
+ * When entering casting mode you can use the mouse scroller
+ * to navigate through the entire castable skill list. Then press
+ * one key to cast the one selected.
*/
SKILL_SCROLL(config -> new SkillScroller(config)),
/**
* Initialize your skill combo by pressing some key.
*
- * Then press a certain amount of keys to
+ * Then press a certain key combo. The config can be used
+ * to map key combos to skill bind slots, for instance LLR
+ * would cast the 2nd skill but LRL the 3rd one.
*/
KEY_COMBOS(config -> new KeyCombos(config)),
+ /**
+ * Entirely disables skill casting.
+ */
+ NONE(config -> new Listener() {
+ });
+
/**
* Not implemented yet.
*
diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java
index 686b78a3..c33637e8 100644
--- a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java
+++ b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java
@@ -111,6 +111,7 @@ public class KeyCombos implements Listener {
return;
}
+
// Adding pressed key
CustomSkillCastingHandler casting = (CustomSkillCastingHandler) playerData.getSkillCasting();
casting.current.registerKey(event.getPressed());
diff --git a/src/main/java/net/Indyuce/mmocore/tree/modifier/UnlockSkillModifier.java b/src/main/java/net/Indyuce/mmocore/tree/modifier/UnlockSkillModifier.java
new file mode 100644
index 00000000..570b8ab2
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/tree/modifier/UnlockSkillModifier.java
@@ -0,0 +1,28 @@
+package net.Indyuce.mmocore.tree.modifier;
+
+import io.lumine.mythic.lib.api.player.EquipmentSlot;
+import io.lumine.mythic.lib.api.player.MMOPlayerData;
+import io.lumine.mythic.lib.player.modifier.ModifierSource;
+import io.lumine.mythic.lib.player.modifier.PlayerModifier;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.skill.RegisteredSkill;
+import org.apache.commons.lang.NotImplementedException;
+
+public class UnlockSkillModifier extends PlayerModifier {
+ private RegisteredSkill unlocked = null;
+
+ public UnlockSkillModifier(String key, EquipmentSlot slot, ModifierSource source) {
+ super(key, slot, source);
+ }
+
+ @Override
+ public void register(MMOPlayerData mmoPlayerData) {
+ PlayerData playerData = PlayerData.get(mmoPlayerData.getUniqueId());
+ // playerData.unlock(unlocked);
+ }
+
+ @Override
+ public void unregister(MMOPlayerData mmoPlayerData) {
+ throw new NotImplementedException("");
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/util/HashableLocation.java b/src/main/java/net/Indyuce/mmocore/util/HashableLocation.java
new file mode 100644
index 00000000..0ef2336e
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/util/HashableLocation.java
@@ -0,0 +1,51 @@
+package net.Indyuce.mmocore.util;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+
+import java.util.Objects;
+
+public class HashableLocation {
+ private final World world;
+ private final int x, y, z;
+
+ public HashableLocation(Location loc) {
+ this.world = loc.getWorld();
+ this.x = loc.getBlockX();
+ this.y = loc.getBlockY();
+ this.z = loc.getBlockZ();
+ }
+
+ public World getWorld() {
+ return world;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getZ() {
+ return z;
+ }
+
+ public Location bukkit() {
+ return new Location(world, x, y, z);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ HashableLocation that = (HashableLocation) o;
+ return x == that.x && y == that.y && z == that.z && world.equals(that.world);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(world, x, y, z);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/Indyuce/mmocore/util/item/ConfigItem.java b/src/main/java/net/Indyuce/mmocore/util/item/ConfigItem.java
index f778f7b6..50693c73 100644
--- a/src/main/java/net/Indyuce/mmocore/util/item/ConfigItem.java
+++ b/src/main/java/net/Indyuce/mmocore/util/item/ConfigItem.java
@@ -1,11 +1,13 @@
package net.Indyuce.mmocore.util.item;
+import io.lumine.mythic.lib.UtilityMethods;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import javax.annotation.Nullable;
import java.util.List;
+import java.util.Objects;
public class ConfigItem {
private final String name, id, texture;
@@ -15,10 +17,12 @@ public class ConfigItem {
private final boolean unbreakable;
public ConfigItem(ConfigurationSection config) {
- id = config.getName();
+ id = UtilityMethods.enumName(config.getName());
name = config.getString("name");
lore = config.getStringList("lore");
- material = Material.valueOf(config.getString("item"));
+ String itemFormat = Objects.requireNonNull(config.getString("item"), "Could not find item material");
+ Validate.isTrue(!itemFormat.contains(":"), "Invalid custom model data format, please use 'custom-model-data: X' instead");
+ material = Material.valueOf(UtilityMethods.enumName(itemFormat));
Validate.notNull(name, "Name cannot be null");
Validate.notNull(lore, "Lore can be empty but not null");
diff --git a/src/main/java/net/Indyuce/mmocore/util/item/SkillBookBuilder.java b/src/main/java/net/Indyuce/mmocore/util/item/SkillBookBuilder.java
new file mode 100644
index 00000000..6c809a26
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/util/item/SkillBookBuilder.java
@@ -0,0 +1,29 @@
+package net.Indyuce.mmocore.util.item;
+
+import io.lumine.mythic.lib.api.item.ItemTag;
+import io.lumine.mythic.lib.api.item.NBTItem;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.skill.RegisteredSkill;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+@Deprecated
+public class SkillBookBuilder extends AbstractItemBuilder {
+ private final RegisteredSkill skill;
+
+ public SkillBookBuilder(RegisteredSkill skill) {
+ super(MMOCore.plugin.configItems.get("SKILL_BOOK"));
+
+ this.skill = skill;
+ }
+
+ @Override
+ public void whenBuildingMeta(ItemStack item, ItemMeta meta) {
+
+ }
+
+ @Override
+ public void whenBuildingNBT(NBTItem nbtItem) {
+ nbtItem.addTag(new ItemTag("SkillBookId", skill.getHandler().getId()));
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/util/item/WaypointBookBuilder.java b/src/main/java/net/Indyuce/mmocore/util/item/WaypointBookBuilder.java
new file mode 100644
index 00000000..d16aeeb0
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/util/item/WaypointBookBuilder.java
@@ -0,0 +1,30 @@
+package net.Indyuce.mmocore.util.item;
+
+import io.lumine.mythic.lib.api.item.ItemTag;
+import io.lumine.mythic.lib.api.item.NBTItem;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.waypoint.Waypoint;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+public class WaypointBookBuilder extends AbstractItemBuilder {
+ private final Waypoint waypoint;
+
+ public WaypointBookBuilder(Waypoint waypoint) {
+ super(MMOCore.plugin.configItems.get("WAYPOINT_BOOK"));
+
+ this.waypoint = waypoint;
+
+ addPlaceholders("waypoint", waypoint.getName());
+ }
+
+ @Override
+ public void whenBuildingMeta(ItemStack item, ItemMeta meta) {
+
+ }
+
+ @Override
+ public void whenBuildingNBT(NBTItem nbtItem) {
+ nbtItem.addTag(new ItemTag("WaypointBookId", waypoint.getId()));
+ }
+}
diff --git a/src/main/java/net/Indyuce/mmocore/waypoint/CostType.java b/src/main/java/net/Indyuce/mmocore/waypoint/CostType.java
deleted file mode 100644
index 6a0fefaa..00000000
--- a/src/main/java/net/Indyuce/mmocore/waypoint/CostType.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package net.Indyuce.mmocore.waypoint;
-
-public enum CostType {
-
- /**
- * When teleporting to this waypoint
- */
- NORMAL_USE,
-
- /**
- * When dynamically teleporting to this waypoint
- */
- DYNAMIC_USE,
-
- /**
- * When setting your spawn point to this waypoint.
- */
- // SET_SPAWNPOINT
- ;
-
- private final String path;
-
- CostType() {
- this.path = name().toLowerCase().replace("_", "-");
- }
-
- public String getPath() {
- return path;
- }
-}
diff --git a/src/main/java/net/Indyuce/mmocore/waypoint/Waypoint.java b/src/main/java/net/Indyuce/mmocore/waypoint/Waypoint.java
index f6d609b7..93bee8ae 100644
--- a/src/main/java/net/Indyuce/mmocore/waypoint/Waypoint.java
+++ b/src/main/java/net/Indyuce/mmocore/waypoint/Waypoint.java
@@ -1,18 +1,27 @@
package net.Indyuce.mmocore.waypoint;
+import io.lumine.mythic.lib.api.MMOLineConfig;
+import io.lumine.mythic.lib.api.util.PostLoadObject;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.loot.chest.condition.Condition;
+import net.Indyuce.mmocore.loot.chest.condition.ConditionInstance;
import net.Indyuce.mmocore.player.Unlockable;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.craftbukkit.libs.jline.internal.Nullable;
import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
import java.util.*;
+import java.util.logging.Level;
-public class Waypoint implements Unlockable {
+public class Waypoint extends PostLoadObject implements Unlockable {
private final String id, name;
private final Location loc;
+ private final List lore;
private final double radiusSquared;
/**
@@ -21,32 +30,53 @@ public class Waypoint implements Unlockable {
*
* If it's empty it can access any waypoint.
*/
- private final Set destinations = new HashSet<>();
+ private final Map destinations = new HashMap<>();
/**
* Waypoint options saved here.
*/
private final Map options = new HashMap<>();
-
- /**
- * Stellium cost for each action (0 being the default cost)
- */
- private final Map costs = new HashMap<>();
+ private final double dynamicCost, setSpawnCost, normalCost;
+ private final List dynamicUseConditions = new ArrayList<>();
public Waypoint(ConfigurationSection config) {
+ super(config);
+
id = Objects.requireNonNull(config, "Could not load config section").getName();
name = Objects.requireNonNull(config.getString("name"), "Could not load waypoint name");
+ lore = Objects.requireNonNullElse(config.getStringList("lore"), new ArrayList<>());
loc = readLocation(Objects.requireNonNull(config.getString("location"), "Could not read location"));
radiusSquared = Math.pow(config.getDouble("radius"), 2);
- for (CostType costType : CostType.values())
- costs.put(costType, config.getDouble("cost." + costType.getPath()));
+ dynamicCost = config.getDouble("cost.dynamic-use");
+ normalCost = config.getDouble("cost.normal-use");
+ setSpawnCost = config.getDouble("cost.set-spawnpoint");
for (WaypointOption option : WaypointOption.values())
options.put(option, config.getBoolean("option." + option.getPath(), option.getDefaultValue()));
- destinations.addAll(config.getStringList("linked"));
+ if (config.contains("dynamic-conditions")) {
+ List conditions = config.getStringList("dynamic-conditions");
+ for (String condition : conditions)
+ try {
+ dynamicUseConditions.add(MMOCore.plugin.loadManager.loadCondition(new MMOLineConfig(condition)));
+ } catch (RuntimeException exception) {
+ MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load condition '" + condition + "' from waypoint '" + id + "': " + exception.getMessage());
+ }
+ }
+ }
+
+
+ @Override
+ protected void whenPostLoaded(@NotNull ConfigurationSection config) {
+
+ // Load waypoint network
+ if (config.contains("linked")) {
+ ConfigurationSection section = config.getConfigurationSection("linked");
+ for (String key : section.getKeys(false))
+ destinations.put(MMOCore.plugin.waypointManager.get(key), section.getDouble(key));
+ }
}
public String getId() {
@@ -57,20 +87,102 @@ public class Waypoint implements Unlockable {
return name;
}
+ public List getLore() {
+ return lore;
+ }
+
public Location getLocation() {
return loc;
}
- /**
- * @param other Another waypoint
- * @return If any player standing on that waypoint can teleport to given waypoint
- */
- public boolean hasDestination(Waypoint other) {
- return destinations.isEmpty() || destinations.contains(other.getId());
+ public double getDynamicCost() {
+ return dynamicCost;
}
- public double getCost(CostType type) {
- return costs.getOrDefault(type, 0d);
+ @Deprecated
+ public double getSetSpawnCost() {
+ return setSpawnCost;
+ }
+
+ public boolean mayBeUsedDynamically(Player player) {
+ if (!options.get(WaypointOption.DYNAMIC))
+ return false;
+
+ for (Condition condition : dynamicUseConditions)
+ if (!condition.isMet(new ConditionInstance(player)))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * @return Integer.POSITIVE_INFINITY if the way point is not linked
+ * If it is, cost of the instant travel between the two waypoints.
+ */
+ public double getDirectCost(Waypoint waypoint) {
+ return destinations.isEmpty() ? normalCost : destinations.getOrDefault(waypoint, Double.POSITIVE_INFINITY);
+ }
+
+ public List getAllPath() {
+ //All the WayPoints that have been registered
+ List checkedPoints = new ArrayList<>();
+ //All the path
+ List paths = new ArrayList();
+ List pointsToCheck = new ArrayList<>();
+ pointsToCheck.add(new WaypointPath(this));
+
+ while (pointsToCheck.size() != 0) {
+ WaypointPath checked = pointsToCheck.get(0);
+ pointsToCheck.remove(0);
+ // If the point has already been checked, pass
+ if (checkedPoints.contains(checked.getFinalWaypoint()))
+ continue;
+
+ paths.add(checked);
+ checkedPoints.add(checked.getFinalWaypoint());
+
+ for (Waypoint toCheck : checked.getFinalWaypoint().destinations.keySet())
+ if (!checkedPoints.contains(toCheck)) {
+ WaypointPath toCheckInfo = checked.addWayPoint(toCheck);
+ // We keep pointsToCheck ordered
+ pointsToCheck = toCheckInfo.addInOrder(pointsToCheck);
+ }
+ }
+ return paths;
+ }
+
+ @Nullable
+ public WaypointPath getPath(Waypoint targetWaypoint) {
+ //All the WayPoints that have been registered
+ List checkedPoints = new ArrayList<>();
+ //All the path
+ List paths = new ArrayList();
+ List pointsToCheck = new ArrayList<>();
+ pointsToCheck.add(new WaypointPath(this));
+
+ while (pointsToCheck.size() != 0) {
+ WaypointPath checked = pointsToCheck.get(0);
+ pointsToCheck.remove(0);
+ // If the point has already been checked, pass
+ if (checkedPoints.contains(checked.getFinalWaypoint()))
+ continue;
+
+ paths.add(checked);
+ checkedPoints.add(checked.getFinalWaypoint());
+
+ if (checked.getFinalWaypoint().equals(targetWaypoint))
+ return checked;
+
+ for (Waypoint toCheck : checked.getFinalWaypoint().destinations.keySet())
+ if (!checkedPoints.contains(toCheck)) {
+ WaypointPath toCheckInfo = checked.addWayPoint(toCheck);
+ // We keep pointsToCheck ordered
+ pointsToCheck = toCheckInfo.addInOrder(pointsToCheck);
+ }
+ }
+
+ //If no path has been found we return null
+ return null;
}
public boolean hasOption(WaypointOption option) {
diff --git a/src/main/java/net/Indyuce/mmocore/waypoint/WaypointPath.java b/src/main/java/net/Indyuce/mmocore/waypoint/WaypointPath.java
new file mode 100644
index 00000000..e987eff5
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/waypoint/WaypointPath.java
@@ -0,0 +1,85 @@
+package net.Indyuce.mmocore.waypoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class WaypointPath {
+ private final List waypoints;
+ private double cost;
+
+ public WaypointPath(Waypoint waypoint) {
+ this.waypoints = new ArrayList<>();
+ this.waypoints.add(waypoint);
+ cost = 0;
+ }
+
+ public WaypointPath(Waypoint waypoint, double cost) {
+ this.waypoints = new ArrayList<>();
+ this.waypoints.add(waypoint);
+ this.cost = cost;
+ }
+
+ public WaypointPath(List waypoints, double cost) {
+ this.waypoints = new ArrayList<>(waypoints);
+ this.cost = cost;
+ }
+
+ public List getWaypoints() {
+ return waypoints;
+ }
+
+ public double getCost() {
+ return cost;
+ }
+
+ public WaypointPath addCost(double cost) {
+ this.cost += cost;
+ return this;
+ }
+
+ public List addInOrder(List pathInfos) {
+ int index = 0;
+ while (index < pathInfos.size()) {
+ if (cost < pathInfos.get(index).cost) {
+ pathInfos.set(index, this);
+ return pathInfos;
+ }
+ index++;
+ }
+ // If index == pathInfos.size() add the waypoint at the end
+ pathInfos.add(this);
+ return pathInfos;
+ }
+
+
+ /**
+ * @param dynamic Display the first waypoint if it is dynamic as it is an intermediary point
+ * @return List with all
+ */
+ public String displayIntermediaryWayPoints(boolean dynamic) {
+ int beginIndex = dynamic ? 0 : 1;
+ if (waypoints.size() <= beginIndex + 1)
+ return "None";
+
+ String result = "";
+ for (int i = beginIndex; i < waypoints.size() - 1; i++)
+ result += waypoints.get(i).getName() + (i != waypoints.size() - 2 ? ", " : "");
+ return result;
+ }
+
+ public WaypointPath addWayPoint(Waypoint waypoint) {
+ List newWaypoints = new ArrayList<>();
+ newWaypoints.addAll(waypoints);
+ newWaypoints.add(waypoint);
+ double cost = this.cost + getFinalWaypoint().getDirectCost(waypoint);
+ return new WaypointPath(newWaypoints, cost);
+ }
+
+ public Waypoint getInitialWaypoint() {
+ return waypoints.get(0);
+ }
+
+ public Waypoint getFinalWaypoint() {
+ return waypoints.get(waypoints.size() - 1);
+ }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index adce82fd..db45266d 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -55,9 +55,29 @@ protect-custom-mine: false
# - mmocore
# - dungeonsxl
# - parties
-# - parties_and_friends
+# - party_and_friends
+# - mcmmo
party-plugin: mmocore
+## Edit the plugin handling parties here.
+## Supported values (just copy and paste):
+## - mmocore
+## - beauty_quest
+## - quest_creator
+## - quests
+quest-plugin: mmocore
+
+# MythicLib introduces a CHANCE stat that is used in
+# several different systems in MMOCore. By changing these
+# weights you can define how much the Chance stat impacts
+# the different features implemented by MMOCore
+#
+# e.g setting some weight to 2 will DOUBLE the effect of
+# the chance stat in one particular system. The weights are
+# all set to 1 by default, this option is really server specific
+chance-stat-weight:
+ loot-chests: 1
+
# Whether blocks generated with a "cobblegenerator" should
# provide the player with experience points or not
should-cobblestone-generators-give-exp: false
diff --git a/src/main/resources/default/drop-tables/example-drop-tables.yml b/src/main/resources/default/drop-tables/example-drop-tables.yml
index 25894aaf..4faf4e78 100644
--- a/src/main/resources/default/drop-tables/example-drop-tables.yml
+++ b/src/main/resources/default/drop-tables/example-drop-tables.yml
@@ -8,7 +8,7 @@
diamond-drop-table:
items:
- 'vanilla{type=DIAMOND} 1 1-3'
- # - 'mmoitem{type=material;id=RARE_DIAMOND} .1 1-3'
+ # - 'mmoitem{type=material;id=RARE_DIAMOND;period=} .1 1-3'
- 'droptable{id=other-drop-table} .1'
other-drop-table:
diff --git a/src/main/resources/default/gui/attribute-view.yml b/src/main/resources/default/gui/attribute-view.yml
index 49c1187a..304eeda4 100644
--- a/src/main/resources/default/gui/attribute-view.yml
+++ b/src/main/resources/default/gui/attribute-view.yml
@@ -31,7 +31,7 @@ items:
- ''
- '&8When Leveled Up:'
- '&7 +{buff_weapon_damage}% Weapon Damage (&a+{total_weapon_damage}%&7)'
- - '&7 +{buff_max_health} Max Health (&a+{total_max_health}&7)'
+ - '&7 +{buff_max_health}% Max Health (&a+{total_max_health}&7)'
- ''
- '&eClick to level up for 1 attribute point.'
- '&e◆ Current Attribute Points: {attribute_points}'
@@ -49,7 +49,7 @@ items:
- '&8When Leveled Up:'
- '&7 +{buff_physical_damage}% Physical Damage (&a+{total_physical_damage}%&7)'
- '&7 +{buff_projectile_damage}% Projectile Damage (&a+{total_projectile_damage}%&7)'
- - '&7 +{buff_attack_speed} Attack Speed (&a+{total_attack_speed}&7)'
+ - '&7 +{buff_attack_speed}% Attack Speed (&a+{total_attack_speed}&7)'
- ''
- '&eClick to level up for 1 attribute point.'
- '&e◆ Current Attribute Points: {attribute_points}'
diff --git a/src/main/resources/default/gui/skill-list.yml b/src/main/resources/default/gui/skill-list.yml
index 60993b92..04dc88a6 100644
--- a/src/main/resources/default/gui/skill-list.yml
+++ b/src/main/resources/default/gui/skill-list.yml
@@ -1,97 +1,95 @@
-
# GUI display name
-name: Your Skills
+name: 'Selected Skill: &6{skill}'
# Number of slots in your inventory. Must be
# between 9 and 54 and must be a multiple of 9.
-slots: 45
+slots: 54
items:
- skill:
- slots: [11,12,13,14,15]
-
- # Index from 'slots' of the skill
- # currently selected in the GUI
- selected-slot: 2
-
- function: skill
- name: '&a{skill} &6[{level}]'
- lore:
- - ''
- - '{unlocked}&a✔ Requires Level {unlock}'
- - '{locked}&c✖ Requires Level {unlock}'
- - '{max_level}&e✔ Maximum Level Hit!'
- - ''
- - '{lore}'
- next:
- slots: [16]
- function: next
- item: PLAYER_HEAD
- texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTliZjMyOTJlMTI2YTEwNWI1NGViYTcxM2FhMWIxNTJkNTQxYTFkODkzODgyOWM1NjM2NGQxNzhlZDIyYmYifX19
- name: '&aNext'
- lore: {}
- previous:
- slots: [10]
- function: previous
- item: PLAYER_HEAD
- texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmQ2OWUwNmU1ZGFkZmQ4NGU1ZjNkMWMyMTA2M2YyNTUzYjJmYTk0NWVlMWQ0ZDcxNTJmZGM1NDI1YmMxMmE5In19fQ==
- name: '&aPrevious'
- lore: {}
- switch:
- slots: [28]
- function: switch
- item: PLAYER_HEAD
- binding:
- item: PINK_STAINED_GLASS
- name: '&aSwitch to Binding'
- lore: {}
- upgrading:
- item: PINK_STAINED_GLASS
- name: '&aSwitch to Upgrading'
- lore: {}
- skill-slot:
- slots: [29,30,31,32,33,34]
- function: slot
- item: BOOK
-
- # Material used when the slot is empty
- empty-item: GRAY_DYE
-
- name: '&aSkill Slot {slot}'
- no-skill: '&cNone'
- lore:
- - '&7Current Skill: &6{skill}'
- - ''
- - '&7&oCast this spell by pressing [F] followed'
- - '&7&oby the keybind displayed on the action bar.'
- - ''
- - '&e► Left click to bind {selected}.'
- - '&e► Right click to unbind.'
- skill-level:
- slots: [29,30,31,32,33,34]
- function: level
-
- # Skill level offset, should be changed
- # according to the amount of inventory
- # slots the skill-level item occupies.
- offset: 2
-
- # Item displayed if the skill level is
- # too low to display a level item in the GUI
- too-low:
- item: AIR
-
- item: LIME_DYE
- name: '&a{skill} Level {roman}'
- lore:
- - ''
- - '{lore}'
- upgrade:
- slots: [31]
- function: upgrade
- item: GREEN_STAINED_GLASS_PANE
- name: '&a&lUPGRADE {skill_caps}'
- lore:
- - '&7Costs 1 skill point.'
- - ''
- - '&eCurrent Skill Points: {skill_points}'
+ skill:
+ slots: [ 10,11,12,19,20,21,28,29,30,37,38,39]
+
+ function: skill
+ name: '&a{skill} &6[{level}]'
+ lore:
+ - ''
+ - '{unlocked}&a✔ Requires Level {unlock}'
+ - '{locked}&c✖ Requires Level {unlock}'
+ - '{max_level}&e✔ Maximum Level Hit!'
+ - ''
+ - '{lore}'
+ next:
+ slots: [ 47 ]
+ function: next
+ item: PLAYER_HEAD
+ texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTliZjMyOTJlMTI2YTEwNWI1NGViYTcxM2FhMWIxNTJkNTQxYTFkODkzODgyOWM1NjM2NGQxNzhlZDIyYmYifX19
+ name: '&aNext'
+ lore: { }
+ previous:
+ slots: [ 2 ]
+ function: previous
+ item: PLAYER_HEAD
+ texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmQ2OWUwNmU1ZGFkZmQ4NGU1ZjNkMWMyMTA2M2YyNTUzYjJmYTk0NWVlMWQ0ZDcxNTJmZGM1NDI1YmMxMmE5In19fQ==
+ name: '&aPrevious'
+ lore: { }
+
+
+ #switch:
+ #
+ # slots: [28]
+ # function: switch
+ # item: PLAYER_HEAD
+ # binding:
+ # item: PINK_STAINED_GLASS
+ # name: '&aSwitch to Binding'
+ # lore: {}
+ # upgrading:
+ # item: PINK_STAINED_GLASS
+ # name: '&aSwitch to Upgrading'
+ # lore: {}
+ skill-slot:
+ slots: [ 8,17,26,35,44,53 ]
+ function: slot
+ item: BOOK
+
+ # Material used when the slot is empty
+ empty-item: GRAY_DYE
+
+ name: '&aSkill Slot {slot}'
+ no-skill: '&cNone'
+ lore:
+ - '&7Current Skill: &6{skill}'
+ - ''
+ - '&7&oCast this spell by pressing [F] followed'
+ - '&7&oby the keybind displayed on the action bar.'
+ - ''
+ - '&e► Left click to bind {selected}.'
+ - '&e► Right click to unbind.'
+ skill-level:
+ slots: [ 6,15,24,33,42,51 ]
+ function: level
+
+ # Skill level offset, should be changed
+ # according to the amount of inventory
+ # slots the skill-level item occupies.
+ offset: 2
+
+ # Item displayed if the skill level is
+ # too low to display a level item in the GUI
+ too-low:
+ item: AIR
+
+ item: LIME_DYE
+ name: '&a{skill} Level {roman}'
+ lore:
+ - ''
+ - '{lore}'
+ upgrade:
+ slots: [ 15 ]
+ function: upgrade
+ item: GREEN_STAINED_GLASS_PANE
+ name: '&a&lUPGRADE {skill_caps}'
+ lore:
+ - '&7Costs 1 skill point.'
+ - ''
+ - '&eCurrent Skill Points: {skill_points}'
diff --git a/src/main/resources/default/gui/waypoints.yml b/src/main/resources/default/gui/waypoints.yml
index 074e6aca..56ade9b9 100644
--- a/src/main/resources/default/gui/waypoints.yml
+++ b/src/main/resources/default/gui/waypoints.yml
@@ -34,7 +34,7 @@ items:
lore:
- '&7You cannot teleport as the two waypoints are not linked.'
- - '&7Teleporting costs &b&l{normal_cost}&7/&b&l{dynamic_cost} &7Stellium.'
+
# When you cannot teleport to a non dynamic waypoint
not-dynamic:
@@ -43,16 +43,23 @@ items:
lore:
- '&7You cannot teleport as you are not standing on a waypoint.'
- - '&7Teleporting costs &b&l{normal_cost}&7/&b&l{dynamic_cost} &7Stellium.'
- # When you cannot teleport to a non dynamic waypoint
+
+ current-waypoint:
+ name: '&a{name}'
+ item: ENDER_PEARL
+
+ lore:
+ - '&7The waypoint you are standing at.'
+
+ # When you don't have enough stellium
not-enough-stellium:
name: '&a{name}'
item: ENDER_PEARL
lore:
- '&7You cannot teleport as you do not have enough Stellium.'
- - '&7Teleporting costs &b&l{normal_cost}&7/&b&l{dynamic_cost} &7Stellium.'
+ - '&7Teleporting costs &b{normal_cost}&7/&b{dynamic_cost} &7Stellium.'
# Displayed when the waypoint is unlocked and usable
display:
@@ -61,6 +68,7 @@ items:
lore:
- '&7You can teleport to this waypoint.'
+ - '&7Intermediary waypoints: {intermediary_waypoints}'
- '&7Click to teleport for &b{current_cost} &7Stellium.'
next:
diff --git a/src/main/resources/default/items.yml b/src/main/resources/default/items.yml
index 05de0cbe..ab5b3967 100644
--- a/src/main/resources/default/items.yml
+++ b/src/main/resources/default/items.yml
@@ -1,3 +1,18 @@
+# The default item template, not used by MMOCore but it does
+# indicates all the item options you can use to configure your items.
+TEMPLATE:
+ item: BOOK
+ name: 'Your item name'
+ custom-model-data: 0
+ damage: 0
+ unbreakable: false
+ lore:
+ - 'This is your item lore'
+ - 'And that is the second line'
+ - 'This supports &acolors &7as well'
+ # For player heads only, that is the skull texture value
+ texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGUwYzUzN2EzZDAyMjlmNzMyZTlkMWY4NTRiMzc3YWQwNDA3NjE4ZmQwM2NkNGJiZTgwMzc2MjI0YTRmMWQxZSJ9fX0='
+
GOLD_COIN:
item: GOLD_NUGGET
name: '&6Gold Coin'
@@ -10,12 +25,23 @@ NOTE:
lore:
- '&eWorth: {worth}g'
-WAYPOINT_BOOK:
- item: ENCHANTED_BOOK
- name: '&6Waypoints Book'
+SKILL_POINT_BOOK:
+ item: book
+ name: '&6Skill Book'
lore:
- - '&7Waypoints are locations used to save your progress.'
- - '&7This book gives you the ability to freely warp between these.'
+ - '&7Click to redeem one skill point.'
+
+SKILL_BOOK:
+ item: book
+ name: '&6Skill Book: {skill}'
+ lore:
+ - '&7Click to unlock {skill}'
+
+WAYPOINT_BOOK:
+ item: BOOK
+ name: '&6Waypoint Book: {waypoint}'
+ lore:
+ - '&7Click to unlock {waypoint}'
DEPOSIT_ITEM:
item: BOOK
diff --git a/src/main/resources/default/messages.yml b/src/main/resources/default/messages.yml
index 697ebaa9..b311b6ba 100644
--- a/src/main/resources/default/messages.yml
+++ b/src/main/resources/default/messages.yml
@@ -67,6 +67,7 @@ leave-combat: '%&aYou left combat.'
# Waypoints
new-waypoint: '%&eYou unlocked the &6{waypoint} &ewaypoint!'
+new-waypoint-book: '%&eYou unlocked the &6{waypoint} &ewaypoint!'
not-enough-stellium: '&cYou don''t have enough stellium: you need {more} more.'
waypoint-cooldown: '&cPlease wait {cooldown} before using a waypoint again.'
not-unlocked-waypoint: '&cYou have not unlocked that waypoint yet.'
diff --git a/src/main/resources/default/professions/fishing.yml b/src/main/resources/default/professions/fishing.yml
index c26e6a6a..ed2b9985 100644
--- a/src/main/resources/default/professions/fishing.yml
+++ b/src/main/resources/default/professions/fishing.yml
@@ -22,7 +22,7 @@ exp-sources: {}
on-fish:
overriding-drop-table:
conditions:
- - 'region{name=swamp}'
+ - 'biome{name=swamp}'
# When drop table is read, one of these
# items will be selected randomly.
diff --git a/src/main/resources/default/stats.yml b/src/main/resources/default/stats.yml
index 1c9ef218..21fdee82 100644
--- a/src/main/resources/default/stats.yml
+++ b/src/main/resources/default/stats.yml
@@ -58,6 +58,11 @@ default:
COOLDOWN_REDUCTION:
base: 0
per-level: 0
+
+ #Increases chance to have rare loot chests
+ CHANCE:
+ base: 0
+ per-level: 0
# Dealt by skills
SKILL_DAMAGE:
diff --git a/src/main/resources/default/waypoints.yml b/src/main/resources/default/waypoints.yml
index 59999359..cb57e8fc 100644
--- a/src/main/resources/default/waypoints.yml
+++ b/src/main/resources/default/waypoints.yml
@@ -12,12 +12,10 @@ spawn:
# Radius of waypoint around the specified location.
radius: 2.0
- cost:
+ lore: {}
- # Stellium cost in order to use the waypoint.
- # Stellium is like stamina however it's not used
- # by skills and regens much slower than mana.
- normal-use: 3
+ # Cost for several specific actions
+ cost:
# Cost when not standing on any waypoint.
dynamic-use: 5
@@ -42,19 +40,17 @@ spawn:
default: true
# All the waypoints you can teleport to when standing
- # on that waypoint. If that list is empty you are able
+ # on that waypoint. And the cost needed to travel to his pointIf that list is empty you are able
# to teleport to any waypoint
linked:
- - spawn1
- - spawn2
- - forest
+ spawn1: 4
+ spawn2: 5
spawn1:
name: Spawn1
location: 'world 69 71 136 136 0'
radius: 2.0
- cost:
- normal-use: 3
+ lore: {}
option:
default: false
@@ -62,11 +58,20 @@ spawn1:
# on any waypoint (waypoint must be unlocked).
dynamic: true
+ # Not necessary if the waypoint doesn't allow dynamic use
+ # The conditions for the dynamic-use of the waypoint
+ dynamic-conditions:
+ - 'distance{world=world;x=69;y=71;z=163;distance=500}'
+
+ linked:
+ spawn: 4
+
spawn2:
name: Spawn2
+ lore: {}
location: 'world 69 71 136 136 0'
radius: 3.0
- cost:
- normal-use: 3
option:
enable-menu: false
+ linked:
+ spawn: 3