Fixed /rpg reload increasing player skill levels when using skill trees

This commit is contained in:
Jules 2024-04-06 23:15:33 -07:00
parent 3231a69bf3
commit d4bea3dba7
11 changed files with 67 additions and 64 deletions

View File

@ -24,6 +24,7 @@ import net.Indyuce.mmocore.api.player.stats.PlayerStats;
import net.Indyuce.mmocore.api.quest.PlayerQuests; import net.Indyuce.mmocore.api.quest.PlayerQuests;
import net.Indyuce.mmocore.api.quest.trigger.StatTrigger; import net.Indyuce.mmocore.api.quest.trigger.StatTrigger;
import net.Indyuce.mmocore.api.quest.trigger.Trigger; import net.Indyuce.mmocore.api.quest.trigger.Trigger;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.experience.*; import net.Indyuce.mmocore.experience.*;
import net.Indyuce.mmocore.experience.droptable.ExperienceItem; import net.Indyuce.mmocore.experience.droptable.ExperienceItem;
@ -173,7 +174,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
if (!nodeLevels.containsKey(node)) nodeLevels.put(node, 0); if (!nodeLevels.containsKey(node)) nodeLevels.put(node, 0);
setupSkillTree(); setupSkillTree();
updateTemporaryTriggers(); applyTemporaryTriggers();
getStats().updateStats(); getStats().updateStats();
} }
@ -184,7 +185,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
@Override @Override
public void markAsSynchronized() { public void markAsSynchronized() {
setupSkillTree(); setupSkillTree();
updateTemporaryTriggers(); applyTemporaryTriggers();
getStats().updateStats(true); getStats().updateStats(true);
/* /*
@ -203,25 +204,19 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
@Deprecated @Deprecated
public void setupRemovableTrigger() { public void setupRemovableTrigger() {
updateTemporaryTriggers(); applyTemporaryTriggers();
} }
/** /**
* Some triggers are marked with the Removable interface as * Some triggers are marked with the {@link Removable} interface as
* they are non-permanent triggers and they need to be updated * they are non-permanent triggers, and they need to be re-applied
* everytime their MMOPlayerData gets flushed from ML cache. * everytime their MMOPlayerData gets flushed from the MythicLib cache
* (everytime the player logs out).
* <p> * <p>
* This method should go through ALL {@link ExperienceTable} * This method goes through all the player's experience tables that
* that the player has spent points into and register all * they have spent points into and register all their non-permanent triggers.
* non-permanent triggers.
* <p>
* 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() { public void applyTemporaryTriggers() {
// Remove all stats and buffs associated to triggers // Remove all stats and buffs associated to triggers
getMMOPlayerData().getStatMap().getInstances().forEach(statInstance -> statInstance.removeIf(Trigger.STAT_MODIFIER_KEY::equals)); getMMOPlayerData().getStatMap().getInstances().forEach(statInstance -> statInstance.removeIf(Trigger.STAT_MODIFIER_KEY::equals));
@ -229,17 +224,17 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Experience tables from main class // Experience tables from main class
if (getProfess().hasExperienceTable()) if (getProfess().hasExperienceTable())
getProfess().getExperienceTable().claimRemovableTrigger(this, getProfess()); getProfess().getExperienceTable().applyTemporaryTriggers(this, getProfess());
// Experience tables from professions // Experience tables from professions
for (Profession profession : MMOCore.plugin.professionManager.getAll()) for (Profession profession : MMOCore.plugin.professionManager.getAll())
if (profession.hasExperienceTable()) if (profession.hasExperienceTable())
profession.getExperienceTable().claimRemovableTrigger(this, profession); profession.getExperienceTable().applyTemporaryTriggers(this, profession);
// Experience tables from skill tree nodes // Experience tables from skill tree nodes
for (SkillTree skillTree : MMOCore.plugin.skillTreeManager.getAll()) for (SkillTree skillTree : MMOCore.plugin.skillTreeManager.getAll())
for (SkillTreeNode node : skillTree.getNodes()) for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().claimRemovableTrigger(this, node); node.getExperienceTable().applyTemporaryTriggers(this, node);
} }
public void setupSkillTree() { public void setupSkillTree() {
@ -383,7 +378,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
public void resetSkillTree(SkillTree skillTree) { public void resetSkillTree(SkillTree skillTree) {
for (SkillTreeNode node : skillTree.getNodes()) { for (SkillTreeNode node : skillTree.getNodes()) {
node.getExperienceTable().reset(this, node); node.getExperienceTable().unclaim(this, node, true);
setNodeLevel(node, 0); setNodeLevel(node, 0);
nodeStates.remove(node); nodeStates.remove(node);
} }
@ -1216,7 +1211,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Clear bound skills // Clear bound skills
boundSkills.forEach((slot, info) -> info.close()); boundSkills.forEach((slot, info) -> info.close());
boundSkills.clear(); boundSkills.clear();
updateTemporaryTriggers(); applyTemporaryTriggers();
// Update stats // Update stats
if (isOnline()) getStats().updateStats(); if (isOnline()) getStats().updateStats();

View File

@ -297,9 +297,9 @@ public class SavedClassInformation implements ClassDataContainer {
// Remove perm stats for nodes and class // Remove perm stats for nodes and class
for (SkillTree skillTree : player.getProfess().getSkillTrees()) for (SkillTree skillTree : player.getProfess().getSkillTrees())
for (SkillTreeNode node : skillTree.getNodes()) for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().removePermStats(player, node); node.getExperienceTable().unclaim(player, node, false);
if (player.getProfess().hasExperienceTable()) if (player.getProfess().hasExperienceTable())
player.getProfess().getExperienceTable().removePermStats(player, player.getProfess()); player.getProfess().getExperienceTable().unclaim(player, player.getProfess(), false);
/* /*
* Resets information which much be reset after everything is saved. * Resets information which much be reset after everything is saved.
@ -356,7 +356,7 @@ public class SavedClassInformation implements ClassDataContainer {
player.setMana(mana); player.setMana(mana);
player.setStellium(stellium); player.setStellium(stellium);
player.setStamina(stamina); player.setStamina(stamina);
player.updateTemporaryTriggers(); player.applyTemporaryTriggers();
player.getStats().updateStats(); player.getStats().updateStats();
} }
} }

View File

@ -8,12 +8,13 @@ import io.lumine.mythic.lib.skill.handler.SkillHandler;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable; import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.quest.trigger.api.Temporary;
import net.Indyuce.mmocore.skill.RegisteredSkill; import net.Indyuce.mmocore.skill.RegisteredSkill;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SkillModifierTrigger extends Trigger implements Removable { public class SkillModifierTrigger extends Trigger implements Removable, Temporary {
private final SkillModifier mod; private final SkillModifier mod;
private final double amount; private final double amount;

View File

@ -5,9 +5,10 @@ import io.lumine.mythic.lib.api.stat.modifier.StatModifier;
import io.lumine.mythic.lib.player.modifier.ModifierType; import io.lumine.mythic.lib.player.modifier.ModifierType;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable; import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.quest.trigger.api.Temporary;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
public class StatTrigger extends Trigger implements Removable { public class StatTrigger extends Trigger implements Removable, Temporary {
private final StatModifier modifier; private final StatModifier modifier;
private final String stat; private final String stat;
private final double amount; private final double amount;

View File

@ -2,8 +2,12 @@ package net.Indyuce.mmocore.api.quest.trigger.api;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
// TODO rename to non-permanent /**
// TODO merge with MythicLib * Cancelable triggers cause problems when letting the player reset
* their advancement on things they can spend points in/level up.
* If you give access to some resource to the player via a trigger,
* you must take it away when resetting their progression.
*/
public interface Removable { public interface Removable {
public void remove(PlayerData playerData); public void remove(PlayerData playerData);
} }

View File

@ -0,0 +1,12 @@
package net.Indyuce.mmocore.api.quest.trigger.api;
/**
* Non-permanent triggers are triggers which are not saved
* by the player and taken off when the player logs off,
* for instance temporary player modifiers. They need to
* be re-applied everytime the player logs back.
*
* @author jules
*/
public interface Temporary {
}

View File

@ -250,7 +250,7 @@ public class ResetCommandTreeNode extends CommandTreeNode {
for (Profession profession : MMOCore.plugin.professionManager.getAll()) { for (Profession profession : MMOCore.plugin.professionManager.getAll()) {
data.getCollectionSkills().setExperience(profession, 0); data.getCollectionSkills().setExperience(profession, 0);
data.getCollectionSkills().setLevel(profession, 0); data.getCollectionSkills().setLevel(profession, 0);
profession.getExperienceTable().reset(data, profession); profession.getExperienceTable().unclaim(data, profession, true);
} }
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,

View File

@ -68,7 +68,6 @@ public class SkillCommandTreeNode extends CommandTreeNode {
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
int amount; int amount;
try { try {
amount = Integer.parseInt(args[5]); amount = Integer.parseInt(args[5]);

View File

@ -3,9 +3,9 @@ package net.Indyuce.mmocore.experience.droptable;
import io.lumine.mythic.lib.api.MMOLineConfig; import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.StatTrigger;
import net.Indyuce.mmocore.api.quest.trigger.Trigger; import net.Indyuce.mmocore.api.quest.trigger.Trigger;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable; import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.quest.trigger.api.Temporary;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
@ -132,12 +132,11 @@ public class ExperienceItem {
} }
/** /**
* Used when a player connects back to give back all the stats that he should have. * Used when a player logs back, in order to apply again
* * all the temporary triggers.
* @param playerData
*/ */
public void applyRemovableTrigger(PlayerData playerData) { public void applyTemporaryTriggers(PlayerData playerData) {
for (Trigger trigger : triggers) for (Trigger trigger : triggers)
if (trigger instanceof Removable) trigger.apply(playerData); if (trigger instanceof Temporary) trigger.apply(playerData);
} }
} }

View File

@ -55,42 +55,30 @@ public class ExperienceTable {
} }
/** /**
* Called when a player changes its class. * Called for example when a player changes its class. This takes
* Removes the perm stat but keeps the item claims in memory. * off triggers which can be canceled.
*/ */
public void removePermStats(PlayerData playerData, ExperienceObject object) { public void unclaim(PlayerData playerData, ExperienceObject object, boolean reset) {
for (ExperienceItem item : items) { for (ExperienceItem item : items) {
int timesClaimed = playerData.getClaims(object, this, item); final int timesClaimed = playerData.getClaims(object, this, item);
if (reset) playerData.setClaims(object, this, item, 0);
for (int i = 0; i < timesClaimed; i++) for (int i = 0; i < timesClaimed; i++)
item.removeTriggers(playerData); item.removeTriggers(playerData);
} }
} }
/** /**
* Called when the progression is reset(e.g skill tree reallocation) * Called when a player joins. All non-permanent/temporary triggers
*/ * must be granted back to the player, including player modifiers.
public void reset(PlayerData playerData, ExperienceObject object) {
for (ExperienceItem item : items) {
int timesClaimed = playerData.getClaims(object, this, item);
playerData.setClaims(object, this, item, 0);
for (int i = 0; i < timesClaimed; i++)
item.removeTriggers(playerData);
}
}
/**
* Called when a player joins and all the removable triggers get claimed back.
* *
* @param data PlayerData * @param data PlayerData
* @param object Either profession, skillTreeNode or class leveling up * @param object Either profession, skillTreeNode or class leveling up
*/ */
public void claimRemovableTrigger(PlayerData data, ExperienceObject object) { public void applyTemporaryTriggers(PlayerData data, ExperienceObject object) {
for (ExperienceItem item : items) { for (ExperienceItem item : items) {
int timesClaimed = data.getClaims(object, this, item); int timesClaimed = data.getClaims(object, this, item);
for (int i = 0; i < timesClaimed; i++) for (int i = 0; i < timesClaimed; i++)
item.applyRemovableTrigger(data); item.applyTemporaryTriggers(data);
} }
} }
} }

View File

@ -3,19 +3,23 @@ package net.Indyuce.mmocore.player;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
/** /**
* Some item that can be unlocked. All unlockables are saved in the * Some object that can be unlocked. All unlockables are saved in the
* same list in the player data. This useful list can be used for: * same list in the player data, in the form of name-spaced keys. This
* tool is currently by:
* - skills
* - skill slots
* - waypoints * - waypoints
* - skill tree nodes * <p>
* - skills using skill books? * These objects are specific to the player's class and will not be
* - external plugins that implement other unlockable items * transferred over to the new class if the player switches classes.
* *
* @see {@link PlayerData#unlock(Unlockable)} and {@link PlayerData#hasUnlocked(Unlockable)} * @see PlayerData#unlock(Unlockable)
* @see PlayerData#hasUnlocked(Unlockable)
*/ */
public interface Unlockable { public interface Unlockable {
/** /**
* Format being used is the minecraft's default namespaced * Format being used is the minecraft's default name-spaced
* key format, e.g "skill_tree:strength_1_5" for readability * key format, e.g "skill_tree:strength_1_5" for readability
*/ */
String getUnlockNamespacedKey(); String getUnlockNamespacedKey();