mirror of
https://gitlab.com/phoenix-dvpmt/mmocore.git
synced 2025-11-18 06:24:17 +01:00
Option to change icon for maxed out skill tree nodes
This commit is contained in:
parent
7e6946097b
commit
51d2ee5508
@ -324,18 +324,19 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
|
||||
@NotNull
|
||||
public NodeIncrementResult canIncrementNodeLevel(@NotNull SkillTreeNode node) {
|
||||
final NodeState nodeState = nodeStates.get(node);
|
||||
final var nodeState = nodeStates.get(node);
|
||||
|
||||
// Check node state
|
||||
// Node is maxed out
|
||||
//if (getNodeLevel(node) >= node.getMaxLevel()) return NodeIncrementResult.MAX_LEVEL_REACHED;
|
||||
if (nodeState == NodeState.MAXED_OUT) return NodeIncrementResult.MAX_LEVEL_REACHED;
|
||||
|
||||
// Node is not ACCESSIBLE yet
|
||||
if (nodeState != NodeState.UNLOCKED && nodeState != NodeState.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.getPointConsumption()) return NodeIncrementResult.NOT_ENOUGH_POINTS;
|
||||
|
||||
@ -352,7 +353,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
node.updateAdvancement(this, newLevel); // Claim the node exp table
|
||||
|
||||
// Update node state
|
||||
nodeStates.compute(node, (key, status) -> status == NodeState.UNLOCKABLE ? NodeState.UNLOCKED : status);
|
||||
// TODO check/remove: USELESS LINE? since node states are updated afterwards
|
||||
// nodeStates.compute(node, (key, status) -> status == NodeState.UNLOCKABLE ? NodeState.UNLOCKED : status);
|
||||
|
||||
// Consume skill tree points
|
||||
final AtomicInteger cost = new AtomicInteger(node.getPointConsumption());
|
||||
|
||||
@ -446,7 +446,7 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
final int y = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.y"), PersistentDataType.INTEGER);
|
||||
if (!inv.skillTree.isNode(new IntegerCoordinates(x, y))) return;
|
||||
|
||||
// Maximum amount of skill points spent in node
|
||||
// Higher number of points spent in SKILL TREE (not node)
|
||||
final SkillTreeNode node = inv.skillTree.getNode(new IntegerCoordinates(x, y));
|
||||
if (inv.playerData.getPointsSpent(inv.skillTree) >= inv.skillTree.getMaxPointSpent()) {
|
||||
ConfigMessage.fromKey("max-points-reached").send(inv.playerData);
|
||||
@ -475,6 +475,7 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
break;
|
||||
}
|
||||
|
||||
// Max number of points spent in that NODE (not skill tree)
|
||||
case MAX_LEVEL_REACHED: {
|
||||
ConfigMessage.fromKey("skill-node-max-level-hit").send(inv.playerData);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(inv.getPlayer());
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
package net.Indyuce.mmocore.skilltree;
|
||||
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
|
||||
/**
|
||||
* State of one skill tree node, or path between nodes.
|
||||
*
|
||||
* @see PlayerData#getNodeState(SkillTreeNode)
|
||||
* @see SkillTreePath#getStatus(PlayerData)
|
||||
*/
|
||||
public enum NodeState {
|
||||
|
||||
/**
|
||||
@ -19,8 +27,20 @@ public enum NodeState {
|
||||
LOCKED,
|
||||
|
||||
/**
|
||||
* The player made a choice making it now impossible to
|
||||
* reach this node given its skill tree exploration.
|
||||
* No more skill points can be spent on the node since it has already
|
||||
* been maxed out. Technically it is a subtype of UNLOCKED since you can't
|
||||
* be MAXED_OUT without being UNLOCKED.
|
||||
*/
|
||||
MAXED_OUT,
|
||||
|
||||
/**
|
||||
* The player made a choice making it now impossible to reach this
|
||||
* node given its skill tree exploration. The player needs to
|
||||
* re-spec to unlock that node.
|
||||
*/
|
||||
FULLY_LOCKED;
|
||||
|
||||
public boolean isUnlocked() {
|
||||
return this == UNLOCKED || this == MAXED_OUT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,15 +16,25 @@ public class SkillTreePath {
|
||||
to = skillTreeNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the status of a path between two nodes, which is determined
|
||||
* by the pair of states of the two nodes.
|
||||
*/
|
||||
public NodeState getStatus(PlayerData playerData) {
|
||||
NodeState fromStatus = playerData.getNodeState(from);
|
||||
NodeState toStatus = playerData.getNodeState(to);
|
||||
if (fromStatus == NodeState.UNLOCKED && toStatus == NodeState.UNLOCKED)
|
||||
return NodeState.UNLOCKED;
|
||||
if ((fromStatus == NodeState.UNLOCKABLE && toStatus == NodeState.UNLOCKED) || (fromStatus == NodeState.UNLOCKED && toStatus == NodeState.UNLOCKABLE))
|
||||
var from = playerData.getNodeState(this.from);
|
||||
var to = playerData.getNodeState(this.to);
|
||||
|
||||
// Either one is fully locked => gray out path
|
||||
if (from == NodeState.FULLY_LOCKED || to == NodeState.FULLY_LOCKED) return NodeState.FULLY_LOCKED;
|
||||
|
||||
// Both are unlocked => path is taken, unlocked
|
||||
if (from.isUnlocked() && to.isUnlocked()) return NodeState.UNLOCKED;
|
||||
|
||||
// One of them is unlocked, other one is unlockable => path is not taken yet, but can be
|
||||
if ((from == NodeState.UNLOCKABLE && to.isUnlocked()) || (from.isUnlocked() && to == NodeState.UNLOCKABLE))
|
||||
return NodeState.UNLOCKABLE;
|
||||
if (fromStatus == NodeState.FULLY_LOCKED || toStatus == NodeState.FULLY_LOCKED)
|
||||
return NodeState.FULLY_LOCKED;
|
||||
|
||||
// Otherwise, locked path
|
||||
return NodeState.LOCKED;
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ import java.util.logging.Level;
|
||||
* - extra attribute pts
|
||||
* - particle or potion effects
|
||||
*
|
||||
* @author Ka0rX
|
||||
* @see SkillTreeNode
|
||||
*/
|
||||
public abstract class SkillTree implements RegisteredObject {
|
||||
@ -99,14 +98,22 @@ public abstract class SkillTree implements RegisteredObject {
|
||||
}
|
||||
|
||||
// Loads all the nodeDisplayInfo
|
||||
for (NodeState status : NodeState.values())
|
||||
for (NodeType nodeType : NodeType.values())
|
||||
for (var status : NodeState.values()) {
|
||||
final var anyType = config.get("display.nodes." + MMOCoreUtils.ymlName(status.name()));
|
||||
|
||||
// Does not depend on node type.
|
||||
if (anyType != null) for (var nodeType : NodeType.values())
|
||||
icons.put(new NodeDisplayInfo(nodeType, status), Icon.from(anyType));
|
||||
|
||||
// Depends on node type
|
||||
else for (var nodeType : NodeType.values())
|
||||
try {
|
||||
final String configPath = "display.nodes." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(nodeType.name());
|
||||
final var configPath = "display.nodes." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(nodeType.name());
|
||||
icons.put(new NodeDisplayInfo(nodeType, status), Icon.from(config.get(configPath)));
|
||||
} catch (Exception exception) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Setup children and parents for each node
|
||||
for (SkillTreeNode node : nodes.values())
|
||||
@ -229,6 +236,8 @@ public abstract class SkillTree implements RegisteredObject {
|
||||
// PASS 3
|
||||
//
|
||||
// Propagate unreachability in O(V * C * P)
|
||||
// Unreachability is transitive, if one node is unreachable, all subsequent
|
||||
// child nodes are all unreachable.
|
||||
while (!unreachable.empty()) {
|
||||
final SkillTreeNode node = unreachable.pop();
|
||||
|
||||
@ -276,6 +285,14 @@ public abstract class SkillTree implements RegisteredObject {
|
||||
// At least one soft parent!
|
||||
if (!hasSoft || soft) playerData.setNodeState(node, NodeState.UNLOCKABLE);
|
||||
}
|
||||
|
||||
// PASS 5
|
||||
//
|
||||
// I'm not sure this is the best place to do that but it works just fine.
|
||||
// Mark unlocked nodes as maxed out if maximum number of points spent
|
||||
for (var node : nodes.values())
|
||||
if (playerData.getNodeState(node) == NodeState.UNLOCKED && playerData.getNodeLevel(node) >= node.getMaxLevel())
|
||||
playerData.setNodeState(node, NodeState.MAXED_OUT);
|
||||
}
|
||||
|
||||
private boolean isUnreachable(@NotNull SkillTreeNode node, @NotNull PlayerData playerData) {
|
||||
|
||||
@ -487,6 +487,23 @@ nodes:
|
||||
# up: 'WHITE_CONCRETE:0'
|
||||
# down: 'WHITE_CONCRETE:0'
|
||||
# no-path: 'WHITE_CONCRETE:0'
|
||||
# maxed-out:
|
||||
# up-right-down-left: 'GREEN_CONCRETE:0'
|
||||
# up-right-down: 'GREEN_CONCRETE:0'
|
||||
# up-right-left: 'GREEN_CONCRETE:0'
|
||||
# up-down-left: 'GREEN_CONCRETE:0'
|
||||
# down-right-left: 'GREEN_CONCRETE:0'
|
||||
# up-right: 'GREEN_CONCRETE:0'
|
||||
# up-down: 'GREEN_CONCRETE:0'
|
||||
# up-left: 'GREEN_CONCRETE:0'
|
||||
# down-right: 'GREEN_CONCRETE:0'
|
||||
# down-left: 'GREEN_CONCRETE:0'
|
||||
# right-left: 'GREEN_CONCRETE:0'
|
||||
# right: 'GREEN_CONCRETE:0'
|
||||
# left: 'GREEN_CONCRETE:0'
|
||||
# up: 'GREEN_CONCRETE:0'
|
||||
# down: 'GREEN_CONCRETE:0'
|
||||
# no-path: 'GREEN_CONCRETE:0'
|
||||
# locked:
|
||||
# up-right-down-left: 'GRAY_CONCRETE:0'
|
||||
# up-right-down: 'GRAY_CONCRETE:0'
|
||||
@ -538,3 +555,16 @@ nodes:
|
||||
# up: 'BLACK_CONCRETE:0'
|
||||
# down: 'BLACK_CONCRETE:0'
|
||||
# no-path: 'BLACK_CONCRETE:0'
|
||||
|
||||
# The following syntax works too, it applies the same texture
|
||||
# no matter the neighboring paths.
|
||||
#
|
||||
#display:
|
||||
# paths:
|
||||
# .......
|
||||
# nodes:
|
||||
# unlocked: 'WHITE_CONCRETE:0'
|
||||
# maxed-out: 'GREEN_CONCRETE:0'
|
||||
# locked: 'GRAY_CONCRETE:0'
|
||||
# unlockable: 'BLUE_CONCRETE:0'
|
||||
# fully-locked: 'BLACK_CONCRETE:0'
|
||||
|
||||
@ -483,6 +483,23 @@ nodes:
|
||||
# up: 'WHITE_CONCRETE:0'
|
||||
# down: 'WHITE_CONCRETE:0'
|
||||
# no-path: 'WHITE_CONCRETE:0'
|
||||
# maxed-out:
|
||||
# up-right-down-left: 'GREEN_CONCRETE:0'
|
||||
# up-right-down: 'GREEN_CONCRETE:0'
|
||||
# up-right-left: 'GREEN_CONCRETE:0'
|
||||
# up-down-left: 'GREEN_CONCRETE:0'
|
||||
# down-right-left: 'GREEN_CONCRETE:0'
|
||||
# up-right: 'GREEN_CONCRETE:0'
|
||||
# up-down: 'GREEN_CONCRETE:0'
|
||||
# up-left: 'GREEN_CONCRETE:0'
|
||||
# down-right: 'GREEN_CONCRETE:0'
|
||||
# down-left: 'GREEN_CONCRETE:0'
|
||||
# right-left: 'GREEN_CONCRETE:0'
|
||||
# right: 'GREEN_CONCRETE:0'
|
||||
# left: 'GREEN_CONCRETE:0'
|
||||
# up: 'GREEN_CONCRETE:0'
|
||||
# down: 'GREEN_CONCRETE:0'
|
||||
# no-path: 'GREEN_CONCRETE:0'
|
||||
# locked:
|
||||
# up-right-down-left: 'GRAY_CONCRETE:0'
|
||||
# up-right-down: 'GRAY_CONCRETE:0'
|
||||
@ -534,3 +551,16 @@ nodes:
|
||||
# up: 'BLACK_CONCRETE:0'
|
||||
# down: 'BLACK_CONCRETE:0'
|
||||
# no-path: 'BLACK_CONCRETE:0'
|
||||
|
||||
# The following syntax works too, it applies the same texture
|
||||
# no matter the neighboring paths.
|
||||
#
|
||||
#display:
|
||||
# paths:
|
||||
# .......
|
||||
# nodes:
|
||||
# unlocked: 'WHITE_CONCRETE:0'
|
||||
# maxed-out: 'GREEN_CONCRETE:0'
|
||||
# locked: 'GRAY_CONCRETE:0'
|
||||
# unlockable: 'BLUE_CONCRETE:0'
|
||||
# fully-locked: 'BLACK_CONCRETE:0'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user