/* * * AttributeTree * * Handles a players individual attribute tree * * */ package jesse.keeblarcraft.AttributeMgr; import java.util.ArrayList; import java.util.List; import java.util.HashMap; import jesse.keeblarcraft.Keeblarcraft; import jesse.keeblarcraft.AttributeMgr.AttributeNodes.AbstractNode; import jesse.keeblarcraft.ConfigMgr.ConfigManager; import jesse.keeblarcraft.Utils.ChatUtil; import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR; import jesse.keeblarcraft.Utils.CustomExceptions.FILE_WRITE_EXCEPTION; public class AttributeTree { PlayerTree playerAttributeTree = new PlayerTree(); ConfigManager config = new ConfigManager(); private AbstractNode root = new RootNode(); private class TreeNode { public TreeNode(AbstractNode node, Integer maxLevel, List parents, List children) { thisNode = node; parentNodes = parents; childrenNodes = children; maxNodeLevel = maxLevel; currentNodeLevel = 1; } AbstractNode thisNode; Integer currentNodeLevel; Integer maxNodeLevel; // Store the names of the parent and children nodes; this lets a node have any number of parents and children // NOTE TO DEVELOPERS: Be aware! The more nodes the harrier the tree will look in the GUI!!! Always test your // code before just adding stuff willy-nilly! List parentNodes; List childrenNodes; } // Separate structure as this is written to the config file private class PlayerTree { String uuid; // Key = name of AbstractNode // Val = The attribute itself HashMap tree = new HashMap(); } private class RootNode extends AbstractNode { @Override public String GetNodeTitle() { return "root"; } @Override public String GetNodeDescription() { return "This is the players tree root! All attributes extend from here"; } @Override public HashMap> GetDetails() { HashMap> ret = new HashMap>(); ret.put("First Attribute", List.of("This is your skill tree", "All attributes grow from this root! Unlocking nodes requires all nodes connected to that previously to be unlocked first")); return ret; } } // Add a new node to the tree arbitrarily // Root can never be a child node // parents can never be empty or null (null or empty would imply you are adding root) // // developer warning: you must personally verify your nodes are in the tree. anywhere is fine, and the tree // is very dynamic (which is why self verification is required). When the tree is drawn in the GUI, it starts from // the root node. As long as your node is attached to this node anywhere in the indirected graph; then your attribute // will be drawn in the gui public Boolean AddNewNode(AbstractNode newNode, Integer maxNodeLevel, List parents, List children) { Boolean ret = false; TreeNode nodeReference = playerAttributeTree.tree.get(newNode.GetNodeTitle()); System.out.println("Is node reference null? -> " + (nodeReference == null ? "YES":"NO")); System.out.println("Is root reference null? -> " + (root == null ? "YES":"NO")); System.out.println("Is parents null? -> " + (parents == null ? "YES":"NO")); // Some special handling is required on checking child list in case it is null Boolean validChildren = true; if (children != null) { validChildren = !children.contains(root.GetNodeTitle()); } if (nodeReference == null && validChildren) { // Since these values can be left as null, we want to guarentee they are at least initialized here maxNodeLevel = (maxNodeLevel == null ? 1 : maxNodeLevel); parents = (parents == null ? new ArrayList() : parents); children = (children == null ? new ArrayList() : children); System.out.println("Parent size: " + parents.size()); if (parents.size() == 0) { System.out.println("Root node title: " + root.GetNodeTitle()); parents.add(root.GetNodeTitle()); // No disconnected nodes allowed! All parentless nodes are adopted by root by default } System.out.println("Parents size: " + parents.size()); // Node implementation here playerAttributeTree.tree.put(newNode.GetNodeTitle(), new TreeNode(newNode, maxNodeLevel, parents, children)); long pSize = playerAttributeTree.tree.get(newNode.GetNodeTitle()).parentNodes.size(); System.out.println("parent size is " + pSize); } else { // Some fancy error handling for console log if (nodeReference != null) { Keeblarcraft.LOGGER.error("Attribute with name " + newNode.GetNodeTitle() + " was already found within the tree! Rename your node to add it"); } else { Keeblarcraft.LOGGER.error("The attribute you attempted to add (name: " + newNode.GetNodeTitle() + "), has root in the children"); } ret = false; } FlashConfig(); return ret; } public void ChangeNodeLevel(String nodeName, Integer newLevel) { TreeNode nodeReference = playerAttributeTree.tree.get(nodeName); if (nodeReference != null) { if (newLevel <= nodeReference.maxNodeLevel) { nodeReference.currentNodeLevel = newLevel; } } FlashConfig(); } public void DeleteNode(String nodeName) { // Do not delete root! if (nodeName != root.GetNodeTitle()) { playerAttributeTree.tree.remove(nodeName); } FlashConfig(); } public HashMap> GetNodeDetails(String nodeName) { HashMap> ret = null; TreeNode nodeReference = playerAttributeTree.tree.get(nodeName); if (nodeReference != null) { ret = nodeReference.thisNode.GetDetails(); } return ret; } // Returns the player's attribute tree public PlayerTree GetPlayerTree() { return playerAttributeTree; } public AttributeTree(String uuid) { // DEVELOPER NOTE: // If you are testing this part of the code, please be reminded that anonymous testing starts a new // player instance everytime you launch. This means the UUID CAN CHANGE when you launch the // game! This is not an issue with proper registered accounts in production Boolean existingFile = false; try { playerAttributeTree = config.GetJsonObjectFromFile("attributes/" + uuid + ".json", PlayerTree.class); existingFile = true; } catch (Exception e) { // Do nothing. This means the file does not exist } // In the event the above code failed out, this means a new file has to be created for the player's uuid if (!existingFile) { System.out.println(ChatUtil.ColoredString("Trying to create new file", CONSOLE_COLOR.BLUE)); try { playerAttributeTree.uuid = uuid; FlashConfig(); } catch (Exception e) { System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED)); } } else { System.out.println(ChatUtil.ColoredString("Moving on", CONSOLE_COLOR.BLUE)); } // It's possible the above code will return a blank class if a file doesn't exist. This will make // a new file with this players uuid if ("".equals(playerAttributeTree.uuid)) { System.out.println(ChatUtil.ColoredString("Assigning new config file for this uuid. No previous existing", CONSOLE_COLOR.BLUE)); playerAttributeTree.uuid = uuid; } // If the tree is empty or missing root, add it! if (playerAttributeTree.tree.size() == 0 || playerAttributeTree.tree.containsKey(root.GetNodeTitle()) == false) { playerAttributeTree.tree.put(root.GetNodeTitle(), new TreeNode(root, 1, null, null)); } } public void FlashConfig() { try { config.WriteToJsonFile("attributes/" + playerAttributeTree.uuid + ".json", playerAttributeTree); } catch (FILE_WRITE_EXCEPTION e) { System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED)); } } }