From 8e504e1a109ce6132931740a69aa7a6998d26c5d Mon Sep 17 00:00:00 2001 From: Ka0rX Date: Sun, 7 May 2023 16:28:18 +0100 Subject: [PATCH] Implemented feature associated to ticket #824 by adding incompatible-parents field in skill tree node config. --- .../gui/skilltree/SkillTreeViewer.java | 25 +++---- .../mmocore/manager/SkillTreeManager.java | 3 +- .../Indyuce/mmocore/skilltree/ParentType.java | 5 +- .../mmocore/skilltree/SkillTreeNode.java | 44 +++++++---- .../skilltree/tree/CustomSkillTree.java | 23 +++--- .../skilltree/tree/ParentInformation.java | 7 ++ .../mmocore/skilltree/tree/SkillTree.java | 74 ++++++------------- 7 files changed, 84 insertions(+), 97 deletions(-) create mode 100644 MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/ParentInformation.java diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/skilltree/SkillTreeViewer.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/skilltree/SkillTreeViewer.java index b67bc46d..cb8973eb 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/skilltree/SkillTreeViewer.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/skilltree/SkillTreeViewer.java @@ -12,10 +12,7 @@ import net.Indyuce.mmocore.gui.api.item.InventoryItem; import net.Indyuce.mmocore.gui.api.item.Placeholders; import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem; import net.Indyuce.mmocore.gui.skilltree.display.*; -import net.Indyuce.mmocore.skilltree.IntegerCoordinates; -import net.Indyuce.mmocore.skilltree.NodeStatus; -import net.Indyuce.mmocore.skilltree.SkillTreeNode; -import net.Indyuce.mmocore.skilltree.SkillTreePath; +import net.Indyuce.mmocore.skilltree.*; import net.Indyuce.mmocore.skilltree.tree.SkillTree; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -43,8 +40,8 @@ public class SkillTreeViewer extends EditableInventory { super.reload(config); //Loads all the pathDisplayInfo for (PathStatus status : PathStatus.values()) - for (PathType pathType :PathType.values()){ - if(!config.contains("display.paths." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(pathType.name()))){ + for (PathType pathType : PathType.values()) { + if (!config.contains("display.paths." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(pathType.name()))) { MMOCore.log("Missing path type: " + MMOCoreUtils.ymlName(pathType.name()) + " for status: " + MMOCoreUtils.ymlName(status.name())); continue; } @@ -52,8 +49,8 @@ public class SkillTreeViewer extends EditableInventory { } //Loads all the nodeDisplayInfo for (NodeStatus status : NodeStatus.values()) - for (NodeType nodeType :NodeType.values()){ - if(!config.contains("display.nodes." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(nodeType.name()))){ + for (NodeType nodeType : NodeType.values()) { + if (!config.contains("display.nodes." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(nodeType.name()))) { MMOCore.log("Missing node type: " + MMOCoreUtils.ymlName(nodeType.name()) + " for status: " + MMOCoreUtils.ymlName(status.name())); continue; } @@ -225,9 +222,11 @@ public class SkillTreeViewer extends EditableInventory { if (str.contains("{node-lore}")) { lore.addAll(node.getLore(inv.getPlayerData())); } else if (str.contains("{strong-parents}")) { - lore.addAll(getParentsLore(inv, node, node.getStrongParents())); + lore.addAll(getParentsLore(inv, node, node.getParents(ParentType.STRONG))); } else if (str.contains("{soft-parents}")) { - lore.addAll(getParentsLore(inv, node, node.getSoftParents())); + lore.addAll(getParentsLore(inv, node, node.getParents(ParentType.SOFT))); + } else if (str.contains("{incompatible-parents}")) { + lore.addAll(getParentsLore(inv, node, node.getParents(ParentType.INCOMPATIBLE))); } else lore.add(getPlaceholders(inv, n).apply(inv.getPlayer(), str)); }); @@ -345,9 +344,9 @@ public class SkillTreeViewer extends EditableInventory { if (skillTree.isNode(coordinates)) { SkillTreeNode node = skillTree.getNode(coordinates); - NodeStatus nodeStatus =playerData.getNodeStatus(node); + NodeStatus nodeStatus = playerData.getNodeStatus(node); //If the node has its own display, it will be shown. - if(node.hasIcon(nodeStatus)) + if (node.hasIcon(nodeStatus)) return node.getIcon(nodeStatus); NodeType nodeType = NodeType.getNodeType(hasUpPath, hasRightPath, hasDownPath, hasLeftPath); @@ -479,7 +478,7 @@ public class SkillTreeViewer extends EditableInventory { } else if (playerData.getNodeLevel(node) >= node.getMaxLevel()) { MMOCore.plugin.configManager.getSimpleMessage("skill-node-max-level-hit").send(player); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); - }else if(!node.hasPermissionRequirement(playerData)){ + } else if (!node.hasPermissionRequirement(playerData)) { MMOCore.plugin.configManager.getSimpleMessage("missing-skill-node-permission").send(player); MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java index c9d14a22..49d0684d 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore.manager; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.skilltree.ParentType; import net.Indyuce.mmocore.skilltree.tree.SkillTree; import net.Indyuce.mmocore.manager.registry.MMOCoreRegister; import net.Indyuce.mmocore.skilltree.SkillTreeNode; @@ -35,7 +36,7 @@ public class SkillTreeManager extends MMOCoreRegister { * @return The list of all the roots (e.g the nodes without any parents */ public List getRootNodes() { - return skillTreeNodes.values().stream().filter(treeNode -> treeNode.getSoftParents().size() == 0).collect(Collectors.toList()); + return skillTreeNodes.values().stream().filter(treeNode -> treeNode.getParents(ParentType.SOFT).size() == 0).collect(Collectors.toList()); } public Collection getAllNodes() { diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/ParentType.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/ParentType.java index 667e5ea5..c2161234 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/ParentType.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/ParentType.java @@ -3,5 +3,8 @@ package net.Indyuce.mmocore.skilltree; public enum ParentType { SOFT, - STRONG + STRONG, + + INCOMPATIBLE; + } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java index 430ab4d8..cedfce56 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/SkillTreeNode.java @@ -9,7 +9,9 @@ import net.Indyuce.mmocore.experience.ExpCurve; import net.Indyuce.mmocore.experience.ExperienceObject; import net.Indyuce.mmocore.experience.droptable.ExperienceTable; import net.Indyuce.mmocore.gui.api.item.Placeholders; +import net.Indyuce.mmocore.gui.skilltree.SkillTreeViewer; import net.Indyuce.mmocore.gui.skilltree.display.Icon; +import net.Indyuce.mmocore.skilltree.tree.ParentInformation; import net.Indyuce.mmocore.skilltree.tree.SkillTree; import org.apache.commons.lang.Validate; import org.bukkit.Location; @@ -18,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.stream.Collectors; // We must use generics to get the type of the corresponding tree public class SkillTreeNode implements ExperienceObject { @@ -52,8 +55,7 @@ public class SkillTreeNode implements ExperienceObject { * You only need to have the requirement for one of your softParents * but you need to fulfill the requirements of all of your strong parents. **/ - private final Map softParents = new HashMap<>(); - private final Map strongParents = new HashMap<>(); + private final Map parents = new HashMap<>(); /** * Prefix used in node key @@ -66,12 +68,12 @@ public class SkillTreeNode implements ExperienceObject { this.tree = tree; if (config.isConfigurationSection("display")) { for (NodeStatus status : NodeStatus.values()) { - String ymlStatus=MMOCoreUtils.ymlName(status.name()); + String ymlStatus = MMOCoreUtils.ymlName(status.name()); if (!config.isConfigurationSection("display." + ymlStatus)) { MMOCore.log("Could not find node display for status " + ymlStatus + " for node " + id + " in tree " + tree.getId() + ". Using default display."); continue; } - icons.put(status, new Icon(config.getConfigurationSection("display." +MMOCoreUtils.ymlName(status.name())))); + icons.put(status, new Icon(config.getConfigurationSection("display." + MMOCoreUtils.ymlName(status.name())))); } } name = Objects.requireNonNull(config.getString("name"), "Could not find node name"); @@ -120,10 +122,7 @@ public class SkillTreeNode implements ExperienceObject { // Used when postLoaded public void addParent(SkillTreeNode parent, int requiredLevel, ParentType parentType) { - if (parentType == ParentType.SOFT) - softParents.put(parent, requiredLevel); - else - strongParents.put(parent, requiredLevel); + parents.put(new ParentInformation(parent, parentType), requiredLevel); } public void addChild(SkillTreeNode child) { @@ -138,12 +137,23 @@ public class SkillTreeNode implements ExperienceObject { this.coordinates = coordinates; } + public int getParentNeededLevel(SkillTreeNode parent) { - return softParents.containsKey(parent) ? softParents.get(parent) : strongParents.containsKey(parent) ? strongParents.get(parent) : 0; + for (Map.Entry entry : parents.entrySet()) + if (entry.getKey().node().equals(parent)) + return entry.getValue(); + throw new RuntimeException("Could not find parent " + parent.getId() + " for node " + id); + } + + public int getParentNeededLevel(SkillTreeNode parent, ParentType parentType) { + return parents.get(new ParentInformation(parent, parentType)); } public boolean hasParent(SkillTreeNode parent) { - return softParents.containsKey(parent) || strongParents.containsKey(parent); + for (Map.Entry entry : parents.entrySet()) + if (entry.getKey().node() == parent) + return true; + return false; } public int getMaxLevel() { @@ -154,18 +164,19 @@ public class SkillTreeNode implements ExperienceObject { return maxChildren; } - public boolean hasPermissionRequirement(PlayerData playerData){ + public boolean hasPermissionRequirement(PlayerData playerData) { return permissionRequired == null || playerData.getPlayer().hasPermission(permissionRequired); } - public Set getSoftParents() { - return softParents.keySet(); + public Set getParents() { + return parents.keySet().stream().map(ParentInformation::node).collect(Collectors.toSet()); } - public Set getStrongParents() { - return strongParents.keySet(); + public Set getParents(ParentType parentType) { + return parents.entrySet().stream().filter(entry -> entry.getKey().type() == parentType).map((entry) -> entry.getKey().node()).collect(Collectors.toSet()); } + public List getChildren() { return children; } @@ -256,7 +267,8 @@ public class SkillTreeNode implements ExperienceObject { } @Override - public void giveExperience(PlayerData playerData, double experience, @Nullable Location hologramLocation, @NotNull EXPSource source) { + public void giveExperience(PlayerData playerData, double experience, @Nullable Location hologramLocation, + @NotNull EXPSource source) { throw new RuntimeException("Attributes don't have experience"); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/CustomSkillTree.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/CustomSkillTree.java index bd13ab63..682f9d97 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/CustomSkillTree.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/CustomSkillTree.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.skilltree.tree; +import io.lumine.mythic.lib.UtilityMethods; import net.Indyuce.mmocore.skilltree.ParentType; import net.Indyuce.mmocore.skilltree.SkillTreeNode; import org.bukkit.configuration.ConfigurationSection; @@ -18,19 +19,13 @@ public class CustomSkillTree extends SkillTree { // Setup the children and parents for each node. for (SkillTreeNode node : nodes.values()) { - - ConfigurationSection section = config.getConfigurationSection("nodes." + node.getId() + ".parents.soft"); - if (section != null) { - for (String parent : section.getKeys(false)) { - node.addParent(getNode(parent), section.getInt(parent), ParentType.SOFT); - getNode(parent).addChild(node); - } - } - section = config.getConfigurationSection("nodes." + node.getId() + ".parents.strong"); - if (section != null) { - for (String parent : section.getKeys(false)) { - node.addParent(getNode(parent), section.getInt(parent), ParentType.STRONG); - getNode(parent).addChild(node); + for (String key : config.getConfigurationSection("nodes." + node.getId() + "parents").getKeys(false)) { + ConfigurationSection section = config.getConfigurationSection("nodes." + node.getId() + ".parents." + key); + if (section != null) { + for (String parent : section.getKeys(false)) { + node.addParent(getNode(parent), section.getInt(parent), ParentType.valueOf(UtilityMethods.enumName(key))); + getNode(parent).addChild(node); + } } } } @@ -41,7 +36,7 @@ public class CustomSkillTree extends SkillTree { // Find the tree roots which don't have any parents for (SkillTreeNode node : nodes.values()) { - if (node.getSoftParents().size() + node.getStrongParents().size() == 0) { + if (node.getParents().size() == 0) { // We mark the node as a root also roots.add(node); node.setIsRoot(); diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/ParentInformation.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/ParentInformation.java new file mode 100644 index 00000000..9999a9a3 --- /dev/null +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/ParentInformation.java @@ -0,0 +1,7 @@ +package net.Indyuce.mmocore.skilltree.tree; + +import net.Indyuce.mmocore.skilltree.ParentType; +import net.Indyuce.mmocore.skilltree.SkillTreeNode; + +public record ParentInformation(SkillTreeNode node, ParentType type) { +} diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/SkillTree.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/SkillTree.java index cc6b6e37..113ab54e 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/SkillTree.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skilltree/tree/SkillTree.java @@ -6,10 +6,7 @@ import io.lumine.mythic.lib.api.util.PostLoadObject; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.manager.registry.RegisteredObject; -import net.Indyuce.mmocore.skilltree.IntegerCoordinates; -import net.Indyuce.mmocore.skilltree.NodeStatus; -import net.Indyuce.mmocore.skilltree.SkillTreeNode; -import net.Indyuce.mmocore.skilltree.SkillTreePath; +import net.Indyuce.mmocore.skilltree.*; import org.apache.commons.lang.Validate; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; @@ -54,7 +51,6 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje protected final Map nodes = new HashMap<>(); protected final int maxPointSpent; //Caches the height of the skill tree - protected int minX, minY, maxX, maxY; protected final List roots = new ArrayList<>(); public SkillTree(ConfigurationSection config) { @@ -108,43 +104,6 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje @Override protected abstract void whenPostLoaded(@NotNull ConfigurationSection configurationSection); -/* - - public Icon getIcon(SkillTreeNode node) { - SkillTree skillTree = node.getTree(); - - DisplayInfo displayInfo = new DisplayInfo(nodeStates.get(node), node.getSize()); - - return skillTree.getIcon(displayInfo); - } - - public Icon getIcon(SkillTree skillTree, IntegerCoordinates coordinates) { - - if (skillTree.isNode(coordinates)) { - SkillTreeNode node = skillTree.getNode(coordinates); - DisplayInfo displayInfo = new DisplayInfo(nodeStates.get(node), node.getSize()); - return skillTree.getIcon(displayInfo); - } - if (skillTree.isPath(coordinates)) return skillTree.getIcon(DisplayInfo.pathInfo); - return null; - } - */ - - public int getMaxX() { - return maxX; - } - - public int getMinX() { - return minX; - } - - public int getMinY() { - return minY; - } - - public int getMaxY() { - return maxY; - } public List getLore() { return lore; @@ -207,13 +166,18 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje } else if (playerData.getNodeLevel(node) == 0 && node.isRoot()) { playerData.setNodeState(node, NodeStatus.UNLOCKABLE); } else { - boolean isUnlockableFromStrongParent = node.getStrongParents().size() == 0 ? true : true; - boolean isUnlockableFromSoftParent = node.getSoftParents().size() == 0 ? true : false; - boolean isFullyLockedFromStrongParent = node.getStrongParents().size() == 0 ? false : false; - boolean isFullyLockedFromSoftParent = node.getSoftParents().size() == 0 ? false : true; + Set strongParents = node.getParents(ParentType.STRONG); + Set softParents = node.getParents(ParentType.SOFT); + Set incompatibleParents = node.getParents(ParentType.INCOMPATIBLE); - for (SkillTreeNode strongParent : node.getStrongParents()) { - if (playerData.getNodeLevel(strongParent) < node.getParentNeededLevel(strongParent)) { + boolean isUnlockableFromStrongParent = true; + boolean isUnlockableFromSoftParent = softParents.size() == 0; + boolean isFullyLockedFromStrongParent = false; + boolean isFullyLockedFromSoftParent = softParents.size() != 0; + boolean isFullyLockedFromIncompatibleParent = false; + + for (SkillTreeNode strongParent : strongParents) { + if (playerData.getNodeLevel(strongParent) < node.getParentNeededLevel(strongParent, ParentType.STRONG)) { isUnlockableFromStrongParent = false; } //We count the number of children the parent @@ -228,11 +192,11 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje } - for (SkillTreeNode softParent : node.getSoftParents()) { - if (playerData.getNodeLevel(softParent) >= node.getParentNeededLevel(softParent)) { + for (SkillTreeNode softParent : node.getParents(ParentType.SOFT)) { + if (playerData.getNodeLevel(softParent) >= node.getParentNeededLevel(softParent, ParentType.SOFT)) { isUnlockableFromSoftParent = true; } - //We count the number of children the parent + //We count the number of children the parent has int numberChildren = 0; for (SkillTreeNode child : softParent.getChildren()) if (playerData.getNodeLevel(child) > 0) @@ -240,8 +204,14 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje if (numberChildren < softParent.getMaxChildren() && playerData.getNodeStatus(softParent) != NodeStatus.FULLY_LOCKED) isFullyLockedFromSoftParent = false; } + for (SkillTreeNode incompatibleParent : node.getParents(ParentType.INCOMPATIBLE)) { + if (playerData.getNodeLevel(incompatibleParent) > 0) { + isFullyLockedFromIncompatibleParent = true; + break; + } + } - boolean isFullyLocked = isFullyLockedFromSoftParent || isFullyLockedFromStrongParent; + boolean isFullyLocked = isFullyLockedFromSoftParent || isFullyLockedFromStrongParent || isFullyLockedFromIncompatibleParent; boolean isUnlockable = isUnlockableFromSoftParent && isUnlockableFromStrongParent; if (isFullyLocked) playerData.setNodeState(node, NodeStatus.FULLY_LOCKED);