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 c47dd73b..aa3bb34b 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 @@ -147,7 +147,6 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD public void reload() { try { profess = profess == null ? null : MMOCore.plugin.classManager.get(profess.getId()); - getStats().updateStats(); } catch (NullPointerException exception) { MMOCore.log(Level.SEVERE, "[Userdata] Could not find class " + getProfess().getId() + " while refreshing player data."); } @@ -172,20 +171,69 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD if (!nodeLevels.containsKey(node)) nodeLevels.put(node, 0); setupSkillTree(); - setupRemovableTrigger(); + updateTemporaryTriggers(); + getStats().updateStats(); } + /** + * This script is called when the player data has been successfully + * loaded from the SQL/local text database. + */ + @Override + public void markAsSynchronized() { + + /* + * If the player is not dead and the health is 0, this means that the data was + * missing from the database and it gives full health to the player. If the + * player is dead however, it must not account for that subtle edge case. + */ + if (isOnline() && !getPlayer().isDead()) + getPlayer().setHealth(MMOCoreUtils.fixResource(getHealth(), getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue())); + + setupSkillTree(); + updateTemporaryTriggers(); + getStats().updateStats(true); + + // Finally mark synchronized + super.markAsSynchronized(); + } + + @Deprecated public void setupRemovableTrigger() { - //We remove all the stats and buffs associated to triggers. + updateTemporaryTriggers(); + } + + /** + * Some triggers are marked with the Removable interface as + * they are non-permanent triggers and they need to be updated + * everytime their MMOPlayerData gets flushed from ML cache. + *
+ * This method should go through ALL {@link ExperienceTable} + * that the player has spent points into and register all + * non-permanent triggers. + *
+ * For ease of implementation, these non-permanent triggers are
+ * refreshed everytime the player joins the server ie on every
+ * player data fetch.
+ *
+ * @see {@link net.Indyuce.mmocore.api.quest.trigger.api.Removable}
+ */
+ public void updateTemporaryTriggers() {
+
+ // Remove all stats and buffs associated to triggers
getMMOPlayerData().getStatMap().getInstances().forEach(statInstance -> statInstance.removeIf(Trigger.STAT_MODIFIER_KEY::equals));
getMMOPlayerData().getSkillModifierMap().getInstances().forEach(skillModifierInstance -> skillModifierInstance.removeIf(Trigger.STAT_MODIFIER_KEY::equals));
+ // Experience tables from main class
if (profess.hasExperienceTable())
profess.getExperienceTable().claimRemovableTrigger(this, profess);
+
+ // Experience tables from professions
for (Profession profession : MMOCore.plugin.professionManager.getAll())
if (profession.hasExperienceTable())
profession.getExperienceTable().claimRemovableTrigger(this, profession);
- // Stat triggers setup
+
+ // Experience tables from skill tree nodes
for (SkillTree skillTree : MMOCore.plugin.skillTreeManager.getAll())
for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().claimRemovableTrigger(this, node);
@@ -358,7 +406,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
/**
* @return If the item is unlocked by the player
- * This is used for skills that can be locked & unlocked.
+ * This is used for skills that can be locked & unlocked.
*/
public boolean hasUnlocked(Unlockable unlockable) {
return unlockable.isUnlockedByDefault() || unlockedItems.contains(unlockable.getUnlockNamespacedKey());
@@ -506,10 +554,10 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return sum;
}
- @Deprecated
+ @NotNull
public List
- * This is also called when reloading the plugin to make class setup easier,
- * see {@link PlayerData#reload()} for more info
+ * Login scripts are a pretty special case of scripts/skills since they are
+ * not loaded yet when MythicLib triggers them naturally. Therefore, they
+ * need to be cast as soon as they are loaded into the MMOCore player data.
+ *
+ * @param castLoginScripts Should login scripts be cast
*/
- public synchronized void updateStats() {
+ public synchronized void updateStats(boolean castLoginScripts) {
// Update player stats
for (String stat : MMOCore.plugin.statManager.getRegistered()) {
@@ -91,19 +101,33 @@ public class PlayerStats {
packet.runUpdate();
}
- // Updates the player's unbindable passive skills.
+ // Updates the player's unbindable CLASS passive skills
final PassiveSkillMap skillMap = data.getMMOPlayerData().getPassiveSkillMap();
skillMap.removeModifiers("MMOCorePassiveSkillNotBound");
- data.getProfess().getSkills().stream()
- .filter((classSkill) -> !classSkill.needsBound() && classSkill.getSkill().getTrigger().isPassive() && data.hasUnlocked(classSkill) && data.hasUnlockedLevel(classSkill))
- .forEach(classSkill -> skillMap.addModifier(classSkill.toPassive(data)));
+ for (ClassSkill skill : data.getProfess().getSkills())
+ if (!skill.needsBound()
+ && skill.getSkill().getTrigger().isPassive()
+ && skill.getSkill().getTrigger() != TriggerType.LOGIN
+ && data.hasUnlocked(skill)
+ && data.hasUnlockedLevel(skill))
+ skillMap.addModifier(skill.toPassive(data));
- /*
- * Updates the player's class scripts, which act just
- * like non-bindable passive skills.
- */
+ // Updates the player's CLASS scripts
skillMap.removeModifiers("MMOCoreClassScript");
for (PassiveSkill script : data.getProfess().getScripts())
- skillMap.addModifier(script);
+ if (script.getType() != TriggerType.LOGIN) skillMap.addModifier(script);
+
+ // If data hasn't been synchronized yet, cast LOGIN scripts
+ if (castLoginScripts) {
+
+ // Call class login skills
+ for (ClassSkill skill : data.getProfess().getSkills())
+ if (skill.getSkill().getTrigger() == TriggerType.LOGIN)
+ skill.toCastable(data).cast(data.getMMOPlayerData());
+
+ // Call class login scripts
+ for (PassiveSkill skill : data.getProfess().getScripts())
+ if (skill.getType() == TriggerType.LOGIN) skill.getTriggeredSkill().cast(data.getMMOPlayerData());
+ }
}
}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/api/Removable.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/api/Removable.java
index 533683ca..eb0c90eb 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/api/Removable.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/api/Removable.java
@@ -2,6 +2,8 @@ package net.Indyuce.mmocore.api.quest.trigger.api;
import net.Indyuce.mmocore.api.player.PlayerData;
+// TODO rename to non-permanent
+// TODO merge with MythicLib
public interface Removable {
public void remove(PlayerData playerData);
}
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java
index 5966f2d6..6f7aaeb8 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java
@@ -382,7 +382,7 @@ public class SkillList extends EditableInventory {
super(playerData, editable);
skills = playerData.getProfess().getSkills()
.stream()
- .filter((classSkill) -> playerData.hasUnlocked(classSkill))
+ .filter(skill -> playerData.hasUnlocked(skill))
.sorted(Comparator.comparingInt(ClassSkill::getUnlockLevel))
.collect(Collectors.toList());
skillSlots = getEditable().getByFunction("skill").getSlots();
diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/sql/MMOCoreDataSynchronizer.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/sql/MMOCoreDataSynchronizer.java
index 99d2f774..bd8eaff0 100644
--- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/sql/MMOCoreDataSynchronizer.java
+++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/sql/MMOCoreDataSynchronizer.java
@@ -16,7 +16,6 @@ import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import org.apache.commons.lang.Validate;
-import org.bukkit.attribute.Attribute;
import org.jetbrains.annotations.Nullable;
import java.sql.ResultSet;
@@ -68,7 +67,6 @@ public class MMOCoreDataSynchronizer extends SQLDataSynchronizer