Fixes points spent in skill tree not updating when unlocking a node.

This commit is contained in:
Jules 2024-08-14 11:20:26 -07:00
parent 62e1ace7b5
commit cba1631d44
3 changed files with 126 additions and 63 deletions

View File

@ -45,6 +45,7 @@ import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import net.Indyuce.mmocore.skill.binding.SkillSlot; import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.skill.cast.SkillCastingInstance; import net.Indyuce.mmocore.skill.cast.SkillCastingInstance;
import net.Indyuce.mmocore.skill.cast.SkillCastingMode; import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
import net.Indyuce.mmocore.skilltree.NodeIncrementResult;
import net.Indyuce.mmocore.skilltree.SkillTreeNode; import net.Indyuce.mmocore.skilltree.SkillTreeNode;
import net.Indyuce.mmocore.skilltree.SkillTreeStatus; import net.Indyuce.mmocore.skilltree.SkillTreeStatus;
import net.Indyuce.mmocore.skilltree.tree.SkillTree; import net.Indyuce.mmocore.skilltree.tree.SkillTree;
@ -63,6 +64,7 @@ import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -107,17 +109,13 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
private final Map<PlayerActivity, Long> lastActivity = new HashMap<>(); private final Map<PlayerActivity, Long> lastActivity = new HashMap<>();
private final CombatHandler combat = new CombatHandler(this); private final CombatHandler combat = new CombatHandler(this);
/**
* Cached for easier access. Amount of points spent in each skill tree.
*/
private final Map<SkillTree, Integer> pointSpent = new HashMap<>();
/** /**
* Cached for easier access. Current status of each skill tree node. * Cached for easier access. Current status of each skill tree node.
*/ */
private final Map<SkillTreeNode, SkillTreeStatus> nodeStates = new HashMap<>(); private final Map<SkillTreeNode, SkillTreeStatus> nodeStates = new HashMap<>();
private final Map<SkillTreeNode, Integer> nodeLevels = new HashMap<>(); private final Map<SkillTreeNode, Integer> nodeLevels = new HashMap<>();
private final Map<String, Integer> skillTreePoints = new HashMap<>(); private final Map<String, Integer> skillTreePoints = new HashMap<>();
private final Map<SkillTree, Integer> skillTreePointsSpent = new HashMap<>();
/** /**
* Saves the namespacedkeys of the items that have been unlocked in the form "namespace:key". * Saves the namespacedkeys of the items that have been unlocked in the form "namespace:key".
@ -224,19 +222,24 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
skillTree.setupNodeStates(this); skillTree.setupNodeStates(this);
} }
public int getPointSpent(SkillTree skillTree) { public int getPointsSpent(@NotNull SkillTree skillTree) {
return pointSpent.getOrDefault(skillTree, 0); return skillTreePointsSpent.getOrDefault(skillTree, 0);
} }
public void setSkillTreePoints(String treeId, int points) { @Deprecated
public int getPointSpent(SkillTree skillTree) {
return getPointsSpent(skillTree);
}
public void setSkillTreePoints(@NotNull String treeId, int points) {
skillTreePoints.put(treeId, points); skillTreePoints.put(treeId, points);
} }
public void giveSkillTreePoints(String id, int val) { public void giveSkillTreePoints(@NotNull String id, int val) {
skillTreePoints.put(id, skillTreePoints.getOrDefault(id, 0) + val); skillTreePoints.merge(id, val, (points, ignored) -> points + val);
} }
public int countSkillTreePoints(SkillTree skillTree) { public int countSkillTreePoints(@NotNull SkillTree skillTree) {
return nodeLevels.keySet().stream().filter(node -> node.getTree().equals(skillTree)).mapToInt(node -> nodeLevels.get(node) * node.getSkillTreePointsConsumed()).sum(); return nodeLevels.keySet().stream().filter(node -> node.getTree().equals(skillTree)).mapToInt(node -> nodeLevels.get(node) * node.getSkillTreePointsConsumed()).sum();
} }
@ -244,6 +247,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* Make a copy to make sure that the object * Make a copy to make sure that the object
* created is independent of the state of playerData. * created is independent of the state of playerData.
*/ */
@NotNull
public Map<String, Integer> mapSkillTreePoints() { public Map<String, Integer> mapSkillTreePoints() {
return new HashMap(skillTreePoints); return new HashMap(skillTreePoints);
} }
@ -290,7 +294,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Node levels, states and points spent // Node levels, states and points spent
nodeLevels.clear(); nodeLevels.clear();
nodeStates.clear(); nodeStates.clear();
pointSpent.clear(); skillTreePointsSpent.clear();
// Skill tree points // Skill tree points
skillTreePoints.clear(); skillTreePoints.clear();
@ -299,11 +303,24 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
tableItemClaims.keySet().removeIf(s -> s.startsWith(SkillTreeNode.KEY_PREFIX)); tableItemClaims.keySet().removeIf(s -> s.startsWith(SkillTreeNode.KEY_PREFIX));
} }
public boolean canIncrementNodeLevel(@NotNull SkillTreeNode node) { @NotNull
SkillTreeStatus skillTreeStatus = nodeStates.get(node); public NodeIncrementResult canIncrementNodeLevel(@NotNull SkillTreeNode node) {
//Check the State of the node final SkillTreeStatus skillTreeStatus = nodeStates.get(node);
if (skillTreeStatus != SkillTreeStatus.UNLOCKED && skillTreeStatus != SkillTreeStatus.UNLOCKABLE) return false;
return node.hasPermissionRequirement(this) && getNodeLevel(node) < node.getMaxLevel() && (skillTreePoints.getOrDefault(node.getTree().getId(), 0) + skillTreePoints.getOrDefault("global", 0) >= node.getSkillTreePointsConsumed()); // Check node state
if (skillTreeStatus != SkillTreeStatus.UNLOCKED && skillTreeStatus != SkillTreeStatus.UNLOCKABLE)
return NodeIncrementResult.LOCKED_NODE;
// Check permission
if (!node.hasPermissionRequirement(this)) return NodeIncrementResult.PERMISSION_DENIED;
// Max node level
if (getNodeLevel(node) >= node.getMaxLevel()) return NodeIncrementResult.MAX_LEVEL_REACHED;
final int skillTreePoints = this.skillTreePoints.getOrDefault(node.getTree().getId(), 0) + this.skillTreePoints.getOrDefault("global", 0);
if (skillTreePoints < node.getSkillTreePointsConsumed()) return NodeIncrementResult.NOT_ENOUGH_POINTS;
return NodeIncrementResult.SUCCESS;
} }
/** /**
@ -312,19 +329,22 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* global skill-tree points ('all') * global skill-tree points ('all')
*/ */
public void incrementNodeLevel(@NotNull SkillTreeNode node) { public void incrementNodeLevel(@NotNull SkillTreeNode node) {
final int newLevel = nodeLevels.merge(node, 1, (level, ignored) -> level + 1); final int newLevel = addNodeLevels(node, 1);
node.updateAdvancement(this, newLevel); // Claim the node exp table node.updateAdvancement(this, newLevel); // Claim the node exp table
if (nodeStates.get(node) == SkillTreeStatus.UNLOCKABLE) setNodeState(node, SkillTreeStatus.UNLOCKED); // Update node state
int cost = node.getSkillTreePointsConsumed(); nodeStates.compute(node, (key, status) -> status == SkillTreeStatus.UNLOCKABLE ? SkillTreeStatus.UNLOCKED : status);
final int skillTreeSpecificPoints = skillTreePoints.getOrDefault(node.getTree().getId(), 0);
if (skillTreeSpecificPoints > 0) { // Consume skill tree points
int pointWithdrawn = Math.min(cost, skillTreeSpecificPoints); final AtomicInteger cost = new AtomicInteger(node.getSkillTreePointsConsumed());
withdrawSkillTreePoints(node.getTree().getId(), pointWithdrawn); skillTreePoints.computeIfPresent(node.getTree().getId(), (key, points) -> {
cost -= pointWithdrawn; final int withdrawn = Math.min(points, cost.get());
} cost.set(cost.get() - withdrawn);
if (cost > 0) withdrawSkillTreePoints("global", cost); return points == withdrawn ? null : points - withdrawn;
// Unload the nodeStates map (for the skill tree) and reload it completely });
if (cost.get() > 0) withdrawSkillTreePoints("global", cost.get());
// Reload node states from full skill tree
for (SkillTreeNode node1 : node.getTree().getNodes()) nodeStates.remove(node1); for (SkillTreeNode node1 : node.getTree().getNodes()) nodeStates.remove(node1);
node.getTree().setupNodeStates(this); node.getTree().setupNodeStates(this);
} }
@ -359,10 +379,19 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return nodeLevels.getOrDefault(node, 0); return nodeLevels.getOrDefault(node, 0);
} }
public void setNodeLevel(SkillTreeNode node, int nodeLevel) { public void setNodeLevel(@NotNull SkillTreeNode node, int nodeLevel) {
int delta = (nodeLevel - nodeLevels.getOrDefault(node, 0)) * node.getSkillTreePointsConsumed(); nodeLevels.compute(node, (ignored, currentLevelInteger) -> {
pointSpent.put(node.getTree(), pointSpent.getOrDefault(node.getTree(), 0) + delta); final int currentLevel = currentLevelInteger == null ? 0 : currentLevelInteger;
nodeLevels.put(node, nodeLevel); final int delta = (nodeLevel - currentLevel) * node.getSkillTreePointsConsumed();
skillTreePointsSpent.merge(node.getTree(), delta, (level, ignored2) -> level + delta);
return nodeLevel;
});
}
public int addNodeLevels(@NotNull SkillTreeNode node, int increment) {
final int delta = increment * node.getSkillTreePointsConsumed();
skillTreePointsSpent.merge(node.getTree(), delta, (points, ignored) -> points + delta);
return nodeLevels.merge(node, increment, (level, ignored) -> level + increment);
} }
public void resetSkillTree(SkillTree skillTree) { public void resetSkillTree(SkillTree skillTree) {

View File

@ -108,7 +108,7 @@ public class SkillTreeViewer extends EditableInventory {
holders.register("realloc-points", inv.getPlayerData().getSkillTreeReallocationPoints()); holders.register("realloc-points", inv.getPlayerData().getSkillTreeReallocationPoints());
int maxPointSpent = inv.getSkillTree().getMaxPointSpent(); int maxPointSpent = inv.getSkillTree().getMaxPointSpent();
holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent); holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent);
holders.register("point-spent", inv.getPlayerData().getPointSpent(inv.getSkillTree())); holders.register("point-spent", inv.getPlayerData().getPointsSpent(inv.getSkillTree()));
return holders; return holders;
} }
@ -184,7 +184,7 @@ public class SkillTreeViewer extends EditableInventory {
holders.register("id", skillTree.getId()); holders.register("id", skillTree.getId());
int maxPointSpent = inv.getSkillTree().getMaxPointSpent(); int maxPointSpent = inv.getSkillTree().getMaxPointSpent();
holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent); holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent);
holders.register("point-spent", inv.getPlayerData().getPointSpent(inv.getSkillTree())); holders.register("point-spent", inv.getPlayerData().getPointsSpent(inv.getSkillTree()));
holders.register("skill-tree-points", inv.getPlayerData().getSkillTreePoints(inv.getSkillTree().getId())); holders.register("skill-tree-points", inv.getPlayerData().getSkillTreePoints(inv.getSkillTree().getId()));
holders.register("global-points", inv.getPlayerData().getSkillTreePoints("global")); holders.register("global-points", inv.getPlayerData().getSkillTreePoints("global"));
return holders; return holders;
@ -315,7 +315,7 @@ public class SkillTreeViewer extends EditableInventory {
} }
int maxPointSpent = inv.getSkillTree().getMaxPointSpent(); int maxPointSpent = inv.getSkillTree().getMaxPointSpent();
holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent); holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent);
holders.register("point-spent", inv.getPlayerData().getPointSpent(inv.getSkillTree())); holders.register("point-spent", inv.getPlayerData().getPointsSpent(inv.getSkillTree()));
holders.register("skill-tree-points", inv.getPlayerData().getSkillTreePoints(inv.getSkillTree().getId())); holders.register("skill-tree-points", inv.getPlayerData().getSkillTreePoints(inv.getSkillTree().getId()));
holders.register("global-points", inv.getPlayerData().getSkillTreePoints("global")); holders.register("global-points", inv.getPlayerData().getSkillTreePoints("global"));
@ -466,7 +466,7 @@ public class SkillTreeViewer extends EditableInventory {
open(); open();
} }
if (item.getFunction().equals("reallocation")) { if (item.getFunction().equals("reallocation")) {
int spent = playerData.getPointSpent(skillTree); int spent = playerData.getPointsSpent(skillTree);
if (spent < 1) { if (spent < 1) {
ConfigMessage.fromKey("no-skill-tree-points-spent").send(player); ConfigMessage.fromKey("no-skill-tree-points-spent").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
@ -478,7 +478,7 @@ public class SkillTreeViewer extends EditableInventory {
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
return; return;
} else { } else {
int reallocated = playerData.getPointSpent(skillTree); int reallocated = playerData.getPointsSpent(skillTree);
//We remove all the nodeStates progress //We remove all the nodeStates progress
playerData.giveSkillTreePoints(skillTree.getId(), reallocated); playerData.giveSkillTreePoints(skillTree.getId(), reallocated);
playerData.giveSkillTreeReallocationPoints(-1); playerData.giveSkillTreeReallocationPoints(-1);
@ -501,47 +501,55 @@ public class SkillTreeViewer extends EditableInventory {
return; return;
} }
if (item.getFunction().equals("skill-tree-node")) { if (item.getFunction().equals("skill-tree-node") && event.getClickType() == ClickType.LEFT) {
if (event.getClickType() == ClickType.LEFT) {
PersistentDataContainer container = event.getClickedItem().getItemMeta().getPersistentDataContainer(); final PersistentDataContainer container = event.getClickedItem().getItemMeta().getPersistentDataContainer();
int x = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.x"), PersistentDataType.INTEGER); final int x = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.x"), PersistentDataType.INTEGER);
int y = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.y"), PersistentDataType.INTEGER); final int y = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.y"), PersistentDataType.INTEGER);
if (!skillTree.isNode(new IntegerCoordinates(x, y))) { if (!skillTree.isNode(new IntegerCoordinates(x, y))) return;
return;
} // Maximum amount of skill points spent in node
SkillTreeNode node = skillTree.getNode(new IntegerCoordinates(x, y)); final SkillTreeNode node = skillTree.getNode(new IntegerCoordinates(x, y));
if (playerData.getPointSpent(skillTree) >= skillTree.getMaxPointSpent()) { if (playerData.getPointsSpent(skillTree) >= skillTree.getMaxPointSpent()) {
ConfigMessage.fromKey("max-points-reached").send(player); ConfigMessage.fromKey("max-points-reached").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
return; return;
} }
if (playerData.canIncrementNodeLevel(node)) { switch (playerData.canIncrementNodeLevel(node)) {
case SUCCESS: {
playerData.incrementNodeLevel(node); playerData.incrementNodeLevel(node);
ConfigMessage.fromKey("upgrade-skill-node", "skill-node", node.getName(), "level", "" + playerData.getNodeLevel(node)).send(player); ConfigMessage.fromKey("upgrade-skill-node", "skill-node", node.getName(), "level", "" + playerData.getNodeLevel(node)).send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_SKILL_TREE_NODE).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_SKILL_TREE_NODE).playTo(getPlayer());
open(); open();
} else if (playerData.getNodeStatus(node) == SkillTreeStatus.LOCKED || playerData.getNodeStatus(node) == SkillTreeStatus.FULLY_LOCKED) { break;
ConfigMessage.fromKey("locked-node").send(player); }
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
} else if (playerData.getNodeLevel(node) >= node.getMaxLevel()) { case PERMISSION_DENIED: {
ConfigMessage.fromKey("skill-node-max-level-hit").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
} else if (!node.hasPermissionRequirement(playerData)) {
ConfigMessage.fromKey("missing-skill-node-permission").send(player); ConfigMessage.fromKey("missing-skill-node-permission").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
break;
} }
//Else the player doesn't doesn't have the skill tree points case LOCKED_NODE: {
else { ConfigMessage.fromKey("locked-node").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
break;
}
case MAX_LEVEL_REACHED: {
ConfigMessage.fromKey("skill-node-max-level-hit").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
break;
}
case NOT_ENOUGH_POINTS: {
ConfigMessage.fromKey("not-enough-skill-tree-points", "point", "" + node.getSkillTreePointsConsumed()).send(player); ConfigMessage.fromKey("not-enough-skill-tree-points", "point", "" + node.getSkillTreePointsConsumed()).send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
} break;
}
} }
} }
} }
} }
} }

View File

@ -0,0 +1,26 @@
package net.Indyuce.mmocore.skilltree;
public enum NodeIncrementResult {
SUCCESS,
/**
* Node is still locked/not unlockable
*/
LOCKED_NODE,
/**
* Player does not have required permission
*/
PERMISSION_DENIED,
/**
* Maximum level of node is reached
*/
MAX_LEVEL_REACHED,
/**
* Player does not have enough skill tree points
*/
NOT_ENOUGH_POINTS,
}