Implemented feature associated to ticket #824 by adding incompatible-parents field in skill tree node config.

This commit is contained in:
Ka0rX 2023-05-07 16:28:18 +01:00
parent a125320dd0
commit 8e504e1a10
7 changed files with 84 additions and 97 deletions

View File

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

View File

@ -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<SkillTree> {
* @return The list of all the roots (e.g the nodes without any parents
*/
public List<SkillTreeNode> 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<SkillTreeNode> getAllNodes() {

View File

@ -3,5 +3,8 @@ package net.Indyuce.mmocore.skilltree;
public enum ParentType {
SOFT,
STRONG
STRONG,
INCOMPATIBLE;
}

View File

@ -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<SkillTreeNode, Integer> softParents = new HashMap<>();
private final Map<SkillTreeNode, Integer> strongParents = new HashMap<>();
private final Map<ParentInformation, Integer> 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<ParentInformation, Integer> 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<ParentInformation, Integer> 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<SkillTreeNode> getSoftParents() {
return softParents.keySet();
public Set<SkillTreeNode> getParents() {
return parents.keySet().stream().map(ParentInformation::node).collect(Collectors.toSet());
}
public Set<SkillTreeNode> getStrongParents() {
return strongParents.keySet();
public Set<SkillTreeNode> getParents(ParentType parentType) {
return parents.entrySet().stream().filter(entry -> entry.getKey().type() == parentType).map((entry) -> entry.getKey().node()).collect(Collectors.toSet());
}
public List<SkillTreeNode> 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");
}

View File

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

View File

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

View File

@ -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<String, SkillTreeNode> nodes = new HashMap<>();
protected final int maxPointSpent;
//Caches the height of the skill tree
protected int minX, minY, maxX, maxY;
protected final List<SkillTreeNode> 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<String> 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<SkillTreeNode> strongParents = node.getParents(ParentType.STRONG);
Set<SkillTreeNode> softParents = node.getParents(ParentType.SOFT);
Set<SkillTreeNode> 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);