Fixed login trigger type not working on class skills & scripts

This commit is contained in:
Jules 2023-11-27 00:11:12 +01:00
parent 947637127e
commit 47cd5c15c9
8 changed files with 109 additions and 57 deletions

View File

@ -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.
* <p>
* This method should go through ALL {@link ExperienceTable}
* that the player has spent points into and register all
* 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() {
// 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<ClassSkill> getUnlockedSkills() {
return getProfess().getSkills().stream()
.filter((classSkill) -> hasUnlocked(classSkill))
.filter(skill -> hasUnlocked(skill) && hasUnlockedLevel(skill))
.collect(Collectors.toList());
}
@ -567,9 +615,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
public void setLevel(int level) {
this.level = Math.max(1, level);
if (isOnline()) {
getStats().updateStats();
if (isSynchronized()) getStats().updateStats();
refreshVanillaExp();
}
}
@ -1018,7 +1065,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
/**
* @return If the PlayerEnterCastingModeEvent successfully put the player
* into casting mode, otherwise if the event is cancelled, returns false.
* into casting mode, otherwise if the event is cancelled, returns false.
*/
public boolean setSkillCasting() {
Validate.isTrue(!isCasting(), "Player already in casting mode");
@ -1037,7 +1084,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
/**
* @return If player successfully left skill casting i.e the Bukkit
* event has not been cancelled
* event has not been cancelled
*/
public boolean leaveSkillCasting() {
return leaveSkillCasting(false);
@ -1046,7 +1093,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
/**
* @param skipEvent Skip firing the exit event
* @return If player successfully left skill casting i.e the Bukkit
* event has not been cancelled
* event has not been cancelled
*/
public boolean leaveSkillCasting(boolean skipEvent) {
Validate.isTrue(isCasting(), "Player not in casting mode");
@ -1166,10 +1213,10 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Clear bound skills
boundSkills.forEach((slot, info) -> info.close());
boundSkills.clear();
setupRemovableTrigger();
updateTemporaryTriggers();
// Update stats
if (isOnline()) getStats().updateStats();
if (isOnline() && isSynchronized()) getStats().updateStats();
}
public boolean hasSkillBound(int slot) {
@ -1225,7 +1272,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* checks if they could potentially upgrade to one of these
*
* @return If the player can change its current class to
* a subclass
* a subclass
*/
@Deprecated
public boolean canChooseSubclass() {

View File

@ -352,12 +352,12 @@ public class SavedClassInformation implements ClassDataContainer {
// This needs to be done at the end to make sure the MAX_HEALTH/MAX_MANA/... stats are loaded.
player.getPlayer().setHealth(MMOCoreUtils.fixResource(health, player.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
player.setHealth(health);
player.setMana(mana);
player.setStellium(stellium);
player.setStamina(stamina);
player.setupRemovableTrigger();
// Updates level on exp bar
player.refreshVanillaExp();
player.updateTemporaryTriggers();
player.getStats().updateStats();
}
}

View File

@ -8,10 +8,12 @@ import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.modifier.ModifierType;
import io.lumine.mythic.lib.player.skill.PassiveSkill;
import io.lumine.mythic.lib.player.skill.PassiveSkillMap;
import io.lumine.mythic.lib.skill.trigger.TriggerType;
import net.Indyuce.mmocore.MMOCore;
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;
public class PlayerStats {
private final PlayerData data;
@ -65,14 +67,22 @@ public class PlayerStats {
return data.getProfess().calculateStat(stat, profession == null ? data.getLevel() : data.getCollectionSkills().getLevel(profession));
}
public void updateStats() {
updateStats(false);
}
/**
* Used to update MMOCore stat modifiers due to class and send them over to
* MythicLib. Must be ran everytime the player levels up or changes class.
* MythicLib. Must be ran everytime the player levels up, changes class or
* when the plugin reloads.
* <p>
* 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());
}
}
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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<PlayerData> {
getData().setNodeLevel(skillTreeNode, json.has(skillTreeNode.getFullId()) ? json.get(skillTreeNode.getFullId()).getAsInt() : 0);
}
}
getData().setupSkillTree();
Set<String> unlockedItems = new HashSet<>();
if (!isEmpty(result.getString("unlocked_items"))) {
JsonArray unlockedItemsArray = MythicLib.plugin.getGson().fromJson(result.getString("unlocked_items"), JsonArray.class);
@ -101,7 +99,6 @@ public class MMOCoreDataSynchronizer extends SQLDataSynchronizer<PlayerData> {
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
ClassSkill skill = getData().getProfess().getSkill(entry.getValue().getAsString());
if (skill != null) getData().bindSkill(Integer.parseInt(entry.getKey()), skill);
}
}
if (!isEmpty(result.getString("class_info"))) {
@ -121,20 +118,10 @@ public class MMOCoreDataSynchronizer extends SQLDataSynchronizer<PlayerData> {
* These should be loaded after to make sure that the
* MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded.
*/
getData().setHealth(result.getDouble("health"));
getData().setMana(result.getDouble("mana"));
getData().setStamina(result.getDouble("stamina"));
getData().setStellium(result.getDouble("stellium"));
getData().setupRemovableTrigger();
if (getData().isOnline() && !getData().getPlayer().isDead()) {
/*
* If the player is not dead and the health is 0, this means that the data was
* missing from the data base and it gives full health to the player. If the
* player is dead however, it must not account for that subtle edge case.
*/
final double health = MMOCoreUtils.fixResource(result.getDouble("health"), getData().getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
getData().getPlayer().setHealth(health);
}
UtilityMethods.debug(MMOCore.plugin, "SQL", String.format("{ class: %s, level: %d }", getData().getProfess().getId(), getData().getLevel()));
}

View File

@ -110,20 +110,15 @@ public class YAMLPlayerDataHandler extends YAMLSynchronizedDataHandler<PlayerDat
for (SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) {
data.setNodeLevel(node, config.getInt("skill-tree-level." + node.getFullId(), 0));
}
data.setupSkillTree();
// Load class slots, use try so the player can log in.
if (config.contains("class-info"))
for (
String key : config.getConfigurationSection("class-info").
getKeys(false))
for (String key : config.getConfigurationSection("class-info").getKeys(false))
try {
PlayerClass profess = MMOCore.plugin.classManager.get(key);
Validate.notNull(profess, "Could not find class '" + key + "'");
data.applyClassInfo(profess, new SavedClassInformation(config.getConfigurationSection("class-info." + key)));
} catch (
IllegalArgumentException exception) {
} catch (IllegalArgumentException exception) {
MMOCore.log(Level.WARNING, "Could not load class info '" + key + "': " + exception.getMessage());
}
@ -135,9 +130,6 @@ public class YAMLPlayerDataHandler extends YAMLSynchronizedDataHandler<PlayerDat
data.setMana(config.contains("mana") ? config.getDouble("mana") : data.getStats().getStat("MAX_MANA"));
data.setStamina(config.contains("stamina") ? config.getDouble("stamina") : data.getStats().getStat("MAX_STAMINA"));
data.setStellium(config.contains("stellium") ? config.getDouble("stellium") : data.getStats().getStat("MAX_STELLIUM"));
data.setupRemovableTrigger();
if (data.isOnline() && !data.getPlayer().isDead())
data.getPlayer().setHealth(MMOCoreUtils.fixResource(config.getDouble("health"), data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
}
@Override

View File

@ -33,7 +33,7 @@ public class BoundSkillInfo implements Closeable {
if (skillModifierTrigger.getTargetSkills().contains(classSkill.getSkill().getHandler()))
skillModifierTrigger.apply(playerData, classSkill.getSkill().getHandler());
if (classSkill.getSkill().getTrigger().isPassive()&& classSkill.needsBound()) {
if (classSkill.getSkill().getTrigger().isPassive() && classSkill.needsBound()) {
registered = classSkill.toPassive(playerData);
registered.register(playerData.getMMOPlayerData());
} else registered = null;