diff --git a/MMOCore-API/pom.xml b/MMOCore-API/pom.xml
index a95c596d..5964ed71 100644
--- a/MMOCore-API/pom.xml
+++ b/MMOCore-API/pom.xml
@@ -94,17 +94,18 @@
https://maven.enginehub.org/repo/
-
- dmulloy2-repo
- https://repo.dmulloy2.net/repository/public/
-
-
simonsators Repo
https://simonsator.de/repo
+
+
+ codemc-repo
+ https://repo.codemc.io/repository/maven-public/
+
+
- me.blackvein
- Quests
- 4.4.1-b340
+ me.pikamug.quests
+ quests-core
+ 5.2.7
provided
true
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
index 0b2c0966..f3ce33d7 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
@@ -725,7 +725,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* @return Experience needed in order to reach next level
*/
public long getLevelUpExperience() {
- return getProfess().getExpCurve().getExperience(getLevel() + 1);
+ return getProfess().getExpCurve().getExperience(this, getLevel());
}
public boolean isOnline() {
@@ -752,7 +752,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
public void giveLevels(int value, @NotNull EXPSource source) {
long equivalentExp = 0;
- while (value-- > 0) equivalentExp += getProfess().getExpCurve().getExperience(getLevel() + value + 1);
+ while (value-- > 0) equivalentExp += getProfess().getExpCurve().getExperience(this, getLevel() + value);
giveExperience(equivalentExp, source);
}
@@ -1088,7 +1088,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* Loop for exp overload when leveling up, will continue
* looping until exp is 0 or max currentLevel has been reached
*/
- while (experience >= (experienceNeeded = getProfess().getExpCurve().getExperience(newLevel + 1))) {
+ while (experience >= (experienceNeeded = getProfess().getExpCurve().getExperience(this, newLevel))) {
if (maxLevel > 0 && newLevel >= maxLevel) {
experience = 0;
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/MMOCoreAttributeStatHandler.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/MMOCoreAttributeStatHandler.java
deleted file mode 100644
index 143b0abc..00000000
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/MMOCoreAttributeStatHandler.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package net.Indyuce.mmocore.api.player.attribute;
-
-import io.lumine.mythic.lib.api.stat.StatInstance;
-import io.lumine.mythic.lib.api.stat.handler.StatHandler;
-import net.Indyuce.mmocore.api.player.PlayerData;
-import org.bukkit.configuration.ConfigurationSection;
-
-/**
- * This fixes an issue where registering new stat modifiers in ML
- * to add extra attribute points does NOT update the stats granted
- * by the attribute.
- *
- * This stat handler MAY call subsequent stat handlers. There might
- * be infinite recursion problems if another attr. grants extra attribute pts.
- */
-public class MMOCoreAttributeStatHandler extends StatHandler {
- private final PlayerAttribute attr;
-
- public MMOCoreAttributeStatHandler(ConfigurationSection config, PlayerAttribute attr) {
- super(config, "ADDITIONAL_" + attr.getId().toUpperCase().replace("-", "_"));
-
- this.attr = attr;
- }
-
- /**
- * This method is called on login but the MMOCore playerData
- * is not loaded yet, hence the try/catch clause
- */
- @Override
- public void runUpdate(StatInstance instance) {
- try {
- final PlayerData playerData = PlayerData.get(instance.getMap().getPlayerData());
- playerData.getAttributes().getInstance(attr).updateStats();
- } catch (NullPointerException exception) {
- // Player data is not loaded yet so there's nothing to update.
- }
- }
-}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttribute.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttribute.java
index 31f9f8c8..487b3255 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttribute.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttribute.java
@@ -6,7 +6,7 @@ import io.lumine.mythic.lib.util.lang3.Validate;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.EXPSource;
-import net.Indyuce.mmocore.experience.ExpCurve;
+import net.Indyuce.mmocore.experience.curve.ExperienceCurve;
import net.Indyuce.mmocore.experience.ExperienceObject;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import org.bukkit.Location;
@@ -84,7 +84,7 @@ public class PlayerAttribute implements ExperienceObject {
}
@Override
- public String getKey() {
+ public @NotNull String getKey() {
return "attribute:" + getId().replace("-", "_");
}
@@ -99,9 +99,9 @@ public class PlayerAttribute implements ExperienceObject {
return expTable != null;
}
- @Nullable
+ @NotNull
@Override
- public ExpCurve getExpCurve() {
+ public ExperienceCurve getExpCurve() {
throw new RuntimeException("Attributes don't have experience");
}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java
index 52e2cf14..8a093317 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java
@@ -25,8 +25,8 @@ import net.Indyuce.mmocore.api.player.profess.resource.ResourceRegeneration;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
import net.Indyuce.mmocore.experience.EXPSource;
-import net.Indyuce.mmocore.experience.ExpCurve;
import net.Indyuce.mmocore.experience.ExperienceObject;
+import net.Indyuce.mmocore.experience.curve.ExperienceCurve;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.loot.chest.particle.CastingParticle;
import net.Indyuce.mmocore.player.stats.StatInfo;
@@ -59,7 +59,7 @@ public class PlayerClass implements ExperienceObject, PreloadedObject {
private final ManaDisplayOptions manaDisplay;
@NotNull
- private final ExpCurve expCurve;
+ private final ExperienceCurve expCurve;
@Nullable
private final ExperienceTable expTable;
@@ -111,10 +111,14 @@ public class PlayerClass implements ExperienceObject, PreloadedObject {
actionBarFormat = config.contains("action-bar") ? config.getString("action-bar") : null;
// Exp curve
- expCurve = config.contains("exp-curve")
- ? MMOCore.plugin.experience.getCurveOrThrow(
- config.get("exp-curve").toString().toLowerCase().replace("_", "-").replace(" ", "-"))
- : ExpCurve.DEFAULT;
+ ExperienceCurve expCurve = ExperienceCurve.DEFAULT;
+ try {
+ expCurve = ExperienceCurve.fromConfig(config.getString("exp-curve"));
+ } catch (Throwable exception) {
+ MMOCore.log(Level.WARNING, "Could not load exp curve from class '" + id + "': " + exception.getMessage());
+ exception.printStackTrace();
+ }
+ this.expCurve = expCurve;
// Main exp table
ExperienceTable expTable = null;
@@ -261,7 +265,7 @@ public class PlayerClass implements ExperienceObject, PreloadedObject {
manaDisplay = ManaDisplayOptions.DEFAULT;
maxLevel = 0;
displayOrder = 0;
- expCurve = ExpCurve.DEFAULT;
+ expCurve = ExperienceCurve.DEFAULT;
expTable = null;
comboMap = null;
castParticle = new CastingParticle(VParticle.INSTANT_EFFECT.get());
@@ -282,7 +286,7 @@ public class PlayerClass implements ExperienceObject, PreloadedObject {
}
@Override
- public String getKey() {
+ public @NotNull String getKey() {
return "class_" + getId();
}
@@ -315,7 +319,7 @@ public class PlayerClass implements ExperienceObject, PreloadedObject {
}
@Override
- public @NotNull ExpCurve getExpCurve() {
+ public @NotNull ExperienceCurve getExpCurve() {
return expCurve;
}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/builtin/mmocore/admin/InfoCommandTreeNode.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/builtin/mmocore/admin/InfoCommandTreeNode.java
index 3828fc08..8dd01bcc 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/builtin/mmocore/admin/InfoCommandTreeNode.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/builtin/mmocore/admin/InfoCommandTreeNode.java
@@ -43,7 +43,7 @@ public class InfoCommandTreeNode extends CommandTreeNode {
for (Profession profession : MMOCore.plugin.professionManager.getAll())
sender.sendMessage(
ChatColor.YELLOW + profession.getName() + ": Lvl " + ChatColor.GOLD + playerData.getCollectionSkills().getLevel(profession)
- + ChatColor.YELLOW + " - " + ChatColor.GOLD + playerData.getCollectionSkills().getExperience(profession)
+ + ChatColor.YELLOW + " - " + ChatColor.GOLD + MythicLib.plugin.getMMOConfig().decimal.format(playerData.getCollectionSkills().getExperience(profession))
+ ChatColor.YELLOW + " / " + ChatColor.GOLD + playerData.getCollectionSkills().getLevelUpExperience(profession));
sender.sendMessage(ChatColor.YELLOW + "----------------------------------------------------");
return CommandResult.SUCCESS;
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java
index fb755002..d5f6da2f 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/comp/placeholder/RPGPlaceholders.java
@@ -200,14 +200,24 @@ public class RPGPlaceholders extends PlaceholderExpansion {
if (playerData.hasSkillBound(slot))
return Double.toString(playerData.getCooldownMap().getCooldown(playerData.getBoundSkill(slot)));
else return MMOCore.plugin.configManager.noSkillBoundPlaceholder;
- } else if (identifier.startsWith("profession_experience_"))
+ }
+
+ // Current exp in profession
+ else if (identifier.startsWith("profession_experience_")) {
return MythicLib.plugin.getMMOConfig().decimal.format(
playerData.getCollectionSkills().getExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase()));
+ }
- else if (identifier.startsWith("profession_next_level_"))
- return String.valueOf(PlayerData.get(player).getCollectionSkills()
- .getLevelUpExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase()));
+ // Exp needed to level up profession
+ else if (identifier.startsWith("profession_next_level_")) {
+ final var professionId = identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase();
+ final @Nullable var profession = MMOCore.plugin.professionManager.get(professionId);
+ if (profession == null) return "{profession_not_found}";
+ final var professionLevel = playerData.getCollectionSkills().getLevel(profession);
+ return String.valueOf(profession.getExpCurve().getExperience(playerData, professionLevel));
+ }
+ // Number of online members in party
else if (identifier.startsWith("party_count")) {
final @Nullable AbstractParty party = playerData.getParty();
return party == null ? "0" : String.valueOf(party.countMembers());
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExpCurve.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExpCurve.java
deleted file mode 100644
index a81422a5..00000000
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExpCurve.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package net.Indyuce.mmocore.experience;
-
-import io.lumine.mythic.lib.util.lang3.Validate;
-import net.Indyuce.mmocore.api.player.PlayerData;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ExpCurve {
- private final String id;
-
- /**
- * Experience needed to level up. Different professions or classes can have
- * different exp curves so that it is easier to balance.
- */
- private final List experience = new ArrayList<>();
-
- /**
- * Purely arbitrary but MMOCore needs a default exp curve for everything
- * otherwise there might be divisions by 0 when trying to update the vanilla
- * exp bar which requires a 0.0 -> 1.0 float as parameter.
- *
- * See {@link PlayerData#refreshVanillaExp()}
- */
- public static final ExpCurve DEFAULT = new ExpCurve("default", 100, 200, 300, 400, 500);
-
- /**
- * Reads an exp curve from a text file, one line after the other. Each exp
- * value has to be the only thing written on every line
- *
- * @param file Text file to read data from
- */
- public ExpCurve(File file) {
- this.id = file.getName().replace(".txt", "").toLowerCase().replace("_", "-").replace(" ", "-");
-
- try {
- BufferedReader reader = new BufferedReader(new FileReader(file));
- String readLine;
- while ((readLine = reader.readLine()) != null)
- experience.add(Long.valueOf(readLine));
- reader.close();
-
- Validate.isTrue(!experience.isEmpty(), "There must be at least one exp value in your exp curve");
- } catch(Throwable throwable) {
- throw new RuntimeException(throwable);
- }
- }
-
- /**
- * Public constructor for external plugins
- *
- * @param id Some unique identifier to let other plugin features refer
- * to your exp curve.
- * @param values The exp values, at to be at least one or the constructor
- * will throw an error
- */
- public ExpCurve(String id, long... values) {
- this.id = id;
- for (long value : values)
- experience.add(value);
- Validate.isTrue(!experience.isEmpty(), "There must be at least one exp value in your exp curve");
- }
-
- public String getId() {
- return id;
- }
-
- /**
- * @param level Level being reached by some player
- * @return Experience needed to reach provided level. The level serves
- * an index for a list checkup. If the level is higher than
- * the list size, it just returns the last value of the list
- */
- public long getExperience(int level) {
- Validate.isTrue(level > 0, "Level must be stricly positive");
- return experience.get(Math.min(level, experience.size()) - 1);
- }
-}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java
index c80e3819..6f12b789 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/ExperienceObject.java
@@ -1,10 +1,10 @@
package net.Indyuce.mmocore.experience;
import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.curve.ExperienceCurve;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/**
* General implementation for professions, classes and attributes.
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
*/
public interface ExperienceObject extends ExperienceDispenser {
+ @NotNull
String getKey();
/**
@@ -24,14 +25,14 @@ public interface ExperienceObject extends ExperienceDispenser {
*
* @return Exp curve of that object
*/
- @Nullable
- ExpCurve getExpCurve();
+ @NotNull
+ ExperienceCurve getExpCurve();
/**
- * Should throw an error if called when
- * {@link #hasExperienceTable()} returns false
+ * Throws an exception no experience table
*
* @return Table read when leveling up
+ * @see #hasExperienceTable()
*/
@NotNull
ExperienceTable getExperienceTable();
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java
index b1ec2403..484cf639 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Random;
public class PlayerProfessions {
private final Map exp = new HashMap<>();
@@ -120,11 +121,14 @@ public class PlayerProfessions {
}
public long getLevelUpExperience(Profession profession) {
- return profession.getExpCurve().getExperience(getLevel(profession) + 1);
+ return profession.getExpCurve().getExperience(playerData, getLevel(profession));
}
+ @Deprecated
public long getLevelUpExperience(String id) {
- return MMOCore.plugin.professionManager.has(id) ? MMOCore.plugin.professionManager.get(id).getExpCurve().getExperience(getLevel(id) + 1) : 0;
+ final var prof = MMOCore.plugin.professionManager.get(id);
+ if (prof == null) return 0;
+ return prof.getExpCurve().getExperience(null, getLevel(id));
}
@Deprecated
@@ -132,6 +136,12 @@ public class PlayerProfessions {
setLevel(profession, value, PlayerLevelChangeEvent.Reason.UNKNOWN);
}
+ @Deprecated
+ public void takeLevels(@NotNull Profession profession, int value) {
+ int current = Math.max(1, level.getOrDefault(profession.getId(), 1));
+ level.put(profession.getId(), Math.max(1, current - value));
+ }
+
public void setLevel(@NotNull Profession profession, int newLevel, @NotNull PlayerLevelChangeEvent.Reason reason) {
if (profession.hasMaxLevel()) newLevel = Math.min(profession.getMaxLevel(), newLevel);
final var previousValue = level.put(profession.getId(), Math.max(1, newLevel));
@@ -146,12 +156,6 @@ public class PlayerProfessions {
}
}
- @Deprecated
- public void takeLevels(@NotNull Profession profession, int value) {
- int current = Math.max(1, level.getOrDefault(profession.getId(), 1));
- level.put(profession.getId(), Math.max(1, current - value));
- }
-
public void setExperience(Profession profession, double value) {
exp.put(profession.getId(), value);
}
@@ -159,7 +163,8 @@ public class PlayerProfessions {
public void giveLevels(Profession profession, int value, EXPSource source) {
long equivalentExp = 0;
final var currentLevel = getLevel(profession);
- while (value-- > 0) equivalentExp += profession.getExpCurve().getExperience(currentLevel + value + 1);
+ while (value-- > 0)
+ equivalentExp += profession.getExpCurve().getExperience(playerData, currentLevel + value);
giveExperience(profession, equivalentExp, source);
}
@@ -171,6 +176,8 @@ public class PlayerProfessions {
giveExperience(profession, value, source, null, true);
}
+ private static final Random RANDOM = new Random();
+
public void giveExperience(@NotNull Profession profession, double value, @NotNull EXPSource source, @Nullable Location hologramLocation, boolean splitExp) {
Validate.isTrue(playerData.isOnline(), "Cannot give experience to offline player");
if (value <= 0) {
@@ -200,8 +207,11 @@ public class PlayerProfessions {
if (event.isCancelled()) return;
// Display hologram
- if (hologramLocation != null && profession.getOption(Profession.ProfessionOption.EXP_HOLOGRAMS))
- MMOCoreUtils.displayIndicator(hologramLocation.add(.5, 1.5, .5), Language.EXP_HOLOGRAM.getFormat().replace("{exp}", MythicLib.plugin.getMMOConfig().decimal.format(event.getExperience())));
+ if (hologramLocation != null && profession.getOption(Profession.ProfessionOption.EXP_HOLOGRAMS)) {
+ // TODO custom location per profession/exp source.
+ hologramLocation.add(.5 + .7 * RANDOM.nextDouble(), 1.3 + RANDOM.nextDouble() / 3, .5 + .7 * RANDOM.nextDouble());
+ MMOCoreUtils.displayIndicator(hologramLocation, Language.EXP_HOLOGRAM.getFormat().replace("{exp}", MythicLib.plugin.getMMOConfig().decimal.format(event.getExperience())));
+ }
final var maxLevel = profession.getMaxLevel();
var currentExp = Math.max(0d, exp.getOrDefault(profession.getId(), 0d) + event.getExperience());
@@ -213,7 +223,7 @@ public class PlayerProfessions {
* Loop for exp overload when leveling up, will continue
* looping until exp is 0 or max newLevel has been reached
*/
- while (currentExp >= (experienceNeeded = profession.getExpCurve().getExperience(newLevel))) {
+ while (currentExp >= (experienceNeeded = profession.getExpCurve().getExperience(playerData, newLevel))) {
if (maxLevel > 0 && newLevel >= maxLevel) {
currentExp = 0;
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/Profession.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/Profession.java
index a1a8435f..4d45218f 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/Profession.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/Profession.java
@@ -6,6 +6,7 @@ import io.lumine.mythic.lib.util.PreloadedObject;
import io.lumine.mythic.lib.util.lang3.Validate;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.experience.curve.ExperienceCurve;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.util.formula.ScalingFormula;
import org.bukkit.Location;
@@ -22,7 +23,8 @@ public class Profession implements ExperienceObject, PreloadedObject {
private final String id, name;
private final int maxLevel;
private final Map options = new HashMap<>();
- private final ExpCurve expCurve;
+ @NotNull
+ private final ExperienceCurve expCurve;
private final ExperienceTable expTable;
/**
@@ -44,9 +46,7 @@ public class Profession implements ExperienceObject, PreloadedObject {
this.name = config.getString("name");
Validate.notNull(name, "Could not load name");
- expCurve = config.contains("exp-curve")
- ? MMOCore.plugin.experience.getCurveOrThrow(config.get("exp-curve").toString().toLowerCase().replace("_", "-").replace(" ", "-"))
- : ExpCurve.DEFAULT;
+ expCurve = ExperienceCurve.fromConfig(config.getString("exp-curve"));
experience = ScalingFormula.fromConfig(config.get("experience"));
ExperienceTable expTable = null;
@@ -99,12 +99,12 @@ public class Profession implements ExperienceObject, PreloadedObject {
}
@Override
- public String getKey() {
+ public @NotNull String getKey() {
return "profession_" + getId();
}
@Override
- public ExpCurve getExpCurve() {
+ public @NotNull ExperienceCurve getExpCurve() {
return expCurve;
}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/ExperienceCurve.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/ExperienceCurve.java
new file mode 100644
index 00000000..f0aca45b
--- /dev/null
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/ExperienceCurve.java
@@ -0,0 +1,57 @@
+package net.Indyuce.mmocore.experience.curve;
+
+import io.lumine.mythic.lib.util.FileUtils;
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface ExperienceCurve {
+
+ /**
+ * Arbitrary values. MMOCore needs a default exp curve for everything otherwise
+ * it would be swarming be divisions by 0 when trying to update the vanilla
+ * exp bar which requires a 0.0 -> 1.0 float as parameter.
+ *
+ * See {@link PlayerData#refreshVanillaExp()}
+ */
+ public static final ExperienceCurve DEFAULT = new ListExperienceCurve(100, 200, 300, 400, 500);
+
+ /**
+ * If 1 is provided as level, this method returns the experience needed
+ * to reach level 2. If the exp curve is a formula, it will provide 1 to the formula.
+ * If the exp curve is a list, it will return the first element of the list.
+ *
+ * @param player Player leveling up
+ * @param level Current level of the player.
+ * @return Experience needed to reach the next level.
+ */
+ public long getExperience(@NotNull PlayerData player, int level);
+
+ @NotNull
+ public static ExperienceCurve fromConfig(@Nullable String configInput) {
+
+ // [BACKWARDS COMPATIBILITY] Load from predefined file
+ try {
+ if (configInput == null) throw new RuntimeException();
+ final var expCurveFile = FileUtils.getFile(MMOCore.plugin, "expcurves/" + configInput + ".txt");
+ return new ListExperienceCurve(expCurveFile);
+ } catch (Exception ignored) {
+ }
+
+ if (configInput == null) {
+ return DEFAULT;
+ }
+
+ // Load from file
+ else if (configInput.endsWith(".txt")) {
+ final var expCurveFile = FileUtils.getFile(MMOCore.plugin, configInput);
+ return new ListExperienceCurve(expCurveFile);
+ }
+
+ // Load as formula
+ else {
+ return new FormulaExperienceCurve(configInput);
+ }
+ }
+}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/FormulaExperienceCurve.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/FormulaExperienceCurve.java
new file mode 100644
index 00000000..5d292309
--- /dev/null
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/FormulaExperienceCurve.java
@@ -0,0 +1,30 @@
+package net.Indyuce.mmocore.experience.curve;
+
+import io.lumine.mythic.lib.MythicLib;
+import io.lumine.mythic.lib.util.formula.NumericalExpression;
+import io.lumine.mythic.lib.util.lang3.Validate;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import org.jetbrains.annotations.NotNull;
+
+public class FormulaExperienceCurve implements ExperienceCurve {
+ private final String formula;
+
+ public FormulaExperienceCurve(@NotNull String formula) {
+ this.formula = formula;
+ }
+
+ @Override
+ public long getExperience(@NotNull PlayerData player, int level) {
+ try {
+ Validate.isTrue(level > 0, "Level must be stricly positive, got " + level);
+ final var parsed = MythicLib.plugin.getPlaceholderParser().parse(player.getPlayer(), this.formula);
+ final var value = (int) NumericalExpression.eval(parsed.replace("{level}", String.valueOf(level)));
+ Validate.isTrue(value > 0, "Exp curve must return a positive value, got " + value);
+ return value;
+
+ } catch (Exception e) {
+ MythicLib.plugin.getLogger().warning("Error parsing exp formula '" + this.formula + "' for player " + player.getPlayer().getName() + ", using 100: " + e.getMessage());
+ return 100;
+ }
+ }
+}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/ListExperienceCurve.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/ListExperienceCurve.java
new file mode 100644
index 00000000..8d1b1807
--- /dev/null
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/curve/ListExperienceCurve.java
@@ -0,0 +1,58 @@
+package net.Indyuce.mmocore.experience.curve;
+
+import io.lumine.mythic.lib.util.lang3.Validate;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListExperienceCurve implements ExperienceCurve {
+
+ /**
+ * Experience needed to level up. At index i, experience needed
+ * to level up from level N to (N + 1).
+ */
+ private final List experience;
+
+ /**
+ * Reads an exp curve from a text file, one line after the other. Each
+ * exp value has to be the only thing written on every line
+ *
+ * @param file Text file to read data from
+ */
+ public ListExperienceCurve(@NotNull File file) {
+ this.experience = new ArrayList<>();
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String readLine;
+ while ((readLine = reader.readLine()) != null)
+ experience.add(Long.valueOf(readLine));
+ reader.close();
+
+ Validate.isTrue(!experience.isEmpty(), "There must be at least one exp value in your exp curve");
+ } catch (Throwable throwable) {
+ throw new RuntimeException(throwable);
+ }
+ }
+
+ /**
+ * Exp curve with specific level up exp values
+ *
+ * @param values The exp values. There has to be at least one
+ */
+ public ListExperienceCurve(long... values) {
+ this.experience = new ArrayList<>(values.length);
+ for (long value : values) experience.add(value);
+ Validate.isTrue(!experience.isEmpty(), "There must be at least one exp value in your exp curve");
+ }
+
+ @Override
+ public long getExperience(@NotNull PlayerData player, int level) {
+ Validate.isTrue(level > 0, "Level must be stricly positive");
+ return experience.get(Math.min(level, experience.size()));
+ }
+}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/source/CraftItemExperienceSource.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/source/CraftItemExperienceSource.java
index 246824d5..0a4bcd86 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/source/CraftItemExperienceSource.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/experience/source/CraftItemExperienceSource.java
@@ -15,6 +15,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.inventory.CraftItemEvent;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CraftItemExperienceSource extends SpecificExperienceSource {
@@ -45,7 +46,7 @@ public class CraftItemExperienceSource extends SpecificExperienceSource {
- // First check
- int newAmount = getAmount(event.getInventory().getMatrix()[index]);
+ final var newItem = event.getInventory().getMatrix()[index];
+ final var newAmount = getEffectiveNewAmount(newItem, oldItem);
+
if (newAmount >= oldAmount) return;
// Deduce amount crafted
- int amountCrafted = (event.getClick().isShiftClick() ? oldAmount - newAmount : 1) * itemsCraftedPerRecipe;
+ final var amountCrafted = (event.getClick().isShiftClick() ? oldAmount - newAmount : 1) * itemsCraftedPerRecipe;
for (CraftItemExperienceSource source : getSources())
if (source.matches(data, resultType))
source.giveExperience(data, amountCrafted, event.getInventory().getLocation());
});
}
- private int getAmount(@Nullable ItemStack item) {
- return item == null || item.getType() == Material.AIR ? 0 : item.getAmount();
+ private int getEffectiveNewAmount(@Nullable ItemStack item, @NotNull ItemStack oldItem) {
+ // Some items are consumed and leave behind a different type (e.g. buckets)
+ // If that's the case just say all items were crafted
+ // Fixes https://gitlab.com/phoenix-dvpmt/mmocore/-/issues/1165
+ return item == null || item.getType() == Material.AIR || item.getType() != oldItem.getType() ? 0 : item.getAmount();
}
private int getLowerAmountIngredientIndex(ItemStack[] matrix) {
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/AttributeManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/AttributeManager.java
index 5ea81849..492e114f 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/AttributeManager.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/AttributeManager.java
@@ -1,10 +1,10 @@
package net.Indyuce.mmocore.manager;
import io.lumine.mythic.lib.MythicLib;
+import io.lumine.mythic.lib.api.stat.StatInstance;
import io.lumine.mythic.lib.util.FileUtils;
-import io.lumine.mythic.lib.util.config.YamlFile;
import net.Indyuce.mmocore.MMOCore;
-import net.Indyuce.mmocore.api.player.attribute.MMOCoreAttributeStatHandler;
+import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,20 +40,23 @@ public class AttributeManager implements MMOCoreManager {
@Override
public void initialize(boolean clearBefore) {
- if (clearBefore) {
+ if (clearBefore)
map.clear();
- MythicLib.plugin.getStats().clearRegisteredStats(handler -> handler instanceof MMOCoreAttributeStatHandler);
- }
FileUtils.loadObjectsFromFolder(MMOCore.plugin, "attributes", false, (key, config) -> {
final String path = key.toLowerCase().replace("_", "-").replace(" ", "-");
map.put(path, new PlayerAttribute(config));
}, "Could not load attribute '%s' from file '%s': %s");
- final var statsConfig = new YamlFile(MythicLib.plugin, "stats").getContent();
+ // MythicLib stat handlers
for (PlayerAttribute attr : getAll()) {
- final MMOCoreAttributeStatHandler handler = new MMOCoreAttributeStatHandler(statsConfig, attr);
- MythicLib.plugin.getStats().registerStat(handler, handler.getStat() + "_PERCENT");
+ final var statId = attr.getId().toUpperCase().replace("-", "_") + "_PERCENT";
+ MythicLib.plugin.getStats().computeStat(statId).addUpdateListener(ins -> this.updateMMOCoreStatAttributeValue(ins, attr));
}
}
+
+ private void updateMMOCoreStatAttributeValue(@NotNull StatInstance instance, PlayerAttribute attribute) {
+ final var playerData = PlayerData.get(instance.getMap().getPlayerData());
+ playerData.getAttributes().getInstance(attribute).updateStats();
+ }
}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java
index db470987..c8572d22 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java
@@ -69,12 +69,6 @@ public class ConfigManager {
if (!FileUtils.getFile(MMOCore.plugin, "exp-tables").exists())
copyDefaultFile("exp-tables/default_exp_tables.yml");
- if (!FileUtils.getFile(MMOCore.plugin, "expcurves").exists()) {
- copyDefaultFile("expcurves/levels.txt");
- copyDefaultFile("expcurves/mining.txt");
- copyDefaultFile("expcurves/skill-tree-node.txt");
- }
-
if (!FileUtils.getFile(MMOCore.plugin, "loot-chests").exists())
copyDefaultFile("loot-chests/default_loot_chests.yml");
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ExperienceManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ExperienceManager.java
index 44cb2ca8..7f97950f 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ExperienceManager.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ExperienceManager.java
@@ -1,9 +1,8 @@
package net.Indyuce.mmocore.manager;
import io.lumine.mythic.lib.util.FileUtils;
-import io.lumine.mythic.lib.util.lang3.Validate;
import net.Indyuce.mmocore.MMOCore;
-import net.Indyuce.mmocore.experience.ExpCurve;
+import net.Indyuce.mmocore.experience.curve.ExperienceCurve;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
@@ -14,7 +13,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class ExperienceManager implements MMOCoreManager {
- private final Map expCurves = new HashMap<>();
private final Map expTables = new HashMap<>();
/**
@@ -45,14 +43,29 @@ public class ExperienceManager implements MMOCoreManager {
getManager(path).registerSource(source);
}
+ @Deprecated
public boolean hasCurve(String id) {
- return expCurves.containsKey(id);
+ try {
+ ExperienceCurve.fromConfig("expcurves/" + id + ".txt");
+ return true;
+ } catch (Exception ignored) {
+ return false;
+ }
}
+ @Deprecated
@NotNull
- public ExpCurve getCurveOrThrow(String id) {
- Validate.isTrue(hasCurve(id), "Could not find exp curve with ID '" + id + "'");
- return expCurves.get(id);
+ public ExperienceCurve getCurveOrThrow(String id) {
+ try {
+ return ExperienceCurve.fromConfig(id + ".txt");
+ } catch (Exception ignored) {
+ throw new IllegalArgumentException("Could not find exp curve with ID '" + id + "'");
+ }
+ }
+
+ @Deprecated
+ public Collection getCurves() {
+ return List.of();
}
@Deprecated
@@ -82,10 +95,6 @@ public class ExperienceManager implements MMOCoreManager {
throw new IllegalArgumentException("Please provide either a string (exp table name) or a config section (locally define an exp table)");
}
- public Collection getCurves() {
- return expCurves.values();
- }
-
public Collection getTables() {
return expTables.values();
}
@@ -100,21 +109,15 @@ public class ExperienceManager implements MMOCoreManager {
@Override
public void initialize(boolean clearBefore) {
if (clearBefore) {
- expCurves.clear();
expTables.clear();
managers.forEach((c, manager) -> manager.close());
managers.clear();
}
- // Exp curves
- FileUtils.loadObjectsFromFolderRaw(MMOCore.plugin, "expcurves", file -> {
- final ExpCurve curve = new ExpCurve(file);
- expCurves.put(curve.getId(), curve);
- }, "Could not load exp curve from file '%s': %s");
// Exp tables
- FileUtils.loadObjectsFromFolder(MMOCore.plugin ,"exp-tables", false, (key, config) -> {
+ FileUtils.loadObjectsFromFolder(MMOCore.plugin, "exp-tables", false, (key, config) -> {
final ExperienceTable table = new ExperienceTable(config);
expTables.put(table.getId(), table);
}, "Could not load exp table '%s' from file '%s': %s");
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLDatabaseImpl.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLDatabaseImpl.java
index 58da095b..4f47d43b 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLDatabaseImpl.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLDatabaseImpl.java
@@ -161,7 +161,11 @@ public class YAMLDatabaseImpl extends YAMLFlatDatabase config.set("skill-tree-level." + node.getFullId(), data.getNodeLevel(node)));
+ config.createSection("skill-tree-level");
+ for (var node : MMOCore.plugin.skillTreeManager.getAllNodes()) {
+ final var nodeLevel = data.getNodeLevel(node);
+ if (nodeLevel > 0) config.set("skill-tree-level." + node.getFullId(), nodeLevel);
+ }
// Skill levels
config.set("skill", null);
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java
index 8f22f1b7..0e9e7da1 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/profession/CustomBlockManager.java
@@ -205,12 +205,15 @@ public class CustomBlockManager extends SpecificProfessionManager {
this.protectVanillaBlocks = config.getBoolean("protect-vanilla-blocks");
this.enableToolRestrictions = config.getBoolean("enable-tool-restrictions");
- for (String key : config.getStringList("conditions"))
- try {
- customMineConditions.add(MMOCore.plugin.loadManager.loadCondition(new MMOLineConfig(key)));
- } catch (IllegalArgumentException exception) {
- MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load custom mining condition '" + key + "': " + exception.getMessage());
- }
+ // Avoid warnings if disabled
+ if (enabled)
+ for (String key : config.getStringList("conditions"))
+ try {
+ customMineConditions.add(MMOCore.plugin.loadManager.loadCondition(new MMOLineConfig(key)));
+ } catch (IllegalArgumentException exception) {
+ MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load custom mining condition '" + key + "': " + exception.getMessage());
+ }
+ else customMineConditions.clear();
}
@Deprecated
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java
index c39d3b76..80067ee7 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/QuestModuleType.java
@@ -1,7 +1,7 @@
package net.Indyuce.mmocore.quest;
import net.Indyuce.mmocore.quest.compat.BeautyQuestModule;
-import net.Indyuce.mmocore.quest.compat.BlackVeinQuestsModule;
+import net.Indyuce.mmocore.quest.compat.QuestsModule;
import net.Indyuce.mmocore.quest.compat.QuestCreatorModule;
import net.Indyuce.mmocore.quest.compat.QuestModule;
import org.bukkit.Bukkit;
@@ -10,7 +10,7 @@ import javax.inject.Provider;
public enum QuestModuleType {
MMOCORE("MMOCore", MMOCoreQuestModule::new),
- QUESTS("Quests", BlackVeinQuestsModule::new),
+ QUESTS("Quests", QuestsModule::new),
BEAUTY_QUEST("BeautyQuests", BeautyQuestModule::new),
QUEST_CREATOR("QuestCreator", QuestCreatorModule::new);
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/compat/BlackVeinQuestsModule.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/compat/BlackVeinQuestsModule.java
deleted file mode 100644
index 8819b5ef..00000000
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/compat/BlackVeinQuestsModule.java
+++ /dev/null
@@ -1,54 +0,0 @@
-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());
- if(quester==null)
- return false;
- 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/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/compat/QuestsModule.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/compat/QuestsModule.java
new file mode 100644
index 00000000..1b69b47f
--- /dev/null
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/quest/compat/QuestsModule.java
@@ -0,0 +1,52 @@
+package net.Indyuce.mmocore.quest.compat;
+
+import me.pikamug.quests.BukkitQuestsPlugin;
+import me.pikamug.quests.player.Quester;
+import me.pikamug.quests.quests.Quest;
+import net.Indyuce.mmocore.quest.AbstractQuest;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+public class QuestsModule implements QuestModule {
+ private final BukkitQuestsPlugin plugin;
+
+ public QuestsModule() {
+ plugin = (BukkitQuestsPlugin) Bukkit.getPluginManager().getPlugin("Quests");
+ }
+
+ @Override
+ public QuestImpl getQuestOrThrow(String id) {
+ final var found = plugin.getQuest(id);
+ return found == null ? null : new QuestImpl(found);
+ }
+
+
+ @Override
+ public boolean hasCompletedQuest(String questId, Player player) {
+ Quester quester = plugin.getQuester(player.getUniqueId());
+ if (quester == null) return false;
+
+ for (var quest : quester.getCompletedQuests())
+ if (quest.getId().equals(questId)) return true;
+
+ return false;
+ }
+
+ public static class QuestImpl implements AbstractQuest {
+ private final Quest quest;
+
+ public QuestImpl(Quest quest) {
+ this.quest = quest;
+ }
+
+ @Override
+ public String getName() {
+ return quest.getName();
+ }
+
+ @Override
+ public String getId() {
+ return quest.getId();
+ }
+ }
+}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java
index 28730015..4192ef0f 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java
@@ -9,7 +9,7 @@ import io.lumine.mythic.lib.util.lang3.Validate;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.EXPSource;
-import net.Indyuce.mmocore.experience.ExpCurve;
+import net.Indyuce.mmocore.experience.curve.ExperienceCurve;
import net.Indyuce.mmocore.experience.ExperienceObject;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.skilltree.display.DisplayMap;
@@ -204,7 +204,7 @@ public class SkillTreeNode implements ExperienceObject {
public static final String KEY_PREFIX = "node";
@Override
- public String getKey() {
+ public @NotNull String getKey() {
return KEY_PREFIX + ":" + getFullId().replace("-", "_");
}
@@ -269,9 +269,9 @@ public class SkillTreeNode implements ExperienceObject {
throw new RuntimeException("Skill trees don't have experience");
}
- @Nullable
+ @NotNull
@Override
- public ExpCurve getExpCurve() {
+ public ExperienceCurve getExpCurve() {
throw new RuntimeException("Skill trees don't have experience");
}
diff --git a/MMOCore-Dist/src/main/resources/default/classes/human.yml b/MMOCore-Dist/src/main/resources/default/classes/human.yml
index 0159f9f0..feae290f 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/human.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/human.yml
@@ -20,8 +20,14 @@ options:
off-combat-health-regen: false
off-combat-mana-regen: false
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
exp-table: class_exp_table
diff --git a/MMOCore-Dist/src/main/resources/default/classes/mage/arcane-mage.yml b/MMOCore-Dist/src/main/resources/default/classes/mage/arcane-mage.yml
index 9ff26ae9..42ef55ed 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/mage/arcane-mage.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/mage/arcane-mage.yml
@@ -36,8 +36,14 @@ display:
# item-model: 'minecraft:dirt'
# texture: '' # Skull texture (set 'item' to 'PLAYER_HEAD')
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
skill-slots:
1:
diff --git a/MMOCore-Dist/src/main/resources/default/classes/mage/mage.yml b/MMOCore-Dist/src/main/resources/default/classes/mage/mage.yml
index 8b8170a8..aa07a29f 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/mage/mage.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/mage/mage.yml
@@ -36,8 +36,14 @@ display:
# item-model: 'minecraft:dirt'
# texture: '' # Skull texture (set 'item' to 'PLAYER_HEAD')
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
# The maximum level players can reach
max-level: 100
diff --git a/MMOCore-Dist/src/main/resources/default/classes/marksman.yml b/MMOCore-Dist/src/main/resources/default/classes/marksman.yml
index ef545ca1..ac99af4b 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/marksman.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/marksman.yml
@@ -35,8 +35,14 @@ display:
# item-model: 'minecraft:dirt'
# texture: '' # Skull texture (set 'item' to 'PLAYER_HEAD')
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
# The maximum level players can reach
max-level: 100
diff --git a/MMOCore-Dist/src/main/resources/default/classes/paladin.yml b/MMOCore-Dist/src/main/resources/default/classes/paladin.yml
index 961053b3..f3cc716d 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/paladin.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/paladin.yml
@@ -35,8 +35,14 @@ display:
# item-model: 'minecraft:dirt'
# texture: '' # Skull texture (set 'item' to 'PLAYER_HEAD')
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
skill-slots:
1:
diff --git a/MMOCore-Dist/src/main/resources/default/classes/rogue.yml b/MMOCore-Dist/src/main/resources/default/classes/rogue.yml
index 1854bc8b..299f539a 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/rogue.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/rogue.yml
@@ -36,8 +36,14 @@ display:
# item-model: 'minecraft:dirt'
# texture: '' # Skull texture (set 'item' to 'PLAYER_HEAD')
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
# The maximum level players can reach
max-level: 100
diff --git a/MMOCore-Dist/src/main/resources/default/classes/warrior.yml b/MMOCore-Dist/src/main/resources/default/classes/warrior.yml
index 76afe9ca..7c9cc689 100644
--- a/MMOCore-Dist/src/main/resources/default/classes/warrior.yml
+++ b/MMOCore-Dist/src/main/resources/default/classes/warrior.yml
@@ -39,8 +39,14 @@ display:
# item-model: 'minecraft:dirt'
# texture: '' # Skull texture (set 'item' to 'PLAYER_HEAD')
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/class_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current class level.
+# | Example: '{level} * 200'
+exp-curve: '{level} * 200'
# The maximum level players can reach
max-level: 100
diff --git a/MMOCore-Dist/src/main/resources/default/expcurves/levels.txt b/MMOCore-Dist/src/main/resources/default/expcurves/levels.txt
deleted file mode 100644
index 426fae7e..00000000
--- a/MMOCore-Dist/src/main/resources/default/expcurves/levels.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-200
-400
-600
-800
-1000
-1200
-1400
-1600
-1800
-2000
-2200
-2400
-2600
-2800
-3000
-3200
-3400
-3600
-3800
-4000
-4200
-4400
-4600
-4800
-5000
diff --git a/MMOCore-Dist/src/main/resources/default/expcurves/mining.txt b/MMOCore-Dist/src/main/resources/default/expcurves/mining.txt
deleted file mode 100644
index c9f11755..00000000
--- a/MMOCore-Dist/src/main/resources/default/expcurves/mining.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-100
-200
-300
-400
-500
-600
-700
-800
-900
-1000
-1100
-1200
-1300
-1400
-1500
-1600
-1700
-1800
-1900
-2000
-2100
-2200
-2300
-2400
-2500
diff --git a/MMOCore-Dist/src/main/resources/default/expcurves/skill-tree-node.txt b/MMOCore-Dist/src/main/resources/default/expcurves/skill-tree-node.txt
deleted file mode 100644
index d82f05d1..00000000
--- a/MMOCore-Dist/src/main/resources/default/expcurves/skill-tree-node.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-23
-24
-25
\ No newline at end of file
diff --git a/MMOCore-Dist/src/main/resources/default/professions/alchemy.yml b/MMOCore-Dist/src/main/resources/default/professions/alchemy.yml
index a66fa797..8a9a1cb7 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/alchemy.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/alchemy.yml
@@ -8,8 +8,14 @@ experience:
base: 20
per-level: 3
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
exp-sources:
- 'brewpotion{effect=SPEED}'
@@ -20,31 +26,31 @@ exp-sources:
alchemy-experience:
special:
-
+
# When brewing a potion into a splash potion,
# only 40% of the base EXP is earned.
splash: 40
-
+
# When brewing a potion into a splash potion,
# only 40% of the base EXP is earned.
lingering: 40
-
+
# When extending a pot duration,
# only 40% of base EXP is earned.
extend: 40
-
+
# When upgrading a potion level,
# only 40% of base EXP is earned.
upgrade: 40
# Base EXP of potions
effects:
-
+
# Water bottles
AWKWARD: 5
MUNDANE: 5
THICK: 5
-
+
# Potion effects
NIGHT_VISION: 10
INVISIBILITY: 10
diff --git a/MMOCore-Dist/src/main/resources/default/professions/enchanting.yml b/MMOCore-Dist/src/main/resources/default/professions/enchanting.yml
index bead3bf5..4e56d02c 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/enchanting.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/enchanting.yml
@@ -8,8 +8,14 @@ experience:
base: 10
per-level: 2
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
# Remove the 'enchant' parameter from the line config
# to make the profession get EXP with ANY enchant.
diff --git a/MMOCore-Dist/src/main/resources/default/professions/farming.yml b/MMOCore-Dist/src/main/resources/default/professions/farming.yml
index 32ee5d84..078a3fc4 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/farming.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/farming.yml
@@ -7,8 +7,14 @@ experience:
base: 10
per-level: 2
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
exp-sources:
- 'mineblock{type=WHEAT;amount=1-3;crop=true;player-placed=true;silk-touch=false}'
diff --git a/MMOCore-Dist/src/main/resources/default/professions/fishing.yml b/MMOCore-Dist/src/main/resources/default/professions/fishing.yml
index c16a7d65..673596db 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/fishing.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/fishing.yml
@@ -8,8 +8,14 @@ experience:
base: 20
per-level: 3
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
exp-sources: {}
diff --git a/MMOCore-Dist/src/main/resources/default/professions/mining.yml b/MMOCore-Dist/src/main/resources/default/professions/mining.yml
index 49e857b7..be7df223 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/mining.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/mining.yml
@@ -8,8 +8,14 @@ experience:
base: 20
per-level: 3
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: mining
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
# This part of the config is ONLY for custom mining.
# Custom Mining must be setup in config.yml and it
diff --git a/MMOCore-Dist/src/main/resources/default/professions/smelting.yml b/MMOCore-Dist/src/main/resources/default/professions/smelting.yml
index 807809b3..d7eee550 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/smelting.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/smelting.yml
@@ -7,8 +7,14 @@ experience:
base: 20
per-level: 3
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
exp-sources:
- 'craftitem{type=BLAST_FURNACE;amount=1}'
diff --git a/MMOCore-Dist/src/main/resources/default/professions/smithing.yml b/MMOCore-Dist/src/main/resources/default/professions/smithing.yml
index 721d14db..94b7537c 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/smithing.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/smithing.yml
@@ -8,8 +8,14 @@ experience:
base: 20
per-level: 3
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
exp-sources:
- 'repairitem{}'
diff --git a/MMOCore-Dist/src/main/resources/default/professions/woodcutting.yml b/MMOCore-Dist/src/main/resources/default/professions/woodcutting.yml
index a80bd226..1f3113d4 100644
--- a/MMOCore-Dist/src/main/resources/default/professions/woodcutting.yml
+++ b/MMOCore-Dist/src/main/resources/default/professions/woodcutting.yml
@@ -8,8 +8,14 @@ experience:
base: 13
per-level: 2.5
-# Must match an existing exp curve filename from the 'expcurves' folder
-exp-curve: levels
+# Can be a file path to an existing .txt file inside the MMOCore/ folder.
+# | The TXT files contains only the experience required per level, one per line.
+# | Example: 'expcurves/profession_exp_curve.txt'
+# Or a formula using the {level} and PAPI placeholders.
+# | The formula should return the total experience required to reach the next
+# | level if {level} is the player's current profession level.
+# | Example: '{level} * 150'
+exp-curve: '{level} * 150'
exp-sources:
- 'mineblock{type=OAK_LOG;amount=1-3}'